Browse Source

Merge tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull TTY changes from Greg Kroah-Hartman:
 "As we skipped the merge window for 3.6-rc1 for the tty tree,
  everything is now settled down and working properly, so we are ready
  for 3.7-rc1.  Here's the patchset, it's big, but the large changes are
  removing a firmware file and adding a staging tty driver (it depended
  on the tty core changes, so it's going through this tree instead of
  the staging tree.)

  All of these patches have been in the linux-next tree for a while.

  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"

Fix up more-or-less trivial conflicts in
 - drivers/char/pcmcia/synclink_cs.c:
    tty NULL dereference fix vs tty_port_cts_enabled() helper function
 - drivers/staging/{Kconfig,Makefile}:
    add-add conflict (dgrp driver added close to other staging drivers)
 - drivers/staging/ipack/devices/ipoctal.c:
    "split ipoctal_channel from iopctal" vs "TTY: use tty_port_register_device"

* tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (235 commits)
  tty/serial: Add kgdb_nmi driver
  tty/serial/amba-pl011: Quiesce interrupts in poll_get_char
  tty/serial/amba-pl011: Implement poll_init callback
  tty/serial/core: Introduce poll_init callback
  kdb: Turn KGDB_KDB=n stubs into static inlines
  kdb: Implement disable_nmi command
  kernel/debug: Mask KGDB NMI upon entry
  serial: pl011: handle corruption at high clock speeds
  serial: sccnxp: Make 'default' choice in switch last
  serial: sccnxp: Remove mask termios caps for SW flow control
  serial: sccnxp: Report actual baudrate back to core
  serial: samsung: Add poll_get_char & poll_put_char
  Powerpc 8xx CPM_UART setting MAXIDL register proportionaly to baud rate
  Powerpc 8xx CPM_UART maxidl should not depend on fifo size
  Powerpc 8xx CPM_UART too many interrupts
  Powerpc 8xx CPM_UART desynchronisation
  serial: set correct baud_base for EXSYS EX-41092 Dual 16950
  serial: omap: fix the reciever line error case
  8250: blacklist Winbond CIR port
  8250_pnp: do pnp probe before legacy probe
  ...
Linus Torvalds 12 years ago
parent
commit
3498d13b80
100 changed files with 12424 additions and 1624 deletions
  1. 9 0
      Documentation/ABI/testing/sysfs-tty
  2. 14 0
      Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt
  3. 2 0
      Documentation/devicetree/bindings/tty/serial/of-serial.txt
  4. 0 2
      Documentation/serial/00-INDEX
  5. 0 520
      Documentation/serial/computone.txt
  6. 1 0
      arch/alpha/kernel/srmcons.c
  7. 12 6
      arch/arm/mach-omap2/serial.c
  8. 0 21
      arch/arm/mach-ux500/board-mop500.c
  9. 9 41
      arch/arm/plat-omap/include/plat/omap-serial.h
  10. 2 1
      arch/ia64/hp/sim/simserial.c
  11. 4 0
      arch/m68k/emu/nfcon.c
  12. 15 15
      arch/mips/cavium-octeon/serial.c
  13. 32 0
      arch/mips/sni/a20r.c
  14. 1 0
      arch/parisc/kernel/pdc_cons.c
  15. 2 1
      arch/um/drivers/line.c
  16. 1 0
      arch/xtensa/platforms/iss/console.c
  17. 1 1
      drivers/bluetooth/hci_ath.c
  18. 8 8
      drivers/char/mwave/mwavedd.c
  19. 60 71
      drivers/char/pcmcia/synclink_cs.c
  20. 13 20
      drivers/char/ttyprintk.c
  21. 2 1
      drivers/isdn/capi/capi.c
  22. 4 3
      drivers/isdn/gigaset/interface.c
  23. 24 17
      drivers/isdn/i4l/isdn_tty.c
  24. 8 8
      drivers/misc/ibmasm/uart.c
  25. 59 69
      drivers/misc/pti.c
  26. 12 12
      drivers/mmc/card/sdio_uart.c
  27. 12 10
      drivers/net/ethernet/sgi/ioc3-eth.c
  28. 5 5
      drivers/net/irda/irtty-sir.c
  29. 9 10
      drivers/net/usb/hso.c
  30. 1 0
      drivers/parport/parport_gsc.c
  31. 10 1
      drivers/parport/parport_serial.c
  32. 22 6
      drivers/s390/char/con3215.c
  33. 1 0
      drivers/s390/char/sclp_tty.c
  34. 1 0
      drivers/s390/char/sclp_vt220.c
  35. 24 10
      drivers/s390/char/tty3270.c
  36. 2 0
      drivers/staging/Kconfig
  37. 1 0
      drivers/staging/Makefile
  38. 9 0
      drivers/staging/dgrp/Kconfig
  39. 12 0
      drivers/staging/dgrp/Makefile
  40. 2 0
      drivers/staging/dgrp/README
  41. 13 0
      drivers/staging/dgrp/TODO
  42. 200 0
      drivers/staging/dgrp/dgrp_common.c
  43. 208 0
      drivers/staging/dgrp/dgrp_common.h
  44. 556 0
      drivers/staging/dgrp/dgrp_dpa_ops.c
  45. 110 0
      drivers/staging/dgrp/dgrp_driver.c
  46. 346 0
      drivers/staging/dgrp/dgrp_mon_ops.c
  47. 3737 0
      drivers/staging/dgrp/dgrp_net_ops.c
  48. 170 0
      drivers/staging/dgrp/dgrp_ports_ops.c
  49. 822 0
      drivers/staging/dgrp/dgrp_specproc.c
  50. 555 0
      drivers/staging/dgrp/dgrp_sysfs.c
  51. 3331 0
      drivers/staging/dgrp/dgrp_tty.c
  52. 129 0
      drivers/staging/dgrp/digirp.h
  53. 693 0
      drivers/staging/dgrp/drp.h
  54. 7 7
      drivers/staging/ipack/devices/ipoctal.c
  55. 8 10
      drivers/staging/serqt_usb2/serqt_usb2.c
  56. 1 2
      drivers/staging/speakup/serialio.h
  57. 4 5
      drivers/tty/Kconfig
  58. 23 22
      drivers/tty/amiserial.c
  59. 1 0
      drivers/tty/bfin_jtag_comm.c
  60. 52 50
      drivers/tty/cyclades.c
  61. 5 4
      drivers/tty/ehv_bytechan.c
  62. 1 1
      drivers/tty/hvc/Kconfig
  63. 26 7
      drivers/tty/hvc/hvc_console.c
  64. 49 33
      drivers/tty/hvc/hvcs.c
  65. 2 0
      drivers/tty/hvc/hvsi.c
  66. 1 1
      drivers/tty/hvc/hvsi_lib.c
  67. 6 1
      drivers/tty/ipwireless/network.c
  68. 1 1
      drivers/tty/ipwireless/tty.c
  69. 7 6
      drivers/tty/isicom.c
  70. 28 11
      drivers/tty/moxa.c
  71. 46 17
      drivers/tty/mxser.c
  72. 87 57
      drivers/tty/n_gsm.c
  73. 5 5
      drivers/tty/n_r3964.c
  74. 24 5
      drivers/tty/n_tty.c
  75. 2 2
      drivers/tty/nozomi.c
  76. 122 112
      drivers/tty/pty.c
  77. 11 11
      drivers/tty/rocket.c
  78. 13 10
      drivers/tty/serial/68328serial.c
  79. 79 77
      drivers/tty/serial/8250/8250.c
  80. 10 33
      drivers/tty/serial/8250/8250.h
  81. 11 11
      drivers/tty/serial/8250/8250_acorn.c
  82. 19 19
      drivers/tty/serial/8250/8250_dw.c
  83. 13 13
      drivers/tty/serial/8250/8250_gsc.c
  84. 13 13
      drivers/tty/serial/8250/8250_hp300.c
  85. 146 68
      drivers/tty/serial/8250/8250_pci.c
  86. 32 27
      drivers/tty/serial/8250/8250_pnp.c
  87. 8 8
      drivers/tty/serial/8250/Kconfig
  88. 3 2
      drivers/tty/serial/8250/Makefile
  89. 15 15
      drivers/tty/serial/8250/serial_cs.c
  90. 68 5
      drivers/tty/serial/Kconfig
  91. 4 1
      drivers/tty/serial/Makefile
  92. 3 3
      drivers/tty/serial/altera_uart.c
  93. 4 11
      drivers/tty/serial/amba-pl010.c
  94. 133 44
      drivers/tty/serial/amba-pl011.c
  95. 1 1
      drivers/tty/serial/bfin_uart.c
  96. 19 4
      drivers/tty/serial/cpm_uart/cpm_uart_core.c
  97. 24 21
      drivers/tty/serial/crisv10.c
  98. 2 2
      drivers/tty/serial/ifx6x60.c
  99. 7 6
      drivers/tty/serial/imx.c
  100. 2 1
      drivers/tty/serial/ioc3_serial.c

+ 9 - 0
Documentation/ABI/testing/sysfs-tty

@@ -17,3 +17,12 @@ Description:
 		 device, like 'tty1'.
 		 The file supports poll() to detect virtual
 		 console switches.
+
+What:		/sys/class/tty/ttyS0/uartclk
+Date:		Sep 2012
+Contact:	Tomas Hlavacek <tmshlvck@gmail.com>
+Description:
+		 Shows the current uartclk value associated with the
+		 UART port in serial_core, that is bound to TTY like ttyS0.
+		 uartclk = 16 * baud_base
+

+ 14 - 0
Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt

@@ -0,0 +1,14 @@
+* NXP LPC32xx SoC High Speed UART
+
+Required properties:
+- compatible: Should be "nxp,lpc3220-hsuart"
+- reg: Should contain registers location and length
+- interrupts: Should contain interrupt
+
+Example:
+
+	uart1: serial@40014000 {
+		compatible = "nxp,lpc3220-hsuart";
+		reg = <0x40014000 0x1000>;
+		interrupts = <26 0>;
+	};

+ 2 - 0
Documentation/devicetree/bindings/tty/serial/of-serial.txt

@@ -25,6 +25,8 @@ Optional properties:
   accesses to the UART (e.g. TI davinci).
 - used-by-rtas : set to indicate that the port is in use by the OpenFirmware
   RTAS and should not be registered.
+- no-loopback-test: set to indicate that the port does not implements loopback
+  test mode
 
 Example:
 

+ 0 - 2
Documentation/serial/00-INDEX

@@ -2,8 +2,6 @@
 	- this file.
 README.cycladesZ
 	- info on Cyclades-Z firmware loading.
-computone.txt
-	- info on Computone Intelliport II/Plus Multiport Serial Driver.
 digiepca.txt
 	- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
 hayes-esp.txt

+ 0 - 520
Documentation/serial/computone.txt

@@ -1,520 +0,0 @@
-NOTE: This is an unmaintained driver.  It is not guaranteed to work due to
-changes made in the tty layer in 2.6.  If you wish to take over maintenance of
-this driver, contact Michael Warfield <mhw@wittsend.com>.
-
-Changelog:
-----------
-11-01-2001:	Original Document
-
-10-29-2004:	Minor misspelling & format fix, update status of driver.
-		James Nelson <james4765@gmail.com>
-
-Computone Intelliport II/Plus Multiport Serial Driver
------------------------------------------------------
-
-Release Notes For Linux Kernel 2.2 and higher.
-These notes are for the drivers which have already been integrated into the
-kernel and have been tested on Linux kernels 2.0, 2.2, 2.3, and 2.4.
-
-Version: 1.2.14
-Date: 11/01/2001
-Historical Author: Andrew Manison <amanison@america.net>
-Primary Author: Doug McNash
-
-This file assumes that you are using the Computone drivers which are
-integrated into the kernel sources.  For updating the drivers or installing
-drivers into kernels which do not already have Computone drivers, please
-refer to the instructions in the README.computone file in the driver patch.
-
-
-1. INTRODUCTION
-
-This driver supports the entire family of Intelliport II/Plus controllers
-with the exception of the MicroChannel controllers.  It does not support
-products previous to the Intelliport II.
-
-This driver was developed on the v2.0.x Linux tree and has been tested up
-to v2.4.14; it will probably not work with earlier v1.X kernels,.
-
-
-2. QUICK INSTALLATION
-
-Hardware - If you have an ISA card, find a free interrupt and io port. 
-		   List those in use with `cat /proc/interrupts` and 
-		   `cat /proc/ioports`.  Set the card dip switches to a free 
-		   address.  You may need to configure your BIOS to reserve an
-		   irq for an ISA card.  PCI and EISA parameters are set
-		   automagically.  Insert card into computer with the power off 
-		   before or after drivers installation.
-
-	Note the hardware address from the Computone ISA cards installed into
-		the system.  These are required for editing ip2.c or editing
-		/etc/modprobe.d/*.conf, or for specification on the modprobe
-		command line.
-
-	Note that the /etc/modules.conf should be used for older (pre-2.6)
-		kernels.
-
-Software -
-
-Module installation:
-
-a) Determine free irq/address to use if any (configure BIOS if need be)
-b) Run "make config" or "make menuconfig" or "make xconfig"
-   Select (m) module for CONFIG_COMPUTONE under character
-   devices.  CONFIG_PCI and CONFIG_MODULES also may need to be set.
-c) Set address on ISA cards then:
-   edit /usr/src/linux/drivers/char/ip2.c if needed 
-	or
-   edit config file in  /etc/modprobe.d/ if needed (module).
-	or both to match this setting.
-d) Run "make modules"
-e) Run "make modules_install"
-f) Run "/sbin/depmod -a"
-g) install driver using `modprobe ip2 <options>` (options listed below)
-h) run ip2mkdev (either the script below or the binary version)
-
-
-Kernel installation:
-
-a) Determine free irq/address to use if any (configure BIOS if need be)
-b) Run "make config" or "make menuconfig" or "make xconfig"
-   Select (y) kernel for CONFIG_COMPUTONE under character
-   devices.  CONFIG_PCI may need to be set if you have PCI bus.
-c) Set address on ISA cards then:
-	   edit /usr/src/linux/drivers/char/ip2.c  
-           (Optional - may be specified on kernel command line now)
-d) Run "make zImage" or whatever target you prefer.
-e) mv /usr/src/linux/arch/x86/boot/zImage to /boot.
-f) Add new config for this kernel into /etc/lilo.conf, run "lilo"
-	or copy to a floppy disk and boot from that floppy disk.
-g) Reboot using this kernel
-h) run ip2mkdev (either the script below or the binary version)
-
-Kernel command line options:
-
-When compiling the driver into the kernel, io and irq may be
-compiled into the driver by editing ip2.c and setting the values for
-io and irq in the appropriate array.  An alternative is to specify
-a command line parameter to the kernel at boot up.
-
-        ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
-
-Note that this order is very different from the specifications for the
-modload parameters which have separate IRQ and IO specifiers.
-
-The io port also selects PCI (1) and EISA (2) boards.
-
-        io=0    No board
-        io=1    PCI board
-        io=2    EISA board
-        else    ISA board io address
-
-You only need to specify the boards which are present.
-
-        Examples:
-
-                2 PCI boards:
-
-                        ip2=1,0,1,0
-
-                1 ISA board at 0x310 irq 5:
-
-                        ip2=0x310,5
-
-This can be added to and "append" option in lilo.conf similar to this:
-
-        append="ip2=1,0,1,0"
-
-
-3. INSTALLATION
-
-Previously, the driver sources were packaged with a set of patch files
-to update the character drivers' makefile and configuration file, and other 
-kernel source files. A build script (ip2build) was included which applies 
-the patches if needed, and build any utilities needed.
-What you receive may be a single patch file in conventional kernel
-patch format build script. That form can also be applied by
-running patch -p1 < ThePatchFile.  Otherwise run ip2build.
- 
-The driver can be installed as a module (recommended) or built into the 
-kernel. This is selected as for other drivers through the `make config`
-command from the root of the Linux source tree. If the driver is built 
-into the kernel you will need to edit the file ip2.c to match the boards 
-you are installing. See that file for instructions. If the driver is 
-installed as a module the configuration can also be specified on the
-modprobe command line as follows:
-
-	modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4
-
-where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11,
-12,15) and addr1-4 are the base addresses for up to four controllers. If 
-the irqs are not specified the driver uses the default in ip2.c (which 
-selects polled mode). If no base addresses are specified the defaults in 
-ip2.c are used. If you are autoloading the driver module with kerneld or
-kmod the base addresses and interrupt number must also be set in ip2.c
-and recompile or just insert and options line in /etc/modprobe.d/*.conf or both.
-The options line is equivalent to the command line and takes precedence over
-what is in ip2.c. 
-
-config sample to put /etc/modprobe.d/*.conf:
-	options ip2 io=1,0x328 irq=1,10
-	alias char-major-71 ip2
-	alias char-major-72 ip2
-	alias char-major-73 ip2
-
-The equivalent in ip2.c:
-
-static int io[IP2_MAX_BOARDS]= { 1, 0x328, 0, 0 };
-static int irq[IP2_MAX_BOARDS] = { 1, 10, -1, -1 }; 
-
-The equivalent for the kernel command line (in lilo.conf):
-
-        append="ip2=1,1,0x328,10"
-
-
-Note:	Both io and irq should be updated to reflect YOUR system.  An "io"
-	address of 1 or 2 indicates a PCI or EISA card in the board table.
-	The PCI or EISA irq will be assigned automatically.
-
-Specifying an invalid or in-use irq will default the driver into
-running in polled mode for that card.  If all irq entries are 0 then
-all cards will operate in polled mode.
-
-If you select the driver as part of the kernel run :
-
-	make zlilo (or whatever you do to create a bootable kernel)
-
-If you selected a module run :
-
-	make modules && make modules_install
-
-The utility ip2mkdev (see 5 and 7 below) creates all the device nodes
-required by the driver.  For a device to be created it must be configured
-in the driver and the board must be installed. Only devices corresponding
-to real IntelliPort II ports are created. With multiple boards and expansion
-boxes this will leave gaps in the sequence of device names. ip2mkdev uses
-Linux tty naming conventions: ttyF0 - ttyF255 for normal devices, and
-cuf0 - cuf255 for callout devices.
-
-
-4. USING THE DRIVERS
-
-As noted above, the driver implements the ports in accordance with Linux
-conventions, and the devices should be interchangeable with the standard
-serial devices. (This is a key point for problem reporting: please make
-sure that what you are trying do works on the ttySx/cuax ports first; then 
-tell us what went wrong with the ip2 ports!)
-
-Higher speeds can be obtained using the setserial utility which remaps 
-38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. 
-Intelliport II installations using the PowerPort expansion module can
-use the custom speed setting to select the highest speeds: 153,600 bps,
-230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for
-custom baud rate configuration is fixed at 921,600 for cards/expansion
-modules with ST654's and 115200 for those with Cirrus CD1400's.  This
-corresponds to the maximum bit rates those chips are capable.  
-For example if the baud base is 921600 and the baud divisor is 18 then
-the custom rate is 921600/18 = 51200 bps.  See the setserial man page for
-complete details. Of course if stty accepts the higher rates now you can
-use that as well as the standard ioctls().
-
-
-5. ip2mkdev and assorted utilities...
-
-Several utilities, including the source for a binary ip2mkdev utility are
-available under .../drivers/char/ip2.  These can be build by changing to
-that directory and typing "make" after the kernel has be built.  If you do
-not wish to compile the binary utilities, the shell script below can be
-cut out and run as "ip2mkdev" to create the necessary device files.  To
-use the ip2mkdev script, you must have procfs enabled and the proc file
-system mounted on /proc.
-
-
-6. NOTES
-
-This is a release version of the driver, but it is impossible to test it
-in all configurations of Linux. If there is any anomalous behaviour that 
-does not match the standard serial port's behaviour please let us know.
-
-
-7. ip2mkdev shell script
-
-Previously, this script was simply attached here.  It is now attached as a
-shar archive to make it easier to extract the script from the documentation.
-To create the ip2mkdev shell script change to a convenient directory (/tmp
-works just fine) and run the following command:
-
-	unshar Documentation/serial/computone.txt
-		(This file)
-
-You should now have a file ip2mkdev in your current working directory with
-permissions set to execute.  Running that script with then create the
-necessary devices for the Computone boards, interfaces, and ports which
-are present on you system at the time it is run.
-
-
-#!/bin/sh
-# This is a shell archive (produced by GNU sharutils 4.2.1).
-# To extract the files from this archive, save it to some FILE, remove
-# everything before the `!/bin/sh' line above, then type `sh FILE'.
-#
-# Made on 2001-10-29 10:32 EST by <mhw@alcove.wittsend.com>.
-# Source directory was `/home2/src/tmp'.
-#
-# Existing files will *not* be overwritten unless `-c' is specified.
-#
-# This shar contains:
-# length mode       name
-# ------ ---------- ------------------------------------------
-#   4251 -rwxr-xr-x ip2mkdev
-#
-save_IFS="${IFS}"
-IFS="${IFS}:"
-gettext_dir=FAILED
-locale_dir=FAILED
-first_param="$1"
-for dir in $PATH
-do
-  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
-     && ($dir/gettext --version >/dev/null 2>&1)
-  then
-    set `$dir/gettext --version 2>&1`
-    if test "$3" = GNU
-    then
-      gettext_dir=$dir
-    fi
-  fi
-  if test "$locale_dir" = FAILED && test -f $dir/shar \
-     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
-  then
-    locale_dir=`$dir/shar --print-text-domain-dir`
-  fi
-done
-IFS="$save_IFS"
-if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
-then
-  echo=echo
-else
-  TEXTDOMAINDIR=$locale_dir
-  export TEXTDOMAINDIR
-  TEXTDOMAIN=sharutils
-  export TEXTDOMAIN
-  echo="$gettext_dir/gettext -s"
-fi
-if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
-  shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
-elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
-  shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
-elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
-  shar_touch='touch -am $3$4$5$6$2 "$8"'
-else
-  shar_touch=:
-  echo
-  $echo 'WARNING: not restoring timestamps.  Consider getting and'
-  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
-  echo
-fi
-rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
-#
-if mkdir _sh17581; then
-  $echo 'x -' 'creating lock directory'
-else
-  $echo 'failed to create lock directory'
-  exit 1
-fi
-# ============= ip2mkdev ==============
-if test -f 'ip2mkdev' && test "$first_param" != -c; then
-  $echo 'x -' SKIPPING 'ip2mkdev' '(file already exists)'
-else
-  $echo 'x -' extracting 'ip2mkdev' '(text)'
-  sed 's/^X//' << 'SHAR_EOF' > 'ip2mkdev' &&
-#!/bin/sh -
-#
-#	ip2mkdev
-#
-#	Make or remove devices as needed for Computone Intelliport drivers
-#
-#	First rule!  If the dev file exists and you need it, don't mess
-#	with it.  That prevents us from screwing up open ttys, ownership
-#	and permissions on a running system!
-#
-#	This script will NOT remove devices that no longer exist if their
-#	board or interface box has been removed.  If you want to get rid
-#	of them, you can manually do an "rm -f /dev/ttyF* /dev/cuaf*"
-#	before running this script.  Running this script will then recreate
-#	all the valid devices.
-#
-#	Michael H. Warfield
-#	/\/\|=mhw=|\/\/
-#	mhw@wittsend.com
-#
-#	Updated 10/29/2000 for version 1.2.13 naming convention
-#		under devfs.	/\/\|=mhw=|\/\/
-#
-#	Updated 03/09/2000 for devfs support in ip2 drivers. /\/\|=mhw=|\/\/
-#
-X
-if test -d /dev/ip2 ; then
-#	This is devfs mode...  We don't do anything except create symlinks
-#	from the real devices to the old names!
-X	cd /dev
-X	echo "Creating symbolic links to devfs devices"
-X	for i in `ls ip2` ; do
-X		if test ! -L ip2$i ; then
-X			# Remove it incase it wasn't a symlink (old device)
-X			rm -f ip2$i
-X			ln -s ip2/$i ip2$i
-X		fi
-X	done
-X	for i in `( cd tts ; ls F* )` ; do
-X		if test ! -L tty$i ; then
-X			# Remove it incase it wasn't a symlink (old device)
-X			rm -f tty$i
-X			ln -s tts/$i tty$i
-X		fi
-X	done
-X	for i in `( cd cua ; ls F* )` ; do
-X		DEVNUMBER=`expr $i : 'F\(.*\)'`
-X		if test ! -L cuf$DEVNUMBER ; then
-X			# Remove it incase it wasn't a symlink (old device)
-X			rm -f cuf$DEVNUMBER
-X			ln -s cua/$i cuf$DEVNUMBER
-X		fi
-X	done
-X	exit 0
-fi
-X
-if test ! -f /proc/tty/drivers
-then
-X	echo "\
-Unable to check driver status.
-Make sure proc file system is mounted."
-X
-X	exit 255
-fi
-X
-if test ! -f /proc/tty/driver/ip2
-then
-X	echo "\
-Unable to locate ip2 proc file.
-Attempting to load driver"
-X
-X	if /sbin/insmod ip2
-X	then
-X		if test ! -f /proc/tty/driver/ip2
-X		then
-X			echo "\
-Unable to locate ip2 proc file after loading driver.
-Driver initialization failure or driver version error.
-"
-X		exit 255
-X		fi
-X	else
-X		echo "Unable to load ip2 driver."
-X		exit 255
-X	fi
-fi
-X
-# Ok...  So we got the driver loaded and we can locate the procfs files.
-# Next we need our major numbers.
-X
-TTYMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/tt/!d' -e 's/.*tt[^ 	]*[ 	]*\([0-9]*\)[ 	]*.*/\1/' < /proc/tty/drivers`
-CUAMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/cu/!d' -e 's/.*cu[^ 	]*[ 	]*\([0-9]*\)[ 	]*.*/\1/' < /proc/tty/drivers`
-BRDMAJOR=`sed -e '/^Driver: /!d' -e 's/.*IMajor=\([0-9]*\)[ 	]*.*/\1/' < /proc/tty/driver/ip2`
-X
-echo "\
-TTYMAJOR = $TTYMAJOR
-CUAMAJOR = $CUAMAJOR
-BRDMAJOR = $BRDMAJOR
-"
-X
-# Ok...  Now we should know our major numbers, if appropriate...
-# Now we need our boards and start the device loops.
-X
-grep '^Board [0-9]:' /proc/tty/driver/ip2 | while read token number type alltherest
-do
-X	# The test for blank "type" will catch the stats lead-in lines
-X	# if they exist in the file
-X	if test "$type" = "vacant" -o "$type" = "Vacant" -o "$type" = ""
-X	then
-X		continue
-X	fi
-X
-X	BOARDNO=`expr "$number" : '\([0-9]\):'`
-X	PORTS=`expr "$alltherest" : '.*ports=\([0-9]*\)' | tr ',' ' '`
-X	MINORS=`expr "$alltherest" : '.*minors=\([0-9,]*\)' | tr ',' ' '`
-X
-X	if test "$BOARDNO" = "" -o "$PORTS" = ""
-X	then
-#	This may be a bug.  We should at least get this much information
-X		echo "Unable to process board line"
-X		continue
-X	fi
-X
-X	if test "$MINORS" = ""
-X	then
-#	Silently skip this one.  This board seems to have no boxes
-X		continue
-X	fi
-X
-X	echo "board $BOARDNO: $type ports = $PORTS; port numbers = $MINORS"
-X
-X	if test "$BRDMAJOR" != ""
-X	then
-X		BRDMINOR=`expr $BOARDNO \* 4`
-X		STSMINOR=`expr $BRDMINOR + 1`
-X		if test ! -c /dev/ip2ipl$BOARDNO ; then
-X			mknod /dev/ip2ipl$BOARDNO c $BRDMAJOR $BRDMINOR
-X		fi
-X		if test ! -c /dev/ip2stat$BOARDNO ; then
-X			mknod /dev/ip2stat$BOARDNO c $BRDMAJOR $STSMINOR
-X		fi
-X	fi
-X
-X	if test "$TTYMAJOR" != ""
-X	then
-X		PORTNO=$BOARDBASE
-X
-X		for PORTNO in $MINORS
-X		do
-X			if test ! -c /dev/ttyF$PORTNO ; then
-X				# We got the hardware but no device - make it
-X				mknod /dev/ttyF$PORTNO c $TTYMAJOR $PORTNO
-X			fi	
-X		done
-X	fi
-X
-X	if test "$CUAMAJOR" != ""
-X	then
-X		PORTNO=$BOARDBASE
-X
-X		for PORTNO in $MINORS
-X		do
-X			if test ! -c /dev/cuf$PORTNO ; then
-X				# We got the hardware but no device - make it
-X				mknod /dev/cuf$PORTNO c $CUAMAJOR $PORTNO
-X			fi	
-X		done
-X	fi
-done
-X
-Xexit 0
-SHAR_EOF
-  (set 20 01 10 29 10 32 01 'ip2mkdev'; eval "$shar_touch") &&
-  chmod 0755 'ip2mkdev' ||
-  $echo 'restore of' 'ip2mkdev' 'failed'
-  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
-  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
-    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
-    || $echo 'ip2mkdev:' 'MD5 check failed'
-cb5717134509f38bad9fde6b1f79b4a4  ip2mkdev
-SHAR_EOF
-  else
-    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'ip2mkdev'`"
-    test 4251 -eq "$shar_count" ||
-    $echo 'ip2mkdev:' 'original size' '4251,' 'current size' "$shar_count!"
-  fi
-fi
-rm -fr _sh17581
-exit 0

+ 1 - 0
arch/alpha/kernel/srmcons.c

@@ -223,6 +223,7 @@ srmcons_init(void)
 		driver->subtype = SYSTEM_TYPE_SYSCONS;
 		driver->init_termios = tty_std_termios;
 		tty_set_operations(driver, &srmcons_ops);
+		tty_port_link_device(&srmcons_singleton.port, driver, 0);
 		err = tty_register_driver(driver);
 		if (err) {
 			put_tty_driver(driver);

+ 12 - 6
arch/arm/mach-omap2/serial.c

@@ -81,8 +81,9 @@ static struct omap_uart_port_info omap_serial_default_info[] __initdata = {
 };
 
 #ifdef CONFIG_PM
-static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
+static void omap_uart_enable_wakeup(struct device *dev, bool enable)
 {
+	struct platform_device *pdev = to_platform_device(dev);
 	struct omap_device *od = to_omap_device(pdev);
 
 	if (!od)
@@ -99,15 +100,17 @@ static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
  * in Smartidle Mode When Configured for DMA Operations.
  * WA: configure uart in force idle mode.
  */
-static void omap_uart_set_noidle(struct platform_device *pdev)
+static void omap_uart_set_noidle(struct device *dev)
 {
+	struct platform_device *pdev = to_platform_device(dev);
 	struct omap_device *od = to_omap_device(pdev);
 
 	omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
 }
 
-static void omap_uart_set_smartidle(struct platform_device *pdev)
+static void omap_uart_set_smartidle(struct device *dev)
 {
+	struct platform_device *pdev = to_platform_device(dev);
 	struct omap_device *od = to_omap_device(pdev);
 	u8 idlemode;
 
@@ -120,10 +123,10 @@ static void omap_uart_set_smartidle(struct platform_device *pdev)
 }
 
 #else
-static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
+static void omap_uart_enable_wakeup(struct device *dev, bool enable)
 {}
-static void omap_uart_set_noidle(struct platform_device *pdev) {}
-static void omap_uart_set_smartidle(struct platform_device *pdev) {}
+static void omap_uart_set_noidle(struct device *dev) {}
+static void omap_uart_set_smartidle(struct device *dev) {}
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_OMAP_MUX
@@ -304,6 +307,9 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
 	omap_up.dma_rx_timeout = info->dma_rx_timeout;
 	omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
 	omap_up.autosuspend_timeout = info->autosuspend_timeout;
+	omap_up.DTR_gpio = info->DTR_gpio;
+	omap_up.DTR_inverted = info->DTR_inverted;
+	omap_up.DTR_present = info->DTR_present;
 
 	pdata = &omap_up;
 	pdata_size = sizeof(struct omap_uart_port_info);

+ 0 - 21
arch/arm/mach-ux500/board-mop500.c

@@ -524,33 +524,12 @@ static struct stedma40_chan_cfg uart2_dma_cfg_tx = {
 };
 #endif
 
-#define PRCC_K_SOFTRST_SET      0x18
-#define PRCC_K_SOFTRST_CLEAR    0x1C
-static void ux500_uart0_reset(void)
-{
-	void __iomem *prcc_rst_set, *prcc_rst_clr;
-
-	prcc_rst_set = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
-			PRCC_K_SOFTRST_SET);
-	prcc_rst_clr = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
-			PRCC_K_SOFTRST_CLEAR);
-
-	/* Activate soft reset PRCC_K_SOFTRST_CLEAR */
-	writel((readl(prcc_rst_clr) | 0x1), prcc_rst_clr);
-	udelay(1);
-
-	/* Release soft reset PRCC_K_SOFTRST_SET */
-	writel((readl(prcc_rst_set) | 0x1), prcc_rst_set);
-	udelay(1);
-}
-
 static struct amba_pl011_data uart0_plat = {
 #ifdef CONFIG_STE_DMA40
 	.dma_filter = stedma40_filter,
 	.dma_rx_param = &uart0_dma_cfg_rx,
 	.dma_tx_param = &uart0_dma_cfg_tx,
 #endif
-	.reset = ux500_uart0_reset,
 };
 
 static struct amba_pl011_data uart1_plat = {

+ 9 - 41
arch/arm/plat-omap/include/plat/omap-serial.h

@@ -18,7 +18,7 @@
 #define __OMAP_SERIAL_H__
 
 #include <linux/serial_core.h>
-#include <linux/platform_device.h>
+#include <linux/device.h>
 #include <linux/pm_qos.h>
 
 #include <plat/mux.h>
@@ -42,10 +42,10 @@
 #define OMAP_UART_WER_MOD_WKUP	0X7F
 
 /* Enable XON/XOFF flow control on output */
-#define OMAP_UART_SW_TX		0x04
+#define OMAP_UART_SW_TX		0x8
 
 /* Enable XON/XOFF flow control on input */
-#define OMAP_UART_SW_RX		0x04
+#define OMAP_UART_SW_RX		0x2
 
 #define OMAP_UART_SYSC_RESET	0X07
 #define OMAP_UART_TCR_TRIG	0X0F
@@ -69,11 +69,14 @@ struct omap_uart_port_info {
 	unsigned int		dma_rx_timeout;
 	unsigned int		autosuspend_timeout;
 	unsigned int		dma_rx_poll_rate;
+	int			DTR_gpio;
+	int			DTR_inverted;
+	int			DTR_present;
 
 	int (*get_context_loss_count)(struct device *);
-	void (*set_forceidle)(struct platform_device *);
-	void (*set_noidle)(struct platform_device *);
-	void (*enable_wakeup)(struct platform_device *, bool);
+	void (*set_forceidle)(struct device *);
+	void (*set_noidle)(struct device *);
+	void (*enable_wakeup)(struct device *, bool);
 };
 
 struct uart_omap_dma {
@@ -102,39 +105,4 @@ struct uart_omap_dma {
 	unsigned int		rx_timeout;
 };
 
-struct uart_omap_port {
-	struct uart_port	port;
-	struct uart_omap_dma	uart_dma;
-	struct platform_device	*pdev;
-
-	unsigned char		ier;
-	unsigned char		lcr;
-	unsigned char		mcr;
-	unsigned char		fcr;
-	unsigned char		efr;
-	unsigned char		dll;
-	unsigned char		dlh;
-	unsigned char		mdr1;
-	unsigned char		scr;
-
-	int			use_dma;
-	/*
-	 * Some bits in registers are cleared on a read, so they must
-	 * be saved whenever the register is read but the bits will not
-	 * be immediately processed.
-	 */
-	unsigned int		lsr_break_flag;
-	unsigned char		msr_saved_flags;
-	char			name[20];
-	unsigned long		port_activity;
-	u32			context_loss_cnt;
-	u32			errata;
-	u8			wakeups_enabled;
-
-	struct pm_qos_request	pm_qos_request;
-	u32			latency;
-	u32			calc_latency;
-	struct work_struct	qos_work;
-};
-
 #endif /* __OMAP_SERIAL_H__ */

+ 2 - 1
arch/ia64/hp/sim/simserial.c

@@ -338,7 +338,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 	}
 }
@@ -545,6 +545,7 @@ static int __init simrs_init(void)
 	/* the port is imaginary */
 	printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq);
 
+	tty_port_link_device(&state->port, hp_simserial_driver, 0);
 	retval = tty_register_driver(hp_simserial_driver);
 	if (retval) {
 		printk(KERN_ERR "Couldn't register simserial driver\n");

+ 4 - 0
arch/m68k/emu/nfcon.c

@@ -19,6 +19,7 @@
 #include <asm/natfeat.h>
 
 static int stderr_id;
+static struct tty_port nfcon_tty_port;
 static struct tty_driver *nfcon_tty_driver;
 
 static void nfputs(const char *str, unsigned int count)
@@ -119,6 +120,8 @@ static int __init nfcon_init(void)
 {
 	int res;
 
+	tty_port_init(&nfcon_tty_port);
+
 	stderr_id = nf_get_id("NF_STDERR");
 	if (!stderr_id)
 		return -ENODEV;
@@ -135,6 +138,7 @@ static int __init nfcon_init(void)
 	nfcon_tty_driver->flags = TTY_DRIVER_REAL_RAW;
 
 	tty_set_operations(nfcon_tty_driver, &nfcon_tty_ops);
+	tty_port_link_device(&nfcon_tty_port, nfcon_tty_driver, 0);
 	res = tty_register_driver(nfcon_tty_driver);
 	if (res) {
 		pr_err("failed to register nfcon tty driver\n");

+ 15 - 15
arch/mips/cavium-octeon/serial.c

@@ -47,40 +47,40 @@ static int __devinit octeon_serial_probe(struct platform_device *pdev)
 {
 	int irq, res;
 	struct resource *res_mem;
-	struct uart_port port;
+	struct uart_8250_port up;
 
 	/* All adaptors have an irq.  */
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
 
-	memset(&port, 0, sizeof(port));
+	memset(&up, 0, sizeof(up));
 
-	port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
-	port.type = PORT_OCTEON;
-	port.iotype = UPIO_MEM;
-	port.regshift = 3;
-	port.dev = &pdev->dev;
+	up.port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+	up.port.type = PORT_OCTEON;
+	up.port.iotype = UPIO_MEM;
+	up.port.regshift = 3;
+	up.port.dev = &pdev->dev;
 
 	if (octeon_is_simulation())
 		/* Make simulator output fast*/
-		port.uartclk = 115200 * 16;
+		up.port.uartclk = 115200 * 16;
 	else
-		port.uartclk = octeon_get_io_clock_rate();
+		up.port.uartclk = octeon_get_io_clock_rate();
 
-	port.serial_in = octeon_serial_in;
-	port.serial_out = octeon_serial_out;
-	port.irq = irq;
+	up.port.serial_in = octeon_serial_in;
+	up.port.serial_out = octeon_serial_out;
+	up.port.irq = irq;
 
 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res_mem == NULL) {
 		dev_err(&pdev->dev, "found no memory resource\n");
 		return -ENXIO;
 	}
-	port.mapbase = res_mem->start;
-	port.membase = ioremap(res_mem->start, resource_size(res_mem));
+	up.port.mapbase = res_mem->start;
+	up.port.membase = ioremap(res_mem->start, resource_size(res_mem));
 
-	res = serial8250_register_port(&port);
+	res = serial8250_register_8250_port(&up);
 
 	return res >= 0 ? 0 : res;
 }

+ 32 - 0
arch/mips/sni/a20r.c

@@ -133,6 +133,38 @@ static struct platform_device sc26xx_pdev = {
 	}
 };
 
+#warning "Please try migrate to use new driver SCCNXP and report the status" \
+	 "in the linux-serial mailing list."
+
+/* The code bellow is a replacement of SC26XX to SCCNXP */
+#if 0
+#include <linux/platform_data/sccnxp.h>
+
+static struct sccnxp_pdata sccnxp_data = {
+	.reg_shift	= 2,
+	.frequency	= 3686400,
+	.mctrl_cfg[0]	= MCTRL_SIG(DTR_OP, LINE_OP7) |
+			  MCTRL_SIG(RTS_OP, LINE_OP3) |
+			  MCTRL_SIG(DSR_IP, LINE_IP5) |
+			  MCTRL_SIG(DCD_IP, LINE_IP6),
+	.mctrl_cfg[1]	= MCTRL_SIG(DTR_OP, LINE_OP2) |
+			  MCTRL_SIG(RTS_OP, LINE_OP1) |
+			  MCTRL_SIG(DSR_IP, LINE_IP0) |
+			  MCTRL_SIG(CTS_IP, LINE_IP1) |
+			  MCTRL_SIG(DCD_IP, LINE_IP2) |
+			  MCTRL_SIG(RNG_IP, LINE_IP3),
+};
+
+static struct platform_device sc2681_pdev = {
+	.name		= "sc2681",
+	.resource	= sc2xxx_rsrc,
+	.num_resources	= ARRAY_SIZE(sc2xxx_rsrc),
+	.dev	= {
+		.platform_data	= &sccnxp_data,
+	},
+};
+#endif
+
 static u32 a20r_ack_hwint(void)
 {
 	u32 status = read_c0_status();

+ 1 - 0
arch/parisc/kernel/pdc_cons.c

@@ -202,6 +202,7 @@ static int __init pdc_console_tty_driver_init(void)
 	pdc_console_tty_driver->flags = TTY_DRIVER_REAL_RAW |
 		TTY_DRIVER_RESET_TERMIOS;
 	tty_set_operations(pdc_console_tty_driver, &pdc_console_tty_ops);
+	tty_port_link_device(&tty_port, pdc_console_tty_driver, 0);
 
 	err = tty_register_driver(pdc_console_tty_driver);
 	if (err) {

+ 2 - 1
arch/um/drivers/line.c

@@ -409,7 +409,8 @@ int setup_one_line(struct line *lines, int n, char *init,
 		line->valid = 1;
 		err = parse_chan_pair(new, line, n, opts, error_out);
 		if (!err) {
-			struct device *d = tty_register_device(driver, n, NULL);
+			struct device *d = tty_port_register_device(&line->port,
+					driver, n, NULL);
 			if (IS_ERR(d)) {
 				*error_out = "Failed to register device";
 				err = PTR_ERR(d);

+ 1 - 0
arch/xtensa/platforms/iss/console.c

@@ -223,6 +223,7 @@ int __init rs_init(void)
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 
 	tty_set_operations(serial_driver, &serial_ops);
+	tty_port_link_device(&serial_port, serial_driver, 0);
 
 	if (tty_register_driver(serial_driver))
 		panic("Couldn't register serial driver\n");

+ 1 - 1
drivers/bluetooth/hci_ath.c

@@ -58,7 +58,7 @@ static int ath_wakeup_ar3k(struct tty_struct *tty)
 		return status;
 
 	/* Disable Automatic RTSCTS */
-	memcpy(&ktermios, tty->termios, sizeof(ktermios));
+	ktermios = tty->termios;
 	ktermios.c_cflag &= ~CRTSCTS;
 	tty_set_termios(tty, &ktermios);
 

+ 8 - 8
drivers/char/mwave/mwavedd.c

@@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf,
 
 static int register_serial_portandirq(unsigned int port, int irq)
 {
-	struct uart_port uart;
+	struct uart_8250_port uart;
 	
 	switch ( port ) {
 		case 0x3f8:
@@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq)
 	} /* switch */
 	/* irq is okay */
 
-	memset(&uart, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 	
-	uart.uartclk =  1843200;
-	uart.iobase = port;
-	uart.irq = irq;
-	uart.iotype = UPIO_PORT;
-	uart.flags =  UPF_SHARE_IRQ;
-	return serial8250_register_port(&uart);
+	uart.port.uartclk =  1843200;
+	uart.port.iobase = port;
+	uart.port.irq = irq;
+	uart.port.iotype = UPIO_PORT;
+	uart.port.flags =  UPF_SHARE_IRQ;
+	return serial8250_register_8250_port(&uart);
 }
 
 

+ 60 - 71
drivers/char/pcmcia/synclink_cs.c

@@ -1058,7 +1058,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
 	wake_up_interruptible(&info->status_event_wait_q);
 	wake_up_interruptible(&info->event_wait_q);
 
-	if (tty && (info->port.flags & ASYNC_CTS_FLOW)) {
+	if (tty && tty_port_cts_enabled(&info->port)) {
 		if (tty->hw_stopped) {
 			if (info->serial_signals & SerialSignal_CTS) {
 				if (debug_level >= DEBUG_LEVEL_ISR)
@@ -1350,7 +1350,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
 	/* TODO:disable interrupts instead of reset to preserve signal states */
 	reset_device(info);
 
- 	if (!tty || tty->termios->c_cflag & HUPCL) {
+ 	if (!tty || tty->termios.c_cflag & HUPCL) {
  		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
 		set_signals(info);
 	}
@@ -1391,7 +1391,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty)
 	port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI);
 	get_signals(info);
 
-	if (info->netcount || (tty && (tty->termios->c_cflag & CREAD)))
+	if (info->netcount || (tty && (tty->termios.c_cflag & CREAD)))
 		rx_start(info);
 
 	spin_unlock_irqrestore(&info->lock,flags);
@@ -1404,14 +1404,14 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
 	unsigned cflag;
 	int bits_per_char;
 
-	if (!tty || !tty->termios)
+	if (!tty)
 		return;
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgslpc_change_params(%s)\n",
 			 __FILE__,__LINE__, info->device_name );
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 	/* if B0 rate (hangup) specified then negate DTR and RTS */
 	/* otherwise assert DTR and RTS */
@@ -1734,7 +1734,7 @@ static void mgslpc_throttle(struct tty_struct * tty)
 	if (I_IXOFF(tty))
 		mgslpc_send_xchar(tty, STOP_CHAR(tty));
 
- 	if (tty->termios->c_cflag & CRTSCTS) {
+ 	if (tty->termios.c_cflag & CRTSCTS) {
 		spin_lock_irqsave(&info->lock,flags);
 		info->serial_signals &= ~SerialSignal_RTS;
 	 	set_signals(info);
@@ -1763,7 +1763,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty)
 			mgslpc_send_xchar(tty, START_CHAR(tty));
 	}
 
- 	if (tty->termios->c_cflag & CRTSCTS) {
+ 	if (tty->termios.c_cflag & CRTSCTS) {
 		spin_lock_irqsave(&info->lock,flags);
 		info->serial_signals |= SerialSignal_RTS;
 	 	set_signals(info);
@@ -2299,8 +2299,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 			tty->driver->name );
 
 	/* just return if nothing has changed */
-	if ((tty->termios->c_cflag == old_termios->c_cflag)
-	    && (RELEVANT_IFLAG(tty->termios->c_iflag)
+	if ((tty->termios.c_cflag == old_termios->c_cflag)
+	    && (RELEVANT_IFLAG(tty->termios.c_iflag)
 		== RELEVANT_IFLAG(old_termios->c_iflag)))
 	  return;
 
@@ -2308,7 +2308,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 
 	/* Handle transition to B0 status */
 	if (old_termios->c_cflag & CBAUD &&
-	    !(tty->termios->c_cflag & CBAUD)) {
+	    !(tty->termios.c_cflag & CBAUD)) {
 		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
 		spin_lock_irqsave(&info->lock,flags);
 	 	set_signals(info);
@@ -2317,9 +2317,9 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 
 	/* Handle transition away from B0 status */
 	if (!(old_termios->c_cflag & CBAUD) &&
-	    tty->termios->c_cflag & CBAUD) {
+	    tty->termios.c_cflag & CBAUD) {
 		info->serial_signals |= SerialSignal_DTR;
- 		if (!(tty->termios->c_cflag & CRTSCTS) ||
+ 		if (!(tty->termios.c_cflag & CRTSCTS) ||
  		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 			info->serial_signals |= SerialSignal_RTS;
  		}
@@ -2330,7 +2330,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 
 	/* Handle turning off CRTSCTS */
 	if (old_termios->c_cflag & CRTSCTS &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tx_release(tty);
 	}
@@ -2737,6 +2737,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
 #if SYNCLINK_GENERIC_HDLC
 	hdlcdev_init(info);
 #endif
+	tty_port_register_device(&info->port, serial_driver, info->line,
+			&info->p_dev->dev);
 }
 
 static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
@@ -2750,6 +2752,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
 				last->next_device = info->next_device;
 			else
 				mgslpc_device_list = info->next_device;
+			tty_unregister_device(serial_driver, info->line);
 #if SYNCLINK_GENERIC_HDLC
 			hdlcdev_exit(info);
 #endif
@@ -2804,77 +2807,63 @@ static const struct tty_operations mgslpc_ops = {
 	.proc_fops = &mgslpc_proc_fops,
 };
 
-static void synclink_cs_cleanup(void)
+static int __init synclink_cs_init(void)
 {
 	int rc;
 
-	while(mgslpc_device_list)
-		mgslpc_remove_device(mgslpc_device_list);
-
-	if (serial_driver) {
-		if ((rc = tty_unregister_driver(serial_driver)))
-			printk("%s(%d) failed to unregister tty driver err=%d\n",
-			       __FILE__,__LINE__,rc);
-		put_tty_driver(serial_driver);
+	if (break_on_load) {
+		mgslpc_get_text_ptr();
+		BREAKPOINT();
 	}
 
-	pcmcia_unregister_driver(&mgslpc_driver);
-}
-
-static int __init synclink_cs_init(void)
-{
-    int rc;
-
-    if (break_on_load) {
-	    mgslpc_get_text_ptr();
-	    BREAKPOINT();
-    }
-
-    if ((rc = pcmcia_register_driver(&mgslpc_driver)) < 0)
-	    return rc;
-
-    serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT);
-    if (!serial_driver) {
-	    rc = -ENOMEM;
-	    goto error;
-    }
+	serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT,
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_DEV);
+	if (IS_ERR(serial_driver)) {
+		rc = PTR_ERR(serial_driver);
+		goto err;
+	}
 
-    /* Initialize the tty_driver structure */
-
-    serial_driver->driver_name = "synclink_cs";
-    serial_driver->name = "ttySLP";
-    serial_driver->major = ttymajor;
-    serial_driver->minor_start = 64;
-    serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-    serial_driver->subtype = SERIAL_TYPE_NORMAL;
-    serial_driver->init_termios = tty_std_termios;
-    serial_driver->init_termios.c_cflag =
-	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-    serial_driver->flags = TTY_DRIVER_REAL_RAW;
-    tty_set_operations(serial_driver, &mgslpc_ops);
-
-    if ((rc = tty_register_driver(serial_driver)) < 0) {
-	    printk("%s(%d):Couldn't register serial driver\n",
-		   __FILE__,__LINE__);
-	    put_tty_driver(serial_driver);
-	    serial_driver = NULL;
-	    goto error;
-    }
+	/* Initialize the tty_driver structure */
+	serial_driver->driver_name = "synclink_cs";
+	serial_driver->name = "ttySLP";
+	serial_driver->major = ttymajor;
+	serial_driver->minor_start = 64;
+	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver->subtype = SERIAL_TYPE_NORMAL;
+	serial_driver->init_termios = tty_std_termios;
+	serial_driver->init_termios.c_cflag =
+	B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	tty_set_operations(serial_driver, &mgslpc_ops);
+
+	rc = tty_register_driver(serial_driver);
+	if (rc < 0) {
+		printk(KERN_ERR "%s(%d):Couldn't register serial driver\n",
+				__FILE__, __LINE__);
+		goto err_put_tty;
+	}
 
-    printk("%s %s, tty major#%d\n",
-	   driver_name, driver_version,
-	   serial_driver->major);
+	rc = pcmcia_register_driver(&mgslpc_driver);
+	if (rc < 0)
+		goto err_unreg_tty;
 
-    return 0;
+	printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version,
+			serial_driver->major);
 
-error:
-    synclink_cs_cleanup();
-    return rc;
+	return 0;
+err_unreg_tty:
+	tty_unregister_driver(serial_driver);
+err_put_tty:
+	put_tty_driver(serial_driver);
+err:
+	return rc;
 }
 
 static void __exit synclink_cs_exit(void)
 {
-	synclink_cs_cleanup();
+	pcmcia_unregister_driver(&mgslpc_driver);
+	tty_unregister_driver(serial_driver);
+	put_tty_driver(serial_driver);
 }
 
 module_init(synclink_cs_init);

+ 13 - 20
drivers/char/ttyprintk.c

@@ -67,7 +67,7 @@ static int tpk_printk(const unsigned char *buf, int count)
 				tmp[tpk_curr + 1] = '\0';
 				printk(KERN_INFO "%s%s", tpk_tag, tmp);
 				tpk_curr = 0;
-				if (buf[i + 1] == '\n')
+				if ((i + 1) < count && buf[i + 1] == '\n')
 					i++;
 				break;
 			case '\n':
@@ -178,11 +178,17 @@ static struct tty_driver *ttyprintk_driver;
 static int __init ttyprintk_init(void)
 {
 	int ret = -ENOMEM;
-	void *rp;
 
-	ttyprintk_driver = alloc_tty_driver(1);
-	if (!ttyprintk_driver)
-		return ret;
+	tty_port_init(&tpk_port.port);
+	tpk_port.port.ops = &null_ops;
+	mutex_init(&tpk_port.port_write_mutex);
+
+	ttyprintk_driver = tty_alloc_driver(1,
+			TTY_DRIVER_RESET_TERMIOS |
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_UNNUMBERED_NODE);
+	if (IS_ERR(ttyprintk_driver))
+		return PTR_ERR(ttyprintk_driver);
 
 	ttyprintk_driver->driver_name = "ttyprintk";
 	ttyprintk_driver->name = "ttyprintk";
@@ -191,9 +197,8 @@ static int __init ttyprintk_init(void)
 	ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
 	ttyprintk_driver->init_termios = tty_std_termios;
 	ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
-	ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS |
-		TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 	tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
+	tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
 
 	ret = tty_register_driver(ttyprintk_driver);
 	if (ret < 0) {
@@ -201,22 +206,10 @@ static int __init ttyprintk_init(void)
 		goto error;
 	}
 
-	/* create our unnumbered device */
-	rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL,
-				ttyprintk_driver->name);
-	if (IS_ERR(rp)) {
-		printk(KERN_ERR "Couldn't create ttyprintk device\n");
-		ret = PTR_ERR(rp);
-		goto error;
-	}
-
-	tty_port_init(&tpk_port.port);
-	tpk_port.port.ops = &null_ops;
-	mutex_init(&tpk_port.port_write_mutex);
-
 	return 0;
 
 error:
+	tty_unregister_driver(ttyprintk_driver);
 	put_tty_driver(ttyprintk_driver);
 	ttyprintk_driver = NULL;
 	return ret;

+ 2 - 1
drivers/isdn/capi/capi.c

@@ -234,7 +234,8 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
 
 	mp->minor = minor;
 
-	dev = tty_register_device(capinc_tty_driver, minor, NULL);
+	dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor,
+			NULL);
 	if (IS_ERR(dev))
 		goto err_out2;
 

+ 4 - 3
drivers/isdn/gigaset/interface.c

@@ -446,8 +446,8 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 		goto out;
 	}
 
-	iflag = tty->termios->c_iflag;
-	cflag = tty->termios->c_cflag;
+	iflag = tty->termios.c_iflag;
+	cflag = tty->termios.c_cflag;
 	old_cflag = old ? old->c_cflag : cflag;
 	gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 		cs->minor_index, iflag, cflag, old_cflag);
@@ -524,7 +524,8 @@ void gigaset_if_init(struct cardstate *cs)
 	tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
 
 	mutex_lock(&cs->mutex);
-	cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL);
+	cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
+			cs->minor_index, NULL);
 
 	if (!IS_ERR(cs->tty_dev))
 		dev_set_drvdata(cs->tty_dev, cs);

+ 24 - 17
drivers/isdn/i4l/isdn_tty.c

@@ -1009,15 +1009,15 @@ isdn_tty_change_speed(modem_info *info)
 		quot;
 	int i;
 
-	if (!port->tty || !port->tty->termios)
+	if (!port->tty)
 		return;
-	cflag = port->tty->termios->c_cflag;
+	cflag = port->tty->termios.c_cflag;
 
 	quot = i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
 		if (i < 1 || i > 2)
-			port->tty->termios->c_cflag &= ~CBAUDEX;
+			port->tty->termios.c_cflag &= ~CBAUDEX;
 		else
 			i += 15;
 	}
@@ -1097,7 +1097,7 @@ isdn_tty_shutdown(modem_info *info)
 #endif
 	isdn_unlock_drivers();
 	info->msr &= ~UART_MSR_RI;
-	if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) {
+	if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) {
 		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
 		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 			isdn_tty_modem_reset_regs(info, 0);
@@ -1469,13 +1469,13 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	if (!old_termios)
 		isdn_tty_change_speed(info);
 	else {
-		if (tty->termios->c_cflag == old_termios->c_cflag &&
-		    tty->termios->c_ispeed == old_termios->c_ispeed &&
-		    tty->termios->c_ospeed == old_termios->c_ospeed)
+		if (tty->termios.c_cflag == old_termios->c_cflag &&
+		    tty->termios.c_ispeed == old_termios->c_ispeed &&
+		    tty->termios.c_ospeed == old_termios->c_ospeed)
 			return;
 		isdn_tty_change_speed(info);
 		if ((old_termios->c_cflag & CRTSCTS) &&
-		    !(tty->termios->c_cflag & CRTSCTS))
+		    !(tty->termios.c_cflag & CRTSCTS))
 			tty->hw_stopped = 0;
 	}
 }
@@ -1486,6 +1486,18 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
  * ------------------------------------------------------------
  */
 
+static int isdn_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	modem_info *info = &dev->mdm.info[tty->index];
+
+	if (isdn_tty_paranoia_check(info, tty->name, __func__))
+		return -ENODEV;
+
+	tty->driver_data = info;
+
+	return tty_port_install(&info->port, driver, tty);
+}
+
 /*
  * This routine is called whenever a serial port is opened.  It
  * enables interrupts for a serial port, linking in its async structure into
@@ -1495,22 +1507,16 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 static int
 isdn_tty_open(struct tty_struct *tty, struct file *filp)
 {
-	struct tty_port *port;
-	modem_info *info;
+	modem_info *info = tty->driver_data;
+	struct tty_port *port = &info->port;
 	int retval;
 
-	info = &dev->mdm.info[tty->index];
-	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open"))
-		return -ENODEV;
-	port = &info->port;
 #ifdef ISDN_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
 	       port->count);
 #endif
 	port->count++;
-	tty->driver_data = info;
 	port->tty = tty;
-	tty->port = port;
 	/*
 	 * Start up serial port
 	 */
@@ -1738,6 +1744,7 @@ modem_write_profile(atemu *m)
 }
 
 static const struct tty_operations modem_ops = {
+	.install = isdn_tty_install,
 	.open = isdn_tty_open,
 	.close = isdn_tty_close,
 	.write = isdn_tty_write,
@@ -1782,7 +1789,7 @@ isdn_tty_modem_init(void)
 	m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
 	m->tty_modem->init_termios = tty_std_termios;
 	m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	m->tty_modem->flags = TTY_DRIVER_REAL_RAW;
 	m->tty_modem->driver_name = "isdn_tty";
 	tty_set_operations(m->tty_modem, &modem_ops);
 	retval = tty_register_driver(m->tty_modem);

+ 8 - 8
drivers/misc/ibmasm/uart.c

@@ -33,7 +33,7 @@
 
 void ibmasm_register_uart(struct service_processor *sp)
 {
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	void __iomem *iomem_base;
 
 	iomem_base = sp->base_address + SCOUT_COM_B_BASE;
@@ -47,14 +47,14 @@ void ibmasm_register_uart(struct service_processor *sp)
 		return;
 	}
 
-	memset(&uport, 0, sizeof(struct uart_port));
-	uport.irq	= sp->irq;
-	uport.uartclk	= 3686400;
-	uport.flags	= UPF_SHARE_IRQ;
-	uport.iotype	= UPIO_MEM;
-	uport.membase	= iomem_base;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.irq		= sp->irq;
+	uart.port.uartclk	= 3686400;
+	uart.port.flags		= UPF_SHARE_IRQ;
+	uart.port.iotype	= UPIO_MEM;
+	uart.port.membase	= iomem_base;
 
-	sp->serial_line = serial8250_register_port(&uport);
+	sp->serial_line = serial8250_register_8250_port(&uart);
 	if (sp->serial_line < 0) {
 		dev_err(sp->dev, "Failed to register serial port\n");
 		return;

+ 59 - 69
drivers/misc/pti.c

@@ -60,7 +60,7 @@ struct pti_tty {
 };
 
 struct pti_dev {
-	struct tty_port port;
+	struct tty_port port[PTITTY_MINOR_NUM];
 	unsigned long pti_addr;
 	unsigned long aperture_base;
 	void __iomem *pti_ioaddr;
@@ -76,7 +76,7 @@ struct pti_dev {
  */
 static DEFINE_MUTEX(alloclock);
 
-static struct pci_device_id pci_ids[] __devinitconst = {
+static const struct pci_device_id pci_ids[] __devinitconst = {
 		{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)},
 		{0}
 };
@@ -393,25 +393,6 @@ void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count)
 }
 EXPORT_SYMBOL_GPL(pti_writedata);
 
-/**
- * pti_pci_remove()- Driver exit method to remove PTI from
- *		   PCI bus.
- * @pdev: variable containing pci info of PTI.
- */
-static void __devexit pti_pci_remove(struct pci_dev *pdev)
-{
-	struct pti_dev *drv_data;
-
-	drv_data = pci_get_drvdata(pdev);
-	if (drv_data != NULL) {
-		pci_iounmap(pdev, drv_data->pti_ioaddr);
-		pci_set_drvdata(pdev, NULL);
-		kfree(drv_data);
-		pci_release_region(pdev, 1);
-		pci_disable_device(pdev);
-	}
-}
-
 /*
  * for the tty_driver_*() basic function descriptions, see tty_driver.h.
  * Specific header comments made for PTI-related specifics.
@@ -446,7 +427,7 @@ static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp)
 	 * also removes a locking requirement for the actual write
 	 * procedure.
 	 */
-	return tty_port_open(&drv_data->port, tty, filp);
+	return tty_port_open(tty->port, tty, filp);
 }
 
 /**
@@ -462,7 +443,7 @@ static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp)
  */
 static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp)
 {
-	tty_port_close(&drv_data->port, tty, filp);
+	tty_port_close(tty->port, tty, filp);
 }
 
 /**
@@ -818,6 +799,7 @@ static const struct tty_port_operations tty_port_ops = {
 static int __devinit pti_pci_probe(struct pci_dev *pdev,
 		const struct pci_device_id *ent)
 {
+	unsigned int a;
 	int retval = -EINVAL;
 	int pci_bar = 1;
 
@@ -830,7 +812,7 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
 			__func__, __LINE__);
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
-		return retval;
+		goto err;
 	}
 
 	retval = pci_enable_device(pdev);
@@ -838,17 +820,16 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
 		dev_err(&pdev->dev,
 			"%s: pci_enable_device() returned error %d\n",
 			__func__, retval);
-		return retval;
+		goto err_unreg_misc;
 	}
 
 	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
-
 	if (drv_data == NULL) {
 		retval = -ENOMEM;
 		dev_err(&pdev->dev,
 			"%s(%d): kmalloc() returned NULL memory.\n",
 			__func__, __LINE__);
-		return retval;
+		goto err_disable_pci;
 	}
 	drv_data->pti_addr = pci_resource_start(pdev, pci_bar);
 
@@ -857,33 +838,65 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
 		dev_err(&pdev->dev,
 			"%s(%d): pci_request_region() returned error %d\n",
 			__func__, __LINE__, retval);
-		kfree(drv_data);
-		return retval;
+		goto err_free_dd;
 	}
 	drv_data->aperture_base = drv_data->pti_addr+APERTURE_14;
 	drv_data->pti_ioaddr =
 		ioremap_nocache((u32)drv_data->aperture_base,
 		APERTURE_LEN);
 	if (!drv_data->pti_ioaddr) {
-		pci_release_region(pdev, pci_bar);
 		retval = -ENOMEM;
-		kfree(drv_data);
-		return retval;
+		goto err_rel_reg;
 	}
 
 	pci_set_drvdata(pdev, drv_data);
 
-	tty_port_init(&drv_data->port);
-	drv_data->port.ops = &tty_port_ops;
+	for (a = 0; a < PTITTY_MINOR_NUM; a++) {
+		struct tty_port *port = &drv_data->port[a];
+		tty_port_init(port);
+		port->ops = &tty_port_ops;
 
-	tty_register_device(pti_tty_driver, 0, &pdev->dev);
-	tty_register_device(pti_tty_driver, 1, &pdev->dev);
+		tty_port_register_device(port, pti_tty_driver, a, &pdev->dev);
+	}
 
 	register_console(&pti_console);
 
+	return 0;
+err_rel_reg:
+	pci_release_region(pdev, pci_bar);
+err_free_dd:
+	kfree(drv_data);
+err_disable_pci:
+	pci_disable_device(pdev);
+err_unreg_misc:
+	misc_deregister(&pti_char_driver);
+err:
 	return retval;
 }
 
+/**
+ * pti_pci_remove()- Driver exit method to remove PTI from
+ *		   PCI bus.
+ * @pdev: variable containing pci info of PTI.
+ */
+static void __devexit pti_pci_remove(struct pci_dev *pdev)
+{
+	struct pti_dev *drv_data = pci_get_drvdata(pdev);
+
+	unregister_console(&pti_console);
+
+	tty_unregister_device(pti_tty_driver, 0);
+	tty_unregister_device(pti_tty_driver, 1);
+
+	iounmap(drv_data->pti_ioaddr);
+	pci_set_drvdata(pdev, NULL);
+	kfree(drv_data);
+	pci_release_region(pdev, 1);
+	pci_disable_device(pdev);
+
+	misc_deregister(&pti_char_driver);
+}
+
 static struct pci_driver pti_pci_driver = {
 	.name		= PCINAME,
 	.id_table	= pci_ids,
@@ -933,25 +946,24 @@ static int __init pti_init(void)
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
 
-		pti_tty_driver = NULL;
-		return retval;
+		goto put_tty;
 	}
 
 	retval = pci_register_driver(&pti_pci_driver);
-
 	if (retval) {
 		pr_err("%s(%d): PCI registration failed of pti driver\n",
 			__func__, __LINE__);
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
-
-		tty_unregister_driver(pti_tty_driver);
-		pr_err("%s(%d): Unregistering TTY part of pti driver\n",
-			__func__, __LINE__);
-		pti_tty_driver = NULL;
-		return retval;
+		goto unreg_tty;
 	}
 
+	return 0;
+unreg_tty:
+	tty_unregister_driver(pti_tty_driver);
+put_tty:
+	put_tty_driver(pti_tty_driver);
+	pti_tty_driver = NULL;
 	return retval;
 }
 
@@ -960,31 +972,9 @@ static int __init pti_init(void)
  */
 static void __exit pti_exit(void)
 {
-	int retval;
-
-	tty_unregister_device(pti_tty_driver, 0);
-	tty_unregister_device(pti_tty_driver, 1);
-
-	retval = tty_unregister_driver(pti_tty_driver);
-	if (retval) {
-		pr_err("%s(%d): TTY unregistration failed of pti driver\n",
-			__func__, __LINE__);
-		pr_err("%s(%d): Error value returned: %d\n",
-			__func__, __LINE__, retval);
-	}
-
+	tty_unregister_driver(pti_tty_driver);
 	pci_unregister_driver(&pti_pci_driver);
-
-	retval = misc_deregister(&pti_char_driver);
-	if (retval) {
-		pr_err("%s(%d): CHAR unregistration failed of pti driver\n",
-			__func__, __LINE__);
-		pr_err("%s(%d): Error value returned: %d\n",
-			__func__, __LINE__, retval);
-	}
-
-	unregister_console(&pti_console);
-	return;
+	put_tty_driver(pti_tty_driver);
 }
 
 module_init(pti_init);

+ 12 - 12
drivers/mmc/card/sdio_uart.c

@@ -518,7 +518,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
 	if (status & UART_MSR_DCTS) {
 		port->icount.cts++;
 		tty = tty_port_tty_get(&port->port);
-		if (tty && (tty->termios->c_cflag & CRTSCTS)) {
+		if (tty && (tty->termios.c_cflag & CRTSCTS)) {
 			int cts = (status & UART_MSR_CTS);
 			if (tty->hw_stopped) {
 				if (cts) {
@@ -671,12 +671,12 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
 	port->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE;
 	port->mctrl = TIOCM_OUT2;
 
-	sdio_uart_change_speed(port, tty->termios, NULL);
+	sdio_uart_change_speed(port, &tty->termios, NULL);
 
-	if (tty->termios->c_cflag & CBAUD)
+	if (tty->termios.c_cflag & CBAUD)
 		sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
 
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
 			tty->hw_stopped = 1;
 
@@ -850,7 +850,7 @@ static void sdio_uart_throttle(struct tty_struct *tty)
 {
 	struct sdio_uart_port *port = tty->driver_data;
 
-	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
+	if (!I_IXOFF(tty) && !(tty->termios.c_cflag & CRTSCTS))
 		return;
 
 	if (sdio_uart_claim_func(port) != 0)
@@ -861,7 +861,7 @@ static void sdio_uart_throttle(struct tty_struct *tty)
 		sdio_uart_start_tx(port);
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		sdio_uart_clear_mctrl(port, TIOCM_RTS);
 
 	sdio_uart_irq(port->func);
@@ -872,7 +872,7 @@ static void sdio_uart_unthrottle(struct tty_struct *tty)
 {
 	struct sdio_uart_port *port = tty->driver_data;
 
-	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
+	if (!I_IXOFF(tty) && !(tty->termios.c_cflag & CRTSCTS))
 		return;
 
 	if (sdio_uart_claim_func(port) != 0)
@@ -887,7 +887,7 @@ static void sdio_uart_unthrottle(struct tty_struct *tty)
 		}
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		sdio_uart_set_mctrl(port, TIOCM_RTS);
 
 	sdio_uart_irq(port->func);
@@ -898,12 +898,12 @@ static void sdio_uart_set_termios(struct tty_struct *tty,
 						struct ktermios *old_termios)
 {
 	struct sdio_uart_port *port = tty->driver_data;
-	unsigned int cflag = tty->termios->c_cflag;
+	unsigned int cflag = tty->termios.c_cflag;
 
 	if (sdio_uart_claim_func(port) != 0)
 		return;
 
-	sdio_uart_change_speed(port, tty->termios, old_termios);
+	sdio_uart_change_speed(port, &tty->termios, old_termios);
 
 	/* Handle transition to B0 status */
 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
@@ -1132,8 +1132,8 @@ static int sdio_uart_probe(struct sdio_func *func,
 		kfree(port);
 	} else {
 		struct device *dev;
-		dev = tty_register_device(sdio_uart_tty_driver,
-						port->index, &func->dev);
+		dev = tty_port_register_device(&port->port,
+				sdio_uart_tty_driver, port->index, &func->dev);
 		if (IS_ERR(dev)) {
 			sdio_uart_port_remove(port);
 			ret = PTR_ERR(dev);

+ 12 - 10
drivers/net/ethernet/sgi/ioc3-eth.c

@@ -1147,15 +1147,17 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart)
 {
 #define COSMISC_CONSTANT 6
 
-	struct uart_port port = {
-		.irq		= 0,
-		.flags		= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,
-		.iotype		= UPIO_MEM,
-		.regshift	= 0,
-		.uartclk	= (22000000 << 1) / COSMISC_CONSTANT,
-
-		.membase	= (unsigned char __iomem *) uart,
-		.mapbase	= (unsigned long) uart,
+	struct uart_8250_port port = {
+	        .port = {
+			.irq		= 0,
+			.flags		= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,
+			.iotype		= UPIO_MEM,
+			.regshift	= 0,
+			.uartclk	= (22000000 << 1) / COSMISC_CONSTANT,
+
+			.membase	= (unsigned char __iomem *) uart,
+			.mapbase	= (unsigned long) uart,
+                }
 	};
 	unsigned char lcr;
 
@@ -1164,7 +1166,7 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart)
 	uart->iu_scr = COSMISC_CONSTANT,
 	uart->iu_lcr = lcr;
 	uart->iu_lcr;
-	serial8250_register_port(&port);
+	serial8250_register_8250_port(&port);
 }
 
 static void __devinit ioc3_serial_probe(struct pci_dev *pdev, struct ioc3 *ioc3)

+ 5 - 5
drivers/net/irda/irtty-sir.c

@@ -124,8 +124,8 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
 	tty = priv->tty;
 
 	mutex_lock(&tty->termios_mutex);
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
+	old_termios = tty->termios;
+	cflag = tty->termios.c_cflag;
 	tty_encode_baud_rate(tty, speed, speed);
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_termios);
@@ -281,15 +281,15 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
 	int cflag;
 
 	mutex_lock(&tty->termios_mutex);
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
+	old_termios = tty->termios;
+	cflag = tty->termios.c_cflag;
 	
 	if (stop)
 		cflag &= ~CREAD;
 	else
 		cflag |= CREAD;
 
-	tty->termios->c_cflag = cflag;
+	tty->termios.c_cflag = cflag;
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_termios);
 	mutex_unlock(&tty->termios_mutex);

+ 9 - 10
drivers/net/usb/hso.c

@@ -1107,7 +1107,6 @@ static void _hso_serial_set_termios(struct tty_struct *tty,
 				    struct ktermios *old)
 {
 	struct hso_serial *serial = tty->driver_data;
-	struct ktermios *termios;
 
 	if (!serial) {
 		printk(KERN_ERR "%s: no tty structures", __func__);
@@ -1119,16 +1118,15 @@ static void _hso_serial_set_termios(struct tty_struct *tty,
 	/*
 	 *	Fix up unsupported bits
 	 */
-	termios = tty->termios;
-	termios->c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */
+	tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */
 
-	termios->c_cflag &=
+	tty->termios.c_cflag &=
 		~(CSIZE		/* no size */
 		| PARENB	/* disable parity bit */
 		| CBAUD		/* clear current baud rate */
 		| CBAUDEX);	/* clear current buad rate */
 
-	termios->c_cflag |= CS8;	/* character size 8 bits */
+	tty->termios.c_cflag |= CS8;	/* character size 8 bits */
 
 	/* baud rate 115200 */
 	tty_encode_baud_rate(tty, 115200, 115200);
@@ -1425,14 +1423,14 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
 
 	if (old)
 		D5("Termios called with: cflags new[%d] - old[%d]",
-		   tty->termios->c_cflag, old->c_cflag);
+		   tty->termios.c_cflag, old->c_cflag);
 
 	/* the actual setup */
 	spin_lock_irqsave(&serial->serial_lock, flags);
 	if (serial->port.count)
 		_hso_serial_set_termios(tty, old);
 	else
-		tty->termios = old;
+		tty->termios = *old;
 	spin_unlock_irqrestore(&serial->serial_lock, flags);
 
 	/* done */
@@ -2289,9 +2287,11 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
 	if (minor < 0)
 		goto exit;
 
+	tty_port_init(&serial->port);
+
 	/* register our minor number */
-	serial->parent->dev = tty_register_device(tty_drv, minor,
-					&serial->parent->interface->dev);
+	serial->parent->dev = tty_port_register_device(&serial->port, tty_drv,
+			minor, &serial->parent->interface->dev);
 	dev = serial->parent->dev;
 	dev_set_drvdata(dev, serial->parent);
 	i = device_create_file(dev, &dev_attr_hsotype);
@@ -2300,7 +2300,6 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
 	serial->minor = minor;
 	serial->magic = HSO_SERIAL_MAGIC;
 	spin_lock_init(&serial->serial_lock);
-	tty_port_init(&serial->port);
 	serial->num_rx_urbs = num_urbs;
 
 	/* RX, allocate urb and initialize */

+ 1 - 0
drivers/parport/parport_gsc.c

@@ -271,6 +271,7 @@ struct parport *__devinit parport_gsc_probe_port (unsigned long base,
 	if (!parport_SPP_supported (p)) {
 		/* No port. */
 		kfree (priv);
+		kfree(ops);
 		return NULL;
 	}
 	parport_PS2_supported (p);

+ 10 - 1
drivers/parport/parport_serial.c

@@ -62,6 +62,7 @@ enum parport_pc_pci_cards {
 	timedia_9079a,
 	timedia_9079b,
 	timedia_9079c,
+	wch_ch353_2s1p,
 };
 
 /* each element directly indexed from enum list, above */
@@ -145,6 +146,7 @@ static struct parport_pc_pci cards[] __devinitdata = {
 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
+	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
 };
 
 static struct pci_device_id parport_serial_pci_tbl[] = {
@@ -243,7 +245,8 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
-
+	/* WCH CARDS */
+	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
@@ -460,6 +463,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = {
 		.base_baud	= 921600,
 		.uart_offset	= 8,
 	},
+	[wch_ch353_2s1p] = {
+		.flags          = FL_BASE0|FL_BASE_BARS,
+		.num_ports      = 2,
+		.base_baud      = 115200,
+		.uart_offset    = 8,
+	},
 };
 
 struct parport_serial_private {

+ 22 - 6
drivers/s390/char/con3215.c

@@ -716,10 +716,17 @@ static int raw3215_probe (struct ccw_device *cdev)
 static void raw3215_remove (struct ccw_device *cdev)
 {
 	struct raw3215_info *raw;
+	unsigned int line;
 
 	ccw_device_set_offline(cdev);
 	raw = dev_get_drvdata(&cdev->dev);
 	if (raw) {
+		spin_lock(&raw3215_device_lock);
+		for (line = 0; line < NR_3215; line++)
+			if (raw3215[line] == raw)
+				break;
+		raw3215[line] = NULL;
+		spin_unlock(&raw3215_device_lock);
 		dev_set_drvdata(&cdev->dev, NULL);
 		raw3215_free_info(raw);
 	}
@@ -935,6 +942,19 @@ static int __init con3215_init(void)
 console_initcall(con3215_init);
 #endif
 
+static int tty3215_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct raw3215_info *raw;
+
+	raw = raw3215[tty->index];
+	if (raw == NULL)
+		return -ENODEV;
+
+	tty->driver_data = raw;
+
+	return tty_port_install(&raw->port, driver, tty);
+}
+
 /*
  * tty3215_open
  *
@@ -942,14 +962,9 @@ console_initcall(con3215_init);
  */
 static int tty3215_open(struct tty_struct *tty, struct file * filp)
 {
-	struct raw3215_info *raw;
+	struct raw3215_info *raw = tty->driver_data;
 	int retval;
 
-	raw = raw3215[tty->index];
-	if (raw == NULL)
-		return -ENODEV;
-
-	tty->driver_data = raw;
 	tty_port_tty_set(&raw->port, tty);
 
 	tty->low_latency = 0;  /* don't use bottom half for pushing chars */
@@ -1110,6 +1125,7 @@ static void tty3215_start(struct tty_struct *tty)
 }
 
 static const struct tty_operations tty3215_ops = {
+	.install = tty3215_install,
 	.open = tty3215_open,
 	.close = tty3215_close,
 	.write = tty3215_write,

+ 1 - 0
drivers/s390/char/sclp_tty.c

@@ -567,6 +567,7 @@ sclp_tty_init(void)
 	driver->init_termios.c_lflag = ISIG | ECHO;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(driver, &sclp_ops);
+	tty_port_link_device(&sclp_port, driver, 0);
 	rc = tty_register_driver(driver);
 	if (rc) {
 		put_tty_driver(driver);

+ 1 - 0
drivers/s390/char/sclp_vt220.c

@@ -691,6 +691,7 @@ static int __init sclp_vt220_tty_init(void)
 	driver->init_termios = tty_std_termios;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(driver, &sclp_vt220_ops);
+	tty_port_link_device(&sclp_vt220_port, driver, 0);
 
 	rc = tty_register_driver(driver);
 	if (rc)

+ 24 - 10
drivers/s390/char/tty3270.c

@@ -842,17 +842,14 @@ static struct raw3270_fn tty3270_fn = {
 };
 
 /*
- * This routine is called whenever a 3270 tty is opened.
+ * This routine is called whenever a 3270 tty is opened first time.
  */
-static int
-tty3270_open(struct tty_struct *tty, struct file * filp)
+static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)
 {
 	struct raw3270_view *view;
 	struct tty3270 *tp;
 	int i, rc;
 
-	if (tty->count > 1)
-		return 0;
 	/* Check if the tty3270 is already there. */
 	view = raw3270_find_view(&tty3270_fn,
 				  tty->index + RAW3270_FIRSTMINOR);
@@ -865,7 +862,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
 		/* why to reassign? */
 		tty_port_tty_set(&tp->port, tty);
 		tp->inattr = TF_INPUT;
-		return 0;
+		return tty_port_install(&tp->port, driver, tty);
 	}
 	if (tty3270_max_index < tty->index + 1)
 		tty3270_max_index = tty->index + 1;
@@ -895,7 +892,6 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
 
 	tty_port_tty_set(&tp->port, tty);
 	tty->low_latency = 0;
-	tty->driver_data = tp;
 	tty->winsize.ws_row = tp->view.rows - 2;
 	tty->winsize.ws_col = tp->view.cols;
 
@@ -915,6 +911,15 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
 	kbd_ascebc(tp->kbd, tp->view.ascebc);
 
 	raw3270_activate_view(&tp->view);
+
+	rc = tty_port_install(&tp->port, driver, tty);
+	if (rc) {
+		raw3270_put_view(&tp->view);
+		return rc;
+	}
+
+	tty->driver_data = tp;
+
 	return 0;
 }
 
@@ -932,10 +937,17 @@ tty3270_close(struct tty_struct *tty, struct file * filp)
 	if (tp) {
 		tty->driver_data = NULL;
 		tty_port_tty_set(&tp->port, NULL);
-		raw3270_put_view(&tp->view);
 	}
 }
 
+static void tty3270_cleanup(struct tty_struct *tty)
+{
+	struct tty3270 *tp = tty->driver_data;
+
+	if (tp)
+		raw3270_put_view(&tp->view);
+}
+
 /*
  * We always have room.
  */
@@ -1737,7 +1749,8 @@ static long tty3270_compat_ioctl(struct tty_struct *tty,
 #endif
 
 static const struct tty_operations tty3270_ops = {
-	.open = tty3270_open,
+	.install = tty3270_install,
+	.cleanup = tty3270_cleanup,
 	.close = tty3270_close,
 	.write = tty3270_write,
 	.put_char = tty3270_put_char,
@@ -1781,7 +1794,7 @@ static int __init tty3270_init(void)
 	driver->type = TTY_DRIVER_TYPE_SYSTEM;
 	driver->subtype = SYSTEM_TYPE_TTY;
 	driver->init_termios = tty_std_termios;
-	driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;
+	driver->flags = TTY_DRIVER_RESET_TERMIOS;
 	tty_set_operations(driver, &tty3270_ops);
 	ret = tty_register_driver(driver);
 	if (ret) {
@@ -1800,6 +1813,7 @@ tty3270_exit(void)
 	driver = tty3270_driver;
 	tty3270_driver = NULL;
 	tty_unregister_driver(driver);
+	put_tty_driver(driver);
 	tty3270_del_views();
 }
 

+ 2 - 0
drivers/staging/Kconfig

@@ -142,4 +142,6 @@ source "drivers/staging/ced1401/Kconfig"
 
 source "drivers/staging/imx-drm/Kconfig"
 
+source "drivers/staging/dgrp/Kconfig"
+
 endif # STAGING

+ 1 - 0
drivers/staging/Makefile

@@ -63,3 +63,4 @@ obj-$(CONFIG_ZCACHE2)		+= ramster/
 obj-$(CONFIG_NET_VENDOR_SILICOM)	+= silicom/
 obj-$(CONFIG_CED1401)		+= ced1401/
 obj-$(CONFIG_DRM_IMX)		+= imx-drm/
+obj-$(CONFIG_DGRP)		+= dgrp/

+ 9 - 0
drivers/staging/dgrp/Kconfig

@@ -0,0 +1,9 @@
+config DGRP
+       tristate "Digi Realport driver"
+       default n
+       depends on SYSFS
+       ---help---
+       Support for Digi Realport devices.  These devices allow you to
+       access remote serial ports as if they are local tty devices.  This
+       will build the kernel driver, you will still need the userspace
+       component to make your Realport device work.

+ 12 - 0
drivers/staging/dgrp/Makefile

@@ -0,0 +1,12 @@
+obj-$(CONFIG_DGRP) += dgrp.o
+
+dgrp-y := 			\
+	dgrp_common.o 		\
+	dgrp_dpa_ops.o 		\
+	dgrp_driver.o 		\
+	dgrp_mon_ops.o 	 	\
+	dgrp_net_ops.o 		\
+	dgrp_ports_ops.o 	\
+	dgrp_specproc.o 	\
+	dgrp_tty.o 		\
+	dgrp_sysfs.o

+ 2 - 0
drivers/staging/dgrp/README

@@ -0,0 +1,2 @@
+The user space code to work with this driver is located at
+https://github.com/wfp5p/dgrp-utils

+ 13 - 0
drivers/staging/dgrp/TODO

@@ -0,0 +1,13 @@
+- Use configfs for config stuff.  This will require changes to the
+  user space code.
+
+- dgrp_send() and dgrp_receive() could use some refactoring
+
+- Don't automatically create CHAN_MAX (64) channel array entries for
+  every device as many devices are going to have much less than 64
+  channels.
+
+- The locking needs to be checked.  It seems haphazardly done in most
+  places.
+
+- Check Kconfig dependencies

+ 200 - 0
drivers/staging/dgrp/dgrp_common.c

@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_common.c
+ *
+ *  Description:
+ *
+ *     Definitions of global variables and functions which are either
+ *     shared by the tty, mon, and net drivers; or which cross them
+ *     functionally (like the poller).
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+
+#include "dgrp_common.h"
+
+/**
+ * dgrp_carrier -- check for carrier change state and act
+ * @ch: struct ch_struct *
+ */
+void dgrp_carrier(struct ch_struct *ch)
+{
+	struct nd_struct *nd;
+
+	int virt_carrier = 0;
+	int phys_carrier = 0;
+
+	/* fix case when the tty has already closed. */
+
+	if (!ch)
+		return;
+	nd  = ch->ch_nd;
+	if (!nd)
+		return;
+
+	/*
+	 *  If we are currently waiting to determine the status of the port,
+	 *  we don't yet know the state of the modem lines.  As a result,
+	 *  we ignore state changes when we are waiting for the modem lines
+	 *  to be established.  We know, as a result of code in dgrp_net_ops,
+	 *  that we will be called again immediately following the reception
+	 *  of the status message with the true modem status flags in it.
+	 */
+	if (ch->ch_expect & RR_STATUS)
+		return;
+
+	/*
+	 * If CH_HANGUP is set, we gotta keep trying to get all the processes
+	 * that have the port open to close the port.
+	 * So lets just keep sending a hangup every time we get here.
+	 */
+	if ((ch->ch_flag & CH_HANGUP) &&
+	    (ch->ch_tun.un_open_count > 0))
+		tty_hangup(ch->ch_tun.un_tty);
+
+	/*
+	 *  Compute the effective state of both the physical and virtual
+	 *  senses of carrier.
+	 */
+
+	if (ch->ch_s_mlast & DM_CD)
+		phys_carrier = 1;
+
+	if ((ch->ch_s_mlast & DM_CD) ||
+	    (ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
+	    (ch->ch_flag & CH_CLOCAL))
+		virt_carrier = 1;
+
+	/*
+	 *  Test for a VIRTUAL carrier transition to HIGH.
+	 *
+	 *  The CH_HANGUP condition is intended to prevent any action
+	 *  except for close.  As a result, we ignore positive carrier
+	 *  transitions during CH_HANGUP.
+	 */
+	if (((ch->ch_flag & CH_HANGUP)  == 0) &&
+	    ((ch->ch_flag & CH_VIRT_CD) == 0) &&
+	    (virt_carrier == 1)) {
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+		nd->nd_tx_work = 1;
+
+		if (waitqueue_active(&ch->ch_flag_wait))
+			wake_up_interruptible(&ch->ch_flag_wait);
+	}
+
+	/*
+	 *  Test for a PHYSICAL transition to low, so long as we aren't
+	 *  currently ignoring physical transitions (which is what "virtual
+	 *  carrier" indicates).
+	 *
+	 *  The transition of the virtual carrier to low really doesn't
+	 *  matter... it really only means "ignore carrier state", not
+	 *  "make pretend that carrier is there".
+	 */
+	if ((virt_carrier == 0) &&
+	    ((ch->ch_flag & CH_PHYS_CD) != 0) &&
+	    (phys_carrier == 0)) {
+		/*
+		 * When carrier drops:
+		 *
+		 *   Do a Hard Hangup if that is called for.
+		 *
+		 *   Drop carrier on all open units.
+		 *
+		 *   Flush queues, waking up any task waiting in the
+		 *   line discipline.
+		 *
+		 *   Send a hangup to the control terminal.
+		 *
+		 *   Enable all select calls.
+		 */
+
+		nd->nd_tx_work = 1;
+
+		ch->ch_flag &= ~(CH_LOW | CH_EMPTY | CH_DRAIN | CH_INPUT);
+
+		if (waitqueue_active(&ch->ch_flag_wait))
+			wake_up_interruptible(&ch->ch_flag_wait);
+
+		if (ch->ch_tun.un_open_count > 0)
+			tty_hangup(ch->ch_tun.un_tty);
+
+		if (ch->ch_pun.un_open_count > 0)
+			tty_hangup(ch->ch_pun.un_tty);
+	}
+
+	/*
+	 *  Make sure that our cached values reflect the current reality.
+	 */
+	if (virt_carrier == 1)
+		ch->ch_flag |= CH_VIRT_CD;
+	else
+		ch->ch_flag &= ~CH_VIRT_CD;
+
+	if (phys_carrier == 1)
+		ch->ch_flag |= CH_PHYS_CD;
+	else
+		ch->ch_flag &= ~CH_PHYS_CD;
+
+}
+
+/**
+ * dgrp_chk_perm() -- check permissions for net device
+ * @inode: pointer to inode structure for the net communication device
+ * @op: operation to be tested
+ *
+ * The file permissions and ownerships are tested to determine whether
+ * the operation "op" is permitted on the file pointed to by the inode.
+ * Returns 0 if the operation is permitted, -EACCESS otherwise
+ */
+int dgrp_chk_perm(int mode, int op)
+{
+	if (!current_euid())
+		mode >>= 6;
+	else if (in_egroup_p(0))
+		mode >>= 3;
+
+	if ((mode & op & 0007) == op)
+		return 0;
+
+	if (capable(CAP_SYS_ADMIN))
+		return 0;
+
+	return -EACCES;
+}
+
+/* dgrp_chk_perm wrapper for permission call in struct inode_operations */
+int dgrp_inode_permission(struct inode *inode, int op)
+{
+	return dgrp_chk_perm(inode->i_mode, op);
+}

+ 208 - 0
drivers/staging/dgrp/dgrp_common.h

@@ -0,0 +1,208 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DGRP_COMMON_H
+#define __DGRP_COMMON_H
+
+#define DIGI_VERSION "1.9-29"
+
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include "drp.h"
+
+#define DGRP_TTIME 100
+#define DGRP_RTIME 100
+
+/************************************************************************
+ * All global storage allocation.
+ ************************************************************************/
+
+extern int dgrp_rawreadok;  /* Allow raw writing of input */
+extern int dgrp_register_cudevices; /* enable legacy cu devices */
+extern int dgrp_register_prdevices; /* enable transparent print devices */
+extern int dgrp_poll_tick;          /* Poll interval - in ms */
+
+extern struct list_head nd_struct_list;
+
+struct dgrp_poll_data {
+	spinlock_t poll_lock;
+	struct timer_list timer;
+	int poll_tick;
+	ulong poll_round;	/* Timer rouding factor */
+	long node_active_count;
+};
+
+extern struct dgrp_poll_data dgrp_poll_data;
+extern void dgrp_poll_handler(unsigned long arg);
+
+/* from dgrp_mon_ops.c */
+extern void dgrp_register_mon_hook(struct proc_dir_entry *de);
+
+/* from dgrp_tty.c */
+extern int dgrp_tty_init(struct nd_struct *nd);
+extern void dgrp_tty_uninit(struct nd_struct *nd);
+
+/* from dgrp_ports_ops.c */
+extern void dgrp_register_ports_hook(struct proc_dir_entry *de);
+
+/* from dgrp_net_ops.c */
+extern void dgrp_register_net_hook(struct proc_dir_entry *de);
+
+/* from dgrp_dpa_ops.c */
+extern void dgrp_register_dpa_hook(struct proc_dir_entry *de);
+extern void dgrp_dpa_data(struct nd_struct *, int, u8 *, int);
+
+/* from dgrp_sysfs.c */
+extern void dgrp_create_class_sysfs_files(void);
+extern void dgrp_remove_class_sysfs_files(void);
+
+extern void dgrp_create_node_class_sysfs_files(struct nd_struct *nd);
+extern void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd);
+
+extern void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c);
+extern void dgrp_remove_tty_sysfs(struct device *c);
+
+/* from dgrp_specproc.c */
+/*
+ *  The list of DGRP entries with r/w capabilities.  These
+ *  magic numbers are used for identification purposes.
+ */
+enum {
+	DGRP_CONFIG = 1,	/* Configure portservers */
+	DGRP_NETDIR = 2,	/* Directory for "net" devices */
+	DGRP_MONDIR = 3,	/* Directory for "mon" devices */
+	DGRP_PORTSDIR = 4,	/* Directory for "ports" devices */
+	DGRP_INFO = 5,		/* Get info. about the running module */
+	DGRP_NODEINFO = 6,	/* Get info. about the configured nodes */
+	DGRP_DPADIR = 7,	/* Directory for the "dpa" devices */
+};
+
+/*
+ *  Directions for proc handlers
+ */
+enum {
+	INBOUND = 1,		/* Data being written to kernel */
+	OUTBOUND = 2,		/* Data being read from the kernel */
+};
+
+/**
+ * dgrp_proc_entry: structure for dgrp proc dirs
+ * @id: ID number associated with this particular entry.  Should be
+ *    unique across all of DGRP.
+ * @name: text name associated with the /proc entry
+ * @mode: file access permisssions for the /proc entry
+ * @child: pointer to table describing a subdirectory for this entry
+ * @de: pointer to directory entry for this object once registered.  Used
+ *    to grab the handle of the object for unregistration
+ * @excl_sem: semaphore to provide exclusive to struct
+ * @excl_cnt: counter of current accesses
+ *
+ *  Each entry in a DGRP proc directory is described with a
+ *  dgrp_proc_entry structure.  A collection of these
+ *  entries (in an array) represents the members associated
+ *  with a particular /proc directory, and is referred to
+ *  as a table.  All tables are terminated by an entry with
+ *  zeros for every member.
+ */
+struct dgrp_proc_entry {
+	int                  id;          /* Integer identifier */
+	const char        *name;          /* ASCII identifier */
+	mode_t             mode;          /* File access permissions */
+	struct dgrp_proc_entry *child;    /* Child pointer */
+
+	/* file ops to use, pass NULL to use default */
+	struct file_operations *proc_file_ops;
+
+	struct proc_dir_entry *de;        /* proc entry pointer */
+	struct semaphore   excl_sem;      /* Protects exclusive access var */
+	int                excl_cnt;      /* Counts number of curr accesses */
+};
+
+extern void dgrp_unregister_proc(void);
+extern void dgrp_register_proc(void);
+
+/*-----------------------------------------------------------------------*
+ *
+ *  Declarations for common operations:
+ *
+ *      (either used by more than one of net, mon, or tty,
+ *       or in interrupt context (i.e. the poller))
+ *
+ *-----------------------------------------------------------------------*/
+
+void dgrp_carrier(struct ch_struct *ch);
+extern int dgrp_inode_permission(struct inode *inode, int op);
+extern int dgrp_chk_perm(int mode, int op);
+
+
+/*
+ *  ID manipulation macros (where c1 & c2 are characters, i is
+ *  a long integer, and s is a character array of at least three members
+ */
+
+static inline void ID_TO_CHAR(long i, char *s)
+{
+	s[0] = ((i & 0xff00)>>8);
+	s[1] = (i & 0xff);
+	s[2] = 0;
+}
+
+static inline long CHAR_TO_ID(char *s)
+{
+	return ((s[0] & 0xff) << 8) | (s[1] & 0xff);
+}
+
+static inline struct nd_struct *nd_struct_get(long major)
+{
+	struct nd_struct *nd;
+
+	list_for_each_entry(nd, &nd_struct_list, list) {
+		if (major == nd->nd_major)
+			return nd;
+	}
+
+	return NULL;
+}
+
+static inline int nd_struct_add(struct nd_struct *entry)
+{
+	struct nd_struct *ptr;
+
+	ptr = nd_struct_get(entry->nd_major);
+
+	if (ptr)
+		return -EBUSY;
+
+	list_add_tail(&entry->list, &nd_struct_list);
+
+	return 0;
+}
+
+static inline int nd_struct_del(struct nd_struct *entry)
+{
+	struct nd_struct *nd;
+
+	nd = nd_struct_get(entry->nd_major);
+
+	if (!nd)
+		return -ENODEV;
+
+	list_del(&nd->list);
+	return 0;
+}
+
+#endif /* __DGRP_COMMON_H */

+ 556 - 0
drivers/staging/dgrp/dgrp_dpa_ops.c

@@ -0,0 +1,556 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_dpa_ops.c
+ *
+ *  Description:
+ *
+ *     Handle the file operations required for the "dpa" devices.
+ *     Includes those functions required to register the "dpa" devices
+ *     in "/proc".
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/poll.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_dpa_open(struct inode *, struct file *);
+static int dgrp_dpa_release(struct inode *, struct file *);
+static ssize_t dgrp_dpa_read(struct file *, char __user *, size_t, loff_t *);
+static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg);
+static unsigned int dgrp_dpa_select(struct file *, struct poll_table_struct *);
+
+static const struct file_operations dpa_ops = {
+	.owner   =  THIS_MODULE,
+	.read    =  dgrp_dpa_read,
+	.poll    =  dgrp_dpa_select,
+	.unlocked_ioctl =  dgrp_dpa_ioctl,
+	.open    =  dgrp_dpa_open,
+	.release =  dgrp_dpa_release,
+};
+
+static struct inode_operations dpa_inode_ops = {
+	.permission = dgrp_inode_permission
+};
+
+
+
+struct digi_node {
+	uint	nd_state;		/* Node state: 1 = up, 0 = down. */
+	uint	nd_chan_count;		/* Number of channels found */
+	uint	nd_tx_byte;		/* Tx data count */
+	uint	nd_rx_byte;		/* RX data count */
+	u8	nd_ps_desc[MAX_DESC_LEN]; /* Description from PS */
+};
+
+#define DIGI_GETNODE      (('d'<<8) | 249)	/* get board info */
+
+
+struct digi_chan {
+	uint	ch_port;	/* Port number to get info on */
+	uint	ch_open;	/* 1 if open, 0 if not */
+	uint	ch_txcount;	/* TX data count  */
+	uint	ch_rxcount;	/* RX data count  */
+	uint	ch_s_brate;	/* Realport BRATE */
+	uint	ch_s_estat;	/* Realport ELAST */
+	uint	ch_s_cflag;	/* Realport CFLAG */
+	uint	ch_s_iflag;	/* Realport IFLAG */
+	uint	ch_s_oflag;	/* Realport OFLAG */
+	uint	ch_s_xflag;	/* Realport XFLAG */
+	uint	ch_s_mstat;	/* Realport MLAST */
+};
+
+#define DIGI_GETCHAN      (('d'<<8) | 248)	/* get channel info */
+
+
+struct digi_vpd {
+	int vpd_len;
+	char vpd_data[VPDSIZE];
+};
+
+#define DIGI_GETVPD       (('d'<<8) | 246)	/* get VPD info */
+
+
+struct digi_debug {
+	int onoff;
+	int port;
+};
+
+#define DIGI_SETDEBUG      (('d'<<8) | 247)	/* set debug info */
+
+
+void dgrp_register_dpa_hook(struct proc_dir_entry *de)
+{
+	struct nd_struct *node = de->data;
+
+	de->proc_iops = &dpa_inode_ops;
+	de->proc_fops = &dpa_ops;
+
+	node->nd_dpa_de = de;
+	spin_lock_init(&node->nd_dpa_lock);
+}
+
+/*
+ * dgrp_dpa_open -- open the DPA device for a particular PortServer
+ */
+static int dgrp_dpa_open(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+	int rtn = 0;
+
+	struct proc_dir_entry *de;
+
+	rtn = try_module_get(THIS_MODULE);
+	if (!rtn)
+		return -ENXIO;
+
+	rtn = 0;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		rtn = -EPERM;
+		goto done;
+	}
+
+	/*
+	 *  Make sure that the "private_data" field hasn't already been used.
+	 */
+	if (file->private_data) {
+		rtn = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 *  Get the node pointer, and fail if it doesn't exist.
+	 */
+	de = PDE(inode);
+	if (!de) {
+		rtn = -ENXIO;
+		goto done;
+	}
+	nd = (struct nd_struct *)de->data;
+	if (!nd) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	file->private_data = (void *) nd;
+
+	/*
+	 * Allocate the DPA buffer.
+	 */
+
+	if (nd->nd_dpa_buf) {
+		rtn = -EBUSY;
+	} else {
+		nd->nd_dpa_buf = kmalloc(DPA_MAX, GFP_KERNEL);
+
+		if (!nd->nd_dpa_buf) {
+			rtn = -ENOMEM;
+		} else {
+			nd->nd_dpa_out = 0;
+			nd->nd_dpa_in = 0;
+			nd->nd_dpa_lbolt = jiffies;
+		}
+	}
+
+done:
+
+	if (rtn)
+		module_put(THIS_MODULE);
+	return rtn;
+}
+
+/*
+ * dgrp_dpa_release -- close the DPA device for a particular PortServer
+ */
+static int dgrp_dpa_release(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+	u8 *buf;
+	unsigned long lock_flags;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		goto done;
+
+	/*
+	 *  Free the dpa buffer.
+	 */
+
+	spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+	buf = nd->nd_dpa_buf;
+
+	nd->nd_dpa_buf = NULL;
+	nd->nd_dpa_out = nd->nd_dpa_in;
+
+	/*
+	 *  Wakeup any thread waiting for buffer space.
+	 */
+
+	if (nd->nd_dpa_flag & DPA_WAIT_SPACE) {
+		nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
+		wake_up_interruptible(&nd->nd_dpa_wqueue);
+	}
+
+	spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+
+	kfree(buf);
+
+done:
+	module_put(THIS_MODULE);
+	file->private_data = NULL;
+	return 0;
+}
+
+/*
+ * dgrp_dpa_read
+ *
+ * Copy data from the monitoring buffer to the user, freeing space
+ * in the monitoring buffer for more messages
+ */
+static ssize_t dgrp_dpa_read(struct file *file, char __user *buf, size_t count,
+			     loff_t *ppos)
+{
+	struct nd_struct *nd;
+	int n;
+	int r;
+	int offset = 0;
+	int res = 0;
+	ssize_t rtn;
+	unsigned long lock_flags;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		return -ENXIO;
+
+	/*
+	 *  Wait for some data to appear in the buffer.
+	 */
+
+	spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+	for (;;) {
+		n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
+
+		if (n != 0)
+			break;
+
+		nd->nd_dpa_flag |= DPA_WAIT_DATA;
+
+		spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+
+		/*
+		 * Go to sleep waiting until the condition becomes true.
+		 */
+		rtn = wait_event_interruptible(nd->nd_dpa_wqueue,
+			((nd->nd_dpa_flag & DPA_WAIT_DATA) == 0));
+
+		if (rtn)
+			return rtn;
+
+		spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+	}
+
+	/*
+	 *  Read whatever is there.
+	 */
+
+	if (n > count)
+		n = count;
+
+	res = n;
+
+	r = DPA_MAX - nd->nd_dpa_out;
+
+	if (r <= n) {
+
+		spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+		rtn = copy_to_user((void __user *)buf,
+				   nd->nd_dpa_buf + nd->nd_dpa_out, r);
+		spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+		if (rtn) {
+			rtn = -EFAULT;
+			goto done;
+		}
+
+		nd->nd_dpa_out = 0;
+		n -= r;
+		offset = r;
+	}
+
+	spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+	rtn = copy_to_user((void __user *)buf + offset,
+			   nd->nd_dpa_buf + nd->nd_dpa_out, n);
+	spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+	if (rtn) {
+		rtn = -EFAULT;
+		goto done;
+	}
+
+	nd->nd_dpa_out += n;
+
+	*ppos += res;
+
+	rtn = res;
+
+	/*
+	 *  Wakeup any thread waiting for buffer space.
+	 */
+
+	n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
+
+	if (nd->nd_dpa_flag & DPA_WAIT_SPACE &&
+	    (DPA_MAX - n) > DPA_HIGH_WATER) {
+		nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
+		wake_up_interruptible(&nd->nd_dpa_wqueue);
+	}
+
+ done:
+	spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+	return rtn;
+}
+
+static unsigned int dgrp_dpa_select(struct file *file,
+				    struct poll_table_struct *table)
+{
+	unsigned int retval = 0;
+	struct nd_struct *nd = file->private_data;
+
+	if (nd->nd_dpa_out != nd->nd_dpa_in)
+		retval |= POLLIN | POLLRDNORM; /* Conditionally readable */
+
+	retval |= POLLOUT | POLLWRNORM;        /* Always writeable */
+
+	return retval;
+}
+
+static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+
+	struct nd_struct  *nd;
+	struct digi_chan getchan;
+	struct digi_node getnode;
+	struct ch_struct *ch;
+	struct digi_debug setdebug;
+	struct digi_vpd vpd;
+	unsigned int port;
+	void __user *uarg = (void __user *) arg;
+
+	nd = file->private_data;
+
+	switch (cmd) {
+	case DIGI_GETCHAN:
+		if (copy_from_user(&getchan, uarg, sizeof(struct digi_chan)))
+			return -EFAULT;
+
+		port = getchan.ch_port;
+
+		if (port < 0 || port > nd->nd_chan_count)
+			return -EINVAL;
+
+		ch = nd->nd_chan + port;
+
+		getchan.ch_open = (ch->ch_open_count > 0) ? 1 : 0;
+		getchan.ch_txcount = ch->ch_txcount;
+		getchan.ch_rxcount = ch->ch_rxcount;
+		getchan.ch_s_brate = ch->ch_s_brate;
+		getchan.ch_s_estat = ch->ch_s_elast;
+		getchan.ch_s_cflag = ch->ch_s_cflag;
+		getchan.ch_s_iflag = ch->ch_s_iflag;
+		getchan.ch_s_oflag = ch->ch_s_oflag;
+		getchan.ch_s_xflag = ch->ch_s_xflag;
+		getchan.ch_s_mstat = ch->ch_s_mlast;
+
+		if (copy_to_user(uarg, &getchan, sizeof(struct digi_chan)))
+			return -EFAULT;
+		break;
+
+
+	case DIGI_GETNODE:
+		getnode.nd_state = (nd->nd_state & NS_READY) ? 1 : 0;
+		getnode.nd_chan_count = nd->nd_chan_count;
+		getnode.nd_tx_byte = nd->nd_tx_byte;
+		getnode.nd_rx_byte = nd->nd_rx_byte;
+
+		memset(&getnode.nd_ps_desc, 0, MAX_DESC_LEN);
+		strncpy(getnode.nd_ps_desc, nd->nd_ps_desc, MAX_DESC_LEN);
+
+		if (copy_to_user(uarg, &getnode, sizeof(struct digi_node)))
+			return -EFAULT;
+		break;
+
+
+	case DIGI_SETDEBUG:
+		if (copy_from_user(&setdebug, uarg, sizeof(struct digi_debug)))
+			return -EFAULT;
+
+		nd->nd_dpa_debug = setdebug.onoff;
+		nd->nd_dpa_port = setdebug.port;
+		break;
+
+
+	case DIGI_GETVPD:
+		if (nd->nd_vpd_len > 0) {
+			vpd.vpd_len = nd->nd_vpd_len;
+			memcpy(&vpd.vpd_data, &nd->nd_vpd, nd->nd_vpd_len);
+		} else {
+			vpd.vpd_len = 0;
+		}
+
+		if (copy_to_user(uarg, &vpd, sizeof(struct digi_vpd)))
+			return -EFAULT;
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * dgrp_dpa() -- send data to the device monitor queue
+ * @nd: pointer to a node structure
+ * @buf: buffer of data to copy to the monitoring buffer
+ * @len: number of bytes to transfer to the buffer
+ *
+ * Called by the net device routines to send data to the device
+ * monitor queue.  If the device monitor buffer is too full to
+ * accept the data, it waits until the buffer is ready.
+ */
+static void dgrp_dpa(struct nd_struct *nd, u8 *buf, int nbuf)
+{
+	int n;
+	int r;
+	unsigned long lock_flags;
+
+	/*
+	 *  Grab DPA lock.
+	 */
+	spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+	/*
+	 *  Loop while data remains.
+	 */
+	while (nbuf > 0 && nd->nd_dpa_buf != NULL) {
+
+		n = (nd->nd_dpa_out - nd->nd_dpa_in - 1) & DPA_MASK;
+
+		/*
+		 * Enforce flow control on the DPA device.
+		 */
+		if (n < (DPA_MAX - DPA_HIGH_WATER))
+			nd->nd_dpa_flag |= DPA_WAIT_SPACE;
+
+		/*
+		 * This should never happen, as the flow control above
+		 * should have stopped things before they got to this point.
+		 */
+		if (n == 0) {
+			spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+			return;
+		}
+
+		/*
+		 * Copy as much data as will fit.
+		 */
+
+		if (n > nbuf)
+			n = nbuf;
+
+		r = DPA_MAX - nd->nd_dpa_in;
+
+		if (r <= n) {
+			memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, r);
+
+			n -= r;
+
+			nd->nd_dpa_in = 0;
+
+			buf += r;
+			nbuf -= r;
+		}
+
+		memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, n);
+
+		nd->nd_dpa_in += n;
+
+		buf += n;
+		nbuf -= n;
+
+		if (nd->nd_dpa_in >= DPA_MAX)
+			pr_info_ratelimited("%s - nd->nd_dpa_in (%i) >= DPA_MAX\n",
+					    __func__, nd->nd_dpa_in);
+
+		/*
+		 *  Wakeup any thread waiting for data
+		 */
+		if (nd->nd_dpa_flag & DPA_WAIT_DATA) {
+			nd->nd_dpa_flag &= ~DPA_WAIT_DATA;
+			wake_up_interruptible(&nd->nd_dpa_wqueue);
+		}
+	}
+
+	/*
+	 *  Release the DPA lock.
+	 */
+	spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+}
+
+/**
+ * dgrp_monitor_data() -- builds a DPA data packet
+ * @nd: pointer to a node structure
+ * @type: type of message to be logged in the DPA buffer
+ * @buf: buffer of data to be logged in the DPA buffer
+ * @size -- number of bytes in the "buf" buffer
+ */
+void dgrp_dpa_data(struct nd_struct *nd, int type, u8 *buf, int size)
+{
+	u8 header[5];
+
+	header[0] = type;
+
+	put_unaligned_be32(size, header + 1);
+
+	dgrp_dpa(nd, header, sizeof(header));
+	dgrp_dpa(nd, buf, size);
+}

+ 110 - 0
drivers/staging/dgrp/dgrp_driver.c

@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright 1999-2003 Digi International (www.digi.com)
+ *     Jeff Randall
+ *     James Puzzo  <jamesp at digi dot com>
+ *     Scott Kilau  <Scott_Kilau at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *	Driver specific includes
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ *  PortServer includes
+ */
+#include "dgrp_common.h"
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("RealPort driver for Digi's ethernet-based serial connectivity product line");
+MODULE_VERSION(DIGI_VERSION);
+
+struct list_head nd_struct_list;
+struct dgrp_poll_data dgrp_poll_data;
+
+int dgrp_rawreadok = 1;		/* Bypass flipbuf on input */
+int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */
+int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */
+int dgrp_poll_tick = 20;	/* Poll interval - in ms */
+
+module_param_named(rawreadok, dgrp_rawreadok, int, 0644);
+MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input");
+
+module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644);
+MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices");
+
+module_param_named(register_prdevices, dgrp_register_prdevices, int, 0644);
+MODULE_PARM_DESC(register_prdevices, "Turn on/off registering transparent print devices");
+
+module_param_named(pollrate, dgrp_poll_tick, int, 0644);
+MODULE_PARM_DESC(pollrate, "Poll interval in ms");
+
+/* Driver load/unload functions */
+static int dgrp_init_module(void);
+static void dgrp_cleanup_module(void);
+
+module_init(dgrp_init_module);
+module_exit(dgrp_cleanup_module);
+
+/*
+ * init_module()
+ *
+ * Module load.  This is where it all starts.
+ */
+static int dgrp_init_module(void)
+{
+	INIT_LIST_HEAD(&nd_struct_list);
+
+	spin_lock_init(&dgrp_poll_data.poll_lock);
+	init_timer(&dgrp_poll_data.timer);
+	dgrp_poll_data.poll_tick = dgrp_poll_tick;
+	dgrp_poll_data.timer.function = dgrp_poll_handler;
+	dgrp_poll_data.timer.data = (unsigned long) &dgrp_poll_data;
+
+	dgrp_create_class_sysfs_files();
+
+	dgrp_register_proc();
+
+	return 0;
+}
+
+
+/*
+ *	Module unload.  This is where it all ends.
+ */
+static void dgrp_cleanup_module(void)
+{
+	struct nd_struct *nd, *next;
+
+	/*
+	 *	Attempting to free resources in backwards
+	 *	order of allocation, in case that helps
+	 *	memory pool fragmentation.
+	 */
+	dgrp_unregister_proc();
+
+	dgrp_remove_class_sysfs_files();
+
+
+	list_for_each_entry_safe(nd, next, &nd_struct_list, list) {
+		dgrp_tty_uninit(nd);
+		kfree(nd);
+	}
+}

+ 346 - 0
drivers/staging/dgrp/dgrp_mon_ops.c

@@ -0,0 +1,346 @@
+/*****************************************************************************
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_mon_ops.c
+ *
+ *  Description:
+ *
+ *     Handle the file operations required for the "monitor" devices.
+ *     Includes those functions required to register the "mon" devices
+ *     in "/proc".
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+#include <linux/proc_fs.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_mon_open(struct inode *, struct file *);
+static int dgrp_mon_release(struct inode *, struct file *);
+static ssize_t dgrp_mon_read(struct file *, char __user *, size_t, loff_t *);
+static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg);
+
+static const struct file_operations mon_ops = {
+	.owner   = THIS_MODULE,
+	.read    = dgrp_mon_read,
+	.unlocked_ioctl = dgrp_mon_ioctl,
+	.open    = dgrp_mon_open,
+	.release = dgrp_mon_release,
+};
+
+static struct inode_operations mon_inode_ops = {
+	.permission = dgrp_inode_permission
+};
+
+void dgrp_register_mon_hook(struct proc_dir_entry *de)
+{
+	struct nd_struct *node = de->data;
+
+	de->proc_iops = &mon_inode_ops;
+	de->proc_fops = &mon_ops;
+	node->nd_mon_de = de;
+	sema_init(&node->nd_mon_semaphore, 1);
+}
+
+/**
+ * dgrp_mon_open() -- open /proc/dgrp/ports device for a PortServer
+ * @inode: struct inode *
+ * @file: struct file *
+ *
+ * Open function to open the /proc/dgrp/ports device for a PortServer.
+ */
+static int dgrp_mon_open(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+	struct proc_dir_entry *de;
+	struct timeval tv;
+	uint32_t time;
+	u8 *buf;
+	int rtn;
+
+	rtn = try_module_get(THIS_MODULE);
+	if (!rtn)
+		return -ENXIO;
+
+	rtn = 0;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		rtn = -EPERM;
+		goto done;
+	}
+
+	/*
+	 *  Make sure that the "private_data" field hasn't already been used.
+	 */
+	if (file->private_data) {
+		rtn = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 *  Get the node pointer, and fail if it doesn't exist.
+	 */
+	de = PDE(inode);
+	if (!de) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	nd = (struct nd_struct *)de->data;
+	if (!nd) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	file->private_data = (void *) nd;
+
+	/*
+	 * Allocate the monitor buffer.
+	 */
+
+	/*
+	 *  Grab the MON lock.
+	 */
+	down(&nd->nd_mon_semaphore);
+
+	if (nd->nd_mon_buf) {
+		rtn = -EBUSY;
+		goto done_up;
+	}
+
+	nd->nd_mon_buf = kmalloc(MON_MAX, GFP_KERNEL);
+
+	if (!nd->nd_mon_buf) {
+		rtn = -ENOMEM;
+		goto done_up;
+	}
+
+	/*
+	 *  Enter an RPDUMP file header into the buffer.
+	 */
+
+	buf = nd->nd_mon_buf;
+
+	strcpy(buf, RPDUMP_MAGIC);
+	buf += strlen(buf) + 1;
+
+	do_gettimeofday(&tv);
+
+	/*
+	 *  tv.tv_sec might be a 64 bit quantity.  Pare
+	 *  it down to 32 bits before attempting to encode
+	 *  it.
+	 */
+	time = (uint32_t) (tv.tv_sec & 0xffffffff);
+
+	put_unaligned_be32(time, buf);
+	put_unaligned_be16(0, buf + 4);
+	buf += 6;
+
+	if (nd->nd_tx_module) {
+		buf[0] = RPDUMP_CLIENT;
+		put_unaligned_be32(0, buf + 1);
+		put_unaligned_be16(1, buf + 5);
+		buf[7] = 0xf0 + nd->nd_tx_module;
+		buf += 8;
+	}
+
+	if (nd->nd_rx_module) {
+		buf[0] = RPDUMP_SERVER;
+		put_unaligned_be32(0, buf + 1);
+		put_unaligned_be16(1, buf + 5);
+		buf[7] = 0xf0 + nd->nd_rx_module;
+		buf += 8;
+	}
+
+	nd->nd_mon_out = 0;
+	nd->nd_mon_in  = buf - nd->nd_mon_buf;
+	nd->nd_mon_lbolt = jiffies;
+
+done_up:
+	up(&nd->nd_mon_semaphore);
+
+done:
+	if (rtn)
+		module_put(THIS_MODULE);
+	return rtn;
+}
+
+
+/**
+ * dgrp_mon_release() - Close the MON device for a particular PortServer
+ * @inode: struct inode *
+ * @file: struct file *
+ */
+static int dgrp_mon_release(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		goto done;
+
+	/*
+	 *  Free the monitor buffer.
+	 */
+
+	down(&nd->nd_mon_semaphore);
+
+	kfree(nd->nd_mon_buf);
+	nd->nd_mon_buf = NULL;
+	nd->nd_mon_out = nd->nd_mon_in;
+
+	/*
+	 *  Wakeup any thread waiting for buffer space.
+	 */
+
+	if (nd->nd_mon_flag & MON_WAIT_SPACE) {
+		nd->nd_mon_flag &= ~MON_WAIT_SPACE;
+		wake_up_interruptible(&nd->nd_mon_wqueue);
+	}
+
+	up(&nd->nd_mon_semaphore);
+
+	/*
+	 *  Make sure there is no thread in the middle of writing a packet.
+	 */
+	down(&nd->nd_net_semaphore);
+	up(&nd->nd_net_semaphore);
+
+done:
+	module_put(THIS_MODULE);
+	file->private_data = NULL;
+	return 0;
+}
+
+/**
+ * dgrp_mon_read() -- Copy data from the monitoring buffer to the user
+ */
+static ssize_t dgrp_mon_read(struct file *file, char __user *buf, size_t count,
+			     loff_t *ppos)
+{
+	struct nd_struct *nd;
+	int r;
+	int offset = 0;
+	int res = 0;
+	ssize_t rtn;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		return -ENXIO;
+
+	/*
+	 *  Wait for some data to appear in the buffer.
+	 */
+
+	down(&nd->nd_mon_semaphore);
+
+	for (;;) {
+		res = (nd->nd_mon_in - nd->nd_mon_out) & MON_MASK;
+
+		if (res)
+			break;
+
+		nd->nd_mon_flag |= MON_WAIT_DATA;
+
+		up(&nd->nd_mon_semaphore);
+
+		/*
+		 * Go to sleep waiting until the condition becomes true.
+		 */
+		rtn = wait_event_interruptible(nd->nd_mon_wqueue,
+					       ((nd->nd_mon_flag & MON_WAIT_DATA) == 0));
+
+		if (rtn)
+			return rtn;
+
+		down(&nd->nd_mon_semaphore);
+	}
+
+	/*
+	 *  Read whatever is there.
+	 */
+
+	if (res > count)
+		res = count;
+
+	r = MON_MAX - nd->nd_mon_out;
+
+	if (r <= res) {
+		rtn = copy_to_user((void __user *)buf,
+				   nd->nd_mon_buf + nd->nd_mon_out, r);
+		if (rtn) {
+			up(&nd->nd_mon_semaphore);
+			return -EFAULT;
+		}
+
+		nd->nd_mon_out = 0;
+		res -= r;
+		offset = r;
+	}
+
+	rtn = copy_to_user((void __user *) buf + offset,
+			   nd->nd_mon_buf + nd->nd_mon_out, res);
+	if (rtn) {
+		up(&nd->nd_mon_semaphore);
+		return -EFAULT;
+	}
+
+	nd->nd_mon_out += res;
+
+	*ppos += res;
+
+	up(&nd->nd_mon_semaphore);
+
+	/*
+	 *  Wakeup any thread waiting for buffer space.
+	 */
+
+	if (nd->nd_mon_flag & MON_WAIT_SPACE) {
+		nd->nd_mon_flag &= ~MON_WAIT_SPACE;
+		wake_up_interruptible(&nd->nd_mon_wqueue);
+	}
+
+	return res;
+}
+
+/*  ioctl is not valid on monitor device */
+static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	return -EINVAL;
+}

+ 3737 - 0
drivers/staging/dgrp/dgrp_net_ops.c

@@ -0,0 +1,3737 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo  <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_net_ops.c
+ *
+ *  Description:
+ *
+ *     Handle the file operations required for the "network" devices.
+ *     Includes those functions required to register the "net" devices
+ *     in "/proc".
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
+
+#define MYFLIPLEN	TBUF_MAX
+
+#include "dgrp_common.h"
+
+#define TTY_FLIPBUF_SIZE 512
+#define DEVICE_NAME_SIZE 50
+
+/*
+ *  Generic helper function declarations
+ */
+static void   parity_scan(struct ch_struct *ch, unsigned char *cbuf,
+				unsigned char *fbuf, int *len);
+
+/*
+ *  File operation declarations
+ */
+static int dgrp_net_open(struct inode *, struct file *);
+static int dgrp_net_release(struct inode *, struct file *);
+static ssize_t dgrp_net_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t dgrp_net_write(struct file *, const char __user *, size_t,
+			      loff_t *);
+static long dgrp_net_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg);
+static unsigned int dgrp_net_select(struct file *file,
+				    struct poll_table_struct *table);
+
+static const struct file_operations net_ops = {
+	.owner   =  THIS_MODULE,
+	.read    =  dgrp_net_read,
+	.write   =  dgrp_net_write,
+	.poll    =  dgrp_net_select,
+	.unlocked_ioctl =  dgrp_net_ioctl,
+	.open    =  dgrp_net_open,
+	.release =  dgrp_net_release,
+};
+
+static struct inode_operations net_inode_ops = {
+	.permission = dgrp_inode_permission
+};
+
+void dgrp_register_net_hook(struct proc_dir_entry *de)
+{
+	struct nd_struct *node = de->data;
+
+	de->proc_iops = &net_inode_ops;
+	de->proc_fops = &net_ops;
+	node->nd_net_de = de;
+	sema_init(&node->nd_net_semaphore, 1);
+	node->nd_state = NS_CLOSED;
+	dgrp_create_node_class_sysfs_files(node);
+}
+
+
+/**
+ * dgrp_dump() -- prints memory for debugging purposes.
+ * @mem: Memory location which should be printed to the console
+ * @len: Number of bytes to be dumped
+ */
+static void dgrp_dump(u8 *mem, int len)
+{
+	int i;
+
+	pr_debug("dgrp dump length = %d, data = ", len);
+	for (i = 0; i < len; ++i)
+		pr_debug("%.2x ", mem[i]);
+	pr_debug("\n");
+}
+
+/**
+ * dgrp_read_data_block() -- Read a data block
+ * @ch: struct ch_struct *
+ * @flipbuf: u8 *
+ * @flipbuf_size: size of flipbuf
+ */
+static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf,
+				 int flipbuf_size)
+{
+	int t;
+	int n;
+
+	if (flipbuf_size <= 0)
+		return;
+
+	t = RBUF_MAX - ch->ch_rout;
+	n = flipbuf_size;
+
+	if (n >= t) {
+		memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, t);
+		flipbuf += t;
+		n -= t;
+		ch->ch_rout = 0;
+	}
+
+	memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, n);
+	flipbuf += n;
+	ch->ch_rout += n;
+}
+
+
+/**
+ * dgrp_input() -- send data to the line disipline
+ * @ch: pointer to channel struct
+ *
+ * Copys the rbuf to the flipbuf and sends to line discipline.
+ * Sends input buffer data to the line discipline.
+ *
+ * There are several modes to consider here:
+ *    rawreadok, tty->real_raw, and IF_PARMRK
+ */
+static void dgrp_input(struct ch_struct *ch)
+{
+	struct nd_struct *nd;
+	struct tty_struct *tty;
+	int remain;
+	int data_len;
+	int len;
+	int flip_len;
+	int tty_count;
+	ulong lock_flags;
+	struct tty_ldisc *ld;
+	u8  *myflipbuf;
+	u8  *myflipflagbuf;
+
+	if (!ch)
+		return;
+
+	nd = ch->ch_nd;
+
+	if (!nd)
+		return;
+
+	spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+	myflipbuf = nd->nd_inputbuf;
+	myflipflagbuf = nd->nd_inputflagbuf;
+
+	if (!ch->ch_open_count) {
+		ch->ch_rout = ch->ch_rin;
+		goto out;
+	}
+
+	if (ch->ch_tun.un_flag & UN_CLOSING) {
+		ch->ch_rout = ch->ch_rin;
+		goto out;
+	}
+
+	tty = (ch->ch_tun).un_tty;
+
+
+	if (!tty || tty->magic != TTY_MAGIC) {
+		ch->ch_rout = ch->ch_rin;
+		goto out;
+	}
+
+	tty_count = tty->count;
+	if (!tty_count) {
+		ch->ch_rout = ch->ch_rin;
+		goto out;
+	}
+
+	if (tty->closing || test_bit(TTY_CLOSING, &tty->flags)) {
+		ch->ch_rout = ch->ch_rin;
+		goto out;
+	}
+
+	spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+	/* Decide how much data we can send into the tty layer */
+	if (dgrp_rawreadok && tty->real_raw)
+		flip_len = MYFLIPLEN;
+	else
+		flip_len = TTY_FLIPBUF_SIZE;
+
+	/* data_len should be the number of chars that we read in */
+	data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK;
+	remain = data_len;
+
+	/* len is the amount of data we are going to transfer here */
+	len = min(data_len, flip_len);
+
+	/* take into consideration length of ldisc */
+	len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt);
+
+	ld = tty_ldisc_ref(tty);
+
+	/*
+	 * If we were unable to get a reference to the ld,
+	 * don't flush our buffer, and act like the ld doesn't
+	 * have any space to put the data right now.
+	 */
+	if (!ld) {
+		len = 0;
+	} else if (!ld->ops->receive_buf) {
+		spin_lock_irqsave(&nd->nd_lock, lock_flags);
+		ch->ch_rout = ch->ch_rin;
+		spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+		len = 0;
+	}
+
+	/* Check DPA flow control */
+	if ((nd->nd_dpa_debug) &&
+	    (nd->nd_dpa_flag & DPA_WAIT_SPACE) &&
+	    (nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty))))
+		len = 0;
+
+	if ((len) && !(ch->ch_flag & CH_RXSTOP)) {
+
+		dgrp_read_data_block(ch, myflipbuf, len);
+
+		/*
+		 * In high performance mode, we don't have to update
+		 * flag_buf or any of the counts or pointers into flip buf.
+		 */
+		if (!dgrp_rawreadok || !tty->real_raw) {
+			if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty))
+				parity_scan(ch, myflipbuf, myflipflagbuf, &len);
+			else
+				memset(myflipflagbuf, TTY_NORMAL, len);
+		}
+
+		if ((nd->nd_dpa_debug) &&
+		    (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty)))))
+			dgrp_dpa_data(nd, 1, myflipbuf, len);
+
+		/*
+		 * If we're doing raw reads, jam it right into the
+		 * line disc bypassing the flip buffers.
+		 */
+		if (dgrp_rawreadok && tty->real_raw)
+			ld->ops->receive_buf(tty, myflipbuf, NULL, len);
+		else {
+			len = tty_buffer_request_room(tty, len);
+			tty_insert_flip_string_flags(tty, myflipbuf,
+						     myflipflagbuf, len);
+
+			/* Tell the tty layer its okay to "eat" the data now */
+			tty_flip_buffer_push(tty);
+		}
+
+		ch->ch_rxcount += len;
+	}
+
+	if (ld)
+		tty_ldisc_deref(ld);
+
+	/*
+	 * Wake up any sleepers (maybe dgrp close) that might be waiting
+	 * for a channel flag state change.
+	 */
+	wake_up_interruptible(&ch->ch_flag_wait);
+	return;
+
+out:
+	spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+}
+
+
+/*
+ *  parity_scan
+ *
+ *  Loop to inspect each single character or 0xFF escape.
+ *
+ *  if PARMRK & ~DOSMODE:
+ *     0xFF  0xFF           Normal 0xFF character, escaped
+ *                          to eliminate confusion.
+ *     0xFF  0x00  0x00     Break
+ *     0xFF  0x00  CC       Error character CC.
+ *     CC                   Normal character CC.
+ *
+ *  if PARMRK & DOSMODE:
+ *     0xFF  0x18  0x00     Break
+ *     0xFF  0x08  0x00     Framing Error
+ *     0xFF  0x04  0x00     Parity error
+ *     0xFF  0x0C  0x00     Both Framing and Parity error
+ *
+ *  TODO:  do we need to do the XMODEM, XOFF, XON, XANY processing??
+ *         as per protocol
+ */
+static void parity_scan(struct ch_struct *ch, unsigned char *cbuf,
+			unsigned char *fbuf, int *len)
+{
+	int l = *len;
+	int count = 0;
+	int DOS = ((ch->ch_iflag & IF_DOSMODE) == 0 ? 0 : 1);
+	unsigned char *cout; /* character buffer */
+	unsigned char *fout; /* flag buffer */
+	unsigned char *in;
+	unsigned char c;
+
+	in = cbuf;
+	cout = cbuf;
+	fout = fbuf;
+
+	while (l--) {
+		c = *in;
+		in++;
+
+		switch (ch->ch_pscan_state) {
+		default:
+			/* reset to sanity and fall through */
+			ch->ch_pscan_state = 0 ;
+
+		case 0:
+			/* No FF seen yet */
+			if (c == 0xff) /* delete this character from stream */
+				ch->ch_pscan_state = 1;
+			else {
+				*cout++ = c;
+				*fout++ = TTY_NORMAL;
+				count += 1;
+			}
+			break;
+
+		case 1:
+			/* first FF seen */
+			if (c == 0xff) {
+				/* doubled ff, transform to single ff */
+				*cout++ = c;
+				*fout++ = TTY_NORMAL;
+				count += 1;
+				ch->ch_pscan_state = 0;
+			} else {
+				/* save value examination in next state */
+				ch->ch_pscan_savechar = c;
+				ch->ch_pscan_state = 2;
+			}
+			break;
+
+		case 2:
+			/* third character of ff sequence */
+			*cout++ = c;
+			if (DOS) {
+				if (ch->ch_pscan_savechar & 0x10)
+					*fout++ = TTY_BREAK;
+				else if (ch->ch_pscan_savechar & 0x08)
+					*fout++ = TTY_FRAME;
+				else
+					/*
+					 * either marked as a parity error,
+					 * indeterminate, or not in DOSMODE
+					 * call it a parity error
+					 */
+					*fout++ = TTY_PARITY;
+			} else {
+				/* case FF XX ?? where XX is not 00 */
+				if (ch->ch_pscan_savechar & 0xff) {
+					/* this should not happen */
+					pr_info("%s: parity_scan: error unexpected byte\n",
+						__func__);
+					*fout++ = TTY_PARITY;
+				}
+				/* case FF 00 XX where XX is not 00 */
+				else if (c == 0xff)
+					*fout++ = TTY_PARITY;
+				/* case FF 00 00 */
+				else
+					*fout++ = TTY_BREAK;
+
+			}
+			count += 1;
+			ch->ch_pscan_state = 0;
+		}
+	}
+	*len = count;
+}
+
+
+/**
+ * dgrp_net_idle() -- Idle the network connection
+ * @nd: pointer to node structure to idle
+ */
+static void dgrp_net_idle(struct nd_struct *nd)
+{
+	struct ch_struct *ch;
+	int i;
+
+	nd->nd_tx_work = 1;
+
+	nd->nd_state = NS_IDLE;
+	nd->nd_flag = 0;
+
+	for (i = nd->nd_seq_out; ; i = (i + 1) & SEQ_MASK) {
+		if (!nd->nd_seq_wait[i]) {
+			nd->nd_seq_wait[i] = 0;
+			wake_up_interruptible(&nd->nd_seq_wque[i]);
+		}
+
+		if (i == nd->nd_seq_in)
+			break;
+	}
+
+	nd->nd_seq_out = nd->nd_seq_in;
+
+	nd->nd_unack = 0;
+	nd->nd_remain = 0;
+
+	nd->nd_tx_module = 0x10;
+	nd->nd_rx_module = 0x00;
+
+	for (i = 0, ch = nd->nd_chan; i < CHAN_MAX; i++, ch++) {
+		ch->ch_state = CS_IDLE;
+
+		ch->ch_otype = 0;
+		ch->ch_otype_waiting = 0;
+	}
+}
+
+/*
+ *  Increase the number of channels, waking up any
+ *  threads that might be waiting for the channels
+ *  to appear.
+ */
+static void increase_channel_count(struct nd_struct *nd, int n)
+{
+	struct ch_struct *ch;
+	struct device *classp;
+	char name[DEVICE_NAME_SIZE];
+	int ret;
+	u8 *buf;
+	int i;
+
+	for (i = nd->nd_chan_count; i < n; ++i) {
+		ch = nd->nd_chan + i;
+
+		/* FIXME: return a useful error instead! */
+		buf = kmalloc(TBUF_MAX, GFP_KERNEL);
+		if (!buf)
+			return;
+
+		if (ch->ch_tbuf)
+			pr_info_ratelimited("%s - ch_tbuf was not NULL\n",
+					    __func__);
+
+		ch->ch_tbuf = buf;
+
+		buf = kmalloc(RBUF_MAX, GFP_KERNEL);
+		if (!buf)
+			return;
+
+		if (ch->ch_rbuf)
+			pr_info("%s - ch_rbuf was not NULL\n",
+				__func__);
+		ch->ch_rbuf = buf;
+
+		classp = tty_port_register_device(&ch->port,
+						  nd->nd_serial_ttdriver, i,
+						  NULL);
+
+		ch->ch_tun.un_sysfs = classp;
+		snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i);
+
+		dgrp_create_tty_sysfs(&ch->ch_tun, classp);
+		ret = sysfs_create_link(&nd->nd_class_dev->kobj,
+					&classp->kobj, name);
+
+		/* NOTE: We don't support "cu" devices anymore,
+		 * so you will notice we don't register them
+		 * here anymore. */
+		if (dgrp_register_prdevices) {
+			classp = tty_register_device(nd->nd_xprint_ttdriver,
+						     i, NULL);
+			ch->ch_pun.un_sysfs = classp;
+			snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i);
+
+			dgrp_create_tty_sysfs(&ch->ch_pun, classp);
+			ret = sysfs_create_link(&nd->nd_class_dev->kobj,
+						&classp->kobj, name);
+		}
+
+		nd->nd_chan_count = i + 1;
+		wake_up_interruptible(&ch->ch_flag_wait);
+	}
+}
+
+/*
+ * Decrease the number of channels, and wake up any threads that might
+ * be waiting on the channels that vanished.
+ */
+static void decrease_channel_count(struct nd_struct *nd, int n)
+{
+	struct ch_struct *ch;
+	char name[DEVICE_NAME_SIZE];
+	int i;
+
+	for (i = nd->nd_chan_count - 1; i >= n; --i) {
+		ch = nd->nd_chan + i;
+
+		/*
+		 *  Make any open ports inoperative.
+		 */
+		ch->ch_state = CS_IDLE;
+
+		ch->ch_otype = 0;
+		ch->ch_otype_waiting = 0;
+
+		/*
+		 *  Only "HANGUP" if we care about carrier
+		 *  transitions and we are already open.
+		 */
+		if (ch->ch_open_count != 0) {
+			ch->ch_flag |= CH_HANGUP;
+			dgrp_carrier(ch);
+		}
+
+		/*
+		 * Unlike the CH_HANGUP flag above, use another
+		 * flag to indicate to the RealPort state machine
+		 * that this port has disappeared.
+		 */
+		if (ch->ch_open_count != 0)
+			ch->ch_flag |= CH_PORT_GONE;
+
+		wake_up_interruptible(&ch->ch_flag_wait);
+
+		nd->nd_chan_count = i;
+
+		kfree(ch->ch_tbuf);
+		ch->ch_tbuf = NULL;
+
+		kfree(ch->ch_rbuf);
+		ch->ch_rbuf = NULL;
+
+		nd->nd_chan_count = i;
+
+		dgrp_remove_tty_sysfs(ch->ch_tun.un_sysfs);
+		snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i);
+		sysfs_remove_link(&nd->nd_class_dev->kobj, name);
+		tty_unregister_device(nd->nd_serial_ttdriver, i);
+
+		/*
+		 * NOTE: We don't support "cu" devices anymore, so don't
+		 * unregister them here anymore.
+		 */
+
+		if (dgrp_register_prdevices) {
+			dgrp_remove_tty_sysfs(ch->ch_pun.un_sysfs);
+			snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i);
+			sysfs_remove_link(&nd->nd_class_dev->kobj, name);
+			tty_unregister_device(nd->nd_xprint_ttdriver, i);
+		}
+	}
+}
+
+/**
+ * dgrp_chan_count() -- Adjust the node channel count.
+ * @nd: pointer to a node structure
+ * @n: new value for channel count
+ *
+ * Adjusts the node channel count.  If new ports have appeared, it tries
+ * to signal those processes that might have been waiting for ports to
+ * appear.  If ports have disappeared it tries to signal those processes
+ * that might be hung waiting for a response for the now non-existant port.
+ */
+static void dgrp_chan_count(struct nd_struct *nd, int n)
+{
+	if (n == nd->nd_chan_count)
+		return;
+
+	if (n > nd->nd_chan_count)
+		increase_channel_count(nd, n);
+
+	if (n < nd->nd_chan_count)
+		decrease_channel_count(nd, n);
+}
+
+/**
+ * dgrp_monitor() -- send data to the device monitor queue
+ * @nd: pointer to a node structure
+ * @buf: data to copy to the monitoring buffer
+ * @len: number of bytes to transfer to the buffer
+ *
+ * Called by the net device routines to send data to the device
+ * monitor queue.  If the device monitor buffer is too full to
+ * accept the data, it waits until the buffer is ready.
+ */
+static void dgrp_monitor(struct nd_struct *nd, u8 *buf, int len)
+{
+	int n;
+	int r;
+	int rtn;
+
+	/*
+	 *  Grab monitor lock.
+	 */
+	down(&nd->nd_mon_semaphore);
+
+	/*
+	 *  Loop while data remains.
+	 */
+	while ((len > 0) && (nd->nd_mon_buf)) {
+		/*
+		 *  Determine the amount of available space left in the
+		 *  buffer.  If there's none, wait until some appears.
+		 */
+
+		n = (nd->nd_mon_out - nd->nd_mon_in - 1) & MON_MASK;
+
+		if (!n) {
+			nd->nd_mon_flag |= MON_WAIT_SPACE;
+
+			up(&nd->nd_mon_semaphore);
+
+			/*
+			 * Go to sleep waiting until the condition becomes true.
+			 */
+			rtn = wait_event_interruptible(nd->nd_mon_wqueue,
+						       ((nd->nd_mon_flag & MON_WAIT_SPACE) == 0));
+
+/* FIXME: really ignore rtn? */
+
+			/*
+			 *  We can't exit here if we receive a signal, since
+			 *  to do so would trash the debug stream.
+			 */
+
+			down(&nd->nd_mon_semaphore);
+
+			continue;
+		}
+
+		/*
+		 * Copy as much data as will fit.
+		 */
+
+		if (n > len)
+			n = len;
+
+		r = MON_MAX - nd->nd_mon_in;
+
+		if (r <= n) {
+			memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, r);
+
+			n -= r;
+
+			nd->nd_mon_in = 0;
+
+			buf += r;
+			len -= r;
+		}
+
+		memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, n);
+
+		nd->nd_mon_in += n;
+
+		buf += n;
+		len -= n;
+
+		if (nd->nd_mon_in >= MON_MAX)
+			pr_info_ratelimited("%s - nd_mon_in (%i) >= MON_MAX\n",
+					    __func__, nd->nd_mon_in);
+
+		/*
+		 *  Wakeup any thread waiting for data
+		 */
+
+		if (nd->nd_mon_flag & MON_WAIT_DATA) {
+			nd->nd_mon_flag &= ~MON_WAIT_DATA;
+			wake_up_interruptible(&nd->nd_mon_wqueue);
+		}
+	}
+
+	/*
+	 *  Release the monitor lock.
+	 */
+	up(&nd->nd_mon_semaphore);
+}
+
+/**
+ * dgrp_encode_time() -- Encodes rpdump time into a 4-byte quantity.
+ * @nd: pointer to a node structure
+ * @buf: destination buffer
+ *
+ * Encodes "rpdump" time into a 4-byte quantity.  Time is measured since
+ * open.
+ */
+static void dgrp_encode_time(struct nd_struct *nd, u8 *buf)
+{
+	ulong t;
+
+	/*
+	 *  Convert time in HZ since open to time in milliseconds
+	 *  since open.
+	 */
+	t = jiffies - nd->nd_mon_lbolt;
+	t = 1000 * (t / HZ) + 1000 * (t % HZ) / HZ;
+
+	put_unaligned_be32((uint)(t & 0xffffffff), buf);
+}
+
+
+
+/**
+ * dgrp_monitor_message() -- Builds a rpdump style message.
+ * @nd: pointer to a node structure
+ * @message: destination buffer
+ */
+static void dgrp_monitor_message(struct nd_struct *nd, char *message)
+{
+	u8 header[7];
+	int n;
+
+	header[0] = RPDUMP_MESSAGE;
+
+	dgrp_encode_time(nd, header + 1);
+
+	n = strlen(message);
+
+	put_unaligned_be16(n, header + 5);
+
+	dgrp_monitor(nd, header, sizeof(header));
+	dgrp_monitor(nd, (u8 *) message, n);
+}
+
+
+
+/**
+ * dgrp_monitor_reset() -- Note a reset in the monitoring buffer.
+ * @nd: pointer to a node structure
+ */
+static void dgrp_monitor_reset(struct nd_struct *nd)
+{
+	u8 header[5];
+
+	header[0] = RPDUMP_RESET;
+
+	dgrp_encode_time(nd, header + 1);
+
+	dgrp_monitor(nd, header, sizeof(header));
+}
+
+/**
+ * dgrp_monitor_data() -- builds a monitor data packet
+ * @nd: pointer to a node structure
+ * @type: type of message to be logged
+ * @buf: data to be logged
+ * @size: number of bytes in the buffer
+ */
+static void dgrp_monitor_data(struct nd_struct *nd, u8 type, u8 *buf, int size)
+{
+	u8 header[7];
+
+	header[0] = type;
+
+	dgrp_encode_time(nd, header + 1);
+
+	put_unaligned_be16(size, header + 5);
+
+	dgrp_monitor(nd, header, sizeof(header));
+	dgrp_monitor(nd, buf, size);
+}
+
+static int alloc_nd_buffers(struct nd_struct *nd)
+{
+
+	nd->nd_iobuf = NULL;
+	nd->nd_writebuf = NULL;
+	nd->nd_inputbuf = NULL;
+	nd->nd_inputflagbuf = NULL;
+
+	/*
+	 *  Allocate the network read/write buffer.
+	 */
+	nd->nd_iobuf = kzalloc(UIO_MAX + 10, GFP_KERNEL);
+	if (!nd->nd_iobuf)
+		goto out_err;
+
+	/*
+	 * Allocate a buffer for doing the copy from user space to
+	 * kernel space in the write routines.
+	 */
+	nd->nd_writebuf = kzalloc(WRITEBUFLEN, GFP_KERNEL);
+	if (!nd->nd_writebuf)
+		goto out_err;
+
+	/*
+	 * Allocate a buffer for doing the copy from kernel space to
+	 * tty buffer space in the read routines.
+	 */
+	nd->nd_inputbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
+	if (!nd->nd_inputbuf)
+		goto out_err;
+
+	/*
+	 * Allocate a buffer for doing the copy from kernel space to
+	 * tty buffer space in the read routines.
+	 */
+	nd->nd_inputflagbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
+	if (!nd->nd_inputflagbuf)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	kfree(nd->nd_iobuf);
+	kfree(nd->nd_writebuf);
+	kfree(nd->nd_inputbuf);
+	kfree(nd->nd_inputflagbuf);
+	return -ENOMEM;
+}
+
+/*
+ * dgrp_net_open() -- Open the NET device for a particular PortServer
+ */
+static int dgrp_net_open(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+	struct proc_dir_entry *de;
+	ulong  lock_flags;
+	int rtn;
+
+	rtn = try_module_get(THIS_MODULE);
+	if (!rtn)
+		return -EAGAIN;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		rtn = -EPERM;
+		goto done;
+	}
+
+	/*
+	 *  Make sure that the "private_data" field hasn't already been used.
+	 */
+	if (file->private_data) {
+		rtn = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 *  Get the node pointer, and fail if it doesn't exist.
+	 */
+	de = PDE(inode);
+	if (!de) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	nd = (struct nd_struct *) de->data;
+	if (!nd) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	file->private_data = (void *) nd;
+
+	/*
+	 *  Grab the NET lock.
+	 */
+	down(&nd->nd_net_semaphore);
+
+	if (nd->nd_state != NS_CLOSED) {
+		rtn = -EBUSY;
+		goto unlock;
+	}
+
+	/*
+	 *  Initialize the link speed parameters.
+	 */
+
+	nd->nd_link.lk_fast_rate = UIO_MAX;
+	nd->nd_link.lk_slow_rate = UIO_MAX;
+
+	nd->nd_link.lk_fast_delay = 1000;
+	nd->nd_link.lk_slow_delay = 1000;
+
+	nd->nd_link.lk_header_size = 46;
+
+
+	rtn = alloc_nd_buffers(nd);
+	if (rtn)
+		goto unlock;
+
+	/*
+	 *  The port is now open, so move it to the IDLE state
+	 */
+	dgrp_net_idle(nd);
+
+	nd->nd_tx_time = jiffies;
+
+	/*
+	 *  If the polling routing is not running, start it running here
+	 */
+	spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+	if (!dgrp_poll_data.node_active_count) {
+		dgrp_poll_data.node_active_count = 2;
+		dgrp_poll_data.timer.expires = jiffies +
+			dgrp_poll_tick * HZ / 1000;
+		add_timer(&dgrp_poll_data.timer);
+	}
+
+	spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+	dgrp_monitor_message(nd, "Net Open");
+
+unlock:
+	/*
+	 *  Release the NET lock.
+	 */
+	up(&nd->nd_net_semaphore);
+
+done:
+	if (rtn)
+		module_put(THIS_MODULE);
+
+	return rtn;
+}
+
+/* dgrp_net_release() -- close the NET device for a particular PortServer */
+static int dgrp_net_release(struct inode *inode, struct file *file)
+{
+	struct nd_struct *nd;
+	ulong  lock_flags;
+
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		goto done;
+
+/* TODO : historical locking placeholder */
+/*
+ *  In the HPUX version of the RealPort driver (which served as a basis
+ *  for this driver) this locking code was used.  Saved if ever we need
+ *  to review the locking under Linux.
+ */
+/*	spinlock(&nd->nd_lock); */
+
+
+	/*
+	 *  Grab the NET lock.
+	 */
+	down(&nd->nd_net_semaphore);
+
+	/*
+	 *  Before "closing" the internal connection, make sure all
+	 *  ports are "idle".
+	 */
+	dgrp_net_idle(nd);
+
+	nd->nd_state = NS_CLOSED;
+	nd->nd_flag = 0;
+
+	/*
+	 *  TODO ... must the wait queue be reset on close?
+	 *  should any pending waiters be reset?
+	 *  Let's decide to assert that the waitq is empty... and see
+	 *  how soon we break.
+	 */
+	if (waitqueue_active(&nd->nd_tx_waitq))
+		pr_info("%s - expected waitqueue_active to be false\n",
+			__func__);
+
+	nd->nd_send = 0;
+
+	kfree(nd->nd_iobuf);
+	nd->nd_iobuf = NULL;
+
+/* TODO : historical locking placeholder */
+/*
+ *  In the HPUX version of the RealPort driver (which served as a basis
+ *  for this driver) this locking code was used.  Saved if ever we need
+ *  to review the locking under Linux.
+ */
+/*	spinunlock( &nd->nd_lock ); */
+
+
+	kfree(nd->nd_writebuf);
+	nd->nd_writebuf = NULL;
+
+	kfree(nd->nd_inputbuf);
+	nd->nd_inputbuf = NULL;
+
+	kfree(nd->nd_inputflagbuf);
+	nd->nd_inputflagbuf = NULL;
+
+/* TODO : historical locking placeholder */
+/*
+ *  In the HPUX version of the RealPort driver (which served as a basis
+ *  for this driver) this locking code was used.  Saved if ever we need
+ *  to review the locking under Linux.
+ */
+/*	spinlock(&nd->nd_lock); */
+
+	/*
+	 *  Set the active port count to zero.
+	 */
+	dgrp_chan_count(nd, 0);
+
+/* TODO : historical locking placeholder */
+/*
+ *  In the HPUX version of the RealPort driver (which served as a basis
+ *  for this driver) this locking code was used.  Saved if ever we need
+ *  to review the locking under Linux.
+ */
+/*	spinunlock(&nd->nd_lock); */
+
+	/*
+	 *  Release the NET lock.
+	 */
+	up(&nd->nd_net_semaphore);
+
+	/*
+	 *  Cause the poller to stop scheduling itself if this is
+	 *  the last active node.
+	 */
+	spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+	if (dgrp_poll_data.node_active_count == 2) {
+		del_timer(&dgrp_poll_data.timer);
+		dgrp_poll_data.node_active_count = 0;
+	}
+
+	spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+done:
+	down(&nd->nd_net_semaphore);
+
+	dgrp_monitor_message(nd, "Net Close");
+
+	up(&nd->nd_net_semaphore);
+
+	module_put(THIS_MODULE);
+	file->private_data = NULL;
+	return 0;
+}
+
+/* used in dgrp_send to setup command header */
+static inline u8 *set_cmd_header(u8 *b, u8 port, u8 cmd)
+{
+	*b++ = 0xb0 + (port & 0x0f);
+	*b++ = cmd;
+	return b;
+}
+
+/**
+ * dgrp_send() -- build a packet for transmission to the server
+ * @nd: pointer to a node structure
+ * @tmax: maximum bytes to transmit
+ *
+ * returns number of bytes sent
+ */
+static int dgrp_send(struct nd_struct *nd, long tmax)
+{
+	struct ch_struct *ch = nd->nd_chan;
+	u8 *b;
+	u8 *buf;
+	u8 *mbuf;
+	u8 port;
+	int mod;
+	long send;
+	int maxport;
+	long lastport = -1;
+	ushort rwin;
+	long in;
+	ushort n;
+	long t;
+	long ttotal;
+	long tchan;
+	long tsend;
+	ushort tsafe;
+	long work;
+	long send_sync;
+	long wanted_sync_port = -1;
+	ushort tdata[CHAN_MAX];
+	long used_buffer;
+
+	mbuf = nd->nd_iobuf + UIO_BASE;
+	buf = b = mbuf;
+
+	send_sync = nd->nd_link.lk_slow_rate < UIO_MAX;
+
+	ttotal = 0;
+	tchan = 0;
+
+	memset(tdata, 0, sizeof(tdata));
+
+
+	/*
+	 * If there are any outstanding requests to be serviced,
+	 * service them here.
+	 */
+	if (nd->nd_send & NR_PASSWORD) {
+
+		/*
+		 *  Send Password response.
+		 */
+
+		b[0] = 0xfc;
+		b[1] = 0x20;
+		put_unaligned_be16(strlen(nd->password), b + 2);
+		b += 4;
+		b += strlen(nd->password);
+		nd->nd_send &= ~(NR_PASSWORD);
+	}
+
+
+	/*
+	 *  Loop over all modules to generate commands, and determine
+	 *  the amount of data queued for transmit.
+	 */
+
+	for (mod = 0, port = 0; port < nd->nd_chan_count; mod++) {
+		/*
+		 *  If this is not the current module, enter a module select
+		 *  code in the buffer.
+		 */
+
+		if (mod != nd->nd_tx_module)
+			mbuf = ++b;
+
+		/*
+		 *  Loop to process one module.
+		 */
+
+		maxport = port + 16;
+
+		if (maxport > nd->nd_chan_count)
+			maxport = nd->nd_chan_count;
+
+		for (; port < maxport; port++, ch++) {
+			/*
+			 *  Switch based on channel state.
+			 */
+
+			switch (ch->ch_state) {
+			/*
+			 *  Send requests when the port is closed, and there
+			 *  are no Open, Close or Cancel requests expected.
+			 */
+
+			case CS_IDLE:
+				/*
+				 * Wait until any open error code
+				 * has been delivered to all
+				 * associated ports.
+				 */
+
+				if (ch->ch_open_error) {
+					if (ch->ch_wait_count[ch->ch_otype]) {
+						work = 1;
+						break;
+					}
+
+					ch->ch_open_error = 0;
+				}
+
+				/*
+				 *  Wait until the channel HANGUP flag is reset
+				 *  before sending the first open.  We can only
+				 *  get to this state after a server disconnect.
+				 */
+
+				if ((ch->ch_flag & CH_HANGUP) != 0)
+					break;
+
+				/*
+				 *  If recovering from a TCP disconnect, or if
+				 *  there is an immediate open pending, send an
+				 *  Immediate Open request.
+				 */
+				if ((ch->ch_flag & CH_PORT_GONE) ||
+				    ch->ch_wait_count[OTYPE_IMMEDIATE] != 0) {
+					b = set_cmd_header(b, port, 10);
+					*b++ = 0;
+
+					ch->ch_state = CS_WAIT_OPEN;
+					ch->ch_otype = OTYPE_IMMEDIATE;
+					break;
+				}
+
+	/*
+	 *  If there is no Persistent or Incoming Open on the wait
+	 *  list in the server, and a thread is waiting for a
+	 *  Persistent or Incoming Open, send a Persistent or Incoming
+	 *  Open Request.
+	 */
+				if (ch->ch_otype_waiting == 0) {
+					if (ch->ch_wait_count[OTYPE_PERSISTENT] != 0) {
+						b = set_cmd_header(b, port, 10);
+						*b++ = 1;
+
+						ch->ch_state = CS_WAIT_OPEN;
+						ch->ch_otype = OTYPE_PERSISTENT;
+					} else if (ch->ch_wait_count[OTYPE_INCOMING] != 0) {
+						b = set_cmd_header(b, port, 10);
+						*b++ = 2;
+
+						ch->ch_state = CS_WAIT_OPEN;
+						ch->ch_otype = OTYPE_INCOMING;
+					}
+					break;
+				}
+
+				/*
+				 *  If a Persistent or Incoming Open is pending in
+				 *  the server, but there is no longer an open
+				 *  thread waiting for it, cancel the request.
+				 */
+
+				if (ch->ch_wait_count[ch->ch_otype_waiting] == 0) {
+					b = set_cmd_header(b, port, 10);
+					*b++ = 4;
+
+					ch->ch_state = CS_WAIT_CANCEL;
+					ch->ch_otype = ch->ch_otype_waiting;
+				}
+				break;
+
+				/*
+				 *  Send port parameter queries.
+				 */
+			case CS_SEND_QUERY:
+				/*
+				 *  Clear out all FEP state that might remain
+				 *  from the last connection.
+				 */
+
+				ch->ch_flag |= CH_PARAM;
+
+				ch->ch_flag &= ~CH_RX_FLUSH;
+
+				ch->ch_expect = 0;
+
+				ch->ch_s_tin   = 0;
+				ch->ch_s_tpos  = 0;
+				ch->ch_s_tsize = 0;
+				ch->ch_s_treq  = 0;
+				ch->ch_s_elast = 0;
+
+				ch->ch_s_rin   = 0;
+				ch->ch_s_rwin  = 0;
+				ch->ch_s_rsize = 0;
+
+				ch->ch_s_tmax  = 0;
+				ch->ch_s_ttime = 0;
+				ch->ch_s_rmax  = 0;
+				ch->ch_s_rtime = 0;
+				ch->ch_s_rlow  = 0;
+				ch->ch_s_rhigh = 0;
+
+				ch->ch_s_brate = 0;
+				ch->ch_s_iflag = 0;
+				ch->ch_s_cflag = 0;
+				ch->ch_s_oflag = 0;
+				ch->ch_s_xflag = 0;
+
+				ch->ch_s_mout  = 0;
+				ch->ch_s_mflow = 0;
+				ch->ch_s_mctrl = 0;
+				ch->ch_s_xon   = 0;
+				ch->ch_s_xoff  = 0;
+				ch->ch_s_lnext = 0;
+				ch->ch_s_xxon  = 0;
+				ch->ch_s_xxoff = 0;
+
+				/* Send Sequence Request */
+				b = set_cmd_header(b, port, 14);
+
+				/* Configure Event Conditions Packet */
+				b = set_cmd_header(b, port, 42);
+				put_unaligned_be16(0x02c0, b);
+				b += 2;
+				*b++ = (DM_DTR | DM_RTS | DM_CTS |
+					DM_DSR | DM_RI | DM_CD);
+
+				/* Send Status Request */
+				b = set_cmd_header(b, port, 16);
+
+				/* Send Buffer Request  */
+				b = set_cmd_header(b, port, 20);
+
+				/* Send Port Capability Request */
+				b = set_cmd_header(b, port, 22);
+
+				ch->ch_expect = (RR_SEQUENCE |
+						 RR_STATUS  |
+						 RR_BUFFER |
+						 RR_CAPABILITY);
+
+				ch->ch_state = CS_WAIT_QUERY;
+
+				/* Raise modem signals */
+				b = set_cmd_header(b, port, 44);
+
+				if (ch->ch_flag & CH_PORT_GONE)
+					ch->ch_s_mout = ch->ch_mout;
+				else
+					ch->ch_s_mout = ch->ch_mout = DM_DTR | DM_RTS;
+
+				*b++ = ch->ch_mout;
+				*b++ = ch->ch_s_mflow = 0;
+				*b++ = ch->ch_s_mctrl = ch->ch_mctrl = 0;
+
+				if (ch->ch_flag & CH_PORT_GONE)
+					ch->ch_flag &= ~CH_PORT_GONE;
+
+				break;
+
+			/*
+			 *  Handle normal open and ready mode.
+			 */
+
+			case CS_READY:
+
+				/*
+				 *  If the port is not open, and there are no
+				 *  no longer any ports requesting an open,
+				 *  then close the port.
+				 */
+
+				if (ch->ch_open_count == 0 &&
+				    ch->ch_wait_count[ch->ch_otype] == 0) {
+					goto send_close;
+				}
+
+	/*
+	 *  Process waiting input.
+	 *
+	 *  If there is no one to read it, discard the data.
+	 *
+	 *  Otherwise if we are not in fastcook mode, or if there is a
+	 *  fastcook thread waiting for data, send the data to the
+	 *  line discipline.
+	 */
+				if (ch->ch_rin != ch->ch_rout) {
+					if (ch->ch_tun.un_open_count == 0 ||
+					     (ch->ch_tun.un_flag & UN_CLOSING) ||
+					    (ch->ch_cflag & CF_CREAD) == 0) {
+						ch->ch_rout = ch->ch_rin;
+					} else if ((ch->ch_flag & CH_FAST_READ) == 0 ||
+							ch->ch_inwait != 0) {
+						dgrp_input(ch);
+
+						if (ch->ch_rin != ch->ch_rout)
+							work = 1;
+					}
+				}
+
+				/*
+				 *  Handle receive flush, and changes to
+				 *  server port parameters.
+				 */
+
+				if (ch->ch_flag & (CH_RX_FLUSH | CH_PARAM)) {
+				/*
+				 *  If we are in receive flush mode,
+				 *  and enough data has gone by, reset
+				 *  receive flush mode.
+				 */
+					if (ch->ch_flag & CH_RX_FLUSH) {
+						if (((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) >
+						    ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK))
+							ch->ch_flag &= ~CH_RX_FLUSH;
+						else
+							work = 1;
+					}
+
+					/*
+					 *  Send TMAX, TTIME.
+					 */
+
+					if (ch->ch_s_tmax  != ch->ch_tmax ||
+					    ch->ch_s_ttime != ch->ch_ttime) {
+						b = set_cmd_header(b, port, 48);
+
+						ch->ch_s_tmax = ch->ch_tmax;
+						ch->ch_s_ttime = ch->ch_ttime;
+
+						put_unaligned_be16(ch->ch_s_tmax,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_ttime,
+								   b);
+						b += 2;
+					}
+
+					/*
+					 *  Send RLOW, RHIGH.
+					 */
+
+					if (ch->ch_s_rlow  != ch->ch_rlow ||
+					    ch->ch_s_rhigh != ch->ch_rhigh) {
+						b = set_cmd_header(b, port, 45);
+
+						ch->ch_s_rlow  = ch->ch_rlow;
+						ch->ch_s_rhigh = ch->ch_rhigh;
+
+						put_unaligned_be16(ch->ch_s_rlow,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_rhigh,
+								   b);
+						b += 2;
+					}
+
+					/*
+					 *  Send BRATE, CFLAG, IFLAG,
+					 *  OFLAG, XFLAG.
+					 */
+
+					if (ch->ch_s_brate != ch->ch_brate ||
+					    ch->ch_s_cflag != ch->ch_cflag ||
+					    ch->ch_s_iflag != ch->ch_iflag ||
+					    ch->ch_s_oflag != ch->ch_oflag ||
+					    ch->ch_s_xflag != ch->ch_xflag) {
+						b = set_cmd_header(b, port, 40);
+
+						ch->ch_s_brate = ch->ch_brate;
+						ch->ch_s_cflag = ch->ch_cflag;
+						ch->ch_s_iflag = ch->ch_iflag;
+						ch->ch_s_oflag = ch->ch_oflag;
+						ch->ch_s_xflag = ch->ch_xflag;
+
+						put_unaligned_be16(ch->ch_s_brate,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_cflag,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_iflag,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_oflag,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_xflag,
+								   b);
+						b += 2;
+					}
+
+					/*
+					 *  Send MOUT, MFLOW, MCTRL.
+					 */
+
+					if (ch->ch_s_mout  != ch->ch_mout  ||
+					    ch->ch_s_mflow != ch->ch_mflow ||
+					    ch->ch_s_mctrl != ch->ch_mctrl) {
+						b = set_cmd_header(b, port, 44);
+
+						*b++ = ch->ch_s_mout  = ch->ch_mout;
+						*b++ = ch->ch_s_mflow = ch->ch_mflow;
+						*b++ = ch->ch_s_mctrl = ch->ch_mctrl;
+					}
+
+					/*
+					 *  Send Flow control characters.
+					 */
+
+					if (ch->ch_s_xon   != ch->ch_xon   ||
+					    ch->ch_s_xoff  != ch->ch_xoff  ||
+					    ch->ch_s_lnext != ch->ch_lnext ||
+					    ch->ch_s_xxon  != ch->ch_xxon  ||
+					    ch->ch_s_xxoff != ch->ch_xxoff) {
+						b = set_cmd_header(b, port, 46);
+
+						*b++ = ch->ch_s_xon   = ch->ch_xon;
+						*b++ = ch->ch_s_xoff  = ch->ch_xoff;
+						*b++ = ch->ch_s_lnext = ch->ch_lnext;
+						*b++ = ch->ch_s_xxon  = ch->ch_xxon;
+						*b++ = ch->ch_s_xxoff = ch->ch_xxoff;
+					}
+
+					/*
+					 *  Send RMAX, RTIME.
+					 */
+
+					if (ch->ch_s_rmax != ch->ch_rmax ||
+					    ch->ch_s_rtime != ch->ch_rtime) {
+						b = set_cmd_header(b, port, 47);
+
+						ch->ch_s_rmax  = ch->ch_rmax;
+						ch->ch_s_rtime = ch->ch_rtime;
+
+						put_unaligned_be16(ch->ch_s_rmax,
+								   b);
+						b += 2;
+
+						put_unaligned_be16(ch->ch_s_rtime,
+								   b);
+						b += 2;
+					}
+
+					ch->ch_flag &= ~CH_PARAM;
+					wake_up_interruptible(&ch->ch_flag_wait);
+				}
+
+
+				/*
+				 *  Handle action commands.
+				 */
+
+				if (ch->ch_send != 0) {
+					/* int send = ch->ch_send & ~ch->ch_expect; */
+					send = ch->ch_send & ~ch->ch_expect;
+
+					/* Send character immediate */
+					if ((send & RR_TX_ICHAR) != 0) {
+						b = set_cmd_header(b, port, 60);
+
+						*b++ = ch->ch_xon;
+						ch->ch_expect |= RR_TX_ICHAR;
+					}
+
+					/* BREAK request */
+					if ((send & RR_TX_BREAK) != 0) {
+						if (ch->ch_break_time != 0) {
+							b = set_cmd_header(b, port, 61);
+							put_unaligned_be16(ch->ch_break_time,
+									   b);
+							b += 2;
+
+							ch->ch_expect |= RR_TX_BREAK;
+							ch->ch_break_time = 0;
+						} else {
+							ch->ch_send &= ~RR_TX_BREAK;
+							ch->ch_flag &= ~CH_TX_BREAK;
+							wake_up_interruptible(&ch->ch_flag_wait);
+						}
+					}
+
+					/*
+					 *  Flush input/output buffers.
+					 */
+
+					if ((send & (RR_RX_FLUSH | RR_TX_FLUSH)) != 0) {
+						b = set_cmd_header(b, port, 62);
+
+						*b++ = ((send & RR_TX_FLUSH) == 0 ? 1 :
+							(send & RR_RX_FLUSH) == 0 ? 2 : 3);
+
+						if (send & RR_RX_FLUSH) {
+							ch->ch_flush_seq = nd->nd_seq_in;
+							ch->ch_flag |= CH_RX_FLUSH;
+							work = 1;
+							send_sync = 1;
+							wanted_sync_port = port;
+						}
+
+						ch->ch_send &= ~(RR_RX_FLUSH | RR_TX_FLUSH);
+					}
+
+					/*  Pause input/output */
+					if ((send & (RR_RX_STOP | RR_TX_STOP)) != 0) {
+						b = set_cmd_header(b, port, 63);
+						*b = 0;
+
+						if ((send & RR_TX_STOP) != 0)
+							*b |= EV_OPU;
+
+						if ((send & RR_RX_STOP) != 0)
+							*b |= EV_IPU;
+
+						b++;
+
+						ch->ch_send &= ~(RR_RX_STOP | RR_TX_STOP);
+					}
+
+					/* Start input/output */
+					if ((send & (RR_RX_START | RR_TX_START)) != 0) {
+						b = set_cmd_header(b, port, 64);
+						*b = 0;
+
+						if ((send & RR_TX_START) != 0)
+							*b |= EV_OPU | EV_OPS | EV_OPX;
+
+						if ((send & RR_RX_START) != 0)
+							*b |= EV_IPU | EV_IPS;
+
+						b++;
+
+						ch->ch_send &= ~(RR_RX_START | RR_TX_START);
+					}
+				}
+
+
+				/*
+				 *  Send a window sequence to acknowledge received data.
+				 */
+
+				rwin = (ch->ch_s_rin +
+					((ch->ch_rout - ch->ch_rin - 1) & RBUF_MASK));
+
+				n = (rwin - ch->ch_s_rwin) & 0xffff;
+
+				if (n >= RBUF_MAX / 4) {
+					b[0] = 0xa0 + (port & 0xf);
+					ch->ch_s_rwin = rwin;
+					put_unaligned_be16(rwin, b + 1);
+					b += 3;
+				}
+
+				/*
+				 *  If the terminal is waiting on LOW
+				 *  water or EMPTY, and the condition
+				 *  is now satisfied, call the line
+				 *  discipline to put more data in the
+				 *  buffer.
+				 */
+
+				n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+				if ((ch->ch_tun.un_flag & (UN_EMPTY|UN_LOW)) != 0) {
+					if ((ch->ch_tun.un_flag & UN_LOW) != 0 ?
+					    (n <= TBUF_LOW) :
+					    (n == 0 && ch->ch_s_tpos == ch->ch_s_tin)) {
+						ch->ch_tun.un_flag &= ~(UN_EMPTY|UN_LOW);
+
+						if (waitqueue_active(&((ch->ch_tun.un_tty)->write_wait)))
+							wake_up_interruptible(&((ch->ch_tun.un_tty)->write_wait));
+						tty_wakeup(ch->ch_tun.un_tty);
+						n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+					}
+				}
+
+				/*
+				 * If the printer is waiting on LOW
+				 * water, TIME, EMPTY or PWAIT, and is
+				 * now ready to put more data in the
+				 * buffer, call the line discipline to
+				 * do the job.
+				 */
+
+				if (ch->ch_pun.un_open_count &&
+				    (ch->ch_pun.un_flag &
+				    (UN_EMPTY|UN_TIME|UN_LOW|UN_PWAIT)) != 0) {
+
+					if ((ch->ch_pun.un_flag & UN_LOW) != 0 ?
+					    (n <= TBUF_LOW) :
+					    (ch->ch_pun.un_flag & UN_TIME) != 0 ?
+					    ((jiffies - ch->ch_waketime) >= 0) :
+					    (n == 0 && ch->ch_s_tpos == ch->ch_s_tin) &&
+					    ((ch->ch_pun.un_flag & UN_EMPTY) != 0 ||
+					    ((ch->ch_tun.un_open_count &&
+					      ch->ch_tun.un_tty->ops->chars_in_buffer) ?
+					     (ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) == 0
+					     : 1
+					    )
+					    )) {
+						ch->ch_pun.un_flag &= ~(UN_EMPTY | UN_TIME | UN_LOW | UN_PWAIT);
+
+						if (waitqueue_active(&((ch->ch_pun.un_tty)->write_wait)))
+							wake_up_interruptible(&((ch->ch_pun.un_tty)->write_wait));
+						tty_wakeup(ch->ch_pun.un_tty);
+						n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+					} else if ((ch->ch_pun.un_flag & UN_TIME) != 0) {
+						work = 1;
+					}
+				}
+
+
+				/*
+				 *  Determine the max number of bytes
+				 *  this port can send, including
+				 *  packet header overhead.
+				 */
+
+				t = ((ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff);
+
+				if (n > t)
+					n = t;
+
+				if (n != 0) {
+					n += (n <= 8 ? 1 : n <= 255 ? 2 : 3);
+
+					tdata[tchan++] = n;
+					ttotal += n;
+				}
+				break;
+
+			/*
+			 *  Close the port.
+			 */
+
+send_close:
+			case CS_SEND_CLOSE:
+				b = set_cmd_header(b, port, 10);
+				if (ch->ch_otype == OTYPE_IMMEDIATE)
+					*b++ = 3;
+				else
+					*b++ = 4;
+
+				ch->ch_state = CS_WAIT_CLOSE;
+				break;
+
+			/*
+			 *  Wait for a previous server request.
+			 */
+
+			case CS_WAIT_OPEN:
+			case CS_WAIT_CANCEL:
+			case CS_WAIT_FAIL:
+			case CS_WAIT_QUERY:
+			case CS_WAIT_CLOSE:
+				break;
+
+			default:
+				pr_info("%s - unexpected channel state (%i)\n",
+					__func__, ch->ch_state);
+			}
+		}
+
+		/*
+		 *  If a module select code is needed, drop one in.  If space
+		 *  was reserved for one, but none is needed, recover the space.
+		 */
+
+		if (mod != nd->nd_tx_module) {
+			if (b != mbuf) {
+				mbuf[-1] = 0xf0 | mod;
+				nd->nd_tx_module = mod;
+			} else {
+				b--;
+			}
+		}
+	}
+
+	/*
+	 *  Adjust "tmax" so that under worst case conditions we do
+	 *  not overflow either the daemon buffer or the internal
+	 *  buffer in the loop that follows.   Leave a safe area
+	 *  of 64 bytes so we start getting asserts before we start
+	 *  losing data or clobbering memory.
+	 */
+
+	n = UIO_MAX - UIO_BASE;
+
+	if (tmax > n)
+		tmax = n;
+
+	tmax -= 64;
+
+	tsafe = tmax;
+
+	/*
+	 *  Allocate space for 5 Module Selects, 1 Sequence Request,
+	 *  and 1 Set TREQ for each active channel.
+	 */
+
+	tmax -= 5 + 3 + 4 * nd->nd_chan_count;
+
+	/*
+	 *  Further reduce "tmax" to the available transmit credit.
+	 *  Note that this is a soft constraint;  The transmit credit
+	 *  can go negative for a time and then recover.
+	 */
+
+	n = nd->nd_tx_deposit - nd->nd_tx_charge - nd->nd_link.lk_header_size;
+
+	if (tmax > n)
+		tmax = n;
+
+	/*
+	 *  Finally reduce tmax by the number of bytes already in
+	 *  the buffer.
+	 */
+
+	tmax -= b - buf;
+
+	/*
+	 *  Suspend data transmit unless every ready channel can send
+	 *  at least 1 character.
+	 */
+	if (tmax < 2 * nd->nd_chan_count) {
+		tsend = 1;
+
+	} else if (tchan > 1 && ttotal > tmax) {
+
+		/*
+		 *  If transmit is limited by the credit budget, find the
+		 *  largest number of characters we can send without driving
+		 *  the credit negative.
+		 */
+
+		long tm = tmax;
+		int tc = tchan;
+		int try;
+
+		tsend = tm / tc;
+
+		for (try = 0; try < 3; try++) {
+			int i;
+			int c = 0;
+
+			for (i = 0; i < tc; i++) {
+				if (tsend < tdata[i])
+					tdata[c++] = tdata[i];
+				else
+					tm -= tdata[i];
+			}
+
+			if (c == tc)
+				break;
+
+			tsend = tm / c;
+
+			if (c == 1)
+				break;
+
+			tc = c;
+		}
+
+		tsend = tm / nd->nd_chan_count;
+
+		if (tsend < 2)
+			tsend = 1;
+
+	} else {
+		/*
+		 *  If no budgetary constraints, or only one channel ready
+		 *  to send, set the character limit to the remaining
+		 *  buffer size.
+		 */
+
+		tsend = tmax;
+	}
+
+	tsend -= (tsend <= 9) ? 1 : (tsend <= 257) ? 2 : 3;
+
+	/*
+	 *  Loop over all channels, sending queued data.
+	 */
+
+	port = 0;
+	ch = nd->nd_chan;
+	used_buffer = tmax;
+
+	for (mod = 0; port < nd->nd_chan_count; mod++) {
+		/*
+		 *  If this is not the current module, enter a module select
+		 *  code in the buffer.
+		 */
+
+		if (mod != nd->nd_tx_module)
+			mbuf = ++b;
+
+		/*
+		 *  Loop to process one module.
+		 */
+
+		maxport = port + 16;
+
+		if (maxport > nd->nd_chan_count)
+			maxport = nd->nd_chan_count;
+
+		for (; port < maxport; port++, ch++) {
+			if (ch->ch_state != CS_READY)
+				continue;
+
+			lastport = port;
+
+			n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+			/*
+			 *  If there is data that can be sent, send it.
+			 */
+
+			if (n != 0 && used_buffer > 0) {
+				t = (ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff;
+
+				if (n > t)
+					n = t;
+
+				if (n > tsend) {
+					work = 1;
+					n = tsend;
+				}
+
+				if (n > used_buffer) {
+					work = 1;
+					n = used_buffer;
+				}
+
+				if (n <= 0)
+					continue;
+
+				/*
+				 *  Create the correct size transmit header,
+				 *  depending on the amount of data to transmit.
+				 */
+
+				if (n <= 8) {
+
+					b[0] = ((n - 1) << 4) + (port & 0xf);
+					b += 1;
+
+				} else if (n <= 255) {
+
+					b[0] = 0x80 + (port & 0xf);
+					b[1] = n;
+					b += 2;
+
+				} else {
+
+					b[0] = 0x90 + (port & 0xf);
+					put_unaligned_be16(n, b + 1);
+					b += 3;
+				}
+
+				ch->ch_s_tin = (ch->ch_s_tin + n) & 0xffff;
+
+				/*
+				 *  Copy transmit data to the packet.
+				 */
+
+				t = TBUF_MAX - ch->ch_tout;
+
+				if (n >= t) {
+					memcpy(b, ch->ch_tbuf + ch->ch_tout, t);
+					b += t;
+					n -= t;
+					used_buffer -= t;
+					ch->ch_tout = 0;
+				}
+
+				memcpy(b, ch->ch_tbuf + ch->ch_tout, n);
+				b += n;
+				used_buffer -= n;
+				ch->ch_tout += n;
+				n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+			}
+
+			/*
+			 *  Wake any terminal unit process waiting in the
+			 *  dgrp_write routine for low water.
+			 */
+
+			if (n > TBUF_LOW)
+				continue;
+
+			if ((ch->ch_flag & CH_LOW) != 0) {
+				ch->ch_flag &= ~CH_LOW;
+				wake_up_interruptible(&ch->ch_flag_wait);
+			}
+
+			/* selwakeup tty_sel */
+			if (ch->ch_tun.un_open_count) {
+				struct tty_struct *tty = (ch->ch_tun.un_tty);
+
+				if (waitqueue_active(&tty->write_wait))
+					wake_up_interruptible(&tty->write_wait);
+
+				tty_wakeup(tty);
+			}
+
+			if (ch->ch_pun.un_open_count) {
+				struct tty_struct *tty = (ch->ch_pun.un_tty);
+
+				if (waitqueue_active(&tty->write_wait))
+					wake_up_interruptible(&tty->write_wait);
+
+				tty_wakeup(tty);
+			}
+
+			/*
+			 *  Do EMPTY processing.
+			 */
+
+			if (n != 0)
+				continue;
+
+			if ((ch->ch_flag & (CH_EMPTY | CH_DRAIN)) != 0 ||
+			    (ch->ch_pun.un_flag & UN_EMPTY) != 0) {
+				/*
+				 *  If there is still data in the server, ask the server
+				 *  to notify us when its all gone.
+				 */
+
+				if (ch->ch_s_treq != ch->ch_s_tin) {
+					b = set_cmd_header(b, port, 43);
+
+					ch->ch_s_treq = ch->ch_s_tin;
+					put_unaligned_be16(ch->ch_s_treq,
+							   b);
+					b += 2;
+				}
+
+				/*
+				 *  If there is a thread waiting for buffer empty,
+				 *  and we are truly empty, wake the thread.
+				 */
+
+				else if ((ch->ch_flag & CH_EMPTY) != 0 &&
+					(ch->ch_send & RR_TX_BREAK) == 0) {
+					ch->ch_flag &= ~CH_EMPTY;
+
+					wake_up_interruptible(&ch->ch_flag_wait);
+				}
+			}
+		}
+
+		/*
+		 *  If a module select code is needed, drop one in.  If space
+		 *  was reserved for one, but none is needed, recover the space.
+		 */
+
+		if (mod != nd->nd_tx_module) {
+			if (b != mbuf) {
+				mbuf[-1] = 0xf0 | mod;
+				nd->nd_tx_module = mod;
+			} else {
+				b--;
+			}
+		}
+	}
+
+	/*
+	 *  Send a synchronization sequence associated with the last open
+	 *  channel that sent data, and remember the time when the data was
+	 *  sent.
+	 */
+
+	in = nd->nd_seq_in;
+
+	if ((send_sync || nd->nd_seq_wait[in] != 0) && lastport >= 0) {
+		u8 *bb = b;
+
+		/*
+		 * Attempt the use the port that really wanted the sync.
+		 * This gets around a race condition where the "lastport" is in
+		 * the middle of the close() routine, and by the time we
+		 * send this command, it will have already acked the close, and
+		 * thus not send the sync response.
+		 */
+		if (wanted_sync_port >= 0)
+			lastport = wanted_sync_port;
+		/*
+		 * Set a flag just in case the port is in the middle of a close,
+		 * it will not be permitted to actually close until we get an
+		 * sync response, and clear the flag there.
+		 */
+		ch = nd->nd_chan + lastport;
+		ch->ch_flag |= CH_WAITING_SYNC;
+
+		mod = lastport >> 4;
+
+		if (mod != nd->nd_tx_module) {
+			bb[0] = 0xf0 + mod;
+			bb += 1;
+
+			nd->nd_tx_module = mod;
+		}
+
+		bb = set_cmd_header(bb, lastport, 12);
+		*bb++ = in;
+
+		nd->nd_seq_size[in] = bb - buf;
+		nd->nd_seq_time[in] = jiffies;
+
+		if (++in >= SEQ_MAX)
+			in = 0;
+
+		if (in != nd->nd_seq_out) {
+			b = bb;
+			nd->nd_seq_in = in;
+			nd->nd_unack += b - buf;
+		}
+	}
+
+	/*
+	 *  If there are no open ports, a sync cannot be sent.
+	 *  There is nothing left to wait for anyway, so wake any
+	 *  thread waiting for an acknowledgement.
+	 */
+
+	else if (nd->nd_seq_wait[in] != 0) {
+		nd->nd_seq_wait[in] = 0;
+
+		wake_up_interruptible(&nd->nd_seq_wque[in]);
+	}
+
+	/*
+	 *  If there is no traffic for an interval of IDLE_MAX, then
+	 *  send a single byte packet.
+	 */
+
+	if (b != buf) {
+		nd->nd_tx_time = jiffies;
+	} else if ((ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX) {
+		*b++ = 0xf0 | nd->nd_tx_module;
+		nd->nd_tx_time = jiffies;
+	}
+
+	n = b - buf;
+
+	if (n >= tsafe)
+		pr_info("%s - n(%i) >= tsafe(%i)\n",
+			__func__, n, tsafe);
+
+	if (tsend < 0)
+		dgrp_dump(buf, n);
+
+	nd->nd_tx_work = work;
+
+	return n;
+}
+
+/*
+ * dgrp_net_read()
+ * Data to be sent TO the PortServer from the "async." half of the driver.
+ */
+static ssize_t dgrp_net_read(struct file *file, char __user *buf, size_t count,
+			     loff_t *ppos)
+{
+	struct nd_struct *nd;
+	long n;
+	u8 *local_buf;
+	u8 *b;
+	ssize_t rtn;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		return -ENXIO;
+
+	if (count < UIO_MIN)
+		return -EINVAL;
+
+	/*
+	 *  Only one read/write operation may be in progress at
+	 *  any given time.
+	 */
+
+	/*
+	 *  Grab the NET lock.
+	 */
+	down(&nd->nd_net_semaphore);
+
+	nd->nd_read_count++;
+
+	nd->nd_tx_ready = 0;
+
+	/*
+	 *  Determine the effective size of the buffer.
+	 */
+
+	if (nd->nd_remain > UIO_BASE)
+		pr_info_ratelimited("%s - nd_remain(%i) > UIO_BASE\n",
+				    __func__, nd->nd_remain);
+
+	b = local_buf = nd->nd_iobuf + UIO_BASE;
+
+	/*
+	 *  Generate data according to the node state.
+	 */
+
+	switch (nd->nd_state) {
+	/*
+	 *  Initialize the connection.
+	 */
+
+	case NS_IDLE:
+		if (nd->nd_mon_buf)
+			dgrp_monitor_reset(nd);
+
+		/*
+		 *  Request a Product ID Packet.
+		 */
+
+		b[0] = 0xfb;
+		b[1] = 0x01;
+		b += 2;
+
+		nd->nd_expect |= NR_IDENT;
+
+		/*
+		 *  Request a Server Capability ID Response.
+		 */
+
+		b[0] = 0xfb;
+		b[1] = 0x02;
+		b += 2;
+
+		nd->nd_expect |= NR_CAPABILITY;
+
+		/*
+		 *  Request a Server VPD Response.
+		 */
+
+		b[0] = 0xfb;
+		b[1] = 0x18;
+		b += 2;
+
+		nd->nd_expect |= NR_VPD;
+
+		nd->nd_state = NS_WAIT_QUERY;
+		break;
+
+	/*
+	 *  We do serious communication with the server only in
+	 *  the READY state.
+	 */
+
+	case NS_READY:
+		b = dgrp_send(nd, count) + local_buf;
+		break;
+
+	/*
+	 *  Send off an error after receiving a bogus message
+	 *  from the server.
+	 */
+
+	case NS_SEND_ERROR:
+		n = strlen(nd->nd_error);
+
+		b[0] = 0xff;
+		b[1] = n;
+		memcpy(b + 2, nd->nd_error, n);
+		b += 2 + n;
+
+		dgrp_net_idle(nd);
+		/*
+		 *  Set the active port count to zero.
+		 */
+		dgrp_chan_count(nd, 0);
+		break;
+
+	default:
+		break;
+	}
+
+	n = b - local_buf;
+
+	if (n != 0) {
+		nd->nd_send_count++;
+
+		nd->nd_tx_byte   += n + nd->nd_link.lk_header_size;
+		nd->nd_tx_charge += n + nd->nd_link.lk_header_size;
+	}
+
+	rtn = copy_to_user((void __user *)buf, local_buf, n);
+	if (rtn) {
+		rtn = -EFAULT;
+		goto done;
+	}
+
+	*ppos += n;
+
+	rtn = n;
+
+	if (nd->nd_mon_buf)
+		dgrp_monitor_data(nd, RPDUMP_CLIENT, local_buf, n);
+
+	/*
+	 *  Release the NET lock.
+	 */
+done:
+	up(&nd->nd_net_semaphore);
+
+	return rtn;
+}
+
+/**
+ * dgrp_receive() -- decode data packets received from the remote PortServer.
+ * @nd: pointer to a node structure
+ */
+static void dgrp_receive(struct nd_struct *nd)
+{
+	struct ch_struct *ch;
+	u8 *buf;
+	u8 *b;
+	u8 *dbuf;
+	char *error;
+	long port;
+	long dlen;
+	long plen;
+	long remain;
+	long n;
+	long mlast;
+	long elast;
+	long mstat;
+	long estat;
+
+	char ID[3];
+
+	nd->nd_tx_time = jiffies;
+
+	ID_TO_CHAR(nd->nd_ID, ID);
+
+	b = buf = nd->nd_iobuf;
+	remain = nd->nd_remain;
+
+	/*
+	 *  Loop to process Realport protocol packets.
+	 */
+
+	while (remain > 0) {
+		int n0 = b[0] >> 4;
+		int n1 = b[0] & 0x0f;
+
+		if (n0 <= 12) {
+			port = (nd->nd_rx_module << 4) + n1;
+
+			if (port >= nd->nd_chan_count) {
+				error = "Improper Port Number";
+				goto prot_error;
+			}
+
+			ch = nd->nd_chan + port;
+		} else {
+			port = -1;
+			ch = NULL;
+		}
+
+		/*
+		 *  Process by major packet type.
+		 */
+
+		switch (n0) {
+
+		/*
+		 *  Process 1-byte header data packet.
+		 */
+
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+		case 7:
+			dlen = n0 + 1;
+			plen = dlen + 1;
+
+			dbuf = b + 1;
+			goto data;
+
+		/*
+		 *  Process 2-byte header data packet.
+		 */
+
+		case 8:
+			if (remain < 3)
+				goto done;
+
+			dlen = b[1];
+			plen = dlen + 2;
+
+			dbuf = b + 2;
+			goto data;
+
+		/*
+		 *  Process 3-byte header data packet.
+		 */
+
+		case 9:
+			if (remain < 4)
+				goto done;
+
+			dlen = get_unaligned_be16(b + 1);
+			plen = dlen + 3;
+
+			dbuf = b + 3;
+
+		/*
+		 *  Common packet handling code.
+		 */
+
+data:
+			nd->nd_tx_work = 1;
+
+			/*
+			 *  Otherwise data should appear only when we are
+			 *  in the CS_READY state.
+			 */
+
+			if (ch->ch_state < CS_READY) {
+				error = "Data received before RWIN established";
+				goto prot_error;
+			}
+
+			/*
+			 *  Assure that the data received is within the
+			 *  allowable window.
+			 */
+
+			n = (ch->ch_s_rwin - ch->ch_s_rin) & 0xffff;
+
+			if (dlen > n) {
+				error = "Receive data overrun";
+				goto prot_error;
+			}
+
+			/*
+			 *  If we received 3 or less characters,
+			 *  assume it is a human typing, and set RTIME
+			 *  to 10 milliseconds.
+			 *
+			 *  If we receive 10 or more characters,
+			 *  assume its not a human typing, and set RTIME
+			 *  to 100 milliseconds.
+			 */
+
+			if (ch->ch_edelay != DGRP_RTIME) {
+				if (ch->ch_rtime != ch->ch_edelay) {
+					ch->ch_rtime = ch->ch_edelay;
+					ch->ch_flag |= CH_PARAM;
+				}
+			} else if (dlen <= 3) {
+				if (ch->ch_rtime != 10) {
+					ch->ch_rtime = 10;
+					ch->ch_flag |= CH_PARAM;
+				}
+			} else {
+				if (ch->ch_rtime != DGRP_RTIME) {
+					ch->ch_rtime = DGRP_RTIME;
+					ch->ch_flag |= CH_PARAM;
+				}
+			}
+
+			/*
+			 *  If a portion of the packet is outside the
+			 *  buffer, shorten the effective length of the
+			 *  data packet to be the amount of data received.
+			 */
+
+			if (remain < plen)
+				dlen -= plen - remain;
+
+			/*
+			 *  Detect if receive flush is now complete.
+			 */
+
+			if ((ch->ch_flag & CH_RX_FLUSH) != 0 &&
+			    ((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) >=
+			    ((nd->nd_seq_in    - nd->nd_seq_out) & SEQ_MASK)) {
+				ch->ch_flag &= ~CH_RX_FLUSH;
+			}
+
+			/*
+			 *  If we are ready to receive, move the data into
+			 *  the receive buffer.
+			 */
+
+			ch->ch_s_rin = (ch->ch_s_rin + dlen) & 0xffff;
+
+			if (ch->ch_state == CS_READY &&
+			    (ch->ch_tun.un_open_count != 0) &&
+			    (ch->ch_tun.un_flag & UN_CLOSING) == 0 &&
+			    (ch->ch_cflag & CF_CREAD) != 0 &&
+			    (ch->ch_flag & (CH_BAUD0 | CH_RX_FLUSH)) == 0 &&
+			    (ch->ch_send & RR_RX_FLUSH) == 0) {
+
+				if (ch->ch_rin + dlen >= RBUF_MAX) {
+					n = RBUF_MAX - ch->ch_rin;
+
+					memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, n);
+
+					ch->ch_rin = 0;
+					dbuf += n;
+					dlen -= n;
+				}
+
+				memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, dlen);
+
+				ch->ch_rin += dlen;
+
+
+				/*
+				 *  If we are not in fastcook mode, or
+				 *  if there is a fastcook thread
+				 *  waiting for data, send the data to
+				 *  the line discipline.
+				 */
+
+				if ((ch->ch_flag & CH_FAST_READ) == 0 ||
+				    ch->ch_inwait != 0) {
+					dgrp_input(ch);
+				}
+
+				/*
+				 *  If there is a read thread waiting
+				 *  in select, and we are in fastcook
+				 *  mode, wake him up.
+				 */
+
+				if (waitqueue_active(&ch->ch_tun.un_tty->read_wait) &&
+				    (ch->ch_flag & CH_FAST_READ) != 0)
+					wake_up_interruptible(&ch->ch_tun.un_tty->read_wait);
+
+				/*
+				 * Wake any thread waiting in the
+				 * fastcook loop.
+				 */
+
+				if ((ch->ch_flag & CH_INPUT) != 0) {
+					ch->ch_flag &= ~CH_INPUT;
+
+					wake_up_interruptible(&ch->ch_flag_wait);
+				}
+			}
+
+			/*
+			 *  Fabricate and insert a data packet header to
+			 *  preceed the remaining data when it comes in.
+			 */
+
+			if (remain < plen) {
+				dlen = plen - remain;
+				b = buf;
+
+				b[0] = 0x90 + n1;
+				put_unaligned_be16(dlen, b + 1);
+
+				remain = 3;
+				goto done;
+			}
+			break;
+
+		/*
+		 *  Handle Window Sequence packets.
+		 */
+
+		case 10:
+			plen = 3;
+			if (remain < plen)
+				goto done;
+
+			nd->nd_tx_work = 1;
+
+			{
+				ushort tpos   = get_unaligned_be16(b + 1);
+
+				ushort ack    = (tpos          - ch->ch_s_tpos) & 0xffff;
+				ushort unack  = (ch->ch_s_tin  - ch->ch_s_tpos) & 0xffff;
+				ushort notify = (ch->ch_s_treq - ch->ch_s_tpos) & 0xffff;
+
+				if (ch->ch_state < CS_READY || ack > unack) {
+					error = "Improper Window Sequence";
+					goto prot_error;
+				}
+
+				ch->ch_s_tpos = tpos;
+
+				if (notify <= ack)
+					ch->ch_s_treq = tpos;
+			}
+			break;
+
+		/*
+		 *  Handle Command response packets.
+		 */
+
+		case 11:
+
+			/*
+			 * RealPort engine fix - 03/11/2004
+			 *
+			 * This check did not used to be here.
+			 *
+			 * We were using b[1] without verifying that the data
+			 * is actually there and valid. On a split packet, it
+			 * might not be yet.
+			 *
+			 * NOTE:  I have never actually seen the failure happen
+			 *        under Linux,  but since I have seen it occur
+			 *        under both Solaris and HP-UX,  the assumption
+			 *        is that it *could* happen here as well...
+			 */
+			if (remain < 2)
+				goto done;
+
+
+			switch (b[1]) {
+
+			/*
+			 *  Handle Open Response.
+			 */
+
+			case 11:
+				plen = 6;
+				if (remain < plen)
+					goto done;
+
+				nd->nd_tx_work = 1;
+
+				{
+					int req = b[2];
+					int resp = b[3];
+					port = get_unaligned_be16(b + 4);
+
+					if (port >= nd->nd_chan_count) {
+						error = "Open channel number out of range";
+						goto prot_error;
+					}
+
+					ch = nd->nd_chan + port;
+
+					/*
+					 *  How we handle an open response depends primarily
+					 *  on our current channel state.
+					 */
+
+					switch (ch->ch_state) {
+					case CS_IDLE:
+
+						/*
+						 *  Handle a delayed open.
+						 */
+
+						if (ch->ch_otype_waiting != 0 &&
+						    req == ch->ch_otype_waiting &&
+						    resp == 0) {
+							ch->ch_otype = req;
+							ch->ch_otype_waiting = 0;
+							ch->ch_state = CS_SEND_QUERY;
+							break;
+						}
+						goto open_error;
+
+					case CS_WAIT_OPEN:
+
+						/*
+						 *  Handle the open response.
+						 */
+
+						if (req == ch->ch_otype) {
+							switch (resp) {
+
+							/*
+							 *  On successful response, open the
+							 *  port and proceed normally.
+							 */
+
+							case 0:
+								ch->ch_state = CS_SEND_QUERY;
+								break;
+
+							/*
+							 *  On a busy response to a persistent open,
+							 *  remember that the open is pending.
+							 */
+
+							case 1:
+							case 2:
+								if (req != OTYPE_IMMEDIATE) {
+									ch->ch_otype_waiting = req;
+									ch->ch_state = CS_IDLE;
+									break;
+								}
+
+							/*
+							 *  Otherwise the server open failed.  If
+							 *  the Unix port is open, hang it up.
+							 */
+
+							default:
+								if (ch->ch_open_count != 0) {
+									ch->ch_flag |= CH_HANGUP;
+									dgrp_carrier(ch);
+									ch->ch_state = CS_IDLE;
+									break;
+								}
+
+								ch->ch_open_error = resp;
+								ch->ch_state = CS_IDLE;
+
+								wake_up_interruptible(&ch->ch_flag_wait);
+							}
+							break;
+						}
+
+						/*
+						 *  Handle delayed response arrival preceeding
+						 *  the open response we are waiting for.
+						 */
+
+						if (ch->ch_otype_waiting != 0 &&
+						    req == ch->ch_otype_waiting &&
+						    resp == 0) {
+							ch->ch_otype = ch->ch_otype_waiting;
+							ch->ch_otype_waiting = 0;
+							ch->ch_state = CS_WAIT_FAIL;
+							break;
+						}
+						goto open_error;
+
+
+					case CS_WAIT_FAIL:
+
+						/*
+						 *  Handle response to immediate open arriving
+						 *  after a delayed open success.
+						 */
+
+						if (req == OTYPE_IMMEDIATE) {
+							ch->ch_state = CS_SEND_QUERY;
+							break;
+						}
+						goto open_error;
+
+
+					case CS_WAIT_CANCEL:
+						/*
+						 *  Handle delayed open response arriving before
+						 *  the cancel response.
+						 */
+
+						if (req == ch->ch_otype_waiting &&
+						    resp == 0) {
+							ch->ch_otype_waiting = 0;
+							break;
+						}
+
+						/*
+						 *  Handle cancel response.
+						 */
+
+						if (req == 4 && resp == 0) {
+							ch->ch_otype_waiting = 0;
+							ch->ch_state = CS_IDLE;
+							break;
+						}
+						goto open_error;
+
+
+					case CS_WAIT_CLOSE:
+						/*
+						 *  Handle a successful response to a port
+						 *  close.
+						 */
+
+						if (req >= 3) {
+							ch->ch_state = CS_IDLE;
+							break;
+						}
+						goto open_error;
+
+open_error:
+					default:
+						{
+							error = "Improper Open Response";
+							goto prot_error;
+						}
+					}
+				}
+				break;
+
+			/*
+			 *  Handle Synchronize Response.
+			 */
+
+			case 13:
+				plen = 3;
+				if (remain < plen)
+					goto done;
+				{
+					int seq = b[2];
+					int s;
+
+					/*
+					 * If channel was waiting for this sync response,
+					 * unset the flag, and wake up anyone waiting
+					 * on the event.
+					 */
+					if (ch->ch_flag & CH_WAITING_SYNC) {
+						ch->ch_flag &= ~(CH_WAITING_SYNC);
+						wake_up_interruptible(&ch->ch_flag_wait);
+					}
+
+					if (((seq - nd->nd_seq_out) & SEQ_MASK) >=
+					    ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) {
+						break;
+					}
+
+					for (s = nd->nd_seq_out;; s = (s + 1) & SEQ_MASK) {
+						if (nd->nd_seq_wait[s] != 0) {
+							nd->nd_seq_wait[s] = 0;
+
+							wake_up_interruptible(&nd->nd_seq_wque[s]);
+						}
+
+						nd->nd_unack -= nd->nd_seq_size[s];
+
+						if (s == seq)
+							break;
+					}
+
+					nd->nd_seq_out = (seq + 1) & SEQ_MASK;
+				}
+				break;
+
+			/*
+			 *  Handle Sequence Response.
+			 */
+
+			case 15:
+				plen = 6;
+				if (remain < plen)
+					goto done;
+
+				{
+				/* Record that we have received the Sequence
+				 * Response, but we aren't interested in the
+				 * sequence numbers.  We were using RIN like it
+				 * was ROUT and that was causing problems,
+				 * fixed 7-13-2001 David Fries. See comment in
+				 * drp.h for ch_s_rin variable.
+					int rin = get_unaligned_be16(b + 2);
+					int tpos = get_unaligned_be16(b + 4);
+				*/
+
+					ch->ch_send   &= ~RR_SEQUENCE;
+					ch->ch_expect &= ~RR_SEQUENCE;
+				}
+				goto check_query;
+
+			/*
+			 *  Handle Status Response.
+			 */
+
+			case 17:
+				plen = 5;
+				if (remain < plen)
+					goto done;
+
+				{
+					ch->ch_s_elast = get_unaligned_be16(b + 2);
+					ch->ch_s_mlast = b[4];
+
+					ch->ch_expect &= ~RR_STATUS;
+					ch->ch_send   &= ~RR_STATUS;
+
+					/*
+					 *  CH_PHYS_CD is cleared because something _could_ be
+					 *  waiting for the initial sense of carrier... and if
+					 *  carrier is high immediately, we want to be sure to
+					 *  wake them as soon as possible.
+					 */
+					ch->ch_flag &= ~CH_PHYS_CD;
+
+					dgrp_carrier(ch);
+				}
+				goto check_query;
+
+			/*
+			 *  Handle Line Error Response.
+			 */
+
+			case 19:
+				plen = 14;
+				if (remain < plen)
+					goto done;
+
+				break;
+
+			/*
+			 *  Handle Buffer Response.
+			 */
+
+			case 21:
+				plen = 6;
+				if (remain < plen)
+					goto done;
+
+				{
+					ch->ch_s_rsize = get_unaligned_be16(b + 2);
+					ch->ch_s_tsize = get_unaligned_be16(b + 4);
+
+					ch->ch_send   &= ~RR_BUFFER;
+					ch->ch_expect &= ~RR_BUFFER;
+				}
+				goto check_query;
+
+			/*
+			 *  Handle Port Capability Response.
+			 */
+
+			case 23:
+				plen = 32;
+				if (remain < plen)
+					goto done;
+
+				{
+					ch->ch_send   &= ~RR_CAPABILITY;
+					ch->ch_expect &= ~RR_CAPABILITY;
+				}
+
+			/*
+			 *  When all queries are complete, set those parameters
+			 *  derived from the query results, then transition
+			 *  to the READY state.
+			 */
+
+check_query:
+				if (ch->ch_state == CS_WAIT_QUERY &&
+				    (ch->ch_expect & (RR_SEQUENCE |
+							RR_STATUS |
+							RR_BUFFER |
+							RR_CAPABILITY)) == 0) {
+					ch->ch_tmax  = ch->ch_s_tsize / 4;
+
+					if (ch->ch_edelay == DGRP_TTIME)
+						ch->ch_ttime = DGRP_TTIME;
+					else
+						ch->ch_ttime = ch->ch_edelay;
+
+					ch->ch_rmax = ch->ch_s_rsize / 4;
+
+					if (ch->ch_edelay == DGRP_RTIME)
+						ch->ch_rtime = DGRP_RTIME;
+					else
+						ch->ch_rtime = ch->ch_edelay;
+
+					ch->ch_rlow  = 2 * ch->ch_s_rsize / 8;
+					ch->ch_rhigh = 6 * ch->ch_s_rsize / 8;
+
+					ch->ch_state = CS_READY;
+
+					nd->nd_tx_work = 1;
+					wake_up_interruptible(&ch->ch_flag_wait);
+
+				}
+				break;
+
+			default:
+				goto decode_error;
+			}
+			break;
+
+		/*
+		 *  Handle Events.
+		 */
+
+		case 12:
+			plen = 4;
+			if (remain < plen)
+				goto done;
+
+			mlast = ch->ch_s_mlast;
+			elast = ch->ch_s_elast;
+
+			mstat = ch->ch_s_mlast = b[1];
+			estat = ch->ch_s_elast = get_unaligned_be16(b + 2);
+
+			/*
+			 *  Handle modem changes.
+			 */
+
+			if (((mstat ^ mlast) & DM_CD) != 0)
+				dgrp_carrier(ch);
+
+
+			/*
+			 *  Handle received break.
+			 */
+
+			if ((estat & ~elast & EV_RXB) != 0 &&
+			    (ch->ch_tun.un_open_count != 0) &&
+			    I_BRKINT(ch->ch_tun.un_tty) &&
+			    !(I_IGNBRK(ch->ch_tun.un_tty))) {
+
+				tty_buffer_request_room(ch->ch_tun.un_tty, 1);
+				tty_insert_flip_char(ch->ch_tun.un_tty, 0, TTY_BREAK);
+				tty_flip_buffer_push(ch->ch_tun.un_tty);
+
+			}
+
+			/*
+			 *  On transmit break complete, if more break traffic
+			 *  is waiting then send it.  Otherwise wake any threads
+			 *  waiting for transmitter empty.
+			 */
+
+			if ((~estat & elast & EV_TXB) != 0 &&
+			    (ch->ch_expect & RR_TX_BREAK) != 0) {
+
+				nd->nd_tx_work = 1;
+
+				ch->ch_expect &= ~RR_TX_BREAK;
+
+				if (ch->ch_break_time != 0) {
+					ch->ch_send |= RR_TX_BREAK;
+				} else {
+					ch->ch_send &= ~RR_TX_BREAK;
+					ch->ch_flag &= ~CH_TX_BREAK;
+					wake_up_interruptible(&ch->ch_flag_wait);
+				}
+			}
+			break;
+
+		case 13:
+		case 14:
+			error = "Unrecognized command";
+			goto prot_error;
+
+		/*
+		 *  Decode Special Codes.
+		 */
+
+		case 15:
+			switch (n1) {
+			/*
+			 *  One byte module select.
+			 */
+
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+			case 4:
+			case 5:
+			case 6:
+			case 7:
+				plen = 1;
+				nd->nd_rx_module = n1;
+				break;
+
+			/*
+			 *  Two byte module select.
+			 */
+
+			case 8:
+				plen = 2;
+				if (remain < plen)
+					goto done;
+
+				nd->nd_rx_module = b[1];
+				break;
+
+			/*
+			 *  ID Request packet.
+			 */
+
+			case 11:
+				if (remain < 4)
+					goto done;
+
+				plen = get_unaligned_be16(b + 2);
+
+				if (plen < 12 || plen > 1000) {
+					error = "Response Packet length error";
+					goto prot_error;
+				}
+
+				nd->nd_tx_work = 1;
+
+				switch (b[1]) {
+				/*
+				 *  Echo packet.
+				 */
+
+				case 0:
+					nd->nd_send |= NR_ECHO;
+					break;
+
+				/*
+				 *  ID Response packet.
+				 */
+
+				case 1:
+					nd->nd_send |= NR_IDENT;
+					break;
+
+				/*
+				 *  ID Response packet.
+				 */
+
+				case 32:
+					nd->nd_send |= NR_PASSWORD;
+					break;
+
+				}
+				break;
+
+			/*
+			 *  Various node-level response packets.
+			 */
+
+			case 12:
+				if (remain < 4)
+					goto done;
+
+				plen = get_unaligned_be16(b + 2);
+
+				if (plen < 4 || plen > 1000) {
+					error = "Response Packet length error";
+					goto prot_error;
+				}
+
+				nd->nd_tx_work = 1;
+
+				switch (b[1]) {
+				/*
+				 *  Echo packet.
+				 */
+
+				case 0:
+					nd->nd_expect &= ~NR_ECHO;
+					break;
+
+				/*
+				 *  Product Response Packet.
+				 */
+
+				case 1:
+					{
+						int desclen;
+
+						nd->nd_hw_ver = (b[8] << 8) | b[9];
+						nd->nd_sw_ver = (b[10] << 8) | b[11];
+						nd->nd_hw_id = b[6];
+						desclen = ((plen - 12) > MAX_DESC_LEN) ? MAX_DESC_LEN :
+							plen - 12;
+
+						if (desclen <= 0) {
+							error = "Response Packet desclen error";
+							goto prot_error;
+						}
+
+						strncpy(nd->nd_ps_desc, b + 12, desclen);
+						nd->nd_ps_desc[desclen] = 0;
+					}
+
+					nd->nd_expect &= ~NR_IDENT;
+					break;
+
+				/*
+				 *  Capability Response Packet.
+				 */
+
+				case 2:
+					{
+						int nn = get_unaligned_be16(b + 4);
+
+						if (nn > CHAN_MAX)
+							nn = CHAN_MAX;
+
+						dgrp_chan_count(nd, nn);
+					}
+
+					nd->nd_expect &= ~NR_CAPABILITY;
+					break;
+
+				/*
+				 *  VPD Response Packet.
+				 */
+
+				case 15:
+					/*
+					 * NOTE: case 15 is here ONLY because the EtherLite
+					 * is broken, and sends a response to 24 back as 15.
+					 * To resolve this, the EtherLite firmware is now
+					 * fixed to send back 24 correctly, but, for backwards
+					 * compatibility, we now have reserved 15 for the
+					 * bad EtherLite response to 24 as well.
+					 */
+
+					/* Fallthru! */
+
+				case 24:
+
+					/*
+					 * If the product doesn't support VPD,
+					 * it will send back a null IDRESP,
+					 * which is a length of 4 bytes.
+					 */
+					if (plen > 4) {
+						memcpy(nd->nd_vpd, b + 4, min(plen - 4, (long) VPDSIZE));
+						nd->nd_vpd_len = min(plen - 4, (long) VPDSIZE);
+					}
+
+					nd->nd_expect &= ~NR_VPD;
+					break;
+
+				default:
+					goto decode_error;
+				}
+
+				if (nd->nd_expect == 0 &&
+				    nd->nd_state == NS_WAIT_QUERY) {
+					nd->nd_state = NS_READY;
+				}
+				break;
+
+			/*
+			 *  Debug packet.
+			 */
+
+			case 14:
+				if (remain < 4)
+					goto done;
+
+				plen = get_unaligned_be16(b + 2) + 4;
+
+				if (plen > 1000) {
+					error = "Debug Packet too large";
+					goto prot_error;
+				}
+
+				if (remain < plen)
+					goto done;
+				break;
+
+			/*
+			 *  Handle reset packet.
+			 */
+
+			case 15:
+				if (remain < 2)
+					goto done;
+
+				plen = 2 + b[1];
+
+				if (remain < plen)
+					goto done;
+
+				nd->nd_tx_work = 1;
+
+				n = b[plen];
+				b[plen] = 0;
+
+				b[plen] = n;
+
+				error = "Client Reset Acknowledge";
+				goto prot_error;
+
+			default:
+				goto decode_error;
+			}
+			break;
+
+		default:
+			goto decode_error;
+		}
+
+		b += plen;
+		remain -= plen;
+	}
+
+	/*
+	 *  When the buffer is exhausted, copy any data left at the
+	 *  top of the buffer back down to the bottom for the next
+	 *  read request.
+	 */
+
+done:
+	if (remain > 0 && b != buf)
+		memcpy(buf, b, remain);
+
+	nd->nd_remain = remain;
+	return;
+
+/*
+ *  Handle a decode error.
+ */
+
+decode_error:
+	error = "Protocol decode error";
+
+/*
+ *  Handle a general protocol error.
+ */
+
+prot_error:
+	nd->nd_remain = 0;
+	nd->nd_state = NS_SEND_ERROR;
+	nd->nd_error = error;
+}
+
+/*
+ * dgrp_net_write() -- write data to the network device.
+ *
+ * A zero byte write indicates that the connection to the RealPort
+ * device has been broken.
+ *
+ * A non-zero write indicates data from the RealPort device.
+ */
+static ssize_t dgrp_net_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	struct nd_struct *nd;
+	ssize_t rtn = 0;
+	long n;
+	long total = 0;
+
+	/*
+	 *  Get the node pointer, and quit if it doesn't exist.
+	 */
+	nd = (struct nd_struct *)(file->private_data);
+	if (!nd)
+		return -ENXIO;
+
+	/*
+	 *  Grab the NET lock.
+	 */
+	down(&nd->nd_net_semaphore);
+
+	nd->nd_write_count++;
+
+	/*
+	 *  Handle disconnect.
+	 */
+
+	if (count == 0) {
+		dgrp_net_idle(nd);
+		/*
+		 *  Set the active port count to zero.
+		 */
+		dgrp_chan_count(nd, 0);
+		goto unlock;
+	}
+
+	/*
+	 *  Loop to process entire receive packet.
+	 */
+
+	while (count > 0) {
+		n = UIO_MAX - nd->nd_remain;
+
+		if (n > count)
+			n = count;
+
+		nd->nd_rx_byte += n + nd->nd_link.lk_header_size;
+
+		rtn = copy_from_user(nd->nd_iobuf + nd->nd_remain,
+				     (void __user *) buf + total, n);
+		if (rtn) {
+			rtn = -EFAULT;
+			goto unlock;
+		}
+
+		*ppos += n;
+
+		total += n;
+
+		count -= n;
+
+		if (nd->nd_mon_buf)
+			dgrp_monitor_data(nd, RPDUMP_SERVER,
+					  nd->nd_iobuf + nd->nd_remain, n);
+
+		nd->nd_remain += n;
+
+		dgrp_receive(nd);
+	}
+
+	rtn = total;
+
+unlock:
+	/*
+	 *  Release the NET lock.
+	 */
+	up(&nd->nd_net_semaphore);
+
+	return rtn;
+}
+
+
+/*
+ * dgrp_net_select()
+ *  Determine whether a device is ready to be read or written to, and
+ *  sleep if not.
+ */
+static unsigned int dgrp_net_select(struct file *file,
+				    struct poll_table_struct *table)
+{
+	unsigned int retval = 0;
+	struct nd_struct *nd = file->private_data;
+
+	poll_wait(file, &nd->nd_tx_waitq, table);
+
+	if (nd->nd_tx_ready)
+		retval |= POLLIN | POLLRDNORM; /* Conditionally readable */
+
+	retval |= POLLOUT | POLLWRNORM;        /* Always writeable */
+
+	return retval;
+}
+
+/*
+ * dgrp_net_ioctl
+ *
+ * Implement those functions which allow the network daemon to control
+ * the network parameters in the driver.  The ioctls include ones to
+ * get and set the link speed parameters for the PortServer.
+ */
+static long dgrp_net_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct nd_struct  *nd;
+	int    rtn = 0;
+	long   size = _IOC_SIZE(cmd);
+	struct link_struct link;
+
+	nd = file->private_data;
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		rtn = access_ok(VERIFY_WRITE, (void __user *) arg, size);
+	else if (_IOC_DIR(cmd) & _IOC_WRITE)
+		rtn = access_ok(VERIFY_READ,  (void __user *) arg, size);
+
+	if (!rtn)
+		return rtn;
+
+	switch (cmd) {
+	case DIGI_SETLINK:
+		if (size != sizeof(struct link_struct))
+			return -EINVAL;
+
+		if (copy_from_user((void *)(&link), (void __user *) arg, size))
+			return -EFAULT;
+
+		if (link.lk_fast_rate < 9600)
+			link.lk_fast_rate = 9600;
+
+		if (link.lk_slow_rate < 2400)
+			link.lk_slow_rate = 2400;
+
+		if (link.lk_fast_rate > 10000000)
+			link.lk_fast_rate = 10000000;
+
+		if (link.lk_slow_rate > link.lk_fast_rate)
+			link.lk_slow_rate = link.lk_fast_rate;
+
+		if (link.lk_fast_delay > 2000)
+			link.lk_fast_delay = 2000;
+
+		if (link.lk_slow_delay > 10000)
+			link.lk_slow_delay = 10000;
+
+		if (link.lk_fast_delay < 60)
+			link.lk_fast_delay = 60;
+
+		if (link.lk_slow_delay < link.lk_fast_delay)
+			link.lk_slow_delay = link.lk_fast_delay;
+
+		if (link.lk_header_size < 2)
+			link.lk_header_size = 2;
+
+		if (link.lk_header_size > 128)
+			link.lk_header_size = 128;
+
+		link.lk_fast_rate /= 8 * 1000 / dgrp_poll_tick;
+		link.lk_slow_rate /= 8 * 1000 / dgrp_poll_tick;
+
+		link.lk_fast_delay /= dgrp_poll_tick;
+		link.lk_slow_delay /= dgrp_poll_tick;
+
+		nd->nd_link = link;
+
+		break;
+
+	case DIGI_GETLINK:
+		if (size != sizeof(struct link_struct))
+			return -EINVAL;
+
+		if (copy_to_user((void __user *)arg, (void *)(&nd->nd_link),
+				 size))
+			return -EFAULT;
+
+		break;
+
+	default:
+		return -EINVAL;
+
+	}
+
+	return 0;
+}
+
+/**
+ * dgrp_poll_handler() -- handler for poll timer
+ *
+ * As each timer expires, it determines (a) whether the "transmit"
+ * waiter needs to be woken up, and (b) whether the poller needs to
+ * be rescheduled.
+ */
+void dgrp_poll_handler(unsigned long arg)
+{
+	struct dgrp_poll_data *poll_data;
+	struct nd_struct *nd;
+	struct link_struct *lk;
+	ulong time;
+	ulong poll_time;
+	ulong freq;
+	ulong lock_flags;
+
+	poll_data = (struct dgrp_poll_data *) arg;
+	freq = 1000 / poll_data->poll_tick;
+	poll_data->poll_round += 17;
+
+	if (poll_data->poll_round >= freq)
+		poll_data->poll_round -= freq;
+
+	/*
+	 * Loop to process all open nodes.
+	 *
+	 * For each node, determine the rate at which it should
+	 * be transmitting data.  Then if the node should wake up
+	 * and transmit data now, enable the net receive select
+	 * to get the transmit going.
+	 */
+
+	list_for_each_entry(nd, &nd_struct_list, list) {
+
+		lk = &nd->nd_link;
+
+		/*
+		 * Decrement statistics.  These are only for use with
+		 * KME, so don't worry that the operations are done
+		 * unlocked, and so the results are occassionally wrong.
+		 */
+
+		nd->nd_read_count -= (nd->nd_read_count +
+				      poll_data->poll_round) / freq;
+		nd->nd_write_count -= (nd->nd_write_count +
+				       poll_data->poll_round) / freq;
+		nd->nd_send_count -= (nd->nd_send_count +
+				      poll_data->poll_round) / freq;
+		nd->nd_tx_byte -= (nd->nd_tx_byte +
+				   poll_data->poll_round) / freq;
+		nd->nd_rx_byte -= (nd->nd_rx_byte +
+				   poll_data->poll_round) / freq;
+
+		/*
+		 * Wake the daemon to transmit data only when there is
+		 * enough byte credit to send data.
+		 *
+		 * The results are approximate because the operations
+		 * are performed unlocked, and we are inspecting
+		 * data asynchronously updated elsewhere.  The whole
+		 * thing is just approximation anyway, so that should
+		 * be okay.
+		 */
+
+		if (lk->lk_slow_rate >= UIO_MAX) {
+
+			nd->nd_delay = 0;
+			nd->nd_rate = UIO_MAX;
+
+			nd->nd_tx_deposit = nd->nd_tx_charge + 3 * UIO_MAX;
+			nd->nd_tx_credit  = 3 * UIO_MAX;
+
+		} else {
+
+			long rate;
+			long delay;
+			long deposit;
+			long charge;
+			long size;
+			long excess;
+
+			long seq_in = nd->nd_seq_in;
+			long seq_out = nd->nd_seq_out;
+
+			/*
+			 * If there are no outstanding packets, run at the
+			 * fastest rate.
+			 */
+
+			if (seq_in == seq_out) {
+				delay = 0;
+				rate = lk->lk_fast_rate;
+			}
+
+			/*
+			 * Otherwise compute the transmit rate based on the
+			 * delay since the oldest packet.
+			 */
+
+			else {
+				/*
+				 * The actual delay is computed as the
+				 * time since the oldest unacknowledged
+				 * packet was sent, minus the time it
+				 * took to send that packet to the server.
+				 */
+
+				delay = ((jiffies - nd->nd_seq_time[seq_out])
+					- (nd->nd_seq_size[seq_out] /
+					lk->lk_fast_rate));
+
+				/*
+				 * If the delay is less than the "fast"
+				 * delay, transmit full speed.  If greater
+				 * than the "slow" delay, transmit at the
+				 * "slow" speed.   In between, interpolate
+				 * between the fast and slow speeds.
+				 */
+
+				rate =
+				  (delay <= lk->lk_fast_delay ?
+				    lk->lk_fast_rate :
+				    delay >= lk->lk_slow_delay ?
+				      lk->lk_slow_rate :
+				      (lk->lk_slow_rate +
+				       (lk->lk_slow_delay - delay) *
+				       (lk->lk_fast_rate - lk->lk_slow_rate) /
+				       (lk->lk_slow_delay - lk->lk_fast_delay)
+				      )
+				  );
+			}
+
+			nd->nd_delay = delay;
+			nd->nd_rate = rate;
+
+			/*
+			 * Increase the transmit credit by depositing the
+			 * current transmit rate.
+			 */
+
+			deposit = nd->nd_tx_deposit;
+			charge  = nd->nd_tx_charge;
+
+			deposit += rate;
+
+			/*
+			 * If the available transmit credit becomes too large,
+			 * reduce the deposit to correct the value.
+			 *
+			 * Too large is the max of:
+			 *		6 times the header size
+			 *		3 times the current transmit rate.
+			 */
+
+			size = 2 * nd->nd_link.lk_header_size;
+
+			if (size < rate)
+				size = rate;
+
+			size *= 3;
+
+			excess = deposit - charge - size;
+
+			if (excess > 0)
+				deposit -= excess;
+
+			nd->nd_tx_deposit = deposit;
+			nd->nd_tx_credit  = deposit - charge;
+
+			/*
+			 * Wake the transmit task only if the transmit credit
+			 * is at least 3 times the transmit header size.
+			 */
+
+			size = 3 * lk->lk_header_size;
+
+			if (nd->nd_tx_credit < size)
+				continue;
+		}
+
+
+		/*
+		 * Enable the READ select to wake the daemon if there
+		 * is useful work for the drp_read routine to perform.
+		 */
+
+		if (waitqueue_active(&nd->nd_tx_waitq) &&
+		    (nd->nd_tx_work != 0 ||
+		    (ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX)) {
+			nd->nd_tx_ready = 1;
+
+			wake_up_interruptible(&nd->nd_tx_waitq);
+
+			/* not needed */
+			/* nd->nd_flag &= ~ND_SELECT; */
+		}
+	}
+
+
+	/*
+	 * Schedule ourself back at the nominal wakeup interval.
+	 */
+	spin_lock_irqsave(&poll_data->poll_lock, lock_flags);
+
+	poll_data->node_active_count--;
+	if (poll_data->node_active_count > 0) {
+		poll_data->node_active_count++;
+		poll_time = poll_data->timer.expires +
+			poll_data->poll_tick * HZ / 1000;
+
+		time = poll_time - jiffies;
+
+		if (time >= 2 * poll_data->poll_tick)
+			poll_time = jiffies + dgrp_poll_tick * HZ / 1000;
+
+		poll_data->timer.expires = poll_time;
+		add_timer(&poll_data->timer);
+	}
+
+	spin_unlock_irqrestore(&poll_data->poll_lock, lock_flags);
+}

+ 170 - 0
drivers/staging/dgrp/dgrp_ports_ops.c

@@ -0,0 +1,170 @@
+/*
+ *
+ * Copyright 1999-2000 Digi International (www.digi.com)
+ *     James Puzzo <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_ports_ops.c
+ *
+ *  Description:
+ *
+ *     Handle the file operations required for the /proc/dgrp/ports/...
+ *     devices.  Basically gathers tty status for the node and returns it.
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_ports_open(struct inode *, struct file *);
+
+static const struct file_operations ports_ops = {
+	.owner   = THIS_MODULE,
+	.open    = dgrp_ports_open,
+	.read    = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release
+};
+
+static struct inode_operations ports_inode_ops = {
+	.permission = dgrp_inode_permission
+};
+
+
+void dgrp_register_ports_hook(struct proc_dir_entry *de)
+{
+	struct nd_struct *node = de->data;
+
+	de->proc_iops = &ports_inode_ops;
+	de->proc_fops = &ports_ops;
+	node->nd_ports_de = de;
+}
+
+static void *dgrp_ports_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos == 0)
+		seq_puts(seq, "#num tty_open pr_open tot_wait MSTAT  IFLAG  OFLAG  CFLAG  BPS    DIGIFLAGS\n");
+
+	return pos;
+}
+
+static void *dgrp_ports_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct nd_struct *nd = seq->private;
+
+	if (*pos >= nd->nd_chan_count)
+		return NULL;
+
+	*pos += 1;
+
+	return pos;
+}
+
+static void dgrp_ports_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int dgrp_ports_seq_show(struct seq_file *seq, void *v)
+{
+	loff_t *pos = v;
+	struct nd_struct *nd;
+	struct ch_struct *ch;
+	struct un_struct *tun, *pun;
+	unsigned int totcnt;
+
+	nd = seq->private;
+	if (!nd)
+		return 0;
+
+	if (*pos >= nd->nd_chan_count)
+		return 0;
+
+	ch = &nd->nd_chan[*pos];
+	tun = &ch->ch_tun;
+	pun = &ch->ch_pun;
+
+	/*
+	 * If port is not open and no one is waiting to
+	 * open it, the modem signal values can't be
+	 * trusted, and will be zeroed.
+	 */
+	totcnt = tun->un_open_count +
+		pun->un_open_count +
+		ch->ch_wait_count[0] +
+		ch->ch_wait_count[1] +
+		ch->ch_wait_count[2];
+
+	seq_printf(seq, "%02d      %02d      %02d      %02d     0x%04X 0x%04X 0x%04X 0x%04X %-6d 0x%04X\n",
+		   (int) *pos,
+		   tun->un_open_count,
+		   pun->un_open_count,
+		   ch->ch_wait_count[0] +
+		   ch->ch_wait_count[1] +
+		   ch->ch_wait_count[2],
+		   (totcnt ? ch->ch_s_mlast : 0),
+		   ch->ch_s_iflag,
+		   ch->ch_s_oflag,
+		   ch->ch_s_cflag,
+		   (ch->ch_s_brate ? (1843200 / ch->ch_s_brate) : 0),
+		   ch->ch_digi.digi_flags);
+
+	return 0;
+}
+
+static const struct seq_operations ports_seq_ops = {
+	.start = dgrp_ports_seq_start,
+	.next  = dgrp_ports_seq_next,
+	.stop  = dgrp_ports_seq_stop,
+	.show  = dgrp_ports_seq_show,
+};
+
+/**
+ * dgrp_ports_open -- open the /proc/dgrp/ports/... device
+ * @inode: struct inode *
+ * @file: struct file *
+ *
+ * Open function to open the /proc/dgrp/ports device for a PortServer.
+ * This is the open function for struct file_operations
+ */
+static int dgrp_ports_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	int rtn;
+
+	rtn = seq_open(file, &ports_seq_ops);
+	if (!rtn) {
+		seq = file->private_data;
+		seq->private = PDE(inode)->data;
+	}
+
+	return rtn;
+}

+ 822 - 0
drivers/staging/dgrp/dgrp_specproc.c

@@ -0,0 +1,822 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     James Puzzo  <jamesp at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_specproc.c
+ *
+ *  Description:
+ *
+ *     Handle the "config" proc entry for the linux realport device driver
+ *     and provide slots for the "net" and "mon" devices
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+
+#include "dgrp_common.h"
+
+static struct dgrp_proc_entry dgrp_table[];
+static struct proc_dir_entry *dgrp_proc_dir_entry;
+
+static int dgrp_add_id(long id);
+static int dgrp_remove_nd(struct nd_struct *nd);
+static void unregister_dgrp_device(struct proc_dir_entry *de);
+static void register_dgrp_device(struct nd_struct *node,
+				 struct proc_dir_entry *root,
+				 void (*register_hook)(struct proc_dir_entry *de));
+
+/* File operation declarations */
+static int dgrp_gen_proc_open(struct inode *, struct file *);
+static int dgrp_gen_proc_close(struct inode *, struct file *);
+static int parse_write_config(char *);
+
+
+static const struct file_operations dgrp_proc_file_ops = {
+	.owner   = THIS_MODULE,
+	.open    = dgrp_gen_proc_open,
+	.release = dgrp_gen_proc_close,
+};
+
+static struct inode_operations proc_inode_ops = {
+	.permission = dgrp_inode_permission
+};
+
+
+static void register_proc_table(struct dgrp_proc_entry *,
+				struct proc_dir_entry *);
+static void unregister_proc_table(struct dgrp_proc_entry *,
+				  struct proc_dir_entry *);
+
+static struct dgrp_proc_entry dgrp_net_table[];
+static struct dgrp_proc_entry dgrp_mon_table[];
+static struct dgrp_proc_entry dgrp_ports_table[];
+static struct dgrp_proc_entry dgrp_dpa_table[];
+
+static ssize_t config_proc_write(struct file *file, const char __user *buffer,
+				 size_t count, loff_t *pos);
+
+static int nodeinfo_proc_open(struct inode *inode, struct file *file);
+static int info_proc_open(struct inode *inode, struct file *file);
+static int config_proc_open(struct inode *inode, struct file *file);
+
+static struct file_operations config_proc_file_ops = {
+	.owner	 = THIS_MODULE,
+	.open	 = config_proc_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+	.write   = config_proc_write
+};
+
+static struct file_operations info_proc_file_ops = {
+	.owner	 = THIS_MODULE,
+	.open	 = info_proc_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations nodeinfo_proc_file_ops = {
+	.owner	 = THIS_MODULE,
+	.open	 = nodeinfo_proc_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
+static struct dgrp_proc_entry dgrp_table[] = {
+	{
+		.id = DGRP_CONFIG,
+		.name = "config",
+		.mode = 0644,
+		.proc_file_ops = &config_proc_file_ops,
+	},
+	{
+		.id = DGRP_INFO,
+		.name = "info",
+		.mode = 0644,
+		.proc_file_ops = &info_proc_file_ops,
+	},
+	{
+		.id = DGRP_NODEINFO,
+		.name = "nodeinfo",
+		.mode = 0644,
+		.proc_file_ops = &nodeinfo_proc_file_ops,
+	},
+	{
+		.id = DGRP_NETDIR,
+		.name = "net",
+		.mode = 0500,
+		.child = dgrp_net_table
+	},
+	{
+		.id = DGRP_MONDIR,
+		.name = "mon",
+		.mode = 0500,
+		.child = dgrp_mon_table
+	},
+	{
+		.id = DGRP_PORTSDIR,
+		.name = "ports",
+		.mode = 0500,
+		.child = dgrp_ports_table
+	},
+	{
+		.id = DGRP_DPADIR,
+		.name = "dpa",
+		.mode = 0500,
+		.child = dgrp_dpa_table
+	}
+};
+
+static struct proc_dir_entry *net_entry_pointer;
+static struct proc_dir_entry *mon_entry_pointer;
+static struct proc_dir_entry *dpa_entry_pointer;
+static struct proc_dir_entry *ports_entry_pointer;
+
+static struct dgrp_proc_entry dgrp_net_table[] = {
+	{0}
+};
+
+static struct dgrp_proc_entry dgrp_mon_table[] = {
+	{0}
+};
+
+static struct dgrp_proc_entry dgrp_ports_table[] = {
+	{0}
+};
+
+static struct dgrp_proc_entry dgrp_dpa_table[] = {
+	{0}
+};
+
+void dgrp_unregister_proc(void)
+{
+	unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
+	net_entry_pointer = NULL;
+	mon_entry_pointer = NULL;
+	dpa_entry_pointer = NULL;
+	ports_entry_pointer = NULL;
+
+	if (dgrp_proc_dir_entry) {
+		remove_proc_entry(dgrp_proc_dir_entry->name,
+				  dgrp_proc_dir_entry->parent);
+		dgrp_proc_dir_entry = NULL;
+	}
+
+}
+
+void dgrp_register_proc(void)
+{
+	/*
+	 *	Register /proc/dgrp
+	 */
+	dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
+					  &dgrp_proc_file_ops);
+	register_proc_table(dgrp_table, dgrp_proc_dir_entry);
+}
+
+/*
+ * /proc/sys support
+ */
+static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
+{
+	if (!de || !de->low_ino)
+		return 0;
+	if (de->namelen != len)
+		return 0;
+	return !memcmp(name, de->name, len);
+}
+
+
+/*
+ *  Scan the entries in table and add them all to /proc at the position
+ *  referred to by "root"
+ */
+static void register_proc_table(struct dgrp_proc_entry *table,
+				struct proc_dir_entry *root)
+{
+	struct proc_dir_entry *de;
+	int len;
+	mode_t mode;
+
+	for (; table->id; table++) {
+		/* Can't do anything without a proc name. */
+		if (!table->name)
+			continue;
+
+		/* Maybe we can't do anything with it... */
+		if (!table->proc_file_ops &&
+		    !table->child) {
+			pr_warn("dgrp: Can't register %s\n",
+				table->name);
+			continue;
+		}
+
+		len = strlen(table->name);
+		mode = table->mode;
+
+		de = NULL;
+		if (!table->child)
+			mode |= S_IFREG;
+		else {
+			mode |= S_IFDIR;
+			for (de = root->subdir; de; de = de->next) {
+				if (dgrp_proc_match(len, table->name, de))
+					break;
+			}
+			/* If the subdir exists already, de is non-NULL */
+		}
+
+		if (!de) {
+			de = create_proc_entry(table->name, mode, root);
+			if (!de)
+				continue;
+			de->data = (void *) table;
+			if (!table->child) {
+				de->proc_iops = &proc_inode_ops;
+				if (table->proc_file_ops)
+					de->proc_fops = table->proc_file_ops;
+				else
+					de->proc_fops = &dgrp_proc_file_ops;
+			}
+		}
+		table->de = de;
+		if (de->mode & S_IFDIR)
+			register_proc_table(table->child, de);
+
+		if (table->id == DGRP_NETDIR)
+			net_entry_pointer = de;
+
+		if (table->id == DGRP_MONDIR)
+			mon_entry_pointer = de;
+
+		if (table->id == DGRP_DPADIR)
+			dpa_entry_pointer = de;
+
+		if (table->id == DGRP_PORTSDIR)
+			ports_entry_pointer = de;
+	}
+}
+
+/*
+ * Unregister a /proc sysctl table and any subdirectories.
+ */
+static void unregister_proc_table(struct dgrp_proc_entry *table,
+				  struct proc_dir_entry *root)
+{
+	struct proc_dir_entry *de;
+	struct nd_struct *tmp;
+
+	list_for_each_entry(tmp, &nd_struct_list, list) {
+		if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
+			unregister_dgrp_device(tmp->nd_net_de);
+			dgrp_remove_node_class_sysfs_files(tmp);
+		}
+
+		if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
+			unregister_dgrp_device(tmp->nd_mon_de);
+
+		if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
+			unregister_dgrp_device(tmp->nd_dpa_de);
+
+		if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
+			unregister_dgrp_device(tmp->nd_ports_de);
+	}
+
+	for (; table->id; table++) {
+		de = table->de;
+
+		if (!de)
+			continue;
+		if (de->mode & S_IFDIR) {
+			if (!table->child) {
+				pr_alert("dgrp: malformed sysctl tree on free\n");
+				continue;
+			}
+			unregister_proc_table(table->child, de);
+
+	/* Don't unregister directories which still have entries */
+			if (de->subdir)
+				continue;
+		}
+
+		/* Don't unregister proc entries that are still being used.. */
+		if ((atomic_read(&de->count)) != 1) {
+			pr_alert("proc entry %s in use, not removing\n",
+				de->name);
+			continue;
+		}
+
+		remove_proc_entry(de->name, de->parent);
+		table->de = NULL;
+	}
+}
+
+static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
+{
+	struct proc_dir_entry *de;
+	struct dgrp_proc_entry *entry;
+	int ret = 0;
+
+	de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
+	if (!de || !de->data) {
+		ret = -ENXIO;
+		goto done;
+	}
+
+	entry = (struct dgrp_proc_entry *) de->data;
+	if (!entry) {
+		ret = -ENXIO;
+		goto done;
+	}
+
+	down(&entry->excl_sem);
+
+	if (entry->excl_cnt)
+		ret = -EBUSY;
+	else
+		entry->excl_cnt++;
+
+	up(&entry->excl_sem);
+
+done:
+	return ret;
+}
+
+static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_dir_entry *de;
+	struct dgrp_proc_entry *entry;
+
+	de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
+	if (!de || !de->data)
+		goto done;
+
+	entry = (struct dgrp_proc_entry *) de->data;
+	if (!entry)
+		goto done;
+
+	down(&entry->excl_sem);
+
+	if (entry->excl_cnt)
+		entry->excl_cnt = 0;
+
+	up(&entry->excl_sem);
+
+done:
+	return 0;
+}
+
+static void *config_proc_start(struct seq_file *m, loff_t *pos)
+{
+	return seq_list_start_head(&nd_struct_list, *pos);
+}
+
+static void *config_proc_next(struct seq_file *p, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &nd_struct_list, pos);
+}
+
+static void config_proc_stop(struct seq_file *m, void *v)
+{
+}
+
+static int config_proc_show(struct seq_file *m, void *v)
+{
+	struct nd_struct *nd;
+	char tmp_id[4];
+
+	if (v == &nd_struct_list) {
+		seq_puts(m, "#-----------------------------------------------------------------------------\n");
+		seq_puts(m, "#                        Avail\n");
+		seq_puts(m, "# ID  Major  State       Ports\n");
+		return 0;
+	}
+
+	nd = list_entry(v, struct nd_struct, list);
+
+	ID_TO_CHAR(nd->nd_ID, tmp_id);
+
+	seq_printf(m, "  %-2.2s  %-5ld  %-10.10s  %-5d\n",
+		   tmp_id,
+		   nd->nd_major,
+		   ND_STATE_STR(nd->nd_state),
+		   nd->nd_chan_count);
+
+	return 0;
+}
+
+static const struct seq_operations proc_config_ops = {
+	.start = config_proc_start,
+	.next  = config_proc_next,
+	.stop  = config_proc_stop,
+	.show  = config_proc_show
+};
+
+static int config_proc_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &proc_config_ops);
+}
+
+
+/*
+ *  When writing configuration information, each "record" (i.e. each
+ *  write) is treated as an independent request.  See the "parse"
+ *  description for more details.
+ */
+static ssize_t config_proc_write(struct file *file, const char __user *buffer,
+				 size_t count, loff_t *pos)
+{
+	ssize_t retval;
+	char *inbuf, *sp;
+	char *line, *ldelim;
+
+	if (count > 32768)
+		return -EINVAL;
+
+	inbuf = sp = vzalloc(count + 1);
+	if (!inbuf)
+		return -ENOMEM;
+
+	if (copy_from_user(inbuf, buffer, count)) {
+		retval = -EFAULT;
+		goto done;
+	}
+
+	inbuf[count] = 0;
+
+	ldelim = "\n";
+
+	line = strpbrk(sp, ldelim);
+	while (line) {
+		*line = 0;
+		retval = parse_write_config(sp);
+		if (retval)
+			goto done;
+
+		sp = line + 1;
+		line = strpbrk(sp, ldelim);
+	}
+
+	retval = count;
+done:
+	vfree(inbuf);
+	return retval;
+}
+
+/*
+ *  ------------------------------------------------------------------------
+ *
+ *  The following are the functions to parse input
+ *
+ *  ------------------------------------------------------------------------
+ */
+static inline char *skip_past_ws(const char *str)
+{
+	while ((*str) && !isspace(*str))
+		++str;
+
+	return skip_spaces(str);
+}
+
+static int parse_id(char **c, char *cID)
+{
+	int tmp = **c;
+
+	if (isalnum(tmp) || (tmp == '_'))
+		cID[0] = tmp;
+	else
+		return -EINVAL;
+
+	(*c)++; tmp = **c;
+
+	if (isalnum(tmp) || (tmp == '_')) {
+		cID[1] = tmp;
+		(*c)++;
+	} else
+		cID[1] = 0;
+
+	return 0;
+}
+
+static int parse_add_config(char *buf)
+{
+	char *c = buf;
+	int  retval;
+	char cID[2];
+	long ID;
+
+	c = skip_past_ws(c);
+
+	retval = parse_id(&c, cID);
+	if (retval < 0)
+		return retval;
+
+	ID = CHAR_TO_ID(cID);
+
+	c = skip_past_ws(c);
+
+	return dgrp_add_id(ID);
+}
+
+static int parse_del_config(char *buf)
+{
+	char *c = buf;
+	int  retval;
+	struct nd_struct *nd;
+	char cID[2];
+	long ID;
+	long major;
+
+	c = skip_past_ws(c);
+
+	retval = parse_id(&c, cID);
+	if (retval < 0)
+		return retval;
+
+	ID = CHAR_TO_ID(cID);
+
+	c = skip_past_ws(c);
+
+	retval = kstrtol(c, 10, &major);
+	if (retval)
+		return retval;
+
+	nd = nd_struct_get(major);
+	if (!nd)
+		return -EINVAL;
+
+	if ((nd->nd_major != major) || (nd->nd_ID != ID))
+		return -EINVAL;
+
+	return dgrp_remove_nd(nd);
+}
+
+static int parse_chg_config(char *buf)
+{
+	return -EINVAL;
+}
+
+/*
+ *  The passed character buffer represents a single configuration request.
+ *  If the first character is a "+", it is parsed as a request to add a
+ *     PortServer
+ *  If the first character is a "-", it is parsed as a request to delete a
+ *     PortServer
+ *  If the first character is a "*", it is parsed as a request to change a
+ *     PortServer
+ *  Any other character (including whitespace) causes the record to be
+ *     ignored.
+ */
+static int parse_write_config(char *buf)
+{
+	int retval;
+
+	switch (buf[0]) {
+	case '+':
+		retval = parse_add_config(buf);
+		break;
+	case '-':
+		retval = parse_del_config(buf);
+		break;
+	case '*':
+		retval = parse_chg_config(buf);
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static int info_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "version: %s\n", DIGI_VERSION);
+	seq_puts(m, "register_with_sysfs: 1\n");
+	seq_printf(m, "rawreadok: 0x%08x\t(%d)\n",
+		   dgrp_rawreadok, dgrp_rawreadok);
+	seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
+		   dgrp_poll_tick, dgrp_poll_tick);
+
+	return 0;
+}
+
+static int info_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, info_proc_show, NULL);
+}
+
+
+static void *nodeinfo_start(struct seq_file *m, loff_t *pos)
+{
+	return seq_list_start_head(&nd_struct_list, *pos);
+}
+
+static void *nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &nd_struct_list, pos);
+}
+
+static void nodeinfo_stop(struct seq_file *m, void *v)
+{
+}
+
+static int nodeinfo_show(struct seq_file *m, void *v)
+{
+	struct nd_struct *nd;
+	char hwver[8];
+	char swver[8];
+	char tmp_id[4];
+
+	if (v == &nd_struct_list) {
+		seq_puts(m, "#-----------------------------------------------------------------------------\n");
+		seq_puts(m, "#                 HW       HW   SW\n");
+		seq_puts(m, "# ID  State       Version  ID   Version  Description\n");
+		return 0;
+	}
+
+	nd = list_entry(v, struct nd_struct, list);
+
+	ID_TO_CHAR(nd->nd_ID, tmp_id);
+
+	if (nd->nd_state == NS_READY) {
+		sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
+			nd->nd_hw_ver & 0xff);
+		sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
+			nd->nd_sw_ver & 0xff);
+		seq_printf(m, "  %-2.2s  %-10.10s  %-7.7s  %-3d  %-7.7s  %-35.35s\n",
+			   tmp_id,
+			   ND_STATE_STR(nd->nd_state),
+			   hwver,
+			   nd->nd_hw_id,
+			   swver,
+			   nd->nd_ps_desc);
+
+	} else {
+		seq_printf(m, "  %-2.2s  %-10.10s\n",
+			   tmp_id,
+			   ND_STATE_STR(nd->nd_state));
+	}
+
+	return 0;
+}
+
+
+static const struct seq_operations nodeinfo_ops = {
+	.start = nodeinfo_start,
+	.next  = nodeinfo_next,
+	.stop  = nodeinfo_stop,
+	.show  = nodeinfo_show
+};
+
+static int nodeinfo_proc_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &nodeinfo_ops);
+}
+
+/**
+ * dgrp_add_id() -- creates new nd struct and adds it to list
+ * @id: id of device to add
+ */
+static int dgrp_add_id(long id)
+{
+	struct nd_struct *nd;
+	int ret;
+	int i;
+
+	nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
+	if (!nd)
+		return -ENOMEM;
+
+	nd->nd_major = 0;
+	nd->nd_ID = id;
+
+	spin_lock_init(&nd->nd_lock);
+
+	init_waitqueue_head(&nd->nd_tx_waitq);
+	init_waitqueue_head(&nd->nd_mon_wqueue);
+	init_waitqueue_head(&nd->nd_dpa_wqueue);
+	for (i = 0; i < SEQ_MAX; i++)
+		init_waitqueue_head(&nd->nd_seq_wque[i]);
+
+	/* setup the structures to get the major number */
+	ret = dgrp_tty_init(nd);
+	if (ret)
+		goto error_out;
+
+	nd->nd_major = nd->nd_serial_ttdriver->major;
+
+	ret = nd_struct_add(nd);
+	if (ret)
+		goto error_out;
+
+	register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
+	register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
+	register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
+	register_dgrp_device(nd, ports_entry_pointer,
+			      dgrp_register_ports_hook);
+
+	return 0;
+
+error_out:
+	kfree(nd);
+	return ret;
+
+}
+
+static int dgrp_remove_nd(struct nd_struct *nd)
+{
+	int ret;
+
+	/* Check to see if the selected structure is in use */
+	if (nd->nd_tty_ref_cnt)
+		return -EBUSY;
+
+	if (nd->nd_net_de) {
+		unregister_dgrp_device(nd->nd_net_de);
+		dgrp_remove_node_class_sysfs_files(nd);
+	}
+
+	if (nd->nd_mon_de)
+		unregister_dgrp_device(nd->nd_mon_de);
+
+	if (nd->nd_ports_de)
+		unregister_dgrp_device(nd->nd_ports_de);
+
+	if (nd->nd_dpa_de)
+		unregister_dgrp_device(nd->nd_dpa_de);
+
+	dgrp_tty_uninit(nd);
+
+	ret = nd_struct_del(nd);
+	if (ret)
+		return ret;
+
+	kfree(nd);
+	return 0;
+}
+
+static void register_dgrp_device(struct nd_struct *node,
+				 struct proc_dir_entry *root,
+				 void (*register_hook)(struct proc_dir_entry *de))
+{
+	char buf[3];
+	struct proc_dir_entry *de;
+
+	ID_TO_CHAR(node->nd_ID, buf);
+
+	de = create_proc_entry(buf, 0600 | S_IFREG, root);
+	if (!de)
+		return;
+
+	de->data = (void *) node;
+
+	if (register_hook)
+		register_hook(de);
+
+}
+
+static void unregister_dgrp_device(struct proc_dir_entry *de)
+{
+	if (!de)
+		return;
+
+	/* Don't unregister proc entries that are still being used.. */
+	if ((atomic_read(&de->count)) != 1) {
+		pr_alert("%s - proc entry %s in use. Not removing.\n",
+			 __func__, de->name);
+		return;
+	}
+
+	remove_proc_entry(de->name, de->parent);
+	de = NULL;
+}

+ 555 - 0
drivers/staging/dgrp/dgrp_sysfs.c

@@ -0,0 +1,555 @@
+/*
+ * Copyright 2004 Digi International (www.digi.com)
+ *      Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+#include "dgrp_common.h"
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/serial_reg.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+
+
+#define PORTSERVER_DIVIDEND 1843200
+#define SERIAL_TYPE_NORMAL      1
+#define SERIAL_TYPE_CALLOUT     2
+#define SERIAL_TYPE_XPRINT      3
+
+
+static struct class *dgrp_class;
+static struct device *dgrp_class_nodes_dev;
+static struct device *dgrp_class_global_settings_dev;
+
+
+static ssize_t dgrp_class_version_show(struct class *class,
+				       struct class_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", DIGI_VERSION);
+}
+static CLASS_ATTR(driver_version, 0400, dgrp_class_version_show, NULL);
+
+
+static ssize_t dgrp_class_register_with_sysfs_show(struct device *c,
+						   struct device_attribute *attr,
+						   char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "1\n");
+}
+static DEVICE_ATTR(register_with_sysfs, 0400,
+		   dgrp_class_register_with_sysfs_show, NULL);
+
+
+static ssize_t dgrp_class_rawreadok_show(struct device *c,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok);
+}
+static ssize_t dgrp_class_rawreadok_store(struct device *c,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	sscanf(buf, "0x%x\n", &dgrp_rawreadok);
+	return count;
+}
+static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show,
+		   dgrp_class_rawreadok_store);
+
+
+static ssize_t dgrp_class_pollrate_show(struct device *c,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_poll_tick);
+}
+
+static ssize_t dgrp_class_pollrate_store(struct device *c,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	sscanf(buf, "0x%x\n", &dgrp_poll_tick);
+	return count;
+}
+static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show,
+		   dgrp_class_pollrate_store);
+
+static struct attribute *dgrp_sysfs_global_settings_entries[] = {
+	&dev_attr_pollrate.attr,
+	&dev_attr_rawreadok.attr,
+	&dev_attr_register_with_sysfs.attr,
+	NULL
+};
+
+
+static struct attribute_group dgrp_global_settings_attribute_group = {
+	.name = NULL,
+	.attrs = dgrp_sysfs_global_settings_entries,
+};
+
+
+
+void dgrp_create_class_sysfs_files(void)
+{
+	int ret = 0;
+	int max_majors = 1U << (32 - MINORBITS);
+
+	dgrp_class = class_create(THIS_MODULE, "digi_realport");
+	ret = class_create_file(dgrp_class, &class_attr_driver_version);
+
+	dgrp_class_global_settings_dev = device_create(dgrp_class, NULL,
+		MKDEV(0, max_majors + 1), NULL, "driver_settings");
+
+	ret = sysfs_create_group(&dgrp_class_global_settings_dev->kobj,
+		&dgrp_global_settings_attribute_group);
+	if (ret) {
+		pr_alert("%s: failed to create sysfs global settings device attributes.\n",
+			__func__);
+		sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
+			&dgrp_global_settings_attribute_group);
+		return;
+	}
+
+	dgrp_class_nodes_dev = device_create(dgrp_class, NULL,
+		MKDEV(0, max_majors + 2), NULL, "nodes");
+
+}
+
+
+void dgrp_remove_class_sysfs_files(void)
+{
+	struct nd_struct *nd;
+	int max_majors = 1U << (32 - MINORBITS);
+
+	list_for_each_entry(nd, &nd_struct_list, list)
+		dgrp_remove_node_class_sysfs_files(nd);
+
+	sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
+		&dgrp_global_settings_attribute_group);
+
+	class_remove_file(dgrp_class, &class_attr_driver_version);
+
+	device_destroy(dgrp_class, MKDEV(0, max_majors + 1));
+	device_destroy(dgrp_class, MKDEV(0, max_majors + 2));
+	class_destroy(dgrp_class);
+}
+
+static ssize_t dgrp_node_state_show(struct device *c,
+				    struct device_attribute *attr, char *buf)
+{
+	struct nd_struct *nd;
+
+	if (!c)
+		return 0;
+	nd = (struct nd_struct *) dev_get_drvdata(c);
+	if (!nd)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", ND_STATE_STR(nd->nd_state));
+}
+
+static DEVICE_ATTR(state, 0600, dgrp_node_state_show, NULL);
+
+static ssize_t dgrp_node_description_show(struct device *c,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct nd_struct *nd;
+
+	if (!c)
+		return 0;
+	nd = (struct nd_struct *) dev_get_drvdata(c);
+	if (!nd)
+		return 0;
+
+	if (nd->nd_state == NS_READY && nd->nd_ps_desc)
+		return snprintf(buf, PAGE_SIZE, "%s\n", nd->nd_ps_desc);
+	return 0;
+}
+static DEVICE_ATTR(description_info, 0600, dgrp_node_description_show, NULL);
+
+static ssize_t dgrp_node_hw_version_show(struct device *c,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct nd_struct *nd;
+
+	if (!c)
+		return 0;
+	nd = (struct nd_struct *) dev_get_drvdata(c);
+	if (!nd)
+		return 0;
+
+	if (nd->nd_state == NS_READY)
+		return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+				(nd->nd_hw_ver >> 8) & 0xff,
+				nd->nd_hw_ver & 0xff);
+
+	return 0;
+}
+static DEVICE_ATTR(hw_version_info, 0600, dgrp_node_hw_version_show, NULL);
+
+static ssize_t dgrp_node_hw_id_show(struct device *c,
+				    struct device_attribute *attr, char *buf)
+{
+	struct nd_struct *nd;
+
+	if (!c)
+		return 0;
+	nd = (struct nd_struct *) dev_get_drvdata(c);
+	if (!nd)
+		return 0;
+
+
+	if (nd->nd_state == NS_READY)
+		return snprintf(buf, PAGE_SIZE, "%d\n", nd->nd_hw_id);
+	return 0;
+}
+static DEVICE_ATTR(hw_id_info, 0600, dgrp_node_hw_id_show, NULL);
+
+static ssize_t dgrp_node_sw_version_show(struct device *c,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct nd_struct *nd;
+
+	if (!c)
+		return 0;
+
+	nd = (struct nd_struct *) dev_get_drvdata(c);
+	if (!nd)
+		return 0;
+
+	if (nd->nd_state == NS_READY)
+		return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+				(nd->nd_sw_ver >> 8) & 0xff,
+				nd->nd_sw_ver & 0xff);
+
+	return 0;
+}
+static DEVICE_ATTR(sw_version_info, 0600, dgrp_node_sw_version_show, NULL);
+
+
+static struct attribute *dgrp_sysfs_node_entries[] = {
+	&dev_attr_state.attr,
+	&dev_attr_description_info.attr,
+	&dev_attr_hw_version_info.attr,
+	&dev_attr_hw_id_info.attr,
+	&dev_attr_sw_version_info.attr,
+	NULL
+};
+
+
+static struct attribute_group dgrp_node_attribute_group = {
+	.name = NULL,
+	.attrs = dgrp_sysfs_node_entries,
+};
+
+
+void dgrp_create_node_class_sysfs_files(struct nd_struct *nd)
+{
+	int ret;
+	char name[10];
+
+	if (nd->nd_ID)
+		ID_TO_CHAR(nd->nd_ID, name);
+	else
+		sprintf(name, "node%ld", nd->nd_major);
+
+	nd->nd_class_dev = device_create(dgrp_class, dgrp_class_nodes_dev,
+		MKDEV(0, nd->nd_major), NULL, name);
+
+	ret = sysfs_create_group(&nd->nd_class_dev->kobj,
+				 &dgrp_node_attribute_group);
+
+	if (ret) {
+		pr_alert("%s: failed to create sysfs node device attributes.\n",
+			__func__);
+		sysfs_remove_group(&nd->nd_class_dev->kobj,
+				   &dgrp_node_attribute_group);
+		return;
+	}
+
+	dev_set_drvdata(nd->nd_class_dev, nd);
+
+}
+
+
+void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd)
+{
+	if (nd->nd_class_dev) {
+		sysfs_remove_group(&nd->nd_class_dev->kobj,
+				   &dgrp_node_attribute_group);
+
+		device_destroy(dgrp_class, MKDEV(0, nd->nd_major));
+		nd->nd_class_dev = NULL;
+	}
+}
+
+
+
+static ssize_t dgrp_tty_state_show(struct device *d,
+				   struct device_attribute *attr, char *buf)
+{
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			un->un_open_count ? "Open" : "Closed");
+}
+static DEVICE_ATTR(state_info, 0600, dgrp_tty_state_show, NULL);
+
+static ssize_t dgrp_tty_baud_show(struct device *d,
+				  struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		un->un_open_count ? (PORTSERVER_DIVIDEND / ch->ch_s_brate) : 0);
+}
+static DEVICE_ATTR(baud_info, 0400, dgrp_tty_baud_show, NULL);
+
+
+static ssize_t dgrp_tty_msignals_show(struct device *d,
+				      struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+
+	if (ch->ch_open_count) {
+		return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+			(ch->ch_s_mlast & DM_RTS) ? "RTS" : "",
+			(ch->ch_s_mlast & DM_CTS) ? "CTS" : "",
+			(ch->ch_s_mlast & DM_DTR) ? "DTR" : "",
+			(ch->ch_s_mlast & DM_DSR) ? "DSR" : "",
+			(ch->ch_s_mlast & DM_CD) ? "DCD" : "",
+			(ch->ch_s_mlast & DM_RI)  ? "RI"  : "");
+	}
+	return 0;
+}
+static DEVICE_ATTR(msignals_info, 0400, dgrp_tty_msignals_show, NULL);
+
+
+static ssize_t dgrp_tty_iflag_show(struct device *d,
+				   struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_iflag);
+}
+static DEVICE_ATTR(iflag_info, 0600, dgrp_tty_iflag_show, NULL);
+
+
+static ssize_t dgrp_tty_cflag_show(struct device *d,
+				   struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_cflag);
+}
+static DEVICE_ATTR(cflag_info, 0600, dgrp_tty_cflag_show, NULL);
+
+
+static ssize_t dgrp_tty_oflag_show(struct device *d,
+				   struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_oflag);
+}
+static DEVICE_ATTR(oflag_info, 0600, dgrp_tty_oflag_show, NULL);
+
+
+static ssize_t dgrp_tty_digi_flag_show(struct device *d,
+				       struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
+}
+static DEVICE_ATTR(digi_flag_info, 0600, dgrp_tty_digi_flag_show, NULL);
+
+
+static ssize_t dgrp_tty_rxcount_show(struct device *d,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_rxcount);
+}
+static DEVICE_ATTR(rxcount_info, 0600, dgrp_tty_rxcount_show, NULL);
+
+
+static ssize_t dgrp_tty_txcount_show(struct device *d,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_txcount);
+}
+static DEVICE_ATTR(txcount_info, 0600, dgrp_tty_txcount_show, NULL);
+
+
+static ssize_t dgrp_tty_name_show(struct device *d,
+				  struct device_attribute *attr, char *buf)
+{
+	struct nd_struct *nd;
+	struct ch_struct *ch;
+	struct un_struct *un;
+	char name[10];
+
+	if (!d)
+		return 0;
+	un = (struct un_struct *) dev_get_drvdata(d);
+	if (!un)
+		return 0;
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+	nd = ch->ch_nd;
+	if (!nd)
+		return 0;
+
+	ID_TO_CHAR(nd->nd_ID, name);
+
+	return snprintf(buf, PAGE_SIZE, "%s%s%02d\n",
+		un->un_type == SERIAL_TYPE_XPRINT ? "pr" : "tty",
+		name, ch->ch_portnum);
+}
+static DEVICE_ATTR(custom_name, 0600, dgrp_tty_name_show, NULL);
+
+
+static struct attribute *dgrp_sysfs_tty_entries[] = {
+	&dev_attr_state_info.attr,
+	&dev_attr_baud_info.attr,
+	&dev_attr_msignals_info.attr,
+	&dev_attr_iflag_info.attr,
+	&dev_attr_cflag_info.attr,
+	&dev_attr_oflag_info.attr,
+	&dev_attr_digi_flag_info.attr,
+	&dev_attr_rxcount_info.attr,
+	&dev_attr_txcount_info.attr,
+	&dev_attr_custom_name.attr,
+	NULL
+};
+
+
+static struct attribute_group dgrp_tty_attribute_group = {
+	.name = NULL,
+	.attrs = dgrp_sysfs_tty_entries,
+};
+
+
+void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c)
+{
+	int ret;
+
+	ret = sysfs_create_group(&c->kobj, &dgrp_tty_attribute_group);
+	if (ret) {
+		pr_alert("%s: failed to create sysfs tty device attributes.\n",
+			__func__);
+		sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
+		return;
+	}
+
+	dev_set_drvdata(c, un);
+
+}
+
+
+void dgrp_remove_tty_sysfs(struct device *c)
+{
+	sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
+}

+ 3331 - 0
drivers/staging/dgrp/dgrp_tty.c

@@ -0,0 +1,3331 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     Gene Olson    <Gene_Olson at digi dot com>
+ *     James Puzzo   <jamesp at digi dot com>
+ *     Jeff Randall
+ *     Scott Kilau   <scottk at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ *  Filename:
+ *
+ *     dgrp_tty.c
+ *
+ *  Description:
+ *
+ *     This file implements the tty driver functionality for the
+ *     RealPort driver software.
+ *
+ *  Author:
+ *
+ *     James A. Puzzo
+ *     Ann-Marie Westgate
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sched.h>
+
+#include "dgrp_common.h"
+
+#ifndef _POSIX_VDISABLE
+#define   _POSIX_VDISABLE ('\0')
+#endif
+
+/*
+ *	forward declarations
+ */
+
+static void drp_param(struct ch_struct *);
+static void dgrp_tty_close(struct tty_struct *, struct file *);
+
+/* ioctl helper functions */
+static int set_modem_info(struct ch_struct *, unsigned int, unsigned int *);
+static int get_modem_info(struct ch_struct *, unsigned int *);
+static void dgrp_set_custom_speed(struct ch_struct *, int);
+static int dgrp_tty_digigetedelay(struct tty_struct *, int *);
+static int dgrp_tty_digisetedelay(struct tty_struct *, int *);
+static int dgrp_send_break(struct ch_struct *, int);
+
+static ushort  tty_to_ch_flags(struct tty_struct *, char);
+static tcflag_t ch_to_tty_flags(unsigned short, char);
+
+static void dgrp_tty_input_start(struct tty_struct *);
+static void dgrp_tty_input_stop(struct tty_struct *);
+
+static void drp_wmove(struct ch_struct *, int, void*, int);
+
+static int dgrp_tty_open(struct tty_struct *, struct file *);
+static void dgrp_tty_close(struct tty_struct *, struct file *);
+static int dgrp_tty_write(struct tty_struct *, const unsigned char *, int);
+static int dgrp_tty_write_room(struct tty_struct *);
+static void dgrp_tty_flush_buffer(struct tty_struct *);
+static int dgrp_tty_chars_in_buffer(struct tty_struct *);
+static int dgrp_tty_ioctl(struct tty_struct *, unsigned int, unsigned long);
+static void dgrp_tty_set_termios(struct tty_struct *, struct ktermios *);
+static void dgrp_tty_stop(struct tty_struct *);
+static void dgrp_tty_start(struct tty_struct *);
+static void dgrp_tty_throttle(struct tty_struct *);
+static void dgrp_tty_unthrottle(struct tty_struct *);
+static void dgrp_tty_hangup(struct tty_struct *);
+static int dgrp_tty_put_char(struct tty_struct *, unsigned char);
+static int dgrp_tty_tiocmget(struct tty_struct *);
+static int dgrp_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+static int dgrp_tty_send_break(struct tty_struct *, int);
+static void dgrp_tty_send_xchar(struct tty_struct *, char);
+
+/*
+ *	tty defines
+ */
+#define	SERIAL_TYPE_NORMAL	1
+#define	SERIAL_TYPE_CALLOUT	2
+#define	SERIAL_TYPE_XPRINT	3
+
+
+/*
+ *	tty globals/statics
+ */
+
+
+#define PORTSERVER_DIVIDEND	1843200
+
+/*
+ *  Default transparent print information.
+ */
+static struct digi_struct digi_init = {
+	.digi_flags   = DIGI_COOK,	/* Flags			*/
+	.digi_maxcps  = 100,		/* Max CPS			*/
+	.digi_maxchar = 50,		/* Max chars in print queue	*/
+	.digi_bufsize = 100,		/* Printer buffer size		*/
+	.digi_onlen   = 4,		/* size of printer on string	*/
+	.digi_offlen  = 4,		/* size of printer off string	*/
+	.digi_onstr   = "\033[5i",	/* ANSI printer on string	*/
+	.digi_offstr  = "\033[4i",	/* ANSI printer off string	*/
+	.digi_term    = "ansi"		/* default terminal type	*/
+};
+
+/*
+ *	Define a local default termios struct. All ports will be created
+ *	with this termios initially.
+ *
+ *	This defines a raw port at 9600 baud, 8 data bits, no parity,
+ *	1 stop bit.
+ */
+static struct ktermios DefaultTermios = {
+	.c_iflag = (ICRNL | IXON),
+	.c_oflag = (OPOST | ONLCR),
+	.c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+	.c_lflag = (ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL
+		    | ECHOKE | IEXTEN),
+	.c_cc    = INIT_C_CC,
+	.c_line  = 0,
+};
+
+/* Define our tty operations struct */
+static const struct tty_operations dgrp_tty_ops = {
+	.open            = dgrp_tty_open,
+	.close           = dgrp_tty_close,
+	.write           = dgrp_tty_write,
+	.write_room      = dgrp_tty_write_room,
+	.flush_buffer    = dgrp_tty_flush_buffer,
+	.chars_in_buffer = dgrp_tty_chars_in_buffer,
+	.flush_chars     = NULL,
+	.ioctl           = dgrp_tty_ioctl,
+	.set_termios     = dgrp_tty_set_termios,
+	.stop            = dgrp_tty_stop,
+	.start           = dgrp_tty_start,
+	.throttle        = dgrp_tty_throttle,
+	.unthrottle      = dgrp_tty_unthrottle,
+	.hangup          = dgrp_tty_hangup,
+	.put_char        = dgrp_tty_put_char,
+	.tiocmget        = dgrp_tty_tiocmget,
+	.tiocmset        = dgrp_tty_tiocmset,
+	.break_ctl       = dgrp_tty_send_break,
+	.send_xchar      = dgrp_tty_send_xchar
+};
+
+
+static int calc_baud_rate(struct un_struct *un)
+{
+	int i;
+	int brate;
+
+	struct baud_rates {
+		unsigned int rate;
+		unsigned int cflag;
+	};
+
+	static struct baud_rates baud_rates[] = {
+		{ 921600, B921600 },
+		{ 460800, B460800 },
+		{ 230400, B230400 },
+		{ 115200, B115200 },
+		{  57600, B57600  },
+		{  38400, B38400  },
+		{  19200, B19200  },
+		{   9600, B9600   },
+		{   4800, B4800   },
+		{   2400, B2400   },
+		{   1200, B1200   },
+		{    600, B600    },
+		{    300, B300    },
+		{    200, B200    },
+		{    150, B150    },
+		{    134, B134    },
+		{    110, B110    },
+		{     75, B75     },
+		{     50, B50     },
+		{      0, B9600  }
+	};
+
+	brate = C_BAUD(un->un_tty);
+
+	for (i = 0; baud_rates[i].rate; i++) {
+		if (baud_rates[i].cflag == brate)
+			break;
+	}
+
+	return baud_rates[i].rate;
+}
+
+static int calc_fastbaud_rate(struct un_struct *un, struct ktermios *uts)
+{
+	int i;
+	int brate;
+
+	ulong bauds[2][16] = {
+		{ /* fastbaud*/
+			0,      57600,	 76800,	115200,
+			131657, 153600, 230400, 460800,
+			921600, 1200,   1800,	2400,
+			4800,   9600,	19200,	38400 },
+		{ /* fastbaud & CBAUDEX */
+			0,      57600,	115200,	230400,
+			460800, 150,    200,    921600,
+			600,    1200,   1800,	2400,
+			4800,   9600,	19200,	38400 }
+	};
+
+	brate = C_BAUD(un->un_tty) & 0xff;
+
+	i = (uts->c_cflag & CBAUDEX) ? 1 : 0;
+
+
+	if ((i >= 0) && (i < 2) && (brate >= 0) && (brate < 16))
+		brate = bauds[i][brate];
+	else
+		brate = 0;
+
+	return brate;
+}
+
+/**
+ * drp_param() -- send parameter values to be sent to the node
+ * @ch: channel structure of port to modify
+ *
+ * Interprets the tty and modem changes made by an application
+ * program (by examining the termios structures) and sets up
+ * parameter values to be sent to the node.
+ */
+static void drp_param(struct ch_struct *ch)
+{
+	struct nd_struct *nd;
+	struct un_struct *un;
+	int   brate;
+	int   mflow;
+	int   xflag;
+	int   iflag;
+	struct ktermios *tts, *pts, *uts;
+
+	nd = ch->ch_nd;
+
+	/*
+	 *  If the terminal device is open, use it to set up all tty
+	 *  modes and functions.  Otherwise use the printer device.
+	 */
+
+	if (ch->ch_tun.un_open_count) {
+
+		un = &ch->ch_tun;
+		tts = &ch->ch_tun.un_tty->termios;
+
+		/*
+		 *  If both devices are open, copy critical line
+		 *  parameters from the tty device to the printer,
+		 *  so that if the tty is closed, the printer will
+		 *  continue without disruption.
+		 */
+
+		if (ch->ch_pun.un_open_count) {
+
+			pts = &ch->ch_pun.un_tty->termios;
+
+			pts->c_cflag ^=
+				(pts->c_cflag ^ tts->c_cflag) &
+				(CBAUD  | CSIZE | CSTOPB | CREAD | PARENB |
+				 PARODD | HUPCL | CLOCAL);
+
+			pts->c_iflag ^=
+				(pts->c_iflag ^ tts->c_iflag) &
+				(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK |
+				 ISTRIP | IXON   | IXANY  | IXOFF);
+
+			pts->c_cc[VSTART] = tts->c_cc[VSTART];
+			pts->c_cc[VSTOP] = tts->c_cc[VSTOP];
+		}
+	} else if (ch->ch_pun.un_open_count == 0) {
+		pr_warn("%s - ch_pun.un_open_count shouldn't be 0\n",
+		       __func__);
+		return;
+	} else {
+		un = &ch->ch_pun;
+	}
+
+	uts = &un->un_tty->termios;
+
+	/*
+	 * Determine if FAST writes can be performed.
+	 */
+
+	if ((ch->ch_digi.digi_flags & DIGI_COOK) != 0 &&
+	    (ch->ch_tun.un_open_count != 0)  &&
+	    !((un->un_tty)->ldisc->ops->flags & LDISC_FLAG_DEFINED) &&
+	    !(L_XCASE(un->un_tty))) {
+		ch->ch_flag |= CH_FAST_WRITE;
+	} else {
+		ch->ch_flag &= ~CH_FAST_WRITE;
+	}
+
+	/*
+	 *  If FAST writes can be performed, and OPOST is on in the
+	 *  terminal device, do OPOST handling in the server.
+	 */
+
+	if ((ch->ch_flag & CH_FAST_WRITE) &&
+	      O_OPOST(un->un_tty) != 0) {
+		int oflag = tty_to_ch_flags(un->un_tty, 'o');
+
+		/* add to ch_ocook any processing flags set in the termio */
+		ch->ch_ocook |= oflag & (OF_OLCUC |
+					 OF_ONLCR |
+					 OF_OCRNL |
+					 OF_ONLRET |
+					 OF_TABDLY);
+
+		/*
+		 * the hpux driver clears any flags set in ch_ocook
+		 * from the termios oflag.  It is STILL reported though
+		 * by a TCGETA
+		 */
+
+		oflag = ch_to_tty_flags(ch->ch_ocook, 'o');
+		uts->c_oflag &= ~oflag;
+
+	} else {
+		/* clear the ch->ch_ocook flag */
+		int oflag = ch_to_tty_flags(ch->ch_ocook, 'o');
+		uts->c_oflag |= oflag;
+		ch->ch_ocook = 0;
+	}
+
+	ch->ch_oflag = ch->ch_ocook;
+
+
+	ch->ch_flag &= ~CH_FAST_READ;
+
+	/*
+	 *  Generate channel flags
+	 */
+
+	if (C_BAUD(un->un_tty) == B0) {
+		if (!(ch->ch_flag & CH_BAUD0)) {
+			/* TODO : the HPUX driver flushes line */
+			/* TODO : discipline, I assume I don't have to */
+
+			ch->ch_tout = ch->ch_tin;
+			ch->ch_rout = ch->ch_rin;
+
+			ch->ch_break_time = 0;
+
+			ch->ch_send |= RR_TX_FLUSH | RR_RX_FLUSH;
+
+			ch->ch_mout &= ~(DM_DTR | DM_RTS);
+
+			ch->ch_flag |= CH_BAUD0;
+		}
+	} else if (ch->ch_custom_speed) {
+		ch->ch_brate = PORTSERVER_DIVIDEND / ch->ch_custom_speed ;
+
+		if (ch->ch_flag & CH_BAUD0) {
+			ch->ch_mout |= DM_DTR | DM_RTS;
+
+			ch->ch_flag &= ~CH_BAUD0;
+		}
+	} else {
+		/*
+		 * Baud rate mapping.
+		 *
+		 * If FASTBAUD isn't on, we can scan the new baud rate list
+		 * as required.
+		 *
+		 * However, if FASTBAUD is on, we must go to the old
+		 * baud rate mapping that existed many many moons ago,
+		 * for compatibility reasons.
+		 */
+
+		if (!(ch->ch_digi.digi_flags & DIGI_FAST))
+			brate = calc_baud_rate(un);
+		else
+			brate = calc_fastbaud_rate(un, uts);
+
+		if (brate == 0)
+			brate = 9600;
+
+		ch->ch_brate = PORTSERVER_DIVIDEND / brate;
+
+		if (ch->ch_flag & CH_BAUD0) {
+			ch->ch_mout |= DM_DTR | DM_RTS;
+
+			ch->ch_flag &= ~CH_BAUD0;
+		}
+	}
+
+	/*
+	 *  Generate channel cflags from the termio.
+	 */
+
+	ch->ch_cflag = tty_to_ch_flags(un->un_tty, 'c');
+
+	/*
+	 *  Generate channel iflags from the termio.
+	 */
+
+	iflag = (int) tty_to_ch_flags(un->un_tty, 'i');
+
+	if (START_CHAR(un->un_tty) == _POSIX_VDISABLE ||
+	    STOP_CHAR(un->un_tty) == _POSIX_VDISABLE) {
+		iflag &= ~(IF_IXON | IF_IXANY | IF_IXOFF);
+	}
+
+	ch->ch_iflag = iflag;
+
+	/*
+	 *  Generate flow control characters
+	 */
+
+	/*
+	 * From the POSIX.1 spec (7.1.2.6): "If {_POSIX_VDISABLE}
+	 * is defined for the terminal device file, and the value
+	 * of one of the changable special control characters (see
+	 * 7.1.1.9) is {_POSIX_VDISABLE}, that function shall be
+	 * disabled, that is, no input data shall be recognized as
+	 * the disabled special character."
+	 *
+	 * OK, so we don't ever assign S/DXB XON or XOFF to _POSIX_VDISABLE.
+	 */
+
+	if (uts->c_cc[VSTART] != _POSIX_VDISABLE)
+		ch->ch_xon = uts->c_cc[VSTART];
+	if (uts->c_cc[VSTOP] != _POSIX_VDISABLE)
+		ch->ch_xoff = uts->c_cc[VSTOP];
+
+	ch->ch_lnext = (uts->c_cc[VLNEXT] == _POSIX_VDISABLE ? 0 :
+			uts->c_cc[VLNEXT]);
+
+	/*
+	 * Also, if either c_cc[START] or c_cc[STOP] is set to
+	 * _POSIX_VDISABLE, we can't really do software flow
+	 * control--in either direction--so we turn it off as
+	 * far as S/DXB is concerned.  In essence, if you disable
+	 * one, you disable the other too.
+	 */
+	if ((uts->c_cc[VSTART] == _POSIX_VDISABLE) ||
+	    (uts->c_cc[VSTOP] == _POSIX_VDISABLE))
+		ch->ch_iflag &= ~(IF_IXOFF | IF_IXON);
+
+	/*
+	 *  Update xflags.
+	 */
+
+	xflag = 0;
+
+	if (ch->ch_digi.digi_flags & DIGI_AIXON)
+		xflag = XF_XIXON;
+
+	if ((ch->ch_xxon == _POSIX_VDISABLE) ||
+	    (ch->ch_xxoff == _POSIX_VDISABLE))
+		xflag &= ~XF_XIXON;
+
+	ch->ch_xflag = xflag;
+
+
+	/*
+	 *  Figure effective DCD value.
+	 */
+
+	if (C_CLOCAL(un->un_tty))
+		ch->ch_flag |= CH_CLOCAL;
+	else
+		ch->ch_flag &= ~CH_CLOCAL;
+
+	/*
+	 *  Check modem signals
+	 */
+
+	dgrp_carrier(ch);
+
+	/*
+	 *  Get hardware handshake value.
+	 */
+
+	mflow = 0;
+
+	if (C_CRTSCTS(un->un_tty))
+		mflow |= (DM_RTS | DM_CTS);
+
+	if (ch->ch_digi.digi_flags & RTSPACE)
+		mflow |= DM_RTS;
+
+	if (ch->ch_digi.digi_flags & DTRPACE)
+		mflow |= DM_DTR;
+
+	if (ch->ch_digi.digi_flags & CTSPACE)
+		mflow |= DM_CTS;
+
+	if (ch->ch_digi.digi_flags & DSRPACE)
+		mflow |= DM_DSR;
+
+	if (ch->ch_digi.digi_flags & DCDPACE)
+		mflow |= DM_CD;
+
+	if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)
+		mflow |= DM_RTS_TOGGLE;
+
+	ch->ch_mflow = mflow;
+
+	/*
+	 *  Send the changes to the server.
+	 */
+
+	ch->ch_flag |= CH_PARAM;
+	(ch->ch_nd)->nd_tx_work = 1;
+
+	if (waitqueue_active(&ch->ch_flag_wait))
+		wake_up_interruptible(&ch->ch_flag_wait);
+}
+
+/*
+ * This function is just used as a callback for timeouts
+ * waiting on the ch_sleep flag.
+ */
+static void wake_up_drp_sleep_timer(unsigned long ptr)
+{
+	struct ch_struct *ch = (struct ch_struct *) ptr;
+	if (ch)
+		wake_up(&ch->ch_sleep);
+}
+
+
+/*
+ * Set up our own sleep that can't be cancelled
+ * until our timeout occurs.
+ */
+static void drp_my_sleep(struct ch_struct *ch)
+{
+	struct timer_list drp_wakeup_timer;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/*
+	 * First make sure we're ready to receive the wakeup.
+	 */
+
+	add_wait_queue(&ch->ch_sleep, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+
+	/*
+	 * Since we are uninterruptible, set a timer to
+	 * unset the uninterruptable state in 1 second.
+	 */
+
+	init_timer(&drp_wakeup_timer);
+	drp_wakeup_timer.function = wake_up_drp_sleep_timer;
+	drp_wakeup_timer.data = (unsigned long) ch;
+	drp_wakeup_timer.expires = jiffies + (1 * HZ);
+	add_timer(&drp_wakeup_timer);
+
+	schedule();
+
+	del_timer(&drp_wakeup_timer);
+
+	remove_wait_queue(&ch->ch_sleep, &wait);
+}
+
+/*
+ * dgrp_tty_open()
+ *
+ * returns:
+ *    -EBUSY    - this is a callout device and the normal device is active
+ *              - there is an error in opening the tty
+ *    -ENODEV   - the channel does not exist
+ *    -EAGAIN   - we are in the middle of hanging up or closing
+ *              - IMMEDIATE_OPEN fails
+ *    -ENXIO or -EAGAIN
+ *              - if the port is outside physical range
+ *    -EINTR    - the open is interrupted
+ *
+ */
+static int dgrp_tty_open(struct tty_struct *tty, struct file *file)
+{
+	int    retval = 0;
+	struct nd_struct  *nd;
+	struct ch_struct *ch;
+	struct un_struct  *un;
+	int    port;
+	int    delay_error;
+	int    otype;
+	int    unf;
+	int    wait_carrier;
+	int    category;
+	int    counts_were_incremented = 0;
+	ulong lock_flags;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/*
+	 * Do some initial checks to see if the node and port exist
+	 */
+
+	nd = nd_struct_get(MAJOR(tty_devnum(tty)));
+	port = PORT_NUM(MINOR(tty_devnum(tty)));
+	category = OPEN_CATEGORY(MINOR(tty_devnum(tty)));
+
+	if (!nd)
+		return -ENODEV;
+
+	if (port >= CHAN_MAX)
+		return -ENODEV;
+
+	/*
+	 *  The channel exists.
+	 */
+
+	ch = nd->nd_chan + port;
+
+	un = IS_PRINT(MINOR(tty_devnum(tty))) ? &ch->ch_pun : &ch->ch_tun;
+	un->un_tty = tty;
+	tty->driver_data = un;
+
+	/*
+	 * If we are in the middle of hanging up,
+	 * then return an error
+	 */
+	if (tty_hung_up_p(file)) {
+		retval = ((un->un_flag & UN_HUP_NOTIFY) ?
+			   -EAGAIN : -ERESTARTSYS);
+		goto done;
+	}
+
+	/*
+	 * If the port is in the middle of closing, then block
+	 * until it is done, then try again.
+	 */
+	retval = wait_event_interruptible(un->un_close_wait,
+			((un->un_flag & UN_CLOSING) == 0));
+
+	if (retval)
+		goto done;
+
+	/*
+	 * If the port is in the middle of a reopen after a network disconnect,
+	 * wait until it is done, then try again.
+	 */
+	retval = wait_event_interruptible(ch->ch_flag_wait,
+			((ch->ch_flag & CH_PORT_GONE) == 0));
+
+	if (retval)
+		goto done;
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+
+	if (tty->driver->subtype == SERIAL_TYPE_CALLOUT) {
+		if (un->un_flag & UN_NORMAL_ACTIVE) {
+			retval = -EBUSY;
+			goto done;
+		} else {
+			un->un_flag |= UN_CALLOUT_ACTIVE;
+		}
+	}
+
+	/*
+	 *  Loop waiting until the open can be successfully completed.
+	 */
+
+	spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+	nd->nd_tx_work = 1;
+
+	for (;;) {
+		wait_carrier = 0;
+
+		/*
+		 * Determine the open type from the flags provided.
+		 */
+
+		/*
+		 * If the port is not enabled, then exit
+		 */
+		if (test_bit(TTY_IO_ERROR, &tty->flags)) {
+			/* there was an error in opening the tty */
+			if (un->un_flag & UN_CALLOUT_ACTIVE)
+				retval = -EBUSY;
+			else
+				un->un_flag |= UN_NORMAL_ACTIVE;
+			goto unlock;
+		}
+
+		if (file->f_flags & O_NONBLOCK) {
+
+			/*
+			 * if the O_NONBLOCK is set, errors on read and write
+			 * must return -EAGAIN immediately and NOT sleep
+			 * on the waitqs.
+			 */
+			otype = OTYPE_IMMEDIATE;
+			delay_error = -EAGAIN;
+
+		} else if (!OPEN_WAIT_AVAIL(category) ||
+			  (file->f_flags & O_NDELAY) != 0) {
+			otype = OTYPE_IMMEDIATE;
+			delay_error = -EBUSY;
+
+		} else if (!OPEN_WAIT_CARRIER(category) ||
+			  ((ch->ch_digi.digi_flags & DIGI_FORCEDCD) != 0) ||
+			  C_CLOCAL(tty)) {
+			otype = OTYPE_PERSISTENT;
+			delay_error = 0;
+
+		} else {
+			otype = OTYPE_INCOMING;
+			delay_error = 0;
+		}
+
+		/*
+		 * Handle port currently outside physical port range.
+		 */
+
+		if (port >= nd->nd_chan_count) {
+			if (otype == OTYPE_IMMEDIATE) {
+				retval = (nd->nd_state == NS_READY) ?
+						-ENXIO : -EAGAIN;
+				goto unlock;
+			}
+		}
+
+		/*
+		 *  Handle port not currently open.
+		 */
+
+		else if (ch->ch_open_count == 0) {
+			/*
+			 * Return an error when an Incoming Open
+			 * response indicates the port is busy.
+			 */
+
+			if (ch->ch_open_error != 0 && otype == ch->ch_otype) {
+				retval = (ch->ch_open_error <= 2) ?
+					  delay_error : -ENXIO ;
+				goto unlock;
+			}
+
+			/*
+			 * Fail any new Immediate open if we do not have
+			 * a normal connection to the server.
+			 */
+
+			if (nd->nd_state != NS_READY &&
+			    otype == OTYPE_IMMEDIATE) {
+				retval = -EAGAIN;
+				goto unlock;
+			}
+
+			/*
+			 * If a Realport open of the correct type has
+			 * succeeded, complete the open.
+			 */
+
+			if (ch->ch_state == CS_READY && ch->ch_otype == otype)
+				break;
+		}
+
+		/*
+		 * Handle port already open and active as a device
+		 * of same category.
+		 */
+
+		else if ((ch->ch_category == category) ||
+			  IS_PRINT(MINOR(tty_devnum(tty)))) {
+			/*
+			 * Fail if opening the device now would
+			 * violate exclusive use.
+			 */
+			unf = ch->ch_tun.un_flag | ch->ch_pun.un_flag;
+
+			if ((file->f_flags & O_EXCL) || (unf & UN_EXCL)) {
+				retval = -EBUSY;
+				goto unlock;
+			}
+
+			/*
+			 * If the open device is in the hangup state, all
+			 * system calls fail except close().
+			 */
+
+			/* TODO : check on hangup_p calls */
+
+			if (ch->ch_flag & CH_HANGUP) {
+				retval = -ENXIO;
+				goto unlock;
+			}
+
+			/*
+			 * If the port is ready, and carrier is ignored
+			 * or present, then complete the open.
+			 */
+
+			if (ch->ch_state == CS_READY &&
+			    (otype != OTYPE_INCOMING ||
+			    ch->ch_flag & CH_VIRT_CD))
+				break;
+
+			wait_carrier = 1;
+		}
+
+		/*
+		 *  Handle port active with a different category device.
+		 */
+
+		else {
+			if (otype == OTYPE_IMMEDIATE) {
+				retval = delay_error;
+				goto unlock;
+			}
+		}
+
+		/*
+		 * Wait until conditions change, then take another
+		 * try at the open.
+		 */
+
+		ch->ch_wait_count[otype]++;
+
+		if (wait_carrier)
+			ch->ch_wait_carrier++;
+
+		/*
+		 * Prepare the task to accept the wakeup, then
+		 * release our locks and release control.
+		 */
+
+		add_wait_queue(&ch->ch_flag_wait, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+
+		spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+		/*
+		 * Give up control, we'll come back if we're
+		 * interrupted or are woken up.
+		 */
+		schedule();
+		remove_wait_queue(&ch->ch_flag_wait, &wait);
+
+		spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+		current->state = TASK_RUNNING;
+
+		ch->ch_wait_count[otype]--;
+
+		if (wait_carrier)
+			ch->ch_wait_carrier--;
+
+		nd->nd_tx_work = 1;
+
+		if (signal_pending(current)) {
+			retval = -EINTR;
+			goto unlock;
+		}
+	} /* end for(;;) */
+
+	/*
+	 *  The open has succeeded.  No turning back.
+	 */
+	counts_were_incremented = 1;
+	un->un_open_count++;
+	ch->ch_open_count++;
+
+	/*
+	 * Initialize the channel, if it's not already open.
+	 */
+
+	if (ch->ch_open_count == 1) {
+		ch->ch_flag = 0;
+		ch->ch_inwait = 0;
+		ch->ch_category = category;
+		ch->ch_pscan_state = 0;
+
+		/* TODO : find out what PS-1 bug Gene was referring to */
+		/* TODO : in the following comment. */
+
+		ch->ch_send = RR_TX_START | RR_RX_START;  /* PS-1 bug */
+
+		if (C_CLOCAL(tty) ||
+		    ch->ch_s_mlast & DM_CD ||
+		    ch->ch_digi.digi_flags & DIGI_FORCEDCD)
+			ch->ch_flag |= CH_VIRT_CD;
+		else if (OPEN_FORCES_CARRIER(category))
+			ch->ch_flag |= CH_VIRT_CD;
+
+	}
+
+	/*
+	 *  Initialize the unit, if it is not already open.
+	 */
+
+	if (un->un_open_count == 1) {
+		/*
+		 *  Since all terminal options are always sticky in Linux,
+		 *  we don't need the UN_STICKY flag to be handled specially.
+		 */
+		/* clears all the digi flags, leaves serial flags */
+		un->un_flag &= ~UN_DIGI_MASK;
+
+		if (file->f_flags & O_EXCL)
+			un->un_flag |= UN_EXCL;
+
+		/* TODO : include "session" and "pgrp" */
+
+		/*
+		 *  In Linux, all terminal parameters are intended to be sticky.
+		 *  as a result, we "remove" the code which once reset the ports
+		 *  to sane values.
+		 */
+
+		drp_param(ch);
+
+	}
+
+	un->un_flag |= UN_INITIALIZED;
+
+	retval = 0;
+
+unlock:
+
+	spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+done:
+	/*
+	 * Linux does a close for every open, even failed ones!
+	 */
+	if (!counts_were_incremented) {
+		un->un_open_count++;
+		ch->ch_open_count++;
+	}
+
+	if (retval)
+		dev_err(tty->dev, "tty open bad return (%i)\n", retval);
+
+	return retval;
+}
+
+
+
+
+/*
+ * dgrp_tty_close() -- close function for tty_operations
+ */
+static void dgrp_tty_close(struct tty_struct *tty, struct file *file)
+{
+	struct ch_struct *ch;
+	struct un_struct *un;
+	struct nd_struct *nd;
+	int	tpos;
+	int	port;
+	int	err = 0;
+	int	s = 0;
+	ulong  waketime;
+	ulong  lock_flags;
+	int sent_printer_offstr = 0;
+
+	port = PORT_NUM(MINOR(tty_devnum(tty)));
+
+	un = tty->driver_data;
+
+	if (!un)
+		return;
+
+	ch = un->un_ch;
+
+	if (!ch)
+		return;
+
+	nd = ch->ch_nd;
+
+	if (!nd)
+		return;
+
+	spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+
+	/* Used to be on channel basis, now we check on a unit basis. */
+	if (un->un_open_count != 1)
+		goto unlock;
+
+	/*
+	 * OK, its the last close on the unit
+	 */
+	un->un_flag |= UN_CLOSING;
+
+	/*
+	 * Notify the discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+
+	/*
+	 * Wait for output to drain only if this is
+	 * the last close against the channel
+	 */
+
+	if (ch->ch_open_count == 1) {
+		/*
+		 * If its the print device, we need to ensure at all costs that
+		 * the offstr will fit. If it won't, flush our tbuf.
+		 */
+		if (IS_PRINT(MINOR(tty_devnum(tty))) &&
+		    (((ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK) <
+		    ch->ch_digi.digi_offlen))
+			ch->ch_tin = ch->ch_tout;
+
+		/*
+		 * Turn off the printer.  Don't bother checking to see if its
+		 * IS_PRINT... Since this is the last close the flag is going
+		 * to be cleared, so we MUST make sure the offstr gets inserted
+		 * into tbuf.
+		 */
+
+		if ((ch->ch_flag & CH_PRON) != 0) {
+			drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+				  ch->ch_digi.digi_offlen);
+			ch->ch_flag &= ~CH_PRON;
+			sent_printer_offstr = 1;
+		}
+	}
+
+	/*
+	 *  Wait until either the output queue has drained, or we see
+	 *  absolutely no progress for 15 seconds.
+	 */
+
+	tpos = ch->ch_s_tpos;
+
+	waketime = jiffies + 15 * HZ;
+
+	for (;;) {
+
+		/*
+		 *  Make sure the port still exists.
+		 */
+
+		if (port >= nd->nd_chan_count) {
+			err = 1;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = 1;
+			break;
+		}
+
+		/*
+		 * If the port is idle (not opened on the server), we have
+		 * no way of draining/flushing/closing the port on that server.
+		 * So break out of loop.
+		 */
+		if (ch->ch_state == CS_IDLE)
+			break;
+
+		nd->nd_tx_work = 1;
+
+		/*
+		 *  Exit if the queues for this unit are empty,
+		 *  and either the other unit is still open or all
+		 *  data has drained.
+		 */
+
+		if ((un->un_tty)->ops->chars_in_buffer ?
+		    ((un->un_tty)->ops->chars_in_buffer)(un->un_tty) == 0 : 1) {
+
+			/*
+			 * We don't need to wait for a buffer to drain
+			 * if the other unit is open.
+			 */
+
+			if (ch->ch_open_count != un->un_open_count)
+				break;
+
+			/*
+			 *  The wait is complete when all queues are
+			 *  drained, and any break in progress is complete.
+			 */
+
+			if (ch->ch_tin == ch->ch_tout &&
+			    ch->ch_s_tin == ch->ch_s_tpos &&
+			    (ch->ch_send & RR_TX_BREAK) == 0) {
+				break;
+			}
+		}
+
+		/*
+		 * Flush TX data and exit the wait if NDELAY is set,
+		 * or this is not a DIGI printer, and the close timeout
+		 * expires.
+		 */
+
+		if ((file->f_flags & (O_NDELAY | O_NONBLOCK)) ||
+		    ((long)(jiffies - waketime) >= 0 &&
+		      (ch->ch_digi.digi_flags & DIGI_PRINTER) == 0)) {
+
+				/*
+				 * If we sent the printer off string, we cannot
+				 * flush our internal buffers, or we might lose
+				 * the offstr.
+				 */
+				if (!sent_printer_offstr)
+					dgrp_tty_flush_buffer(tty);
+
+				tty_ldisc_flush(tty);
+				break;
+		}
+
+		/*
+		 *  Otherwise take a short nap.
+		 */
+
+		ch->ch_flag |= CH_DRAIN;
+
+		spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+		schedule_timeout_interruptible(1);
+		s = signal_pending(current);
+
+		spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+		if (s) {
+			/*
+			 * If we had sent the printer off string, we now have
+			 * some problems.
+			 *
+			 * The system won't let us sleep since we got an error
+			 * back from sleep, presumably because the user did
+			 * a ctrl-c...
+			 * But we need to ensure that the offstr gets sent!
+			 * Thus, we have to do something else besides sleeping.
+			 * The plan:
+			 * 1) Make this task uninterruptable.
+			 * 2) Set up a timer to go off in 1 sec.
+			 * 3) Act as tho we just got out of the sleep above.
+			 *
+			 * Thankfully, in the real world, this just
+			 * never happens.
+			 */
+
+			if (sent_printer_offstr) {
+				spin_unlock_irqrestore(&nd->nd_lock,
+						       lock_flags);
+				drp_my_sleep(ch);
+				spin_lock_irqsave(&nd->nd_lock, lock_flags);
+			} else {
+				err = 1;
+				break;
+			}
+		}
+
+		/*
+		 *  Restart the wait if any progress is seen.
+		 */
+
+		if (ch->ch_s_tpos != tpos) {
+			tpos = ch->ch_s_tpos;
+
+			/* TODO:  this gives us timeout problems with nist ?? */
+			waketime = jiffies + 15 * HZ;
+		}
+	}
+
+	/*
+	 *  Close the line discipline
+	 */
+
+	/* this is done in tty_io.c */
+	/* if ((un->un_tty)->ldisc.close)
+	 *	((un->un_tty)->ldisc.close)(un->un_tty);
+	 */
+
+	/* don't do this here */
+	/* un->un_flag = 0; */
+
+	/*
+	 *  Flush the receive buffer on terminal unit close only.
+	 */
+
+	if (!IS_PRINT(MINOR(tty_devnum(tty))))
+		ch->ch_rout = ch->ch_rin;
+
+
+	/*
+	 * Don't permit the close to happen until we get any pending
+	 * sync request responses.
+	 * There could be other ports depending upon the response as well.
+	 *
+	 * Also, don't permit the close to happen until any parameter
+	 * changes have been sent out from the state machine as well.
+	 * This is required because of a ditty -a race with -HUPCL
+	 * We MUST make sure all channel parameters have been sent to the
+	 * Portserver before sending a close.
+	 */
+
+	if ((err != 1) && (ch->ch_state != CS_IDLE)) {
+		spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+		s = wait_event_interruptible(ch->ch_flag_wait,
+			((ch->ch_flag & (CH_WAITING_SYNC | CH_PARAM)) == 0));
+		spin_lock_irqsave(&nd->nd_lock, lock_flags);
+	}
+
+	/*
+	 * Cleanup the channel if last unit open.
+	 */
+
+	if (ch->ch_open_count == 1) {
+		ch->ch_flag = 0;
+		ch->ch_category = 0;
+		ch->ch_send = 0;
+		ch->ch_expect = 0;
+		ch->ch_tout = ch->ch_tin;
+		/* (un->un_tty)->device = 0; */
+
+		if (ch->ch_state == CS_READY)
+			ch->ch_state = CS_SEND_CLOSE;
+	}
+
+	/*
+	 * Send the changes to the server
+	 */
+	if (ch->ch_state != CS_IDLE) {
+		ch->ch_flag |= CH_PARAM;
+		wake_up_interruptible(&ch->ch_flag_wait);
+	}
+
+	nd->nd_tx_work = 1;
+	nd->nd_tx_ready = 1;
+
+unlock:
+	tty->closing = 0;
+
+	if (ch->ch_open_count <= 0)
+		dev_info(tty->dev,
+			 "%s - unexpected value for ch->ch_open_count: %i\n",
+			 __func__, ch->ch_open_count);
+	else
+		ch->ch_open_count--;
+
+	if (un->un_open_count <= 0)
+		dev_info(tty->dev,
+			 "%s - unexpected value for un->un_open_count: %i\n",
+			 __func__, un->un_open_count);
+	else
+		un->un_open_count--;
+
+	un->un_flag &= ~(UN_NORMAL_ACTIVE | UN_CALLOUT_ACTIVE | UN_CLOSING);
+	if (waitqueue_active(&un->un_close_wait))
+		wake_up_interruptible(&un->un_close_wait);
+
+	spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+	return;
+
+}
+
+static void drp_wmove(struct ch_struct *ch, int from_user, void *buf, int count)
+{
+	int n;
+	int ret = 0;
+
+	ch->ch_nd->nd_tx_work = 1;
+
+	n = TBUF_MAX - ch->ch_tin;
+
+	if (count >= n) {
+		if (from_user)
+			ret = copy_from_user(ch->ch_tbuf + ch->ch_tin,
+					     (void __user *) buf, n);
+		else
+			memcpy(ch->ch_tbuf + ch->ch_tin, buf, n);
+
+		buf = (char *) buf + n;
+		count -= n;
+		ch->ch_tin = 0;
+	}
+
+	if (from_user)
+		ret = copy_from_user(ch->ch_tbuf + ch->ch_tin,
+				     (void __user *) buf, count);
+	else
+		memcpy(ch->ch_tbuf + ch->ch_tin, buf, count);
+
+	ch->ch_tin += count;
+}
+
+
+static int dgrp_calculate_txprint_bounds(struct ch_struct *ch, int space,
+					 int *un_flag)
+{
+	clock_t tt;
+	clock_t mt;
+	unsigned short tmax = 0;
+
+	/*
+	 * If the terminal device is busy, reschedule when
+	 * the terminal device becomes idle.
+	 */
+
+	if (ch->ch_tun.un_open_count != 0 &&
+	    ch->ch_tun.un_tty->ops->chars_in_buffer &&
+	    ((ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) != 0)) {
+		*un_flag = UN_PWAIT;
+		return 0;
+	}
+
+	/*
+	 * Assure that whenever there is printer data in the output
+	 * buffer, there always remains enough space after it to
+	 * turn the printer off.
+	 */
+	space -= ch->ch_digi.digi_offlen;
+
+	if (space <= 0) {
+		*un_flag = UN_EMPTY;
+		return 0;
+	}
+
+	/*
+	 * We measure printer CPS speed by incrementing
+	 * ch_cpstime by (HZ / digi_maxcps) for every
+	 * character we output, restricting output so
+	 * that ch_cpstime never exceeds lbolt.
+	 *
+	 * However if output has not been done for some
+	 * time, lbolt will grow to very much larger than
+	 * ch_cpstime, which would allow essentially
+	 * unlimited amounts of output until ch_cpstime
+	 * finally caught up.   To avoid this, we adjust
+	 * cps_time when necessary so the difference
+	 * between lbolt and ch_cpstime never results
+	 * in sending more than digi_bufsize characters.
+	 *
+	 * This nicely models a printer with an internal
+	 * buffer of digi_bufsize characters.
+	 *
+	 * Get the time between lbolt and ch->ch_cpstime;
+	 */
+
+	tt = jiffies - ch->ch_cpstime;
+
+	/*
+	 * Compute the time required to send digi_bufsize
+	 * characters.
+	 */
+
+	mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps;
+
+	/*
+	 * Compute the number of characters that can be sent
+	 * without violating the time constraint.   If the
+	 * direct calculation of this number is bigger than
+	 * digi_bufsize, limit the number to digi_bufsize,
+	 * and adjust cpstime to match.
+	 */
+
+	if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) {
+		tmax = ch->ch_digi.digi_bufsize;
+		ch->ch_cpstime = jiffies - mt;
+	} else {
+		tmax = ch->ch_digi.digi_maxcps * tt / HZ;
+	}
+
+	/*
+	 * If the time constraint now binds, limit the transmit
+	 * count accordingly, and tentatively arrange to be
+	 * rescheduled based on time.
+	 */
+
+	if (tmax < space) {
+		*un_flag = UN_TIME;
+		space = tmax;
+	}
+
+	/*
+	 * Compute the total number of characters we can
+	 * output before the total number of characters known
+	 * to be in the output queue exceeds digi_maxchar.
+	 */
+
+	tmax = (ch->ch_digi.digi_maxchar -
+		((ch->ch_tin - ch->ch_tout) & TBUF_MASK) -
+		((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff));
+
+
+	/*
+	 * If the digi_maxchar constraint now holds, limit
+	 * the transmit count accordingly, and arrange to
+	 * be rescheduled when the queue becomes empty.
+	 */
+
+	if (space > tmax) {
+		*un_flag = UN_EMPTY;
+		space = tmax;
+	}
+
+	if (space <= 0)
+		*un_flag |= UN_EMPTY;
+
+	return space;
+}
+
+
+static int dgrp_tty_write(struct tty_struct *tty,
+			  const unsigned char *buf,
+			  int count)
+{
+	struct nd_struct *nd;
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int	space;
+	int	n;
+	int	t;
+	int sendcount;
+	int un_flag;
+	ulong lock_flags;
+
+	if (tty == NULL)
+		return 0;
+
+	un = tty->driver_data;
+	if (!un)
+		return 0;
+
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+
+	nd = ch->ch_nd;
+	if (!nd)
+		return 0;
+
+	/*
+	 * Ignore the request if the channel is not ready.
+	 */
+	if (ch->ch_state != CS_READY)
+		return 0;
+
+	spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+	/*
+	 * Ignore the request if output is blocked.
+	 */
+	if ((un->un_flag & (UN_EMPTY | UN_LOW | UN_TIME | UN_PWAIT)) != 0) {
+		count = 0;
+		goto out;
+	}
+
+	/*
+	 * Also ignore the request if DPA has this port open,
+	 * and is flow controlled on reading more data.
+	 */
+	if (nd->nd_dpa_debug && nd->nd_dpa_flag & DPA_WAIT_SPACE &&
+		nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty))) {
+		count = 0;
+		goto out;
+	}
+
+	/*
+	 *	Limit amount we will write to the amount of space
+	 *	available in the channel buffer.
+	 */
+	sendcount = 0;
+
+	space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+	/*
+	 * Handle the printer device.
+	 */
+
+	un_flag = UN_LOW;
+
+	if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+		clock_t tt;
+		clock_t mt;
+		unsigned short tmax = 0;
+
+		/*
+		 * If the terminal device is busy, reschedule when
+		 * the terminal device becomes idle.
+		 */
+
+		if (ch->ch_tun.un_open_count != 0 &&
+		    ((ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) != 0)) {
+			un->un_flag |= UN_PWAIT;
+			count = 0;
+			goto out;
+		}
+
+		/*
+		 * Assure that whenever there is printer data in the output
+		 * buffer, there always remains enough space after it to
+		 * turn the printer off.
+		 */
+		space -= ch->ch_digi.digi_offlen;
+
+		/*
+		 * Output the printer on string.
+		 */
+
+		if ((ch->ch_flag & CH_PRON) == 0) {
+			space -= ch->ch_digi.digi_onlen;
+
+			if (space < 0) {
+				un->un_flag |= UN_EMPTY;
+				(ch->ch_nd)->nd_tx_work = 1;
+				count = 0;
+				goto out;
+			}
+
+			drp_wmove(ch, 0, ch->ch_digi.digi_onstr,
+				ch->ch_digi.digi_onlen);
+
+			ch->ch_flag |= CH_PRON;
+		}
+
+		/*
+		 * We measure printer CPS speed by incrementing
+		 * ch_cpstime by (HZ / digi_maxcps) for every
+		 * character we output, restricting output so
+		 * that ch_cpstime never exceeds lbolt.
+		 *
+		 * However if output has not been done for some
+		 * time, lbolt will grow to very much larger than
+		 * ch_cpstime, which would allow essentially
+		 * unlimited amounts of output until ch_cpstime
+		 * finally caught up.   To avoid this, we adjust
+		 * cps_time when necessary so the difference
+		 * between lbolt and ch_cpstime never results
+		 * in sending more than digi_bufsize characters.
+		 *
+		 * This nicely models a printer with an internal
+		 * buffer of digi_bufsize characters.
+		 *
+		 * Get the time between lbolt and ch->ch_cpstime;
+		 */
+
+		tt = jiffies - ch->ch_cpstime;
+
+		/*
+		 * Compute the time required to send digi_bufsize
+		 * characters.
+		 */
+
+		mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps;
+
+		/*
+		 * Compute the number of characters that can be sent
+		 * without violating the time constraint.   If the
+		 * direct calculation of this number is bigger than
+		 * digi_bufsize, limit the number to digi_bufsize,
+		 * and adjust cpstime to match.
+		 */
+
+		if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) {
+			tmax = ch->ch_digi.digi_bufsize;
+			ch->ch_cpstime = jiffies - mt;
+		} else {
+			tmax = ch->ch_digi.digi_maxcps * tt / HZ;
+		}
+
+		/*
+		 * If the time constraint now binds, limit the transmit
+		 * count accordingly, and tentatively arrange to be
+		 * rescheduled based on time.
+		 */
+
+		if (tmax < space) {
+			space = tmax;
+			un_flag = UN_TIME;
+		}
+
+		/*
+		 * Compute the total number of characters we can
+		 * output before the total number of characters known
+		 * to be in the output queue exceeds digi_maxchar.
+		 */
+
+		tmax = (ch->ch_digi.digi_maxchar -
+			((ch->ch_tin - ch->ch_tout) & TBUF_MASK) -
+			((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff));
+
+
+		/*
+		 * If the digi_maxchar constraint now holds, limit
+		 * the transmit count accordingly, and arrange to
+		 * be rescheduled when the queue becomes empty.
+		 */
+
+		if (space > tmax) {
+			space = tmax;
+			un_flag = UN_EMPTY;
+		}
+
+	}
+	/*
+	 * Handle the terminal device.
+	 */
+	else {
+
+		/*
+		 * If the printer device is on, turn it off.
+		 */
+
+		if ((ch->ch_flag & CH_PRON) != 0) {
+
+			space -= ch->ch_digi.digi_offlen;
+
+			drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+				  ch->ch_digi.digi_offlen);
+
+			ch->ch_flag &= ~CH_PRON;
+		}
+	}
+
+	/*
+	 *	If space is 0 and its because the ch->tbuf
+	 *	is full, then Linux will handle a callback when queue
+	 *	space becomes available.
+	 *	tty_write returns count = 0
+	 */
+
+	if (space <= 0) {
+		/* the linux tty_io.c handles this if we return 0 */
+		/* if (fp->flags & O_NONBLOCK) return -EAGAIN; */
+
+		un->un_flag |= UN_EMPTY;
+		(ch->ch_nd)->nd_tx_work = 1;
+		count = 0;
+		goto out;
+	}
+
+	count = min(count, space);
+
+	if (count > 0) {
+
+		un->un_tbusy++;
+
+		/*
+		 *	Copy the buffer contents to the ch_tbuf
+		 *	being careful to wrap around the circular queue
+		 */
+
+		t = TBUF_MAX - ch->ch_tin;
+		n = count;
+
+		if (n >= t) {
+			memcpy(ch->ch_tbuf + ch->ch_tin, buf, t);
+			if (nd->nd_dpa_debug && nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(un->un_tty))))
+				dgrp_dpa_data(nd, 0, (char *) buf, t);
+			buf += t;
+			n -= t;
+			ch->ch_tin = 0;
+			sendcount += n;
+		}
+
+		memcpy(ch->ch_tbuf + ch->ch_tin, buf, n);
+		if (nd->nd_dpa_debug && nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(un->un_tty))))
+			dgrp_dpa_data(nd, 0, (char *) buf, n);
+		buf += n;
+		ch->ch_tin += n;
+		sendcount += n;
+
+		un->un_tbusy--;
+		(ch->ch_nd)->nd_tx_work = 1;
+		if (ch->ch_edelay != DGRP_RTIME) {
+			(ch->ch_nd)->nd_tx_ready = 1;
+			wake_up_interruptible(&nd->nd_tx_waitq);
+		}
+	}
+
+	ch->ch_txcount += count;
+
+	if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+
+		/*
+		 * Adjust ch_cpstime to account
+		 * for the characters just output.
+		 */
+
+		if (sendcount > 0) {
+			int cc = HZ * sendcount + ch->ch_cpsrem;
+
+			ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps;
+			ch->ch_cpsrem   = cc % ch->ch_digi.digi_maxcps;
+		}
+
+		/*
+		 * If we are now waiting on time, schedule ourself
+		 * back when we'll be able to send a block of
+		 * digi_maxchar characters.
+		 */
+
+		if ((un_flag & UN_TIME) != 0) {
+			ch->ch_waketime = (ch->ch_cpstime +
+				(ch->ch_digi.digi_maxchar * HZ /
+				ch->ch_digi.digi_maxcps));
+		}
+	}
+
+	/*
+	 * If the printer unit is waiting for completion
+	 * of terminal output, get him going again.
+	 */
+
+	if ((ch->ch_pun.un_flag & UN_PWAIT) != 0)
+		(ch->ch_nd)->nd_tx_work = 1;
+
+out:
+	spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+	return count;
+}
+
+
+/*
+ *	Put a character into ch->ch_buf
+ *
+ *	- used by the line discipline for OPOST processing
+ */
+
+static int dgrp_tty_put_char(struct tty_struct *tty, unsigned char new_char)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	ulong  lock_flags;
+	int space;
+	int retval = 0;
+
+	if (tty == NULL)
+		return 0;
+
+	un = tty->driver_data;
+	if (!un)
+		return 0;
+
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+
+	if (ch->ch_state != CS_READY)
+		return 0;
+
+	spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+
+	/*
+	 *	If space is 0 and its because the ch->tbuf
+	 *	Warn and dump the character, there isn't anything else
+	 *	we can do about it.  David_Fries@digi.com
+	 */
+
+	space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+	un->un_tbusy++;
+
+	/*
+	 * Output the printer on string if device is TXPrint.
+	 */
+	if (IS_PRINT(MINOR(tty_devnum(tty))) && (ch->ch_flag & CH_PRON) == 0) {
+		if (space < ch->ch_digi.digi_onlen) {
+			un->un_tbusy--;
+			goto out;
+		}
+		space -= ch->ch_digi.digi_onlen;
+		drp_wmove(ch, 0, ch->ch_digi.digi_onstr,
+			  ch->ch_digi.digi_onlen);
+		ch->ch_flag |= CH_PRON;
+	}
+
+	/*
+	 * Output the printer off string if device is NOT TXPrint.
+	 */
+
+	if (!IS_PRINT(MINOR(tty_devnum(tty))) &&
+	    ((ch->ch_flag & CH_PRON) != 0)) {
+		if (space < ch->ch_digi.digi_offlen) {
+			un->un_tbusy--;
+			goto out;
+		}
+
+		space -= ch->ch_digi.digi_offlen;
+		drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+			  ch->ch_digi.digi_offlen);
+		ch->ch_flag &= ~CH_PRON;
+	}
+
+	if (!space) {
+		un->un_tbusy--;
+		goto out;
+	}
+
+	/*
+	 *	Copy the character to the ch_tbuf being
+	 *	careful to wrap around the circular queue
+	 */
+	ch->ch_tbuf[ch->ch_tin] = new_char;
+	ch->ch_tin = (1 + ch->ch_tin) & TBUF_MASK;
+
+	if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+
+		/*
+		 * Adjust ch_cpstime to account
+		 * for the character just output.
+		 */
+
+		int cc = HZ + ch->ch_cpsrem;
+
+		ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps;
+		ch->ch_cpsrem   = cc % ch->ch_digi.digi_maxcps;
+
+		/*
+		 * If we are now waiting on time, schedule ourself
+		 * back when we'll be able to send a block of
+		 * digi_maxchar characters.
+		 */
+
+		ch->ch_waketime = (ch->ch_cpstime +
+			(ch->ch_digi.digi_maxchar * HZ /
+			ch->ch_digi.digi_maxcps));
+	}
+
+
+	un->un_tbusy--;
+	(ch->ch_nd)->nd_tx_work = 1;
+
+	retval = 1;
+out:
+	spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+	return retval;
+}
+
+
+
+/*
+ *	Flush TX buffer (make in == out)
+ *
+ *	check tty_ioctl.c  -- this is called after TCOFLUSH
+ */
+static void dgrp_tty_flush_buffer(struct tty_struct *tty)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+	un = tty->driver_data;
+	if (!un)
+		return;
+
+	ch = un->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_tout = ch->ch_tin;
+	/* do NOT do this here! */
+	/* ch->ch_s_tpos = ch->ch_s_tin = 0; */
+
+	/* send the flush output command now */
+	ch->ch_send |= RR_TX_FLUSH;
+	(ch->ch_nd)->nd_tx_ready = 1;
+	(ch->ch_nd)->nd_tx_work = 1;
+	wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+	if (waitqueue_active(&tty->write_wait))
+		wake_up_interruptible(&tty->write_wait);
+
+	tty_wakeup(tty);
+
+}
+
+/*
+ *	Return space available in Tx buffer
+ *	count = ( ch->ch_tout - ch->ch_tin ) mod (TBUF_MAX - 1)
+ */
+static int dgrp_tty_write_room(struct tty_struct *tty)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int	count;
+
+	if (!tty)
+		return 0;
+
+	un = tty->driver_data;
+	if (!un)
+		return 0;
+
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+
+	count = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+	/* We *MUST* check this, and return 0 if the Printer Unit cannot
+	 * take any more data within its time constraints...  If we don't
+	 * return 0 and the printer has hit it time constraint, the ld will
+	 * call us back doing a put_char, which cannot be rejected!!!
+	 */
+	if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+		int un_flag = 0;
+		count = dgrp_calculate_txprint_bounds(ch, count, &un_flag);
+		if (count <= 0)
+			count = 0;
+
+		ch->ch_pun.un_flag |= un_flag;
+		(ch->ch_nd)->nd_tx_work = 1;
+	}
+
+	return count;
+}
+
+/*
+ *	Return number of characters that have not been transmitted yet.
+ *	chars_in_buffer = ( ch->ch_tin - ch->ch_tout ) mod (TBUF_MAX - 1)
+ *			+ ( ch->ch_s_tin - ch->ch_s_tout ) mod (0xffff)
+ *			= number of characters "in transit"
+ *
+ * Remember that sequence number math is always with a sixteen bit
+ * mask, not the TBUF_MASK.
+ */
+
+static int dgrp_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int	count;
+	int	count1;
+
+	if (!tty)
+		return 0;
+
+	un = tty->driver_data;
+	if (!un)
+		return 0;
+
+	ch = un->un_ch;
+	if (!ch)
+		return 0;
+
+	count1 = count = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+	count += (ch->ch_s_tin - ch->ch_s_tpos) & 0xffff;
+	/* one for tbuf, one for the PS */
+
+	/*
+	 * If we are busy transmitting add 1
+	 */
+	count += un->un_tbusy;
+
+	return count;
+}
+
+
+/*****************************************************************************
+ *
+ * Helper applications for dgrp_tty_ioctl()
+ *
+ *****************************************************************************
+ */
+
+
+/**
+ * ch_to_tty_flags() -- convert channel flags to termio flags
+ * @ch_flag: Digi channel flags
+ * @flagtype: type of ch_flag (iflag, oflag or cflag)
+ *
+ * take the channel flags of the specified type and return the
+ * corresponding termio flag
+ */
+static tcflag_t ch_to_tty_flags(ushort ch_flag, char flagtype)
+{
+	tcflag_t retval = 0;
+
+	switch (flagtype) {
+	case 'i':
+		retval = ((ch_flag & IF_IGNBRK) ? IGNBRK : 0)
+		     | ((ch_flag & IF_BRKINT) ? BRKINT : 0)
+		     | ((ch_flag & IF_IGNPAR) ? IGNPAR : 0)
+		     | ((ch_flag & IF_PARMRK) ? PARMRK : 0)
+		     | ((ch_flag & IF_INPCK) ? INPCK  : 0)
+		     | ((ch_flag & IF_ISTRIP) ? ISTRIP : 0)
+		     | ((ch_flag & IF_IXON) ? IXON   : 0)
+		     | ((ch_flag & IF_IXANY) ? IXANY  : 0)
+		     | ((ch_flag & IF_IXOFF) ? IXOFF  : 0);
+		break;
+
+	case 'o':
+		retval = ((ch_flag & OF_OLCUC) ? OLCUC : 0)
+		     | ((ch_flag & OF_ONLCR) ? ONLCR  : 0)
+		     | ((ch_flag & OF_OCRNL) ? OCRNL  : 0)
+		     | ((ch_flag & OF_ONOCR) ? ONOCR  : 0)
+		     | ((ch_flag & OF_ONLRET) ? ONLRET : 0)
+		  /* | ((ch_flag & OF_OTAB3) ? OFILL  : 0) */
+		     | ((ch_flag & OF_TABDLY) ? TABDLY : 0);
+		break;
+
+	case 'c':
+		retval = ((ch_flag & CF_CSTOPB) ? CSTOPB : 0)
+		     | ((ch_flag & CF_CREAD) ? CREAD  : 0)
+		     | ((ch_flag & CF_PARENB) ? PARENB : 0)
+		     | ((ch_flag & CF_PARODD) ? PARODD : 0)
+		     | ((ch_flag & CF_HUPCL) ? HUPCL  : 0);
+
+		switch (ch_flag & CF_CSIZE) {
+		case CF_CS5:
+			retval |= CS5;
+			break;
+		case CF_CS6:
+			retval |= CS6;
+			break;
+		case CF_CS7:
+			retval |= CS7;
+			break;
+		case CF_CS8:
+			retval |= CS8;
+			break;
+		default:
+			retval |= CS8;
+			break;
+		}
+		break;
+	case 'x':
+		break;
+	case 'l':
+		break;
+	default:
+		return 0;
+	}
+
+	return retval;
+}
+
+
+/**
+ * tty_to_ch_flags() -- convert termio flags to digi channel flags
+ * @tty: pointer to a TTY structure holding flag to be converted
+ * @flagtype: identifies which flag (iflags, oflags, or cflags) should
+ *                 be converted
+ *
+ * take the termio flag of the specified type and return the
+ * corresponding Digi version of the flags
+ */
+static ushort tty_to_ch_flags(struct tty_struct *tty, char flagtype)
+{
+	ushort retval = 0;
+	tcflag_t tflag = 0;
+
+	switch (flagtype) {
+	case 'i':
+		tflag  = tty->termios.c_iflag;
+		retval = (I_IGNBRK(tty) ? IF_IGNBRK : 0)
+		      | (I_BRKINT(tty) ? IF_BRKINT : 0)
+		      | (I_IGNPAR(tty) ? IF_IGNPAR : 0)
+		      | (I_PARMRK(tty) ? IF_PARMRK : 0)
+		      | (I_INPCK(tty)  ? IF_INPCK  : 0)
+		      | (I_ISTRIP(tty) ? IF_ISTRIP : 0)
+		      | (I_IXON(tty)   ? IF_IXON   : 0)
+		      | (I_IXANY(tty)  ? IF_IXANY  : 0)
+		      | (I_IXOFF(tty)  ? IF_IXOFF  : 0);
+		break;
+	case 'o':
+		tflag  = tty->termios.c_oflag;
+		/*
+		 * If OPOST is set, then do the post processing in the
+		 * firmware by setting all the processing flags on.
+		 * If ~OPOST, then make sure we are not doing any
+		 * output processing!!
+		 */
+		if (!O_OPOST(tty))
+			retval = 0;
+		else
+			retval = (O_OLCUC(tty) ? OF_OLCUC : 0)
+			     | (O_ONLCR(tty)  ? OF_ONLCR  : 0)
+			     | (O_OCRNL(tty)  ? OF_OCRNL  : 0)
+			     | (O_ONOCR(tty)  ? OF_ONOCR  : 0)
+			     | (O_ONLRET(tty) ? OF_ONLRET : 0)
+			  /* | (O_OFILL(tty)  ? OF_TAB3   : 0) */
+			     | (O_TABDLY(tty) ? OF_TABDLY : 0);
+		break;
+	case 'c':
+		tflag  = tty->termios.c_cflag;
+		retval = (C_CSTOPB(tty) ? CF_CSTOPB : 0)
+		     | (C_CREAD(tty)  ? CF_CREAD  : 0)
+		     | (C_PARENB(tty) ? CF_PARENB : 0)
+		     | (C_PARODD(tty) ? CF_PARODD : 0)
+		     | (C_HUPCL(tty)  ? CF_HUPCL  : 0);
+		switch (C_CSIZE(tty)) {
+		case CS8:
+			retval |= CF_CS8;
+			break;
+		case CS7:
+			retval |= CF_CS7;
+			break;
+		case CS6:
+			retval |= CF_CS6;
+			break;
+		case CS5:
+			retval |= CF_CS5;
+			break;
+		default:
+			retval |= CF_CS8;
+			break;
+		}
+		break;
+	case 'x':
+		break;
+	case 'l':
+		break;
+	default:
+		return 0;
+	}
+
+	return retval;
+}
+
+
+static int dgrp_tty_send_break(struct tty_struct *tty, int msec)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int ret = -EIO;
+
+	if (!tty)
+		return ret;
+
+	un = tty->driver_data;
+	if (!un)
+		return ret;
+
+	ch = un->un_ch;
+	if (!ch)
+		return ret;
+
+	dgrp_send_break(ch, msec);
+	return 0;
+}
+
+
+/*
+ * This routine sends a break character out the serial port.
+ *
+ * duration is in 1/1000's of a second
+ */
+static int dgrp_send_break(struct ch_struct *ch, int msec)
+{
+	ulong x;
+
+	wait_event_interruptible(ch->ch_flag_wait,
+		((ch->ch_flag & CH_TX_BREAK) == 0));
+	ch->ch_break_time += max(msec, 250);
+	ch->ch_send |= RR_TX_BREAK;
+	ch->ch_flag |= CH_TX_BREAK;
+	(ch->ch_nd)->nd_tx_work = 1;
+
+	x = (msec * HZ) / 1000;
+	wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+	return 0;
+}
+
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgrp_tty_tiocmget(struct tty_struct *tty)
+{
+	unsigned int mlast;
+	struct un_struct *un = tty->driver_data;
+	struct ch_struct *ch;
+
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) |
+		(ch->ch_mout & (DM_RTS | DM_DTR)));
+
+	/* defined in /usr/include/asm/termios.h */
+	mlast =   ((mlast & DM_RTS) ? TIOCM_RTS : 0)
+		| ((mlast & DM_DTR) ? TIOCM_DTR : 0)
+		| ((mlast & DM_CD)  ? TIOCM_CAR : 0)
+		| ((mlast & DM_RI)  ? TIOCM_RNG : 0)
+		| ((mlast & DM_DSR) ? TIOCM_DSR : 0)
+		| ((mlast & DM_CTS) ? TIOCM_CTS : 0);
+
+	return mlast;
+}
+
+
+/*
+ *      Set modem lines
+ */
+static int dgrp_tty_tiocmset(struct tty_struct *tty,
+			     unsigned int set, unsigned int clear)
+{
+	ulong lock_flags;
+	struct un_struct *un = tty->driver_data;
+	struct ch_struct *ch;
+
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	if (set & TIOCM_RTS)
+		ch->ch_mout |= DM_RTS;
+
+	if (set & TIOCM_DTR)
+		ch->ch_mout |= DM_DTR;
+
+	if (clear & TIOCM_RTS)
+		ch->ch_mout &= ~(DM_RTS);
+
+	if (clear & TIOCM_DTR)
+		ch->ch_mout &= ~(DM_DTR);
+
+	spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags);
+	ch->ch_flag |= CH_PARAM;
+	(ch->ch_nd)->nd_tx_work = 1;
+	wake_up_interruptible(&ch->ch_flag_wait);
+
+	spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags);
+
+	return 0;
+}
+
+
+
+/*
+ *      Get current modem status
+ */
+static int get_modem_info(struct ch_struct *ch, unsigned int *value)
+{
+	unsigned int mlast;
+
+	mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) |
+		(ch->ch_mout    &  (DM_RTS | DM_DTR)));
+
+	/* defined in /usr/include/asm/termios.h */
+	mlast =   ((mlast & DM_RTS) ? TIOCM_RTS : 0)
+		| ((mlast & DM_DTR) ? TIOCM_DTR : 0)
+		| ((mlast & DM_CD)  ? TIOCM_CAR : 0)
+		| ((mlast & DM_RI)  ? TIOCM_RNG : 0)
+		| ((mlast & DM_DSR) ? TIOCM_DSR : 0)
+		| ((mlast & DM_CTS) ? TIOCM_CTS : 0);
+	put_user(mlast, (unsigned int __user *) value);
+
+	return 0;
+}
+
+/*
+ *      Set modem lines
+ */
+static int set_modem_info(struct ch_struct *ch, unsigned int command,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	int mval = 0;
+	ulong lock_flags;
+
+	error = access_ok(VERIFY_READ, (void __user *) value, sizeof(int));
+	if (error == 0)
+		return -EFAULT;
+
+	get_user(arg, (unsigned int __user *) value);
+	mval |= ((arg & TIOCM_RTS) ? DM_RTS : 0)
+		| ((arg & TIOCM_DTR) ? DM_DTR : 0);
+
+	switch (command) {
+	case TIOCMBIS:  /* set flags */
+		ch->ch_mout |= mval;
+		break;
+	case TIOCMBIC:  /* clear flags */
+		ch->ch_mout &= ~mval;
+		break;
+	case TIOCMSET:
+		ch->ch_mout = mval;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags);
+
+	ch->ch_flag |= CH_PARAM;
+	(ch->ch_nd)->nd_tx_work = 1;
+	wake_up_interruptible(&ch->ch_flag_wait);
+
+	spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags);
+
+	return 0;
+}
+
+
+/*
+ *  Assign the custom baud rate to the channel structure
+ */
+static void dgrp_set_custom_speed(struct ch_struct *ch, int newrate)
+{
+	int testdiv;
+	int testrate_high;
+	int testrate_low;
+
+	int deltahigh, deltalow;
+
+	if (newrate < 0)
+		newrate = 0;
+
+	/*
+	 * Since the divisor is stored in a 16-bit integer, we make sure
+	 * we don't allow any rates smaller than a 16-bit integer would allow.
+	 * And of course, rates above the dividend won't fly.
+	 */
+	if (newrate && newrate < ((PORTSERVER_DIVIDEND / 0xFFFF) + 1))
+		newrate = ((PORTSERVER_DIVIDEND / 0xFFFF) + 1);
+	if (newrate && newrate > PORTSERVER_DIVIDEND)
+		newrate = PORTSERVER_DIVIDEND;
+
+	while (newrate > 0) {
+		testdiv = PORTSERVER_DIVIDEND / newrate;
+
+		/*
+		 * If we try to figure out what rate the PortServer would use
+		 * with the test divisor, it will be either equal or higher
+		 * than the requested baud rate.  If we then determine the
+		 * rate with a divisor one higher, we will get the next lower
+		 * supported rate below the requested.
+		 */
+		testrate_high = PORTSERVER_DIVIDEND / testdiv;
+		testrate_low  = PORTSERVER_DIVIDEND / (testdiv + 1);
+
+		/*
+		 * If the rate for the requested divisor is correct, just
+		 * use it and be done.
+		 */
+		if (testrate_high == newrate)
+			break;
+
+		/*
+		 * Otherwise, pick the rate that is closer (i.e. whichever rate
+		 * has a smaller delta).
+		 */
+		deltahigh = testrate_high - newrate;
+		deltalow = newrate - testrate_low;
+
+		if (deltahigh < deltalow)
+			newrate = testrate_high;
+		else
+			newrate = testrate_low;
+
+		break;
+	}
+
+	ch->ch_custom_speed = newrate;
+
+	drp_param(ch);
+
+	return;
+}
+
+
+/*
+ # dgrp_tty_digiseta()
+ *
+ * Ioctl to set the information from ditty.
+ *
+ * NOTE: DIGI_IXON, DSRPACE, DCDPACE, and DTRPACE are unsupported.  JAR 990922
+ */
+static int dgrp_tty_digiseta(struct tty_struct *tty,
+			     struct digi_struct *new_info)
+{
+	struct un_struct *un = tty->driver_data;
+	struct ch_struct *ch;
+
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	if (copy_from_user(&ch->ch_digi, (void __user *) new_info,
+			   sizeof(struct digi_struct)))
+		return -EFAULT;
+
+	if ((ch->ch_digi.digi_flags & RTSPACE) ||
+	    (ch->ch_digi.digi_flags & CTSPACE))
+		tty->termios.c_cflag |= CRTSCTS;
+	else
+		tty->termios.c_cflag &= ~CRTSCTS;
+
+	if (ch->ch_digi.digi_maxcps < 1)
+		ch->ch_digi.digi_maxcps = 1;
+
+	if (ch->ch_digi.digi_maxcps > 10000)
+		ch->ch_digi.digi_maxcps = 10000;
+
+	if (ch->ch_digi.digi_bufsize < 10)
+		ch->ch_digi.digi_bufsize = 10;
+
+	if (ch->ch_digi.digi_maxchar < 1)
+		ch->ch_digi.digi_maxchar = 1;
+
+	if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
+		ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
+
+	if (ch->ch_digi.digi_onlen > DIGI_PLEN)
+		ch->ch_digi.digi_onlen = DIGI_PLEN;
+
+	if (ch->ch_digi.digi_offlen > DIGI_PLEN)
+		ch->ch_digi.digi_offlen = DIGI_PLEN;
+
+	/* make the changes now */
+	drp_param(ch);
+
+	return 0;
+}
+
+
+
+/*
+ * dgrp_tty_digigetedelay()
+ *
+ * Ioctl to get the current edelay setting.
+ *
+ *
+ *
+ */
+static int dgrp_tty_digigetedelay(struct tty_struct *tty, int *retinfo)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return -EFAULT;
+
+	un = tty->driver_data;
+
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	tmp = ch->ch_edelay;
+
+	if (copy_to_user((void __user *) retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
+
+/*
+ * dgrp_tty_digisetedelay()
+ *
+ * Ioctl to set the EDELAY setting
+ *
+ */
+static int dgrp_tty_digisetedelay(struct tty_struct *tty, int *new_info)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int new_digi;
+
+	if (!tty || tty->magic != TTY_MAGIC)
+		return -EFAULT;
+
+	un = tty->driver_data;
+
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	if (copy_from_user(&new_digi, (void __user *)new_info, sizeof(int)))
+		return -EFAULT;
+
+	ch->ch_edelay = new_digi;
+
+	/* make the changes now */
+	drp_param(ch);
+
+	return 0;
+}
+
+
+/*
+ *	The usual assortment of ioctl's
+ *
+ *	note:  use tty_check_change to make sure that we are not
+ *	changing the state of a terminal when we are not a process
+ *	in the forground.  See tty_io.c
+ *		rc = tty_check_change(tty);
+ *		if (rc) return rc;
+ */
+static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+	int rc;
+	struct digiflow_struct   dflow;
+
+	if (!tty)
+		return -ENODEV;
+
+	un = tty->driver_data;
+	if (!un)
+		return -ENODEV;
+
+	ch = un->un_ch;
+	if (!ch)
+		return -ENODEV;
+
+	switch (cmd) {
+
+	/*
+	 * Here are all the standard ioctl's that we MUST implement
+	 */
+
+	case TCSBRK:
+		/*
+		 * TCSBRK is SVID version: non-zero arg --> no break
+		 * this behaviour is exploited by tcdrain().
+		 *
+		 * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+		 * between 0.25 and 0.5 seconds
+		 */
+
+		rc = tty_check_change(tty);
+		if (rc)
+			return rc;
+		tty_wait_until_sent(tty, 0);
+
+		if (!arg)
+			rc = dgrp_send_break(ch, 250); /* 1/4 second */
+
+		if (dgrp_tty_chars_in_buffer(tty) != 0)
+			return -EINTR;
+
+		return 0;
+
+	case TCSBRKP:
+		/* support for POSIX tcsendbreak()
+		 *
+		 * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+		 * between 0.25 and 0.5 seconds so we'll ask for something
+		 * in the middle: 0.375 seconds.
+		 */
+		rc = tty_check_change(tty);
+		if (rc)
+			return rc;
+		tty_wait_until_sent(tty, 0);
+
+		rc = dgrp_send_break(ch, arg ? arg*250 : 250);
+
+		if (dgrp_tty_chars_in_buffer(tty) != 0)
+			return -EINTR;
+		return 0;
+
+	case TIOCSBRK:
+		rc = tty_check_change(tty);
+		if (rc)
+			return rc;
+		tty_wait_until_sent(tty, 0);
+
+		/*
+		 * RealPort doesn't support turning on a break unconditionally.
+		 * The RealPort device will stop sending a break automatically
+		 * after the specified time value that we send in.
+		 */
+		rc = dgrp_send_break(ch, 250); /* 1/4 second */
+
+		if (dgrp_tty_chars_in_buffer(tty) != 0)
+			return -EINTR;
+		return 0;
+
+	case TIOCCBRK:
+		/*
+		 * RealPort doesn't support turning off a break unconditionally.
+		 * The RealPort device will stop sending a break automatically
+		 * after the specified time value that was sent when turning on
+		 * the break.
+		 */
+		return 0;
+
+	case TIOCGSOFTCAR:
+		rc = access_ok(VERIFY_WRITE, (void __user *) arg,
+			       sizeof(long));
+		if (rc == 0)
+			return -EFAULT;
+		put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg);
+		return 0;
+
+	case TIOCSSOFTCAR:
+		get_user(arg, (unsigned long __user *) arg);
+		tty->termios.c_cflag =
+			((tty->termios.c_cflag & ~CLOCAL) |
+			 (arg ? CLOCAL : 0));
+		return 0;
+
+	case TIOCMGET:
+		rc = access_ok(VERIFY_WRITE, (void __user *) arg,
+				 sizeof(unsigned int));
+		if (rc == 0)
+			return -EFAULT;
+		return get_modem_info(ch, (unsigned int *) arg);
+
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return set_modem_info(ch, cmd, (unsigned int *) arg);
+
+	/*
+	 * Here are any additional ioctl's that we want to implement
+	 */
+
+	case TCFLSH:
+		/*
+		 * The linux tty driver doesn't have a flush
+		 * input routine for the driver, assuming all backed
+		 * up data is in the line disc. buffers.  However,
+		 * we all know that's not the case.  Here, we
+		 * act on the ioctl, but then lie and say we didn't
+		 * so the line discipline will process the flush
+		 * also.
+		 */
+		rc = tty_check_change(tty);
+		if (rc)
+			return rc;
+
+		switch (arg) {
+		case TCIFLUSH:
+		case TCIOFLUSH:
+			/* only flush input if this is the only open unit */
+			if (!IS_PRINT(MINOR(tty_devnum(tty)))) {
+				ch->ch_rout = ch->ch_rin;
+				ch->ch_send |= RR_RX_FLUSH;
+				(ch->ch_nd)->nd_tx_work = 1;
+				(ch->ch_nd)->nd_tx_ready = 1;
+				wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+			}
+			if (arg == TCIFLUSH)
+				break;
+
+		case TCOFLUSH: /* flush output, or the receive buffer */
+			/*
+			 * This is handled in the tty_ioctl.c code
+			 * calling tty_flush_buffer
+			 */
+			break;
+
+		default:
+			/* POSIX.1 says return EINVAL if we got a bad arg */
+			return -EINVAL;
+		}
+		/* pretend we didn't recognize this IOCTL */
+		return -ENOIOCTLCMD;
+
+#ifdef TIOCGETP
+	case TIOCGETP:
+#endif
+	/*****************************************
+	Linux		HPUX		Function
+	TCSETA		TCSETA		- set the termios
+	TCSETAF		TCSETAF		- wait for drain first, then set termios
+	TCSETAW		TCSETAW		- wait for drain, flush the input queue, then set termios
+	- looking at the tty_ioctl code, these command all call our
+	tty_set_termios at the driver's end, when a TCSETA* is sent,
+	it is expecting the tty to have a termio structure,
+	NOT a termios stucture.  These two structures differ in size
+	and the tty_ioctl code does a conversion before processing them both.
+	- we should treat the TCSETAW TCSETAF ioctls the same, and let
+	the tty_ioctl code do the conversion stuff.
+
+	TCSETS
+	TCSETSF		(none)
+	TCSETSW
+	- the associated tty structure has a termios structure.
+	*****************************************/
+
+	case TCGETS:
+	case TCGETA:
+		return -ENOIOCTLCMD;
+
+	case TCSETAW:
+	case TCSETAF:
+	case TCSETSF:
+	case TCSETSW:
+		/*
+		 * The linux tty driver doesn't have a flush
+		 * input routine for the driver, assuming all backed
+		 * up data is in the line disc. buffers.  However,
+		 * we all know that's not the case.  Here, we
+		 * act on the ioctl, but then lie and say we didn't
+		 * so the line discipline will process the flush
+		 * also.
+		 */
+
+		/*
+		 * Also, now that we have TXPrint, we have to check
+		 * if this is the TXPrint device and the terminal
+		 * device is open. If so, do NOT run check_change,
+		 * as the terminal device is ALWAYS the parent.
+		 */
+		if (!IS_PRINT(MINOR(tty_devnum(tty))) ||
+		    !ch->ch_tun.un_open_count) {
+			rc = tty_check_change(tty);
+			if (rc)
+				return rc;
+		}
+
+		/* wait for all the characters in tbuf to drain */
+		tty_wait_until_sent(tty, 0);
+
+		if ((cmd == TCSETSF) || (cmd == TCSETAF)) {
+			/* flush the contents of the rbuf queue */
+			/* TODO:  check if this is print device? */
+			ch->ch_send |= RR_RX_FLUSH;
+			(ch->ch_nd)->nd_tx_ready = 1;
+			(ch->ch_nd)->nd_tx_work = 1;
+			wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+			/* do we need to do this?  just to be safe! */
+			ch->ch_rout = ch->ch_rin;
+		}
+
+		/* pretend we didn't recognize this */
+		return -ENOIOCTLCMD;
+
+	case TCXONC:
+		/*
+		 * The Linux Line Discipline (LD) would do this for us if we
+		 * let it, but we have the special firmware options to do this
+		 * the "right way" regardless of hardware or software flow
+		 * control so we'll do it outselves instead of letting the LD
+		 * do it.
+		 */
+		rc = tty_check_change(tty);
+		if (rc)
+			return rc;
+
+		switch (arg) {
+		case TCOON:
+			dgrp_tty_start(tty);
+			return 0;
+		case TCOOFF:
+			dgrp_tty_stop(tty);
+			return 0;
+		case TCION:
+			dgrp_tty_input_start(tty);
+			return 0;
+		case TCIOFF:
+			dgrp_tty_input_stop(tty);
+			return 0;
+		default:
+			return -EINVAL;
+		}
+
+	case DIGI_GETA:
+		/* get information for ditty */
+		if (copy_to_user((struct digi_struct __user *) arg,
+				 &ch->ch_digi, sizeof(struct digi_struct)))
+			return -EFAULT;
+		break;
+
+	case DIGI_SETAW:
+	case DIGI_SETAF:
+		/* wait for all the characters in tbuf to drain */
+		tty_wait_until_sent(tty, 0);
+
+		if (cmd == DIGI_SETAF) {
+			/* flush the contents of the rbuf queue */
+			/* send down a packet with RR_RX_FLUSH set */
+			ch->ch_send |= RR_RX_FLUSH;
+			(ch->ch_nd)->nd_tx_ready = 1;
+			(ch->ch_nd)->nd_tx_work = 1;
+			wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+			/* do we need to do this?  just to be safe! */
+			ch->ch_rout = ch->ch_rin;
+		}
+
+		/* pretend we didn't recognize this */
+
+	case DIGI_SETA:
+		return dgrp_tty_digiseta(tty, (struct digi_struct *) arg);
+
+	case DIGI_SEDELAY:
+		return dgrp_tty_digisetedelay(tty, (int *) arg);
+
+	case DIGI_GEDELAY:
+		return dgrp_tty_digigetedelay(tty, (int *) arg);
+
+	case DIGI_GETFLOW:
+	case DIGI_GETAFLOW:
+		if (cmd == (DIGI_GETFLOW)) {
+			dflow.startc = tty->termios.c_cc[VSTART];
+			dflow.stopc = tty->termios.c_cc[VSTOP];
+		} else {
+			dflow.startc = ch->ch_xxon;
+			dflow.stopc = ch->ch_xxoff;
+		}
+
+		if (copy_to_user((char __user *)arg, &dflow, sizeof(dflow)))
+			return -EFAULT;
+		break;
+
+	case DIGI_SETFLOW:
+	case DIGI_SETAFLOW:
+
+		if (copy_from_user(&dflow, (char __user *)arg, sizeof(dflow)))
+			return -EFAULT;
+
+		if (cmd == (DIGI_SETFLOW)) {
+			tty->termios.c_cc[VSTART] = dflow.startc;
+			tty->termios.c_cc[VSTOP] = dflow.stopc;
+		} else {
+			ch->ch_xxon = dflow.startc;
+			ch->ch_xxoff = dflow.stopc;
+		}
+		break;
+
+	case DIGI_GETCUSTOMBAUD:
+		rc = access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(int));
+		if (rc == 0)
+			return -EFAULT;
+		put_user(ch->ch_custom_speed, (unsigned int __user *) arg);
+		break;
+
+	case DIGI_SETCUSTOMBAUD:
+	{
+		int new_rate;
+
+		get_user(new_rate, (unsigned int __user *) arg);
+		dgrp_set_custom_speed(ch, new_rate);
+
+		break;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+/*
+ *  This routine allows the tty driver to be notified when
+ *  the device's termios setting have changed.  Note that we
+ *  should be prepared to accept the case where old == NULL
+ *  and try to do something rational.
+ *
+ *  So we need to make sure that our copies of ch_oflag,
+ *  ch_clag, and ch_iflag reflect the tty->termios flags.
+ */
+static void dgrp_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct ktermios *ts;
+	struct ch_struct *ch;
+	struct un_struct *un;
+
+	/* seems silly, but we have to check all these! */
+	if (!tty)
+		return;
+
+	un = tty->driver_data;
+	if (!un)
+		return;
+
+	ts = &tty->termios;
+
+	ch = un->un_ch;
+	if (!ch)
+		return;
+
+	drp_param(ch);
+
+	/* the CLOCAL flag has just been set */
+	if (!(old->c_cflag & CLOCAL) && C_CLOCAL(tty))
+		wake_up_interruptible(&un->un_open_wait);
+}
+
+
+/*
+ *	Throttle receiving data.  We just set a bit and stop reading
+ *	data out of the channel buffer.  It will back up and the
+ *	FEP will do whatever is necessary to stop the far end.
+ */
+static void dgrp_tty_throttle(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_flag |= CH_RXSTOP;
+}
+
+
+static void dgrp_tty_unthrottle(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_flag &= ~CH_RXSTOP;
+}
+
+/*
+ *	Stop the transmitter
+ */
+static void dgrp_tty_stop(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_send |= RR_TX_STOP;
+	ch->ch_send &= ~RR_TX_START;
+
+	/* make the change NOW! */
+	(ch->ch_nd)->nd_tx_ready = 1;
+	if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+		wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+}
+
+/*
+ *	Start the transmitter
+ */
+static void dgrp_tty_start(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	/* TODO: don't do anything if the transmitter is not stopped */
+
+	ch->ch_send |= RR_TX_START;
+	ch->ch_send &= ~RR_TX_STOP;
+
+	/* make the change NOW! */
+	(ch->ch_nd)->nd_tx_ready = 1;
+	(ch->ch_nd)->nd_tx_work = 1;
+	if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+		wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+/*
+ *	Stop the reciever
+ */
+static void dgrp_tty_input_stop(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_send |= RR_RX_STOP;
+	ch->ch_send &= ~RR_RX_START;
+	(ch->ch_nd)->nd_tx_ready = 1;
+	if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+		wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+
+static void dgrp_tty_send_xchar(struct tty_struct *tty, char c)
+{
+	struct un_struct *un;
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	un = tty->driver_data;
+	if (!un)
+		return;
+
+	ch = un->un_ch;
+	if (!ch)
+		return;
+	if (c == STOP_CHAR(tty))
+		ch->ch_send |= RR_RX_STOP;
+	else if (c == START_CHAR(tty))
+		ch->ch_send |= RR_RX_START;
+
+	ch->ch_nd->nd_tx_ready = 1;
+	ch->ch_nd->nd_tx_work = 1;
+
+	return;
+}
+
+
+static void dgrp_tty_input_start(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+
+	if (!tty)
+		return;
+
+	ch = ((struct un_struct *) tty->driver_data)->un_ch;
+	if (!ch)
+		return;
+
+	ch->ch_send |= RR_RX_START;
+	ch->ch_send &= ~RR_RX_STOP;
+	(ch->ch_nd)->nd_tx_ready = 1;
+	(ch->ch_nd)->nd_tx_work = 1;
+	if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+		wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+
+/*
+ *	Hangup the port.  Like a close, but don't wait for output
+ *	to drain.
+ *
+ *	How do we close all the channels that are open?
+ */
+static void dgrp_tty_hangup(struct tty_struct *tty)
+{
+	struct ch_struct *ch;
+	struct nd_struct *nd;
+	struct un_struct *un;
+
+	if (!tty)
+		return;
+
+	un = tty->driver_data;
+	if (!un)
+		return;
+
+	ch = un->un_ch;
+	if (!ch)
+		return;
+
+	nd = ch->ch_nd;
+
+	if (C_HUPCL(tty)) {
+		/* LOWER DTR */
+		ch->ch_mout &= ~DM_DTR;
+		/* Don't do this here */
+		/* ch->ch_flag |= CH_HANGUP; */
+		ch->ch_nd->nd_tx_ready = 1;
+		ch->ch_nd->nd_tx_work  = 1;
+		if (waitqueue_active(&ch->ch_flag_wait))
+			wake_up_interruptible(&ch->ch_flag_wait);
+	}
+
+}
+
+/************************************************************************/
+/*                                                                      */
+/*       TTY Initialization/Cleanup Functions                           */
+/*                                                                      */
+/************************************************************************/
+
+/*
+ *	Uninitialize the TTY portion of the supplied node.  Free all
+ *      memory and resources associated with this node.  Do it in reverse
+ *      allocation order: this might possibly result in less fragmentation
+ *      of memory, though I don't know this for sure.
+ */
+void
+dgrp_tty_uninit(struct nd_struct *nd)
+{
+	char id[3];
+
+	ID_TO_CHAR(nd->nd_ID, id);
+
+	if (nd->nd_ttdriver_flags & SERIAL_TTDRV_REG) {
+		tty_unregister_driver(nd->nd_serial_ttdriver);
+
+		kfree(nd->nd_serial_ttdriver->ttys);
+		nd->nd_serial_ttdriver->ttys = NULL;
+
+		put_tty_driver(nd->nd_serial_ttdriver);
+		nd->nd_ttdriver_flags &= ~SERIAL_TTDRV_REG;
+	}
+
+	if (nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG) {
+		tty_unregister_driver(nd->nd_callout_ttdriver);
+
+		kfree(nd->nd_callout_ttdriver->ttys);
+		nd->nd_callout_ttdriver->ttys = NULL;
+
+		put_tty_driver(nd->nd_callout_ttdriver);
+		nd->nd_ttdriver_flags &= ~CALLOUT_TTDRV_REG;
+	}
+
+	if (nd->nd_ttdriver_flags & XPRINT_TTDRV_REG) {
+		tty_unregister_driver(nd->nd_xprint_ttdriver);
+
+		kfree(nd->nd_xprint_ttdriver->ttys);
+		nd->nd_xprint_ttdriver->ttys = NULL;
+
+		put_tty_driver(nd->nd_xprint_ttdriver);
+		nd->nd_ttdriver_flags &= ~XPRINT_TTDRV_REG;
+	}
+}
+
+
+
+/*
+ *     Initialize the TTY portion of the supplied node.
+ */
+int
+dgrp_tty_init(struct nd_struct *nd)
+{
+	char id[3];
+	int  rc;
+	int  i;
+
+	ID_TO_CHAR(nd->nd_ID, id);
+
+	/*
+	 *  Initialize the TTDRIVER structures.
+	 */
+
+	nd->nd_serial_ttdriver = alloc_tty_driver(CHAN_MAX);
+	sprintf(nd->nd_serial_name,  "tty_dgrp_%s_", id);
+
+	nd->nd_serial_ttdriver->owner = THIS_MODULE;
+	nd->nd_serial_ttdriver->name = nd->nd_serial_name;
+	nd->nd_serial_ttdriver->name_base = 0;
+	nd->nd_serial_ttdriver->major = 0;
+	nd->nd_serial_ttdriver->minor_start = 0;
+	nd->nd_serial_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+	nd->nd_serial_ttdriver->subtype = SERIAL_TYPE_NORMAL;
+	nd->nd_serial_ttdriver->init_termios = DefaultTermios;
+	nd->nd_serial_ttdriver->driver_name = "dgrp";
+	nd->nd_serial_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+					 TTY_DRIVER_DYNAMIC_DEV |
+					 TTY_DRIVER_HARDWARE_BREAK);
+
+	/* The kernel wants space to store pointers to tty_structs. */
+	nd->nd_serial_ttdriver->ttys =
+		kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!nd->nd_serial_ttdriver->ttys)
+		return -ENOMEM;
+
+	tty_set_operations(nd->nd_serial_ttdriver, &dgrp_tty_ops);
+
+	if (!(nd->nd_ttdriver_flags & SERIAL_TTDRV_REG)) {
+		/*
+		 *   Register tty devices
+		 */
+		rc = tty_register_driver(nd->nd_serial_ttdriver);
+		if (rc < 0) {
+			/*
+			 * If errno is EBUSY, this means there are no more
+			 * slots available to have us auto-majored.
+			 * (Which is currently supported up to 256)
+			 *
+			 * We can still request majors above 256,
+			 * we just have to do it manually.
+			 */
+			if (rc == -EBUSY) {
+				int i;
+				int max_majors = 1U << (32 - MINORBITS);
+				for (i = 256; i < max_majors; i++) {
+					nd->nd_serial_ttdriver->major = i;
+					rc = tty_register_driver(nd->nd_serial_ttdriver);
+					if (rc >= 0)
+						break;
+				}
+				/* Really fail now, since we ran out
+				 * of majors to try. */
+				if (i == max_majors)
+					return rc;
+
+			} else {
+				return rc;
+			}
+		}
+		nd->nd_ttdriver_flags |= SERIAL_TTDRV_REG;
+	}
+
+	nd->nd_callout_ttdriver = alloc_tty_driver(CHAN_MAX);
+	sprintf(nd->nd_callout_name, "cu_dgrp_%s_",  id);
+
+	nd->nd_callout_ttdriver->owner = THIS_MODULE;
+	nd->nd_callout_ttdriver->name = nd->nd_callout_name;
+	nd->nd_callout_ttdriver->name_base = 0;
+	nd->nd_callout_ttdriver->major = nd->nd_serial_ttdriver->major;
+	nd->nd_callout_ttdriver->minor_start = 0x40;
+	nd->nd_callout_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+	nd->nd_callout_ttdriver->subtype = SERIAL_TYPE_CALLOUT;
+	nd->nd_callout_ttdriver->init_termios = DefaultTermios;
+	nd->nd_callout_ttdriver->driver_name = "dgrp";
+	nd->nd_callout_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+					  TTY_DRIVER_DYNAMIC_DEV |
+					  TTY_DRIVER_HARDWARE_BREAK);
+
+	/* The kernel wants space to store pointers to tty_structs. */
+	nd->nd_callout_ttdriver->ttys =
+		kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!nd->nd_callout_ttdriver->ttys)
+		return -ENOMEM;
+
+	tty_set_operations(nd->nd_callout_ttdriver, &dgrp_tty_ops);
+
+	if (dgrp_register_cudevices) {
+		if (!(nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG)) {
+			/*
+			 *   Register cu devices
+			 */
+			rc = tty_register_driver(nd->nd_callout_ttdriver);
+			if (rc < 0)
+				return rc;
+			nd->nd_ttdriver_flags |= CALLOUT_TTDRV_REG;
+		}
+	}
+
+
+	nd->nd_xprint_ttdriver = alloc_tty_driver(CHAN_MAX);
+	sprintf(nd->nd_xprint_name,  "pr_dgrp_%s_", id);
+
+	nd->nd_xprint_ttdriver->owner = THIS_MODULE;
+	nd->nd_xprint_ttdriver->name = nd->nd_xprint_name;
+	nd->nd_xprint_ttdriver->name_base = 0;
+	nd->nd_xprint_ttdriver->major = nd->nd_serial_ttdriver->major;
+	nd->nd_xprint_ttdriver->minor_start = 0x80;
+	nd->nd_xprint_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+	nd->nd_xprint_ttdriver->subtype = SERIAL_TYPE_XPRINT;
+	nd->nd_xprint_ttdriver->init_termios = DefaultTermios;
+	nd->nd_xprint_ttdriver->driver_name = "dgrp";
+	nd->nd_xprint_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+					 TTY_DRIVER_DYNAMIC_DEV |
+					 TTY_DRIVER_HARDWARE_BREAK);
+
+	/* The kernel wants space to store pointers to tty_structs. */
+	nd->nd_xprint_ttdriver->ttys =
+		kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+	if (!nd->nd_xprint_ttdriver->ttys)
+		return -ENOMEM;
+
+	tty_set_operations(nd->nd_xprint_ttdriver, &dgrp_tty_ops);
+
+	if (dgrp_register_prdevices) {
+		if (!(nd->nd_ttdriver_flags & XPRINT_TTDRV_REG)) {
+			/*
+			 *   Register transparent print devices
+			 */
+			rc = tty_register_driver(nd->nd_xprint_ttdriver);
+			if (rc < 0)
+				return rc;
+			nd->nd_ttdriver_flags |= XPRINT_TTDRV_REG;
+		}
+	}
+
+	for (i = 0; i < CHAN_MAX; i++) {
+		struct ch_struct *ch = nd->nd_chan + i;
+
+		ch->ch_nd = nd;
+		ch->ch_digi = digi_init;
+		ch->ch_edelay = 100;
+		ch->ch_custom_speed = 0;
+		ch->ch_portnum = i;
+		ch->ch_tun.un_ch = ch;
+		ch->ch_pun.un_ch = ch;
+		ch->ch_tun.un_type = SERIAL_TYPE_NORMAL;
+		ch->ch_pun.un_type = SERIAL_TYPE_XPRINT;
+
+		init_waitqueue_head(&(ch->ch_flag_wait));
+		init_waitqueue_head(&(ch->ch_sleep));
+
+		init_waitqueue_head(&(ch->ch_tun.un_open_wait));
+		init_waitqueue_head(&(ch->ch_tun.un_close_wait));
+
+		init_waitqueue_head(&(ch->ch_pun.un_open_wait));
+		init_waitqueue_head(&(ch->ch_pun.un_close_wait));
+		tty_port_init(&ch->port);
+		tty_port_init(&ch->port);
+	}
+	return 0;
+}

+ 129 - 0
drivers/staging/dgrp/digirp.h

@@ -0,0 +1,129 @@
+/************************************************************************
+ * HP-UX Realport Daemon interface file.
+ *
+ * Copyright (C) 1998, by Digi International.  All Rights Reserved.
+ ************************************************************************/
+
+#ifndef _DIGIDRP_H
+#define _DIGIDRP_H
+
+/************************************************************************
+ * This file contains defines for the ioctl() interface to
+ * the realport driver.   This ioctl() interface is used by the
+ * daemon to set speed setup parameters honored by the driver.
+ ************************************************************************/
+
+struct link_struct {
+	int lk_fast_rate;  /* Fast line rate to be used
+			      when the delay is less-equal
+			      to lk_fast_delay */
+
+	int lk_fast_delay; /* Fast line rate delay in
+			      milliseconds */
+
+	int lk_slow_rate;  /* Slow line rate to be used when
+			      the delay is greater-equal
+			      to lk_slow_delay */
+
+	int lk_slow_delay; /* Slow line rate delay in
+			      milliseconds */
+
+	int lk_header_size; /* Estimated packet header size
+			       when sent across the slowest
+			       link.  */
+};
+
+#define DIGI_GETLINK	_IOW('e', 103, struct link_struct)	/* Get link parameters */
+#define DIGI_SETLINK	_IOW('e', 104, struct link_struct)	/* Set link parameters */
+
+
+/************************************************************************
+ * This module provides application access to special Digi
+ * serial line enhancements which are not standard UNIX(tm) features.
+ ************************************************************************/
+
+struct	digiflow_struct {
+	unsigned char	startc;				/* flow cntl start char	*/
+	unsigned char	stopc;				/* flow cntl stop char	*/
+};
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON	0x0001		/* Handle IXON in the FEP	*/
+#define DIGI_FAST	0x0002		/* Fast baud rates		*/
+#define RTSPACE		0x0004		/* RTS input flow control	*/
+#define CTSPACE		0x0008		/* CTS output flow control	*/
+#define DSRPACE		0x0010		/* DSR output flow control	*/
+#define DCDPACE		0x0020		/* DCD output flow control	*/
+#define DTRPACE		0x0040		/* DTR input flow control	*/
+#define DIGI_COOK	0x0080		/* Cooked processing done in FEP */
+#define DIGI_FORCEDCD	0x0100		/* Force carrier		*/
+#define	DIGI_ALTPIN	0x0200		/* Alternate RJ-45 pin config	*/
+#define	DIGI_AIXON	0x0400		/* Aux flow control in fep	*/
+#define	DIGI_PRINTER	0x0800		/* Hold port open for flow cntrl */
+#define DIGI_PP_INPUT	0x1000		/* Change parallel port to input */
+#define DIGI_422	0x4000		/* Change parallel port to input */
+#define DIGI_RTS_TOGGLE	0x8000		/* Support RTS Toggle		 */
+
+
+/************************************************************************
+ * Values associated with transparent print
+ ************************************************************************/
+#define DIGI_PLEN	8		/* String length */
+#define	DIGI_TSIZ	10		/* Terminal string len */
+
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_struct {
+	unsigned short	digi_flags;		/* Flags (see above)	*/
+	unsigned short	digi_maxcps;		/* Max printer CPS	*/
+	unsigned short	digi_maxchar;		/* Max chars in print queue */
+	unsigned short	digi_bufsize;		/* Buffer size		*/
+	unsigned char	digi_onlen;		/* Length of ON string	*/
+	unsigned char	digi_offlen;		/* Length of OFF string	*/
+	char		digi_onstr[DIGI_PLEN];	/* Printer on string	*/
+	char		digi_offstr[DIGI_PLEN];	/* Printer off string	*/
+	char		digi_term[DIGI_TSIZ];	/* terminal string	*/
+};
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+/* Read params */
+#define DIGI_GETA	_IOR('e', 94, struct digi_struct)
+
+/* Set params */
+#define DIGI_SETA	_IOW('e', 95, struct digi_struct)
+
+/* Drain & set params	*/
+#define DIGI_SETAW	_IOW('e', 96, struct digi_struct)
+
+/* Drain, flush & set params */
+#define DIGI_SETAF	_IOW('e', 97, struct digi_struct)
+
+/* Get startc/stopc flow control characters */
+#define	DIGI_GETFLOW	_IOR('e', 99, struct digiflow_struct)
+
+/* Set startc/stopc flow control characters */
+#define	DIGI_SETFLOW	_IOW('e', 100, struct digiflow_struct)
+
+/* Get Aux. startc/stopc flow control chars */
+#define	DIGI_GETAFLOW	_IOR('e', 101, struct digiflow_struct)
+
+/* Set Aux. startc/stopc flow control chars */
+#define	DIGI_SETAFLOW	_IOW('e', 102, struct digiflow_struct)
+
+/* Set integer baud rate */
+#define	DIGI_SETCUSTOMBAUD	_IOW('e', 106, int)
+
+/* Get integer baud rate */
+#define	DIGI_GETCUSTOMBAUD	_IOR('e', 107, int)
+
+#define	DIGI_GEDELAY	_IOR('d', 246, int)	/* Get edelay */
+#define	DIGI_SEDELAY	_IOW('d', 247, int)	/* Get edelay */
+
+
+#endif /* _DIGIDRP_H */

+ 693 - 0
drivers/staging/dgrp/drp.h

@@ -0,0 +1,693 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ *     Gene Olson  <gene at digi dot com>
+ *     James Puzzo <jamesp at digi dot com>
+ *     Scott Kilau <scottk at digi dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+ * Master include file for Linux Realport Driver.
+ ************************************************************************/
+
+#ifndef __DRP_H
+#define __DRP_H
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/semaphore.h>
+#include <linux/tty.h>
+
+
+#include "digirp.h"
+
+/************************************************************************
+ * Tuning parameters.
+ ************************************************************************/
+
+#define CHAN_MAX	64		/* Max # ports per server */
+
+#define SEQ_MAX		128		/* Max # transmit sequences (2^n) */
+#define SEQ_MASK	(SEQ_MAX-1)	/* Sequence buffer modulus mask */
+
+#define TBUF_MAX	4096		/* Size of transmit buffer (2^n) */
+#define RBUF_MAX	4096		/* Size of receive buffer (2^n) */
+
+#define TBUF_MASK	(TBUF_MAX-1)	/* Transmit buffer modulus mask */
+#define RBUF_MASK	(RBUF_MAX-1)	/* Receive buffer modulus mask */
+
+#define TBUF_LOW	1000		/* Transmit low water mark */
+
+#define UIO_BASE	1000		/* Base for write operations */
+#define UIO_MIN		2000		/* Minimum size application buffer */
+#define UIO_MAX		8100		/* Unix I/O buffer size */
+
+#define MON_MAX		65536		/* Monitor buffer size (2^n) */
+#define MON_MASK	(MON_MAX-1)	/* Monitor wrap mask */
+
+#define DPA_MAX		65536		/* DPA buffer size (2^n) */
+#define DPA_MASK	(DPA_MAX-1)	/* DPA wrap mask */
+#define DPA_HIGH_WATER	58000		/* Enforce flow control when
+					 * over this amount
+					 */
+
+#define IDLE_MAX	(20 * HZ)	/* Max TCP link idle time */
+
+#define MAX_DESC_LEN	100		/* Maximum length of stored PS
+					 * description
+					 */
+
+#define WRITEBUFLEN	((4096) + 4)    /* 4 extra for alignment play space */
+
+#define VPDSIZE		512
+
+/************************************************************************
+ * Minor device decoding conventions.
+ ************************************************************************
+ *
+ * For Linux, the net and mon devices are handled via "proc", so we
+ * only have to mux the "tty" devices.  Since every PortServer will
+ * have an individual major number, the PortServer number does not
+ * need to be encoded, and in fact, does not need to exist.
+ *
+ */
+
+/*
+ * Port device decoding conventions:
+ *
+ *	Device 00 - 3f        64 dial-in modem devices. (tty)
+ *	Device 40 - 7f        64 dial-out tty devices.  (cu)
+ *	Device 80 - bf        64 dial-out printer devices.
+ *
+ *  IS_PRINT(dev)		This is a printer device.
+ *
+ *  OPEN_CATEGORY(dev)		Specifies the device category.  No two
+ *				devices of different categories may be open
+ *				at the same time.
+ *
+ * The following require the category returned by OPEN_CATEGORY().
+ *
+ *  OPEN_WAIT_AVAIL(cat)	Waits on open until the device becomes
+ *				available.  Fails if NDELAY specified.
+ *
+ *  OPEN_WAIT_CARRIER(cat)	Waits on open if carrier is not present.
+ *				Succeeds if NDELAY is given.
+ *
+ *  OPEN_FORCES_CARRIER(cat)	Carrier is forced high on open.
+ *
+ */
+
+#define PORT_NUM(dev)			((dev) & 0x3f)
+
+#define OPEN_CATEGORY(dev)		((((dev) & 0x80) & 0x40))
+#define IS_PRINT(dev)			(((dev) & 0xff) >= 0x80)
+
+#define OPEN_WAIT_AVAIL(cat)		(((cat) & 0x40) == 0x000)
+#define OPEN_WAIT_CARRIER(cat)		(((cat) & 0x40) == 0x000)
+#define OPEN_FORCES_CARRIER(cat)	(((cat) & 0x40) != 0x000)
+
+
+/************************************************************************
+ * Modem signal defines for 16450/16550 compatible FEP.
+ * set in ch_mout, ch_mflow, ch_mlast etc
+ ************************************************************************/
+
+/* TODO : Re-verify that these modem signal definitions are correct */
+
+#define DM_DTR		0x01
+#define DM_RTS		0x02
+#define DM_RTS_TOGGLE	0x04
+
+#define DM_OUT1		0x04
+#define DM_OUT2		0x08
+
+#define DM_CTS		0x10
+#define DM_DSR		0x20
+#define DM_RI		0x40
+#define DM_CD		0x80		/* This is the DCD flag */
+
+
+/************************************************************************
+ * Realport Event Flags.
+ ************************************************************************/
+
+#define EV_OPU		0x0001		/* Ouput paused by client */
+#define EV_OPS		0x0002		/* Output paused by XOFF */
+#define EV_OPX		0x0004		/* Output paused by XXOFF */
+#define EV_OPH		0x0008		/* Output paused by MFLOW */
+#define EV_IPU		0x0010		/* Input paused by client */
+#define EV_IPS		0x0020		/* Input paused by hi/low water */
+#define EV_TXB		0x0040		/* Transmit break pending */
+#define EV_TXI		0x0080		/* Transmit immediate pending */
+#define EV_TXF		0x0100		/* Transmit flow control pending */
+#define EV_RXB		0x0200		/* Break received */
+
+
+/************************************************************************
+ * Realport CFLAGS.
+ ************************************************************************/
+
+#define CF_CS5		0x0000		/* 5 bit characters */
+#define CF_CS6		0x0010		/* 6 bit characters */
+#define CF_CS7		0x0020		/* 7 bit characters */
+#define CF_CS8		0x0030		/* 8 bit characters */
+#define CF_CSIZE	0x0030		/* Character size */
+#define CF_CSTOPB	0x0040		/* Two stop bits */
+#define CF_CREAD	0x0080		/* Enable receiver */
+#define CF_PARENB	0x0100		/* Enable parity */
+#define CF_PARODD	0x0200		/* Odd parity */
+#define CF_HUPCL	0x0400		/* Drop DTR on close */
+
+
+/************************************************************************
+ * Realport XFLAGS.
+ ************************************************************************/
+
+#define XF_XPAR		0x0001		/* Enable Mark/Space Parity */
+#define XF_XMODEM	0x0002		/* Enable in-band modem signalling */
+#define XF_XCASE	0x0004		/* Convert special characters */
+#define XF_XEDATA	0x0008		/* Error data in stream */
+#define XF_XTOSS	0x0010		/* Toss IXANY characters */
+#define XF_XIXON	0x0020		/* xxon/xxoff enable */
+
+
+/************************************************************************
+ * Realport IFLAGS.
+ ************************************************************************/
+
+#define IF_IGNBRK	0x0001		/* Ignore input break */
+#define IF_BRKINT	0x0002		/* Break interrupt */
+#define IF_IGNPAR	0x0004		/* Ignore error characters */
+#define IF_PARMRK	0x0008		/* Error chars marked with 0xff */
+#define IF_INPCK	0x0010		/* Input parity checking enabled */
+#define IF_ISTRIP	0x0020		/* Input chars masked with 0x7F */
+#define IF_IXON		0x0400		/* Output software flow control */
+#define IF_IXANY	0x0800		/* Restart output on any char */
+#define	IF_IXOFF	0x1000		/* Input software flow control */
+#define IF_DOSMODE	0x8000		/* 16450-compatible errors */
+
+
+/************************************************************************
+ * Realport OFLAGS.
+ ************************************************************************/
+
+#define OF_OLCUC	0x0002		/* Map lower to upper case */
+#define OF_ONLCR	0x0004		/* Map NL to CR-NL */
+#define OF_OCRNL	0x0008		/* Map CR to NL */
+#define OF_ONOCR	0x0010		/* No CR output at column 0 */
+#define OF_ONLRET	0x0020		/* Assume NL does NL/CR */
+#define OF_TAB3		0x1800		/* Tabs expand to 8 spaces */
+#define OF_TABDLY	0x1800		/* Tab delay */
+
+/************************************************************************
+ * Unit flag definitions for un_flag.
+ ************************************************************************/
+
+/* These are the DIGI unit flags */
+#define UN_EXCL		0x00010000	/* Exclusive open */
+#define UN_STICKY	0x00020000	/* TTY Settings are now sticky */
+#define UN_BUSY		0x00040000	/* Some work this channel */
+#define UN_PWAIT	0x00080000	/* Printer waiting for terminal */
+#define UN_TIME		0x00100000	/* Waiting on time */
+#define UN_EMPTY	0x00200000	/* Waiting output queue empty */
+#define UN_LOW		0x00400000	/* Waiting output low water */
+#define UN_DIGI_MASK	0x00FF0000	/* Waiting output low water */
+
+/*
+ * Definitions for async_struct (and serial_struct) flags field
+ *
+ * these are the ASYNC flags copied from serial.h
+ *
+ */
+#define UN_HUP_NOTIFY	0x0001 /* Notify getty on hangups and
+				* closes on the callout port
+				*/
+#define UN_FOURPORT	0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define UN_SAK		0x0004	/* Secure Attention Key (Orange book) */
+#define UN_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define UN_SPD_MASK	0x0030
+#define UN_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+#define UN_SPD_VHI	0x0020	/* Use 115200 instead of 38400 bps */
+#define UN_SPD_CUST	0x0030	/* Use user-specified divisor */
+
+#define UN_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define UN_AUTO_IRQ	0x0080 /* Do automatic IRQ during autoconfiguration */
+
+#define UN_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define UN_PGRP_LOCKOUT	   0x0200 /* Lock out cua opens based on pgrp */
+#define UN_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define UN_FLAGS	0x0FFF	/* Possible legal async flags */
+#define UN_USR_MASK	0x0430	/* Legal flags that non-privileged
+				 * users can set or reset
+				 */
+
+#define UN_INITIALIZED		0x80000000 /* Serial port was initialized */
+#define UN_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define UN_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define UN_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define UN_CLOSING		0x08000000 /* Serial port is closing */
+#define UN_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define UN_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+#define UN_SHARE_IRQ		0x01000000 /* for multifunction cards */
+
+
+/************************************************************************
+ * Structure for terminal or printer unit.  struct un_struct
+ *
+ * Note that in some places the code assumes the "tty_t" is placed
+ * first in the structure.
+ ************************************************************************/
+
+struct un_struct {
+	struct tty_struct *un_tty;		/* System TTY struct */
+	struct ch_struct *un_ch;		/* Associated channel */
+
+	ushort     un_open_count;		/* Successful open count */
+	int		un_flag;		/* Unit flags */
+	ushort     un_tbusy;		/* Busy transmit count */
+
+	wait_queue_head_t  un_open_wait;
+	wait_queue_head_t  un_close_wait;
+	ushort	un_type;
+	struct device *un_sysfs;
+};
+
+
+/************************************************************************
+ * Channel State Numbers for ch_state.
+ ************************************************************************/
+
+/*
+ * The ordering is important.
+ *
+ *    state <= CS_WAIT_CANCEL implies the channel is definitely closed.
+ *
+ *    state >= CS_WAIT_FAIL  implies the channel is definitely open.
+ *
+ *    state >= CS_READY implies data is allowed on the channel.
+ */
+
+enum dgrp_ch_state_t {
+	CS_IDLE = 0,	    /* Channel is idle */
+	CS_WAIT_OPEN = 1,   /* Waiting for Immediate Open Resp */
+	CS_WAIT_CANCEL = 2, /* Waiting for Per/Incom Cancel Resp */
+	CS_WAIT_FAIL = 3,   /* Waiting for Immed Open Failure */
+	CS_SEND_QUERY = 4,  /* Ready to send Port Query */
+	CS_WAIT_QUERY = 5,  /* Waiting for Port Query Response */
+	CS_READY = 6,	    /* Ready to accept commands and data */
+	CS_SEND_CLOSE =	7,  /* Ready to send Close Request */
+	CS_WAIT_CLOSE =	8   /* Waiting for Close Response */
+};
+
+/************************************************************************
+ * Device flag definitions for ch_flag.
+ ************************************************************************/
+
+/*
+ *  Note that the state of the two carrier based flags is key.	When
+ *  we check for carrier state transitions, we look at the current
+ *  physical state of the DCD line and compare it with PHYS_CD (which
+ *  was the state the last time we checked), and we also determine
+ *  a new virtual state (composite of the physical state, FORCEDCD,
+ *  CLOCAL, etc.) and compare it with VIRT_CD.
+ *
+ *  VIRTUAL transitions high will have the side effect of waking blocked
+ *  opens.
+ *
+ *  PHYSICAL transitions low will cause hangups to occur _IF_ the virtual
+ *  state is also low.	We DON'T want to hangup on a PURE virtual drop.
+ */
+
+#define CH_HANGUP	0x00002		/* Server port ready to close */
+
+#define CH_VIRT_CD	0x00004		/* Carrier was virtually present */
+#define CH_PHYS_CD	0x00008		/* Carrier was physically present */
+
+#define CH_CLOCAL	0x00010		/* CLOCAL set in cflags */
+#define CH_BAUD0	0x00020		/* Baud rate zero hangup */
+
+#define CH_FAST_READ	0x00040		/* Fast reads are enabled */
+#define CH_FAST_WRITE	0x00080		/* Fast writes are enabled */
+
+#define CH_PRON		0x00100		/* Printer on string active */
+#define CH_RX_FLUSH	0x00200		/* Flushing receive data */
+#define CH_LOW		0x00400		/* Thread waiting for LOW water */
+#define CH_EMPTY	0x00800		/* Thread waiting for EMPTY */
+#define CH_DRAIN	0x01000		/* Close is waiting to drain */
+#define CH_INPUT	0x02000		/* Thread waiting for INPUT */
+#define CH_RXSTOP	0x04000		/* Stop output to ldisc */
+#define CH_PARAM	0x08000		/* A parameter was updated */
+#define CH_WAITING_SYNC 0x10000		/* A pending sync was assigned
+					 * to this port.
+					 */
+#define CH_PORT_GONE	0x20000		/* Port has disappeared */
+#define CH_TX_BREAK	0x40000		/* TX Break to be sent,
+					 * but has not yet.
+					 */
+
+/************************************************************************
+ * Types of Open Requests for ch_otype.
+ ************************************************************************/
+
+#define OTYPE_IMMEDIATE	  0		/* Immediate Open */
+#define OTYPE_PERSISTENT  1		/* Persistent Open */
+#define OTYPE_INCOMING	  2		/* Incoming Open */
+
+
+/************************************************************************
+ * Request/Response flags.
+ ************************************************************************/
+
+#define RR_SEQUENCE	0x0001		/* Get server RLAST, TIN */
+#define RR_STATUS	0x0002		/* Get server MINT, EINT */
+#define RR_BUFFER	0x0004		/* Get server RSIZE, TSIZE */
+#define RR_CAPABILITY	0x0008		/* Get server port capabilities */
+
+#define RR_TX_FLUSH	0x0040		/* Flush output buffers */
+#define RR_RX_FLUSH	0x0080		/* Flush input buffers */
+
+#define RR_TX_STOP	0x0100		/* Pause output */
+#define RR_RX_STOP	0x0200		/* Pause input */
+#define RR_TX_START	0x0400		/* Start output */
+#define RR_RX_START	0x0800		/* Start input */
+
+#define RR_TX_BREAK	0x1000		/* Send BREAK */
+#define RR_TX_ICHAR	0x2000		/* Send character immediate */
+
+
+/************************************************************************
+ * Channel information structure.   struct ch_struct
+ ************************************************************************/
+
+struct ch_struct {
+	struct digi_struct ch_digi;		/* Digi variables */
+	int	ch_edelay;		/* Digi edelay */
+
+	struct tty_port port;
+	struct un_struct ch_tun;	/* Terminal unit info */
+	struct un_struct ch_pun;	/* Printer unit info */
+
+	struct nd_struct *ch_nd;	/* Node pointer */
+	u8  *ch_tbuf;		/* Local Transmit Buffer */
+	u8  *ch_rbuf;		/* Local Receive Buffer */
+	ulong	ch_cpstime;		/* Printer CPS time */
+	ulong	ch_waketime;		/* Printer wake time */
+
+	ulong	ch_flag;		/* CH_* flags */
+
+	enum dgrp_ch_state_t ch_state;		/* CS_* Protocol state */
+	ushort	ch_send;		/* Bit vector of RR_* requests */
+	ushort	ch_expect;		/* Bit vector of RR_* responses */
+	ushort	ch_wait_carrier;	/* Thread count waiting for carrier */
+	ushort	ch_wait_count[3];	/* Thread count waiting by otype */
+
+	ushort	ch_portnum;		/* Port number */
+	ushort	ch_open_count;		/* Successful open count */
+	ushort	ch_category;		/* Device category */
+	ushort	ch_open_error;		/* Last open error number */
+	ushort	ch_break_time;		/* Pending break request time */
+	ushort	ch_cpsrem;		/* Printer CPS remainder */
+	ushort	ch_ocook;		/* Realport fastcook oflags */
+	ushort	ch_inwait;		/* Thread count in CLIST input */
+
+	ushort	ch_tin;			/* Local transmit buffer in ptr */
+	ushort	ch_tout;		/* Local transmit buffer out ptr */
+	ushort	ch_s_tin;		/* Realport TIN */
+	ushort	ch_s_tpos;		/* Realport TPOS */
+	ushort	ch_s_tsize;		/* Realport TSIZE */
+	ushort	ch_s_treq;		/* Realport TREQ */
+	ushort	ch_s_elast;		/* Realport ELAST */
+
+	ushort	ch_rin;			/* Local receive buffer in ptr */
+	ushort	ch_rout;		/* Local receive buffer out ptr */
+	ushort	ch_s_rin;		/* Realport RIN */
+	/* David Fries 7-13-2001, ch_s_rin should be renamed ch_s_rout because
+	 * the variable we want to represent is the PortServer's ROUT, which is
+	 * the sequence number for the next byte the PortServer will send us.
+	 * RIN is the sequence number for the next byte the PortServer will
+	 * receive from the uart.  The port server will send data as long as
+	 * ROUT is less than RWIN.  What would happen is the port is opened, it
+	 * receives data, it gives the value of RIN, we set the RWIN to
+	 * RIN+RBUF_MAX-1, it sends us RWIN-ROUT bytes which overflows.	 ROUT
+	 * is set to zero when the port is opened, so we start at zero and
+	 * count up as data is received.
+	 */
+	ushort	ch_s_rwin;		/* Realport RWIN */
+	ushort	ch_s_rsize;		/* Realport RSIZE */
+
+	ushort	ch_tmax;		/* Local TMAX */
+	ushort	ch_ttime;		/* Local TTIME */
+	ushort	ch_rmax;		/* Local RMAX */
+	ushort	ch_rtime;		/* Local RTIME */
+	ushort	ch_rlow;		/* Local RLOW */
+	ushort	ch_rhigh;		/* Local RHIGH */
+
+	ushort	ch_s_tmax;		/* Realport TMAX */
+	ushort	ch_s_ttime;		/* Realport TTIME */
+	ushort	ch_s_rmax;		/* Realport RMAX */
+	ushort	ch_s_rtime;		/* Realport RTIME */
+	ushort	ch_s_rlow;		/* Realport RLOW */
+	ushort	ch_s_rhigh;		/* Realport RHIGH */
+
+	ushort	ch_brate;		/* Local baud rate */
+	ushort	ch_cflag;		/* Local tty cflags */
+	ushort	ch_iflag;		/* Local tty iflags */
+	ushort	ch_oflag;		/* Local tty oflags */
+	ushort	ch_xflag;		/* Local tty xflags */
+
+	ushort	ch_s_brate;		/* Realport BRATE */
+	ushort	ch_s_cflag;		/* Realport CFLAG */
+	ushort	ch_s_iflag;		/* Realport IFLAG */
+	ushort	ch_s_oflag;		/* Realport OFLAG */
+	ushort	ch_s_xflag;		/* Realport XFLAG */
+
+	u8	ch_otype;		/* Open request type */
+	u8	ch_pscan_savechar;	/* Last character read by parity scan */
+	u8	ch_pscan_state;		/* PScan State based on last 2 chars */
+	u8	ch_otype_waiting;	/* Type of open pending in server */
+	u8	ch_flush_seq;		/* Receive flush end sequence */
+	u8	ch_s_mlast;		/* Realport MLAST */
+
+	u8	ch_mout;		/* Local MOUT */
+	u8	ch_mflow;		/* Local MFLOW */
+	u8	ch_mctrl;		/* Local MCTRL */
+	u8	ch_xon;			/* Local XON */
+	u8	ch_xoff;		/* Local XOFF */
+	u8	ch_lnext;		/* Local LNEXT */
+	u8	ch_xxon;		/* Local XXON */
+	u8	ch_xxoff;		/* Local XXOFF */
+
+	u8	ch_s_mout;		/* Realport MOUT */
+	u8	ch_s_mflow;		/* Realport MFLOW */
+	u8	ch_s_mctrl;		/* Realport MCTRL */
+	u8	ch_s_xon;		/* Realport XON */
+	u8	ch_s_xoff;		/* Realport XOFF */
+	u8	ch_s_lnext;		/* Realport LNEXT */
+	u8	ch_s_xxon;		/* Realport XXON */
+	u8	ch_s_xxoff;		/* Realport XXOFF */
+
+	wait_queue_head_t ch_flag_wait;	/* Wait queue for ch_flag changes */
+	wait_queue_head_t ch_sleep;	/* Wait queue for my_sleep() */
+
+	int	ch_custom_speed;	/* Realport custom speed */
+	int	ch_txcount;		/* Running TX count */
+	int	ch_rxcount;		/* Running RX count */
+};
+
+
+/************************************************************************
+ * Node State definitions.
+ ************************************************************************/
+
+enum dgrp_nd_state_t {
+	NS_CLOSED = 0,	   /* Network device is closed */
+	NS_IDLE = 1,	   /* Network connection inactive */
+	NS_SEND_QUERY =	2, /* Send server query */
+	NS_WAIT_QUERY =	3, /* Wait for query response */
+	NS_READY = 4,	   /* Network ready */
+	NS_SEND_ERROR =	5  /* Must send error hangup */
+};
+
+#define ND_STATE_STR(x) \
+	((x) == NS_CLOSED     ? "CLOSED"     : \
+	((x) == NS_IDLE	      ? "IDLE"	     : \
+	((x) == NS_SEND_QUERY ? "SEND_QUERY" : \
+	((x) == NS_WAIT_QUERY ? "WAIT_QUERY" : \
+	((x) == NS_READY      ? "READY"	     : \
+	((x) == NS_SEND_ERROR ? "SEND_ERROR" : "UNKNOWN"))))))
+
+/************************************************************************
+ * Node Flag definitions.
+ ************************************************************************/
+
+#define ND_SELECT	0x0001		/* Multiple net read selects */
+#define ND_DEB_WAIT	0x0002		/* Debug Device waiting */
+
+
+/************************************************************************
+ * Monitoring flag definitions.
+ ************************************************************************/
+
+#define MON_WAIT_DATA	0x0001		/* Waiting for buffer data */
+#define MON_WAIT_SPACE	0x0002		/* Waiting for buffer space */
+
+/************************************************************************
+ * DPA flag definitions.
+ ************************************************************************/
+
+#define DPA_WAIT_DATA	0x0001		/* Waiting for buffer data */
+#define DPA_WAIT_SPACE	0x0002		/* Waiting for buffer space */
+
+
+/************************************************************************
+ * Definitions taken from Realport Dump.
+ ************************************************************************/
+
+#define RPDUMP_MAGIC	"Digi-RealPort-1.0"
+
+#define RPDUMP_MESSAGE	0xE2		/* Descriptive message */
+#define RPDUMP_RESET	0xE7		/* Connection reset */
+#define RPDUMP_CLIENT	0xE8		/* Client data */
+#define RPDUMP_SERVER	0xE9		/* Server data */
+
+
+/************************************************************************
+ * Node request/response definitions.
+ ************************************************************************/
+
+#define NR_ECHO		0x0001		/* Server echo packet */
+#define NR_IDENT	0x0002		/* Server Product ID */
+#define NR_CAPABILITY	0x0004		/* Server Capabilties */
+#define NR_VPD		0x0008		/* Server VPD, if any */
+#define NR_PASSWORD	0x0010		/* Server Password */
+
+/************************************************************************
+ * Registration status of the node's Linux struct tty_driver structures.
+ ************************************************************************/
+#define SERIAL_TTDRV_REG   0x0001     /* nd_serial_ttdriver registered	*/
+#define CALLOUT_TTDRV_REG  0x0002     /* nd_callout_ttdriver registered */
+#define XPRINT_TTDRV_REG   0x0004     /* nd_xprint_ttdriver registered	*/
+
+
+/************************************************************************
+ * Node structure.  There exists one of these for each associated
+ * realport server.
+ ************************************************************************/
+
+struct nd_struct {
+	struct list_head	list;
+	long	      nd_major;		   /* Node's major number	    */
+	long	      nd_ID;		   /* Node's ID code		    */
+
+	char	      nd_serial_name[50];   /* "tty_dgrp_<id>_" + null	    */
+	char	      nd_callout_name[50];  /* "cu_dgrp_<id>_" + null	    */
+	char	      nd_xprint_name[50];   /* "pr_dgrp_<id>_" + null	    */
+
+	char	     password[16];	  /* Password for server, if needed */
+	int	     nd_tty_ref_cnt;	  /* Linux tty reference count	   */
+
+	struct proc_dir_entry *nd_net_de; /* Dir entry for /proc/dgrp/net  */
+	struct proc_dir_entry *nd_mon_de; /* Dir entry for /proc/dgrp/mon  */
+	struct proc_dir_entry *nd_ports_de; /* Dir entry for /proc/dgrp/ports*/
+	struct proc_dir_entry *nd_dpa_de; /* Dir entry for /proc/dgrp/dpa  */
+
+	spinlock_t nd_lock;		  /* General node lock		   */
+
+	struct semaphore nd_net_semaphore; /* Net read/write lock	    */
+	struct semaphore nd_mon_semaphore; /* Monitor buffer lock	    */
+	spinlock_t nd_dpa_lock;		/* DPA buffer lock	     */
+
+	enum dgrp_nd_state_t nd_state;	  /* NS_* network state */
+	int	      nd_chan_count;	   /* # active channels		    */
+	int	      nd_flag;		   /* Node flags		    */
+	int	      nd_send;		   /* Responses to send		    */
+	int	      nd_expect;	   /* Responses we expect	    */
+
+	u8	 *nd_iobuf;	       /* Network R/W Buffer		*/
+	wait_queue_head_t nd_tx_waitq;	  /* Network select wait queue	   */
+
+	u8	 *nd_inputbuf;	       /* Input Buffer			*/
+	u8	 *nd_inputflagbuf;     /* Input Flags Buffer		*/
+
+	int	      nd_tx_deposit;	   /* Accumulated transmit deposits */
+	int	      nd_tx_charge;	   /* Accumulated transmit charges  */
+	int	      nd_tx_credit;	   /* Current TX credit		    */
+	int	      nd_tx_ready;	   /* Ready to transmit		    */
+	int	      nd_tx_work;	   /* TX work waiting		    */
+	ulong	     nd_tx_time;	  /* Last transmit time		   */
+	ulong	     nd_poll_time;	  /* Next scheduled poll time	   */
+
+	int	      nd_delay;		   /* Current TX delay		    */
+	int	      nd_rate;		   /* Current TX rate		    */
+	struct link_struct nd_link;		/* Link speed params.		 */
+
+	int	      nd_seq_in;	   /* TX seq in ptr		    */
+	int	      nd_seq_out;	   /* TX seq out ptr		    */
+	int	      nd_unack;		   /* Unacknowledged byte count	    */
+	int	      nd_remain;	   /* Remaining receive bytes	    */
+	int	      nd_tx_module;	   /* Current TX module #	    */
+	int	      nd_rx_module;	   /* Current RX module #	    */
+	char	     *nd_error;		   /* Protocol error message	    */
+
+	int	      nd_write_count;	   /* drp_write() call count	    */
+	int	      nd_read_count;	   /* drp_read() count		    */
+	int	      nd_send_count;	   /* TCP message sent		    */
+	int	      nd_tx_byte;	   /* Transmit byte count	    */
+	int	      nd_rx_byte;	   /* Receive byte count	    */
+
+	ulong	     nd_mon_lbolt;	 /* Monitor start time		   */
+	int	      nd_mon_flag;	  /* Monitor flags		    */
+	int	      nd_mon_in;	  /* Monitor in pointer		    */
+	int	      nd_mon_out;	  /* Monitor out pointer	    */
+	wait_queue_head_t nd_mon_wqueue;  /* Monitor wait queue (on flags)  */
+	u8	 *nd_mon_buf;	      /* Monitor buffer			*/
+
+	ulong	     nd_dpa_lbolt;	/* DPA start time	      */
+	int	     nd_dpa_flag;	/* DPA flags		      */
+	int	     nd_dpa_in;		/* DPA in pointer	      */
+	int	     nd_dpa_out;	/* DPA out pointer	      */
+	wait_queue_head_t nd_dpa_wqueue; /* DPA wait queue (on flags)  */
+	u8	  *nd_dpa_buf;	/* DPA buffer		      */
+
+	uint	     nd_dpa_debug;
+	uint	     nd_dpa_port;
+
+	wait_queue_head_t nd_seq_wque[SEQ_MAX];	  /* TX thread wait queues */
+	u8	  nd_seq_wait[SEQ_MAX];	  /* Transmit thread wait count */
+
+	ushort	     nd_seq_size[SEQ_MAX];   /* Transmit seq packet size   */
+	ulong	     nd_seq_time[SEQ_MAX];   /* Transmit seq packet time   */
+
+	ushort	     nd_hw_ver;		  /* HW version returned from PS   */
+	ushort	     nd_sw_ver;		  /* SW version returned from PS   */
+	uint	     nd_hw_id;		  /* HW ID returned from PS	   */
+	u8	  nd_ps_desc[MAX_DESC_LEN+1];  /* Description from PS	*/
+	uint	     nd_vpd_len;		/* VPD len, if any */
+	u8	     nd_vpd[VPDSIZE];		/* VPD, if any */
+
+	ulong	     nd_ttdriver_flags;	  /* Registration status	    */
+	struct tty_driver *nd_serial_ttdriver;	/* Linux TTYDRIVER structure */
+	struct tty_driver *nd_callout_ttdriver; /* Linux TTYDRIVER structure */
+	struct tty_driver *nd_xprint_ttdriver;	/* Linux TTYDRIVER structure */
+
+	u8	     *nd_writebuf;		/* Used to cache data read
+						 * from user
+						 */
+	struct ch_struct nd_chan[CHAN_MAX];  /* Channel array		    */
+	struct device *nd_class_dev;	/* Hang our sysfs stuff off of here */
+};
+
+#endif /* __DRP_H */

+ 7 - 7
drivers/staging/ipack/devices/ipoctal.c

@@ -443,7 +443,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
 		spin_lock_init(&channel->lock);
 		channel->pointer_read = 0;
 		channel->pointer_write = 0;
-		tty_dev = tty_register_device(tty, i, NULL);
+		tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL);
 		if (IS_ERR(tty_dev)) {
 			dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n");
 			continue;
@@ -543,7 +543,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	struct ipoctal_channel *channel = tty->driver_data;
 	speed_t baud;
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 	/* Disable and reset everything before change the setup */
 	iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
@@ -564,7 +564,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	default:
 		mr1 |= MR1_CHRL_8_BITS;
 		/* By default, select CS8 */
-		tty->termios->c_cflag = (cflag & ~CSIZE) | CS8;
+		tty->termios.c_cflag = (cflag & ~CSIZE) | CS8;
 		break;
 	}
 
@@ -578,7 +578,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 		mr1 |= MR1_PARITY_OFF;
 
 	/* Mark or space parity is not supported */
-	tty->termios->c_cflag &= ~CMSPAR;
+	tty->termios.c_cflag &= ~CMSPAR;
 
 	/* Set stop bits */
 	if (cflag & CSTOPB)
@@ -611,10 +611,10 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	}
 
 	baud = tty_get_baud_rate(tty);
-	tty_termios_encode_baud_rate(tty->termios, baud, baud);
+	tty_termios_encode_baud_rate(&tty->termios, baud, baud);
 
 	/* Set baud rate */
-	switch (tty->termios->c_ospeed) {
+	switch (baud) {
 	case 75:
 		csr |= TX_CLK_75 | RX_CLK_75;
 		break;
@@ -655,7 +655,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	default:
 		csr |= TX_CLK_38400 | RX_CLK_38400;
 		/* In case of default, we establish 38400 bps */
-		tty_termios_encode_baud_rate(tty->termios, 38400, 38400);
+		tty_termios_encode_baud_rate(&tty->termios, 38400, 38400);
 		break;
 	}
 

+ 8 - 10
drivers/staging/serqt_usb2/serqt_usb2.c

@@ -313,10 +313,8 @@ static void qt_read_bulk_callback(struct urb *urb)
 	}
 
 	tty = tty_port_tty_get(&port->port);
-	if (!tty) {
-		dbg("%s - bad tty pointer - exiting", __func__);
+	if (!tty)
 		return;
-	}
 
 	data = urb->transfer_buffer;
 
@@ -362,7 +360,7 @@ static void qt_read_bulk_callback(struct urb *urb)
 		goto exit;
 	}
 
-	if (tty && RxCount) {
+	if (RxCount) {
 		flag_data = 0;
 		for (i = 0; i < RxCount; ++i) {
 			/* Look ahead code here */
@@ -426,7 +424,7 @@ static void qt_read_bulk_callback(struct urb *urb)
 		dbg("%s - failed resubmitting read urb, error %d",
 		    __func__, result);
 	else {
-		if (tty && RxCount) {
+		if (RxCount) {
 			tty_flip_buffer_push(tty);
 			tty_schedule_flip(tty);
 		}
@@ -895,8 +893,6 @@ static int qt_open(struct tty_struct *tty,
 	 * Put this here to make it responsive to stty and defaults set by
 	 * the tty layer
 	 */
-	/* FIXME: is this needed? */
-	/* qt_set_termios(tty, port, NULL); */
 
 	/*  Check to see if we've set up our endpoint info yet */
 	if (port0->open_ports == 1) {
@@ -1193,7 +1189,7 @@ static void qt_set_termios(struct tty_struct *tty,
 			   struct usb_serial_port *port,
 			   struct ktermios *old_termios)
 {
-	struct ktermios *termios = tty->termios;
+	struct ktermios *termios = &tty->termios;
 	unsigned char new_LCR = 0;
 	unsigned int cflag = termios->c_cflag;
 	unsigned int index;
@@ -1202,7 +1198,7 @@ static void qt_set_termios(struct tty_struct *tty,
 
 	index = tty->index - port->serial->minor;
 
-	switch (cflag) {
+	switch (cflag & CSIZE) {
 	case CS5:
 		new_LCR |= SERIAL_5_DATA;
 		break;
@@ -1213,6 +1209,8 @@ static void qt_set_termios(struct tty_struct *tty,
 		new_LCR |= SERIAL_7_DATA;
 		break;
 	default:
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
 	case CS8:
 		new_LCR |= SERIAL_8_DATA;
 		break;
@@ -1299,7 +1297,7 @@ static void qt_set_termios(struct tty_struct *tty,
 			dbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n");
 
 	}
-	tty->termios->c_cflag &= ~CMSPAR;
+	termios->c_cflag &= ~CMSPAR;
 	/* FIXME: Error cases should be returning the actual bits changed only */
 }
 

+ 1 - 2
drivers/staging/speakup/serialio.h

@@ -1,8 +1,7 @@
 #ifndef _SPEAKUP_SERIAL_H
 #define _SPEAKUP_SERIAL_H
 
-#include <linux/serial.h>	/* for rs_table, serial constants &
-				   serial_uart_config */
+#include <linux/serial.h>	/* for rs_table, serial constants */
 #include <linux/serial_reg.h>	/* for more serial constants */
 #ifndef __sparc__
 #include <asm/serial.h>

+ 4 - 5
drivers/tty/Kconfig

@@ -214,8 +214,8 @@ config CYCLADES
 	  If you haven't heard about it, it's safe to say N.
 
 config CYZ_INTR
-	bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && CYCLADES
+	bool "Cyclades-Z interrupt mode operation"
+	depends on CYCLADES
 	help
 	  The Cyclades-Z family of multiport cards allows 2 (two) driver op
 	  modes: polling and interrupt. In polling mode, the driver will check
@@ -285,7 +285,7 @@ config SYNCLINK_GT
 
 config NOZOMI
 	tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
-	depends on PCI && EXPERIMENTAL
+	depends on PCI
 	help
 	  If you have a HSDPA driver Broadband Wireless Data Card -
 	  Globe Trotter PCMCIA card, say Y here.
@@ -294,7 +294,7 @@ config NOZOMI
 	  will be called nozomi.
 
 config ISI
-	tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+	tristate "Multi-Tech multiport card support"
 	depends on SERIAL_NONSTANDARD && PCI
 	select FW_LOADER
 	help
@@ -317,7 +317,6 @@ config N_HDLC
 
 config N_GSM
 	tristate "GSM MUX line discipline support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
 	depends on NET
 	help
 	  This line discipline provides support for the GSM MUX protocol and

+ 23 - 22
drivers/tty/amiserial.c

@@ -420,7 +420,7 @@ static void check_modem_status(struct serial_state *info)
 				tty_hangup(port->tty);
 		}
 	}
-	if (port->flags & ASYNC_CTS_FLOW) {
+	if (tty_port_cts_enabled(port)) {
 		if (port->tty->hw_stopped) {
 			if (!(status & SER_CTS)) {
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
@@ -646,7 +646,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info)
 	custom.adkcon = AC_UARTBRK;
 	mb();
 
-	if (tty->termios->c_cflag & HUPCL)
+	if (tty->termios.c_cflag & HUPCL)
 		info->MCR &= ~(SER_DTR|SER_RTS);
 	rtsdtr_ctrl(info->MCR);
 
@@ -670,7 +670,7 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info,
 	int	bits;
 	unsigned long	flags;
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 	/* Byte size is always 8 bits plus parity bit if requested */
 
@@ -707,8 +707,8 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info,
 	/* If the quotient is zero refuse the change */
 	if (!quot && old_termios) {
 		/* FIXME: Will need updating for new tty in the end */
-		tty->termios->c_cflag &= ~CBAUD;
-		tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+		tty->termios.c_cflag &= ~CBAUD;
+		tty->termios.c_cflag |= (old_termios->c_cflag & CBAUD);
 		baud = tty_get_baud_rate(tty);
 		if (!baud)
 			baud = 9600;
@@ -984,7 +984,7 @@ static void rs_throttle(struct tty_struct * tty)
 	if (I_IXOFF(tty))
 		rs_send_xchar(tty, STOP_CHAR(tty));
 
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		info->MCR &= ~SER_RTS;
 
 	local_irq_save(flags);
@@ -1012,7 +1012,7 @@ static void rs_unthrottle(struct tty_struct * tty)
 		else
 			rs_send_xchar(tty, START_CHAR(tty));
 	}
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		info->MCR |= SER_RTS;
 	local_irq_save(flags);
 	rtsdtr_ctrl(info->MCR);
@@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
 	if (!retinfo)
 		return -EFAULT;
 	memset(&tmp, 0, sizeof(tmp));
-	tty_lock();
+	tty_lock(tty);
 	tmp.line = tty->index;
 	tmp.port = state->port;
 	tmp.flags = state->tport.flags;
@@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
 	tmp.close_delay = state->tport.close_delay;
 	tmp.closing_wait = state->tport.closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
-	tty_unlock();
+	tty_unlock(tty);
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
@@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
 
-	tty_lock();
+	tty_lock(tty);
 	change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
 		new_serial.custom_divisor != state->custom_divisor;
 	if (new_serial.irq || new_serial.port != state->port ||
 			new_serial.xmit_fifo_size != state->xmit_fifo_size) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 	}
   
@@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 		    (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
 		     (port->flags & ~ASYNC_USR_MASK))) {
-			tty_unlock();
+			tty_unlock(tty);
 			return -EPERM;
 		}
 		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
@@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 	}
 
 	if (new_serial.baud_base < 9600) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 	}
 
@@ -1116,7 +1116,7 @@ check_and_exit:
 		}
 	} else
 		retval = startup(tty, state);
-	tty_unlock();
+	tty_unlock(tty);
 	return retval;
 }
 
@@ -1330,7 +1330,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
 	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
-	unsigned int cflag = tty->termios->c_cflag;
+	unsigned int cflag = tty->termios.c_cflag;
 
 	change_speed(tty, info, old_termios);
 
@@ -1347,7 +1347,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	if (!(old_termios->c_cflag & CBAUD) &&
 	    (cflag & CBAUD)) {
 		info->MCR |= SER_DTR;
-		if (!(tty->termios->c_cflag & CRTSCTS) || 
+		if (!(tty->termios.c_cflag & CRTSCTS) || 
 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 			info->MCR |= SER_RTS;
 		}
@@ -1358,7 +1358,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		rs_start(tty);
 	}
@@ -1371,7 +1371,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	 * or not.  Hence, this may change.....
 	 */
 	if (!(old_termios->c_cflag & CLOCAL) &&
-	    (tty->termios->c_cflag & CLOCAL))
+	    (tty->termios.c_cflag & CLOCAL))
 		wake_up_interruptible(&info->open_wait);
 #endif
 }
@@ -1710,10 +1710,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev)
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(serial_driver, &serial_ops);
 
-	error = tty_register_driver(serial_driver);
-	if (error)
-		goto fail_put_tty_driver;
-
 	state = rs_table;
 	state->port = (int)&custom.serdatr; /* Just to give it a value */
 	state->custom_divisor = 0;
@@ -1724,6 +1720,11 @@ static int __init amiga_serial_probe(struct platform_device *pdev)
 	state->icount.overrun = state->icount.brk = 0;
 	tty_port_init(&state->tport);
 	state->tport.ops = &amiga_port_ops;
+	tty_port_link_device(&state->tport, serial_driver, 0);
+
+	error = tty_register_driver(serial_driver);
+	if (error)
+		goto fail_put_tty_driver;
 
 	printk(KERN_INFO "ttyS0 is the amiga builtin serial port\n");
 

+ 1 - 0
drivers/tty/bfin_jtag_comm.c

@@ -263,6 +263,7 @@ static int __init bfin_jc_init(void)
 	bfin_jc_driver->subtype      = SERIAL_TYPE_NORMAL;
 	bfin_jc_driver->init_termios = tty_std_termios;
 	tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
+	tty_port_link_device(&port, bfin_jc_driver, 0);
 
 	ret = tty_register_driver(bfin_jc_driver);
 	if (ret)

+ 52 - 50
drivers/tty/cyclades.c

@@ -727,7 +727,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
 		else
 			tty_hangup(tty);
 	}
-	if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) {
+	if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) {
 		if (tty->hw_stopped) {
 			if (mdm_status & CyCTS) {
 				/* cy_start isn't used
@@ -1459,7 +1459,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty)
 			info->port.xmit_buf = NULL;
 			free_page((unsigned long)temp);
 		}
-		if (tty->termios->c_cflag & HUPCL)
+		if (tty->termios.c_cflag & HUPCL)
 			cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR);
 
 		cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR);
@@ -1488,7 +1488,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty)
 			free_page((unsigned long)temp);
 		}
 
-		if (tty->termios->c_cflag & HUPCL)
+		if (tty->termios.c_cflag & HUPCL)
 			tty_port_lower_dtr_rts(&info->port);
 
 		set_bit(TTY_IO_ERROR, &tty->flags);
@@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
 	 * If the port is the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->port.close_wait,
+		wait_event_interruptible_tty(tty, info->port.close_wait,
 				!(info->port.flags & ASYNC_CLOSING));
 		return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
 	}
@@ -1999,14 +1999,11 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty)
 	int baud, baud_rate = 0;
 	int i;
 
-	if (!tty->termios) /* XXX can this happen at all? */
-		return;
-
 	if (info->line == -1)
 		return;
 
-	cflag = tty->termios->c_cflag;
-	iflag = tty->termios->c_iflag;
+	cflag = tty->termios.c_cflag;
+	iflag = tty->termios.c_iflag;
 
 	/*
 	 * Set up the tty->alt_speed kludge
@@ -2825,7 +2822,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	cy_set_line_char(info, tty);
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		cy_start(tty);
 	}
@@ -2837,7 +2834,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	 * or not.  Hence, this may change.....
 	 */
 	if (!(old_termios->c_cflag & CLOCAL) &&
-	    (tty->termios->c_cflag & CLOCAL))
+	    (tty->termios.c_cflag & CLOCAL))
 		wake_up_interruptible(&info->port.open_wait);
 #endif
 }				/* cy_set_termios */
@@ -2899,7 +2896,7 @@ static void cy_throttle(struct tty_struct *tty)
 			info->throttle = 1;
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		if (!cy_is_Z(card)) {
 			spin_lock_irqsave(&card->card_lock, flags);
 			cyy_change_rts_dtr(info, 0, TIOCM_RTS);
@@ -2938,7 +2935,7 @@ static void cy_unthrottle(struct tty_struct *tty)
 			cy_send_xchar(tty, START_CHAR(tty));
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		card = info->card;
 		if (!cy_is_Z(card)) {
 			spin_lock_irqsave(&card->card_lock, flags);
@@ -3289,9 +3286,10 @@ static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr,
 static int __init cy_detect_isa(void)
 {
 #ifdef CONFIG_ISA
+	struct cyclades_card *card;
 	unsigned short cy_isa_irq, nboard;
 	void __iomem *cy_isa_address;
-	unsigned short i, j, cy_isa_nchan;
+	unsigned short i, j, k, cy_isa_nchan;
 	int isparam = 0;
 
 	nboard = 0;
@@ -3349,7 +3347,8 @@ static int __init cy_detect_isa(void)
 		}
 		/* fill the next cy_card structure available */
 		for (j = 0; j < NR_CARDS; j++) {
-			if (cy_card[j].base_addr == NULL)
+			card = &cy_card[j];
+			if (card->base_addr == NULL)
 				break;
 		}
 		if (j == NR_CARDS) {	/* no more cy_cards available */
@@ -3363,7 +3362,7 @@ static int __init cy_detect_isa(void)
 
 		/* allocate IRQ */
 		if (request_irq(cy_isa_irq, cyy_interrupt,
-				0, "Cyclom-Y", &cy_card[j])) {
+				0, "Cyclom-Y", card)) {
 			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
 				"could not allocate IRQ#%d.\n",
 				(unsigned long)cy_isa_address, cy_isa_irq);
@@ -3372,16 +3371,16 @@ static int __init cy_detect_isa(void)
 		}
 
 		/* set cy_card */
-		cy_card[j].base_addr = cy_isa_address;
-		cy_card[j].ctl_addr.p9050 = NULL;
-		cy_card[j].irq = (int)cy_isa_irq;
-		cy_card[j].bus_index = 0;
-		cy_card[j].first_line = cy_next_channel;
-		cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
-		cy_card[j].nports = cy_isa_nchan;
-		if (cy_init_card(&cy_card[j])) {
-			cy_card[j].base_addr = NULL;
-			free_irq(cy_isa_irq, &cy_card[j]);
+		card->base_addr = cy_isa_address;
+		card->ctl_addr.p9050 = NULL;
+		card->irq = (int)cy_isa_irq;
+		card->bus_index = 0;
+		card->first_line = cy_next_channel;
+		card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
+		card->nports = cy_isa_nchan;
+		if (cy_init_card(card)) {
+			card->base_addr = NULL;
+			free_irq(cy_isa_irq, card);
 			iounmap(cy_isa_address);
 			continue;
 		}
@@ -3393,9 +3392,10 @@ static int __init cy_detect_isa(void)
 			(unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
 			cy_isa_irq, cy_isa_nchan, cy_next_channel);
 
-		for (j = cy_next_channel;
-				j < cy_next_channel + cy_isa_nchan; j++)
-			tty_register_device(cy_serial_driver, j, NULL);
+		for (k = 0, j = cy_next_channel;
+				j < cy_next_channel + cy_isa_nchan; j++, k++)
+			tty_port_register_device(&card->ports[k].port,
+					cy_serial_driver, j, NULL);
 		cy_next_channel += cy_isa_nchan;
 	}
 	return nboard;
@@ -3695,10 +3695,11 @@ err:
 static int __devinit cy_pci_probe(struct pci_dev *pdev,
 		const struct pci_device_id *ent)
 {
+	struct cyclades_card *card;
 	void __iomem *addr0 = NULL, *addr2 = NULL;
 	char *card_name = NULL;
 	u32 uninitialized_var(mailbox);
-	unsigned int device_id, nchan = 0, card_no, i;
+	unsigned int device_id, nchan = 0, card_no, i, j;
 	unsigned char plx_ver;
 	int retval, irq;
 
@@ -3829,7 +3830,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 	}
 	/* fill the next cy_card structure available */
 	for (card_no = 0; card_no < NR_CARDS; card_no++) {
-		if (cy_card[card_no].base_addr == NULL)
+		card = &cy_card[card_no];
+		if (card->base_addr == NULL)
 			break;
 	}
 	if (card_no == NR_CARDS) {	/* no more cy_cards available */
@@ -3843,27 +3845,26 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
 		/* allocate IRQ */
 		retval = request_irq(irq, cyy_interrupt,
-				IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]);
+				IRQF_SHARED, "Cyclom-Y", card);
 		if (retval) {
 			dev_err(&pdev->dev, "could not allocate IRQ\n");
 			goto err_unmap;
 		}
-		cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
+		card->num_chips = nchan / CyPORTS_PER_CHIP;
 	} else {
 		struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
 		struct ZFW_CTRL __iomem *zfw_ctrl;
 
 		zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
 
-		cy_card[card_no].hw_ver = mailbox;
-		cy_card[card_no].num_chips = (unsigned int)-1;
-		cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl;
+		card->hw_ver = mailbox;
+		card->num_chips = (unsigned int)-1;
+		card->board_ctrl = &zfw_ctrl->board_ctrl;
 #ifdef CONFIG_CYZ_INTR
 		/* allocate IRQ only if board has an IRQ */
 		if (irq != 0 && irq != 255) {
 			retval = request_irq(irq, cyz_interrupt,
-					IRQF_SHARED, "Cyclades-Z",
-					&cy_card[card_no]);
+					IRQF_SHARED, "Cyclades-Z", card);
 			if (retval) {
 				dev_err(&pdev->dev, "could not allocate IRQ\n");
 				goto err_unmap;
@@ -3873,17 +3874,17 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 	}
 
 	/* set cy_card */
-	cy_card[card_no].base_addr = addr2;
-	cy_card[card_no].ctl_addr.p9050 = addr0;
-	cy_card[card_no].irq = irq;
-	cy_card[card_no].bus_index = 1;
-	cy_card[card_no].first_line = cy_next_channel;
-	cy_card[card_no].nports = nchan;
-	retval = cy_init_card(&cy_card[card_no]);
+	card->base_addr = addr2;
+	card->ctl_addr.p9050 = addr0;
+	card->irq = irq;
+	card->bus_index = 1;
+	card->first_line = cy_next_channel;
+	card->nports = nchan;
+	retval = cy_init_card(card);
 	if (retval)
 		goto err_null;
 
-	pci_set_drvdata(pdev, &cy_card[card_no]);
+	pci_set_drvdata(pdev, card);
 
 	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
 			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
@@ -3909,14 +3910,15 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 
 	dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
 		"port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
-	for (i = cy_next_channel; i < cy_next_channel + nchan; i++)
-		tty_register_device(cy_serial_driver, i, &pdev->dev);
+	for (j = 0, i = cy_next_channel; i < cy_next_channel + nchan; i++, j++)
+		tty_port_register_device(&card->ports[j].port,
+				cy_serial_driver, i, &pdev->dev);
 	cy_next_channel += nchan;
 
 	return 0;
 err_null:
-	cy_card[card_no].base_addr = NULL;
-	free_irq(irq, &cy_card[card_no]);
+	card->base_addr = NULL;
+	free_irq(irq, card);
 err_unmap:
 	iounmap(addr0);
 	if (addr2)

+ 5 - 4
drivers/tty/ehv_bytechan.c

@@ -738,16 +738,17 @@ static int __devinit ehv_bc_tty_probe(struct platform_device *pdev)
 		goto error;
 	}
 
-	bc->dev = tty_register_device(ehv_bc_driver, i, &pdev->dev);
+	tty_port_init(&bc->port);
+	bc->port.ops = &ehv_bc_tty_port_ops;
+
+	bc->dev = tty_port_register_device(&bc->port, ehv_bc_driver, i,
+			&pdev->dev);
 	if (IS_ERR(bc->dev)) {
 		ret = PTR_ERR(bc->dev);
 		dev_err(&pdev->dev, "could not register tty (ret=%i)\n", ret);
 		goto error;
 	}
 
-	tty_port_init(&bc->port);
-	bc->port.ops = &ehv_bc_tty_port_ops;
-
 	dev_set_drvdata(&pdev->dev, bc);
 
 	dev_info(&pdev->dev, "registered /dev/%s%u for byte channel %u\n",

+ 1 - 1
drivers/tty/hvc/Kconfig

@@ -76,7 +76,7 @@ config HVC_XEN_FRONTEND
 
 config HVC_UDBG
        bool "udbg based fake hypervisor console"
-       depends on PPC && EXPERIMENTAL
+       depends on PPC
        select HVC_DRIVER
        default n
        help

+ 26 - 7
drivers/tty/hvc/hvc_console.c

@@ -299,20 +299,33 @@ static void hvc_unthrottle(struct tty_struct *tty)
 	hvc_kick();
 }
 
+static int hvc_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct hvc_struct *hp;
+	int rc;
+
+	/* Auto increments kref reference if found. */
+	if (!(hp = hvc_get_by_index(tty->index)))
+		return -ENODEV;
+
+	tty->driver_data = hp;
+
+	rc = tty_port_install(&hp->port, driver, tty);
+	if (rc)
+		tty_port_put(&hp->port);
+	return rc;
+}
+
 /*
  * The TTY interface won't be used until after the vio layer has exposed the vty
  * adapter to the kernel.
  */
 static int hvc_open(struct tty_struct *tty, struct file * filp)
 {
-	struct hvc_struct *hp;
+	struct hvc_struct *hp = tty->driver_data;
 	unsigned long flags;
 	int rc = 0;
 
-	/* Auto increments kref reference if found. */
-	if (!(hp = hvc_get_by_index(tty->index)))
-		return -ENODEV;
-
 	spin_lock_irqsave(&hp->port.lock, flags);
 	/* Check and then increment for fast path open. */
 	if (hp->port.count++ > 0) {
@@ -322,7 +335,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
 	} /* else count == 0 */
 	spin_unlock_irqrestore(&hp->port.lock, flags);
 
-	tty->driver_data = hp;
 	tty_port_tty_set(&hp->port, tty);
 
 	if (hp->ops->notifier_add)
@@ -389,6 +401,11 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
 				hp->vtermno, hp->port.count);
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 	}
+}
+
+static void hvc_cleanup(struct tty_struct *tty)
+{
+	struct hvc_struct *hp = tty->driver_data;
 
 	tty_port_put(&hp->port);
 }
@@ -541,7 +558,7 @@ static int hvc_write_room(struct tty_struct *tty)
 	struct hvc_struct *hp = tty->driver_data;
 
 	if (!hp)
-		return -1;
+		return 0;
 
 	return hp->outbuf_size - hp->n_outbuf;
 }
@@ -792,8 +809,10 @@ static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch)
 #endif
 
 static const struct tty_operations hvc_ops = {
+	.install = hvc_install,
 	.open = hvc_open,
 	.close = hvc_close,
+	.cleanup = hvc_cleanup,
 	.write = hvc_write,
 	.hangup = hvc_hangup,
 	.unthrottle = hvc_unthrottle,

+ 49 - 33
drivers/tty/hvc/hvcs.c

@@ -1102,27 +1102,20 @@ static struct hvcs_struct *hvcs_get_by_index(int index)
 	return NULL;
 }
 
-/*
- * This is invoked via the tty_open interface when a user app connects to the
- * /dev node.
- */
-static int hvcs_open(struct tty_struct *tty, struct file *filp)
+static int hvcs_install(struct tty_driver *driver, struct tty_struct *tty)
 {
 	struct hvcs_struct *hvcsd;
-	int rc, retval = 0;
-	unsigned long flags;
-	unsigned int irq;
 	struct vio_dev *vdev;
-	unsigned long unit_address;
-
-	if (tty->driver_data)
-		goto fast_open;
+	unsigned long unit_address, flags;
+	unsigned int irq;
+	int retval;
 
 	/*
 	 * Is there a vty-server that shares the same index?
 	 * This function increments the kref index.
 	 */
-	if (!(hvcsd = hvcs_get_by_index(tty->index))) {
+	hvcsd = hvcs_get_by_index(tty->index);
+	if (!hvcsd) {
 		printk(KERN_WARNING "HVCS: open failed, no device associated"
 				" with tty->index %d.\n", tty->index);
 		return -ENODEV;
@@ -1130,11 +1123,16 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
 
-	if (hvcsd->connected == 0)
-		if ((retval = hvcs_partner_connect(hvcsd)))
-			goto error_release;
+	if (hvcsd->connected == 0) {
+		retval = hvcs_partner_connect(hvcsd);
+		if (retval) {
+			spin_unlock_irqrestore(&hvcsd->lock, flags);
+			printk(KERN_WARNING "HVCS: partner connect failed.\n");
+			goto err_put;
+		}
+	}
 
-	hvcsd->port.count = 1;
+	hvcsd->port.count = 0;
 	hvcsd->port.tty = tty;
 	tty->driver_data = hvcsd;
 
@@ -1155,37 +1153,48 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
 	 * This must be done outside of the spinlock because it requests irqs
 	 * and will grab the spinlock and free the connection if it fails.
 	 */
-	if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) {
-		tty_port_put(&hvcsd->port);
+	retval = hvcs_enable_device(hvcsd, unit_address, irq, vdev);
+	if (retval) {
 		printk(KERN_WARNING "HVCS: enable device failed.\n");
-		return rc;
+		goto err_put;
 	}
 
-	goto open_success;
+	retval = tty_port_install(&hvcsd->port, driver, tty);
+	if (retval)
+		goto err_irq;
 
-fast_open:
-	hvcsd = tty->driver_data;
+	return 0;
+err_irq:
+	spin_lock_irqsave(&hvcsd->lock, flags);
+	vio_disable_interrupts(hvcsd->vdev);
+	spin_unlock_irqrestore(&hvcsd->lock, flags);
+	free_irq(irq, hvcsd);
+err_put:
+	tty_port_put(&hvcsd->port);
+
+	return retval;
+}
+
+/*
+ * This is invoked via the tty_open interface when a user app connects to the
+ * /dev node.
+ */
+static int hvcs_open(struct tty_struct *tty, struct file *filp)
+{
+	struct hvcs_struct *hvcsd = tty->driver_data;
+	unsigned long flags;
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
-	tty_port_get(&hvcsd->port);
 	hvcsd->port.count++;
 	hvcsd->todo_mask |= HVCS_SCHED_READ;
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
 
-open_success:
 	hvcs_kick();
 
 	printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n",
 		hvcsd->vdev->unit_address );
 
 	return 0;
-
-error_release:
-	spin_unlock_irqrestore(&hvcsd->lock, flags);
-	tty_port_put(&hvcsd->port);
-
-	printk(KERN_WARNING "HVCS: partner connect failed.\n");
-	return retval;
 }
 
 static void hvcs_close(struct tty_struct *tty, struct file *filp)
@@ -1236,7 +1245,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
 		tty->driver_data = NULL;
 
 		free_irq(irq, hvcsd);
-		tty_port_put(&hvcsd->port);
 		return;
 	} else if (hvcsd->port.count < 0) {
 		printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
@@ -1245,6 +1253,12 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
 	}
 
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
+}
+
+static void hvcs_cleanup(struct tty_struct * tty)
+{
+	struct hvcs_struct *hvcsd = tty->driver_data;
+
 	tty_port_put(&hvcsd->port);
 }
 
@@ -1431,8 +1445,10 @@ static int hvcs_chars_in_buffer(struct tty_struct *tty)
 }
 
 static const struct tty_operations hvcs_ops = {
+	.install = hvcs_install,
 	.open = hvcs_open,
 	.close = hvcs_close,
+	.cleanup = hvcs_cleanup,
 	.hangup = hvcs_hangup,
 	.write = hvcs_write,
 	.write_room = hvcs_write_room,

+ 2 - 0
drivers/tty/hvc/hvsi.c

@@ -1080,6 +1080,8 @@ static int __init hvsi_init(void)
 		struct hvsi_struct *hp = &hvsi_ports[i];
 		int ret = 1;
 
+		tty_port_link_device(&hp->port, hvsi_driver, i);
+
 		ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
 		if (ret)
 			printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n",

+ 1 - 1
drivers/tty/hvc/hvsi_lib.c

@@ -400,7 +400,7 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp)
 		spin_unlock_irqrestore(&hp->lock, flags);
 
 		/* Clear our own DTR */
-		if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
+		if (!pv->tty || (pv->tty->termios.c_cflag & HUPCL))
 			hvsilib_write_mctrl(pv, 0);
 
 		/* Tear down the connection */

+ 6 - 1
drivers/tty/ipwireless/network.c

@@ -274,7 +274,12 @@ static void do_go_online(struct work_struct *work_go_online)
 		network->xaccm[0] = ~0U;
 		network->xaccm[3] = 0x60000000U;
 		network->raccm = ~0U;
-		ppp_register_channel(channel);
+		if (ppp_register_channel(channel) < 0) {
+			printk(KERN_ERR IPWIRELESS_PCCARD_NAME
+					": unable to register PPP channel\n");
+			kfree(channel);
+			return;
+		}
 		spin_lock_irqsave(&network->lock, flags);
 		network->ppp_channel = channel;
 	}

+ 1 - 1
drivers/tty/ipwireless/tty.c

@@ -476,7 +476,7 @@ static int add_tty(int j,
 	mutex_init(&ttys[j]->ipw_tty_mutex);
 	tty_port_init(&ttys[j]->port);
 
-	tty_register_device(ipw_tty_driver, j, NULL);
+	tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
 	ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
 
 	if (secondary_channel_idx != -1)

+ 7 - 6
drivers/tty/isicom.c

@@ -600,7 +600,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
 					port->status &= ~ISI_DCD;
 			}
 
-			if (port->port.flags & ASYNC_CTS_FLOW) {
+			if (tty_port_cts_enabled(&port->port)) {
 				if (tty->hw_stopped) {
 					if (header & ISI_CTS) {
 						port->port.tty->hw_stopped = 0;
@@ -702,7 +702,7 @@ static void isicom_config_port(struct tty_struct *tty)
 
 		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
 		if (baud < 1 || baud > 4)
-			tty->termios->c_cflag &= ~CBAUDEX;
+			tty->termios.c_cflag &= ~CBAUDEX;
 		else
 			baud += 15;
 	}
@@ -1196,8 +1196,8 @@ static void isicom_set_termios(struct tty_struct *tty,
 	if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
 		return;
 
-	if (tty->termios->c_cflag == old_termios->c_cflag &&
-			tty->termios->c_iflag == old_termios->c_iflag)
+	if (tty->termios.c_cflag == old_termios->c_cflag &&
+			tty->termios.c_iflag == old_termios->c_iflag)
 		return;
 
 	spin_lock_irqsave(&port->card->card_lock, flags);
@@ -1205,7 +1205,7 @@ static void isicom_set_termios(struct tty_struct *tty,
 	spin_unlock_irqrestore(&port->card->card_lock, flags);
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		isicom_start(tty);
 	}
@@ -1611,7 +1611,8 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
 		goto errunri;
 
 	for (index = 0; index < board->port_count; index++)
-		tty_register_device(isicom_normal, board->index * 16 + index,
+		tty_port_register_device(&board->ports[index].port,
+				isicom_normal, board->index * 16 + index,
 				&pdev->dev);
 
 	return 0;

+ 28 - 11
drivers/tty/moxa.c

@@ -169,6 +169,7 @@ static DEFINE_SPINLOCK(moxa_lock);
 static unsigned long baseaddr[MAX_BOARDS];
 static unsigned int type[MAX_BOARDS];
 static unsigned int numports[MAX_BOARDS];
+static struct tty_port moxa_service_port;
 
 MODULE_AUTHOR("William Chen");
 MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
@@ -367,10 +368,10 @@ static int moxa_ioctl(struct tty_struct *tty,
 					tmp.dcd = 1;
 
 				ttyp = tty_port_tty_get(&p->port);
-				if (!ttyp || !ttyp->termios)
+				if (!ttyp)
 					tmp.cflag = p->cflag;
 				else
-					tmp.cflag = ttyp->termios->c_cflag;
+					tmp.cflag = ttyp->termios.c_cflag;
 				tty_kref_put(ttyp);
 copy:
 				if (copy_to_user(argm, &tmp, sizeof(tmp)))
@@ -834,7 +835,7 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
 	const struct firmware *fw;
 	const char *file;
 	struct moxa_port *p;
-	unsigned int i;
+	unsigned int i, first_idx;
 	int ret;
 
 	brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
@@ -887,6 +888,11 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
 		mod_timer(&moxaTimer, jiffies + HZ / 50);
 	spin_unlock_bh(&moxa_lock);
 
+	first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
+	for (i = 0; i < brd->numPorts; i++)
+		tty_port_register_device(&brd->ports[i].port, moxaDriver,
+				first_idx + i, dev);
+
 	return 0;
 err_free:
 	kfree(brd->ports);
@@ -896,7 +902,7 @@ err:
 
 static void moxa_board_deinit(struct moxa_board_conf *brd)
 {
-	unsigned int a, opened;
+	unsigned int a, opened, first_idx;
 
 	mutex_lock(&moxa_openlock);
 	spin_lock_bh(&moxa_lock);
@@ -925,6 +931,10 @@ static void moxa_board_deinit(struct moxa_board_conf *brd)
 		mutex_lock(&moxa_openlock);
 	}
 
+	first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
+	for (a = 0; a < brd->numPorts; a++)
+		tty_unregister_device(moxaDriver, first_idx + a);
+
 	iounmap(brd->basemem);
 	brd->basemem = NULL;
 	kfree(brd->ports);
@@ -967,6 +977,7 @@ static int __devinit moxa_pci_probe(struct pci_dev *pdev,
 	board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000);
 	if (board->basemem == NULL) {
 		dev_err(&pdev->dev, "can't remap io space 2\n");
+		retval = -ENOMEM;
 		goto err_reg;
 	}
 
@@ -1031,9 +1042,14 @@ static int __init moxa_init(void)
 
 	printk(KERN_INFO "MOXA Intellio family driver version %s\n",
 			MOXA_VERSION);
-	moxaDriver = alloc_tty_driver(MAX_PORTS + 1);
-	if (!moxaDriver)
-		return -ENOMEM;
+
+	tty_port_init(&moxa_service_port);
+
+	moxaDriver = tty_alloc_driver(MAX_PORTS + 1,
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_DEV);
+	if (IS_ERR(moxaDriver))
+		return PTR_ERR(moxaDriver);
 
 	moxaDriver->name = "ttyMX";
 	moxaDriver->major = ttymajor;
@@ -1044,8 +1060,9 @@ static int __init moxa_init(void)
 	moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
 	moxaDriver->init_termios.c_ispeed = 9600;
 	moxaDriver->init_termios.c_ospeed = 9600;
-	moxaDriver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(moxaDriver, &moxa_ops);
+	/* Having one more port only for ioctls is ugly */
+	tty_port_link_device(&moxa_service_port, moxaDriver, MAX_PORTS);
 
 	if (tty_register_driver(moxaDriver)) {
 		printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
@@ -1178,7 +1195,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
 	mutex_lock(&ch->port.mutex);
 	if (!(ch->port.flags & ASYNC_INITIALIZED)) {
 		ch->statusflags = 0;
-		moxa_set_tty_param(tty, tty->termios);
+		moxa_set_tty_param(tty, &tty->termios);
 		MoxaPortLineCtrl(ch, 1, 1);
 		MoxaPortEnable(ch);
 		MoxaSetFifo(ch, ch->type == PORT_16550A);
@@ -1193,7 +1210,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
 static void moxa_close(struct tty_struct *tty, struct file *filp)
 {
 	struct moxa_port *ch = tty->driver_data;
-	ch->cflag = tty->termios->c_cflag;
+	ch->cflag = tty->termios.c_cflag;
 	tty_port_close(&ch->port, tty, filp);
 }
 
@@ -1464,7 +1481,7 @@ static void moxa_poll(unsigned long ignored)
 
 static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios)
 {
-	register struct ktermios *ts = tty->termios;
+	register struct ktermios *ts = &tty->termios;
 	struct moxa_port *ch = tty->driver_data;
 	int rts, cts, txflow, rxflow, xany, baud;
 

+ 46 - 17
drivers/tty/mxser.c

@@ -643,7 +643,7 @@ static int mxser_change_speed(struct tty_struct *tty,
 	int ret = 0;
 	unsigned char status;
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 	if (!info->ioaddr)
 		return ret;
 
@@ -830,7 +830,7 @@ static void mxser_check_modem_status(struct tty_struct *tty,
 			wake_up_interruptible(&port->port.open_wait);
 	}
 
-	if (port->port.flags & ASYNC_CTS_FLOW) {
+	if (tty_port_cts_enabled(&port->port)) {
 		if (tty->hw_stopped) {
 			if (status & UART_MSR_CTS) {
 				tty->hw_stopped = 0;
@@ -1520,10 +1520,10 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
 				
 				tty = tty_port_tty_get(port);
 
-				if (!tty || !tty->termios)
+				if (!tty)
 					ms.cflag = ip->normal_termios.c_cflag;
 				else
-					ms.cflag = tty->termios->c_cflag;
+					ms.cflag = tty->termios.c_cflag;
 				tty_kref_put(tty);
 				spin_lock_irq(&ip->slock);
 				status = inb(ip->ioaddr + UART_MSR);
@@ -1589,13 +1589,13 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
 
 				tty = tty_port_tty_get(&ip->port);
 
-				if (!tty || !tty->termios) {
+				if (!tty) {
 					cflag = ip->normal_termios.c_cflag;
 					iflag = ip->normal_termios.c_iflag;
 					me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
 				} else {
-					cflag = tty->termios->c_cflag;
-					iflag = tty->termios->c_iflag;
+					cflag = tty->termios.c_cflag;
+					iflag = tty->termios.c_iflag;
 					me->baudrate[p] = tty_get_baud_rate(tty);
 				}
 				tty_kref_put(tty);
@@ -1853,7 +1853,7 @@ static void mxser_stoprx(struct tty_struct *tty)
 		}
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		info->MCR &= ~UART_MCR_RTS;
 		outb(info->MCR, info->ioaddr + UART_MCR);
 	}
@@ -1890,7 +1890,7 @@ static void mxser_unthrottle(struct tty_struct *tty)
 		}
 	}
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		info->MCR |= UART_MCR_RTS;
 		outb(info->MCR, info->ioaddr + UART_MCR);
 	}
@@ -1939,14 +1939,14 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi
 	spin_unlock_irqrestore(&info->slock, flags);
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		mxser_start(tty);
 	}
 
 	/* Handle sw stopped */
 	if ((old_termios->c_iflag & IXON) &&
-			!(tty->termios->c_iflag & IXON)) {
+			!(tty->termios.c_iflag & IXON)) {
 		tty->stopped = 0;
 
 		if (info->board->chip_flag) {
@@ -2337,11 +2337,36 @@ static struct tty_port_operations mxser_port_ops = {
  * The MOXA Smartio/Industio serial driver boot-time initialization code!
  */
 
+static bool allow_overlapping_vector;
+module_param(allow_overlapping_vector, bool, S_IRUGO);
+MODULE_PARM_DESC(allow_overlapping_vector, "whether we allow ISA cards to be configured such that vector overlabs IO ports (default=no)");
+
+static bool mxser_overlapping_vector(struct mxser_board *brd)
+{
+	return allow_overlapping_vector &&
+		brd->vector >= brd->ports[0].ioaddr &&
+		brd->vector < brd->ports[0].ioaddr + 8 * brd->info->nports;
+}
+
+static int mxser_request_vector(struct mxser_board *brd)
+{
+	if (mxser_overlapping_vector(brd))
+		return 0;
+	return request_region(brd->vector, 1, "mxser(vector)") ? 0 : -EIO;
+}
+
+static void mxser_release_vector(struct mxser_board *brd)
+{
+	if (mxser_overlapping_vector(brd))
+		return;
+	release_region(brd->vector, 1);
+}
+
 static void mxser_release_ISA_res(struct mxser_board *brd)
 {
 	free_irq(brd->irq, brd);
 	release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
-	release_region(brd->vector, 1);
+	mxser_release_vector(brd);
 }
 
 static int __devinit mxser_initbrd(struct mxser_board *brd,
@@ -2396,7 +2421,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd,
 
 static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
 {
-	int id, i, bits;
+	int id, i, bits, ret;
 	unsigned short regs[16], irq;
 	unsigned char scratch, scratch2;
 
@@ -2492,13 +2517,15 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
 				8 * brd->info->nports - 1);
 		return -EIO;
 	}
-	if (!request_region(brd->vector, 1, "mxser(vector)")) {
+
+	ret = mxser_request_vector(brd);
+	if (ret) {
 		release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
 		printk(KERN_ERR "mxser: can't request interrupt vector region: "
 				"0x%.8lx-0x%.8lx\n",
 				brd->ports[0].ioaddr, brd->ports[0].ioaddr +
 				8 * brd->info->nports - 1);
-		return -EIO;
+		return ret;
 	}
 	return brd->info->nports;
 
@@ -2598,7 +2625,8 @@ static int __devinit mxser_probe(struct pci_dev *pdev,
 		goto err_rel3;
 
 	for (i = 0; i < brd->info->nports; i++)
-		tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);
+		tty_port_register_device(&brd->ports[i].port, mxvar_sdriver,
+				brd->idx + i, &pdev->dev);
 
 	pci_set_drvdata(pdev, brd);
 
@@ -2695,7 +2723,8 @@ static int __init mxser_module_init(void)
 
 		brd->idx = m * MXSER_PORTS_PER_BOARD;
 		for (i = 0; i < brd->info->nports; i++)
-			tty_register_device(mxvar_sdriver, brd->idx + i, NULL);
+			tty_port_register_device(&brd->ports[i].port,
+					mxvar_sdriver, brd->idx + i, NULL);
 
 		m++;
 	}

+ 87 - 57
drivers/tty/n_gsm.c

@@ -108,7 +108,7 @@ struct gsm_mux_net {
  */
 
 struct gsm_msg {
-	struct gsm_msg *next;
+	struct list_head list;
 	u8 addr;		/* DLCI address + flags */
 	u8 ctrl;		/* Control byte + flags */
 	unsigned int len;	/* Length of data block (can be zero) */
@@ -245,8 +245,7 @@ struct gsm_mux {
 	unsigned int tx_bytes;		/* TX data outstanding */
 #define TX_THRESH_HI		8192
 #define TX_THRESH_LO		2048
-	struct gsm_msg *tx_head;	/* Pending data packets */
-	struct gsm_msg *tx_tail;
+	struct list_head tx_list;	/* Pending data packets */
 
 	/* Control messages */
 	struct timer_list t2_timer;	/* Retransmit timer for commands */
@@ -489,7 +488,7 @@ static void gsm_print_packet(const char *hdr, int addr, int cr,
 	default:
 		if (!(control & 0x01)) {
 			pr_cont("I N(S)%d N(R)%d",
-				(control & 0x0E) >> 1, (control & 0xE) >> 5);
+				(control & 0x0E) >> 1, (control & 0xE0) >> 5);
 		} else switch (control & 0x0F) {
 			case RR:
 				pr_cont("RR(%d)", (control & 0xE0) >> 5);
@@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
 	m->len = len;
 	m->addr = addr;
 	m->ctrl = ctrl;
-	m->next = NULL;
+	INIT_LIST_HEAD(&m->list);
 	return m;
 }
 
@@ -673,22 +672,21 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
  *
  *	The tty device has called us to indicate that room has appeared in
  *	the transmit queue. Ram more data into the pipe if we have any
+ *	If we have been flow-stopped by a CMD_FCOFF, then we can only
+ *	send messages on DLCI0 until CMD_FCON
  *
  *	FIXME: lock against link layer control transmissions
  */
 
 static void gsm_data_kick(struct gsm_mux *gsm)
 {
-	struct gsm_msg *msg = gsm->tx_head;
+	struct gsm_msg *msg, *nmsg;
 	int len;
 	int skip_sof = 0;
 
-	/* FIXME: We need to apply this solely to data messages */
-	if (gsm->constipated)
-		return;
-
-	while (gsm->tx_head != NULL) {
-		msg = gsm->tx_head;
+	list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
+		if (gsm->constipated && msg->addr)
+			continue;
 		if (gsm->encoding != 0) {
 			gsm->txframe[0] = GSM1_SOF;
 			len = gsm_stuff_frame(msg->data,
@@ -711,14 +709,13 @@ static void gsm_data_kick(struct gsm_mux *gsm)
 						len - skip_sof) < 0)
 			break;
 		/* FIXME: Can eliminate one SOF in many more cases */
-		gsm->tx_head = msg->next;
-		if (gsm->tx_head == NULL)
-			gsm->tx_tail = NULL;
 		gsm->tx_bytes -= msg->len;
-		kfree(msg);
 		/* For a burst of frames skip the extra SOF within the
 		   burst */
 		skip_sof = 1;
+
+		list_del(&msg->list);
+		kfree(msg);
 	}
 }
 
@@ -768,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
 	msg->data = dp;
 
 	/* Add to the actual output queue */
-	if (gsm->tx_tail)
-		gsm->tx_tail->next = msg;
-	else
-		gsm->tx_head = msg;
-	gsm->tx_tail = msg;
+	list_add_tail(&msg->list, &gsm->tx_list);
 	gsm->tx_bytes += msg->len;
 	gsm_data_kick(gsm);
 }
@@ -875,7 +868,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 
 	/* dlci->skb is locked by tx_lock */
 	if (dlci->skb == NULL) {
-		dlci->skb = skb_dequeue(&dlci->skb_list);
+		dlci->skb = skb_dequeue_tail(&dlci->skb_list);
 		if (dlci->skb == NULL)
 			return 0;
 		first = 1;
@@ -886,7 +879,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 	if (len > gsm->mtu) {
 		if (dlci->adaption == 3) {
 			/* Over long frame, bin it */
-			kfree_skb(dlci->skb);
+			dev_kfree_skb_any(dlci->skb);
 			dlci->skb = NULL;
 			return 0;
 		}
@@ -899,8 +892,11 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 
 	/* FIXME: need a timer or something to kick this so it can't
 	   get stuck with no work outstanding and no buffer free */
-	if (msg == NULL)
+	if (msg == NULL) {
+		skb_queue_tail(&dlci->skb_list, dlci->skb);
+		dlci->skb = NULL;
 		return -ENOMEM;
+	}
 	dp = msg->data;
 
 	if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */
@@ -912,7 +908,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 	skb_pull(dlci->skb, len);
 	__gsm_data_queue(dlci, msg);
 	if (last) {
-		kfree_skb(dlci->skb);
+		dev_kfree_skb_any(dlci->skb);
 		dlci->skb = NULL;
 	}
 	return size;
@@ -971,16 +967,22 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
 static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
 {
 	unsigned long flags;
+	int sweep;
+
+	if (dlci->constipated) 
+		return;
 
 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
 	/* If we have nothing running then we need to fire up */
+	sweep = (dlci->gsm->tx_bytes < TX_THRESH_LO);
 	if (dlci->gsm->tx_bytes == 0) {
 		if (dlci->net)
 			gsm_dlci_data_output_framed(dlci->gsm, dlci);
 		else
 			gsm_dlci_data_output(dlci->gsm, dlci);
-	} else if (dlci->gsm->tx_bytes < TX_THRESH_LO)
-		gsm_dlci_data_sweep(dlci->gsm);
+	}
+	if (sweep)
+ 		gsm_dlci_data_sweep(dlci->gsm);
 	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
 }
 
@@ -1027,6 +1029,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
 {
 	int  mlines = 0;
 	u8 brk = 0;
+	int fc;
 
 	/* The modem status command can either contain one octet (v.24 signals)
 	   or two octets (v.24 signals + break signals). The length field will
@@ -1038,19 +1041,21 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
 	else {
 		brk = modem & 0x7f;
 		modem = (modem >> 7) & 0x7f;
-	};
+	}
 
 	/* Flow control/ready to communicate */
-	if (modem & MDM_FC) {
+	fc = (modem & MDM_FC) || !(modem & MDM_RTR);
+	if (fc && !dlci->constipated) {
 		/* Need to throttle our output on this device */
 		dlci->constipated = 1;
-	}
-	if (modem & MDM_RTC) {
-		mlines |= TIOCM_DSR | TIOCM_DTR;
+	} else if (!fc && dlci->constipated) {
 		dlci->constipated = 0;
 		gsm_dlci_data_kick(dlci);
 	}
+
 	/* Map modem bits */
+	if (modem & MDM_RTC)
+		mlines |= TIOCM_DSR | TIOCM_DTR;
 	if (modem & MDM_RTR)
 		mlines |= TIOCM_RTS | TIOCM_CTS;
 	if (modem & MDM_IC)
@@ -1061,7 +1066,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
 	/* Carrier drop -> hangup */
 	if (tty) {
 		if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD))
-			if (!(tty->termios->c_cflag & CLOCAL))
+			if (!(tty->termios.c_cflag & CLOCAL))
 				tty_hangup(tty);
 		if (brk & 0x01)
 			tty_insert_flip_char(tty, 0, TTY_BREAK);
@@ -1190,6 +1195,8 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
 							u8 *data, int clen)
 {
 	u8 buf[1];
+	unsigned long flags;
+
 	switch (command) {
 	case CMD_CLD: {
 		struct gsm_dlci *dlci = gsm->dlci[0];
@@ -1206,16 +1213,18 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
 		gsm_control_reply(gsm, CMD_TEST, data, clen);
 		break;
 	case CMD_FCON:
-		/* Modem wants us to STFU */
-		gsm->constipated = 1;
-		gsm_control_reply(gsm, CMD_FCON, NULL, 0);
-		break;
-	case CMD_FCOFF:
 		/* Modem can accept data again */
 		gsm->constipated = 0;
-		gsm_control_reply(gsm, CMD_FCOFF, NULL, 0);
+		gsm_control_reply(gsm, CMD_FCON, NULL, 0);
 		/* Kick the link in case it is idling */
+		spin_lock_irqsave(&gsm->tx_lock, flags);
 		gsm_data_kick(gsm);
+		spin_unlock_irqrestore(&gsm->tx_lock, flags);
+		break;
+	case CMD_FCOFF:
+		/* Modem wants us to STFU */
+		gsm->constipated = 1;
+		gsm_control_reply(gsm, CMD_FCOFF, NULL, 0);
 		break;
 	case CMD_MSC:
 		/* Out of band modem line change indicator for a DLCI */
@@ -1668,7 +1677,7 @@ static void gsm_dlci_free(struct kref *ref)
 	dlci->gsm->dlci[dlci->addr] = NULL;
 	kfifo_free(dlci->fifo);
 	while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
-		kfree_skb(dlci->skb);
+		dev_kfree_skb(dlci->skb);
 	kfree(dlci);
 }
 
@@ -2007,7 +2016,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
 {
 	int i;
 	struct gsm_dlci *dlci = gsm->dlci[0];
-	struct gsm_msg *txq;
+	struct gsm_msg *txq, *ntxq;
 	struct gsm_control *gc;
 
 	gsm->dead = 1;
@@ -2042,11 +2051,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
 		if (gsm->dlci[i])
 			gsm_dlci_release(gsm->dlci[i]);
 	/* Now wipe the queues */
-	for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) {
-		gsm->tx_head = txq->next;
+	list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
 		kfree(txq);
-	}
-	gsm->tx_tail = NULL;
+	INIT_LIST_HEAD(&gsm->tx_list);
 }
 EXPORT_SYMBOL_GPL(gsm_cleanup_mux);
 
@@ -2157,6 +2164,7 @@ struct gsm_mux *gsm_alloc_mux(void)
 	}
 	spin_lock_init(&gsm->lock);
 	kref_init(&gsm->ref);
+	INIT_LIST_HEAD(&gsm->tx_list);
 
 	gsm->t1 = T1;
 	gsm->t2 = T2;
@@ -2273,7 +2281,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
 			gsm->error(gsm, *dp, flags);
 			break;
 		default:
-			WARN_ONCE("%s: unknown flag %d\n",
+			WARN_ONCE(1, "%s: unknown flag %d\n",
 			       tty_name(tty, buf), flags);
 			break;
 		}
@@ -2377,12 +2385,12 @@ static void gsmld_write_wakeup(struct tty_struct *tty)
 
 	/* Queue poll */
 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	spin_lock_irqsave(&gsm->tx_lock, flags);
 	gsm_data_kick(gsm);
 	if (gsm->tx_bytes < TX_THRESH_LO) {
-		spin_lock_irqsave(&gsm->tx_lock, flags);
 		gsm_dlci_data_sweep(gsm);
-		spin_unlock_irqrestore(&gsm->tx_lock, flags);
 	}
+	spin_unlock_irqrestore(&gsm->tx_lock, flags);
 }
 
 /**
@@ -2868,14 +2876,14 @@ static const struct tty_port_operations gsm_port_ops = {
 	.dtr_rts = gsm_dtr_rts,
 };
 
-
-static int gsmtty_open(struct tty_struct *tty, struct file *filp)
+static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
 {
 	struct gsm_mux *gsm;
 	struct gsm_dlci *dlci;
-	struct tty_port *port;
 	unsigned int line = tty->index;
 	unsigned int mux = line >> 6;
+	bool alloc = false;
+	int ret;
 
 	line = line & 0x3F;
 
@@ -2889,14 +2897,35 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
 	gsm = gsm_mux[mux];
 	if (gsm->dead)
 		return -EL2HLT;
+	/* If DLCI 0 is not yet fully open return an error. This is ok from a locking
+	   perspective as we don't have to worry about this if DLCI0 is lost */
+	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) 
+		return -EL2NSYNC;
 	dlci = gsm->dlci[line];
-	if (dlci == NULL)
+	if (dlci == NULL) {
+		alloc = true;
 		dlci = gsm_dlci_alloc(gsm, line);
+	}
 	if (dlci == NULL)
 		return -ENOMEM;
-	port = &dlci->port;
-	port->count++;
+	ret = tty_port_install(&dlci->port, driver, tty);
+	if (ret) {
+		if (alloc)
+			dlci_put(dlci);
+		return ret;
+	}
+
 	tty->driver_data = dlci;
+
+	return 0;
+}
+
+static int gsmtty_open(struct tty_struct *tty, struct file *filp)
+{
+	struct gsm_dlci *dlci = tty->driver_data;
+	struct tty_port *port = &dlci->port;
+
+	port->count++;
 	dlci_get(dlci);
 	dlci_get(dlci->gsm->dlci[0]);
 	mux_get(dlci->gsm);
@@ -3043,13 +3072,13 @@ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
 	   the RPN control message. This however rapidly gets nasty as we
 	   then have to remap modem signals each way according to whether
 	   our virtual cable is null modem etc .. */
-	tty_termios_copy_hw(tty->termios, old);
+	tty_termios_copy_hw(&tty->termios, old);
 }
 
 static void gsmtty_throttle(struct tty_struct *tty)
 {
 	struct gsm_dlci *dlci = tty->driver_data;
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		dlci->modem_tx &= ~TIOCM_DTR;
 	dlci->throttled = 1;
 	/* Send an MSC with DTR cleared */
@@ -3059,7 +3088,7 @@ static void gsmtty_throttle(struct tty_struct *tty)
 static void gsmtty_unthrottle(struct tty_struct *tty)
 {
 	struct gsm_dlci *dlci = tty->driver_data;
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		dlci->modem_tx |= TIOCM_DTR;
 	dlci->throttled = 0;
 	/* Send an MSC with DTR set */
@@ -3085,6 +3114,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
 
 /* Virtual ttys for the demux */
 static const struct tty_operations gsmtty_ops = {
+	.install		= gsmtty_install,
 	.open			= gsmtty_open,
 	.close			= gsmtty_close,
 	.write			= gsmtty_write,

+ 5 - 5
drivers/tty/n_r3964.c

@@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 
 	TRACE_L("read()");
 
-	tty_lock();
+	tty_lock(tty);
 
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
@@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 				goto unlock;
 			}
 			/* block until there is a message: */
-			wait_event_interruptible_tty(pInfo->read_wait,
+			wait_event_interruptible_tty(tty, pInfo->read_wait,
 					(pMsg = remove_msg(pInfo, pClient)));
 		}
 
@@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 	}
 	ret = -EPERM;
 unlock:
-	tty_unlock();
+	tty_unlock(tty);
 	return ret;
 }
 
@@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	pHeader->locks = 0;
 	pHeader->owner = NULL;
 
-	tty_lock();
+	tty_lock(tty);
 
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
@@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	add_tx_queue(pInfo, pHeader);
 	trigger_transmit(pInfo);
 
-	tty_unlock();
+	tty_unlock(tty);
 
 	return 0;
 }

+ 24 - 5
drivers/tty/n_tty.c

@@ -92,10 +92,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
 
 static void n_tty_set_room(struct tty_struct *tty)
 {
-	/* tty->read_cnt is not read locked ? */
-	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+	int left;
 	int old_left;
 
+	/* tty->read_cnt is not read locked ? */
+	if (I_PARMRK(tty)) {
+		/* Multiply read_cnt by 3, since each byte might take up to
+		 * three times as many spaces when PARMRK is set (depending on
+		 * its flags, e.g. parity error). */
+		left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1;
+	} else
+		left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+
 	/*
 	 * If we are doing input canonicalization, and there are no
 	 * pending newlines, let characters through without limit, so
@@ -1432,6 +1440,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
 	 */
 	if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
 		tty_throttle(tty);
+
+        /* FIXME: there is a tiny race here if the receive room check runs
+           before the other work executes and empties the buffer (upping
+           the receiving room and unthrottling. We then throttle and get
+           stuck. This has been observed and traced down by Vincent Pillet/
+           We need to address this when we sort out out the rx path locking */
 }
 
 int is_ignored(int sig)
@@ -1460,7 +1474,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 	BUG_ON(!tty);
 
 	if (old)
-		canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON;
+		canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
 	if (canon_change) {
 		memset(&tty->read_flags, 0, sizeof tty->read_flags);
 		tty->canon_head = tty->read_tail;
@@ -1728,7 +1742,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 
 do_it_again:
 
-	BUG_ON(!tty->read_buf);
+	if (WARN_ON(!tty->read_buf))
+		return -EAGAIN;
 
 	c = job_control(tty, file);
 	if (c < 0)
@@ -1832,13 +1847,13 @@ do_it_again:
 
 		if (tty->icanon && !L_EXTPROC(tty)) {
 			/* N.B. avoid overrun if nr == 0 */
+			spin_lock_irqsave(&tty->read_lock, flags);
 			while (nr && tty->read_cnt) {
 				int eol;
 
 				eol = test_and_clear_bit(tty->read_tail,
 						tty->read_flags);
 				c = tty->read_buf[tty->read_tail];
-				spin_lock_irqsave(&tty->read_lock, flags);
 				tty->read_tail = ((tty->read_tail+1) &
 						  (N_TTY_BUF_SIZE-1));
 				tty->read_cnt--;
@@ -1856,15 +1871,19 @@ do_it_again:
 					if (tty_put_user(tty, c, b++)) {
 						retval = -EFAULT;
 						b--;
+						spin_lock_irqsave(&tty->read_lock, flags);
 						break;
 					}
 					nr--;
 				}
 				if (eol) {
 					tty_audit_push(tty);
+					spin_lock_irqsave(&tty->read_lock, flags);
 					break;
 				}
+				spin_lock_irqsave(&tty->read_lock, flags);
 			}
+			spin_unlock_irqrestore(&tty->read_lock, flags);
 			if (retval)
 				break;
 		} else {

+ 2 - 2
drivers/tty/nozomi.c

@@ -1473,8 +1473,8 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
 		port->dc = dc;
 		tty_port_init(&port->port);
 		port->port.ops = &noz_tty_port_ops;
-		tty_dev = tty_register_device(ntty_driver, dc->index_start + i,
-							&pdev->dev);
+		tty_dev = tty_port_register_device(&port->port, ntty_driver,
+				dc->index_start + i, &pdev->dev);
 
 		if (IS_ERR(tty_dev)) {
 			ret = PTR_ERR(tty_dev);

+ 122 - 112
drivers/tty/pty.c

@@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 	wake_up_interruptible(&tty->read_wait);
 	wake_up_interruptible(&tty->write_wait);
 	tty->packet = 0;
+	/* Review - krefs on tty_link ?? */
 	if (!tty->link)
 		return;
 	tty->link->packet = 0;
@@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 		        mutex_unlock(&devpts_mutex);
 		}
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		tty_vhangup(tty->link);
-		tty_lock();
+		tty_lock(tty);
 	}
 }
 
@@ -231,8 +232,8 @@ out:
 static void pty_set_termios(struct tty_struct *tty,
 					struct ktermios *old_termios)
 {
-	tty->termios->c_cflag &= ~(CSIZE | PARENB);
-	tty->termios->c_cflag |= (CS8 | CREAD);
+	tty->termios.c_cflag &= ~(CSIZE | PARENB);
+	tty->termios.c_cflag |= (CS8 | CREAD);
 }
 
 /**
@@ -282,60 +283,110 @@ done:
 	return 0;
 }
 
-/* Traditional BSD devices */
-#ifdef CONFIG_LEGACY_PTYS
-
-static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
+/**
+ *	pty_common_install		-	set up the pty pair
+ *	@driver: the pty driver
+ *	@tty: the tty being instantiated
+ *	@bool: legacy, true if this is BSD style
+ *
+ *	Perform the initial set up for the tty/pty pair. Called from the
+ *	tty layer when the port is first opened.
+ *
+ *	Locking: the caller must hold the tty_mutex
+ */
+static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
+		bool legacy)
 {
 	struct tty_struct *o_tty;
+	struct tty_port *ports[2];
 	int idx = tty->index;
-	int retval;
+	int retval = -ENOMEM;
 
 	o_tty = alloc_tty_struct();
 	if (!o_tty)
-		return -ENOMEM;
+		goto err;
+	ports[0] = kmalloc(sizeof **ports, GFP_KERNEL);
+	ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);
+	if (!ports[0] || !ports[1])
+		goto err_free_tty;
 	if (!try_module_get(driver->other->owner)) {
 		/* This cannot in fact currently happen */
-		retval = -ENOMEM;
 		goto err_free_tty;
 	}
 	initialize_tty_struct(o_tty, driver->other, idx);
 
-	/* We always use new tty termios data so we can do this
-	   the easy way .. */
-	retval = tty_init_termios(tty);
-	if (retval)
-		goto err_deinit_tty;
-
-	retval = tty_init_termios(o_tty);
-	if (retval)
-		goto err_free_termios;
+	if (legacy) {
+		/* We always use new tty termios data so we can do this
+		   the easy way .. */
+		retval = tty_init_termios(tty);
+		if (retval)
+			goto err_deinit_tty;
+
+		retval = tty_init_termios(o_tty);
+		if (retval)
+			goto err_free_termios;
+
+		driver->other->ttys[idx] = o_tty;
+		driver->ttys[idx] = tty;
+	} else {
+		memset(&tty->termios_locked, 0, sizeof(tty->termios_locked));
+		tty->termios = driver->init_termios;
+		memset(&o_tty->termios_locked, 0, sizeof(tty->termios_locked));
+		o_tty->termios = driver->other->init_termios;
+	}
 
 	/*
 	 * Everything allocated ... set up the o_tty structure.
 	 */
-	driver->other->ttys[idx] = o_tty;
 	tty_driver_kref_get(driver->other);
 	if (driver->subtype == PTY_TYPE_MASTER)
 		o_tty->count++;
 	/* Establish the links in both directions */
 	tty->link   = o_tty;
 	o_tty->link = tty;
+	tty_port_init(ports[0]);
+	tty_port_init(ports[1]);
+	o_tty->port = ports[0];
+	tty->port = ports[1];
 
 	tty_driver_kref_get(driver);
 	tty->count++;
-	driver->ttys[idx] = tty;
 	return 0;
 err_free_termios:
-	tty_free_termios(tty);
+	if (legacy)
+		tty_free_termios(tty);
 err_deinit_tty:
 	deinitialize_tty_struct(o_tty);
 	module_put(o_tty->driver->owner);
 err_free_tty:
+	kfree(ports[0]);
+	kfree(ports[1]);
 	free_tty_struct(o_tty);
+err:
 	return retval;
 }
 
+static void pty_cleanup(struct tty_struct *tty)
+{
+	kfree(tty->port);
+}
+
+/* Traditional BSD devices */
+#ifdef CONFIG_LEGACY_PTYS
+
+static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	return pty_common_install(driver, tty, true);
+}
+
+static void pty_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct tty_struct *pair = tty->link;
+	driver->ttys[tty->index] = NULL;
+	if (pair)
+		pair->driver->ttys[pair->index] = NULL;
+}
+
 static int pty_bsd_ioctl(struct tty_struct *tty,
 			 unsigned int cmd, unsigned long arg)
 {
@@ -366,7 +417,9 @@ static const struct tty_operations master_pty_ops_bsd = {
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.ioctl = pty_bsd_ioctl,
-	.resize = pty_resize
+	.cleanup = pty_cleanup,
+	.resize = pty_resize,
+	.remove = pty_remove
 };
 
 static const struct tty_operations slave_pty_ops_bsd = {
@@ -379,7 +432,9 @@ static const struct tty_operations slave_pty_ops_bsd = {
 	.chars_in_buffer = pty_chars_in_buffer,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
-	.resize = pty_resize
+	.cleanup = pty_cleanup,
+	.resize = pty_resize,
+	.remove = pty_remove
 };
 
 static void __init legacy_pty_init(void)
@@ -389,12 +444,18 @@ static void __init legacy_pty_init(void)
 	if (legacy_count <= 0)
 		return;
 
-	pty_driver = alloc_tty_driver(legacy_count);
-	if (!pty_driver)
+	pty_driver = tty_alloc_driver(legacy_count,
+			TTY_DRIVER_RESET_TERMIOS |
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_ALLOC);
+	if (IS_ERR(pty_driver))
 		panic("Couldn't allocate pty driver");
 
-	pty_slave_driver = alloc_tty_driver(legacy_count);
-	if (!pty_slave_driver)
+	pty_slave_driver = tty_alloc_driver(legacy_count,
+			TTY_DRIVER_RESET_TERMIOS |
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_ALLOC);
+	if (IS_ERR(pty_slave_driver))
 		panic("Couldn't allocate pty slave driver");
 
 	pty_driver->driver_name = "pty_master";
@@ -410,7 +471,6 @@ static void __init legacy_pty_init(void)
 	pty_driver->init_termios.c_lflag = 0;
 	pty_driver->init_termios.c_ispeed = 38400;
 	pty_driver->init_termios.c_ospeed = 38400;
-	pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
 	pty_driver->other = pty_slave_driver;
 	tty_set_operations(pty_driver, &master_pty_ops_bsd);
 
@@ -424,8 +484,6 @@ static void __init legacy_pty_init(void)
 	pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
 	pty_slave_driver->init_termios.c_ispeed = 38400;
 	pty_slave_driver->init_termios.c_ospeed = 38400;
-	pty_slave_driver->flags = TTY_DRIVER_RESET_TERMIOS |
-					TTY_DRIVER_REAL_RAW;
 	pty_slave_driver->other = pty_driver;
 	tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd);
 
@@ -497,78 +555,22 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
 	return tty;
 }
 
-static void pty_unix98_shutdown(struct tty_struct *tty)
-{
-	tty_driver_remove_tty(tty->driver, tty);
-	/* We have our own method as we don't use the tty index */
-	kfree(tty->termios);
-}
-
 /* We have no need to install and remove our tty objects as devpts does all
    the work for us */
 
 static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
 {
-	struct tty_struct *o_tty;
-	int idx = tty->index;
-
-	o_tty = alloc_tty_struct();
-	if (!o_tty)
-		return -ENOMEM;
-	if (!try_module_get(driver->other->owner)) {
-		/* This cannot in fact currently happen */
-		goto err_free_tty;
-	}
-	initialize_tty_struct(o_tty, driver->other, idx);
-
-	tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
-	if (tty->termios == NULL)
-		goto err_free_mem;
-	*tty->termios = driver->init_termios;
-	tty->termios_locked = tty->termios + 1;
-
-	o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
-	if (o_tty->termios == NULL)
-		goto err_free_mem;
-	*o_tty->termios = driver->other->init_termios;
-	o_tty->termios_locked = o_tty->termios + 1;
-
-	tty_driver_kref_get(driver->other);
-	if (driver->subtype == PTY_TYPE_MASTER)
-		o_tty->count++;
-	/* Establish the links in both directions */
-	tty->link   = o_tty;
-	o_tty->link = tty;
-	/*
-	 * All structures have been allocated, so now we install them.
-	 * Failures after this point use release_tty to clean up, so
-	 * there's no need to null out the local pointers.
-	 */
-	tty_driver_kref_get(driver);
-	tty->count++;
-	return 0;
-err_free_mem:
-	deinitialize_tty_struct(o_tty);
-	kfree(o_tty->termios);
-	kfree(tty->termios);
-	module_put(o_tty->driver->owner);
-err_free_tty:
-	free_tty_struct(o_tty);
-	return -ENOMEM;
-}
-
-static void ptm_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
-{
+	return pty_common_install(driver, tty, false);
 }
 
-static void pts_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
+static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
 {
 }
 
 static const struct tty_operations ptm_unix98_ops = {
 	.lookup = ptm_unix98_lookup,
 	.install = pty_unix98_install,
-	.remove = ptm_unix98_remove,
+	.remove = pty_unix98_remove,
 	.open = pty_open,
 	.close = pty_close,
 	.write = pty_write,
@@ -578,14 +580,14 @@ static const struct tty_operations ptm_unix98_ops = {
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.ioctl = pty_unix98_ioctl,
-	.shutdown = pty_unix98_shutdown,
-	.resize = pty_resize
+	.resize = pty_resize,
+	.cleanup = pty_cleanup
 };
 
 static const struct tty_operations pty_unix98_ops = {
 	.lookup = pts_unix98_lookup,
 	.install = pty_unix98_install,
-	.remove = pts_unix98_remove,
+	.remove = pty_unix98_remove,
 	.open = pty_open,
 	.close = pty_close,
 	.write = pty_write,
@@ -594,7 +596,7 @@ static const struct tty_operations pty_unix98_ops = {
 	.chars_in_buffer = pty_chars_in_buffer,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
-	.shutdown = pty_unix98_shutdown
+	.cleanup = pty_cleanup,
 };
 
 /**
@@ -622,26 +624,28 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 		return retval;
 
 	/* find a device that is not in use. */
-	tty_lock();
+	mutex_lock(&devpts_mutex);
 	index = devpts_new_index(inode);
-	tty_unlock();
 	if (index < 0) {
 		retval = index;
+		mutex_unlock(&devpts_mutex);
 		goto err_file;
 	}
 
+	mutex_unlock(&devpts_mutex);
+
 	mutex_lock(&tty_mutex);
-	mutex_lock(&devpts_mutex);
 	tty = tty_init_dev(ptm_driver, index);
-	mutex_unlock(&devpts_mutex);
-	tty_lock();
-	mutex_unlock(&tty_mutex);
 
 	if (IS_ERR(tty)) {
 		retval = PTR_ERR(tty);
 		goto out;
 	}
 
+	/* The tty returned here is locked so we can safely
+	   drop the mutex */
+	mutex_unlock(&tty_mutex);
+
 	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
 
 	tty_add_file(tty, filp);
@@ -654,15 +658,15 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 	if (retval)
 		goto err_release;
 
-	tty_unlock();
+	tty_unlock(tty);
 	return 0;
 err_release:
-	tty_unlock();
+	tty_unlock(tty);
 	tty_release(inode, filp);
 	return retval;
 out:
+	mutex_unlock(&tty_mutex);
 	devpts_kill_index(inode, index);
-	tty_unlock();
 err_file:
 	tty_free_file(filp);
 	return retval;
@@ -672,11 +676,21 @@ static struct file_operations ptmx_fops;
 
 static void __init unix98_pty_init(void)
 {
-	ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
-	if (!ptm_driver)
+	ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
+			TTY_DRIVER_RESET_TERMIOS |
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_DEV |
+			TTY_DRIVER_DEVPTS_MEM |
+			TTY_DRIVER_DYNAMIC_ALLOC);
+	if (IS_ERR(ptm_driver))
 		panic("Couldn't allocate Unix98 ptm driver");
-	pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
-	if (!pts_driver)
+	pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
+			TTY_DRIVER_RESET_TERMIOS |
+			TTY_DRIVER_REAL_RAW |
+			TTY_DRIVER_DYNAMIC_DEV |
+			TTY_DRIVER_DEVPTS_MEM |
+			TTY_DRIVER_DYNAMIC_ALLOC);
+	if (IS_ERR(pts_driver))
 		panic("Couldn't allocate Unix98 pts driver");
 
 	ptm_driver->driver_name = "pty_master";
@@ -692,8 +706,6 @@ static void __init unix98_pty_init(void)
 	ptm_driver->init_termios.c_lflag = 0;
 	ptm_driver->init_termios.c_ispeed = 38400;
 	ptm_driver->init_termios.c_ospeed = 38400;
-	ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
-		TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
 	ptm_driver->other = pts_driver;
 	tty_set_operations(ptm_driver, &ptm_unix98_ops);
 
@@ -707,8 +719,6 @@ static void __init unix98_pty_init(void)
 	pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
 	pts_driver->init_termios.c_ispeed = 38400;
 	pts_driver->init_termios.c_ospeed = 38400;
-	pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
-		TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
 	pts_driver->other = ptm_driver;
 	tty_set_operations(pts_driver, &pty_unix98_ops);
 

+ 11 - 11
drivers/tty/rocket.c

@@ -704,8 +704,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev)
 	spin_lock_init(&info->slock);
 	mutex_init(&info->write_mtx);
 	rp_table[line] = info;
-	tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev :
-			NULL);
+	tty_port_register_device(&info->port, rocket_driver, line,
+			pci_dev ? &pci_dev->dev : NULL);
 }
 
 /*
@@ -720,7 +720,7 @@ static void configure_r_port(struct tty_struct *tty, struct r_port *info,
 	unsigned rocketMode;
 	int bits, baud, divisor;
 	CHANNEL_t *cp;
-	struct ktermios *t = tty->termios;
+	struct ktermios *t = &tty->termios;
 
 	cp = &info->channel;
 	cflag = t->c_cflag;
@@ -978,7 +978,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
 			tty->alt_speed = 460800;
 
 		configure_r_port(tty, info, NULL);
-		if (tty->termios->c_cflag & CBAUD) {
+		if (tty->termios.c_cflag & CBAUD) {
 			sSetDTR(cp);
 			sSetRTS(cp);
 		}
@@ -1089,35 +1089,35 @@ static void rp_set_termios(struct tty_struct *tty,
 	if (rocket_paranoia_check(info, "rp_set_termios"))
 		return;
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 	/*
 	 * This driver doesn't support CS5 or CS6
 	 */
 	if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
-		tty->termios->c_cflag =
+		tty->termios.c_cflag =
 		    ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
 	/* Or CMSPAR */
-	tty->termios->c_cflag &= ~CMSPAR;
+	tty->termios.c_cflag &= ~CMSPAR;
 
 	configure_r_port(tty, info, old_termios);
 
 	cp = &info->channel;
 
 	/* Handle transition to B0 status */
-	if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) {
+	if ((old_termios->c_cflag & CBAUD) && !(tty->termios.c_cflag & CBAUD)) {
 		sClrDTR(cp);
 		sClrRTS(cp);
 	}
 
 	/* Handle transition away from B0 status */
-	if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) {
-		if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS))
+	if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) {
+		if (!tty->hw_stopped || !(tty->termios.c_cflag & CRTSCTS))
 			sSetRTS(cp);
 		sSetDTR(cp);
 	}
 
-	if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
+	if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		rp_start(tty);
 	}

+ 13 - 10
drivers/tty/serial/68328serial.c

@@ -515,7 +515,7 @@ static void change_speed(struct m68k_serial *info, struct tty_struct *tty)
 	unsigned cflag;
 	int	i;
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 	if (!(port = info->port))
 		return;
 
@@ -617,7 +617,7 @@ static void rs_set_ldisc(struct tty_struct *tty)
 	if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
 		return;
 
-	info->is_cons = (tty->termios->c_line == N_TTY);
+	info->is_cons = (tty->termios.c_line == N_TTY);
 	
 	printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");
 }
@@ -985,7 +985,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	change_speed(info, tty);
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		rs_start(tty);
 	}
@@ -1070,7 +1070,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		if (tty->ldisc.close)
 			(tty->ldisc.close)(tty);
 		tty->ldisc = ldiscs[N_TTY];
-		tty->termios->c_line = N_TTY;
+		tty->termios.c_line = N_TTY;
 		if (tty->ldisc.open)
 			(tty->ldisc.open)(tty);
 	}
@@ -1189,12 +1189,6 @@ rs68328_init(void)
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(serial_driver, &rs_ops);
 
-	if (tty_register_driver(serial_driver)) {
-		put_tty_driver(serial_driver);
-		printk(KERN_ERR "Couldn't register serial driver\n");
-		return -ENOMEM;
-	}
-
 	local_irq_save(flags);
 
 	for(i=0;i<NR_PORTS;i++) {
@@ -1224,8 +1218,17 @@ rs68328_init(void)
 			    0,
 			    "M68328_UART", info))
                 panic("Unable to attach 68328 serial interrupt\n");
+
+	    tty_port_link_device(&info->tport, serial_driver, i);
 	}
 	local_irq_restore(flags);
+
+	if (tty_register_driver(serial_driver)) {
+		put_tty_driver(serial_driver);
+		printk(KERN_ERR "Couldn't register serial driver\n");
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 

+ 79 - 77
drivers/tty/serial/8250/8250.c

@@ -290,6 +290,9 @@ static const struct serial8250_config uart_config[] = {
 				  UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
 		.flags		= UART_CAP_FIFO,
 	},
+	[PORT_8250_CIR] = {
+		.name		= "CIR port"
+	}
 };
 
 /* Uart divisor latch read */
@@ -1037,6 +1040,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 	unsigned char save_lcr, save_mcr;
 	struct uart_port *port = &up->port;
 	unsigned long flags;
+	unsigned int old_capabilities;
 
 	if (!port->iobase && !port->mapbase && !port->membase)
 		return;
@@ -1087,6 +1091,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 			/*
 			 * We failed; there's nothing here
 			 */
+			spin_unlock_irqrestore(&port->lock, flags);
 			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
 				       scratch2, scratch3);
 			goto out;
@@ -1110,6 +1115,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 		status1 = serial_in(up, UART_MSR) & 0xF0;
 		serial_out(up, UART_MCR, save_mcr);
 		if (status1 != 0x90) {
+			spin_unlock_irqrestore(&port->lock, flags);
 			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
 				       status1);
 			goto out;
@@ -1132,8 +1138,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
 	scratch = serial_in(up, UART_IIR) >> 6;
 
-	DEBUG_AUTOCONF("iir=%d ", scratch);
-
 	switch (scratch) {
 	case 0:
 		autoconfig_8250(up);
@@ -1167,19 +1171,13 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 
 	serial_out(up, UART_LCR, save_lcr);
 
-	if (up->capabilities != uart_config[port->type].flags) {
-		printk(KERN_WARNING
-		       "ttyS%d: detected caps %08x should be %08x\n",
-		       serial_index(port), up->capabilities,
-		       uart_config[port->type].flags);
-	}
-
 	port->fifosize = uart_config[up->port.type].fifo_size;
+	old_capabilities = up->capabilities; 
 	up->capabilities = uart_config[port->type].flags;
 	up->tx_loadsz = uart_config[port->type].tx_loadsz;
 
 	if (port->type == PORT_UNKNOWN)
-		goto out;
+		goto out_lock;
 
 	/*
 	 * Reset the UART.
@@ -1196,8 +1194,16 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 	else
 		serial_out(up, UART_IER, 0);
 
- out:
+out_lock:
 	spin_unlock_irqrestore(&port->lock, flags);
+	if (up->capabilities != old_capabilities) {
+		printk(KERN_WARNING
+		       "ttyS%d: detected caps %08x should be %08x\n",
+		       serial_index(port), old_capabilities,
+		       up->capabilities);
+	}
+out:
+	DEBUG_AUTOCONF("iir=%d ", scratch);
 	DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name);
 }
 
@@ -1897,6 +1903,9 @@ static int serial8250_startup(struct uart_port *port)
 	unsigned char lsr, iir;
 	int retval;
 
+	if (port->type == PORT_8250_CIR)
+		return -ENODEV;
+
 	port->fifosize = uart_config[up->port.type].fifo_size;
 	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
 	up->capabilities = uart_config[up->port.type].flags;
@@ -2202,6 +2211,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
 	unsigned int baud, quot;
+	int fifo_bug = 0;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -2221,8 +2231,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
 	if (termios->c_cflag & CSTOPB)
 		cval |= UART_LCR_STOP;
-	if (termios->c_cflag & PARENB)
+	if (termios->c_cflag & PARENB) {
 		cval |= UART_LCR_PARITY;
+		if (up->bugs & UART_BUG_PARITY)
+			fifo_bug = 1;
+	}
 	if (!(termios->c_cflag & PARODD))
 		cval |= UART_LCR_EPAR;
 #ifdef CMSPAR
@@ -2246,7 +2259,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
 	if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
 		fcr = uart_config[port->type].fcr;
-		if (baud < 2400) {
+		if (baud < 2400 || fifo_bug) {
 			fcr &= ~UART_FCR_TRIGGER_MASK;
 			fcr |= UART_FCR_TRIGGER_1;
 		}
@@ -2336,7 +2349,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 			serial_port_out(port, UART_EFR, efr);
 	}
 
-#ifdef CONFIG_ARCH_OMAP
+#ifdef CONFIG_ARCH_OMAP1
 	/* Workaround to enable 115200 baud on OMAP1510 internal ports */
 	if (cpu_is_omap1510() && is_omap_port(up)) {
 		if (baud == 115200) {
@@ -2426,7 +2439,7 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
 {
 	if (pt->port.iotype == UPIO_AU)
 		return 0x1000;
-#ifdef CONFIG_ARCH_OMAP
+#ifdef CONFIG_ARCH_OMAP1
 	if (is_omap_port(pt))
 		return 0x16 << pt->port.regshift;
 #endif
@@ -2550,7 +2563,10 @@ static int serial8250_request_port(struct uart_port *port)
 {
 	struct uart_8250_port *up =
 		container_of(port, struct uart_8250_port, port);
-	int ret = 0;
+	int ret;
+
+	if (port->type == PORT_8250_CIR)
+		return -ENODEV;
 
 	ret = serial8250_request_std_resource(up);
 	if (ret == 0 && port->type == PORT_RSA) {
@@ -2569,6 +2585,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
 	int probeflags = PROBE_ANY;
 	int ret;
 
+	if (port->type == PORT_8250_CIR)
+		return;
+
 	/*
 	 * Find the region that we can probe for.  This in turn
 	 * tells us whether we can probe for the type of port.
@@ -2668,6 +2687,9 @@ static void __init serial8250_isa_init_ports(void)
 		return;
 	first = 0;
 
+	if (nr_uarts > UART_NR)
+		nr_uarts = UART_NR;
+
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 		struct uart_port *port = &up->port;
@@ -2677,6 +2699,7 @@ static void __init serial8250_isa_init_ports(void)
 
 		init_timer(&up->timer);
 		up->timer.function = serial8250_timeout;
+		up->cur_iotype = 0xFF;
 
 		/*
 		 * ALPHA_KLUDGE_MCR needs to be killed.
@@ -2728,13 +2751,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
-		up->cur_iotype = 0xFF;
-	}
-
-	serial8250_isa_init_ports();
 
-	for (i = 0; i < nr_uarts; i++) {
-		struct uart_8250_port *up = &serial8250_ports[i];
+		if (up->port.dev)
+			continue;
 
 		up->port.dev = dev;
 
@@ -2859,9 +2878,6 @@ static struct console serial8250_console = {
 
 static int __init serial8250_console_init(void)
 {
-	if (nr_uarts > UART_NR)
-		nr_uarts = UART_NR;
-
 	serial8250_isa_init_ports();
 	register_console(&serial8250_console);
 	return 0;
@@ -2979,36 +2995,36 @@ void serial8250_resume_port(int line)
 static int __devinit serial8250_probe(struct platform_device *dev)
 {
 	struct plat_serial8250_port *p = dev->dev.platform_data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 	if (share_irqs)
 		irqflag = IRQF_SHARED;
 
 	for (i = 0; p && p->flags != 0; p++, i++) {
-		port.iobase		= p->iobase;
-		port.membase		= p->membase;
-		port.irq		= p->irq;
-		port.irqflags		= p->irqflags;
-		port.uartclk		= p->uartclk;
-		port.regshift		= p->regshift;
-		port.iotype		= p->iotype;
-		port.flags		= p->flags;
-		port.mapbase		= p->mapbase;
-		port.hub6		= p->hub6;
-		port.private_data	= p->private_data;
-		port.type		= p->type;
-		port.serial_in		= p->serial_in;
-		port.serial_out		= p->serial_out;
-		port.handle_irq		= p->handle_irq;
-		port.handle_break	= p->handle_break;
-		port.set_termios	= p->set_termios;
-		port.pm			= p->pm;
-		port.dev		= &dev->dev;
-		port.irqflags		|= irqflag;
-		ret = serial8250_register_port(&port);
+		uart.port.iobase	= p->iobase;
+		uart.port.membase	= p->membase;
+		uart.port.irq		= p->irq;
+		uart.port.irqflags	= p->irqflags;
+		uart.port.uartclk	= p->uartclk;
+		uart.port.regshift	= p->regshift;
+		uart.port.iotype	= p->iotype;
+		uart.port.flags		= p->flags;
+		uart.port.mapbase	= p->mapbase;
+		uart.port.hub6		= p->hub6;
+		uart.port.private_data	= p->private_data;
+		uart.port.type		= p->type;
+		uart.port.serial_in	= p->serial_in;
+		uart.port.serial_out	= p->serial_out;
+		uart.port.handle_irq	= p->handle_irq;
+		uart.port.handle_break	= p->handle_break;
+		uart.port.set_termios	= p->set_termios;
+		uart.port.pm		= p->pm;
+		uart.port.dev		= &dev->dev;
+		uart.port.irqflags	|= irqflag;
+		ret = serial8250_register_8250_port(&uart);
 		if (ret < 0) {
 			dev_err(&dev->dev, "unable to register port at index %d "
 				"(IO%lx MEM%llx IRQ%d): %d\n", i,
@@ -3081,7 +3097,7 @@ static struct platform_driver serial8250_isa_driver = {
 static struct platform_device *serial8250_isa_devs;
 
 /*
- * serial8250_register_port and serial8250_unregister_port allows for
+ * serial8250_register_8250_port and serial8250_unregister_port allows for
  * 16x50 serial ports to be configured at run-time, to support PCMCIA
  * modems and PCI multiport cards.
  */
@@ -3143,8 +3159,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 	mutex_lock(&serial_mutex);
 
 	uart = serial8250_find_match_or_unused(&up->port);
-	if (uart) {
-		uart_remove_one_port(&serial8250_reg, &uart->port);
+	if (uart && uart->port.type != PORT_8250_CIR) {
+		if (uart->port.dev)
+			uart_remove_one_port(&serial8250_reg, &uart->port);
 
 		uart->port.iobase       = up->port.iobase;
 		uart->port.membase      = up->port.membase;
@@ -3155,6 +3172,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		uart->port.regshift     = up->port.regshift;
 		uart->port.iotype       = up->port.iotype;
 		uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
+		uart->bugs		= up->bugs;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.private_data = up->port.private_data;
 		if (up->port.dev)
@@ -3197,29 +3215,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 }
 EXPORT_SYMBOL(serial8250_register_8250_port);
 
-/**
- *	serial8250_register_port - register a serial port
- *	@port: serial port template
- *
- *	Configure the serial port specified by the request. If the
- *	port exists and is in use, it is hung up and unregistered
- *	first.
- *
- *	The port is then probed and if necessary the IRQ is autodetected
- *	If this fails an error is returned.
- *
- *	On success the port is ready to use and the line number is returned.
- */
-int serial8250_register_port(struct uart_port *port)
-{
-	struct uart_8250_port up;
-
-	memset(&up, 0, sizeof(up));
-	memcpy(&up.port, port, sizeof(*port));
-	return serial8250_register_8250_port(&up);
-}
-EXPORT_SYMBOL(serial8250_register_port);
-
 /**
  *	serial8250_unregister_port - remove a 16x50 serial port at runtime
  *	@line: serial line number
@@ -3250,8 +3245,7 @@ static int __init serial8250_init(void)
 {
 	int ret;
 
-	if (nr_uarts > UART_NR)
-		nr_uarts = UART_NR;
+	serial8250_isa_init_ports();
 
 	printk(KERN_INFO "Serial: 8250/16550 driver, "
 		"%d ports, IRQ sharing %sabled\n", nr_uarts,
@@ -3266,11 +3260,15 @@ static int __init serial8250_init(void)
 	if (ret)
 		goto out;
 
+	ret = serial8250_pnp_init();
+	if (ret)
+		goto unreg_uart_drv;
+
 	serial8250_isa_devs = platform_device_alloc("serial8250",
 						    PLAT8250_DEV_LEGACY);
 	if (!serial8250_isa_devs) {
 		ret = -ENOMEM;
-		goto unreg_uart_drv;
+		goto unreg_pnp;
 	}
 
 	ret = platform_device_add(serial8250_isa_devs);
@@ -3286,6 +3284,8 @@ static int __init serial8250_init(void)
 	platform_device_del(serial8250_isa_devs);
 put_dev:
 	platform_device_put(serial8250_isa_devs);
+unreg_pnp:
+	serial8250_pnp_exit();
 unreg_uart_drv:
 #ifdef CONFIG_SPARC
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
@@ -3310,6 +3310,8 @@ static void __exit serial8250_exit(void)
 	platform_driver_unregister(&serial8250_isa_driver);
 	platform_device_unregister(isa_dev);
 
+	serial8250_pnp_exit();
+
 #ifdef CONFIG_SPARC
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
 #else

+ 10 - 33
drivers/tty/serial/8250/8250.h

@@ -13,36 +13,6 @@
 
 #include <linux/serial_8250.h>
 
-struct uart_8250_port {
-	struct uart_port	port;
-	struct timer_list	timer;		/* "no irq" timer */
-	struct list_head	list;		/* ports on this IRQ */
-	unsigned short		capabilities;	/* port capabilities */
-	unsigned short		bugs;		/* port bugs */
-	unsigned int		tx_loadsz;	/* transmit fifo load size */
-	unsigned char		acr;
-	unsigned char		ier;
-	unsigned char		lcr;
-	unsigned char		mcr;
-	unsigned char		mcr_mask;	/* mask of user bits */
-	unsigned char		mcr_force;	/* mask of forced bits */
-	unsigned char		cur_iotype;	/* Running I/O type */
-
-	/*
-	 * Some bits in registers are cleared on a read, so they must
-	 * be saved whenever the register is read but the bits will not
-	 * be immediately processed.
-	 */
-#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
-	unsigned char		lsr_saved_flags;
-#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
-	unsigned char		msr_saved_flags;
-
-	/* 8250 specific callbacks */
-	int			(*dl_read)(struct uart_8250_port *);
-	void			(*dl_write)(struct uart_8250_port *, int);
-};
-
 struct old_serial_port {
 	unsigned int uart;
 	unsigned int baud_base;
@@ -56,9 +26,6 @@ struct old_serial_port {
 	unsigned long irqflags;
 };
 
-/*
- * This replaces serial_uart_config in include/linux/serial.h
- */
 struct serial8250_config {
 	const char	*name;
 	unsigned short	fifo_size;
@@ -78,6 +45,7 @@ struct serial8250_config {
 #define UART_BUG_TXEN	(1 << 1)	/* UART has buggy TX IIR status */
 #define UART_BUG_NOMSR	(1 << 2)	/* UART has buggy MSR status bits (Au1x00) */
 #define UART_BUG_THRE	(1 << 3)	/* UART has buggy THRE reassertion */
+#define UART_BUG_PARITY	(1 << 4)	/* UART mishandles parity if FIFO enabled */
 
 #define PROBE_RSA	(1 << 0)
 #define PROBE_ANY	(~0)
@@ -129,3 +97,12 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
 #else
 #define ALPHA_KLUDGE_MCR 0
 #endif
+
+#ifdef CONFIG_SERIAL_8250_PNP
+int serial8250_pnp_init(void);
+void serial8250_pnp_exit(void);
+#else
+static inline int serial8250_pnp_init(void) { return 0; }
+static inline void serial8250_pnp_exit(void) { }
+#endif
+

+ 11 - 11
drivers/tty/serial/8250/8250_acorn.c

@@ -43,7 +43,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
 {
 	struct serial_card_info *info;
 	struct serial_card_type *type = id->data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long bus_addr;
 	unsigned int i;
 
@@ -62,19 +62,19 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
 
 	ecard_set_drvdata(ec, info);
 
-	memset(&port, 0, sizeof(struct uart_port));
-	port.irq	= ec->irq;
-	port.flags	= UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
-	port.uartclk	= type->uartclk;
-	port.iotype	= UPIO_MEM;
-	port.regshift	= 2;
-	port.dev	= &ec->dev;
+	memset(&uart, 0, sizeof(struct uart_8250_port));
+	uart.port.irq	= ec->irq;
+	uart.port.flags	= UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	uart.port.uartclk	= type->uartclk;
+	uart.port.iotype	= UPIO_MEM;
+	uart.port.regshift	= 2;
+	uart.port.dev	= &ec->dev;
 
 	for (i = 0; i < info->num_ports; i ++) {
-		port.membase = info->vaddr + type->offset[i];
-		port.mapbase = bus_addr + type->offset[i];
+		uart.port.membase = info->vaddr + type->offset[i];
+		uart.port.mapbase = bus_addr + type->offset[i];
 
-		info->ports[i] = serial8250_register_port(&port);
+		info->ports[i] = serial8250_register_8250_port(&uart);
 	}
 
 	return 0;

+ 19 - 19
drivers/tty/serial/8250/8250_dw.c

@@ -89,7 +89,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 
 static int __devinit dw8250_probe(struct platform_device *pdev)
 {
-	struct uart_port port = {};
+	struct uart_8250_port uart = {};
 	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct device_node *np = pdev->dev.of_node;
@@ -104,28 +104,28 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-	port.private_data = data;
-
-	spin_lock_init(&port.lock);
-	port.mapbase = regs->start;
-	port.irq = irq->start;
-	port.handle_irq = dw8250_handle_irq;
-	port.type = PORT_8250;
-	port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.irq = irq->start;
+	uart.port.handle_irq = dw8250_handle_irq;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
 		UPF_FIXED_PORT | UPF_FIXED_TYPE;
-	port.dev = &pdev->dev;
+	uart.port.dev = &pdev->dev;
 
-	port.iotype = UPIO_MEM;
-	port.serial_in = dw8250_serial_in;
-	port.serial_out = dw8250_serial_out;
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dw8250_serial_in;
+	uart.port.serial_out = dw8250_serial_out;
 	if (!of_property_read_u32(np, "reg-io-width", &val)) {
 		switch (val) {
 		case 1:
 			break;
 		case 4:
-			port.iotype = UPIO_MEM32;
-			port.serial_in = dw8250_serial_in32;
-			port.serial_out = dw8250_serial_out32;
+			uart.port.iotype = UPIO_MEM32;
+			uart.port.serial_in = dw8250_serial_in32;
+			uart.port.serial_out = dw8250_serial_out32;
 			break;
 		default:
 			dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
@@ -135,15 +135,15 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 	}
 
 	if (!of_property_read_u32(np, "reg-shift", &val))
-		port.regshift = val;
+		uart.port.regshift = val;
 
 	if (of_property_read_u32(np, "clock-frequency", &val)) {
 		dev_err(&pdev->dev, "no clock-frequency property set\n");
 		return -EINVAL;
 	}
-	port.uartclk = val;
+	uart.port.uartclk = val;
 
-	data->line = serial8250_register_port(&port);
+	data->line = serial8250_register_8250_port(&uart);
 	if (data->line < 0)
 		return data->line;
 

+ 13 - 13
drivers/tty/serial/8250/8250_gsc.c

@@ -26,7 +26,7 @@
 
 static int __init serial_init_chip(struct parisc_device *dev)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long address;
 	int err;
 
@@ -48,21 +48,21 @@ static int __init serial_init_chip(struct parisc_device *dev)
 	if (dev->id.sversion != 0x8d)
 		address += 0x800;
 
-	memset(&port, 0, sizeof(port));
-	port.iotype	= UPIO_MEM;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.iotype	= UPIO_MEM;
 	/* 7.272727MHz on Lasi.  Assumed the same for Dino, Wax and Timi. */
-	port.uartclk	= 7272727;
-	port.mapbase	= address;
-	port.membase	= ioremap_nocache(address, 16);
-	port.irq	= dev->irq;
-	port.flags	= UPF_BOOT_AUTOCONF;
-	port.dev	= &dev->dev;
-
-	err = serial8250_register_port(&port);
+	uart.port.uartclk	= 7272727;
+	uart.port.mapbase	= address;
+	uart.port.membase	= ioremap_nocache(address, 16);
+	uart.port.irq	= dev->irq;
+	uart.port.flags	= UPF_BOOT_AUTOCONF;
+	uart.port.dev	= &dev->dev;
+
+	err = serial8250_register_8250_port(&uart);
 	if (err < 0) {
 		printk(KERN_WARNING
-			"serial8250_register_port returned error %d\n", err);
-		iounmap(port.membase);
+			"serial8250_register_8250_port returned error %d\n", err);
+		iounmap(uart.port.membase);
 		return err;
 	}
 

+ 13 - 13
drivers/tty/serial/8250/8250_hp300.c

@@ -171,7 +171,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d,
 		return 0;
 	}
 #endif
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 	/* Memory mapped I/O */
 	port.iotype = UPIO_MEM;
@@ -182,7 +182,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d,
 	port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
 	port.regshift = 1;
 	port.dev = &d->dev;
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 
 	if (line < 0) {
 		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
@@ -210,7 +210,7 @@ static int __init hp300_8250_init(void)
 #ifdef CONFIG_HPAPCI
 	int line;
 	unsigned long base;
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	struct hp300_port *port;
 	int i;
 #endif
@@ -248,26 +248,26 @@ static int __init hp300_8250_init(void)
 		if (!port)
 			return -ENOMEM;
 
-		memset(&uport, 0, sizeof(struct uart_port));
+		memset(&uart, 0, sizeof(uart));
 
 		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
 
 		/* Memory mapped I/O */
-		uport.iotype = UPIO_MEM;
-		uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
+		uart.port.iotype = UPIO_MEM;
+		uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
 			      | UPF_BOOT_AUTOCONF;
 		/* XXX - no interrupt support yet */
-		uport.irq = 0;
-		uport.uartclk = HPAPCI_BAUD_BASE * 16;
-		uport.mapbase = base;
-		uport.membase = (char *)(base + DIO_VIRADDRBASE);
-		uport.regshift = 2;
+		uart.port.irq = 0;
+		uart.port.uartclk = HPAPCI_BAUD_BASE * 16;
+		uart.port.mapbase = base;
+		uart.port.membase = (char *)(base + DIO_VIRADDRBASE);
+		uart.port.regshift = 2;
 
-		line = serial8250_register_port(&uport);
+		line = serial8250_register_8250_port(&uart);
 
 		if (line < 0) {
 			printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
-			       " %d irq %d failed\n", i, uport.irq);
+			       " %d irq %d failed\n", i, uart.port.irq);
 			kfree(port);
 			continue;
 		}

+ 146 - 68
drivers/tty/serial/8250/8250_pci.c

@@ -44,7 +44,7 @@ struct pci_serial_quirk {
 	int	(*init)(struct pci_dev *dev);
 	int	(*setup)(struct serial_private *,
 			 const struct pciserial_board *,
-			 struct uart_port *, int);
+			 struct uart_8250_port *, int);
 	void	(*exit)(struct pci_dev *dev);
 };
 
@@ -59,7 +59,7 @@ struct serial_private {
 };
 
 static int pci_default_setup(struct serial_private*,
-	  const struct pciserial_board*, struct uart_port*, int);
+	  const struct pciserial_board*, struct uart_8250_port *, int);
 
 static void moan_device(const char *str, struct pci_dev *dev)
 {
@@ -74,7 +74,7 @@ static void moan_device(const char *str, struct pci_dev *dev)
 }
 
 static int
-setup_port(struct serial_private *priv, struct uart_port *port,
+setup_port(struct serial_private *priv, struct uart_8250_port *port,
 	   int bar, int offset, int regshift)
 {
 	struct pci_dev *dev = priv->dev;
@@ -93,17 +93,17 @@ setup_port(struct serial_private *priv, struct uart_port *port,
 		if (!priv->remapped_bar[bar])
 			return -ENOMEM;
 
-		port->iotype = UPIO_MEM;
-		port->iobase = 0;
-		port->mapbase = base + offset;
-		port->membase = priv->remapped_bar[bar] + offset;
-		port->regshift = regshift;
+		port->port.iotype = UPIO_MEM;
+		port->port.iobase = 0;
+		port->port.mapbase = base + offset;
+		port->port.membase = priv->remapped_bar[bar] + offset;
+		port->port.regshift = regshift;
 	} else {
-		port->iotype = UPIO_PORT;
-		port->iobase = base + offset;
-		port->mapbase = 0;
-		port->membase = NULL;
-		port->regshift = 0;
+		port->port.iotype = UPIO_PORT;
+		port->port.iobase = base + offset;
+		port->port.mapbase = 0;
+		port->port.membase = NULL;
+		port->port.regshift = 0;
 	}
 	return 0;
 }
@@ -113,7 +113,7 @@ setup_port(struct serial_private *priv, struct uart_port *port,
  */
 static int addidata_apci7800_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
-				struct uart_port *port, int idx)
+				struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = 0, offset = board->first_offset;
 	bar = FL_GET_BASE(board->flags);
@@ -140,7 +140,7 @@ static int addidata_apci7800_setup(struct serial_private *priv,
  */
 static int
 afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
-	      struct uart_port *port, int idx)
+	      struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -195,7 +195,7 @@ static int pci_hp_diva_init(struct pci_dev *dev)
 static int
 pci_hp_diva_setup(struct serial_private *priv,
 		const struct pciserial_board *board,
-		struct uart_port *port, int idx)
+		struct uart_8250_port *port, int idx)
 {
 	unsigned int offset = board->first_offset;
 	unsigned int bar = FL_GET_BASE(board->flags);
@@ -370,7 +370,7 @@ static void __devexit pci_ni8430_exit(struct pci_dev *dev)
 /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
 static int
 sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
-		struct uart_port *port, int idx)
+		struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -525,7 +525,7 @@ static int pci_siig_init(struct pci_dev *dev)
 
 static int pci_siig_setup(struct serial_private *priv,
 			  const struct pciserial_board *board,
-			  struct uart_port *port, int idx)
+			  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0;
 
@@ -619,7 +619,7 @@ static int pci_timedia_init(struct pci_dev *dev)
 static int
 pci_timedia_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = 0, offset = board->first_offset;
 
@@ -653,7 +653,7 @@ pci_timedia_setup(struct serial_private *priv,
 static int
 titan_400l_800l_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
-		      struct uart_port *port, int idx)
+		      struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -754,7 +754,7 @@ static int pci_ni8430_init(struct pci_dev *dev)
 static int
 pci_ni8430_setup(struct serial_private *priv,
 		 const struct pciserial_board *board,
-		 struct uart_port *port, int idx)
+		 struct uart_8250_port *port, int idx)
 {
 	void __iomem *p;
 	unsigned long base, len;
@@ -781,7 +781,7 @@ pci_ni8430_setup(struct serial_private *priv,
 
 static int pci_netmos_9900_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
-				struct uart_port *port, int idx)
+				struct uart_8250_port *port, int idx)
 {
 	unsigned int bar;
 
@@ -1032,10 +1032,17 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
 	return number_uarts;
 }
 
-static int
-pci_default_setup(struct serial_private *priv,
+static int pci_asix_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_8250_port *port, int idx)
+{
+	port->bugs |= UART_BUG_PARITY;
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static int pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset, maxnr;
 
@@ -1057,15 +1064,15 @@ pci_default_setup(struct serial_private *priv,
 static int
 ce4100_serial_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	int ret;
 
 	ret = setup_port(priv, port, 0, 0, board->reg_shift);
-	port->iotype = UPIO_MEM32;
-	port->type = PORT_XSCALE;
-	port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
-	port->regshift = 2;
+	port->port.iotype = UPIO_MEM32;
+	port->port.type = PORT_XSCALE;
+	port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+	port->port.regshift = 2;
 
 	return ret;
 }
@@ -1073,16 +1080,16 @@ ce4100_serial_setup(struct serial_private *priv,
 static int
 pci_omegapci_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
-		      struct uart_port *port, int idx)
+		      struct uart_8250_port *port, int idx)
 {
 	return setup_port(priv, port, 2, idx * 8, 0);
 }
 
 static int skip_tx_en_setup(struct serial_private *priv,
 			const struct pciserial_board *board,
-			struct uart_port *port, int idx)
+			struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_NO_TXEN_TEST;
+	port->port.flags |= UPF_NO_TXEN_TEST;
 	printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
 			  "[%04x:%04x] subsystem [%04x:%04x]\n",
 			  priv->dev->vendor,
@@ -1131,11 +1138,11 @@ static unsigned int kt_serial_in(struct uart_port *p, int offset)
 
 static int kt_serial_setup(struct serial_private *priv,
 			   const struct pciserial_board *board,
-			   struct uart_port *port, int idx)
+			   struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_BUG_THRE;
-	port->serial_in = kt_serial_in;
-	port->handle_break = kt_handle_break;
+	port->port.flags |= UPF_BUG_THRE;
+	port->port.serial_in = kt_serial_in;
+	port->port.handle_break = kt_handle_break;
 	return skip_tx_en_setup(priv, board, port, idx);
 }
 
@@ -1151,9 +1158,19 @@ static int pci_eg20t_init(struct pci_dev *dev)
 static int
 pci_xr17c154_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_EXAR_EFR;
+	port->port.flags |= UPF_EXAR_EFR;
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static int
+pci_wch_ch353_setup(struct serial_private *priv,
+                    const struct pciserial_board *board,
+                    struct uart_8250_port *port, int idx)
+{
+	port->port.flags |= UPF_FIXED_TYPE;
+	port->port.type = PORT_16550A;
 	return pci_default_setup(priv, board, port, idx);
 }
 
@@ -1164,6 +1181,8 @@ pci_xr17c154_setup(struct serial_private *priv,
 #define PCI_SUBDEVICE_ID_OCTPRO422	0x0208
 #define PCI_SUBDEVICE_ID_POCTAL232	0x0308
 #define PCI_SUBDEVICE_ID_POCTAL422	0x0408
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_00	0x2500
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_30	0x2530
 #define PCI_VENDOR_ID_ADVANTECH		0x13fe
 #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
 #define PCI_DEVICE_ID_ADVANTECH_PCI3620	0x3620
@@ -1187,6 +1206,13 @@ pci_xr17c154_setup(struct serial_private *priv,
 #define PCIE_DEVICE_ID_NEO_2_OX_IBM	0x00F6
 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001
 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
+#define PCI_VENDOR_ID_WCH		0x4348
+#define PCI_DEVICE_ID_WCH_CH353_4S	0x3453
+#define PCI_DEVICE_ID_WCH_CH353_2S1PF	0x5046
+#define PCI_DEVICE_ID_WCH_CH353_2S1P	0x7053
+#define PCI_VENDOR_ID_AGESTAR		0x5372
+#define PCI_DEVICE_ID_AGESTAR_9375	0x6872
+#define PCI_VENDOR_ID_ASIX		0x9710
 
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
@@ -1726,7 +1752,41 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.subvendor	= PCI_ANY_ID,
 		.subdevice	= PCI_ANY_ID,
 		.setup		= pci_omegapci_setup,
-	 },
+	},
+	/* WCH CH353 2S1P card (16550 clone) */
+	{
+		.vendor         = PCI_VENDOR_ID_WCH,
+		.device         = PCI_DEVICE_ID_WCH_CH353_2S1P,
+		.subvendor      = PCI_ANY_ID,
+		.subdevice      = PCI_ANY_ID,
+		.setup          = pci_wch_ch353_setup,
+	},
+	/* WCH CH353 4S card (16550 clone) */
+	{
+		.vendor         = PCI_VENDOR_ID_WCH,
+		.device         = PCI_DEVICE_ID_WCH_CH353_4S,
+		.subvendor      = PCI_ANY_ID,
+		.subdevice      = PCI_ANY_ID,
+		.setup          = pci_wch_ch353_setup,
+	},
+	/* WCH CH353 2S1PF card (16550 clone) */
+	{
+		.vendor         = PCI_VENDOR_ID_WCH,
+		.device         = PCI_DEVICE_ID_WCH_CH353_2S1PF,
+		.subvendor      = PCI_ANY_ID,
+		.subdevice      = PCI_ANY_ID,
+		.setup          = pci_wch_ch353_setup,
+	},
+	/*
+	 * ASIX devices with FIFO bug
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_ASIX,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_asix_setup,
+	},
 	/*
 	 * Default "match everything" terminator entry
 	 */
@@ -1887,7 +1947,6 @@ enum pci_board_num_t {
 	pbn_panacom,
 	pbn_panacom2,
 	pbn_panacom4,
-	pbn_exsys_4055,
 	pbn_plx_romulus,
 	pbn_oxsemi,
 	pbn_oxsemi_1_4000000,
@@ -2393,13 +2452,6 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 		.reg_shift	= 7,
 	},
 
-	[pbn_exsys_4055] = {
-		.flags		= FL_BASE2,
-		.num_ports	= 4,
-		.base_baud	= 115200,
-		.uart_offset	= 8,
-	},
-
 	/* I think this entry is broken - the first_offset looks wrong --rmk */
 	[pbn_plx_romulus] = {
 		.flags		= FL_BASE2,
@@ -2624,10 +2676,14 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 	},
 };
 
-static const struct pci_device_id softmodem_blacklist[] = {
+static const struct pci_device_id blacklist[] = {
+	/* softmodems */
 	{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
 	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
 	{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
+
+	/* multi-io cards handled by parport_serial */
+	{ PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
 };
 
 /*
@@ -2638,7 +2694,7 @@ static const struct pci_device_id softmodem_blacklist[] = {
 static int __devinit
 serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 {
-	const struct pci_device_id *blacklist;
+	const struct pci_device_id *bldev;
 	int num_iomem, num_port, first_port = -1, i;
 
 	/*
@@ -2655,13 +2711,13 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 
 	/*
 	 * Do not access blacklisted devices that are known not to
-	 * feature serial ports.
+	 * feature serial ports or are handled by other modules.
 	 */
-	for (blacklist = softmodem_blacklist;
-	     blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist);
-	     blacklist++) {
-		if (dev->vendor == blacklist->vendor &&
-		    dev->device == blacklist->device)
+	for (bldev = blacklist;
+	     bldev < blacklist + ARRAY_SIZE(blacklist);
+	     bldev++) {
+		if (dev->vendor == bldev->vendor &&
+		    dev->device == bldev->device)
 			return -ENODEV;
 	}
 
@@ -2728,7 +2784,7 @@ serial_pci_matches(const struct pciserial_board *board,
 struct serial_private *
 pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
 {
-	struct uart_port serial_port;
+	struct uart_8250_port uart;
 	struct serial_private *priv;
 	struct pci_serial_quirk *quirk;
 	int rc, nr_ports, i;
@@ -2768,22 +2824,22 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
 	priv->dev = dev;
 	priv->quirk = quirk;
 
-	memset(&serial_port, 0, sizeof(struct uart_port));
-	serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
-	serial_port.uartclk = board->base_baud * 16;
-	serial_port.irq = get_pci_irq(dev, board);
-	serial_port.dev = &dev->dev;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	uart.port.uartclk = board->base_baud * 16;
+	uart.port.irq = get_pci_irq(dev, board);
+	uart.port.dev = &dev->dev;
 
 	for (i = 0; i < nr_ports; i++) {
-		if (quirk->setup(priv, board, &serial_port, i))
+		if (quirk->setup(priv, board, &uart, i))
 			break;
 
 #ifdef SERIAL_DEBUG_PCI
 		printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
-		       serial_port.iobase, serial_port.irq, serial_port.iotype);
+		       uart.port.iobase, uart.port.irq, uart.port.iotype);
 #endif
 
-		priv->line[i] = serial8250_register_port(&serial_port);
+		priv->line[i] = serial8250_register_8250_port(&uart);
 		if (priv->line[i] < 0) {
 			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
 			break;
@@ -3193,7 +3249,7 @@ static struct pci_device_id serial_pci_tbl[] = {
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
 		PCI_SUBVENDOR_ID_EXSYS,
 		PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
-		pbn_exsys_4055 },
+		pbn_b2_4_115200 },
 	/*
 	 * Megawolf Romulus PCI Serial Card, from Mike Hudson
 	 * (Exoray@isys.ca)
@@ -3232,8 +3288,11 @@ static struct pci_device_id serial_pci_tbl[] = {
 		 * For now just used the hex ID 0x950a.
 		 */
 	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
-		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0,
-		pbn_b0_2_115200 },
+		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00,
+		0, 0, pbn_b0_2_115200 },
+	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
+		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30,
+		0, 0, pbn_b0_2_115200 },
 	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b0_2_1130000 },
@@ -4178,6 +4237,25 @@ static struct pci_device_id serial_pci_tbl[] = {
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_omegapci },
 
+	/*
+	 * AgeStar as-prs2-009
+	 */
+	{	PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, pbn_b0_bt_2_115200 },
+
+	/*
+	 * WCH CH353 series devices: The 2S1P is handled by parport_serial
+	 * so not listed here.
+	 */
+	{	PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_4S,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, pbn_b0_bt_4_115200 },
+
+	{	PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_2S1PF,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, pbn_b0_bt_2_115200 },
+
 	/*
 	 * These entries match devices with class COMMUNICATION_SERIAL,
 	 * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL

+ 32 - 27
drivers/tty/serial/8250/8250_pnp.c

@@ -1,5 +1,5 @@
 /*
- *  Probe module for 8250/16550-type ISAPNP serial ports.
+ *  Probe for 8250/16550-type ISAPNP serial ports.
  *
  *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
  *
@@ -25,7 +25,7 @@
 #include "8250.h"
 
 #define UNKNOWN_DEV 0x3000
-
+#define CIR_PORT	0x0800
 
 static const struct pnp_device_id pnp_dev_table[] = {
 	/* Archtek America Corp. */
@@ -362,6 +362,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
 	{	"PNPCXXX",		UNKNOWN_DEV	},
 	/* More unknown PnP modems */
 	{	"PNPDXXX",		UNKNOWN_DEV	},
+	/* Winbond CIR port, should not be probed. We should keep track
+	   of it to prevent the legacy serial driver from probing it */
+	{	"WEC1022",		CIR_PORT	},
 	{	"",			0	}
 };
 
@@ -409,7 +412,7 @@ static int __devinit check_resources(struct pnp_dev *dev)
  * PnP modems, alternatively we must hardcode all modems in pnp_devices[]
  * table.
  */
-static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
+static int __devinit serial_pnp_guess_board(struct pnp_dev *dev)
 {
 	if (!(check_name(pnp_dev_name(dev)) ||
 		(dev->card && check_name(dev->card->name))))
@@ -424,42 +427,49 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
 static int __devinit
 serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int ret, line, flags = dev_id->driver_data;
 
 	if (flags & UNKNOWN_DEV) {
-		ret = serial_pnp_guess_board(dev, &flags);
+		ret = serial_pnp_guess_board(dev);
 		if (ret < 0)
 			return ret;
 	}
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 	if (pnp_irq_valid(dev, 0))
-		port.irq = pnp_irq(dev, 0);
-	if (pnp_port_valid(dev, 0)) {
-		port.iobase = pnp_port_start(dev, 0);
-		port.iotype = UPIO_PORT;
+		uart.port.irq = pnp_irq(dev, 0);
+	if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
+		uart.port.iobase = pnp_port_start(dev, 2);
+		uart.port.iotype = UPIO_PORT;
+	} else if (pnp_port_valid(dev, 0)) {
+		uart.port.iobase = pnp_port_start(dev, 0);
+		uart.port.iotype = UPIO_PORT;
 	} else if (pnp_mem_valid(dev, 0)) {
-		port.mapbase = pnp_mem_start(dev, 0);
-		port.iotype = UPIO_MEM;
-		port.flags = UPF_IOREMAP;
+		uart.port.mapbase = pnp_mem_start(dev, 0);
+		uart.port.iotype = UPIO_MEM;
+		uart.port.flags = UPF_IOREMAP;
 	} else
 		return -ENODEV;
 
 #ifdef SERIAL_DEBUG_PNP
 	printk(KERN_DEBUG
 		"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
-		       port.iobase, port.mapbase, port.irq, port.iotype);
+		       uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype);
 #endif
+	if (flags & CIR_PORT) {
+		uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
+		uart.port.type = PORT_8250_CIR;
+	}
 
-	port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
 	if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
-		port.flags |= UPF_SHARE_IRQ;
-	port.uartclk = 1843200;
-	port.dev = &dev->dev;
+		uart.port.flags |= UPF_SHARE_IRQ;
+	uart.port.uartclk = 1843200;
+	uart.port.dev = &dev->dev;
 
-	line = serial8250_register_port(&port);
-	if (line < 0)
+	line = serial8250_register_8250_port(&uart);
+	if (line < 0 || (flags & CIR_PORT))
 		return -ENODEV;
 
 	pnp_set_drvdata(dev, (void *)((long)line + 1));
@@ -507,18 +517,13 @@ static struct pnp_driver serial_pnp_driver = {
 	.id_table	= pnp_dev_table,
 };
 
-static int __init serial8250_pnp_init(void)
+int serial8250_pnp_init(void)
 {
 	return pnp_register_driver(&serial_pnp_driver);
 }
 
-static void __exit serial8250_pnp_exit(void)
+void serial8250_pnp_exit(void)
 {
 	pnp_unregister_driver(&serial_pnp_driver);
 }
 
-module_init(serial8250_pnp_init);
-module_exit(serial8250_pnp_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");

+ 8 - 8
drivers/tty/serial/8250/Kconfig

@@ -33,6 +33,14 @@ config SERIAL_8250
 	  Most people will say Y or M here, so that they can use serial mice,
 	  modems and similar devices connecting to the standard serial ports.
 
+config SERIAL_8250_PNP
+	bool "8250/16550 PNP device support" if EXPERT
+	depends on SERIAL_8250 && PNP
+	default y
+	---help---
+	  This builds standard PNP serial support. You may be able to
+	  disable this feature if you only need legacy serial support.
+
 config SERIAL_8250_CONSOLE
 	bool "Console on 8250/16550 and compatible serial port"
 	depends on SERIAL_8250=y
@@ -85,14 +93,6 @@ config SERIAL_8250_PCI
 	  disable this feature if you only need legacy serial support.
 	  Saves about 9K.
 
-config SERIAL_8250_PNP
-	tristate "8250/16550 PNP device support" if EXPERT
-	depends on SERIAL_8250 && PNP
-	default SERIAL_8250
-	help
-	  This builds standard PNP serial support. You may be able to
-	  disable this feature if you only need legacy serial support.
-
 config SERIAL_8250_HP300
 	tristate
 	depends on SERIAL_8250 && HP300

+ 3 - 2
drivers/tty/serial/8250/Makefile

@@ -2,8 +2,9 @@
 # Makefile for the 8250 serial device drivers.
 #
 
-obj-$(CONFIG_SERIAL_8250)		+= 8250.o
-obj-$(CONFIG_SERIAL_8250_PNP)		+= 8250_pnp.o
+obj-$(CONFIG_SERIAL_8250)		+= 8250_core.o
+8250_core-y				:= 8250.o
+8250_core-$(CONFIG_SERIAL_8250_PNP)	+= 8250_pnp.o
 obj-$(CONFIG_SERIAL_8250_GSC)		+= 8250_gsc.o
 obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o
 obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.o

+ 15 - 15
drivers/tty/serial/8250/serial_cs.c

@@ -73,7 +73,7 @@ struct serial_quirk {
 	unsigned int prodid;
 	int multi;		/* 1 = multifunction, > 1 = # ports */
 	void (*config)(struct pcmcia_device *);
-	void (*setup)(struct pcmcia_device *, struct uart_port *);
+	void (*setup)(struct pcmcia_device *, struct uart_8250_port *);
 	void (*wakeup)(struct pcmcia_device *);
 	int (*post)(struct pcmcia_device *);
 };
@@ -105,9 +105,9 @@ struct serial_cfg_mem {
  * Elan VPU16551 UART with 14.7456MHz oscillator
  * manfid 0x015D, 0x4C45
  */
-static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port)
+static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart)
 {
-	port->uartclk = 14745600;
+	uart->port.uartclk = 14745600;
 }
 
 static int quirk_post_ibm(struct pcmcia_device *link)
@@ -343,25 +343,25 @@ static void serial_detach(struct pcmcia_device *link)
 static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
 			unsigned int iobase, int irq)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int line;
 
-	memset(&port, 0, sizeof (struct uart_port));
-	port.iobase = iobase;
-	port.irq = irq;
-	port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
-	port.uartclk = 1843200;
-	port.dev = &handle->dev;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.iobase = iobase;
+	uart.port.irq = irq;
+	uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
+	uart.port.uartclk = 1843200;
+	uart.port.dev = &handle->dev;
 	if (buggy_uart)
-		port.flags |= UPF_BUGGY_UART;
+		uart.port.flags |= UPF_BUGGY_UART;
 
 	if (info->quirk && info->quirk->setup)
-		info->quirk->setup(handle, &port);
+		info->quirk->setup(handle, &uart);
 
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 	if (line < 0) {
-		printk(KERN_NOTICE "serial_cs: serial8250_register_port() at "
-		       "0x%04lx, irq %d failed\n", (u_long)iobase, irq);
+		pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n",
+							(unsigned long)iobase, irq);
 		return -EINVAL;
 	}
 

+ 68 - 5
drivers/tty/serial/Kconfig

@@ -141,6 +141,25 @@ config SERIAL_ATMEL_TTYAT
 
 	  Say Y if you have an external 8250/16C550 UART.  If unsure, say N.
 
+config SERIAL_KGDB_NMI
+	bool "Serial console over KGDB NMI debugger port"
+	depends on KGDB_SERIAL_CONSOLE
+	help
+	  This special driver allows you to temporary use NMI debugger port
+	  as a normal console (assuming that the port is attached to KGDB).
+
+	  Unlike KDB's disable_nmi command, with this driver you are always
+	  able to go back to the debugger using KGDB escape sequence ($3#33).
+	  This is because this console driver processes the input in NMI
+	  context, and thus is able to intercept the magic sequence.
+
+	  Note that since the console interprets input and uses polling
+	  communication methods, for things like PPP you still must fully
+	  detach debugger port from the KGDB NMI (i.e. disable_nmi), and
+	  use raw console.
+
+	  If unsure, say N.
+
 config SERIAL_KS8695
 	bool "Micrel KS8695 (Centaur) serial port support"
 	depends on ARCH_KS8695
@@ -257,12 +276,19 @@ config SERIAL_MAX3100
 	help
 	  MAX3100 chip support
 
-config SERIAL_MAX3107
-	tristate "MAX3107 support"
+config SERIAL_MAX310X
+	bool "MAX310X support"
 	depends on SPI
 	select SERIAL_CORE
+	select REGMAP_SPI if SPI
+	default n
 	help
-	  MAX3107 chip support
+	  This selects support for an advanced UART from Maxim (Dallas).
+	  Supported ICs are MAX3107, MAX3108.
+	  Each IC contains 128 words each of receive and transmit FIFO
+	  that can be controlled through I2C or high-speed SPI.
+
+	  Say Y here if you want to support this ICs.
 
 config SERIAL_DZ
 	bool "DECstation DZ serial driver"
@@ -686,7 +712,7 @@ config SERIAL_SH_SCI_CONSOLE
 
 config SERIAL_SH_SCI_DMA
 	bool "DMA support"
-	depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+	depends on SERIAL_SH_SCI && SH_DMAE
 
 config SERIAL_PNX8XXX
 	bool "Enable PNX8XXX SoCs' UART Support"
@@ -704,6 +730,25 @@ config SERIAL_PNX8XXX_CONSOLE
 	  If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330
 	  and you want to use serial console, say Y. Otherwise, say N.
 
+config SERIAL_HS_LPC32XX
+	tristate "LPC32XX high speed serial port support"
+	depends on ARCH_LPC32XX && OF
+	select SERIAL_CORE
+	help
+	  Support for the LPC32XX high speed serial ports (up to 900kbps).
+	  Those are UARTs completely different from the Standard UARTs on the
+	  LPC32XX SoC.
+	  Choose M or Y here to build this driver.
+
+config SERIAL_HS_LPC32XX_CONSOLE
+	bool "Enable LPC32XX high speed UART serial console"
+	depends on SERIAL_HS_LPC32XX
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you would like to be able to use one of the high speed serial
+	  ports on the LPC32XX as the console, you can do so by answering
+	  Y to this option.
+
 config SERIAL_CORE
 	tristate
 
@@ -1104,6 +1149,24 @@ config SERIAL_SC26XX_CONSOLE
 	help
 	  Support for Console on SC2681/SC2692 serial ports.
 
+config SERIAL_SCCNXP
+	bool "SCCNXP serial port support"
+	depends on !SERIAL_SC26XX
+	select SERIAL_CORE
+	default n
+	help
+	  This selects support for an advanced UART from NXP (Philips).
+	  Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92,
+	  SC28L202, SCC68681 and SCC68692.
+	  Positioned as a replacement for the driver SC26XX.
+
+config SERIAL_SCCNXP_CONSOLE
+	bool "Console on SCCNXP serial port"
+	depends on SERIAL_SCCNXP
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for console on SCCNXP serial ports.
+
 config SERIAL_BFIN_SPORT
 	tristate "Blackfin SPORT emulate UART"
 	depends on BLACKFIN
@@ -1260,7 +1323,7 @@ config SERIAL_ALTERA_UART_CONSOLE
 
 config SERIAL_IFX6X60
         tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
-	depends on GPIOLIB && SPI && EXPERIMENTAL
+	depends on GPIOLIB && SPI
 	help
 	  Support for the IFX6x60 modem devices on Intel MID platforms.
 

+ 4 - 1
drivers/tty/serial/Makefile

@@ -28,12 +28,13 @@ obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
 obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
 obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
-obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX310X) += max310x.o
 obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
 obj-$(CONFIG_SERIAL_MCF) += mcf.o
 obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
+obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o
 obj-$(CONFIG_SERIAL_DZ) += dz.o
 obj-$(CONFIG_SERIAL_ZS) += zs.o
 obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
+obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
@@ -59,6 +61,7 @@ obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
+obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o

+ 3 - 3
drivers/tty/serial/altera_uart.c

@@ -591,7 +591,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
 	port->ops = &altera_uart_ops;
 	port->flags = UPF_BOOT_AUTOCONF;
 
-	dev_set_drvdata(&pdev->dev, port);
+	platform_set_drvdata(pdev, port);
 
 	uart_add_one_port(&altera_uart_driver, port);
 
@@ -600,11 +600,11 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
 
 static int __devexit altera_uart_remove(struct platform_device *pdev)
 {
-	struct uart_port *port = dev_get_drvdata(&pdev->dev);
+	struct uart_port *port = platform_get_drvdata(pdev);
 
 	if (port) {
 		uart_remove_one_port(&altera_uart_driver, port);
-		dev_set_drvdata(&pdev->dev, NULL);
+		platform_set_drvdata(pdev, NULL);
 		port->mapbase = 0;
 	}
 

+ 4 - 11
drivers/tty/serial/amba-pl010.c

@@ -312,16 +312,12 @@ static int pl010_startup(struct uart_port *port)
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	int retval;
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	 * Try to enable the clock producer.
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 
@@ -346,9 +342,7 @@ static int pl010_startup(struct uart_port *port)
 	return 0;
 
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
  out:
 	return retval;
 }
@@ -375,8 +369,7 @@ static void pl010_shutdown(struct uart_port *port)
 	/*
 	 * Shut down the clock producer
 	 */
-	clk_disable(uap->clk);
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
 }
 
 static void

+ 133 - 44
drivers/tty/serial/amba-pl011.c

@@ -52,6 +52,8 @@
 #include <linux/scatterlist.h>
 #include <linux/delay.h>
 #include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/sizes.h>
 
@@ -75,7 +77,6 @@ struct vendor_data {
 	unsigned int		lcrh_tx;
 	unsigned int		lcrh_rx;
 	bool			oversampling;
-	bool			interrupt_may_hang;   /* vendor-specific */
 	bool			dma_threshold;
 	bool			cts_event_workaround;
 };
@@ -96,7 +97,6 @@ static struct vendor_data vendor_st = {
 	.lcrh_tx		= ST_UART011_LCRH_TX,
 	.lcrh_rx		= ST_UART011_LCRH_RX,
 	.oversampling		= true,
-	.interrupt_may_hang	= true,
 	.dma_threshold		= true,
 	.cts_event_workaround	= true,
 };
@@ -147,7 +147,6 @@ struct uart_amba_port {
 	unsigned int		old_cr;		/* state during shutdown */
 	bool			autorts;
 	char			type[12];
-	bool			interrupt_may_hang; /* vendor-specific */
 #ifdef CONFIG_DMA_ENGINE
 	/* DMA stuff */
 	bool			using_tx_dma;
@@ -1215,14 +1214,14 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
 	return IRQ_RETVAL(handled);
 }
 
-static unsigned int pl01x_tx_empty(struct uart_port *port)
+static unsigned int pl011_tx_empty(struct uart_port *port)
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int status = readw(uap->port.membase + UART01x_FR);
 	return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
 }
 
-static unsigned int pl01x_get_mctrl(struct uart_port *port)
+static unsigned int pl011_get_mctrl(struct uart_port *port)
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int result = 0;
@@ -1285,11 +1284,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
 }
 
 #ifdef CONFIG_CONSOLE_POLL
-static int pl010_get_poll_char(struct uart_port *port)
+
+static void pl011_quiesce_irqs(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned char __iomem *regs = uap->port.membase;
+
+	writew(readw(regs + UART011_MIS), regs + UART011_ICR);
+	/*
+	 * There is no way to clear TXIM as this is "ready to transmit IRQ", so
+	 * we simply mask it. start_tx() will unmask it.
+	 *
+	 * Note we can race with start_tx(), and if the race happens, the
+	 * polling user might get another interrupt just after we clear it.
+	 * But it should be OK and can happen even w/o the race, e.g.
+	 * controller immediately got some new data and raised the IRQ.
+	 *
+	 * And whoever uses polling routines assumes that it manages the device
+	 * (including tx queue), so we're also fine with start_tx()'s caller
+	 * side.
+	 */
+	writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC);
+}
+
+static int pl011_get_poll_char(struct uart_port *port)
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int status;
 
+	/*
+	 * The caller might need IRQs lowered, e.g. if used with KDB NMI
+	 * debugger.
+	 */
+	pl011_quiesce_irqs(port);
+
 	status = readw(uap->port.membase + UART01x_FR);
 	if (status & UART01x_FR_RXFE)
 		return NO_POLL_CHAR;
@@ -1297,7 +1325,7 @@ static int pl010_get_poll_char(struct uart_port *port)
 	return readw(uap->port.membase + UART01x_DR);
 }
 
-static void pl010_put_poll_char(struct uart_port *port,
+static void pl011_put_poll_char(struct uart_port *port,
 			 unsigned char ch)
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -1310,10 +1338,9 @@ static void pl010_put_poll_char(struct uart_port *port,
 
 #endif /* CONFIG_CONSOLE_POLL */
 
-static int pl011_startup(struct uart_port *port)
+static int pl011_hwinit(struct uart_port *port)
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
-	unsigned int cr;
 	int retval;
 
 	/* Optionaly enable pins to be muxed in and configured */
@@ -1324,16 +1351,12 @@ static int pl011_startup(struct uart_port *port)
 				"could not set default pins\n");
 	}
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	 * Try to enable the clock producer.
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 
@@ -1341,6 +1364,37 @@ static int pl011_startup(struct uart_port *port)
 	writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
 	       UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
 
+	/*
+	 * Save interrupts enable mask, and enable RX interrupts in case if
+	 * the interrupt is used for NMI entry.
+	 */
+	uap->im = readw(uap->port.membase + UART011_IMSC);
+	writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
+
+	if (uap->port.dev->platform_data) {
+		struct amba_pl011_data *plat;
+
+		plat = uap->port.dev->platform_data;
+		if (plat->init)
+			plat->init();
+	}
+	return 0;
+ out:
+	return retval;
+}
+
+static int pl011_startup(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+	int retval;
+
+	retval = pl011_hwinit(port);
+	if (retval)
+		goto clk_dis;
+
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+
 	/*
 	 * Allocate the IRQ
 	 */
@@ -1400,21 +1454,10 @@ static int pl011_startup(struct uart_port *port)
 	writew(uap->im, uap->port.membase + UART011_IMSC);
 	spin_unlock_irq(&uap->port.lock);
 
-	if (uap->port.dev->platform_data) {
-		struct amba_pl011_data *plat;
-
-		plat = uap->port.dev->platform_data;
-		if (plat->init)
-			plat->init();
-	}
-
 	return 0;
 
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
- out:
+	clk_disable_unprepare(uap->clk);
 	return retval;
 }
 
@@ -1473,8 +1516,7 @@ static void pl011_shutdown(struct uart_port *port)
 	/*
 	 * Shut down the clock producer
 	 */
-	clk_disable(uap->clk);
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
 	/* Optionally let pins go into sleep states */
 	if (!IS_ERR(uap->pins_sleep)) {
 		retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep);
@@ -1603,13 +1645,26 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
 			old_cr &= ~ST_UART011_CR_OVSFACT;
 	}
 
+	/*
+	 * Workaround for the ST Micro oversampling variants to
+	 * increase the bitrate slightly, by lowering the divisor,
+	 * to avoid delayed sampling of start bit at high speeds,
+	 * else we see data corruption.
+	 */
+	if (uap->vendor->oversampling) {
+		if ((baud >= 3000000) && (baud < 3250000) && (quot > 1))
+			quot -= 1;
+		else if ((baud > 3250000) && (quot > 2))
+			quot -= 2;
+	}
 	/* Set baud rate */
 	writew(quot & 0x3f, port->membase + UART011_FBRD);
 	writew(quot >> 6, port->membase + UART011_IBRD);
 
 	/*
 	 * ----------v----------v----------v----------v-----
-	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+	 * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
+	 * UART011_FBRD & UART011_IBRD.
 	 * ----------^----------^----------^----------^-----
 	 */
 	writew(lcr_h, port->membase + uap->lcrh_rx);
@@ -1637,7 +1692,7 @@ static const char *pl011_type(struct uart_port *port)
 /*
  * Release the memory region(s) being used by 'port'
  */
-static void pl010_release_port(struct uart_port *port)
+static void pl011_release_port(struct uart_port *port)
 {
 	release_mem_region(port->mapbase, SZ_4K);
 }
@@ -1645,7 +1700,7 @@ static void pl010_release_port(struct uart_port *port)
 /*
  * Request the memory region(s) being used by 'port'
  */
-static int pl010_request_port(struct uart_port *port)
+static int pl011_request_port(struct uart_port *port)
 {
 	return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
 			!= NULL ? 0 : -EBUSY;
@@ -1654,18 +1709,18 @@ static int pl010_request_port(struct uart_port *port)
 /*
  * Configure/autoconfigure the port.
  */
-static void pl010_config_port(struct uart_port *port, int flags)
+static void pl011_config_port(struct uart_port *port, int flags)
 {
 	if (flags & UART_CONFIG_TYPE) {
 		port->type = PORT_AMBA;
-		pl010_request_port(port);
+		pl011_request_port(port);
 	}
 }
 
 /*
  * verify the new serial_struct (for TIOCSSERIAL).
  */
-static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser)
 {
 	int ret = 0;
 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
@@ -1678,9 +1733,9 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
 }
 
 static struct uart_ops amba_pl011_pops = {
-	.tx_empty	= pl01x_tx_empty,
+	.tx_empty	= pl011_tx_empty,
 	.set_mctrl	= pl011_set_mctrl,
-	.get_mctrl	= pl01x_get_mctrl,
+	.get_mctrl	= pl011_get_mctrl,
 	.stop_tx	= pl011_stop_tx,
 	.start_tx	= pl011_start_tx,
 	.stop_rx	= pl011_stop_rx,
@@ -1691,13 +1746,14 @@ static struct uart_ops amba_pl011_pops = {
 	.flush_buffer	= pl011_dma_flush_buffer,
 	.set_termios	= pl011_set_termios,
 	.type		= pl011_type,
-	.release_port	= pl010_release_port,
-	.request_port	= pl010_request_port,
-	.config_port	= pl010_config_port,
-	.verify_port	= pl010_verify_port,
+	.release_port	= pl011_release_port,
+	.request_port	= pl011_request_port,
+	.config_port	= pl011_config_port,
+	.verify_port	= pl011_verify_port,
 #ifdef CONFIG_CONSOLE_POLL
-	.poll_get_char = pl010_get_poll_char,
-	.poll_put_char = pl010_put_poll_char,
+	.poll_init     = pl011_hwinit,
+	.poll_get_char = pl011_get_poll_char,
+	.poll_put_char = pl011_put_poll_char,
 #endif
 };
 
@@ -1869,6 +1925,38 @@ static struct uart_driver amba_reg = {
 	.cons			= AMBA_CONSOLE,
 };
 
+static int pl011_probe_dt_alias(int index, struct device *dev)
+{
+	struct device_node *np;
+	static bool seen_dev_with_alias = false;
+	static bool seen_dev_without_alias = false;
+	int ret = index;
+
+	if (!IS_ENABLED(CONFIG_OF))
+		return ret;
+
+	np = dev->of_node;
+	if (!np)
+		return ret;
+
+	ret = of_alias_get_id(np, "serial");
+	if (IS_ERR_VALUE(ret)) {
+		seen_dev_without_alias = true;
+		ret = index;
+	} else {
+		seen_dev_with_alias = true;
+		if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret] != NULL) {
+			dev_warn(dev, "requested serial port %d  not available.\n", ret);
+			ret = index;
+		}
+	}
+
+	if (seen_dev_with_alias && seen_dev_without_alias)
+		dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n");
+
+	return ret;
+}
+
 static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 {
 	struct uart_amba_port *uap;
@@ -1891,6 +1979,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 		goto out;
 	}
 
+	i = pl011_probe_dt_alias(i, &dev->dev);
+
 	base = ioremap(dev->res.start, resource_size(&dev->res));
 	if (!base) {
 		ret = -ENOMEM;
@@ -1923,7 +2013,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 	uap->lcrh_tx = vendor->lcrh_tx;
 	uap->old_cr = 0;
 	uap->fifosize = vendor->fifosize;
-	uap->interrupt_may_hang = vendor->interrupt_may_hang;
 	uap->port.dev = &dev->dev;
 	uap->port.mapbase = dev->res.start;
 	uap->port.membase = base;

+ 1 - 1
drivers/tty/serial/bfin_uart.c

@@ -182,7 +182,7 @@ static void bfin_serial_start_tx(struct uart_port *port)
 	 * To avoid losting RX interrupt, we reset IR function
 	 * before sending data.
 	 */
-	if (tty->termios->c_line == N_IRDA)
+	if (tty->termios.c_line == N_IRDA)
 		bfin_serial_reset_irda(port);
 
 #ifdef CONFIG_SERIAL_BFIN_DMA

+ 19 - 4
drivers/tty/serial/cpm_uart/cpm_uart_core.c

@@ -71,7 +71,7 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
 
 /**************************************************************/
 
-#define HW_BUF_SPD_THRESHOLD    9600
+#define HW_BUF_SPD_THRESHOLD    2400
 
 /*
  * Check, if transmit buffers are processed
@@ -417,6 +417,7 @@ static int cpm_uart_startup(struct uart_port *port)
 			clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
 			clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
 		}
+		cpm_uart_initbd(pinfo);
 		cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
 	}
 	/* Install interrupt handler. */
@@ -500,16 +501,28 @@ static void cpm_uart_set_termios(struct uart_port *port,
 	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
 	smc_t __iomem *smcp = pinfo->smcp;
 	scc_t __iomem *sccp = pinfo->sccp;
+	int maxidl;
 
 	pr_debug("CPM uart[%d]:set_termios\n", port->line);
 
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
-	if (baud <= HW_BUF_SPD_THRESHOLD ||
+	if (baud < HW_BUF_SPD_THRESHOLD ||
 	    (pinfo->port.state && pinfo->port.state->port.tty->low_latency))
 		pinfo->rx_fifosize = 1;
 	else
 		pinfo->rx_fifosize = RX_BUF_SIZE;
 
+	/* MAXIDL is the timeout after which a receive buffer is closed
+	 * when not full if no more characters are received.
+	 * We calculate it from the baudrate so that the duration is
+	 * always the same at standard rates: about 4ms.
+	 */
+	maxidl = baud / 2400;
+	if (maxidl < 1)
+		maxidl = 1;
+	if (maxidl > 0x10)
+		maxidl = 0x10;
+
 	/* Character length programmed into the mode register is the
 	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
 	 * 1 or 2 stop bits, minus 1.
@@ -610,6 +623,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
 		 * SMC/SCC receiver is disabled.
 		 */
 		out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
+		out_be16(&pinfo->smcup->smc_maxidl, maxidl);
 
 		/* Set the mode register.  We want to keep a copy of the
 		 * enables, because we want to put them back if they were
@@ -622,6 +636,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
 		    SMCMR_SM_UART | prev_mode);
 	} else {
 		out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+		out_be16(&pinfo->sccup->scc_maxidl, maxidl);
 		out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
 	}
 
@@ -798,7 +813,7 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
 	cpm_set_scc_fcr(sup);
 
 	out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
-	out_be16(&sup->scc_maxidl, pinfo->rx_fifosize);
+	out_be16(&sup->scc_maxidl, 0x10);
 	out_be16(&sup->scc_brkcr, 1);
 	out_be16(&sup->scc_parec, 0);
 	out_be16(&sup->scc_frmec, 0);
@@ -872,7 +887,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
 
 	/* Using idle character time requires some additional tuning.  */
 	out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
-	out_be16(&up->smc_maxidl, pinfo->rx_fifosize);
+	out_be16(&up->smc_maxidl, 0x10);
 	out_be16(&up->smc_brklen, 0);
 	out_be16(&up->smc_brkec, 0);
 	out_be16(&up->smc_brkcr, 1);

+ 24 - 21
drivers/tty/serial/crisv10.c

@@ -955,7 +955,7 @@ static const struct control_pins e100_modem_pins[NR_PORTS] =
 /* Calculate the chartime depending on baudrate, numbor of bits etc. */
 static void update_char_time(struct e100_serial * info)
 {
-	tcflag_t cflags = info->port.tty->termios->c_cflag;
+	tcflag_t cflags = info->port.tty->termios.c_cflag;
 	int bits;
 
 	/* calc. number of bits / data byte */
@@ -1473,7 +1473,7 @@ rs_stop(struct tty_struct *tty)
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
 				STOP_CHAR(info->port.tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
-		if (tty->termios->c_iflag & IXON ) {
+		if (tty->termios.c_iflag & IXON ) {
 			xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 		}
 
@@ -1496,7 +1496,7 @@ rs_start(struct tty_struct *tty)
 					 info->xmit.tail,SERIAL_XMIT_SIZE)));
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
-		if (tty->termios->c_iflag & IXON ) {
+		if (tty->termios.c_iflag & IXON ) {
 			xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 		}
 
@@ -2929,7 +2929,7 @@ shutdown(struct e100_serial * info)
 			descr[i].buf = 0;
 		}
 
-	if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) {
+	if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) {
 		/* hang up DTR and RTS if HUPCL is enabled */
 		e100_dtr(info, 0);
 		e100_rts(info, 0); /* could check CRTSCTS before doing this */
@@ -2953,12 +2953,12 @@ change_speed(struct e100_serial *info)
 	unsigned long flags;
 	/* first some safety checks */
 
-	if (!info->port.tty || !info->port.tty->termios)
+	if (!info->port.tty)
 		return;
 	if (!info->ioport)
 		return;
 
-	cflag = info->port.tty->termios->c_cflag;
+	cflag = info->port.tty->termios.c_cflag;
 
 	/* possibly, the tx/rx should be disabled first to do this safely */
 
@@ -3088,7 +3088,7 @@ change_speed(struct e100_serial *info)
 	info->ioport[REG_REC_CTRL] = info->rx_ctrl;
 	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
 	xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
-	if (info->port.tty->termios->c_iflag & IXON ) {
+	if (info->port.tty->termios.c_iflag & IXON ) {
 		DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n",
 				STOP_CHAR(info->port.tty)));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
@@ -3355,7 +3355,7 @@ rs_throttle(struct tty_struct * tty)
 	DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
 
 	/* Do RTS before XOFF since XOFF might take some time */
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		/* Turn off RTS line */
 		e100_rts(info, 0);
 	}
@@ -3377,7 +3377,7 @@ rs_unthrottle(struct tty_struct * tty)
 	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
 	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
 	/* Do RTS before XOFF since XOFF might take some time */
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		/* Assert RTS line  */
 		e100_rts(info, 1);
 	}
@@ -3748,7 +3748,7 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		rs_start(tty);
 	}
@@ -3815,7 +3815,7 @@ rs_close(struct tty_struct *tty, struct file * filp)
 	 * separate termios for callout and dialin.
 	 */
 	if (info->flags & ASYNC_NORMAL_ACTIVE)
-		info->normal_termios = *tty->termios;
+		info->normal_termios = tty->termios;
 	/*
 	 * Now we wait for the transmit buffer to clear; and we notify
 	 * the line discipline to only process XON/XOFF characters.
@@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
@@ -3998,7 +3998,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		return 0;
 	}
 
-	if (tty->termios->c_cflag & CLOCAL) {
+	if (tty->termios.c_cflag & CLOCAL) {
 			do_clocal = 1;
 	}
 
@@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
@@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
@@ -4219,7 +4219,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
 	}
 
 	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
-		*tty->termios = info->normal_termios;
+		tty->termios = info->normal_termios;
 		change_speed(info);
 	}
 
@@ -4443,14 +4443,12 @@ static int __init rs_init(void)
 		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
 	driver->init_termios.c_ispeed = 115200;
 	driver->init_termios.c_ospeed = 115200;
-	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	driver->flags = TTY_DRIVER_REAL_RAW;
 
 	tty_set_operations(driver, &rs_ops);
         serial_driver = driver;
-	if (tty_register_driver(driver))
-		panic("Couldn't register serial driver\n");
-	/* do some initializing for the separate ports */
 
+	/* do some initializing for the separate ports */
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
 		if (info->enabled) {
 			if (cris_request_io_interface(info->io_if,
@@ -4502,7 +4500,12 @@ static int __init rs_init(void)
 			printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
 			       serial_driver->name, info->line, info->ioport);
 		}
+		tty_port_link_device(&info->port, driver, i);
 	}
+
+	if (tty_register_driver(driver))
+		panic("Couldn't register serial driver\n");
+
 #ifdef CONFIG_ETRAX_FAST_TIMER
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 	memset(fast_timers, 0, sizeof(fast_timers));

+ 2 - 2
drivers/tty/serial/ifx6x60.c

@@ -800,8 +800,8 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev)
 	tty_port_init(pport);
 	pport->ops = &ifx_tty_port_ops;
 	ifx_dev->minor = IFX_SPI_TTY_ID;
-	ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor,
-					       &ifx_dev->spi_dev->dev);
+	ifx_dev->tty_dev = tty_port_register_device(pport, tty_drv,
+			ifx_dev->minor, &ifx_dev->spi_dev->dev);
 	if (IS_ERR(ifx_dev->tty_dev)) {
 		dev_dbg(&ifx_dev->spi_dev->dev,
 			"%s: registering tty device failed", __func__);

+ 7 - 6
drivers/tty/serial/imx.c

@@ -207,7 +207,7 @@ struct imx_port {
 	unsigned short		trcv_delay; /* transceiver delay */
 	struct clk		*clk_ipg;
 	struct clk		*clk_per;
-	struct imx_uart_data	*devdata;
+	const struct imx_uart_data *devdata;
 };
 
 struct imx_port_ucrs {
@@ -1373,8 +1373,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
 	val |= UCR3_AWAKEN;
 	writel(val, sport->port.membase + UCR3);
 
-	if (sport)
-		uart_suspend_port(&imx_reg, &sport->port);
+	uart_suspend_port(&imx_reg, &sport->port);
 
 	return 0;
 }
@@ -1389,8 +1388,7 @@ static int serial_imx_resume(struct platform_device *dev)
 	val &= ~UCR3_AWAKEN;
 	writel(val, sport->port.membase + UCR3);
 
-	if (sport)
-		uart_resume_port(&imx_reg, &sport->port);
+	uart_resume_port(&imx_reg, &sport->port);
 
 	return 0;
 }
@@ -1505,18 +1503,21 @@ static int serial_imx_probe(struct platform_device *pdev)
 	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 	if (IS_ERR(pinctrl)) {
 		ret = PTR_ERR(pinctrl);
+		dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
 		goto unmap;
 	}
 
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	if (IS_ERR(sport->clk_ipg)) {
 		ret = PTR_ERR(sport->clk_ipg);
+		dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
 		goto unmap;
 	}
 
 	sport->clk_per = devm_clk_get(&pdev->dev, "per");
 	if (IS_ERR(sport->clk_per)) {
 		ret = PTR_ERR(sport->clk_per);
+		dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
 		goto unmap;
 	}
 
@@ -1537,7 +1538,7 @@ static int serial_imx_probe(struct platform_device *pdev)
 	ret = uart_add_one_port(&imx_reg, &sport->port);
 	if (ret)
 		goto deinit;
-	platform_set_drvdata(pdev, &sport->port);
+	platform_set_drvdata(pdev, sport);
 
 	return 0;
 deinit:

+ 2 - 1
drivers/tty/serial/ioc3_serial.c

@@ -1120,13 +1120,14 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len)
 	struct ioc3_port *port = get_ioc3_port(the_port);
 	struct ring *inring;
 	struct ring_entry *entry;
-	struct port_hooks *hooks = port->ip_hooks;
+	struct port_hooks *hooks;
 	int byte_num;
 	char *sc;
 	int loop_counter;
 
 	BUG_ON(!(len >= 0));
 	BUG_ON(!port);
+	hooks = port->ip_hooks;
 
 	/* There is a nasty timing issue in the IOC3. When the rx_timer
 	 * expires or the rx_high condition arises, we take an interrupt.

Some files were not shown because too many files changed in this diff