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 13 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'.
 		 device, like 'tty1'.
 		 The file supports poll() to detect virtual
 		 The file supports poll() to detect virtual
 		 console switches.
 		 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).
   accesses to the UART (e.g. TI davinci).
 - used-by-rtas : set to indicate that the port is in use by the OpenFirmware
 - used-by-rtas : set to indicate that the port is in use by the OpenFirmware
   RTAS and should not be registered.
   RTAS and should not be registered.
+- no-loopback-test: set to indicate that the port does not implements loopback
+  test mode
 
 
 Example:
 Example:
 
 

+ 0 - 2
Documentation/serial/00-INDEX

@@ -2,8 +2,6 @@
 	- this file.
 	- this file.
 README.cycladesZ
 README.cycladesZ
 	- info on Cyclades-Z firmware loading.
 	- info on Cyclades-Z firmware loading.
-computone.txt
-	- info on Computone Intelliport II/Plus Multiport Serial Driver.
 digiepca.txt
 digiepca.txt
 	- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
 	- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
 hayes-esp.txt
 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->subtype = SYSTEM_TYPE_SYSCONS;
 		driver->init_termios = tty_std_termios;
 		driver->init_termios = tty_std_termios;
 		tty_set_operations(driver, &srmcons_ops);
 		tty_set_operations(driver, &srmcons_ops);
+		tty_port_link_device(&srmcons_singleton.port, driver, 0);
 		err = tty_register_driver(driver);
 		err = tty_register_driver(driver);
 		if (err) {
 		if (err) {
 			put_tty_driver(driver);
 			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
 #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);
 	struct omap_device *od = to_omap_device(pdev);
 
 
 	if (!od)
 	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.
  * in Smartidle Mode When Configured for DMA Operations.
  * WA: configure uart in force idle mode.
  * 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);
 	struct omap_device *od = to_omap_device(pdev);
 
 
 	omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
 	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);
 	struct omap_device *od = to_omap_device(pdev);
 	u8 idlemode;
 	u8 idlemode;
 
 
@@ -120,10 +123,10 @@ static void omap_uart_set_smartidle(struct platform_device *pdev)
 }
 }
 
 
 #else
 #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 */
 #endif /* CONFIG_PM */
 
 
 #ifdef CONFIG_OMAP_MUX
 #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_timeout = info->dma_rx_timeout;
 	omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
 	omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
 	omap_up.autosuspend_timeout = info->autosuspend_timeout;
 	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 = &omap_up;
 	pdata_size = sizeof(struct omap_uart_port_info);
 	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
 #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 = {
 static struct amba_pl011_data uart0_plat = {
 #ifdef CONFIG_STE_DMA40
 #ifdef CONFIG_STE_DMA40
 	.dma_filter = stedma40_filter,
 	.dma_filter = stedma40_filter,
 	.dma_rx_param = &uart0_dma_cfg_rx,
 	.dma_rx_param = &uart0_dma_cfg_rx,
 	.dma_tx_param = &uart0_dma_cfg_tx,
 	.dma_tx_param = &uart0_dma_cfg_tx,
 #endif
 #endif
-	.reset = ux500_uart0_reset,
 };
 };
 
 
 static struct amba_pl011_data uart1_plat = {
 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__
 #define __OMAP_SERIAL_H__
 
 
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
-#include <linux/platform_device.h>
+#include <linux/device.h>
 #include <linux/pm_qos.h>
 #include <linux/pm_qos.h>
 
 
 #include <plat/mux.h>
 #include <plat/mux.h>
@@ -42,10 +42,10 @@
 #define OMAP_UART_WER_MOD_WKUP	0X7F
 #define OMAP_UART_WER_MOD_WKUP	0X7F
 
 
 /* Enable XON/XOFF flow control on output */
 /* 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 */
 /* 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_SYSC_RESET	0X07
 #define OMAP_UART_TCR_TRIG	0X0F
 #define OMAP_UART_TCR_TRIG	0X0F
@@ -69,11 +69,14 @@ struct omap_uart_port_info {
 	unsigned int		dma_rx_timeout;
 	unsigned int		dma_rx_timeout;
 	unsigned int		autosuspend_timeout;
 	unsigned int		autosuspend_timeout;
 	unsigned int		dma_rx_poll_rate;
 	unsigned int		dma_rx_poll_rate;
+	int			DTR_gpio;
+	int			DTR_inverted;
+	int			DTR_present;
 
 
 	int (*get_context_loss_count)(struct device *);
 	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 {
 struct uart_omap_dma {
@@ -102,39 +105,4 @@ struct uart_omap_dma {
 	unsigned int		rx_timeout;
 	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__ */
 #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 */
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 	}
 	}
 }
 }
@@ -545,6 +545,7 @@ static int __init simrs_init(void)
 	/* the port is imaginary */
 	/* the port is imaginary */
 	printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq);
 	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);
 	retval = tty_register_driver(hp_simserial_driver);
 	if (retval) {
 	if (retval) {
 		printk(KERN_ERR "Couldn't register simserial driver\n");
 		printk(KERN_ERR "Couldn't register simserial driver\n");

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

@@ -19,6 +19,7 @@
 #include <asm/natfeat.h>
 #include <asm/natfeat.h>
 
 
 static int stderr_id;
 static int stderr_id;
+static struct tty_port nfcon_tty_port;
 static struct tty_driver *nfcon_tty_driver;
 static struct tty_driver *nfcon_tty_driver;
 
 
 static void nfputs(const char *str, unsigned int count)
 static void nfputs(const char *str, unsigned int count)
@@ -119,6 +120,8 @@ static int __init nfcon_init(void)
 {
 {
 	int res;
 	int res;
 
 
+	tty_port_init(&nfcon_tty_port);
+
 	stderr_id = nf_get_id("NF_STDERR");
 	stderr_id = nf_get_id("NF_STDERR");
 	if (!stderr_id)
 	if (!stderr_id)
 		return -ENODEV;
 		return -ENODEV;
@@ -135,6 +138,7 @@ static int __init nfcon_init(void)
 	nfcon_tty_driver->flags = TTY_DRIVER_REAL_RAW;
 	nfcon_tty_driver->flags = TTY_DRIVER_REAL_RAW;
 
 
 	tty_set_operations(nfcon_tty_driver, &nfcon_tty_ops);
 	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);
 	res = tty_register_driver(nfcon_tty_driver);
 	if (res) {
 	if (res) {
 		pr_err("failed to register nfcon tty driver\n");
 		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;
 	int irq, res;
 	struct resource *res_mem;
 	struct resource *res_mem;
-	struct uart_port port;
+	struct uart_8250_port up;
 
 
 	/* All adaptors have an irq.  */
 	/* All adaptors have an irq.  */
 	irq = platform_get_irq(pdev, 0);
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 	if (irq < 0)
 		return irq;
 		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())
 	if (octeon_is_simulation())
 		/* Make simulator output fast*/
 		/* Make simulator output fast*/
-		port.uartclk = 115200 * 16;
+		up.port.uartclk = 115200 * 16;
 	else
 	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);
 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res_mem == NULL) {
 	if (res_mem == NULL) {
 		dev_err(&pdev->dev, "found no memory resource\n");
 		dev_err(&pdev->dev, "found no memory resource\n");
 		return -ENXIO;
 		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;
 	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)
 static u32 a20r_ack_hwint(void)
 {
 {
 	u32 status = read_c0_status();
 	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 |
 	pdc_console_tty_driver->flags = TTY_DRIVER_REAL_RAW |
 		TTY_DRIVER_RESET_TERMIOS;
 		TTY_DRIVER_RESET_TERMIOS;
 	tty_set_operations(pdc_console_tty_driver, &pdc_console_tty_ops);
 	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);
 	err = tty_register_driver(pdc_console_tty_driver);
 	if (err) {
 	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;
 		line->valid = 1;
 		err = parse_chan_pair(new, line, n, opts, error_out);
 		err = parse_chan_pair(new, line, n, opts, error_out);
 		if (!err) {
 		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)) {
 			if (IS_ERR(d)) {
 				*error_out = "Failed to register device";
 				*error_out = "Failed to register device";
 				err = PTR_ERR(d);
 				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;
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 
 
 	tty_set_operations(serial_driver, &serial_ops);
 	tty_set_operations(serial_driver, &serial_ops);
+	tty_port_link_device(&serial_port, serial_driver, 0);
 
 
 	if (tty_register_driver(serial_driver))
 	if (tty_register_driver(serial_driver))
 		panic("Couldn't register serial driver\n");
 		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;
 		return status;
 
 
 	/* Disable Automatic RTSCTS */
 	/* Disable Automatic RTSCTS */
-	memcpy(&ktermios, tty->termios, sizeof(ktermios));
+	ktermios = tty->termios;
 	ktermios.c_cflag &= ~CRTSCTS;
 	ktermios.c_cflag &= ~CRTSCTS;
 	tty_set_termios(tty, &ktermios);
 	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)
 static int register_serial_portandirq(unsigned int port, int irq)
 {
 {
-	struct uart_port uart;
+	struct uart_8250_port uart;
 	
 	
 	switch ( port ) {
 	switch ( port ) {
 		case 0x3f8:
 		case 0x3f8:
@@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq)
 	} /* switch */
 	} /* switch */
 	/* irq is okay */
 	/* 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->status_event_wait_q);
 	wake_up_interruptible(&info->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 (tty->hw_stopped) {
 			if (info->serial_signals & SerialSignal_CTS) {
 			if (info->serial_signals & SerialSignal_CTS) {
 				if (debug_level >= DEBUG_LEVEL_ISR)
 				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 */
 	/* TODO:disable interrupts instead of reset to preserve signal states */
 	reset_device(info);
 	reset_device(info);
 
 
- 	if (!tty || tty->termios->c_cflag & HUPCL) {
+ 	if (!tty || tty->termios.c_cflag & HUPCL) {
  		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
  		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
 		set_signals(info);
 		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);
 	port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI);
 	get_signals(info);
 	get_signals(info);
 
 
-	if (info->netcount || (tty && (tty->termios->c_cflag & CREAD)))
+	if (info->netcount || (tty && (tty->termios.c_cflag & CREAD)))
 		rx_start(info);
 		rx_start(info);
 
 
 	spin_unlock_irqrestore(&info->lock,flags);
 	spin_unlock_irqrestore(&info->lock,flags);
@@ -1404,14 +1404,14 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
 	unsigned cflag;
 	unsigned cflag;
 	int bits_per_char;
 	int bits_per_char;
 
 
-	if (!tty || !tty->termios)
+	if (!tty)
 		return;
 		return;
 
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgslpc_change_params(%s)\n",
 		printk("%s(%d):mgslpc_change_params(%s)\n",
 			 __FILE__,__LINE__, info->device_name );
 			 __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 */
 	/* if B0 rate (hangup) specified then negate DTR and RTS */
 	/* otherwise assert DTR and RTS */
 	/* otherwise assert DTR and RTS */
@@ -1734,7 +1734,7 @@ static void mgslpc_throttle(struct tty_struct * tty)
 	if (I_IXOFF(tty))
 	if (I_IXOFF(tty))
 		mgslpc_send_xchar(tty, STOP_CHAR(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);
 		spin_lock_irqsave(&info->lock,flags);
 		info->serial_signals &= ~SerialSignal_RTS;
 		info->serial_signals &= ~SerialSignal_RTS;
 	 	set_signals(info);
 	 	set_signals(info);
@@ -1763,7 +1763,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty)
 			mgslpc_send_xchar(tty, START_CHAR(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);
 		spin_lock_irqsave(&info->lock,flags);
 		info->serial_signals |= SerialSignal_RTS;
 		info->serial_signals |= SerialSignal_RTS;
 	 	set_signals(info);
 	 	set_signals(info);
@@ -2299,8 +2299,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 			tty->driver->name );
 			tty->driver->name );
 
 
 	/* just return if nothing has changed */
 	/* 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)))
 		== RELEVANT_IFLAG(old_termios->c_iflag)))
 	  return;
 	  return;
 
 
@@ -2308,7 +2308,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
 
 
 	/* Handle transition to B0 status */
 	/* Handle transition to B0 status */
 	if (old_termios->c_cflag & CBAUD &&
 	if (old_termios->c_cflag & CBAUD &&
-	    !(tty->termios->c_cflag & CBAUD)) {
+	    !(tty->termios.c_cflag & CBAUD)) {
 		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
 		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
 		spin_lock_irqsave(&info->lock,flags);
 		spin_lock_irqsave(&info->lock,flags);
 	 	set_signals(info);
 	 	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 */
 	/* Handle transition away from B0 status */
 	if (!(old_termios->c_cflag & CBAUD) &&
 	if (!(old_termios->c_cflag & CBAUD) &&
-	    tty->termios->c_cflag & CBAUD) {
+	    tty->termios.c_cflag & CBAUD) {
 		info->serial_signals |= SerialSignal_DTR;
 		info->serial_signals |= SerialSignal_DTR;
- 		if (!(tty->termios->c_cflag & CRTSCTS) ||
+ 		if (!(tty->termios.c_cflag & CRTSCTS) ||
  		    !test_bit(TTY_THROTTLED, &tty->flags)) {
  		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 			info->serial_signals |= SerialSignal_RTS;
 			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 */
 	/* Handle turning off CRTSCTS */
 	if (old_termios->c_cflag & CRTSCTS &&
 	if (old_termios->c_cflag & CRTSCTS &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		tx_release(tty);
 		tx_release(tty);
 	}
 	}
@@ -2737,6 +2737,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
 #if SYNCLINK_GENERIC_HDLC
 #if SYNCLINK_GENERIC_HDLC
 	hdlcdev_init(info);
 	hdlcdev_init(info);
 #endif
 #endif
+	tty_port_register_device(&info->port, serial_driver, info->line,
+			&info->p_dev->dev);
 }
 }
 
 
 static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
 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;
 				last->next_device = info->next_device;
 			else
 			else
 				mgslpc_device_list = info->next_device;
 				mgslpc_device_list = info->next_device;
+			tty_unregister_device(serial_driver, info->line);
 #if SYNCLINK_GENERIC_HDLC
 #if SYNCLINK_GENERIC_HDLC
 			hdlcdev_exit(info);
 			hdlcdev_exit(info);
 #endif
 #endif
@@ -2804,77 +2807,63 @@ static const struct tty_operations mgslpc_ops = {
 	.proc_fops = &mgslpc_proc_fops,
 	.proc_fops = &mgslpc_proc_fops,
 };
 };
 
 
-static void synclink_cs_cleanup(void)
+static int __init synclink_cs_init(void)
 {
 {
 	int rc;
 	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)
 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);
 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';
 				tmp[tpk_curr + 1] = '\0';
 				printk(KERN_INFO "%s%s", tpk_tag, tmp);
 				printk(KERN_INFO "%s%s", tpk_tag, tmp);
 				tpk_curr = 0;
 				tpk_curr = 0;
-				if (buf[i + 1] == '\n')
+				if ((i + 1) < count && buf[i + 1] == '\n')
 					i++;
 					i++;
 				break;
 				break;
 			case '\n':
 			case '\n':
@@ -178,11 +178,17 @@ static struct tty_driver *ttyprintk_driver;
 static int __init ttyprintk_init(void)
 static int __init ttyprintk_init(void)
 {
 {
 	int ret = -ENOMEM;
 	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->driver_name = "ttyprintk";
 	ttyprintk_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->type = TTY_DRIVER_TYPE_CONSOLE;
 	ttyprintk_driver->init_termios = tty_std_termios;
 	ttyprintk_driver->init_termios = tty_std_termios;
 	ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
 	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_set_operations(ttyprintk_driver, &ttyprintk_ops);
+	tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
 
 
 	ret = tty_register_driver(ttyprintk_driver);
 	ret = tty_register_driver(ttyprintk_driver);
 	if (ret < 0) {
 	if (ret < 0) {
@@ -201,22 +206,10 @@ static int __init ttyprintk_init(void)
 		goto error;
 		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;
 	return 0;
 
 
 error:
 error:
+	tty_unregister_driver(ttyprintk_driver);
 	put_tty_driver(ttyprintk_driver);
 	put_tty_driver(ttyprintk_driver);
 	ttyprintk_driver = NULL;
 	ttyprintk_driver = NULL;
 	return ret;
 	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;
 	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))
 	if (IS_ERR(dev))
 		goto err_out2;
 		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;
 		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;
 	old_cflag = old ? old->c_cflag : cflag;
 	gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 	gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 		cs->minor_index, iflag, cflag, old_cflag);
 		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);
 	tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
 
 
 	mutex_lock(&cs->mutex);
 	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))
 	if (!IS_ERR(cs->tty_dev))
 		dev_set_drvdata(cs->tty_dev, cs);
 		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;
 		quot;
 	int i;
 	int i;
 
 
-	if (!port->tty || !port->tty->termios)
+	if (!port->tty)
 		return;
 		return;
-	cflag = port->tty->termios->c_cflag;
+	cflag = port->tty->termios.c_cflag;
 
 
 	quot = i = cflag & CBAUD;
 	quot = i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
 		i &= ~CBAUDEX;
 		if (i < 1 || i > 2)
 		if (i < 1 || i > 2)
-			port->tty->termios->c_cflag &= ~CBAUDEX;
+			port->tty->termios.c_cflag &= ~CBAUDEX;
 		else
 		else
 			i += 15;
 			i += 15;
 	}
 	}
@@ -1097,7 +1097,7 @@ isdn_tty_shutdown(modem_info *info)
 #endif
 #endif
 	isdn_unlock_drivers();
 	isdn_unlock_drivers();
 	info->msr &= ~UART_MSR_RI;
 	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);
 		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
 		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 			isdn_tty_modem_reset_regs(info, 0);
 			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)
 	if (!old_termios)
 		isdn_tty_change_speed(info);
 		isdn_tty_change_speed(info);
 	else {
 	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;
 			return;
 		isdn_tty_change_speed(info);
 		isdn_tty_change_speed(info);
 		if ((old_termios->c_cflag & CRTSCTS) &&
 		if ((old_termios->c_cflag & CRTSCTS) &&
-		    !(tty->termios->c_cflag & CRTSCTS))
+		    !(tty->termios.c_cflag & CRTSCTS))
 			tty->hw_stopped = 0;
 			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
  * This routine is called whenever a serial port is opened.  It
  * enables interrupts for a serial port, linking in its async structure into
  * 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
 static int
 isdn_tty_open(struct tty_struct *tty, struct file *filp)
 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;
 	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
 #ifdef ISDN_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
 	printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
 	       port->count);
 	       port->count);
 #endif
 #endif
 	port->count++;
 	port->count++;
-	tty->driver_data = info;
 	port->tty = tty;
 	port->tty = tty;
-	tty->port = port;
 	/*
 	/*
 	 * Start up serial port
 	 * Start up serial port
 	 */
 	 */
@@ -1738,6 +1744,7 @@ modem_write_profile(atemu *m)
 }
 }
 
 
 static const struct tty_operations modem_ops = {
 static const struct tty_operations modem_ops = {
+	.install = isdn_tty_install,
 	.open = isdn_tty_open,
 	.open = isdn_tty_open,
 	.close = isdn_tty_close,
 	.close = isdn_tty_close,
 	.write = isdn_tty_write,
 	.write = isdn_tty_write,
@@ -1782,7 +1789,7 @@ isdn_tty_modem_init(void)
 	m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
 	m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
 	m->tty_modem->init_termios = tty_std_termios;
 	m->tty_modem->init_termios = tty_std_termios;
 	m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 	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";
 	m->tty_modem->driver_name = "isdn_tty";
 	tty_set_operations(m->tty_modem, &modem_ops);
 	tty_set_operations(m->tty_modem, &modem_ops);
 	retval = tty_register_driver(m->tty_modem);
 	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)
 void ibmasm_register_uart(struct service_processor *sp)
 {
 {
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	void __iomem *iomem_base;
 	void __iomem *iomem_base;
 
 
 	iomem_base = sp->base_address + SCOUT_COM_B_BASE;
 	iomem_base = sp->base_address + SCOUT_COM_B_BASE;
@@ -47,14 +47,14 @@ void ibmasm_register_uart(struct service_processor *sp)
 		return;
 		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) {
 	if (sp->serial_line < 0) {
 		dev_err(sp->dev, "Failed to register serial port\n");
 		dev_err(sp->dev, "Failed to register serial port\n");
 		return;
 		return;

+ 59 - 69
drivers/misc/pti.c

@@ -60,7 +60,7 @@ struct pti_tty {
 };
 };
 
 
 struct pti_dev {
 struct pti_dev {
-	struct tty_port port;
+	struct tty_port port[PTITTY_MINOR_NUM];
 	unsigned long pti_addr;
 	unsigned long pti_addr;
 	unsigned long aperture_base;
 	unsigned long aperture_base;
 	void __iomem *pti_ioaddr;
 	void __iomem *pti_ioaddr;
@@ -76,7 +76,7 @@ struct pti_dev {
  */
  */
 static DEFINE_MUTEX(alloclock);
 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)},
 		{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)},
 		{0}
 		{0}
 };
 };
@@ -393,25 +393,6 @@ void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count)
 }
 }
 EXPORT_SYMBOL_GPL(pti_writedata);
 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.
  * for the tty_driver_*() basic function descriptions, see tty_driver.h.
  * Specific header comments made for PTI-related specifics.
  * 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
 	 * also removes a locking requirement for the actual write
 	 * procedure.
 	 * 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)
 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,
 static int __devinit pti_pci_probe(struct pci_dev *pdev,
 		const struct pci_device_id *ent)
 		const struct pci_device_id *ent)
 {
 {
+	unsigned int a;
 	int retval = -EINVAL;
 	int retval = -EINVAL;
 	int pci_bar = 1;
 	int pci_bar = 1;
 
 
@@ -830,7 +812,7 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
 			__func__, __LINE__);
 			__func__, __LINE__);
 		pr_err("%s(%d): Error value returned: %d\n",
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
 			__func__, __LINE__, retval);
-		return retval;
+		goto err;
 	}
 	}
 
 
 	retval = pci_enable_device(pdev);
 	retval = pci_enable_device(pdev);
@@ -838,17 +820,16 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev,
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 			"%s: pci_enable_device() returned error %d\n",
 			"%s: pci_enable_device() returned error %d\n",
 			__func__, retval);
 			__func__, retval);
-		return retval;
+		goto err_unreg_misc;
 	}
 	}
 
 
 	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
 	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
-
 	if (drv_data == NULL) {
 	if (drv_data == NULL) {
 		retval = -ENOMEM;
 		retval = -ENOMEM;
 		dev_err(&pdev->dev,
 		dev_err(&pdev->dev,
 			"%s(%d): kmalloc() returned NULL memory.\n",
 			"%s(%d): kmalloc() returned NULL memory.\n",
 			__func__, __LINE__);
 			__func__, __LINE__);
-		return retval;
+		goto err_disable_pci;
 	}
 	}
 	drv_data->pti_addr = pci_resource_start(pdev, pci_bar);
 	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,
 		dev_err(&pdev->dev,
 			"%s(%d): pci_request_region() returned error %d\n",
 			"%s(%d): pci_request_region() returned error %d\n",
 			__func__, __LINE__, retval);
 			__func__, __LINE__, retval);
-		kfree(drv_data);
-		return retval;
+		goto err_free_dd;
 	}
 	}
 	drv_data->aperture_base = drv_data->pti_addr+APERTURE_14;
 	drv_data->aperture_base = drv_data->pti_addr+APERTURE_14;
 	drv_data->pti_ioaddr =
 	drv_data->pti_ioaddr =
 		ioremap_nocache((u32)drv_data->aperture_base,
 		ioremap_nocache((u32)drv_data->aperture_base,
 		APERTURE_LEN);
 		APERTURE_LEN);
 	if (!drv_data->pti_ioaddr) {
 	if (!drv_data->pti_ioaddr) {
-		pci_release_region(pdev, pci_bar);
 		retval = -ENOMEM;
 		retval = -ENOMEM;
-		kfree(drv_data);
-		return retval;
+		goto err_rel_reg;
 	}
 	}
 
 
 	pci_set_drvdata(pdev, drv_data);
 	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);
 	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;
 	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 = {
 static struct pci_driver pti_pci_driver = {
 	.name		= PCINAME,
 	.name		= PCINAME,
 	.id_table	= pci_ids,
 	.id_table	= pci_ids,
@@ -933,25 +946,24 @@ static int __init pti_init(void)
 		pr_err("%s(%d): Error value returned: %d\n",
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
 			__func__, __LINE__, retval);
 
 
-		pti_tty_driver = NULL;
-		return retval;
+		goto put_tty;
 	}
 	}
 
 
 	retval = pci_register_driver(&pti_pci_driver);
 	retval = pci_register_driver(&pti_pci_driver);
-
 	if (retval) {
 	if (retval) {
 		pr_err("%s(%d): PCI registration failed of pti driver\n",
 		pr_err("%s(%d): PCI registration failed of pti driver\n",
 			__func__, __LINE__);
 			__func__, __LINE__);
 		pr_err("%s(%d): Error value returned: %d\n",
 		pr_err("%s(%d): Error value returned: %d\n",
 			__func__, __LINE__, retval);
 			__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;
 	return retval;
 }
 }
 
 
@@ -960,31 +972,9 @@ static int __init pti_init(void)
  */
  */
 static void __exit pti_exit(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);
 	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);
 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) {
 	if (status & UART_MSR_DCTS) {
 		port->icount.cts++;
 		port->icount.cts++;
 		tty = tty_port_tty_get(&port->port);
 		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);
 			int cts = (status & UART_MSR_CTS);
 			if (tty->hw_stopped) {
 			if (tty->hw_stopped) {
 				if (cts) {
 				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->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE;
 	port->mctrl = TIOCM_OUT2;
 	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);
 		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))
 		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
 			tty->hw_stopped = 1;
 			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;
 	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;
 		return;
 
 
 	if (sdio_uart_claim_func(port) != 0)
 	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);
 		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_clear_mctrl(port, TIOCM_RTS);
 
 
 	sdio_uart_irq(port->func);
 	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;
 	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;
 		return;
 
 
 	if (sdio_uart_claim_func(port) != 0)
 	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_set_mctrl(port, TIOCM_RTS);
 
 
 	sdio_uart_irq(port->func);
 	sdio_uart_irq(port->func);
@@ -898,12 +898,12 @@ static void sdio_uart_set_termios(struct tty_struct *tty,
 						struct ktermios *old_termios)
 						struct ktermios *old_termios)
 {
 {
 	struct sdio_uart_port *port = tty->driver_data;
 	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)
 	if (sdio_uart_claim_func(port) != 0)
 		return;
 		return;
 
 
-	sdio_uart_change_speed(port, tty->termios, old_termios);
+	sdio_uart_change_speed(port, &tty->termios, old_termios);
 
 
 	/* Handle transition to B0 status */
 	/* Handle transition to B0 status */
 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
@@ -1132,8 +1132,8 @@ static int sdio_uart_probe(struct sdio_func *func,
 		kfree(port);
 		kfree(port);
 	} else {
 	} else {
 		struct device *dev;
 		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)) {
 		if (IS_ERR(dev)) {
 			sdio_uart_port_remove(port);
 			sdio_uart_port_remove(port);
 			ret = PTR_ERR(dev);
 			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
 #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;
 	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_scr = COSMISC_CONSTANT,
 	uart->iu_lcr = lcr;
 	uart->iu_lcr = lcr;
 	uart->iu_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)
 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;
 	tty = priv->tty;
 
 
 	mutex_lock(&tty->termios_mutex);
 	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);
 	tty_encode_baud_rate(tty, speed, speed);
 	if (tty->ops->set_termios)
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_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;
 	int cflag;
 
 
 	mutex_lock(&tty->termios_mutex);
 	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)
 	if (stop)
 		cflag &= ~CREAD;
 		cflag &= ~CREAD;
 	else
 	else
 		cflag |= CREAD;
 		cflag |= CREAD;
 
 
-	tty->termios->c_cflag = cflag;
+	tty->termios.c_cflag = cflag;
 	if (tty->ops->set_termios)
 	if (tty->ops->set_termios)
 		tty->ops->set_termios(tty, &old_termios);
 		tty->ops->set_termios(tty, &old_termios);
 	mutex_unlock(&tty->termios_mutex);
 	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 ktermios *old)
 {
 {
 	struct hso_serial *serial = tty->driver_data;
 	struct hso_serial *serial = tty->driver_data;
-	struct ktermios *termios;
 
 
 	if (!serial) {
 	if (!serial) {
 		printk(KERN_ERR "%s: no tty structures", __func__);
 		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
 	 *	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 */
 		~(CSIZE		/* no size */
 		| PARENB	/* disable parity bit */
 		| PARENB	/* disable parity bit */
 		| CBAUD		/* clear current baud rate */
 		| CBAUD		/* clear current baud rate */
 		| CBAUDEX);	/* clear current buad 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 */
 	/* baud rate 115200 */
 	tty_encode_baud_rate(tty, 115200, 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)
 	if (old)
 		D5("Termios called with: cflags new[%d] - old[%d]",
 		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 */
 	/* the actual setup */
 	spin_lock_irqsave(&serial->serial_lock, flags);
 	spin_lock_irqsave(&serial->serial_lock, flags);
 	if (serial->port.count)
 	if (serial->port.count)
 		_hso_serial_set_termios(tty, old);
 		_hso_serial_set_termios(tty, old);
 	else
 	else
-		tty->termios = old;
+		tty->termios = *old;
 	spin_unlock_irqrestore(&serial->serial_lock, flags);
 	spin_unlock_irqrestore(&serial->serial_lock, flags);
 
 
 	/* done */
 	/* done */
@@ -2289,9 +2287,11 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
 	if (minor < 0)
 	if (minor < 0)
 		goto exit;
 		goto exit;
 
 
+	tty_port_init(&serial->port);
+
 	/* register our minor number */
 	/* 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 = serial->parent->dev;
 	dev_set_drvdata(dev, serial->parent);
 	dev_set_drvdata(dev, serial->parent);
 	i = device_create_file(dev, &dev_attr_hsotype);
 	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->minor = minor;
 	serial->magic = HSO_SERIAL_MAGIC;
 	serial->magic = HSO_SERIAL_MAGIC;
 	spin_lock_init(&serial->serial_lock);
 	spin_lock_init(&serial->serial_lock);
-	tty_port_init(&serial->port);
 	serial->num_rx_urbs = num_urbs;
 	serial->num_rx_urbs = num_urbs;
 
 
 	/* RX, allocate urb and initialize */
 	/* 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)) {
 	if (!parport_SPP_supported (p)) {
 		/* No port. */
 		/* No port. */
 		kfree (priv);
 		kfree (priv);
+		kfree(ops);
 		return NULL;
 		return NULL;
 	}
 	}
 	parport_PS2_supported (p);
 	parport_PS2_supported (p);

+ 10 - 1
drivers/parport/parport_serial.c

@@ -62,6 +62,7 @@ enum parport_pc_pci_cards {
 	timedia_9079a,
 	timedia_9079a,
 	timedia_9079b,
 	timedia_9079b,
 	timedia_9079c,
 	timedia_9079c,
+	wch_ch353_2s1p,
 };
 };
 
 
 /* each element directly indexed from enum list, above */
 /* 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_9079a */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
+	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
 };
 };
 
 
 static struct pci_device_id parport_serial_pci_tbl[] = {
 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, 0xb079, 0, 0, timedia_9079a },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
-
+	/* WCH CARDS */
+	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
 	{ 0, } /* terminate list */
 	{ 0, } /* terminate list */
 };
 };
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
@@ -460,6 +463,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = {
 		.base_baud	= 921600,
 		.base_baud	= 921600,
 		.uart_offset	= 8,
 		.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 {
 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)
 static void raw3215_remove (struct ccw_device *cdev)
 {
 {
 	struct raw3215_info *raw;
 	struct raw3215_info *raw;
+	unsigned int line;
 
 
 	ccw_device_set_offline(cdev);
 	ccw_device_set_offline(cdev);
 	raw = dev_get_drvdata(&cdev->dev);
 	raw = dev_get_drvdata(&cdev->dev);
 	if (raw) {
 	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);
 		dev_set_drvdata(&cdev->dev, NULL);
 		raw3215_free_info(raw);
 		raw3215_free_info(raw);
 	}
 	}
@@ -935,6 +942,19 @@ static int __init con3215_init(void)
 console_initcall(con3215_init);
 console_initcall(con3215_init);
 #endif
 #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
  * tty3215_open
  *
  *
@@ -942,14 +962,9 @@ console_initcall(con3215_init);
  */
  */
 static int tty3215_open(struct tty_struct *tty, struct file * filp)
 static int tty3215_open(struct tty_struct *tty, struct file * filp)
 {
 {
-	struct raw3215_info *raw;
+	struct raw3215_info *raw = tty->driver_data;
 	int retval;
 	int retval;
 
 
-	raw = raw3215[tty->index];
-	if (raw == NULL)
-		return -ENODEV;
-
-	tty->driver_data = raw;
 	tty_port_tty_set(&raw->port, tty);
 	tty_port_tty_set(&raw->port, tty);
 
 
 	tty->low_latency = 0;  /* don't use bottom half for pushing chars */
 	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 = {
 static const struct tty_operations tty3215_ops = {
+	.install = tty3215_install,
 	.open = tty3215_open,
 	.open = tty3215_open,
 	.close = tty3215_close,
 	.close = tty3215_close,
 	.write = tty3215_write,
 	.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->init_termios.c_lflag = ISIG | ECHO;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(driver, &sclp_ops);
 	tty_set_operations(driver, &sclp_ops);
+	tty_port_link_device(&sclp_port, driver, 0);
 	rc = tty_register_driver(driver);
 	rc = tty_register_driver(driver);
 	if (rc) {
 	if (rc) {
 		put_tty_driver(driver);
 		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->init_termios = tty_std_termios;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(driver, &sclp_vt220_ops);
 	tty_set_operations(driver, &sclp_vt220_ops);
+	tty_port_link_device(&sclp_vt220_port, driver, 0);
 
 
 	rc = tty_register_driver(driver);
 	rc = tty_register_driver(driver);
 	if (rc)
 	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 raw3270_view *view;
 	struct tty3270 *tp;
 	struct tty3270 *tp;
 	int i, rc;
 	int i, rc;
 
 
-	if (tty->count > 1)
-		return 0;
 	/* Check if the tty3270 is already there. */
 	/* Check if the tty3270 is already there. */
 	view = raw3270_find_view(&tty3270_fn,
 	view = raw3270_find_view(&tty3270_fn,
 				  tty->index + RAW3270_FIRSTMINOR);
 				  tty->index + RAW3270_FIRSTMINOR);
@@ -865,7 +862,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
 		/* why to reassign? */
 		/* why to reassign? */
 		tty_port_tty_set(&tp->port, tty);
 		tty_port_tty_set(&tp->port, tty);
 		tp->inattr = TF_INPUT;
 		tp->inattr = TF_INPUT;
-		return 0;
+		return tty_port_install(&tp->port, driver, tty);
 	}
 	}
 	if (tty3270_max_index < tty->index + 1)
 	if (tty3270_max_index < tty->index + 1)
 		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_port_tty_set(&tp->port, tty);
 	tty->low_latency = 0;
 	tty->low_latency = 0;
-	tty->driver_data = tp;
 	tty->winsize.ws_row = tp->view.rows - 2;
 	tty->winsize.ws_row = tp->view.rows - 2;
 	tty->winsize.ws_col = tp->view.cols;
 	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);
 	kbd_ascebc(tp->kbd, tp->view.ascebc);
 
 
 	raw3270_activate_view(&tp->view);
 	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;
 	return 0;
 }
 }
 
 
@@ -932,10 +937,17 @@ tty3270_close(struct tty_struct *tty, struct file * filp)
 	if (tp) {
 	if (tp) {
 		tty->driver_data = NULL;
 		tty->driver_data = NULL;
 		tty_port_tty_set(&tp->port, 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.
  * We always have room.
  */
  */
@@ -1737,7 +1749,8 @@ static long tty3270_compat_ioctl(struct tty_struct *tty,
 #endif
 #endif
 
 
 static const struct tty_operations tty3270_ops = {
 static const struct tty_operations tty3270_ops = {
-	.open = tty3270_open,
+	.install = tty3270_install,
+	.cleanup = tty3270_cleanup,
 	.close = tty3270_close,
 	.close = tty3270_close,
 	.write = tty3270_write,
 	.write = tty3270_write,
 	.put_char = tty3270_put_char,
 	.put_char = tty3270_put_char,
@@ -1781,7 +1794,7 @@ static int __init tty3270_init(void)
 	driver->type = TTY_DRIVER_TYPE_SYSTEM;
 	driver->type = TTY_DRIVER_TYPE_SYSTEM;
 	driver->subtype = SYSTEM_TYPE_TTY;
 	driver->subtype = SYSTEM_TYPE_TTY;
 	driver->init_termios = tty_std_termios;
 	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);
 	tty_set_operations(driver, &tty3270_ops);
 	ret = tty_register_driver(driver);
 	ret = tty_register_driver(driver);
 	if (ret) {
 	if (ret) {
@@ -1800,6 +1813,7 @@ tty3270_exit(void)
 	driver = tty3270_driver;
 	driver = tty3270_driver;
 	tty3270_driver = NULL;
 	tty3270_driver = NULL;
 	tty_unregister_driver(driver);
 	tty_unregister_driver(driver);
+	put_tty_driver(driver);
 	tty3270_del_views();
 	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/imx-drm/Kconfig"
 
 
+source "drivers/staging/dgrp/Kconfig"
+
 endif # STAGING
 endif # STAGING

+ 1 - 0
drivers/staging/Makefile

@@ -63,3 +63,4 @@ obj-$(CONFIG_ZCACHE2)		+= ramster/
 obj-$(CONFIG_NET_VENDOR_SILICOM)	+= silicom/
 obj-$(CONFIG_NET_VENDOR_SILICOM)	+= silicom/
 obj-$(CONFIG_CED1401)		+= ced1401/
 obj-$(CONFIG_CED1401)		+= ced1401/
 obj-$(CONFIG_DRM_IMX)		+= imx-drm/
 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);
 		spin_lock_init(&channel->lock);
 		channel->pointer_read = 0;
 		channel->pointer_read = 0;
 		channel->pointer_write = 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)) {
 		if (IS_ERR(tty_dev)) {
 			dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n");
 			dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n");
 			continue;
 			continue;
@@ -543,7 +543,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	struct ipoctal_channel *channel = tty->driver_data;
 	struct ipoctal_channel *channel = tty->driver_data;
 	speed_t baud;
 	speed_t baud;
 
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 
 	/* Disable and reset everything before change the setup */
 	/* Disable and reset everything before change the setup */
 	iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
 	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:
 	default:
 		mr1 |= MR1_CHRL_8_BITS;
 		mr1 |= MR1_CHRL_8_BITS;
 		/* By default, select CS8 */
 		/* By default, select CS8 */
-		tty->termios->c_cflag = (cflag & ~CSIZE) | CS8;
+		tty->termios.c_cflag = (cflag & ~CSIZE) | CS8;
 		break;
 		break;
 	}
 	}
 
 
@@ -578,7 +578,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 		mr1 |= MR1_PARITY_OFF;
 		mr1 |= MR1_PARITY_OFF;
 
 
 	/* Mark or space parity is not supported */
 	/* Mark or space parity is not supported */
-	tty->termios->c_cflag &= ~CMSPAR;
+	tty->termios.c_cflag &= ~CMSPAR;
 
 
 	/* Set stop bits */
 	/* Set stop bits */
 	if (cflag & CSTOPB)
 	if (cflag & CSTOPB)
@@ -611,10 +611,10 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	}
 	}
 
 
 	baud = tty_get_baud_rate(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 */
 	/* Set baud rate */
-	switch (tty->termios->c_ospeed) {
+	switch (baud) {
 	case 75:
 	case 75:
 		csr |= TX_CLK_75 | RX_CLK_75;
 		csr |= TX_CLK_75 | RX_CLK_75;
 		break;
 		break;
@@ -655,7 +655,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
 	default:
 	default:
 		csr |= TX_CLK_38400 | RX_CLK_38400;
 		csr |= TX_CLK_38400 | RX_CLK_38400;
 		/* In case of default, we establish 38400 bps */
 		/* 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;
 		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);
 	tty = tty_port_tty_get(&port->port);
-	if (!tty) {
-		dbg("%s - bad tty pointer - exiting", __func__);
+	if (!tty)
 		return;
 		return;
-	}
 
 
 	data = urb->transfer_buffer;
 	data = urb->transfer_buffer;
 
 
@@ -362,7 +360,7 @@ static void qt_read_bulk_callback(struct urb *urb)
 		goto exit;
 		goto exit;
 	}
 	}
 
 
-	if (tty && RxCount) {
+	if (RxCount) {
 		flag_data = 0;
 		flag_data = 0;
 		for (i = 0; i < RxCount; ++i) {
 		for (i = 0; i < RxCount; ++i) {
 			/* Look ahead code here */
 			/* 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",
 		dbg("%s - failed resubmitting read urb, error %d",
 		    __func__, result);
 		    __func__, result);
 	else {
 	else {
-		if (tty && RxCount) {
+		if (RxCount) {
 			tty_flip_buffer_push(tty);
 			tty_flip_buffer_push(tty);
 			tty_schedule_flip(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
 	 * Put this here to make it responsive to stty and defaults set by
 	 * the tty layer
 	 * 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 */
 	/*  Check to see if we've set up our endpoint info yet */
 	if (port0->open_ports == 1) {
 	if (port0->open_ports == 1) {
@@ -1193,7 +1189,7 @@ static void qt_set_termios(struct tty_struct *tty,
 			   struct usb_serial_port *port,
 			   struct usb_serial_port *port,
 			   struct ktermios *old_termios)
 			   struct ktermios *old_termios)
 {
 {
-	struct ktermios *termios = tty->termios;
+	struct ktermios *termios = &tty->termios;
 	unsigned char new_LCR = 0;
 	unsigned char new_LCR = 0;
 	unsigned int cflag = termios->c_cflag;
 	unsigned int cflag = termios->c_cflag;
 	unsigned int index;
 	unsigned int index;
@@ -1202,7 +1198,7 @@ static void qt_set_termios(struct tty_struct *tty,
 
 
 	index = tty->index - port->serial->minor;
 	index = tty->index - port->serial->minor;
 
 
-	switch (cflag) {
+	switch (cflag & CSIZE) {
 	case CS5:
 	case CS5:
 		new_LCR |= SERIAL_5_DATA;
 		new_LCR |= SERIAL_5_DATA;
 		break;
 		break;
@@ -1213,6 +1209,8 @@ static void qt_set_termios(struct tty_struct *tty,
 		new_LCR |= SERIAL_7_DATA;
 		new_LCR |= SERIAL_7_DATA;
 		break;
 		break;
 	default:
 	default:
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
 	case CS8:
 	case CS8:
 		new_LCR |= SERIAL_8_DATA;
 		new_LCR |= SERIAL_8_DATA;
 		break;
 		break;
@@ -1299,7 +1297,7 @@ static void qt_set_termios(struct tty_struct *tty,
 			dbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n");
 			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 */
 	/* 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
 #ifndef _SPEAKUP_SERIAL_H
 #define _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 */
 #include <linux/serial_reg.h>	/* for more serial constants */
 #ifndef __sparc__
 #ifndef __sparc__
 #include <asm/serial.h>
 #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.
 	  If you haven't heard about it, it's safe to say N.
 
 
 config CYZ_INTR
 config CYZ_INTR
-	bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && CYCLADES
+	bool "Cyclades-Z interrupt mode operation"
+	depends on CYCLADES
 	help
 	help
 	  The Cyclades-Z family of multiport cards allows 2 (two) driver op
 	  The Cyclades-Z family of multiport cards allows 2 (two) driver op
 	  modes: polling and interrupt. In polling mode, the driver will check
 	  modes: polling and interrupt. In polling mode, the driver will check
@@ -285,7 +285,7 @@ config SYNCLINK_GT
 
 
 config NOZOMI
 config NOZOMI
 	tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
 	tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
-	depends on PCI && EXPERIMENTAL
+	depends on PCI
 	help
 	help
 	  If you have a HSDPA driver Broadband Wireless Data Card -
 	  If you have a HSDPA driver Broadband Wireless Data Card -
 	  Globe Trotter PCMCIA card, say Y here.
 	  Globe Trotter PCMCIA card, say Y here.
@@ -294,7 +294,7 @@ config NOZOMI
 	  will be called nozomi.
 	  will be called nozomi.
 
 
 config ISI
 config ISI
-	tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+	tristate "Multi-Tech multiport card support"
 	depends on SERIAL_NONSTANDARD && PCI
 	depends on SERIAL_NONSTANDARD && PCI
 	select FW_LOADER
 	select FW_LOADER
 	help
 	help
@@ -317,7 +317,6 @@ config N_HDLC
 
 
 config N_GSM
 config N_GSM
 	tristate "GSM MUX line discipline support (EXPERIMENTAL)"
 	tristate "GSM MUX line discipline support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
 	depends on NET
 	depends on NET
 	help
 	help
 	  This line discipline provides support for the GSM MUX protocol and
 	  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);
 				tty_hangup(port->tty);
 		}
 		}
 	}
 	}
-	if (port->flags & ASYNC_CTS_FLOW) {
+	if (tty_port_cts_enabled(port)) {
 		if (port->tty->hw_stopped) {
 		if (port->tty->hw_stopped) {
 			if (!(status & SER_CTS)) {
 			if (!(status & SER_CTS)) {
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
 #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;
 	custom.adkcon = AC_UARTBRK;
 	mb();
 	mb();
 
 
-	if (tty->termios->c_cflag & HUPCL)
+	if (tty->termios.c_cflag & HUPCL)
 		info->MCR &= ~(SER_DTR|SER_RTS);
 		info->MCR &= ~(SER_DTR|SER_RTS);
 	rtsdtr_ctrl(info->MCR);
 	rtsdtr_ctrl(info->MCR);
 
 
@@ -670,7 +670,7 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info,
 	int	bits;
 	int	bits;
 	unsigned long	flags;
 	unsigned long	flags;
 
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 
 	/* Byte size is always 8 bits plus parity bit if requested */
 	/* 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 the quotient is zero refuse the change */
 	if (!quot && old_termios) {
 	if (!quot && old_termios) {
 		/* FIXME: Will need updating for new tty in the end */
 		/* 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);
 		baud = tty_get_baud_rate(tty);
 		if (!baud)
 		if (!baud)
 			baud = 9600;
 			baud = 9600;
@@ -984,7 +984,7 @@ static void rs_throttle(struct tty_struct * tty)
 	if (I_IXOFF(tty))
 	if (I_IXOFF(tty))
 		rs_send_xchar(tty, STOP_CHAR(tty));
 		rs_send_xchar(tty, STOP_CHAR(tty));
 
 
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		info->MCR &= ~SER_RTS;
 		info->MCR &= ~SER_RTS;
 
 
 	local_irq_save(flags);
 	local_irq_save(flags);
@@ -1012,7 +1012,7 @@ static void rs_unthrottle(struct tty_struct * tty)
 		else
 		else
 			rs_send_xchar(tty, START_CHAR(tty));
 			rs_send_xchar(tty, START_CHAR(tty));
 	}
 	}
-	if (tty->termios->c_cflag & CRTSCTS)
+	if (tty->termios.c_cflag & CRTSCTS)
 		info->MCR |= SER_RTS;
 		info->MCR |= SER_RTS;
 	local_irq_save(flags);
 	local_irq_save(flags);
 	rtsdtr_ctrl(info->MCR);
 	rtsdtr_ctrl(info->MCR);
@@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
 	if (!retinfo)
 	if (!retinfo)
 		return -EFAULT;
 		return -EFAULT;
 	memset(&tmp, 0, sizeof(tmp));
 	memset(&tmp, 0, sizeof(tmp));
-	tty_lock();
+	tty_lock(tty);
 	tmp.line = tty->index;
 	tmp.line = tty->index;
 	tmp.port = state->port;
 	tmp.port = state->port;
 	tmp.flags = state->tport.flags;
 	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.close_delay = state->tport.close_delay;
 	tmp.closing_wait = state->tport.closing_wait;
 	tmp.closing_wait = state->tport.closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
 	tmp.custom_divisor = state->custom_divisor;
-	tty_unlock();
+	tty_unlock(tty);
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 		return -EFAULT;
 		return -EFAULT;
 	return 0;
 	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)))
 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
 		return -EFAULT;
 
 
-	tty_lock();
+	tty_lock(tty);
 	change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
 	change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
 		new_serial.custom_divisor != state->custom_divisor;
 		new_serial.custom_divisor != state->custom_divisor;
 	if (new_serial.irq || new_serial.port != state->port ||
 	if (new_serial.irq || new_serial.port != state->port ||
 			new_serial.xmit_fifo_size != state->xmit_fifo_size) {
 			new_serial.xmit_fifo_size != state->xmit_fifo_size) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 		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.xmit_fifo_size != state->xmit_fifo_size) ||
 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
 		     (port->flags & ~ASYNC_USR_MASK))) {
 		     (port->flags & ~ASYNC_USR_MASK))) {
-			tty_unlock();
+			tty_unlock(tty);
 			return -EPERM;
 			return -EPERM;
 		}
 		}
 		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
 		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) {
 	if (new_serial.baud_base < 9600) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -1116,7 +1116,7 @@ check_and_exit:
 		}
 		}
 	} else
 	} else
 		retval = startup(tty, state);
 		retval = startup(tty, state);
-	tty_unlock();
+	tty_unlock(tty);
 	return retval;
 	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;
 	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 	unsigned long flags;
-	unsigned int cflag = tty->termios->c_cflag;
+	unsigned int cflag = tty->termios.c_cflag;
 
 
 	change_speed(tty, info, old_termios);
 	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) &&
 	if (!(old_termios->c_cflag & CBAUD) &&
 	    (cflag & CBAUD)) {
 	    (cflag & CBAUD)) {
 		info->MCR |= SER_DTR;
 		info->MCR |= SER_DTR;
-		if (!(tty->termios->c_cflag & CRTSCTS) || 
+		if (!(tty->termios.c_cflag & CRTSCTS) || 
 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 			info->MCR |= SER_RTS;
 			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 */
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		rs_start(tty);
 		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.....
 	 * or not.  Hence, this may change.....
 	 */
 	 */
 	if (!(old_termios->c_cflag & CLOCAL) &&
 	if (!(old_termios->c_cflag & CLOCAL) &&
-	    (tty->termios->c_cflag & CLOCAL))
+	    (tty->termios.c_cflag & CLOCAL))
 		wake_up_interruptible(&info->open_wait);
 		wake_up_interruptible(&info->open_wait);
 #endif
 #endif
 }
 }
@@ -1710,10 +1710,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev)
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(serial_driver, &serial_ops);
 	tty_set_operations(serial_driver, &serial_ops);
 
 
-	error = tty_register_driver(serial_driver);
-	if (error)
-		goto fail_put_tty_driver;
-
 	state = rs_table;
 	state = rs_table;
 	state->port = (int)&custom.serdatr; /* Just to give it a value */
 	state->port = (int)&custom.serdatr; /* Just to give it a value */
 	state->custom_divisor = 0;
 	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;
 	state->icount.overrun = state->icount.brk = 0;
 	tty_port_init(&state->tport);
 	tty_port_init(&state->tport);
 	state->tport.ops = &amiga_port_ops;
 	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");
 	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->subtype      = SERIAL_TYPE_NORMAL;
 	bfin_jc_driver->init_termios = tty_std_termios;
 	bfin_jc_driver->init_termios = tty_std_termios;
 	tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
 	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);
 	ret = tty_register_driver(bfin_jc_driver);
 	if (ret)
 	if (ret)

+ 52 - 50
drivers/tty/cyclades.c

@@ -727,7 +727,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
 		else
 		else
 			tty_hangup(tty);
 			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 (tty->hw_stopped) {
 			if (mdm_status & CyCTS) {
 			if (mdm_status & CyCTS) {
 				/* cy_start isn't used
 				/* 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;
 			info->port.xmit_buf = NULL;
 			free_page((unsigned long)temp);
 			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_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR);
 
 
 		cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR);
 		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);
 			free_page((unsigned long)temp);
 		}
 		}
 
 
-		if (tty->termios->c_cflag & HUPCL)
+		if (tty->termios.c_cflag & HUPCL)
 			tty_port_lower_dtr_rts(&info->port);
 			tty_port_lower_dtr_rts(&info->port);
 
 
 		set_bit(TTY_IO_ERROR, &tty->flags);
 		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 the port is the middle of closing, bail out now
 	 */
 	 */
 	if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
 	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));
 				!(info->port.flags & ASYNC_CLOSING));
 		return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
 		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 baud, baud_rate = 0;
 	int i;
 	int i;
 
 
-	if (!tty->termios) /* XXX can this happen at all? */
-		return;
-
 	if (info->line == -1)
 	if (info->line == -1)
 		return;
 		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
 	 * 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);
 	cy_set_line_char(info, tty);
 
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		cy_start(tty);
 		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.....
 	 * or not.  Hence, this may change.....
 	 */
 	 */
 	if (!(old_termios->c_cflag & CLOCAL) &&
 	if (!(old_termios->c_cflag & CLOCAL) &&
-	    (tty->termios->c_cflag & CLOCAL))
+	    (tty->termios.c_cflag & CLOCAL))
 		wake_up_interruptible(&info->port.open_wait);
 		wake_up_interruptible(&info->port.open_wait);
 #endif
 #endif
 }				/* cy_set_termios */
 }				/* cy_set_termios */
@@ -2899,7 +2896,7 @@ static void cy_throttle(struct tty_struct *tty)
 			info->throttle = 1;
 			info->throttle = 1;
 	}
 	}
 
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		if (!cy_is_Z(card)) {
 		if (!cy_is_Z(card)) {
 			spin_lock_irqsave(&card->card_lock, flags);
 			spin_lock_irqsave(&card->card_lock, flags);
 			cyy_change_rts_dtr(info, 0, TIOCM_RTS);
 			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));
 			cy_send_xchar(tty, START_CHAR(tty));
 	}
 	}
 
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios.c_cflag & CRTSCTS) {
 		card = info->card;
 		card = info->card;
 		if (!cy_is_Z(card)) {
 		if (!cy_is_Z(card)) {
 			spin_lock_irqsave(&card->card_lock, flags);
 			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)
 static int __init cy_detect_isa(void)
 {
 {
 #ifdef CONFIG_ISA
 #ifdef CONFIG_ISA
+	struct cyclades_card *card;
 	unsigned short cy_isa_irq, nboard;
 	unsigned short cy_isa_irq, nboard;
 	void __iomem *cy_isa_address;
 	void __iomem *cy_isa_address;
-	unsigned short i, j, cy_isa_nchan;
+	unsigned short i, j, k, cy_isa_nchan;
 	int isparam = 0;
 	int isparam = 0;
 
 
 	nboard = 0;
 	nboard = 0;
@@ -3349,7 +3347,8 @@ static int __init cy_detect_isa(void)
 		}
 		}
 		/* fill the next cy_card structure available */
 		/* fill the next cy_card structure available */
 		for (j = 0; j < NR_CARDS; j++) {
 		for (j = 0; j < NR_CARDS; j++) {
-			if (cy_card[j].base_addr == NULL)
+			card = &cy_card[j];
+			if (card->base_addr == NULL)
 				break;
 				break;
 		}
 		}
 		if (j == NR_CARDS) {	/* no more cy_cards available */
 		if (j == NR_CARDS) {	/* no more cy_cards available */
@@ -3363,7 +3362,7 @@ static int __init cy_detect_isa(void)
 
 
 		/* allocate IRQ */
 		/* allocate IRQ */
 		if (request_irq(cy_isa_irq, cyy_interrupt,
 		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 "
 			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
 				"could not allocate IRQ#%d.\n",
 				"could not allocate IRQ#%d.\n",
 				(unsigned long)cy_isa_address, cy_isa_irq);
 				(unsigned long)cy_isa_address, cy_isa_irq);
@@ -3372,16 +3371,16 @@ static int __init cy_detect_isa(void)
 		}
 		}
 
 
 		/* set cy_card */
 		/* 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);
 			iounmap(cy_isa_address);
 			continue;
 			continue;
 		}
 		}
@@ -3393,9 +3392,10 @@ static int __init cy_detect_isa(void)
 			(unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
 			(unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
 			cy_isa_irq, cy_isa_nchan, cy_next_channel);
 			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;
 		cy_next_channel += cy_isa_nchan;
 	}
 	}
 	return nboard;
 	return nboard;
@@ -3695,10 +3695,11 @@ err:
 static int __devinit cy_pci_probe(struct pci_dev *pdev,
 static int __devinit cy_pci_probe(struct pci_dev *pdev,
 		const struct pci_device_id *ent)
 		const struct pci_device_id *ent)
 {
 {
+	struct cyclades_card *card;
 	void __iomem *addr0 = NULL, *addr2 = NULL;
 	void __iomem *addr0 = NULL, *addr2 = NULL;
 	char *card_name = NULL;
 	char *card_name = NULL;
 	u32 uninitialized_var(mailbox);
 	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;
 	unsigned char plx_ver;
 	int retval, irq;
 	int retval, irq;
 
 
@@ -3829,7 +3830,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 	}
 	}
 	/* fill the next cy_card structure available */
 	/* fill the next cy_card structure available */
 	for (card_no = 0; card_no < NR_CARDS; card_no++) {
 	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;
 			break;
 	}
 	}
 	if (card_no == NR_CARDS) {	/* no more cy_cards available */
 	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) {
 			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
 		/* allocate IRQ */
 		/* allocate IRQ */
 		retval = request_irq(irq, cyy_interrupt,
 		retval = request_irq(irq, cyy_interrupt,
-				IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]);
+				IRQF_SHARED, "Cyclom-Y", card);
 		if (retval) {
 		if (retval) {
 			dev_err(&pdev->dev, "could not allocate IRQ\n");
 			dev_err(&pdev->dev, "could not allocate IRQ\n");
 			goto err_unmap;
 			goto err_unmap;
 		}
 		}
-		cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
+		card->num_chips = nchan / CyPORTS_PER_CHIP;
 	} else {
 	} else {
 		struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
 		struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
 		struct ZFW_CTRL __iomem *zfw_ctrl;
 		struct ZFW_CTRL __iomem *zfw_ctrl;
 
 
 		zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
 		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
 #ifdef CONFIG_CYZ_INTR
 		/* allocate IRQ only if board has an IRQ */
 		/* allocate IRQ only if board has an IRQ */
 		if (irq != 0 && irq != 255) {
 		if (irq != 0 && irq != 255) {
 			retval = request_irq(irq, cyz_interrupt,
 			retval = request_irq(irq, cyz_interrupt,
-					IRQF_SHARED, "Cyclades-Z",
-					&cy_card[card_no]);
+					IRQF_SHARED, "Cyclades-Z", card);
 			if (retval) {
 			if (retval) {
 				dev_err(&pdev->dev, "could not allocate IRQ\n");
 				dev_err(&pdev->dev, "could not allocate IRQ\n");
 				goto err_unmap;
 				goto err_unmap;
@@ -3873,17 +3874,17 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
 	}
 	}
 
 
 	/* set cy_card */
 	/* 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)
 	if (retval)
 		goto err_null;
 		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 ||
 	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
 			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
 			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 "
 	dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
 		"port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
 		"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;
 	cy_next_channel += nchan;
 
 
 	return 0;
 	return 0;
 err_null:
 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:
 err_unmap:
 	iounmap(addr0);
 	iounmap(addr0);
 	if (addr2)
 	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;
 		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)) {
 	if (IS_ERR(bc->dev)) {
 		ret = PTR_ERR(bc->dev);
 		ret = PTR_ERR(bc->dev);
 		dev_err(&pdev->dev, "could not register tty (ret=%i)\n", ret);
 		dev_err(&pdev->dev, "could not register tty (ret=%i)\n", ret);
 		goto error;
 		goto error;
 	}
 	}
 
 
-	tty_port_init(&bc->port);
-	bc->port.ops = &ehv_bc_tty_port_ops;
-
 	dev_set_drvdata(&pdev->dev, bc);
 	dev_set_drvdata(&pdev->dev, bc);
 
 
 	dev_info(&pdev->dev, "registered /dev/%s%u for byte channel %u\n",
 	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
 config HVC_UDBG
        bool "udbg based fake hypervisor console"
        bool "udbg based fake hypervisor console"
-       depends on PPC && EXPERIMENTAL
+       depends on PPC
        select HVC_DRIVER
        select HVC_DRIVER
        default n
        default n
        help
        help

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

@@ -299,20 +299,33 @@ static void hvc_unthrottle(struct tty_struct *tty)
 	hvc_kick();
 	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
  * The TTY interface won't be used until after the vio layer has exposed the vty
  * adapter to the kernel.
  * adapter to the kernel.
  */
  */
 static int hvc_open(struct tty_struct *tty, struct file * filp)
 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;
 	unsigned long flags;
 	int rc = 0;
 	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);
 	spin_lock_irqsave(&hp->port.lock, flags);
 	/* Check and then increment for fast path open. */
 	/* Check and then increment for fast path open. */
 	if (hp->port.count++ > 0) {
 	if (hp->port.count++ > 0) {
@@ -322,7 +335,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
 	} /* else count == 0 */
 	} /* else count == 0 */
 	spin_unlock_irqrestore(&hp->port.lock, flags);
 	spin_unlock_irqrestore(&hp->port.lock, flags);
 
 
-	tty->driver_data = hp;
 	tty_port_tty_set(&hp->port, tty);
 	tty_port_tty_set(&hp->port, tty);
 
 
 	if (hp->ops->notifier_add)
 	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);
 				hp->vtermno, hp->port.count);
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 		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);
 	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;
 	struct hvc_struct *hp = tty->driver_data;
 
 
 	if (!hp)
 	if (!hp)
-		return -1;
+		return 0;
 
 
 	return hp->outbuf_size - hp->n_outbuf;
 	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
 #endif
 
 
 static const struct tty_operations hvc_ops = {
 static const struct tty_operations hvc_ops = {
+	.install = hvc_install,
 	.open = hvc_open,
 	.open = hvc_open,
 	.close = hvc_close,
 	.close = hvc_close,
+	.cleanup = hvc_cleanup,
 	.write = hvc_write,
 	.write = hvc_write,
 	.hangup = hvc_hangup,
 	.hangup = hvc_hangup,
 	.unthrottle = hvc_unthrottle,
 	.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;
 	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;
 	struct hvcs_struct *hvcsd;
-	int rc, retval = 0;
-	unsigned long flags;
-	unsigned int irq;
 	struct vio_dev *vdev;
 	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?
 	 * Is there a vty-server that shares the same index?
 	 * This function increments the kref 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"
 		printk(KERN_WARNING "HVCS: open failed, no device associated"
 				" with tty->index %d.\n", tty->index);
 				" with tty->index %d.\n", tty->index);
 		return -ENODEV;
 		return -ENODEV;
@@ -1130,11 +1123,16 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
 
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
 	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;
 	hvcsd->port.tty = tty;
 	tty->driver_data = hvcsd;
 	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
 	 * This must be done outside of the spinlock because it requests irqs
 	 * and will grab the spinlock and free the connection if it fails.
 	 * 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");
 		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);
 	spin_lock_irqsave(&hvcsd->lock, flags);
-	tty_port_get(&hvcsd->port);
 	hvcsd->port.count++;
 	hvcsd->port.count++;
 	hvcsd->todo_mask |= HVCS_SCHED_READ;
 	hvcsd->todo_mask |= HVCS_SCHED_READ;
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
 
 
-open_success:
 	hvcs_kick();
 	hvcs_kick();
 
 
 	printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n",
 	printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n",
 		hvcsd->vdev->unit_address );
 		hvcsd->vdev->unit_address );
 
 
 	return 0;
 	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)
 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;
 		tty->driver_data = NULL;
 
 
 		free_irq(irq, hvcsd);
 		free_irq(irq, hvcsd);
-		tty_port_put(&hvcsd->port);
 		return;
 		return;
 	} else if (hvcsd->port.count < 0) {
 	} else if (hvcsd->port.count < 0) {
 		printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
 		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);
 	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);
 	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 = {
 static const struct tty_operations hvcs_ops = {
+	.install = hvcs_install,
 	.open = hvcs_open,
 	.open = hvcs_open,
 	.close = hvcs_close,
 	.close = hvcs_close,
+	.cleanup = hvcs_cleanup,
 	.hangup = hvcs_hangup,
 	.hangup = hvcs_hangup,
 	.write = hvcs_write,
 	.write = hvcs_write,
 	.write_room = hvcs_write_room,
 	.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];
 		struct hvsi_struct *hp = &hvsi_ports[i];
 		int ret = 1;
 		int ret = 1;
 
 
+		tty_port_link_device(&hp->port, hvsi_driver, i);
+
 		ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
 		ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
 		if (ret)
 		if (ret)
 			printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n",
 			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);
 		spin_unlock_irqrestore(&hp->lock, flags);
 
 
 		/* Clear our own DTR */
 		/* 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);
 			hvsilib_write_mctrl(pv, 0);
 
 
 		/* Tear down the connection */
 		/* 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[0] = ~0U;
 		network->xaccm[3] = 0x60000000U;
 		network->xaccm[3] = 0x60000000U;
 		network->raccm = ~0U;
 		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);
 		spin_lock_irqsave(&network->lock, flags);
 		network->ppp_channel = channel;
 		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);
 	mutex_init(&ttys[j]->ipw_tty_mutex);
 	tty_port_init(&ttys[j]->port);
 	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]);
 	ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
 
 
 	if (secondary_channel_idx != -1)
 	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;
 					port->status &= ~ISI_DCD;
 			}
 			}
 
 
-			if (port->port.flags & ASYNC_CTS_FLOW) {
+			if (tty_port_cts_enabled(&port->port)) {
 				if (tty->hw_stopped) {
 				if (tty->hw_stopped) {
 					if (header & ISI_CTS) {
 					if (header & ISI_CTS) {
 						port->port.tty->hw_stopped = 0;
 						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. */
 		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
 		if (baud < 1 || baud > 4)
 		if (baud < 1 || baud > 4)
-			tty->termios->c_cflag &= ~CBAUDEX;
+			tty->termios.c_cflag &= ~CBAUDEX;
 		else
 		else
 			baud += 15;
 			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"))
 	if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
 		return;
 		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;
 		return;
 
 
 	spin_lock_irqsave(&port->card->card_lock, flags);
 	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);
 	spin_unlock_irqrestore(&port->card->card_lock, flags);
 
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		isicom_start(tty);
 		isicom_start(tty);
 	}
 	}
@@ -1611,7 +1611,8 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
 		goto errunri;
 		goto errunri;
 
 
 	for (index = 0; index < board->port_count; index++)
 	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);
 				&pdev->dev);
 
 
 	return 0;
 	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 long baseaddr[MAX_BOARDS];
 static unsigned int type[MAX_BOARDS];
 static unsigned int type[MAX_BOARDS];
 static unsigned int numports[MAX_BOARDS];
 static unsigned int numports[MAX_BOARDS];
+static struct tty_port moxa_service_port;
 
 
 MODULE_AUTHOR("William Chen");
 MODULE_AUTHOR("William Chen");
 MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
 MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
@@ -367,10 +368,10 @@ static int moxa_ioctl(struct tty_struct *tty,
 					tmp.dcd = 1;
 					tmp.dcd = 1;
 
 
 				ttyp = tty_port_tty_get(&p->port);
 				ttyp = tty_port_tty_get(&p->port);
-				if (!ttyp || !ttyp->termios)
+				if (!ttyp)
 					tmp.cflag = p->cflag;
 					tmp.cflag = p->cflag;
 				else
 				else
-					tmp.cflag = ttyp->termios->c_cflag;
+					tmp.cflag = ttyp->termios.c_cflag;
 				tty_kref_put(ttyp);
 				tty_kref_put(ttyp);
 copy:
 copy:
 				if (copy_to_user(argm, &tmp, sizeof(tmp)))
 				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 struct firmware *fw;
 	const char *file;
 	const char *file;
 	struct moxa_port *p;
 	struct moxa_port *p;
-	unsigned int i;
+	unsigned int i, first_idx;
 	int ret;
 	int ret;
 
 
 	brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
 	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);
 		mod_timer(&moxaTimer, jiffies + HZ / 50);
 	spin_unlock_bh(&moxa_lock);
 	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;
 	return 0;
 err_free:
 err_free:
 	kfree(brd->ports);
 	kfree(brd->ports);
@@ -896,7 +902,7 @@ err:
 
 
 static void moxa_board_deinit(struct moxa_board_conf *brd)
 static void moxa_board_deinit(struct moxa_board_conf *brd)
 {
 {
-	unsigned int a, opened;
+	unsigned int a, opened, first_idx;
 
 
 	mutex_lock(&moxa_openlock);
 	mutex_lock(&moxa_openlock);
 	spin_lock_bh(&moxa_lock);
 	spin_lock_bh(&moxa_lock);
@@ -925,6 +931,10 @@ static void moxa_board_deinit(struct moxa_board_conf *brd)
 		mutex_lock(&moxa_openlock);
 		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);
 	iounmap(brd->basemem);
 	brd->basemem = NULL;
 	brd->basemem = NULL;
 	kfree(brd->ports);
 	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);
 	board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000);
 	if (board->basemem == NULL) {
 	if (board->basemem == NULL) {
 		dev_err(&pdev->dev, "can't remap io space 2\n");
 		dev_err(&pdev->dev, "can't remap io space 2\n");
+		retval = -ENOMEM;
 		goto err_reg;
 		goto err_reg;
 	}
 	}
 
 
@@ -1031,9 +1042,14 @@ static int __init moxa_init(void)
 
 
 	printk(KERN_INFO "MOXA Intellio family driver version %s\n",
 	printk(KERN_INFO "MOXA Intellio family driver version %s\n",
 			MOXA_VERSION);
 			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->name = "ttyMX";
 	moxaDriver->major = ttymajor;
 	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_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
 	moxaDriver->init_termios.c_ispeed = 9600;
 	moxaDriver->init_termios.c_ispeed = 9600;
 	moxaDriver->init_termios.c_ospeed = 9600;
 	moxaDriver->init_termios.c_ospeed = 9600;
-	moxaDriver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(moxaDriver, &moxa_ops);
 	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)) {
 	if (tty_register_driver(moxaDriver)) {
 		printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
 		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);
 	mutex_lock(&ch->port.mutex);
 	if (!(ch->port.flags & ASYNC_INITIALIZED)) {
 	if (!(ch->port.flags & ASYNC_INITIALIZED)) {
 		ch->statusflags = 0;
 		ch->statusflags = 0;
-		moxa_set_tty_param(tty, tty->termios);
+		moxa_set_tty_param(tty, &tty->termios);
 		MoxaPortLineCtrl(ch, 1, 1);
 		MoxaPortLineCtrl(ch, 1, 1);
 		MoxaPortEnable(ch);
 		MoxaPortEnable(ch);
 		MoxaSetFifo(ch, ch->type == PORT_16550A);
 		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)
 static void moxa_close(struct tty_struct *tty, struct file *filp)
 {
 {
 	struct moxa_port *ch = tty->driver_data;
 	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);
 	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)
 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;
 	struct moxa_port *ch = tty->driver_data;
 	int rts, cts, txflow, rxflow, xany, baud;
 	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;
 	int ret = 0;
 	unsigned char status;
 	unsigned char status;
 
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 	if (!info->ioaddr)
 	if (!info->ioaddr)
 		return ret;
 		return ret;
 
 
@@ -830,7 +830,7 @@ static void mxser_check_modem_status(struct tty_struct *tty,
 			wake_up_interruptible(&port->port.open_wait);
 			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 (tty->hw_stopped) {
 			if (status & UART_MSR_CTS) {
 			if (status & UART_MSR_CTS) {
 				tty->hw_stopped = 0;
 				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);
 				tty = tty_port_tty_get(port);
 
 
-				if (!tty || !tty->termios)
+				if (!tty)
 					ms.cflag = ip->normal_termios.c_cflag;
 					ms.cflag = ip->normal_termios.c_cflag;
 				else
 				else
-					ms.cflag = tty->termios->c_cflag;
+					ms.cflag = tty->termios.c_cflag;
 				tty_kref_put(tty);
 				tty_kref_put(tty);
 				spin_lock_irq(&ip->slock);
 				spin_lock_irq(&ip->slock);
 				status = inb(ip->ioaddr + UART_MSR);
 				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);
 				tty = tty_port_tty_get(&ip->port);
 
 
-				if (!tty || !tty->termios) {
+				if (!tty) {
 					cflag = ip->normal_termios.c_cflag;
 					cflag = ip->normal_termios.c_cflag;
 					iflag = ip->normal_termios.c_iflag;
 					iflag = ip->normal_termios.c_iflag;
 					me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
 					me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
 				} else {
 				} 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);
 					me->baudrate[p] = tty_get_baud_rate(tty);
 				}
 				}
 				tty_kref_put(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;
 		info->MCR &= ~UART_MCR_RTS;
 		outb(info->MCR, info->ioaddr + UART_MCR);
 		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;
 		info->MCR |= UART_MCR_RTS;
 		outb(info->MCR, info->ioaddr + UART_MCR);
 		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);
 	spin_unlock_irqrestore(&info->slock, flags);
 
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-			!(tty->termios->c_cflag & CRTSCTS)) {
+			!(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		mxser_start(tty);
 		mxser_start(tty);
 	}
 	}
 
 
 	/* Handle sw stopped */
 	/* Handle sw stopped */
 	if ((old_termios->c_iflag & IXON) &&
 	if ((old_termios->c_iflag & IXON) &&
-			!(tty->termios->c_iflag & IXON)) {
+			!(tty->termios.c_iflag & IXON)) {
 		tty->stopped = 0;
 		tty->stopped = 0;
 
 
 		if (info->board->chip_flag) {
 		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!
  * 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)
 static void mxser_release_ISA_res(struct mxser_board *brd)
 {
 {
 	free_irq(brd->irq, brd);
 	free_irq(brd->irq, brd);
 	release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
 	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,
 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)
 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 short regs[16], irq;
 	unsigned char scratch, scratch2;
 	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);
 				8 * brd->info->nports - 1);
 		return -EIO;
 		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);
 		release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
 		printk(KERN_ERR "mxser: can't request interrupt vector region: "
 		printk(KERN_ERR "mxser: can't request interrupt vector region: "
 				"0x%.8lx-0x%.8lx\n",
 				"0x%.8lx-0x%.8lx\n",
 				brd->ports[0].ioaddr, brd->ports[0].ioaddr +
 				brd->ports[0].ioaddr, brd->ports[0].ioaddr +
 				8 * brd->info->nports - 1);
 				8 * brd->info->nports - 1);
-		return -EIO;
+		return ret;
 	}
 	}
 	return brd->info->nports;
 	return brd->info->nports;
 
 
@@ -2598,7 +2625,8 @@ static int __devinit mxser_probe(struct pci_dev *pdev,
 		goto err_rel3;
 		goto err_rel3;
 
 
 	for (i = 0; i < brd->info->nports; i++)
 	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);
 	pci_set_drvdata(pdev, brd);
 
 
@@ -2695,7 +2723,8 @@ static int __init mxser_module_init(void)
 
 
 		brd->idx = m * MXSER_PORTS_PER_BOARD;
 		brd->idx = m * MXSER_PORTS_PER_BOARD;
 		for (i = 0; i < brd->info->nports; i++)
 		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++;
 		m++;
 	}
 	}

+ 87 - 57
drivers/tty/n_gsm.c

@@ -108,7 +108,7 @@ struct gsm_mux_net {
  */
  */
 
 
 struct gsm_msg {
 struct gsm_msg {
-	struct gsm_msg *next;
+	struct list_head list;
 	u8 addr;		/* DLCI address + flags */
 	u8 addr;		/* DLCI address + flags */
 	u8 ctrl;		/* Control byte + flags */
 	u8 ctrl;		/* Control byte + flags */
 	unsigned int len;	/* Length of data block (can be zero) */
 	unsigned int len;	/* Length of data block (can be zero) */
@@ -245,8 +245,7 @@ struct gsm_mux {
 	unsigned int tx_bytes;		/* TX data outstanding */
 	unsigned int tx_bytes;		/* TX data outstanding */
 #define TX_THRESH_HI		8192
 #define TX_THRESH_HI		8192
 #define TX_THRESH_LO		2048
 #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 */
 	/* Control messages */
 	struct timer_list t2_timer;	/* Retransmit timer for commands */
 	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:
 	default:
 		if (!(control & 0x01)) {
 		if (!(control & 0x01)) {
 			pr_cont("I N(S)%d N(R)%d",
 			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) {
 		} else switch (control & 0x0F) {
 			case RR:
 			case RR:
 				pr_cont("RR(%d)", (control & 0xE0) >> 5);
 				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->len = len;
 	m->addr = addr;
 	m->addr = addr;
 	m->ctrl = ctrl;
 	m->ctrl = ctrl;
-	m->next = NULL;
+	INIT_LIST_HEAD(&m->list);
 	return m;
 	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 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
  *	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
  *	FIXME: lock against link layer control transmissions
  */
  */
 
 
 static void gsm_data_kick(struct gsm_mux *gsm)
 static void gsm_data_kick(struct gsm_mux *gsm)
 {
 {
-	struct gsm_msg *msg = gsm->tx_head;
+	struct gsm_msg *msg, *nmsg;
 	int len;
 	int len;
 	int skip_sof = 0;
 	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) {
 		if (gsm->encoding != 0) {
 			gsm->txframe[0] = GSM1_SOF;
 			gsm->txframe[0] = GSM1_SOF;
 			len = gsm_stuff_frame(msg->data,
 			len = gsm_stuff_frame(msg->data,
@@ -711,14 +709,13 @@ static void gsm_data_kick(struct gsm_mux *gsm)
 						len - skip_sof) < 0)
 						len - skip_sof) < 0)
 			break;
 			break;
 		/* FIXME: Can eliminate one SOF in many more cases */
 		/* 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;
 		gsm->tx_bytes -= msg->len;
-		kfree(msg);
 		/* For a burst of frames skip the extra SOF within the
 		/* For a burst of frames skip the extra SOF within the
 		   burst */
 		   burst */
 		skip_sof = 1;
 		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;
 	msg->data = dp;
 
 
 	/* Add to the actual output queue */
 	/* 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->tx_bytes += msg->len;
 	gsm_data_kick(gsm);
 	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 */
 	/* dlci->skb is locked by tx_lock */
 	if (dlci->skb == NULL) {
 	if (dlci->skb == NULL) {
-		dlci->skb = skb_dequeue(&dlci->skb_list);
+		dlci->skb = skb_dequeue_tail(&dlci->skb_list);
 		if (dlci->skb == NULL)
 		if (dlci->skb == NULL)
 			return 0;
 			return 0;
 		first = 1;
 		first = 1;
@@ -886,7 +879,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 	if (len > gsm->mtu) {
 	if (len > gsm->mtu) {
 		if (dlci->adaption == 3) {
 		if (dlci->adaption == 3) {
 			/* Over long frame, bin it */
 			/* Over long frame, bin it */
-			kfree_skb(dlci->skb);
+			dev_kfree_skb_any(dlci->skb);
 			dlci->skb = NULL;
 			dlci->skb = NULL;
 			return 0;
 			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
 	/* FIXME: need a timer or something to kick this so it can't
 	   get stuck with no work outstanding and no buffer free */
 	   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;
 		return -ENOMEM;
+	}
 	dp = msg->data;
 	dp = msg->data;
 
 
 	if (dlci->adaption == 4) { /* Interruptible framed (Packetised 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);
 	skb_pull(dlci->skb, len);
 	__gsm_data_queue(dlci, msg);
 	__gsm_data_queue(dlci, msg);
 	if (last) {
 	if (last) {
-		kfree_skb(dlci->skb);
+		dev_kfree_skb_any(dlci->skb);
 		dlci->skb = NULL;
 		dlci->skb = NULL;
 	}
 	}
 	return size;
 	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)
 static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
+	int sweep;
+
+	if (dlci->constipated) 
+		return;
 
 
 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
 	/* If we have nothing running then we need to fire up */
 	/* 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->gsm->tx_bytes == 0) {
 		if (dlci->net)
 		if (dlci->net)
 			gsm_dlci_data_output_framed(dlci->gsm, dlci);
 			gsm_dlci_data_output_framed(dlci->gsm, dlci);
 		else
 		else
 			gsm_dlci_data_output(dlci->gsm, dlci);
 			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);
 	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;
 	int  mlines = 0;
 	u8 brk = 0;
 	u8 brk = 0;
+	int fc;
 
 
 	/* The modem status command can either contain one octet (v.24 signals)
 	/* The modem status command can either contain one octet (v.24 signals)
 	   or two octets (v.24 signals + break signals). The length field will
 	   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 {
 	else {
 		brk = modem & 0x7f;
 		brk = modem & 0x7f;
 		modem = (modem >> 7) & 0x7f;
 		modem = (modem >> 7) & 0x7f;
-	};
+	}
 
 
 	/* Flow control/ready to communicate */
 	/* 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 */
 		/* Need to throttle our output on this device */
 		dlci->constipated = 1;
 		dlci->constipated = 1;
-	}
-	if (modem & MDM_RTC) {
-		mlines |= TIOCM_DSR | TIOCM_DTR;
+	} else if (!fc && dlci->constipated) {
 		dlci->constipated = 0;
 		dlci->constipated = 0;
 		gsm_dlci_data_kick(dlci);
 		gsm_dlci_data_kick(dlci);
 	}
 	}
+
 	/* Map modem bits */
 	/* Map modem bits */
+	if (modem & MDM_RTC)
+		mlines |= TIOCM_DSR | TIOCM_DTR;
 	if (modem & MDM_RTR)
 	if (modem & MDM_RTR)
 		mlines |= TIOCM_RTS | TIOCM_CTS;
 		mlines |= TIOCM_RTS | TIOCM_CTS;
 	if (modem & MDM_IC)
 	if (modem & MDM_IC)
@@ -1061,7 +1066,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
 	/* Carrier drop -> hangup */
 	/* Carrier drop -> hangup */
 	if (tty) {
 	if (tty) {
 		if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD))
 		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);
 				tty_hangup(tty);
 		if (brk & 0x01)
 		if (brk & 0x01)
 			tty_insert_flip_char(tty, 0, TTY_BREAK);
 			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 *data, int clen)
 {
 {
 	u8 buf[1];
 	u8 buf[1];
+	unsigned long flags;
+
 	switch (command) {
 	switch (command) {
 	case CMD_CLD: {
 	case CMD_CLD: {
 		struct gsm_dlci *dlci = gsm->dlci[0];
 		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);
 		gsm_control_reply(gsm, CMD_TEST, data, clen);
 		break;
 		break;
 	case CMD_FCON:
 	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 */
 		/* Modem can accept data again */
 		gsm->constipated = 0;
 		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 */
 		/* Kick the link in case it is idling */
+		spin_lock_irqsave(&gsm->tx_lock, flags);
 		gsm_data_kick(gsm);
 		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;
 		break;
 	case CMD_MSC:
 	case CMD_MSC:
 		/* Out of band modem line change indicator for a DLCI */
 		/* 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;
 	dlci->gsm->dlci[dlci->addr] = NULL;
 	kfifo_free(dlci->fifo);
 	kfifo_free(dlci->fifo);
 	while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
 	while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
-		kfree_skb(dlci->skb);
+		dev_kfree_skb(dlci->skb);
 	kfree(dlci);
 	kfree(dlci);
 }
 }
 
 
@@ -2007,7 +2016,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
 {
 {
 	int i;
 	int i;
 	struct gsm_dlci *dlci = gsm->dlci[0];
 	struct gsm_dlci *dlci = gsm->dlci[0];
-	struct gsm_msg *txq;
+	struct gsm_msg *txq, *ntxq;
 	struct gsm_control *gc;
 	struct gsm_control *gc;
 
 
 	gsm->dead = 1;
 	gsm->dead = 1;
@@ -2042,11 +2051,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
 		if (gsm->dlci[i])
 		if (gsm->dlci[i])
 			gsm_dlci_release(gsm->dlci[i]);
 			gsm_dlci_release(gsm->dlci[i]);
 	/* Now wipe the queues */
 	/* 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);
 		kfree(txq);
-	}
-	gsm->tx_tail = NULL;
+	INIT_LIST_HEAD(&gsm->tx_list);
 }
 }
 EXPORT_SYMBOL_GPL(gsm_cleanup_mux);
 EXPORT_SYMBOL_GPL(gsm_cleanup_mux);
 
 
@@ -2157,6 +2164,7 @@ struct gsm_mux *gsm_alloc_mux(void)
 	}
 	}
 	spin_lock_init(&gsm->lock);
 	spin_lock_init(&gsm->lock);
 	kref_init(&gsm->ref);
 	kref_init(&gsm->ref);
+	INIT_LIST_HEAD(&gsm->tx_list);
 
 
 	gsm->t1 = T1;
 	gsm->t1 = T1;
 	gsm->t2 = T2;
 	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);
 			gsm->error(gsm, *dp, flags);
 			break;
 			break;
 		default:
 		default:
-			WARN_ONCE("%s: unknown flag %d\n",
+			WARN_ONCE(1, "%s: unknown flag %d\n",
 			       tty_name(tty, buf), flags);
 			       tty_name(tty, buf), flags);
 			break;
 			break;
 		}
 		}
@@ -2377,12 +2385,12 @@ static void gsmld_write_wakeup(struct tty_struct *tty)
 
 
 	/* Queue poll */
 	/* Queue poll */
 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	spin_lock_irqsave(&gsm->tx_lock, flags);
 	gsm_data_kick(gsm);
 	gsm_data_kick(gsm);
 	if (gsm->tx_bytes < TX_THRESH_LO) {
 	if (gsm->tx_bytes < TX_THRESH_LO) {
-		spin_lock_irqsave(&gsm->tx_lock, flags);
 		gsm_dlci_data_sweep(gsm);
 		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,
 	.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_mux *gsm;
 	struct gsm_dlci *dlci;
 	struct gsm_dlci *dlci;
-	struct tty_port *port;
 	unsigned int line = tty->index;
 	unsigned int line = tty->index;
 	unsigned int mux = line >> 6;
 	unsigned int mux = line >> 6;
+	bool alloc = false;
+	int ret;
 
 
 	line = line & 0x3F;
 	line = line & 0x3F;
 
 
@@ -2889,14 +2897,35 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
 	gsm = gsm_mux[mux];
 	gsm = gsm_mux[mux];
 	if (gsm->dead)
 	if (gsm->dead)
 		return -EL2HLT;
 		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];
 	dlci = gsm->dlci[line];
-	if (dlci == NULL)
+	if (dlci == NULL) {
+		alloc = true;
 		dlci = gsm_dlci_alloc(gsm, line);
 		dlci = gsm_dlci_alloc(gsm, line);
+	}
 	if (dlci == NULL)
 	if (dlci == NULL)
 		return -ENOMEM;
 		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;
 	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);
 	dlci_get(dlci->gsm->dlci[0]);
 	dlci_get(dlci->gsm->dlci[0]);
 	mux_get(dlci->gsm);
 	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
 	   the RPN control message. This however rapidly gets nasty as we
 	   then have to remap modem signals each way according to whether
 	   then have to remap modem signals each way according to whether
 	   our virtual cable is null modem etc .. */
 	   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)
 static void gsmtty_throttle(struct tty_struct *tty)
 {
 {
 	struct gsm_dlci *dlci = tty->driver_data;
 	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->modem_tx &= ~TIOCM_DTR;
 	dlci->throttled = 1;
 	dlci->throttled = 1;
 	/* Send an MSC with DTR cleared */
 	/* 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)
 static void gsmtty_unthrottle(struct tty_struct *tty)
 {
 {
 	struct gsm_dlci *dlci = tty->driver_data;
 	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->modem_tx |= TIOCM_DTR;
 	dlci->throttled = 0;
 	dlci->throttled = 0;
 	/* Send an MSC with DTR set */
 	/* 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 */
 /* Virtual ttys for the demux */
 static const struct tty_operations gsmtty_ops = {
 static const struct tty_operations gsmtty_ops = {
+	.install		= gsmtty_install,
 	.open			= gsmtty_open,
 	.open			= gsmtty_open,
 	.close			= gsmtty_close,
 	.close			= gsmtty_close,
 	.write			= gsmtty_write,
 	.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()");
 	TRACE_L("read()");
 
 
-	tty_lock();
+	tty_lock(tty);
 
 
 	pClient = findClient(pInfo, task_pid(current));
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
 	if (pClient) {
@@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 				goto unlock;
 				goto unlock;
 			}
 			}
 			/* block until there is a message: */
 			/* 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)));
 					(pMsg = remove_msg(pInfo, pClient)));
 		}
 		}
 
 
@@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 	}
 	}
 	ret = -EPERM;
 	ret = -EPERM;
 unlock:
 unlock:
-	tty_unlock();
+	tty_unlock(tty);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	pHeader->locks = 0;
 	pHeader->locks = 0;
 	pHeader->owner = NULL;
 	pHeader->owner = NULL;
 
 
-	tty_lock();
+	tty_lock(tty);
 
 
 	pClient = findClient(pInfo, task_pid(current));
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
 	if (pClient) {
@@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	add_tx_queue(pInfo, pHeader);
 	add_tx_queue(pInfo, pHeader);
 	trigger_transmit(pInfo);
 	trigger_transmit(pInfo);
 
 
-	tty_unlock();
+	tty_unlock(tty);
 
 
 	return 0;
 	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)
 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;
 	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
 	 * If we are doing input canonicalization, and there are no
 	 * pending newlines, let characters through without limit, so
 	 * 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)
 	if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
 		tty_throttle(tty);
 		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)
 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);
 	BUG_ON(!tty);
 
 
 	if (old)
 	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) {
 	if (canon_change) {
 		memset(&tty->read_flags, 0, sizeof tty->read_flags);
 		memset(&tty->read_flags, 0, sizeof tty->read_flags);
 		tty->canon_head = tty->read_tail;
 		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:
 do_it_again:
 
 
-	BUG_ON(!tty->read_buf);
+	if (WARN_ON(!tty->read_buf))
+		return -EAGAIN;
 
 
 	c = job_control(tty, file);
 	c = job_control(tty, file);
 	if (c < 0)
 	if (c < 0)
@@ -1832,13 +1847,13 @@ do_it_again:
 
 
 		if (tty->icanon && !L_EXTPROC(tty)) {
 		if (tty->icanon && !L_EXTPROC(tty)) {
 			/* N.B. avoid overrun if nr == 0 */
 			/* N.B. avoid overrun if nr == 0 */
+			spin_lock_irqsave(&tty->read_lock, flags);
 			while (nr && tty->read_cnt) {
 			while (nr && tty->read_cnt) {
 				int eol;
 				int eol;
 
 
 				eol = test_and_clear_bit(tty->read_tail,
 				eol = test_and_clear_bit(tty->read_tail,
 						tty->read_flags);
 						tty->read_flags);
 				c = tty->read_buf[tty->read_tail];
 				c = tty->read_buf[tty->read_tail];
-				spin_lock_irqsave(&tty->read_lock, flags);
 				tty->read_tail = ((tty->read_tail+1) &
 				tty->read_tail = ((tty->read_tail+1) &
 						  (N_TTY_BUF_SIZE-1));
 						  (N_TTY_BUF_SIZE-1));
 				tty->read_cnt--;
 				tty->read_cnt--;
@@ -1856,15 +1871,19 @@ do_it_again:
 					if (tty_put_user(tty, c, b++)) {
 					if (tty_put_user(tty, c, b++)) {
 						retval = -EFAULT;
 						retval = -EFAULT;
 						b--;
 						b--;
+						spin_lock_irqsave(&tty->read_lock, flags);
 						break;
 						break;
 					}
 					}
 					nr--;
 					nr--;
 				}
 				}
 				if (eol) {
 				if (eol) {
 					tty_audit_push(tty);
 					tty_audit_push(tty);
+					spin_lock_irqsave(&tty->read_lock, flags);
 					break;
 					break;
 				}
 				}
+				spin_lock_irqsave(&tty->read_lock, flags);
 			}
 			}
+			spin_unlock_irqrestore(&tty->read_lock, flags);
 			if (retval)
 			if (retval)
 				break;
 				break;
 		} else {
 		} else {

+ 2 - 2
drivers/tty/nozomi.c

@@ -1473,8 +1473,8 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
 		port->dc = dc;
 		port->dc = dc;
 		tty_port_init(&port->port);
 		tty_port_init(&port->port);
 		port->port.ops = &noz_tty_port_ops;
 		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)) {
 		if (IS_ERR(tty_dev)) {
 			ret = PTR_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->read_wait);
 	wake_up_interruptible(&tty->write_wait);
 	wake_up_interruptible(&tty->write_wait);
 	tty->packet = 0;
 	tty->packet = 0;
+	/* Review - krefs on tty_link ?? */
 	if (!tty->link)
 	if (!tty->link)
 		return;
 		return;
 	tty->link->packet = 0;
 	tty->link->packet = 0;
@@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 		        mutex_unlock(&devpts_mutex);
 		        mutex_unlock(&devpts_mutex);
 		}
 		}
 #endif
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		tty_vhangup(tty->link);
 		tty_vhangup(tty->link);
-		tty_lock();
+		tty_lock(tty);
 	}
 	}
 }
 }
 
 
@@ -231,8 +232,8 @@ out:
 static void pty_set_termios(struct tty_struct *tty,
 static void pty_set_termios(struct tty_struct *tty,
 					struct ktermios *old_termios)
 					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;
 	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_struct *o_tty;
+	struct tty_port *ports[2];
 	int idx = tty->index;
 	int idx = tty->index;
-	int retval;
+	int retval = -ENOMEM;
 
 
 	o_tty = alloc_tty_struct();
 	o_tty = alloc_tty_struct();
 	if (!o_tty)
 	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)) {
 	if (!try_module_get(driver->other->owner)) {
 		/* This cannot in fact currently happen */
 		/* This cannot in fact currently happen */
-		retval = -ENOMEM;
 		goto err_free_tty;
 		goto err_free_tty;
 	}
 	}
 	initialize_tty_struct(o_tty, driver->other, idx);
 	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.
 	 * Everything allocated ... set up the o_tty structure.
 	 */
 	 */
-	driver->other->ttys[idx] = o_tty;
 	tty_driver_kref_get(driver->other);
 	tty_driver_kref_get(driver->other);
 	if (driver->subtype == PTY_TYPE_MASTER)
 	if (driver->subtype == PTY_TYPE_MASTER)
 		o_tty->count++;
 		o_tty->count++;
 	/* Establish the links in both directions */
 	/* Establish the links in both directions */
 	tty->link   = o_tty;
 	tty->link   = o_tty;
 	o_tty->link = 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_driver_kref_get(driver);
 	tty->count++;
 	tty->count++;
-	driver->ttys[idx] = tty;
 	return 0;
 	return 0;
 err_free_termios:
 err_free_termios:
-	tty_free_termios(tty);
+	if (legacy)
+		tty_free_termios(tty);
 err_deinit_tty:
 err_deinit_tty:
 	deinitialize_tty_struct(o_tty);
 	deinitialize_tty_struct(o_tty);
 	module_put(o_tty->driver->owner);
 	module_put(o_tty->driver->owner);
 err_free_tty:
 err_free_tty:
+	kfree(ports[0]);
+	kfree(ports[1]);
 	free_tty_struct(o_tty);
 	free_tty_struct(o_tty);
+err:
 	return retval;
 	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,
 static int pty_bsd_ioctl(struct tty_struct *tty,
 			 unsigned int cmd, unsigned long arg)
 			 unsigned int cmd, unsigned long arg)
 {
 {
@@ -366,7 +417,9 @@ static const struct tty_operations master_pty_ops_bsd = {
 	.unthrottle = pty_unthrottle,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.set_termios = pty_set_termios,
 	.ioctl = pty_bsd_ioctl,
 	.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 = {
 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,
 	.chars_in_buffer = pty_chars_in_buffer,
 	.unthrottle = pty_unthrottle,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.set_termios = pty_set_termios,
-	.resize = pty_resize
+	.cleanup = pty_cleanup,
+	.resize = pty_resize,
+	.remove = pty_remove
 };
 };
 
 
 static void __init legacy_pty_init(void)
 static void __init legacy_pty_init(void)
@@ -389,12 +444,18 @@ static void __init legacy_pty_init(void)
 	if (legacy_count <= 0)
 	if (legacy_count <= 0)
 		return;
 		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");
 		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");
 		panic("Couldn't allocate pty slave driver");
 
 
 	pty_driver->driver_name = "pty_master";
 	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_lflag = 0;
 	pty_driver->init_termios.c_ispeed = 38400;
 	pty_driver->init_termios.c_ispeed = 38400;
 	pty_driver->init_termios.c_ospeed = 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;
 	pty_driver->other = pty_slave_driver;
 	tty_set_operations(pty_driver, &master_pty_ops_bsd);
 	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_cflag = B38400 | CS8 | CREAD;
 	pty_slave_driver->init_termios.c_ispeed = 38400;
 	pty_slave_driver->init_termios.c_ispeed = 38400;
 	pty_slave_driver->init_termios.c_ospeed = 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;
 	pty_slave_driver->other = pty_driver;
 	tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd);
 	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;
 	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
 /* We have no need to install and remove our tty objects as devpts does all
    the work for us */
    the work for us */
 
 
 static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
 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 = {
 static const struct tty_operations ptm_unix98_ops = {
 	.lookup = ptm_unix98_lookup,
 	.lookup = ptm_unix98_lookup,
 	.install = pty_unix98_install,
 	.install = pty_unix98_install,
-	.remove = ptm_unix98_remove,
+	.remove = pty_unix98_remove,
 	.open = pty_open,
 	.open = pty_open,
 	.close = pty_close,
 	.close = pty_close,
 	.write = pty_write,
 	.write = pty_write,
@@ -578,14 +580,14 @@ static const struct tty_operations ptm_unix98_ops = {
 	.unthrottle = pty_unthrottle,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.set_termios = pty_set_termios,
 	.ioctl = pty_unix98_ioctl,
 	.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 = {
 static const struct tty_operations pty_unix98_ops = {
 	.lookup = pts_unix98_lookup,
 	.lookup = pts_unix98_lookup,
 	.install = pty_unix98_install,
 	.install = pty_unix98_install,
-	.remove = pts_unix98_remove,
+	.remove = pty_unix98_remove,
 	.open = pty_open,
 	.open = pty_open,
 	.close = pty_close,
 	.close = pty_close,
 	.write = pty_write,
 	.write = pty_write,
@@ -594,7 +596,7 @@ static const struct tty_operations pty_unix98_ops = {
 	.chars_in_buffer = pty_chars_in_buffer,
 	.chars_in_buffer = pty_chars_in_buffer,
 	.unthrottle = pty_unthrottle,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.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;
 		return retval;
 
 
 	/* find a device that is not in use. */
 	/* find a device that is not in use. */
-	tty_lock();
+	mutex_lock(&devpts_mutex);
 	index = devpts_new_index(inode);
 	index = devpts_new_index(inode);
-	tty_unlock();
 	if (index < 0) {
 	if (index < 0) {
 		retval = index;
 		retval = index;
+		mutex_unlock(&devpts_mutex);
 		goto err_file;
 		goto err_file;
 	}
 	}
 
 
+	mutex_unlock(&devpts_mutex);
+
 	mutex_lock(&tty_mutex);
 	mutex_lock(&tty_mutex);
-	mutex_lock(&devpts_mutex);
 	tty = tty_init_dev(ptm_driver, index);
 	tty = tty_init_dev(ptm_driver, index);
-	mutex_unlock(&devpts_mutex);
-	tty_lock();
-	mutex_unlock(&tty_mutex);
 
 
 	if (IS_ERR(tty)) {
 	if (IS_ERR(tty)) {
 		retval = PTR_ERR(tty);
 		retval = PTR_ERR(tty);
 		goto out;
 		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 */
 	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
 
 
 	tty_add_file(tty, filp);
 	tty_add_file(tty, filp);
@@ -654,15 +658,15 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 	if (retval)
 	if (retval)
 		goto err_release;
 		goto err_release;
 
 
-	tty_unlock();
+	tty_unlock(tty);
 	return 0;
 	return 0;
 err_release:
 err_release:
-	tty_unlock();
+	tty_unlock(tty);
 	tty_release(inode, filp);
 	tty_release(inode, filp);
 	return retval;
 	return retval;
 out:
 out:
+	mutex_unlock(&tty_mutex);
 	devpts_kill_index(inode, index);
 	devpts_kill_index(inode, index);
-	tty_unlock();
 err_file:
 err_file:
 	tty_free_file(filp);
 	tty_free_file(filp);
 	return retval;
 	return retval;
@@ -672,11 +676,21 @@ static struct file_operations ptmx_fops;
 
 
 static void __init unix98_pty_init(void)
 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");
 		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");
 		panic("Couldn't allocate Unix98 pts driver");
 
 
 	ptm_driver->driver_name = "pty_master";
 	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_lflag = 0;
 	ptm_driver->init_termios.c_ispeed = 38400;
 	ptm_driver->init_termios.c_ispeed = 38400;
 	ptm_driver->init_termios.c_ospeed = 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;
 	ptm_driver->other = pts_driver;
 	tty_set_operations(ptm_driver, &ptm_unix98_ops);
 	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_cflag = B38400 | CS8 | CREAD;
 	pts_driver->init_termios.c_ispeed = 38400;
 	pts_driver->init_termios.c_ispeed = 38400;
 	pts_driver->init_termios.c_ospeed = 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;
 	pts_driver->other = ptm_driver;
 	tty_set_operations(pts_driver, &pty_unix98_ops);
 	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);
 	spin_lock_init(&info->slock);
 	mutex_init(&info->write_mtx);
 	mutex_init(&info->write_mtx);
 	rp_table[line] = info;
 	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;
 	unsigned rocketMode;
 	int bits, baud, divisor;
 	int bits, baud, divisor;
 	CHANNEL_t *cp;
 	CHANNEL_t *cp;
-	struct ktermios *t = tty->termios;
+	struct ktermios *t = &tty->termios;
 
 
 	cp = &info->channel;
 	cp = &info->channel;
 	cflag = t->c_cflag;
 	cflag = t->c_cflag;
@@ -978,7 +978,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
 			tty->alt_speed = 460800;
 			tty->alt_speed = 460800;
 
 
 		configure_r_port(tty, info, NULL);
 		configure_r_port(tty, info, NULL);
-		if (tty->termios->c_cflag & CBAUD) {
+		if (tty->termios.c_cflag & CBAUD) {
 			sSetDTR(cp);
 			sSetDTR(cp);
 			sSetRTS(cp);
 			sSetRTS(cp);
 		}
 		}
@@ -1089,35 +1089,35 @@ static void rp_set_termios(struct tty_struct *tty,
 	if (rocket_paranoia_check(info, "rp_set_termios"))
 	if (rocket_paranoia_check(info, "rp_set_termios"))
 		return;
 		return;
 
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 
 
 	/*
 	/*
 	 * This driver doesn't support CS5 or CS6
 	 * This driver doesn't support CS5 or CS6
 	 */
 	 */
 	if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
 	if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
-		tty->termios->c_cflag =
+		tty->termios.c_cflag =
 		    ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
 		    ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
 	/* Or CMSPAR */
 	/* Or CMSPAR */
-	tty->termios->c_cflag &= ~CMSPAR;
+	tty->termios.c_cflag &= ~CMSPAR;
 
 
 	configure_r_port(tty, info, old_termios);
 	configure_r_port(tty, info, old_termios);
 
 
 	cp = &info->channel;
 	cp = &info->channel;
 
 
 	/* Handle transition to B0 status */
 	/* 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);
 		sClrDTR(cp);
 		sClrRTS(cp);
 		sClrRTS(cp);
 	}
 	}
 
 
 	/* Handle transition away from B0 status */
 	/* 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);
 			sSetRTS(cp);
 		sSetDTR(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;
 		tty->hw_stopped = 0;
 		rp_start(tty);
 		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;
 	unsigned cflag;
 	int	i;
 	int	i;
 
 
-	cflag = tty->termios->c_cflag;
+	cflag = tty->termios.c_cflag;
 	if (!(port = info->port))
 	if (!(port = info->port))
 		return;
 		return;
 
 
@@ -617,7 +617,7 @@ static void rs_set_ldisc(struct tty_struct *tty)
 	if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
 	if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
 		return;
 		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");
 	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);
 	change_speed(info, tty);
 
 
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		rs_start(tty);
 		rs_start(tty);
 	}
 	}
@@ -1070,7 +1070,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		if (tty->ldisc.close)
 		if (tty->ldisc.close)
 			(tty->ldisc.close)(tty);
 			(tty->ldisc.close)(tty);
 		tty->ldisc = ldiscs[N_TTY];
 		tty->ldisc = ldiscs[N_TTY];
-		tty->termios->c_line = N_TTY;
+		tty->termios.c_line = N_TTY;
 		if (tty->ldisc.open)
 		if (tty->ldisc.open)
 			(tty->ldisc.open)(tty);
 			(tty->ldisc.open)(tty);
 	}
 	}
@@ -1189,12 +1189,6 @@ rs68328_init(void)
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
 	tty_set_operations(serial_driver, &rs_ops);
 	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);
 	local_irq_save(flags);
 
 
 	for(i=0;i<NR_PORTS;i++) {
 	for(i=0;i<NR_PORTS;i++) {
@@ -1224,8 +1218,17 @@ rs68328_init(void)
 			    0,
 			    0,
 			    "M68328_UART", info))
 			    "M68328_UART", info))
                 panic("Unable to attach 68328 serial interrupt\n");
                 panic("Unable to attach 68328 serial interrupt\n");
+
+	    tty_port_link_device(&info->tport, serial_driver, i);
 	}
 	}
 	local_irq_restore(flags);
 	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;
 	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,
 				  UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
 		.flags		= UART_CAP_FIFO,
 		.flags		= UART_CAP_FIFO,
 	},
 	},
+	[PORT_8250_CIR] = {
+		.name		= "CIR port"
+	}
 };
 };
 
 
 /* Uart divisor latch read */
 /* 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;
 	unsigned char save_lcr, save_mcr;
 	struct uart_port *port = &up->port;
 	struct uart_port *port = &up->port;
 	unsigned long flags;
 	unsigned long flags;
+	unsigned int old_capabilities;
 
 
 	if (!port->iobase && !port->mapbase && !port->membase)
 	if (!port->iobase && !port->mapbase && !port->membase)
 		return;
 		return;
@@ -1087,6 +1091,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 			/*
 			/*
 			 * We failed; there's nothing here
 			 * We failed; there's nothing here
 			 */
 			 */
+			spin_unlock_irqrestore(&port->lock, flags);
 			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
 			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
 				       scratch2, scratch3);
 				       scratch2, scratch3);
 			goto out;
 			goto out;
@@ -1110,6 +1115,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 		status1 = serial_in(up, UART_MSR) & 0xF0;
 		status1 = serial_in(up, UART_MSR) & 0xF0;
 		serial_out(up, UART_MCR, save_mcr);
 		serial_out(up, UART_MCR, save_mcr);
 		if (status1 != 0x90) {
 		if (status1 != 0x90) {
+			spin_unlock_irqrestore(&port->lock, flags);
 			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
 			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
 				       status1);
 				       status1);
 			goto out;
 			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);
 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
 	scratch = serial_in(up, UART_IIR) >> 6;
 	scratch = serial_in(up, UART_IIR) >> 6;
 
 
-	DEBUG_AUTOCONF("iir=%d ", scratch);
-
 	switch (scratch) {
 	switch (scratch) {
 	case 0:
 	case 0:
 		autoconfig_8250(up);
 		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);
 	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;
 	port->fifosize = uart_config[up->port.type].fifo_size;
+	old_capabilities = up->capabilities; 
 	up->capabilities = uart_config[port->type].flags;
 	up->capabilities = uart_config[port->type].flags;
 	up->tx_loadsz = uart_config[port->type].tx_loadsz;
 	up->tx_loadsz = uart_config[port->type].tx_loadsz;
 
 
 	if (port->type == PORT_UNKNOWN)
 	if (port->type == PORT_UNKNOWN)
-		goto out;
+		goto out_lock;
 
 
 	/*
 	/*
 	 * Reset the UART.
 	 * Reset the UART.
@@ -1196,8 +1194,16 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
 	else
 	else
 		serial_out(up, UART_IER, 0);
 		serial_out(up, UART_IER, 0);
 
 
- out:
+out_lock:
 	spin_unlock_irqrestore(&port->lock, flags);
 	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);
 	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;
 	unsigned char lsr, iir;
 	int retval;
 	int retval;
 
 
+	if (port->type == PORT_8250_CIR)
+		return -ENODEV;
+
 	port->fifosize = uart_config[up->port.type].fifo_size;
 	port->fifosize = uart_config[up->port.type].fifo_size;
 	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
 	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
 	up->capabilities = uart_config[up->port.type].flags;
 	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 char cval, fcr = 0;
 	unsigned long flags;
 	unsigned long flags;
 	unsigned int baud, quot;
 	unsigned int baud, quot;
+	int fifo_bug = 0;
 
 
 	switch (termios->c_cflag & CSIZE) {
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
 	case CS5:
@@ -2221,8 +2231,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
 
 	if (termios->c_cflag & CSTOPB)
 	if (termios->c_cflag & CSTOPB)
 		cval |= UART_LCR_STOP;
 		cval |= UART_LCR_STOP;
-	if (termios->c_cflag & PARENB)
+	if (termios->c_cflag & PARENB) {
 		cval |= UART_LCR_PARITY;
 		cval |= UART_LCR_PARITY;
+		if (up->bugs & UART_BUG_PARITY)
+			fifo_bug = 1;
+	}
 	if (!(termios->c_cflag & PARODD))
 	if (!(termios->c_cflag & PARODD))
 		cval |= UART_LCR_EPAR;
 		cval |= UART_LCR_EPAR;
 #ifdef CMSPAR
 #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) {
 	if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
 		fcr = uart_config[port->type].fcr;
 		fcr = uart_config[port->type].fcr;
-		if (baud < 2400) {
+		if (baud < 2400 || fifo_bug) {
 			fcr &= ~UART_FCR_TRIGGER_MASK;
 			fcr &= ~UART_FCR_TRIGGER_MASK;
 			fcr |= UART_FCR_TRIGGER_1;
 			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);
 			serial_port_out(port, UART_EFR, efr);
 	}
 	}
 
 
-#ifdef CONFIG_ARCH_OMAP
+#ifdef CONFIG_ARCH_OMAP1
 	/* Workaround to enable 115200 baud on OMAP1510 internal ports */
 	/* Workaround to enable 115200 baud on OMAP1510 internal ports */
 	if (cpu_is_omap1510() && is_omap_port(up)) {
 	if (cpu_is_omap1510() && is_omap_port(up)) {
 		if (baud == 115200) {
 		if (baud == 115200) {
@@ -2426,7 +2439,7 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
 {
 {
 	if (pt->port.iotype == UPIO_AU)
 	if (pt->port.iotype == UPIO_AU)
 		return 0x1000;
 		return 0x1000;
-#ifdef CONFIG_ARCH_OMAP
+#ifdef CONFIG_ARCH_OMAP1
 	if (is_omap_port(pt))
 	if (is_omap_port(pt))
 		return 0x16 << pt->port.regshift;
 		return 0x16 << pt->port.regshift;
 #endif
 #endif
@@ -2550,7 +2563,10 @@ static int serial8250_request_port(struct uart_port *port)
 {
 {
 	struct uart_8250_port *up =
 	struct uart_8250_port *up =
 		container_of(port, struct uart_8250_port, port);
 		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);
 	ret = serial8250_request_std_resource(up);
 	if (ret == 0 && port->type == PORT_RSA) {
 	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 probeflags = PROBE_ANY;
 	int ret;
 	int ret;
 
 
+	if (port->type == PORT_8250_CIR)
+		return;
+
 	/*
 	/*
 	 * Find the region that we can probe for.  This in turn
 	 * Find the region that we can probe for.  This in turn
 	 * tells us whether we can probe for the type of port.
 	 * tells us whether we can probe for the type of port.
@@ -2668,6 +2687,9 @@ static void __init serial8250_isa_init_ports(void)
 		return;
 		return;
 	first = 0;
 	first = 0;
 
 
+	if (nr_uarts > UART_NR)
+		nr_uarts = UART_NR;
+
 	for (i = 0; i < nr_uarts; i++) {
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[i];
 		struct uart_8250_port *up = &serial8250_ports[i];
 		struct uart_port *port = &up->port;
 		struct uart_port *port = &up->port;
@@ -2677,6 +2699,7 @@ static void __init serial8250_isa_init_ports(void)
 
 
 		init_timer(&up->timer);
 		init_timer(&up->timer);
 		up->timer.function = serial8250_timeout;
 		up->timer.function = serial8250_timeout;
+		up->cur_iotype = 0xFF;
 
 
 		/*
 		/*
 		 * ALPHA_KLUDGE_MCR needs to be killed.
 		 * 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++) {
 	for (i = 0; i < nr_uarts; i++) {
 		struct uart_8250_port *up = &serial8250_ports[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;
 		up->port.dev = dev;
 
 
@@ -2859,9 +2878,6 @@ static struct console serial8250_console = {
 
 
 static int __init serial8250_console_init(void)
 static int __init serial8250_console_init(void)
 {
 {
-	if (nr_uarts > UART_NR)
-		nr_uarts = UART_NR;
-
 	serial8250_isa_init_ports();
 	serial8250_isa_init_ports();
 	register_console(&serial8250_console);
 	register_console(&serial8250_console);
 	return 0;
 	return 0;
@@ -2979,36 +2995,36 @@ void serial8250_resume_port(int line)
 static int __devinit serial8250_probe(struct platform_device *dev)
 static int __devinit serial8250_probe(struct platform_device *dev)
 {
 {
 	struct plat_serial8250_port *p = dev->dev.platform_data;
 	struct plat_serial8250_port *p = dev->dev.platform_data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
 	int ret, i, irqflag = 0;
 
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 
 	if (share_irqs)
 	if (share_irqs)
 		irqflag = IRQF_SHARED;
 		irqflag = IRQF_SHARED;
 
 
 	for (i = 0; p && p->flags != 0; p++, i++) {
 	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) {
 		if (ret < 0) {
 			dev_err(&dev->dev, "unable to register port at index %d "
 			dev_err(&dev->dev, "unable to register port at index %d "
 				"(IO%lx MEM%llx IRQ%d): %d\n", i,
 				"(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;
 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
  * 16x50 serial ports to be configured at run-time, to support PCMCIA
  * modems and PCI multiport cards.
  * modems and PCI multiport cards.
  */
  */
@@ -3143,8 +3159,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 	mutex_lock(&serial_mutex);
 	mutex_lock(&serial_mutex);
 
 
 	uart = serial8250_find_match_or_unused(&up->port);
 	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.iobase       = up->port.iobase;
 		uart->port.membase      = up->port.membase;
 		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.regshift     = up->port.regshift;
 		uart->port.iotype       = up->port.iotype;
 		uart->port.iotype       = up->port.iotype;
 		uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
 		uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
+		uart->bugs		= up->bugs;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.private_data = up->port.private_data;
 		uart->port.private_data = up->port.private_data;
 		if (up->port.dev)
 		if (up->port.dev)
@@ -3197,29 +3215,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 }
 }
 EXPORT_SYMBOL(serial8250_register_8250_port);
 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
  *	serial8250_unregister_port - remove a 16x50 serial port at runtime
  *	@line: serial line number
  *	@line: serial line number
@@ -3250,8 +3245,7 @@ static int __init serial8250_init(void)
 {
 {
 	int ret;
 	int ret;
 
 
-	if (nr_uarts > UART_NR)
-		nr_uarts = UART_NR;
+	serial8250_isa_init_ports();
 
 
 	printk(KERN_INFO "Serial: 8250/16550 driver, "
 	printk(KERN_INFO "Serial: 8250/16550 driver, "
 		"%d ports, IRQ sharing %sabled\n", nr_uarts,
 		"%d ports, IRQ sharing %sabled\n", nr_uarts,
@@ -3266,11 +3260,15 @@ static int __init serial8250_init(void)
 	if (ret)
 	if (ret)
 		goto out;
 		goto out;
 
 
+	ret = serial8250_pnp_init();
+	if (ret)
+		goto unreg_uart_drv;
+
 	serial8250_isa_devs = platform_device_alloc("serial8250",
 	serial8250_isa_devs = platform_device_alloc("serial8250",
 						    PLAT8250_DEV_LEGACY);
 						    PLAT8250_DEV_LEGACY);
 	if (!serial8250_isa_devs) {
 	if (!serial8250_isa_devs) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto unreg_uart_drv;
+		goto unreg_pnp;
 	}
 	}
 
 
 	ret = platform_device_add(serial8250_isa_devs);
 	ret = platform_device_add(serial8250_isa_devs);
@@ -3286,6 +3284,8 @@ static int __init serial8250_init(void)
 	platform_device_del(serial8250_isa_devs);
 	platform_device_del(serial8250_isa_devs);
 put_dev:
 put_dev:
 	platform_device_put(serial8250_isa_devs);
 	platform_device_put(serial8250_isa_devs);
+unreg_pnp:
+	serial8250_pnp_exit();
 unreg_uart_drv:
 unreg_uart_drv:
 #ifdef CONFIG_SPARC
 #ifdef CONFIG_SPARC
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
@@ -3310,6 +3310,8 @@ static void __exit serial8250_exit(void)
 	platform_driver_unregister(&serial8250_isa_driver);
 	platform_driver_unregister(&serial8250_isa_driver);
 	platform_device_unregister(isa_dev);
 	platform_device_unregister(isa_dev);
 
 
+	serial8250_pnp_exit();
+
 #ifdef CONFIG_SPARC
 #ifdef CONFIG_SPARC
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
 	sunserial_unregister_minors(&serial8250_reg, UART_NR);
 #else
 #else

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

@@ -13,36 +13,6 @@
 
 
 #include <linux/serial_8250.h>
 #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 {
 struct old_serial_port {
 	unsigned int uart;
 	unsigned int uart;
 	unsigned int baud_base;
 	unsigned int baud_base;
@@ -56,9 +26,6 @@ struct old_serial_port {
 	unsigned long irqflags;
 	unsigned long irqflags;
 };
 };
 
 
-/*
- * This replaces serial_uart_config in include/linux/serial.h
- */
 struct serial8250_config {
 struct serial8250_config {
 	const char	*name;
 	const char	*name;
 	unsigned short	fifo_size;
 	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_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_NOMSR	(1 << 2)	/* UART has buggy MSR status bits (Au1x00) */
 #define UART_BUG_THRE	(1 << 3)	/* UART has buggy THRE reassertion */
 #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_RSA	(1 << 0)
 #define PROBE_ANY	(~0)
 #define PROBE_ANY	(~0)
@@ -129,3 +97,12 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
 #else
 #else
 #define ALPHA_KLUDGE_MCR 0
 #define ALPHA_KLUDGE_MCR 0
 #endif
 #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_info *info;
 	struct serial_card_type *type = id->data;
 	struct serial_card_type *type = id->data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long bus_addr;
 	unsigned long bus_addr;
 	unsigned int i;
 	unsigned int i;
 
 
@@ -62,19 +62,19 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
 
 
 	ecard_set_drvdata(ec, info);
 	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 ++) {
 	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;
 	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)
 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 *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct device_node *np = pdev->dev.of_node;
 	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);
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 	if (!data)
 		return -ENOMEM;
 		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;
 		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)) {
 	if (!of_property_read_u32(np, "reg-io-width", &val)) {
 		switch (val) {
 		switch (val) {
 		case 1:
 		case 1:
 			break;
 			break;
 		case 4:
 		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;
 			break;
 		default:
 		default:
 			dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
 			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))
 	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)) {
 	if (of_property_read_u32(np, "clock-frequency", &val)) {
 		dev_err(&pdev->dev, "no clock-frequency property set\n");
 		dev_err(&pdev->dev, "no clock-frequency property set\n");
 		return -EINVAL;
 		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)
 	if (data->line < 0)
 		return data->line;
 		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)
 static int __init serial_init_chip(struct parisc_device *dev)
 {
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long address;
 	unsigned long address;
 	int err;
 	int err;
 
 
@@ -48,21 +48,21 @@ static int __init serial_init_chip(struct parisc_device *dev)
 	if (dev->id.sversion != 0x8d)
 	if (dev->id.sversion != 0x8d)
 		address += 0x800;
 		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. */
 	/* 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) {
 	if (err < 0) {
 		printk(KERN_WARNING
 		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;
 		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;
 		return 0;
 	}
 	}
 #endif
 #endif
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 
 	/* Memory mapped I/O */
 	/* Memory mapped I/O */
 	port.iotype = UPIO_MEM;
 	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.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
 	port.regshift = 1;
 	port.regshift = 1;
 	port.dev = &d->dev;
 	port.dev = &d->dev;
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 
 
 	if (line < 0) {
 	if (line < 0) {
 		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
 		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
@@ -210,7 +210,7 @@ static int __init hp300_8250_init(void)
 #ifdef CONFIG_HPAPCI
 #ifdef CONFIG_HPAPCI
 	int line;
 	int line;
 	unsigned long base;
 	unsigned long base;
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	struct hp300_port *port;
 	struct hp300_port *port;
 	int i;
 	int i;
 #endif
 #endif
@@ -248,26 +248,26 @@ static int __init hp300_8250_init(void)
 		if (!port)
 		if (!port)
 			return -ENOMEM;
 			return -ENOMEM;
 
 
-		memset(&uport, 0, sizeof(struct uart_port));
+		memset(&uart, 0, sizeof(uart));
 
 
 		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
 		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
 
 
 		/* Memory mapped I/O */
 		/* 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;
 			      | UPF_BOOT_AUTOCONF;
 		/* XXX - no interrupt support yet */
 		/* 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) {
 		if (line < 0) {
 			printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
 			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);
 			kfree(port);
 			continue;
 			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	(*init)(struct pci_dev *dev);
 	int	(*setup)(struct serial_private *,
 	int	(*setup)(struct serial_private *,
 			 const struct pciserial_board *,
 			 const struct pciserial_board *,
-			 struct uart_port *, int);
+			 struct uart_8250_port *, int);
 	void	(*exit)(struct pci_dev *dev);
 	void	(*exit)(struct pci_dev *dev);
 };
 };
 
 
@@ -59,7 +59,7 @@ struct serial_private {
 };
 };
 
 
 static int pci_default_setup(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)
 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
 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)
 	   int bar, int offset, int regshift)
 {
 {
 	struct pci_dev *dev = priv->dev;
 	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])
 		if (!priv->remapped_bar[bar])
 			return -ENOMEM;
 			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 {
 	} 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;
 	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,
 static int addidata_apci7800_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
 				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;
 	unsigned int bar = 0, offset = board->first_offset;
 	bar = FL_GET_BASE(board->flags);
 	bar = FL_GET_BASE(board->flags);
@@ -140,7 +140,7 @@ static int addidata_apci7800_setup(struct serial_private *priv,
  */
  */
 static int
 static int
 afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
 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;
 	unsigned int bar, offset = board->first_offset;
 
 
@@ -195,7 +195,7 @@ static int pci_hp_diva_init(struct pci_dev *dev)
 static int
 static int
 pci_hp_diva_setup(struct serial_private *priv,
 pci_hp_diva_setup(struct serial_private *priv,
 		const struct pciserial_board *board,
 		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 offset = board->first_offset;
 	unsigned int bar = FL_GET_BASE(board->flags);
 	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 */
 /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
 static int
 static int
 sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
 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;
 	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,
 static int pci_siig_setup(struct serial_private *priv,
 			  const struct pciserial_board *board,
 			  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;
 	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
 static int
 pci_timedia_setup(struct serial_private *priv,
 pci_timedia_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  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;
 	unsigned int bar = 0, offset = board->first_offset;
 
 
@@ -653,7 +653,7 @@ pci_timedia_setup(struct serial_private *priv,
 static int
 static int
 titan_400l_800l_setup(struct serial_private *priv,
 titan_400l_800l_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
 		      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;
 	unsigned int bar, offset = board->first_offset;
 
 
@@ -754,7 +754,7 @@ static int pci_ni8430_init(struct pci_dev *dev)
 static int
 static int
 pci_ni8430_setup(struct serial_private *priv,
 pci_ni8430_setup(struct serial_private *priv,
 		 const struct pciserial_board *board,
 		 const struct pciserial_board *board,
-		 struct uart_port *port, int idx)
+		 struct uart_8250_port *port, int idx)
 {
 {
 	void __iomem *p;
 	void __iomem *p;
 	unsigned long base, len;
 	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,
 static int pci_netmos_9900_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
 				const struct pciserial_board *board,
-				struct uart_port *port, int idx)
+				struct uart_8250_port *port, int idx)
 {
 {
 	unsigned int bar;
 	unsigned int bar;
 
 
@@ -1032,10 +1032,17 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
 	return number_uarts;
 	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,
 		  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;
 	unsigned int bar, offset = board->first_offset, maxnr;
 
 
@@ -1057,15 +1064,15 @@ pci_default_setup(struct serial_private *priv,
 static int
 static int
 ce4100_serial_setup(struct serial_private *priv,
 ce4100_serial_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 {
 	int ret;
 	int ret;
 
 
 	ret = setup_port(priv, port, 0, 0, board->reg_shift);
 	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;
 	return ret;
 }
 }
@@ -1073,16 +1080,16 @@ ce4100_serial_setup(struct serial_private *priv,
 static int
 static int
 pci_omegapci_setup(struct serial_private *priv,
 pci_omegapci_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
 		      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);
 	return setup_port(priv, port, 2, idx * 8, 0);
 }
 }
 
 
 static int skip_tx_en_setup(struct serial_private *priv,
 static int skip_tx_en_setup(struct serial_private *priv,
 			const struct pciserial_board *board,
 			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 "
 	printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
 			  "[%04x:%04x] subsystem [%04x:%04x]\n",
 			  "[%04x:%04x] subsystem [%04x:%04x]\n",
 			  priv->dev->vendor,
 			  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,
 static int kt_serial_setup(struct serial_private *priv,
 			   const struct pciserial_board *board,
 			   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);
 	return skip_tx_en_setup(priv, board, port, idx);
 }
 }
 
 
@@ -1151,9 +1158,19 @@ static int pci_eg20t_init(struct pci_dev *dev)
 static int
 static int
 pci_xr17c154_setup(struct serial_private *priv,
 pci_xr17c154_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  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);
 	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_OCTPRO422	0x0208
 #define PCI_SUBDEVICE_ID_POCTAL232	0x0308
 #define PCI_SUBDEVICE_ID_POCTAL232	0x0308
 #define PCI_SUBDEVICE_ID_POCTAL422	0x0408
 #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_VENDOR_ID_ADVANTECH		0x13fe
 #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
 #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
 #define PCI_DEVICE_ID_ADVANTECH_PCI3620	0x3620
 #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 PCIE_DEVICE_ID_NEO_2_OX_IBM	0x00F6
 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001
 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001
 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
 #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 */
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
@@ -1726,7 +1752,41 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.subvendor	= PCI_ANY_ID,
 		.subvendor	= PCI_ANY_ID,
 		.subdevice	= PCI_ANY_ID,
 		.subdevice	= PCI_ANY_ID,
 		.setup		= pci_omegapci_setup,
 		.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
 	 * Default "match everything" terminator entry
 	 */
 	 */
@@ -1887,7 +1947,6 @@ enum pci_board_num_t {
 	pbn_panacom,
 	pbn_panacom,
 	pbn_panacom2,
 	pbn_panacom2,
 	pbn_panacom4,
 	pbn_panacom4,
-	pbn_exsys_4055,
 	pbn_plx_romulus,
 	pbn_plx_romulus,
 	pbn_oxsemi,
 	pbn_oxsemi,
 	pbn_oxsemi_1_4000000,
 	pbn_oxsemi_1_4000000,
@@ -2393,13 +2452,6 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 		.reg_shift	= 7,
 		.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 */
 	/* I think this entry is broken - the first_offset looks wrong --rmk */
 	[pbn_plx_romulus] = {
 	[pbn_plx_romulus] = {
 		.flags		= FL_BASE2,
 		.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(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
 	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
 	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
 	{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
 	{ 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
 static int __devinit
 serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 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;
 	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
 	 * 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;
 			return -ENODEV;
 	}
 	}
 
 
@@ -2728,7 +2784,7 @@ serial_pci_matches(const struct pciserial_board *board,
 struct serial_private *
 struct serial_private *
 pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
 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 serial_private *priv;
 	struct pci_serial_quirk *quirk;
 	struct pci_serial_quirk *quirk;
 	int rc, nr_ports, i;
 	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->dev = dev;
 	priv->quirk = quirk;
 	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++) {
 	for (i = 0; i < nr_ports; i++) {
-		if (quirk->setup(priv, board, &serial_port, i))
+		if (quirk->setup(priv, board, &uart, i))
 			break;
 			break;
 
 
 #ifdef SERIAL_DEBUG_PCI
 #ifdef SERIAL_DEBUG_PCI
 		printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
 		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
 #endif
 
 
-		priv->line[i] = serial8250_register_port(&serial_port);
+		priv->line[i] = serial8250_register_8250_port(&uart);
 		if (priv->line[i] < 0) {
 		if (priv->line[i] < 0) {
 			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
 			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
 			break;
 			break;
@@ -3193,7 +3249,7 @@ static struct pci_device_id serial_pci_tbl[] = {
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
 		PCI_SUBVENDOR_ID_EXSYS,
 		PCI_SUBVENDOR_ID_EXSYS,
 		PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
 		PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
-		pbn_exsys_4055 },
+		pbn_b2_4_115200 },
 	/*
 	/*
 	 * Megawolf Romulus PCI Serial Card, from Mike Hudson
 	 * Megawolf Romulus PCI Serial Card, from Mike Hudson
 	 * (Exoray@isys.ca)
 	 * (Exoray@isys.ca)
@@ -3232,8 +3288,11 @@ static struct pci_device_id serial_pci_tbl[] = {
 		 * For now just used the hex ID 0x950a.
 		 * For now just used the hex ID 0x950a.
 		 */
 		 */
 	{	PCI_VENDOR_ID_OXSEMI, 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_VENDOR_ID_OXSEMI, 0x950a,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b0_2_1130000 },
 		pbn_b0_2_1130000 },
@@ -4178,6 +4237,25 @@ static struct pci_device_id serial_pci_tbl[] = {
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_omegapci },
 		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,
 	 * These entries match devices with class COMMUNICATION_SERIAL,
 	 * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
 	 * 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.
  *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
  *
  *
@@ -25,7 +25,7 @@
 #include "8250.h"
 #include "8250.h"
 
 
 #define UNKNOWN_DEV 0x3000
 #define UNKNOWN_DEV 0x3000
-
+#define CIR_PORT	0x0800
 
 
 static const struct pnp_device_id pnp_dev_table[] = {
 static const struct pnp_device_id pnp_dev_table[] = {
 	/* Archtek America Corp. */
 	/* Archtek America Corp. */
@@ -362,6 +362,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
 	{	"PNPCXXX",		UNKNOWN_DEV	},
 	{	"PNPCXXX",		UNKNOWN_DEV	},
 	/* More unknown PnP modems */
 	/* More unknown PnP modems */
 	{	"PNPDXXX",		UNKNOWN_DEV	},
 	{	"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	}
 	{	"",			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[]
  * PnP modems, alternatively we must hardcode all modems in pnp_devices[]
  * table.
  * 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)) ||
 	if (!(check_name(pnp_dev_name(dev)) ||
 		(dev->card && check_name(dev->card->name))))
 		(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
 static int __devinit
 serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 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;
 	int ret, line, flags = dev_id->driver_data;
 
 
 	if (flags & UNKNOWN_DEV) {
 	if (flags & UNKNOWN_DEV) {
-		ret = serial_pnp_guess_board(dev, &flags);
+		ret = serial_pnp_guess_board(dev);
 		if (ret < 0)
 		if (ret < 0)
 			return ret;
 			return ret;
 	}
 	}
 
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 	if (pnp_irq_valid(dev, 0))
 	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)) {
 	} 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
 	} else
 		return -ENODEV;
 		return -ENODEV;
 
 
 #ifdef SERIAL_DEBUG_PNP
 #ifdef SERIAL_DEBUG_PNP
 	printk(KERN_DEBUG
 	printk(KERN_DEBUG
 		"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
 		"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
 #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)
 	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;
 		return -ENODEV;
 
 
 	pnp_set_drvdata(dev, (void *)((long)line + 1));
 	pnp_set_drvdata(dev, (void *)((long)line + 1));
@@ -507,18 +517,13 @@ static struct pnp_driver serial_pnp_driver = {
 	.id_table	= pnp_dev_table,
 	.id_table	= pnp_dev_table,
 };
 };
 
 
-static int __init serial8250_pnp_init(void)
+int serial8250_pnp_init(void)
 {
 {
 	return pnp_register_driver(&serial_pnp_driver);
 	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);
 	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,
 	  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.
 	  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
 config SERIAL_8250_CONSOLE
 	bool "Console on 8250/16550 and compatible serial port"
 	bool "Console on 8250/16550 and compatible serial port"
 	depends on SERIAL_8250=y
 	depends on SERIAL_8250=y
@@ -85,14 +93,6 @@ config SERIAL_8250_PCI
 	  disable this feature if you only need legacy serial support.
 	  disable this feature if you only need legacy serial support.
 	  Saves about 9K.
 	  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
 config SERIAL_8250_HP300
 	tristate
 	tristate
 	depends on SERIAL_8250 && HP300
 	depends on SERIAL_8250 && HP300

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

@@ -2,8 +2,9 @@
 # Makefile for the 8250 serial device drivers.
 # 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_GSC)		+= 8250_gsc.o
 obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o
 obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o
 obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.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;
 	unsigned int prodid;
 	int multi;		/* 1 = multifunction, > 1 = # ports */
 	int multi;		/* 1 = multifunction, > 1 = # ports */
 	void (*config)(struct pcmcia_device *);
 	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 *);
 	void (*wakeup)(struct pcmcia_device *);
 	int (*post)(struct pcmcia_device *);
 	int (*post)(struct pcmcia_device *);
 };
 };
@@ -105,9 +105,9 @@ struct serial_cfg_mem {
  * Elan VPU16551 UART with 14.7456MHz oscillator
  * Elan VPU16551 UART with 14.7456MHz oscillator
  * manfid 0x015D, 0x4C45
  * 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)
 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,
 static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
 			unsigned int iobase, int irq)
 			unsigned int iobase, int irq)
 {
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int line;
 	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)
 	if (buggy_uart)
-		port.flags |= UPF_BUGGY_UART;
+		uart.port.flags |= UPF_BUGGY_UART;
 
 
 	if (info->quirk && info->quirk->setup)
 	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) {
 	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;
 		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.
 	  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
 config SERIAL_KS8695
 	bool "Micrel KS8695 (Centaur) serial port support"
 	bool "Micrel KS8695 (Centaur) serial port support"
 	depends on ARCH_KS8695
 	depends on ARCH_KS8695
@@ -257,12 +276,19 @@ config SERIAL_MAX3100
 	help
 	help
 	  MAX3100 chip support
 	  MAX3100 chip support
 
 
-config SERIAL_MAX3107
-	tristate "MAX3107 support"
+config SERIAL_MAX310X
+	bool "MAX310X support"
 	depends on SPI
 	depends on SPI
 	select SERIAL_CORE
 	select SERIAL_CORE
+	select REGMAP_SPI if SPI
+	default n
 	help
 	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
 config SERIAL_DZ
 	bool "DECstation DZ serial driver"
 	bool "DECstation DZ serial driver"
@@ -686,7 +712,7 @@ config SERIAL_SH_SCI_CONSOLE
 
 
 config SERIAL_SH_SCI_DMA
 config SERIAL_SH_SCI_DMA
 	bool "DMA support"
 	bool "DMA support"
-	depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+	depends on SERIAL_SH_SCI && SH_DMAE
 
 
 config SERIAL_PNX8XXX
 config SERIAL_PNX8XXX
 	bool "Enable PNX8XXX SoCs' UART Support"
 	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
 	  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.
 	  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
 config SERIAL_CORE
 	tristate
 	tristate
 
 
@@ -1104,6 +1149,24 @@ config SERIAL_SC26XX_CONSOLE
 	help
 	help
 	  Support for Console on SC2681/SC2692 serial ports.
 	  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
 config SERIAL_BFIN_SPORT
 	tristate "Blackfin SPORT emulate UART"
 	tristate "Blackfin SPORT emulate UART"
 	depends on BLACKFIN
 	depends on BLACKFIN
@@ -1260,7 +1323,7 @@ config SERIAL_ALTERA_UART_CONSOLE
 
 
 config SERIAL_IFX6X60
 config SERIAL_IFX6X60
         tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
         tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
-	depends on GPIOLIB && SPI && EXPERIMENTAL
+	depends on GPIOLIB && SPI
 	help
 	help
 	  Support for the IFX6x60 modem devices on Intel MID platforms.
 	  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_BFIN_SPORT) += bfin_sport_uart.o
 obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
 obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.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_IP22_ZILOG) += ip22zilog.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
 obj-$(CONFIG_SERIAL_MCF) += mcf.o
 obj-$(CONFIG_SERIAL_MCF) += mcf.o
 obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.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_DZ) += dz.o
 obj-$(CONFIG_SERIAL_ZS) += zs.o
 obj-$(CONFIG_SERIAL_ZS) += zs.o
 obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.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_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
 obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
+obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.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_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.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_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.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->ops = &altera_uart_ops;
 	port->flags = UPF_BOOT_AUTOCONF;
 	port->flags = UPF_BOOT_AUTOCONF;
 
 
-	dev_set_drvdata(&pdev->dev, port);
+	platform_set_drvdata(pdev, port);
 
 
 	uart_add_one_port(&altera_uart_driver, 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)
 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) {
 	if (port) {
 		uart_remove_one_port(&altera_uart_driver, port);
 		uart_remove_one_port(&altera_uart_driver, port);
-		dev_set_drvdata(&pdev->dev, NULL);
+		platform_set_drvdata(pdev, NULL);
 		port->mapbase = 0;
 		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;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	int retval;
 	int retval;
 
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	/*
 	 * Try to enable the clock producer.
 	 * Try to enable the clock producer.
 	 */
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 	uap->port.uartclk = clk_get_rate(uap->clk);
 
 
@@ -346,9 +342,7 @@ static int pl010_startup(struct uart_port *port)
 	return 0;
 	return 0;
 
 
  clk_dis:
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
  out:
  out:
 	return retval;
 	return retval;
 }
 }
@@ -375,8 +369,7 @@ static void pl010_shutdown(struct uart_port *port)
 	/*
 	/*
 	 * Shut down the clock producer
 	 * Shut down the clock producer
 	 */
 	 */
-	clk_disable(uap->clk);
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
 }
 }
 
 
 static void
 static void

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

@@ -52,6 +52,8 @@
 #include <linux/scatterlist.h>
 #include <linux/scatterlist.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/types.h>
 #include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/sizes.h>
 #include <linux/sizes.h>
 
 
@@ -75,7 +77,6 @@ struct vendor_data {
 	unsigned int		lcrh_tx;
 	unsigned int		lcrh_tx;
 	unsigned int		lcrh_rx;
 	unsigned int		lcrh_rx;
 	bool			oversampling;
 	bool			oversampling;
-	bool			interrupt_may_hang;   /* vendor-specific */
 	bool			dma_threshold;
 	bool			dma_threshold;
 	bool			cts_event_workaround;
 	bool			cts_event_workaround;
 };
 };
@@ -96,7 +97,6 @@ static struct vendor_data vendor_st = {
 	.lcrh_tx		= ST_UART011_LCRH_TX,
 	.lcrh_tx		= ST_UART011_LCRH_TX,
 	.lcrh_rx		= ST_UART011_LCRH_RX,
 	.lcrh_rx		= ST_UART011_LCRH_RX,
 	.oversampling		= true,
 	.oversampling		= true,
-	.interrupt_may_hang	= true,
 	.dma_threshold		= true,
 	.dma_threshold		= true,
 	.cts_event_workaround	= true,
 	.cts_event_workaround	= true,
 };
 };
@@ -147,7 +147,6 @@ struct uart_amba_port {
 	unsigned int		old_cr;		/* state during shutdown */
 	unsigned int		old_cr;		/* state during shutdown */
 	bool			autorts;
 	bool			autorts;
 	char			type[12];
 	char			type[12];
-	bool			interrupt_may_hang; /* vendor-specific */
 #ifdef CONFIG_DMA_ENGINE
 #ifdef CONFIG_DMA_ENGINE
 	/* DMA stuff */
 	/* DMA stuff */
 	bool			using_tx_dma;
 	bool			using_tx_dma;
@@ -1215,14 +1214,14 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
 	return IRQ_RETVAL(handled);
 	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;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int status = readw(uap->port.membase + UART01x_FR);
 	unsigned int status = readw(uap->port.membase + UART01x_FR);
 	return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
 	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;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int result = 0;
 	unsigned int result = 0;
@@ -1285,11 +1284,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
 }
 }
 
 
 #ifdef CONFIG_CONSOLE_POLL
 #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;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	unsigned int status;
 	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);
 	status = readw(uap->port.membase + UART01x_FR);
 	if (status & UART01x_FR_RXFE)
 	if (status & UART01x_FR_RXFE)
 		return NO_POLL_CHAR;
 		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);
 	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)
 			 unsigned char ch)
 {
 {
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	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 */
 #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;
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
-	unsigned int cr;
 	int retval;
 	int retval;
 
 
 	/* Optionaly enable pins to be muxed in and configured */
 	/* 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");
 				"could not set default pins\n");
 	}
 	}
 
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	/*
 	 * Try to enable the clock producer.
 	 * Try to enable the clock producer.
 	 */
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 	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 |
 	writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
 	       UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
 	       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
 	 * Allocate the IRQ
 	 */
 	 */
@@ -1400,21 +1454,10 @@ static int pl011_startup(struct uart_port *port)
 	writew(uap->im, uap->port.membase + UART011_IMSC);
 	writew(uap->im, uap->port.membase + UART011_IMSC);
 	spin_unlock_irq(&uap->port.lock);
 	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;
 	return 0;
 
 
  clk_dis:
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
- out:
+	clk_disable_unprepare(uap->clk);
 	return retval;
 	return retval;
 }
 }
 
 
@@ -1473,8 +1516,7 @@ static void pl011_shutdown(struct uart_port *port)
 	/*
 	/*
 	 * Shut down the clock producer
 	 * 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 */
 	/* Optionally let pins go into sleep states */
 	if (!IS_ERR(uap->pins_sleep)) {
 	if (!IS_ERR(uap->pins_sleep)) {
 		retval = pinctrl_select_state(uap->pinctrl, 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;
 			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 */
 	/* Set baud rate */
 	writew(quot & 0x3f, port->membase + UART011_FBRD);
 	writew(quot & 0x3f, port->membase + UART011_FBRD);
 	writew(quot >> 6, port->membase + UART011_IBRD);
 	writew(quot >> 6, port->membase + UART011_IBRD);
 
 
 	/*
 	/*
 	 * ----------v----------v----------v----------v-----
 	 * ----------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);
 	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'
  * 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);
 	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'
  * 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")
 	return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
 			!= NULL ? 0 : -EBUSY;
 			!= NULL ? 0 : -EBUSY;
@@ -1654,18 +1709,18 @@ static int pl010_request_port(struct uart_port *port)
 /*
 /*
  * Configure/autoconfigure the 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) {
 	if (flags & UART_CONFIG_TYPE) {
 		port->type = PORT_AMBA;
 		port->type = PORT_AMBA;
-		pl010_request_port(port);
+		pl011_request_port(port);
 	}
 	}
 }
 }
 
 
 /*
 /*
  * verify the new serial_struct (for TIOCSSERIAL).
  * 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;
 	int ret = 0;
 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
 	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 = {
 static struct uart_ops amba_pl011_pops = {
-	.tx_empty	= pl01x_tx_empty,
+	.tx_empty	= pl011_tx_empty,
 	.set_mctrl	= pl011_set_mctrl,
 	.set_mctrl	= pl011_set_mctrl,
-	.get_mctrl	= pl01x_get_mctrl,
+	.get_mctrl	= pl011_get_mctrl,
 	.stop_tx	= pl011_stop_tx,
 	.stop_tx	= pl011_stop_tx,
 	.start_tx	= pl011_start_tx,
 	.start_tx	= pl011_start_tx,
 	.stop_rx	= pl011_stop_rx,
 	.stop_rx	= pl011_stop_rx,
@@ -1691,13 +1746,14 @@ static struct uart_ops amba_pl011_pops = {
 	.flush_buffer	= pl011_dma_flush_buffer,
 	.flush_buffer	= pl011_dma_flush_buffer,
 	.set_termios	= pl011_set_termios,
 	.set_termios	= pl011_set_termios,
 	.type		= pl011_type,
 	.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
 #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
 #endif
 };
 };
 
 
@@ -1869,6 +1925,38 @@ static struct uart_driver amba_reg = {
 	.cons			= AMBA_CONSOLE,
 	.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)
 static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 {
 {
 	struct uart_amba_port *uap;
 	struct uart_amba_port *uap;
@@ -1891,6 +1979,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 		goto out;
 		goto out;
 	}
 	}
 
 
+	i = pl011_probe_dt_alias(i, &dev->dev);
+
 	base = ioremap(dev->res.start, resource_size(&dev->res));
 	base = ioremap(dev->res.start, resource_size(&dev->res));
 	if (!base) {
 	if (!base) {
 		ret = -ENOMEM;
 		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->lcrh_tx = vendor->lcrh_tx;
 	uap->old_cr = 0;
 	uap->old_cr = 0;
 	uap->fifosize = vendor->fifosize;
 	uap->fifosize = vendor->fifosize;
-	uap->interrupt_may_hang = vendor->interrupt_may_hang;
 	uap->port.dev = &dev->dev;
 	uap->port.dev = &dev->dev;
 	uap->port.mapbase = dev->res.start;
 	uap->port.mapbase = dev->res.start;
 	uap->port.membase = base;
 	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
 	 * To avoid losting RX interrupt, we reset IR function
 	 * before sending data.
 	 * before sending data.
 	 */
 	 */
-	if (tty->termios->c_line == N_IRDA)
+	if (tty->termios.c_line == N_IRDA)
 		bfin_serial_reset_irda(port);
 		bfin_serial_reset_irda(port);
 
 
 #ifdef CONFIG_SERIAL_BFIN_DMA
 #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
  * 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);
 			clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
 			clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
 			clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
 		}
 		}
+		cpm_uart_initbd(pinfo);
 		cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
 		cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
 	}
 	}
 	/* Install interrupt handler. */
 	/* 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;
 	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
 	smc_t __iomem *smcp = pinfo->smcp;
 	smc_t __iomem *smcp = pinfo->smcp;
 	scc_t __iomem *sccp = pinfo->sccp;
 	scc_t __iomem *sccp = pinfo->sccp;
+	int maxidl;
 
 
 	pr_debug("CPM uart[%d]:set_termios\n", port->line);
 	pr_debug("CPM uart[%d]:set_termios\n", port->line);
 
 
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
 	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->port.state && pinfo->port.state->port.tty->low_latency))
 		pinfo->rx_fifosize = 1;
 		pinfo->rx_fifosize = 1;
 	else
 	else
 		pinfo->rx_fifosize = RX_BUF_SIZE;
 		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
 	/* Character length programmed into the mode register is the
 	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
 	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
 	 * 1 or 2 stop bits, minus 1.
 	 * 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.
 		 * SMC/SCC receiver is disabled.
 		 */
 		 */
 		out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
 		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
 		/* Set the mode register.  We want to keep a copy of the
 		 * enables, because we want to put them back if they were
 		 * 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);
 		    SMCMR_SM_UART | prev_mode);
 	} else {
 	} else {
 		out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
 		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);
 		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);
 	cpm_set_scc_fcr(sup);
 
 
 	out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
 	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_brkcr, 1);
 	out_be16(&sup->scc_parec, 0);
 	out_be16(&sup->scc_parec, 0);
 	out_be16(&sup->scc_frmec, 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.  */
 	/* Using idle character time requires some additional tuning.  */
 	out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
 	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_brklen, 0);
 	out_be16(&up->smc_brkec, 0);
 	out_be16(&up->smc_brkec, 0);
 	out_be16(&up->smc_brkcr, 1);
 	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. */
 /* Calculate the chartime depending on baudrate, numbor of bits etc. */
 static void update_char_time(struct e100_serial * info)
 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;
 	int bits;
 
 
 	/* calc. number of bits / data byte */
 	/* calc. number of bits / data byte */
@@ -1473,7 +1473,7 @@ rs_stop(struct tty_struct *tty)
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
 				STOP_CHAR(info->port.tty));
 				STOP_CHAR(info->port.tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
 		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);
 			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)));
 					 info->xmit.tail,SERIAL_XMIT_SIZE)));
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 		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);
 			xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 		}
 		}
 
 
@@ -2929,7 +2929,7 @@ shutdown(struct e100_serial * info)
 			descr[i].buf = 0;
 			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 */
 		/* hang up DTR and RTS if HUPCL is enabled */
 		e100_dtr(info, 0);
 		e100_dtr(info, 0);
 		e100_rts(info, 0); /* could check CRTSCTS before doing this */
 		e100_rts(info, 0); /* could check CRTSCTS before doing this */
@@ -2953,12 +2953,12 @@ change_speed(struct e100_serial *info)
 	unsigned long flags;
 	unsigned long flags;
 	/* first some safety checks */
 	/* first some safety checks */
 
 
-	if (!info->port.tty || !info->port.tty->termios)
+	if (!info->port.tty)
 		return;
 		return;
 	if (!info->ioport)
 	if (!info->ioport)
 		return;
 		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 */
 	/* 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;
 	info->ioport[REG_REC_CTRL] = info->rx_ctrl;
 	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
 	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
 	xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 	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",
 		DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n",
 				STOP_CHAR(info->port.tty)));
 				STOP_CHAR(info->port.tty)));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 		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)));
 	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 */
 	/* 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 */
 		/* Turn off RTS line */
 		e100_rts(info, 0);
 		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 ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
 	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
 	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
 	/* Do RTS before XOFF since XOFF might take some time */
 	/* 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  */
 		/* Assert RTS line  */
 		e100_rts(info, 1);
 		e100_rts(info, 1);
 	}
 	}
@@ -3748,7 +3748,7 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 
 
 	/* Handle turning off CRTSCTS */
 	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
+	    !(tty->termios.c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
 		tty->hw_stopped = 0;
 		rs_start(tty);
 		rs_start(tty);
 	}
 	}
@@ -3815,7 +3815,7 @@ rs_close(struct tty_struct *tty, struct file * filp)
 	 * separate termios for callout and dialin.
 	 * separate termios for callout and dialin.
 	 */
 	 */
 	if (info->flags & ASYNC_NORMAL_ACTIVE)
 	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
 	 * Now we wait for the transmit buffer to clear; and we notify
 	 * the line discipline to only process XON/XOFF characters.
 	 * 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) ||
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
 		if (info->flags & ASYNC_HUP_NOTIFY)
@@ -3998,7 +3998,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		return 0;
 		return 0;
 	}
 	}
 
 
-	if (tty->termios->c_cflag & CLOCAL) {
+	if (tty->termios.c_cflag & CLOCAL) {
 			do_clocal = 1;
 			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",
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 		       info->line, info->count);
 #endif
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	}
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	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) ||
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 		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)) {
 	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
-		*tty->termios = info->normal_termios;
+		tty->termios = info->normal_termios;
 		change_speed(info);
 		change_speed(info);
 	}
 	}
 
 
@@ -4443,14 +4443,12 @@ static int __init rs_init(void)
 		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
 		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
 	driver->init_termios.c_ispeed = 115200;
 	driver->init_termios.c_ispeed = 115200;
 	driver->init_termios.c_ospeed = 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);
 	tty_set_operations(driver, &rs_ops);
         serial_driver = driver;
         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++) {
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
 		if (info->enabled) {
 		if (info->enabled) {
 			if (cris_request_io_interface(info->io_if,
 			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",
 			printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
 			       serial_driver->name, info->line, info->ioport);
 			       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_FAST_TIMER
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 	memset(fast_timers, 0, sizeof(fast_timers));
 	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);
 	tty_port_init(pport);
 	pport->ops = &ifx_tty_port_ops;
 	pport->ops = &ifx_tty_port_ops;
 	ifx_dev->minor = IFX_SPI_TTY_ID;
 	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)) {
 	if (IS_ERR(ifx_dev->tty_dev)) {
 		dev_dbg(&ifx_dev->spi_dev->dev,
 		dev_dbg(&ifx_dev->spi_dev->dev,
 			"%s: registering tty device failed", __func__);
 			"%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 */
 	unsigned short		trcv_delay; /* transceiver delay */
 	struct clk		*clk_ipg;
 	struct clk		*clk_ipg;
 	struct clk		*clk_per;
 	struct clk		*clk_per;
-	struct imx_uart_data	*devdata;
+	const struct imx_uart_data *devdata;
 };
 };
 
 
 struct imx_port_ucrs {
 struct imx_port_ucrs {
@@ -1373,8 +1373,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
 	val |= UCR3_AWAKEN;
 	val |= UCR3_AWAKEN;
 	writel(val, sport->port.membase + UCR3);
 	writel(val, sport->port.membase + UCR3);
 
 
-	if (sport)
-		uart_suspend_port(&imx_reg, &sport->port);
+	uart_suspend_port(&imx_reg, &sport->port);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1389,8 +1388,7 @@ static int serial_imx_resume(struct platform_device *dev)
 	val &= ~UCR3_AWAKEN;
 	val &= ~UCR3_AWAKEN;
 	writel(val, sport->port.membase + UCR3);
 	writel(val, sport->port.membase + UCR3);
 
 
-	if (sport)
-		uart_resume_port(&imx_reg, &sport->port);
+	uart_resume_port(&imx_reg, &sport->port);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1505,18 +1503,21 @@ static int serial_imx_probe(struct platform_device *pdev)
 	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 	if (IS_ERR(pinctrl)) {
 	if (IS_ERR(pinctrl)) {
 		ret = PTR_ERR(pinctrl);
 		ret = PTR_ERR(pinctrl);
+		dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
 		goto unmap;
 		goto unmap;
 	}
 	}
 
 
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	if (IS_ERR(sport->clk_ipg)) {
 	if (IS_ERR(sport->clk_ipg)) {
 		ret = PTR_ERR(sport->clk_ipg);
 		ret = PTR_ERR(sport->clk_ipg);
+		dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
 		goto unmap;
 		goto unmap;
 	}
 	}
 
 
 	sport->clk_per = devm_clk_get(&pdev->dev, "per");
 	sport->clk_per = devm_clk_get(&pdev->dev, "per");
 	if (IS_ERR(sport->clk_per)) {
 	if (IS_ERR(sport->clk_per)) {
 		ret = PTR_ERR(sport->clk_per);
 		ret = PTR_ERR(sport->clk_per);
+		dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
 		goto unmap;
 		goto unmap;
 	}
 	}
 
 
@@ -1537,7 +1538,7 @@ static int serial_imx_probe(struct platform_device *pdev)
 	ret = uart_add_one_port(&imx_reg, &sport->port);
 	ret = uart_add_one_port(&imx_reg, &sport->port);
 	if (ret)
 	if (ret)
 		goto deinit;
 		goto deinit;
-	platform_set_drvdata(pdev, &sport->port);
+	platform_set_drvdata(pdev, sport);
 
 
 	return 0;
 	return 0;
 deinit:
 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 ioc3_port *port = get_ioc3_port(the_port);
 	struct ring *inring;
 	struct ring *inring;
 	struct ring_entry *entry;
 	struct ring_entry *entry;
-	struct port_hooks *hooks = port->ip_hooks;
+	struct port_hooks *hooks;
 	int byte_num;
 	int byte_num;
 	char *sc;
 	char *sc;
 	int loop_counter;
 	int loop_counter;
 
 
 	BUG_ON(!(len >= 0));
 	BUG_ON(!(len >= 0));
 	BUG_ON(!port);
 	BUG_ON(!port);
+	hooks = port->ip_hooks;
 
 
 	/* There is a nasty timing issue in the IOC3. When the rx_timer
 	/* There is a nasty timing issue in the IOC3. When the rx_timer
 	 * expires or the rx_high condition arises, we take an interrupt.
 	 * expires or the rx_high condition arises, we take an interrupt.

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