diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 12:26:52 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 12:26:52 -0700 |
commit | 3498d13b8090c0b0ef911409fbc503a7c4cca6ef (patch) | |
tree | 254ca00276e863d9fba25707690c66b2a04c49e9 | |
parent | def7cb8cd4e3258db88050eaaca5438bcc3dafca (diff) | |
parent | 0c57dfcc6c1d037243c2f8fbf62eab3633326ec0 (diff) | |
download | op-kernel-dev-3498d13b8090c0b0ef911409fbc503a7c4cca6ef.zip op-kernel-dev-3498d13b8090c0b0ef911409fbc503a7c4cca6ef.tar.gz |
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
...
201 files changed, 18056 insertions, 8963 deletions
diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index b138b66..0c43015 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -17,3 +17,12 @@ Description: device, like 'tty1'. The file supports poll() to detect virtual console switches. + +What: /sys/class/tty/ttyS0/uartclk +Date: Sep 2012 +Contact: Tomas Hlavacek <tmshlvck@gmail.com> +Description: + Shows the current uartclk value associated with the + UART port in serial_core, that is bound to TTY like ttyS0. + uartclk = 16 * baud_base + diff --git a/Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt b/Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt new file mode 100644 index 0000000..0d439df --- /dev/null +++ b/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>; + }; diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index 0847fde..ba385f2 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -25,6 +25,8 @@ Optional properties: accesses to the UART (e.g. TI davinci). - used-by-rtas : set to indicate that the port is in use by the OpenFirmware RTAS and should not be registered. +- no-loopback-test: set to indicate that the port does not implements loopback + test mode Example: diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX index e09468a..f7b0c7d 100644 --- a/Documentation/serial/00-INDEX +++ b/Documentation/serial/00-INDEX @@ -2,8 +2,6 @@ - this file. README.cycladesZ - info on Cyclades-Z firmware loading. -computone.txt - - info on Computone Intelliport II/Plus Multiport Serial Driver. digiepca.txt - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. hayes-esp.txt diff --git a/Documentation/serial/computone.txt b/Documentation/serial/computone.txt deleted file mode 100644 index a6a1158..0000000 --- a/Documentation/serial/computone.txt +++ /dev/null @@ -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 diff --git a/arch/alpha/kernel/srmcons.c b/arch/alpha/kernel/srmcons.c index 3ea8094..5d58652 100644 --- a/arch/alpha/kernel/srmcons.c +++ b/arch/alpha/kernel/srmcons.c @@ -223,6 +223,7 @@ srmcons_init(void) driver->subtype = SYSTEM_TYPE_SYSCONS; driver->init_termios = tty_std_termios; tty_set_operations(driver, &srmcons_ops); + tty_port_link_device(&srmcons_singleton.port, driver, 0); err = tty_register_driver(driver); if (err) { put_tty_driver(driver); diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index c1b93c7..9e80d20 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -81,8 +81,9 @@ static struct omap_uart_port_info omap_serial_default_info[] __initdata = { }; #ifdef CONFIG_PM -static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) +static void omap_uart_enable_wakeup(struct device *dev, bool enable) { + struct platform_device *pdev = to_platform_device(dev); struct omap_device *od = to_omap_device(pdev); if (!od) @@ -99,15 +100,17 @@ static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) * in Smartidle Mode When Configured for DMA Operations. * WA: configure uart in force idle mode. */ -static void omap_uart_set_noidle(struct platform_device *pdev) +static void omap_uart_set_noidle(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct omap_device *od = to_omap_device(pdev); omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO); } -static void omap_uart_set_smartidle(struct platform_device *pdev) +static void omap_uart_set_smartidle(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct omap_device *od = to_omap_device(pdev); u8 idlemode; @@ -120,10 +123,10 @@ static void omap_uart_set_smartidle(struct platform_device *pdev) } #else -static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) +static void omap_uart_enable_wakeup(struct device *dev, bool enable) {} -static void omap_uart_set_noidle(struct platform_device *pdev) {} -static void omap_uart_set_smartidle(struct platform_device *pdev) {} +static void omap_uart_set_noidle(struct device *dev) {} +static void omap_uart_set_smartidle(struct device *dev) {} #endif /* CONFIG_PM */ #ifdef CONFIG_OMAP_MUX @@ -304,6 +307,9 @@ void __init omap_serial_init_port(struct omap_board_data *bdata, omap_up.dma_rx_timeout = info->dma_rx_timeout; omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate; omap_up.autosuspend_timeout = info->autosuspend_timeout; + omap_up.DTR_gpio = info->DTR_gpio; + omap_up.DTR_inverted = info->DTR_inverted; + omap_up.DTR_present = info->DTR_present; pdata = &omap_up; pdata_size = sizeof(struct omap_uart_port_info); diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index a534d88..1d2e3c6 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -524,33 +524,12 @@ static struct stedma40_chan_cfg uart2_dma_cfg_tx = { }; #endif -#define PRCC_K_SOFTRST_SET 0x18 -#define PRCC_K_SOFTRST_CLEAR 0x1C -static void ux500_uart0_reset(void) -{ - void __iomem *prcc_rst_set, *prcc_rst_clr; - - prcc_rst_set = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE + - PRCC_K_SOFTRST_SET); - prcc_rst_clr = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE + - PRCC_K_SOFTRST_CLEAR); - - /* Activate soft reset PRCC_K_SOFTRST_CLEAR */ - writel((readl(prcc_rst_clr) | 0x1), prcc_rst_clr); - udelay(1); - - /* Release soft reset PRCC_K_SOFTRST_SET */ - writel((readl(prcc_rst_set) | 0x1), prcc_rst_set); - udelay(1); -} - static struct amba_pl011_data uart0_plat = { #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &uart0_dma_cfg_rx, .dma_tx_param = &uart0_dma_cfg_tx, #endif - .reset = ux500_uart0_reset, }; static struct amba_pl011_data uart1_plat = { diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h index 1a52725..a531149 100644 --- a/arch/arm/plat-omap/include/plat/omap-serial.h +++ b/arch/arm/plat-omap/include/plat/omap-serial.h @@ -18,7 +18,7 @@ #define __OMAP_SERIAL_H__ #include <linux/serial_core.h> -#include <linux/platform_device.h> +#include <linux/device.h> #include <linux/pm_qos.h> #include <plat/mux.h> @@ -42,10 +42,10 @@ #define OMAP_UART_WER_MOD_WKUP 0X7F /* Enable XON/XOFF flow control on output */ -#define OMAP_UART_SW_TX 0x04 +#define OMAP_UART_SW_TX 0x8 /* Enable XON/XOFF flow control on input */ -#define OMAP_UART_SW_RX 0x04 +#define OMAP_UART_SW_RX 0x2 #define OMAP_UART_SYSC_RESET 0X07 #define OMAP_UART_TCR_TRIG 0X0F @@ -69,11 +69,14 @@ struct omap_uart_port_info { unsigned int dma_rx_timeout; unsigned int autosuspend_timeout; unsigned int dma_rx_poll_rate; + int DTR_gpio; + int DTR_inverted; + int DTR_present; int (*get_context_loss_count)(struct device *); - void (*set_forceidle)(struct platform_device *); - void (*set_noidle)(struct platform_device *); - void (*enable_wakeup)(struct platform_device *, bool); + void (*set_forceidle)(struct device *); + void (*set_noidle)(struct device *); + void (*enable_wakeup)(struct device *, bool); }; struct uart_omap_dma { @@ -102,39 +105,4 @@ struct uart_omap_dma { unsigned int rx_timeout; }; -struct uart_omap_port { - struct uart_port port; - struct uart_omap_dma uart_dma; - struct platform_device *pdev; - - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned char fcr; - unsigned char efr; - unsigned char dll; - unsigned char dlh; - unsigned char mdr1; - unsigned char scr; - - int use_dma; - /* - * Some bits in registers are cleared on a read, so they must - * be saved whenever the register is read but the bits will not - * be immediately processed. - */ - unsigned int lsr_break_flag; - unsigned char msr_saved_flags; - char name[20]; - unsigned long port_activity; - u32 context_loss_cnt; - u32 errata; - u8 wakeups_enabled; - - struct pm_qos_request pm_qos_request; - u32 latency; - u32 calc_latency; - struct work_struct qos_work; -}; - #endif /* __OMAP_SERIAL_H__ */ diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index c34785d..ec536e4 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -338,7 +338,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; } } @@ -545,6 +545,7 @@ static int __init simrs_init(void) /* the port is imaginary */ printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq); + tty_port_link_device(&state->port, hp_simserial_driver, 0); retval = tty_register_driver(hp_simserial_driver); if (retval) { printk(KERN_ERR "Couldn't register simserial driver\n"); diff --git a/arch/m68k/emu/nfcon.c b/arch/m68k/emu/nfcon.c index 8db25e8..16d170f 100644 --- a/arch/m68k/emu/nfcon.c +++ b/arch/m68k/emu/nfcon.c @@ -19,6 +19,7 @@ #include <asm/natfeat.h> static int stderr_id; +static struct tty_port nfcon_tty_port; static struct tty_driver *nfcon_tty_driver; static void nfputs(const char *str, unsigned int count) @@ -119,6 +120,8 @@ static int __init nfcon_init(void) { int res; + tty_port_init(&nfcon_tty_port); + stderr_id = nf_get_id("NF_STDERR"); if (!stderr_id) return -ENODEV; @@ -135,6 +138,7 @@ static int __init nfcon_init(void) nfcon_tty_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(nfcon_tty_driver, &nfcon_tty_ops); + tty_port_link_device(&nfcon_tty_port, nfcon_tty_driver, 0); res = tty_register_driver(nfcon_tty_driver); if (res) { pr_err("failed to register nfcon tty driver\n"); diff --git a/arch/mips/cavium-octeon/serial.c b/arch/mips/cavium-octeon/serial.c index 138b221..569f41b 100644 --- a/arch/mips/cavium-octeon/serial.c +++ b/arch/mips/cavium-octeon/serial.c @@ -47,40 +47,40 @@ static int __devinit octeon_serial_probe(struct platform_device *pdev) { int irq, res; struct resource *res_mem; - struct uart_port port; + struct uart_8250_port up; /* All adaptors have an irq. */ irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - memset(&port, 0, sizeof(port)); + memset(&up, 0, sizeof(up)); - port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; - port.type = PORT_OCTEON; - port.iotype = UPIO_MEM; - port.regshift = 3; - port.dev = &pdev->dev; + up.port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + up.port.type = PORT_OCTEON; + up.port.iotype = UPIO_MEM; + up.port.regshift = 3; + up.port.dev = &pdev->dev; if (octeon_is_simulation()) /* Make simulator output fast*/ - port.uartclk = 115200 * 16; + up.port.uartclk = 115200 * 16; else - port.uartclk = octeon_get_io_clock_rate(); + up.port.uartclk = octeon_get_io_clock_rate(); - port.serial_in = octeon_serial_in; - port.serial_out = octeon_serial_out; - port.irq = irq; + up.port.serial_in = octeon_serial_in; + up.port.serial_out = octeon_serial_out; + up.port.irq = irq; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res_mem == NULL) { dev_err(&pdev->dev, "found no memory resource\n"); return -ENXIO; } - port.mapbase = res_mem->start; - port.membase = ioremap(res_mem->start, resource_size(res_mem)); + up.port.mapbase = res_mem->start; + up.port.membase = ioremap(res_mem->start, resource_size(res_mem)); - res = serial8250_register_port(&port); + res = serial8250_register_8250_port(&up); return res >= 0 ? 0 : res; } diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c index c48194c..b2d4f49 100644 --- a/arch/mips/sni/a20r.c +++ b/arch/mips/sni/a20r.c @@ -133,6 +133,38 @@ static struct platform_device sc26xx_pdev = { } }; +#warning "Please try migrate to use new driver SCCNXP and report the status" \ + "in the linux-serial mailing list." + +/* The code bellow is a replacement of SC26XX to SCCNXP */ +#if 0 +#include <linux/platform_data/sccnxp.h> + +static struct sccnxp_pdata sccnxp_data = { + .reg_shift = 2, + .frequency = 3686400, + .mctrl_cfg[0] = MCTRL_SIG(DTR_OP, LINE_OP7) | + MCTRL_SIG(RTS_OP, LINE_OP3) | + MCTRL_SIG(DSR_IP, LINE_IP5) | + MCTRL_SIG(DCD_IP, LINE_IP6), + .mctrl_cfg[1] = MCTRL_SIG(DTR_OP, LINE_OP2) | + MCTRL_SIG(RTS_OP, LINE_OP1) | + MCTRL_SIG(DSR_IP, LINE_IP0) | + MCTRL_SIG(CTS_IP, LINE_IP1) | + MCTRL_SIG(DCD_IP, LINE_IP2) | + MCTRL_SIG(RNG_IP, LINE_IP3), +}; + +static struct platform_device sc2681_pdev = { + .name = "sc2681", + .resource = sc2xxx_rsrc, + .num_resources = ARRAY_SIZE(sc2xxx_rsrc), + .dev = { + .platform_data = &sccnxp_data, + }, +}; +#endif + static u32 a20r_ack_hwint(void) { u32 status = read_c0_status(); diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index 47341aa..8823863 100644 --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -202,6 +202,7 @@ static int __init pdc_console_tty_driver_init(void) pdc_console_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; tty_set_operations(pdc_console_tty_driver, &pdc_console_tty_ops); + tty_port_link_device(&tty_port, pdc_console_tty_driver, 0); err = tty_register_driver(pdc_console_tty_driver); if (err) { diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index bbaf2c5..457475f 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -409,7 +409,8 @@ int setup_one_line(struct line *lines, int n, char *init, line->valid = 1; err = parse_chan_pair(new, line, n, opts, error_out); if (!err) { - struct device *d = tty_register_device(driver, n, NULL); + struct device *d = tty_port_register_device(&line->port, + driver, n, NULL); if (IS_ERR(d)) { *error_out = "Failed to register device"; err = PTR_ERR(d); diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c index f9726f6..2cd3d3a 100644 --- a/arch/xtensa/platforms/iss/console.c +++ b/arch/xtensa/platforms/iss/console.c @@ -223,6 +223,7 @@ int __init rs_init(void) serial_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(serial_driver, &serial_ops); + tty_port_link_device(&serial_port, serial_driver, 0); if (tty_register_driver(serial_driver)) panic("Couldn't register serial driver\n"); diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 12172a6..0bc8a6a 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -58,7 +58,7 @@ static int ath_wakeup_ar3k(struct tty_struct *tty) return status; /* Disable Automatic RTSCTS */ - memcpy(&ktermios, tty->termios, sizeof(ktermios)); + ktermios = tty->termios; ktermios.c_cflag &= ~CRTSCTS; tty_set_termios(tty, &ktermios); diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 1d82d58..164544a 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf, static int register_serial_portandirq(unsigned int port, int irq) { - struct uart_port uart; + struct uart_8250_port uart; switch ( port ) { case 0x3f8: @@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq) } /* switch */ /* irq is okay */ - memset(&uart, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); - uart.uartclk = 1843200; - uart.iobase = port; - uart.irq = irq; - uart.iotype = UPIO_PORT; - uart.flags = UPF_SHARE_IRQ; - return serial8250_register_port(&uart); + uart.port.uartclk = 1843200; + uart.port.iobase = port; + uart.port.irq = irq; + uart.port.iotype = UPIO_PORT; + uart.port.flags = UPF_SHARE_IRQ; + return serial8250_register_8250_port(&uart); } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index a6b8dde..21721d2 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -1058,7 +1058,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if (tty && (info->port.flags & ASYNC_CTS_FLOW)) { + if (tty && tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (info->serial_signals & SerialSignal_CTS) { if (debug_level >= DEBUG_LEVEL_ISR) @@ -1350,7 +1350,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty) /* TODO:disable interrupts instead of reset to preserve signal states */ reset_device(info); - if (!tty || tty->termios->c_cflag & HUPCL) { + if (!tty || tty->termios.c_cflag & HUPCL) { info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); set_signals(info); } @@ -1391,7 +1391,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty) port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI); get_signals(info); - if (info->netcount || (tty && (tty->termios->c_cflag & CREAD))) + if (info->netcount || (tty && (tty->termios.c_cflag & CREAD))) rx_start(info); spin_unlock_irqrestore(&info->lock,flags); @@ -1404,14 +1404,14 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty) unsigned cflag; int bits_per_char; - if (!tty || !tty->termios) + if (!tty) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_change_params(%s)\n", __FILE__,__LINE__, info->device_name ); - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* if B0 rate (hangup) specified then negate DTR and RTS */ /* otherwise assert DTR and RTS */ @@ -1734,7 +1734,7 @@ static void mgslpc_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) mgslpc_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals &= ~SerialSignal_RTS; set_signals(info); @@ -1763,7 +1763,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty) mgslpc_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals |= SerialSignal_RTS; set_signals(info); @@ -2299,8 +2299,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term tty->driver->name ); /* just return if nothing has changed */ - if ((tty->termios->c_cflag == old_termios->c_cflag) - && (RELEVANT_IFLAG(tty->termios->c_iflag) + if ((tty->termios.c_cflag == old_termios->c_cflag) + && (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; @@ -2308,7 +2308,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { + !(tty->termios.c_cflag & CBAUD)) { info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -2317,9 +2317,9 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { + tty->termios.c_cflag & CBAUD) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->serial_signals |= SerialSignal_RTS; } @@ -2330,7 +2330,7 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); } @@ -2737,6 +2737,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info) #if SYNCLINK_GENERIC_HDLC hdlcdev_init(info); #endif + tty_port_register_device(&info->port, serial_driver, info->line, + &info->p_dev->dev); } static void mgslpc_remove_device(MGSLPC_INFO *remove_info) @@ -2750,6 +2752,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info) last->next_device = info->next_device; else mgslpc_device_list = info->next_device; + tty_unregister_device(serial_driver, info->line); #if SYNCLINK_GENERIC_HDLC hdlcdev_exit(info); #endif @@ -2804,77 +2807,63 @@ static const struct tty_operations mgslpc_ops = { .proc_fops = &mgslpc_proc_fops, }; -static void synclink_cs_cleanup(void) +static int __init synclink_cs_init(void) { int rc; - while(mgslpc_device_list) - mgslpc_remove_device(mgslpc_device_list); - - if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) - printk("%s(%d) failed to unregister tty driver err=%d\n", - __FILE__,__LINE__,rc); - put_tty_driver(serial_driver); + if (break_on_load) { + mgslpc_get_text_ptr(); + BREAKPOINT(); } - pcmcia_unregister_driver(&mgslpc_driver); -} - -static int __init synclink_cs_init(void) -{ - int rc; - - if (break_on_load) { - mgslpc_get_text_ptr(); - BREAKPOINT(); - } - - if ((rc = pcmcia_register_driver(&mgslpc_driver)) < 0) - return rc; - - serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT); - if (!serial_driver) { - rc = -ENOMEM; - goto error; - } + serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(serial_driver)) { + rc = PTR_ERR(serial_driver); + goto err; + } - /* Initialize the tty_driver structure */ - - serial_driver->driver_name = "synclink_cs"; - serial_driver->name = "ttySLP"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &mgslpc_ops); - - if ((rc = tty_register_driver(serial_driver)) < 0) { - printk("%s(%d):Couldn't register serial driver\n", - __FILE__,__LINE__); - put_tty_driver(serial_driver); - serial_driver = NULL; - goto error; - } + /* Initialize the tty_driver structure */ + serial_driver->driver_name = "synclink_cs"; + serial_driver->name = "ttySLP"; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(serial_driver, &mgslpc_ops); + + rc = tty_register_driver(serial_driver); + if (rc < 0) { + printk(KERN_ERR "%s(%d):Couldn't register serial driver\n", + __FILE__, __LINE__); + goto err_put_tty; + } - printk("%s %s, tty major#%d\n", - driver_name, driver_version, - serial_driver->major); + rc = pcmcia_register_driver(&mgslpc_driver); + if (rc < 0) + goto err_unreg_tty; - return 0; + printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version, + serial_driver->major); -error: - synclink_cs_cleanup(); - return rc; + return 0; +err_unreg_tty: + tty_unregister_driver(serial_driver); +err_put_tty: + put_tty_driver(serial_driver); +err: + return rc; } static void __exit synclink_cs_exit(void) { - synclink_cs_cleanup(); + pcmcia_unregister_driver(&mgslpc_driver); + tty_unregister_driver(serial_driver); + put_tty_driver(serial_driver); } module_init(synclink_cs_init); diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 46b77ed..af98f6d 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -67,7 +67,7 @@ static int tpk_printk(const unsigned char *buf, int count) tmp[tpk_curr + 1] = '\0'; printk(KERN_INFO "%s%s", tpk_tag, tmp); tpk_curr = 0; - if (buf[i + 1] == '\n') + if ((i + 1) < count && buf[i + 1] == '\n') i++; break; case '\n': @@ -178,11 +178,17 @@ static struct tty_driver *ttyprintk_driver; static int __init ttyprintk_init(void) { int ret = -ENOMEM; - void *rp; - ttyprintk_driver = alloc_tty_driver(1); - if (!ttyprintk_driver) - return ret; + tty_port_init(&tpk_port.port); + tpk_port.port.ops = &null_ops; + mutex_init(&tpk_port.port_write_mutex); + + ttyprintk_driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE); + if (IS_ERR(ttyprintk_driver)) + return PTR_ERR(ttyprintk_driver); ttyprintk_driver->driver_name = "ttyprintk"; ttyprintk_driver->name = "ttyprintk"; @@ -191,9 +197,8 @@ static int __init ttyprintk_init(void) ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; ttyprintk_driver->init_termios = tty_std_termios; ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; - ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ttyprintk_driver, &ttyprintk_ops); + tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); ret = tty_register_driver(ttyprintk_driver); if (ret < 0) { @@ -201,22 +206,10 @@ static int __init ttyprintk_init(void) goto error; } - /* create our unnumbered device */ - rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, - ttyprintk_driver->name); - if (IS_ERR(rp)) { - printk(KERN_ERR "Couldn't create ttyprintk device\n"); - ret = PTR_ERR(rp); - goto error; - } - - tty_port_init(&tpk_port.port); - tpk_port.port.ops = &null_ops; - mutex_init(&tpk_port.port_write_mutex); - return 0; error: + tty_unregister_driver(ttyprintk_driver); put_tty_driver(ttyprintk_driver); ttyprintk_driver = NULL; return ret; diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 38c4bd8..c679867 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -234,7 +234,8 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) mp->minor = minor; - dev = tty_register_device(capinc_tty_driver, minor, NULL); + dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor, + NULL); if (IS_ERR(dev)) goto err_out2; diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index a6d9fd2..67abf3f 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -446,8 +446,8 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old) goto out; } - iflag = tty->termios->c_iflag; - cflag = tty->termios->c_cflag; + iflag = tty->termios.c_iflag; + cflag = tty->termios.c_cflag; old_cflag = old ? old->c_cflag : cflag; gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index, iflag, cflag, old_cflag); @@ -524,7 +524,8 @@ void gigaset_if_init(struct cardstate *cs) tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs); mutex_lock(&cs->mutex); - cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL); + cs->tty_dev = tty_port_register_device(&cs->port, drv->tty, + cs->minor_index, NULL); if (!IS_ERR(cs->tty_dev)) dev_set_drvdata(cs->tty_dev, cs); diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 7bc5067..b817809 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1009,15 +1009,15 @@ isdn_tty_change_speed(modem_info *info) quot; int i; - if (!port->tty || !port->tty->termios) + if (!port->tty) return; - cflag = port->tty->termios->c_cflag; + cflag = port->tty->termios.c_cflag; quot = i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) - port->tty->termios->c_cflag &= ~CBAUDEX; + port->tty->termios.c_cflag &= ~CBAUDEX; else i += 15; } @@ -1097,7 +1097,7 @@ isdn_tty_shutdown(modem_info *info) #endif isdn_unlock_drivers(); info->msr &= ~UART_MSR_RI; - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); @@ -1469,13 +1469,13 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) if (!old_termios) isdn_tty_change_speed(info); else { - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_ispeed == old_termios->c_ispeed && - tty->termios->c_ospeed == old_termios->c_ospeed) + if (tty->termios.c_cflag == old_termios->c_cflag && + tty->termios.c_ispeed == old_termios->c_ispeed && + tty->termios.c_ospeed == old_termios->c_ospeed) return; isdn_tty_change_speed(info); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) + !(tty->termios.c_cflag & CRTSCTS)) tty->hw_stopped = 0; } } @@ -1486,6 +1486,18 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * ------------------------------------------------------------ */ +static int isdn_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + modem_info *info = &dev->mdm.info[tty->index]; + + if (isdn_tty_paranoia_check(info, tty->name, __func__)) + return -ENODEV; + + tty->driver_data = info; + + return tty_port_install(&info->port, driver, tty); +} + /* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its async structure into @@ -1495,22 +1507,16 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) static int isdn_tty_open(struct tty_struct *tty, struct file *filp) { - struct tty_port *port; - modem_info *info; + modem_info *info = tty->driver_data; + struct tty_port *port = &info->port; int retval; - info = &dev->mdm.info[tty->index]; - if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open")) - return -ENODEV; - port = &info->port; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, port->count); #endif port->count++; - tty->driver_data = info; port->tty = tty; - tty->port = port; /* * Start up serial port */ @@ -1738,6 +1744,7 @@ modem_write_profile(atemu *m) } static const struct tty_operations modem_ops = { + .install = isdn_tty_install, .open = isdn_tty_open, .close = isdn_tty_close, .write = isdn_tty_write, @@ -1782,7 +1789,7 @@ isdn_tty_modem_init(void) m->tty_modem->subtype = SERIAL_TYPE_NORMAL; m->tty_modem->init_termios = tty_std_termios; m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + m->tty_modem->flags = TTY_DRIVER_REAL_RAW; m->tty_modem->driver_name = "isdn_tty"; tty_set_operations(m->tty_modem, &modem_ops); retval = tty_register_driver(m->tty_modem); diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c index 1dcb9ae..01e2b0d 100644 --- a/drivers/misc/ibmasm/uart.c +++ b/drivers/misc/ibmasm/uart.c @@ -33,7 +33,7 @@ void ibmasm_register_uart(struct service_processor *sp) { - struct uart_port uport; + struct uart_8250_port uart; void __iomem *iomem_base; iomem_base = sp->base_address + SCOUT_COM_B_BASE; @@ -47,14 +47,14 @@ void ibmasm_register_uart(struct service_processor *sp) return; } - memset(&uport, 0, sizeof(struct uart_port)); - uport.irq = sp->irq; - uport.uartclk = 3686400; - uport.flags = UPF_SHARE_IRQ; - uport.iotype = UPIO_MEM; - uport.membase = iomem_base; + memset(&uart, 0, sizeof(uart)); + uart.port.irq = sp->irq; + uart.port.uartclk = 3686400; + uart.port.flags = UPF_SHARE_IRQ; + uart.port.iotype = UPIO_MEM; + uart.port.membase = iomem_base; - sp->serial_line = serial8250_register_port(&uport); + sp->serial_line = serial8250_register_8250_port(&uart); if (sp->serial_line < 0) { dev_err(sp->dev, "Failed to register serial port\n"); return; diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index b7eb545..4999b34 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -60,7 +60,7 @@ struct pti_tty { }; struct pti_dev { - struct tty_port port; + struct tty_port port[PTITTY_MINOR_NUM]; unsigned long pti_addr; unsigned long aperture_base; void __iomem *pti_ioaddr; @@ -76,7 +76,7 @@ struct pti_dev { */ static DEFINE_MUTEX(alloclock); -static struct pci_device_id pci_ids[] __devinitconst = { +static const struct pci_device_id pci_ids[] __devinitconst = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)}, {0} }; @@ -393,25 +393,6 @@ void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count) } EXPORT_SYMBOL_GPL(pti_writedata); -/** - * pti_pci_remove()- Driver exit method to remove PTI from - * PCI bus. - * @pdev: variable containing pci info of PTI. - */ -static void __devexit pti_pci_remove(struct pci_dev *pdev) -{ - struct pti_dev *drv_data; - - drv_data = pci_get_drvdata(pdev); - if (drv_data != NULL) { - pci_iounmap(pdev, drv_data->pti_ioaddr); - pci_set_drvdata(pdev, NULL); - kfree(drv_data); - pci_release_region(pdev, 1); - pci_disable_device(pdev); - } -} - /* * for the tty_driver_*() basic function descriptions, see tty_driver.h. * Specific header comments made for PTI-related specifics. @@ -446,7 +427,7 @@ static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp) * also removes a locking requirement for the actual write * procedure. */ - return tty_port_open(&drv_data->port, tty, filp); + return tty_port_open(tty->port, tty, filp); } /** @@ -462,7 +443,7 @@ static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp) */ static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp) { - tty_port_close(&drv_data->port, tty, filp); + tty_port_close(tty->port, tty, filp); } /** @@ -818,6 +799,7 @@ static const struct tty_port_operations tty_port_ops = { static int __devinit pti_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + unsigned int a; int retval = -EINVAL; int pci_bar = 1; @@ -830,7 +812,7 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev, __func__, __LINE__); pr_err("%s(%d): Error value returned: %d\n", __func__, __LINE__, retval); - return retval; + goto err; } retval = pci_enable_device(pdev); @@ -838,17 +820,16 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "%s: pci_enable_device() returned error %d\n", __func__, retval); - return retval; + goto err_unreg_misc; } drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); - if (drv_data == NULL) { retval = -ENOMEM; dev_err(&pdev->dev, "%s(%d): kmalloc() returned NULL memory.\n", __func__, __LINE__); - return retval; + goto err_disable_pci; } drv_data->pti_addr = pci_resource_start(pdev, pci_bar); @@ -857,33 +838,65 @@ static int __devinit pti_pci_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "%s(%d): pci_request_region() returned error %d\n", __func__, __LINE__, retval); - kfree(drv_data); - return retval; + goto err_free_dd; } drv_data->aperture_base = drv_data->pti_addr+APERTURE_14; drv_data->pti_ioaddr = ioremap_nocache((u32)drv_data->aperture_base, APERTURE_LEN); if (!drv_data->pti_ioaddr) { - pci_release_region(pdev, pci_bar); retval = -ENOMEM; - kfree(drv_data); - return retval; + goto err_rel_reg; } pci_set_drvdata(pdev, drv_data); - tty_port_init(&drv_data->port); - drv_data->port.ops = &tty_port_ops; + for (a = 0; a < PTITTY_MINOR_NUM; a++) { + struct tty_port *port = &drv_data->port[a]; + tty_port_init(port); + port->ops = &tty_port_ops; - tty_register_device(pti_tty_driver, 0, &pdev->dev); - tty_register_device(pti_tty_driver, 1, &pdev->dev); + tty_port_register_device(port, pti_tty_driver, a, &pdev->dev); + } register_console(&pti_console); + return 0; +err_rel_reg: + pci_release_region(pdev, pci_bar); +err_free_dd: + kfree(drv_data); +err_disable_pci: + pci_disable_device(pdev); +err_unreg_misc: + misc_deregister(&pti_char_driver); +err: return retval; } +/** + * pti_pci_remove()- Driver exit method to remove PTI from + * PCI bus. + * @pdev: variable containing pci info of PTI. + */ +static void __devexit pti_pci_remove(struct pci_dev *pdev) +{ + struct pti_dev *drv_data = pci_get_drvdata(pdev); + + unregister_console(&pti_console); + + tty_unregister_device(pti_tty_driver, 0); + tty_unregister_device(pti_tty_driver, 1); + + iounmap(drv_data->pti_ioaddr); + pci_set_drvdata(pdev, NULL); + kfree(drv_data); + pci_release_region(pdev, 1); + pci_disable_device(pdev); + + misc_deregister(&pti_char_driver); +} + static struct pci_driver pti_pci_driver = { .name = PCINAME, .id_table = pci_ids, @@ -933,25 +946,24 @@ static int __init pti_init(void) pr_err("%s(%d): Error value returned: %d\n", __func__, __LINE__, retval); - pti_tty_driver = NULL; - return retval; + goto put_tty; } retval = pci_register_driver(&pti_pci_driver); - if (retval) { pr_err("%s(%d): PCI registration failed of pti driver\n", __func__, __LINE__); pr_err("%s(%d): Error value returned: %d\n", __func__, __LINE__, retval); - - tty_unregister_driver(pti_tty_driver); - pr_err("%s(%d): Unregistering TTY part of pti driver\n", - __func__, __LINE__); - pti_tty_driver = NULL; - return retval; + goto unreg_tty; } + return 0; +unreg_tty: + tty_unregister_driver(pti_tty_driver); +put_tty: + put_tty_driver(pti_tty_driver); + pti_tty_driver = NULL; return retval; } @@ -960,31 +972,9 @@ static int __init pti_init(void) */ static void __exit pti_exit(void) { - int retval; - - tty_unregister_device(pti_tty_driver, 0); - tty_unregister_device(pti_tty_driver, 1); - - retval = tty_unregister_driver(pti_tty_driver); - if (retval) { - pr_err("%s(%d): TTY unregistration failed of pti driver\n", - __func__, __LINE__); - pr_err("%s(%d): Error value returned: %d\n", - __func__, __LINE__, retval); - } - + tty_unregister_driver(pti_tty_driver); pci_unregister_driver(&pti_pci_driver); - - retval = misc_deregister(&pti_char_driver); - if (retval) { - pr_err("%s(%d): CHAR unregistration failed of pti driver\n", - __func__, __LINE__); - pr_err("%s(%d): Error value returned: %d\n", - __func__, __LINE__, retval); - } - - unregister_console(&pti_console); - return; + put_tty_driver(pti_tty_driver); } module_init(pti_init); diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 5a2cbfa..d2339ea 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -518,7 +518,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) if (status & UART_MSR_DCTS) { port->icount.cts++; tty = tty_port_tty_get(&port->port); - if (tty && (tty->termios->c_cflag & CRTSCTS)) { + if (tty && (tty->termios.c_cflag & CRTSCTS)) { int cts = (status & UART_MSR_CTS); if (tty->hw_stopped) { if (cts) { @@ -671,12 +671,12 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) port->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE; port->mctrl = TIOCM_OUT2; - sdio_uart_change_speed(port, tty->termios, NULL); + sdio_uart_change_speed(port, &tty->termios, NULL); - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -850,7 +850,7 @@ static void sdio_uart_throttle(struct tty_struct *tty) { struct sdio_uart_port *port = tty->driver_data; - if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) + if (!I_IXOFF(tty) && !(tty->termios.c_cflag & CRTSCTS)) return; if (sdio_uart_claim_func(port) != 0) @@ -861,7 +861,7 @@ static void sdio_uart_throttle(struct tty_struct *tty) sdio_uart_start_tx(port); } - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) sdio_uart_clear_mctrl(port, TIOCM_RTS); sdio_uart_irq(port->func); @@ -872,7 +872,7 @@ static void sdio_uart_unthrottle(struct tty_struct *tty) { struct sdio_uart_port *port = tty->driver_data; - if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) + if (!I_IXOFF(tty) && !(tty->termios.c_cflag & CRTSCTS)) return; if (sdio_uart_claim_func(port) != 0) @@ -887,7 +887,7 @@ static void sdio_uart_unthrottle(struct tty_struct *tty) } } - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) sdio_uart_set_mctrl(port, TIOCM_RTS); sdio_uart_irq(port->func); @@ -898,12 +898,12 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct sdio_uart_port *port = tty->driver_data; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; if (sdio_uart_claim_func(port) != 0) return; - sdio_uart_change_speed(port, tty->termios, old_termios); + sdio_uart_change_speed(port, &tty->termios, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) @@ -1132,8 +1132,8 @@ static int sdio_uart_probe(struct sdio_func *func, kfree(port); } else { struct device *dev; - dev = tty_register_device(sdio_uart_tty_driver, - port->index, &func->dev); + dev = tty_port_register_device(&port->port, + sdio_uart_tty_driver, port->index, &func->dev); if (IS_ERR(dev)) { sdio_uart_port_remove(port); ret = PTR_ERR(dev); diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index b5ba308..3e5519a 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1147,15 +1147,17 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart) { #define COSMISC_CONSTANT 6 - struct uart_port port = { - .irq = 0, - .flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF, - .iotype = UPIO_MEM, - .regshift = 0, - .uartclk = (22000000 << 1) / COSMISC_CONSTANT, - - .membase = (unsigned char __iomem *) uart, - .mapbase = (unsigned long) uart, + struct uart_8250_port port = { + .port = { + .irq = 0, + .flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 0, + .uartclk = (22000000 << 1) / COSMISC_CONSTANT, + + .membase = (unsigned char __iomem *) uart, + .mapbase = (unsigned long) uart, + } }; unsigned char lcr; @@ -1164,7 +1166,7 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart) uart->iu_scr = COSMISC_CONSTANT, uart->iu_lcr = lcr; uart->iu_lcr; - serial8250_register_port(&port); + serial8250_register_8250_port(&port); } static void __devinit ioc3_serial_probe(struct pci_dev *pdev, struct ioc3 *ioc3) diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index 3352b24..30087ca 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -124,8 +124,8 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed) tty = priv->tty; mutex_lock(&tty->termios_mutex); - old_termios = *(tty->termios); - cflag = tty->termios->c_cflag; + old_termios = tty->termios; + cflag = tty->termios.c_cflag; tty_encode_baud_rate(tty, speed, speed); if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); @@ -281,15 +281,15 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) int cflag; mutex_lock(&tty->termios_mutex); - old_termios = *(tty->termios); - cflag = tty->termios->c_cflag; + old_termios = tty->termios; + cflag = tty->termios.c_cflag; if (stop) cflag &= ~CREAD; else cflag |= CREAD; - tty->termios->c_cflag = cflag; + tty->termios.c_cflag = cflag; if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); mutex_unlock(&tty->termios_mutex); diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 62f30b4..605a4ba 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1107,7 +1107,6 @@ static void _hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) { struct hso_serial *serial = tty->driver_data; - struct ktermios *termios; if (!serial) { printk(KERN_ERR "%s: no tty structures", __func__); @@ -1119,16 +1118,15 @@ static void _hso_serial_set_termios(struct tty_struct *tty, /* * Fix up unsupported bits */ - termios = tty->termios; - termios->c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ + tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ - termios->c_cflag &= + tty->termios.c_cflag &= ~(CSIZE /* no size */ | PARENB /* disable parity bit */ | CBAUD /* clear current baud rate */ | CBAUDEX); /* clear current buad rate */ - termios->c_cflag |= CS8; /* character size 8 bits */ + tty->termios.c_cflag |= CS8; /* character size 8 bits */ /* baud rate 115200 */ tty_encode_baud_rate(tty, 115200, 115200); @@ -1425,14 +1423,14 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) if (old) D5("Termios called with: cflags new[%d] - old[%d]", - tty->termios->c_cflag, old->c_cflag); + tty->termios.c_cflag, old->c_cflag); /* the actual setup */ spin_lock_irqsave(&serial->serial_lock, flags); if (serial->port.count) _hso_serial_set_termios(tty, old); else - tty->termios = old; + tty->termios = *old; spin_unlock_irqrestore(&serial->serial_lock, flags); /* done */ @@ -2289,9 +2287,11 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, if (minor < 0) goto exit; + tty_port_init(&serial->port); + /* register our minor number */ - serial->parent->dev = tty_register_device(tty_drv, minor, - &serial->parent->interface->dev); + serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, + minor, &serial->parent->interface->dev); dev = serial->parent->dev; dev_set_drvdata(dev, serial->parent); i = device_create_file(dev, &dev_attr_hsotype); @@ -2300,7 +2300,6 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, serial->minor = minor; serial->magic = HSO_SERIAL_MAGIC; spin_lock_init(&serial->serial_lock); - tty_port_init(&serial->port); serial->num_rx_urbs = num_urbs; /* RX, allocate urb and initialize */ diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 5d6de38..352f961 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -271,6 +271,7 @@ struct parport *__devinit parport_gsc_probe_port (unsigned long base, if (!parport_SPP_supported (p)) { /* No port. */ kfree (priv); + kfree(ops); return NULL; } parport_PS2_supported (p); diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index e9c3227..1631eeaf 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -62,6 +62,7 @@ enum parport_pc_pci_cards { timedia_9079a, timedia_9079b, timedia_9079c, + wch_ch353_2s1p, }; /* each element directly indexed from enum list, above */ @@ -145,6 +146,7 @@ static struct parport_pc_pci cards[] __devinitdata = { /* timedia_9079a */ { 1, { { 2, 3 }, } }, /* timedia_9079b */ { 1, { { 2, 3 }, } }, /* timedia_9079c */ { 1, { { 2, 3 }, } }, + /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, }; static struct pci_device_id parport_serial_pci_tbl[] = { @@ -243,7 +245,8 @@ static struct pci_device_id parport_serial_pci_tbl[] = { { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, - + /* WCH CARDS */ + { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); @@ -460,6 +463,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { .base_baud = 921600, .uart_offset = 8, }, + [wch_ch353_2s1p] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, }; struct parport_serial_private { diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 6c0116d..9ffb6d5 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -716,10 +716,17 @@ static int raw3215_probe (struct ccw_device *cdev) static void raw3215_remove (struct ccw_device *cdev) { struct raw3215_info *raw; + unsigned int line; ccw_device_set_offline(cdev); raw = dev_get_drvdata(&cdev->dev); if (raw) { + spin_lock(&raw3215_device_lock); + for (line = 0; line < NR_3215; line++) + if (raw3215[line] == raw) + break; + raw3215[line] = NULL; + spin_unlock(&raw3215_device_lock); dev_set_drvdata(&cdev->dev, NULL); raw3215_free_info(raw); } @@ -935,6 +942,19 @@ static int __init con3215_init(void) console_initcall(con3215_init); #endif +static int tty3215_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct raw3215_info *raw; + + raw = raw3215[tty->index]; + if (raw == NULL) + return -ENODEV; + + tty->driver_data = raw; + + return tty_port_install(&raw->port, driver, tty); +} + /* * tty3215_open * @@ -942,14 +962,9 @@ console_initcall(con3215_init); */ static int tty3215_open(struct tty_struct *tty, struct file * filp) { - struct raw3215_info *raw; + struct raw3215_info *raw = tty->driver_data; int retval; - raw = raw3215[tty->index]; - if (raw == NULL) - return -ENODEV; - - tty->driver_data = raw; tty_port_tty_set(&raw->port, tty); tty->low_latency = 0; /* don't use bottom half for pushing chars */ @@ -1110,6 +1125,7 @@ static void tty3215_start(struct tty_struct *tty) } static const struct tty_operations tty3215_ops = { + .install = tty3215_install, .open = tty3215_open, .close = tty3215_close, .write = tty3215_write, diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 0792c85..30ec09e 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -567,6 +567,7 @@ sclp_tty_init(void) driver->init_termios.c_lflag = ISIG | ECHO; driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(driver, &sclp_ops); + tty_port_link_device(&sclp_port, driver, 0); rc = tty_register_driver(driver); if (rc) { put_tty_driver(driver); diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index edfc0fd..7e60f3d 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -691,6 +691,7 @@ static int __init sclp_vt220_tty_init(void) driver->init_termios = tty_std_termios; driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(driver, &sclp_vt220_ops); + tty_port_link_device(&sclp_vt220_port, driver, 0); rc = tty_register_driver(driver); if (rc) diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 1928f34..482ee02 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -842,17 +842,14 @@ static struct raw3270_fn tty3270_fn = { }; /* - * This routine is called whenever a 3270 tty is opened. + * This routine is called whenever a 3270 tty is opened first time. */ -static int -tty3270_open(struct tty_struct *tty, struct file * filp) +static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) { struct raw3270_view *view; struct tty3270 *tp; int i, rc; - if (tty->count > 1) - return 0; /* Check if the tty3270 is already there. */ view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); @@ -865,7 +862,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp) /* why to reassign? */ tty_port_tty_set(&tp->port, tty); tp->inattr = TF_INPUT; - return 0; + return tty_port_install(&tp->port, driver, tty); } if (tty3270_max_index < tty->index + 1) tty3270_max_index = tty->index + 1; @@ -895,7 +892,6 @@ tty3270_open(struct tty_struct *tty, struct file * filp) tty_port_tty_set(&tp->port, tty); tty->low_latency = 0; - tty->driver_data = tp; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; @@ -915,6 +911,15 @@ tty3270_open(struct tty_struct *tty, struct file * filp) kbd_ascebc(tp->kbd, tp->view.ascebc); raw3270_activate_view(&tp->view); + + rc = tty_port_install(&tp->port, driver, tty); + if (rc) { + raw3270_put_view(&tp->view); + return rc; + } + + tty->driver_data = tp; + return 0; } @@ -932,10 +937,17 @@ tty3270_close(struct tty_struct *tty, struct file * filp) if (tp) { tty->driver_data = NULL; tty_port_tty_set(&tp->port, NULL); - raw3270_put_view(&tp->view); } } +static void tty3270_cleanup(struct tty_struct *tty) +{ + struct tty3270 *tp = tty->driver_data; + + if (tp) + raw3270_put_view(&tp->view); +} + /* * We always have room. */ @@ -1737,7 +1749,8 @@ static long tty3270_compat_ioctl(struct tty_struct *tty, #endif static const struct tty_operations tty3270_ops = { - .open = tty3270_open, + .install = tty3270_install, + .cleanup = tty3270_cleanup, .close = tty3270_close, .write = tty3270_write, .put_char = tty3270_put_char, @@ -1781,7 +1794,7 @@ static int __init tty3270_init(void) driver->type = TTY_DRIVER_TYPE_SYSTEM; driver->subtype = SYSTEM_TYPE_TTY; driver->init_termios = tty_std_termios; - driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV; + driver->flags = TTY_DRIVER_RESET_TERMIOS; tty_set_operations(driver, &tty3270_ops); ret = tty_register_driver(driver); if (ret) { @@ -1800,6 +1813,7 @@ tty3270_exit(void) driver = tty3270_driver; tty3270_driver = NULL; tty_unregister_driver(driver); + put_tty_driver(driver); tty3270_del_views(); } diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 0f51a15..d805eef 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -142,4 +142,6 @@ source "drivers/staging/ced1401/Kconfig" source "drivers/staging/imx-drm/Kconfig" +source "drivers/staging/dgrp/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index f4b2bc4..76e2ebd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_ZCACHE2) += ramster/ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ +obj-$(CONFIG_DGRP) += dgrp/ diff --git a/drivers/staging/dgrp/Kconfig b/drivers/staging/dgrp/Kconfig new file mode 100644 index 0000000..39f4bb6 --- /dev/null +++ b/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. diff --git a/drivers/staging/dgrp/Makefile b/drivers/staging/dgrp/Makefile new file mode 100644 index 0000000..d9c3b88 --- /dev/null +++ b/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 diff --git a/drivers/staging/dgrp/README b/drivers/staging/dgrp/README new file mode 100644 index 0000000..1d8aaee --- /dev/null +++ b/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 diff --git a/drivers/staging/dgrp/TODO b/drivers/staging/dgrp/TODO new file mode 100644 index 0000000..3ef2611 --- /dev/null +++ b/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 diff --git a/drivers/staging/dgrp/dgrp_common.c b/drivers/staging/dgrp/dgrp_common.c new file mode 100644 index 0000000..fce74e7 --- /dev/null +++ b/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); +} diff --git a/drivers/staging/dgrp/dgrp_common.h b/drivers/staging/dgrp/dgrp_common.h new file mode 100644 index 0000000..05ff338 --- /dev/null +++ b/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 */ diff --git a/drivers/staging/dgrp/dgrp_dpa_ops.c b/drivers/staging/dgrp/dgrp_dpa_ops.c new file mode 100644 index 0000000..49e6709 --- /dev/null +++ b/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); +} diff --git a/drivers/staging/dgrp/dgrp_driver.c b/drivers/staging/dgrp/dgrp_driver.c new file mode 100644 index 0000000..6e4a0eb --- /dev/null +++ b/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); + } +} diff --git a/drivers/staging/dgrp/dgrp_mon_ops.c b/drivers/staging/dgrp/dgrp_mon_ops.c new file mode 100644 index 0000000..268dcb9 --- /dev/null +++ b/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; +} diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c new file mode 100644 index 0000000..ab839ea --- /dev/null +++ b/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); +} diff --git a/drivers/staging/dgrp/dgrp_ports_ops.c b/drivers/staging/dgrp/dgrp_ports_ops.c new file mode 100644 index 0000000..cd1fc20 --- /dev/null +++ b/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; +} diff --git a/drivers/staging/dgrp/dgrp_specproc.c b/drivers/staging/dgrp/dgrp_specproc.c new file mode 100644 index 0000000..28f5c9a --- /dev/null +++ b/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; +} diff --git a/drivers/staging/dgrp/dgrp_sysfs.c b/drivers/staging/dgrp/dgrp_sysfs.c new file mode 100644 index 0000000..e5a3c88 --- /dev/null +++ b/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); +} diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c new file mode 100644 index 0000000..7d7de87 --- /dev/null +++ b/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; +} diff --git a/drivers/staging/dgrp/digirp.h b/drivers/staging/dgrp/digirp.h new file mode 100644 index 0000000..33c1394 --- /dev/null +++ b/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 */ diff --git a/drivers/staging/dgrp/drp.h b/drivers/staging/dgrp/drp.h new file mode 100644 index 0000000..84a1e7b --- /dev/null +++ b/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 */ diff --git a/drivers/staging/ipack/devices/ipoctal.c b/drivers/staging/ipack/devices/ipoctal.c index 2cdbf28..d751edf 100644 --- a/drivers/staging/ipack/devices/ipoctal.c +++ b/drivers/staging/ipack/devices/ipoctal.c @@ -443,7 +443,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, spin_lock_init(&channel->lock); channel->pointer_read = 0; channel->pointer_write = 0; - tty_dev = tty_register_device(tty, i, NULL); + tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); if (IS_ERR(tty_dev)) { dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); continue; @@ -543,7 +543,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, struct ipoctal_channel *channel = tty->driver_data; speed_t baud; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* Disable and reset everything before change the setup */ iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); @@ -564,7 +564,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, default: mr1 |= MR1_CHRL_8_BITS; /* By default, select CS8 */ - tty->termios->c_cflag = (cflag & ~CSIZE) | CS8; + tty->termios.c_cflag = (cflag & ~CSIZE) | CS8; break; } @@ -578,7 +578,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, mr1 |= MR1_PARITY_OFF; /* Mark or space parity is not supported */ - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; /* Set stop bits */ if (cflag & CSTOPB) @@ -611,10 +611,10 @@ static void ipoctal_set_termios(struct tty_struct *tty, } baud = tty_get_baud_rate(tty); - tty_termios_encode_baud_rate(tty->termios, baud, baud); + tty_termios_encode_baud_rate(&tty->termios, baud, baud); /* Set baud rate */ - switch (tty->termios->c_ospeed) { + switch (baud) { case 75: csr |= TX_CLK_75 | RX_CLK_75; break; @@ -655,7 +655,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, default: csr |= TX_CLK_38400 | RX_CLK_38400; /* In case of default, we establish 38400 bps */ - tty_termios_encode_baud_rate(tty->termios, 38400, 38400); + tty_termios_encode_baud_rate(&tty->termios, 38400, 38400); break; } diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index c56609c..5c969ad 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -313,10 +313,8 @@ static void qt_read_bulk_callback(struct urb *urb) } tty = tty_port_tty_get(&port->port); - if (!tty) { - dbg("%s - bad tty pointer - exiting", __func__); + if (!tty) return; - } data = urb->transfer_buffer; @@ -362,7 +360,7 @@ static void qt_read_bulk_callback(struct urb *urb) goto exit; } - if (tty && RxCount) { + if (RxCount) { flag_data = 0; for (i = 0; i < RxCount; ++i) { /* Look ahead code here */ @@ -426,7 +424,7 @@ static void qt_read_bulk_callback(struct urb *urb) dbg("%s - failed resubmitting read urb, error %d", __func__, result); else { - if (tty && RxCount) { + if (RxCount) { tty_flip_buffer_push(tty); tty_schedule_flip(tty); } @@ -895,8 +893,6 @@ static int qt_open(struct tty_struct *tty, * Put this here to make it responsive to stty and defaults set by * the tty layer */ - /* FIXME: is this needed? */ - /* qt_set_termios(tty, port, NULL); */ /* Check to see if we've set up our endpoint info yet */ if (port0->open_ports == 1) { @@ -1193,7 +1189,7 @@ static void qt_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; unsigned char new_LCR = 0; unsigned int cflag = termios->c_cflag; unsigned int index; @@ -1202,7 +1198,7 @@ static void qt_set_termios(struct tty_struct *tty, index = tty->index - port->serial->minor; - switch (cflag) { + switch (cflag & CSIZE) { case CS5: new_LCR |= SERIAL_5_DATA; break; @@ -1213,6 +1209,8 @@ static void qt_set_termios(struct tty_struct *tty, new_LCR |= SERIAL_7_DATA; break; default: + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; case CS8: new_LCR |= SERIAL_8_DATA; break; @@ -1299,7 +1297,7 @@ static void qt_set_termios(struct tty_struct *tty, dbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n"); } - tty->termios->c_cflag &= ~CMSPAR; + termios->c_cflag &= ~CMSPAR; /* FIXME: Error cases should be returning the actual bits changed only */ } diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h index 614271f..55d68b5 100644 --- a/drivers/staging/speakup/serialio.h +++ b/drivers/staging/speakup/serialio.h @@ -1,8 +1,7 @@ #ifndef _SPEAKUP_SERIAL_H #define _SPEAKUP_SERIAL_H -#include <linux/serial.h> /* for rs_table, serial constants & - serial_uart_config */ +#include <linux/serial.h> /* for rs_table, serial constants */ #include <linux/serial_reg.h> /* for more serial constants */ #ifndef __sparc__ #include <asm/serial.h> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 830cd62..d8e05ee 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -214,8 +214,8 @@ config CYCLADES If you haven't heard about it, it's safe to say N. config CYZ_INTR - bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)" - depends on EXPERIMENTAL && CYCLADES + bool "Cyclades-Z interrupt mode operation" + depends on CYCLADES help The Cyclades-Z family of multiport cards allows 2 (two) driver op modes: polling and interrupt. In polling mode, the driver will check @@ -285,7 +285,7 @@ config SYNCLINK_GT config NOZOMI tristate "HSDPA Broadband Wireless Data Card - Globe Trotter" - depends on PCI && EXPERIMENTAL + depends on PCI help If you have a HSDPA driver Broadband Wireless Data Card - Globe Trotter PCMCIA card, say Y here. @@ -294,7 +294,7 @@ config NOZOMI will be called nozomi. config ISI - tristate "Multi-Tech multiport card support (EXPERIMENTAL)" + tristate "Multi-Tech multiport card support" depends on SERIAL_NONSTANDARD && PCI select FW_LOADER help @@ -317,7 +317,6 @@ config N_HDLC config N_GSM tristate "GSM MUX line discipline support (EXPERIMENTAL)" - depends on EXPERIMENTAL depends on NET help This line discipline provides support for the GSM MUX protocol and diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6cc4358..42d0a25 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -420,7 +420,7 @@ static void check_modem_status(struct serial_state *info) tty_hangup(port->tty); } } - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { if (port->tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) @@ -646,7 +646,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) custom.adkcon = AC_UARTBRK; mb(); - if (tty->termios->c_cflag & HUPCL) + if (tty->termios.c_cflag & HUPCL) info->MCR &= ~(SER_DTR|SER_RTS); rtsdtr_ctrl(info->MCR); @@ -670,7 +670,7 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, int bits; unsigned long flags; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* Byte size is always 8 bits plus parity bit if requested */ @@ -707,8 +707,8 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, /* If the quotient is zero refuse the change */ if (!quot && old_termios) { /* FIXME: Will need updating for new tty in the end */ - tty->termios->c_cflag &= ~CBAUD; - tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + tty->termios.c_cflag &= ~CBAUD; + tty->termios.c_cflag |= (old_termios->c_cflag & CBAUD); baud = tty_get_baud_rate(tty); if (!baud) baud = 9600; @@ -984,7 +984,7 @@ static void rs_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) info->MCR &= ~SER_RTS; local_irq_save(flags); @@ -1012,7 +1012,7 @@ static void rs_unthrottle(struct tty_struct * tty) else rs_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) info->MCR |= SER_RTS; local_irq_save(flags); rtsdtr_ctrl(info->MCR); @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(); + tty_unlock(tty); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -1330,7 +1330,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct serial_state *info = tty->driver_data; unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; change_speed(tty, info, old_termios); @@ -1347,7 +1347,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->MCR |= SER_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= SER_RTS; } @@ -1358,7 +1358,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); } @@ -1371,7 +1371,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) + (tty->termios.c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait); #endif } @@ -1710,10 +1710,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev) serial_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(serial_driver, &serial_ops); - error = tty_register_driver(serial_driver); - if (error) - goto fail_put_tty_driver; - state = rs_table; state->port = (int)&custom.serdatr; /* Just to give it a value */ state->custom_divisor = 0; @@ -1724,6 +1720,11 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state->icount.overrun = state->icount.brk = 0; tty_port_init(&state->tport); state->tport.ops = &amiga_port_ops; + tty_port_link_device(&state->tport, serial_driver, 0); + + error = tty_register_driver(serial_driver); + if (error) + goto fail_put_tty_driver; printk(KERN_INFO "ttyS0 is the amiga builtin serial port\n"); diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index 61fc74f..02b7d3a 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -263,6 +263,7 @@ static int __init bfin_jc_init(void) bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL; bfin_jc_driver->init_termios = tty_std_termios; tty_set_operations(bfin_jc_driver, &bfin_jc_ops); + tty_port_link_device(&port, bfin_jc_driver, 0); ret = tty_register_driver(bfin_jc_driver); if (ret) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e61cabd..0a6a0bc 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -727,7 +727,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, else tty_hangup(tty); } - if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { + if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used @@ -1459,7 +1459,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - if (tty->termios->c_cflag & HUPCL) + if (tty->termios.c_cflag & HUPCL) cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); @@ -1488,7 +1488,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) free_page((unsigned long)temp); } - if (tty->termios->c_cflag & HUPCL) + if (tty->termios.c_cflag & HUPCL) tty_port_lower_dtr_rts(&info->port); set_bit(TTY_IO_ERROR, &tty->flags); @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } @@ -1999,14 +1999,11 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) int baud, baud_rate = 0; int i; - if (!tty->termios) /* XXX can this happen at all? */ - return; - if (info->line == -1) return; - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; /* * Set up the tty->alt_speed kludge @@ -2825,7 +2822,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) cy_set_line_char(info, tty); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; cy_start(tty); } @@ -2837,7 +2834,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) + (tty->termios.c_cflag & CLOCAL)) wake_up_interruptible(&info->port.open_wait); #endif } /* cy_set_termios */ @@ -2899,7 +2896,7 @@ static void cy_throttle(struct tty_struct *tty) info->throttle = 1; } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); cyy_change_rts_dtr(info, 0, TIOCM_RTS); @@ -2938,7 +2935,7 @@ static void cy_unthrottle(struct tty_struct *tty) cy_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { card = info->card; if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); @@ -3289,9 +3286,10 @@ static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, static int __init cy_detect_isa(void) { #ifdef CONFIG_ISA + struct cyclades_card *card; unsigned short cy_isa_irq, nboard; void __iomem *cy_isa_address; - unsigned short i, j, cy_isa_nchan; + unsigned short i, j, k, cy_isa_nchan; int isparam = 0; nboard = 0; @@ -3349,7 +3347,8 @@ static int __init cy_detect_isa(void) } /* fill the next cy_card structure available */ for (j = 0; j < NR_CARDS; j++) { - if (cy_card[j].base_addr == NULL) + card = &cy_card[j]; + if (card->base_addr == NULL) break; } if (j == NR_CARDS) { /* no more cy_cards available */ @@ -3363,7 +3362,7 @@ static int __init cy_detect_isa(void) /* allocate IRQ */ if (request_irq(cy_isa_irq, cyy_interrupt, - 0, "Cyclom-Y", &cy_card[j])) { + 0, "Cyclom-Y", card)) { printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " "could not allocate IRQ#%d.\n", (unsigned long)cy_isa_address, cy_isa_irq); @@ -3372,16 +3371,16 @@ static int __init cy_detect_isa(void) } /* set cy_card */ - cy_card[j].base_addr = cy_isa_address; - cy_card[j].ctl_addr.p9050 = NULL; - cy_card[j].irq = (int)cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; - cy_card[j].nports = cy_isa_nchan; - if (cy_init_card(&cy_card[j])) { - cy_card[j].base_addr = NULL; - free_irq(cy_isa_irq, &cy_card[j]); + card->base_addr = cy_isa_address; + card->ctl_addr.p9050 = NULL; + card->irq = (int)cy_isa_irq; + card->bus_index = 0; + card->first_line = cy_next_channel; + card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; + card->nports = cy_isa_nchan; + if (cy_init_card(card)) { + card->base_addr = NULL; + free_irq(cy_isa_irq, card); iounmap(cy_isa_address); continue; } @@ -3393,9 +3392,10 @@ static int __init cy_detect_isa(void) (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), cy_isa_irq, cy_isa_nchan, cy_next_channel); - for (j = cy_next_channel; - j < cy_next_channel + cy_isa_nchan; j++) - tty_register_device(cy_serial_driver, j, NULL); + for (k = 0, j = cy_next_channel; + j < cy_next_channel + cy_isa_nchan; j++, k++) + tty_port_register_device(&card->ports[k].port, + cy_serial_driver, j, NULL); cy_next_channel += cy_isa_nchan; } return nboard; @@ -3695,10 +3695,11 @@ err: static int __devinit cy_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct cyclades_card *card; void __iomem *addr0 = NULL, *addr2 = NULL; char *card_name = NULL; u32 uninitialized_var(mailbox); - unsigned int device_id, nchan = 0, card_no, i; + unsigned int device_id, nchan = 0, card_no, i, j; unsigned char plx_ver; int retval, irq; @@ -3829,7 +3830,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } /* fill the next cy_card structure available */ for (card_no = 0; card_no < NR_CARDS; card_no++) { - if (cy_card[card_no].base_addr == NULL) + card = &cy_card[card_no]; + if (card->base_addr == NULL) break; } if (card_no == NR_CARDS) { /* no more cy_cards available */ @@ -3843,27 +3845,26 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { /* allocate IRQ */ retval = request_irq(irq, cyy_interrupt, - IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); + IRQF_SHARED, "Cyclom-Y", card); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ\n"); goto err_unmap; } - cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; + card->num_chips = nchan / CyPORTS_PER_CHIP; } else { struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; struct ZFW_CTRL __iomem *zfw_ctrl; zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - cy_card[card_no].hw_ver = mailbox; - cy_card[card_no].num_chips = (unsigned int)-1; - cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; + card->hw_ver = mailbox; + card->num_chips = (unsigned int)-1; + card->board_ctrl = &zfw_ctrl->board_ctrl; #ifdef CONFIG_CYZ_INTR /* allocate IRQ only if board has an IRQ */ if (irq != 0 && irq != 255) { retval = request_irq(irq, cyz_interrupt, - IRQF_SHARED, "Cyclades-Z", - &cy_card[card_no]); + IRQF_SHARED, "Cyclades-Z", card); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ\n"); goto err_unmap; @@ -3873,17 +3874,17 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } /* set cy_card */ - cy_card[card_no].base_addr = addr2; - cy_card[card_no].ctl_addr.p9050 = addr0; - cy_card[card_no].irq = irq; - cy_card[card_no].bus_index = 1; - cy_card[card_no].first_line = cy_next_channel; - cy_card[card_no].nports = nchan; - retval = cy_init_card(&cy_card[card_no]); + card->base_addr = addr2; + card->ctl_addr.p9050 = addr0; + card->irq = irq; + card->bus_index = 1; + card->first_line = cy_next_channel; + card->nports = nchan; + retval = cy_init_card(card); if (retval) goto err_null; - pci_set_drvdata(pdev, &cy_card[card_no]); + pci_set_drvdata(pdev, card); if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { @@ -3909,14 +3910,15 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); - for (i = cy_next_channel; i < cy_next_channel + nchan; i++) - tty_register_device(cy_serial_driver, i, &pdev->dev); + for (j = 0, i = cy_next_channel; i < cy_next_channel + nchan; i++, j++) + tty_port_register_device(&card->ports[j].port, + cy_serial_driver, i, &pdev->dev); cy_next_channel += nchan; return 0; err_null: - cy_card[card_no].base_addr = NULL; - free_irq(irq, &cy_card[card_no]); + card->base_addr = NULL; + free_irq(irq, card); err_unmap: iounmap(addr0); if (addr2) diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 4813684..4ab936b 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -738,16 +738,17 @@ static int __devinit ehv_bc_tty_probe(struct platform_device *pdev) goto error; } - bc->dev = tty_register_device(ehv_bc_driver, i, &pdev->dev); + tty_port_init(&bc->port); + bc->port.ops = &ehv_bc_tty_port_ops; + + bc->dev = tty_port_register_device(&bc->port, ehv_bc_driver, i, + &pdev->dev); if (IS_ERR(bc->dev)) { ret = PTR_ERR(bc->dev); dev_err(&pdev->dev, "could not register tty (ret=%i)\n", ret); goto error; } - tty_port_init(&bc->port); - bc->port.ops = &ehv_bc_tty_port_ops; - dev_set_drvdata(&pdev->dev, bc); dev_info(&pdev->dev, "registered /dev/%s%u for byte channel %u\n", diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 0282a83..f47b734 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -76,7 +76,7 @@ config HVC_XEN_FRONTEND config HVC_UDBG bool "udbg based fake hypervisor console" - depends on PPC && EXPERIMENTAL + depends on PPC select HVC_DRIVER default n help diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 2d691eb..4a65299 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -299,20 +299,33 @@ static void hvc_unthrottle(struct tty_struct *tty) hvc_kick(); } +static int hvc_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct hvc_struct *hp; + int rc; + + /* Auto increments kref reference if found. */ + if (!(hp = hvc_get_by_index(tty->index))) + return -ENODEV; + + tty->driver_data = hp; + + rc = tty_port_install(&hp->port, driver, tty); + if (rc) + tty_port_put(&hp->port); + return rc; +} + /* * The TTY interface won't be used until after the vio layer has exposed the vty * adapter to the kernel. */ static int hvc_open(struct tty_struct *tty, struct file * filp) { - struct hvc_struct *hp; + struct hvc_struct *hp = tty->driver_data; unsigned long flags; int rc = 0; - /* Auto increments kref reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) - return -ENODEV; - spin_lock_irqsave(&hp->port.lock, flags); /* Check and then increment for fast path open. */ if (hp->port.count++ > 0) { @@ -322,7 +335,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) } /* else count == 0 */ spin_unlock_irqrestore(&hp->port.lock, flags); - tty->driver_data = hp; tty_port_tty_set(&hp->port, tty); if (hp->ops->notifier_add) @@ -389,6 +401,11 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) hp->vtermno, hp->port.count); spin_unlock_irqrestore(&hp->port.lock, flags); } +} + +static void hvc_cleanup(struct tty_struct *tty) +{ + struct hvc_struct *hp = tty->driver_data; tty_port_put(&hp->port); } @@ -541,7 +558,7 @@ static int hvc_write_room(struct tty_struct *tty) struct hvc_struct *hp = tty->driver_data; if (!hp) - return -1; + return 0; return hp->outbuf_size - hp->n_outbuf; } @@ -792,8 +809,10 @@ static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch) #endif static const struct tty_operations hvc_ops = { + .install = hvc_install, .open = hvc_open, .close = hvc_close, + .cleanup = hvc_cleanup, .write = hvc_write, .hangup = hvc_hangup, .unthrottle = hvc_unthrottle, diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index d56788c..cab5c7a 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1102,27 +1102,20 @@ static struct hvcs_struct *hvcs_get_by_index(int index) return NULL; } -/* - * This is invoked via the tty_open interface when a user app connects to the - * /dev node. - */ -static int hvcs_open(struct tty_struct *tty, struct file *filp) +static int hvcs_install(struct tty_driver *driver, struct tty_struct *tty) { struct hvcs_struct *hvcsd; - int rc, retval = 0; - unsigned long flags; - unsigned int irq; struct vio_dev *vdev; - unsigned long unit_address; - - if (tty->driver_data) - goto fast_open; + unsigned long unit_address, flags; + unsigned int irq; + int retval; /* * Is there a vty-server that shares the same index? * This function increments the kref index. */ - if (!(hvcsd = hvcs_get_by_index(tty->index))) { + hvcsd = hvcs_get_by_index(tty->index); + if (!hvcsd) { printk(KERN_WARNING "HVCS: open failed, no device associated" " with tty->index %d.\n", tty->index); return -ENODEV; @@ -1130,11 +1123,16 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&hvcsd->lock, flags); - if (hvcsd->connected == 0) - if ((retval = hvcs_partner_connect(hvcsd))) - goto error_release; + if (hvcsd->connected == 0) { + retval = hvcs_partner_connect(hvcsd); + if (retval) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + printk(KERN_WARNING "HVCS: partner connect failed.\n"); + goto err_put; + } + } - hvcsd->port.count = 1; + hvcsd->port.count = 0; hvcsd->port.tty = tty; tty->driver_data = hvcsd; @@ -1155,37 +1153,48 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) * This must be done outside of the spinlock because it requests irqs * and will grab the spinlock and free the connection if it fails. */ - if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { - tty_port_put(&hvcsd->port); + retval = hvcs_enable_device(hvcsd, unit_address, irq, vdev); + if (retval) { printk(KERN_WARNING "HVCS: enable device failed.\n"); - return rc; + goto err_put; } - goto open_success; + retval = tty_port_install(&hvcsd->port, driver, tty); + if (retval) + goto err_irq; -fast_open: - hvcsd = tty->driver_data; + return 0; +err_irq: + spin_lock_irqsave(&hvcsd->lock, flags); + vio_disable_interrupts(hvcsd->vdev); + spin_unlock_irqrestore(&hvcsd->lock, flags); + free_irq(irq, hvcsd); +err_put: + tty_port_put(&hvcsd->port); + + return retval; +} + +/* + * This is invoked via the tty_open interface when a user app connects to the + * /dev node. + */ +static int hvcs_open(struct tty_struct *tty, struct file *filp) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + unsigned long flags; spin_lock_irqsave(&hvcsd->lock, flags); - tty_port_get(&hvcsd->port); hvcsd->port.count++; hvcsd->todo_mask |= HVCS_SCHED_READ; spin_unlock_irqrestore(&hvcsd->lock, flags); -open_success: hvcs_kick(); printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", hvcsd->vdev->unit_address ); return 0; - -error_release: - spin_unlock_irqrestore(&hvcsd->lock, flags); - tty_port_put(&hvcsd->port); - - printk(KERN_WARNING "HVCS: partner connect failed.\n"); - return retval; } static void hvcs_close(struct tty_struct *tty, struct file *filp) @@ -1236,7 +1245,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) tty->driver_data = NULL; free_irq(irq, hvcsd); - tty_port_put(&hvcsd->port); return; } else if (hvcsd->port.count < 0) { printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" @@ -1245,6 +1253,12 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) } spin_unlock_irqrestore(&hvcsd->lock, flags); +} + +static void hvcs_cleanup(struct tty_struct * tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + tty_port_put(&hvcsd->port); } @@ -1431,8 +1445,10 @@ static int hvcs_chars_in_buffer(struct tty_struct *tty) } static const struct tty_operations hvcs_ops = { + .install = hvcs_install, .open = hvcs_open, .close = hvcs_close, + .cleanup = hvcs_cleanup, .hangup = hvcs_hangup, .write = hvcs_write, .write_room = hvcs_write_room, diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 6f5bc49..0083bc1 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1080,6 +1080,8 @@ static int __init hvsi_init(void) struct hvsi_struct *hp = &hvsi_ports[i]; int ret = 1; + tty_port_link_device(&hp->port, hvsi_driver, i); + ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp); if (ret) printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 59c135d..3396eb9 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -400,7 +400,7 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp) spin_unlock_irqrestore(&hp->lock, flags); /* Clear our own DTR */ - if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) + if (!pv->tty || (pv->tty->termios.c_cflag & HUPCL)) hvsilib_write_mctrl(pv, 0); /* Tear down the connection */ diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index 57c8b48..d2af155 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -274,7 +274,12 @@ static void do_go_online(struct work_struct *work_go_online) network->xaccm[0] = ~0U; network->xaccm[3] = 0x60000000U; network->raccm = ~0U; - ppp_register_channel(channel); + if (ppp_register_channel(channel) < 0) { + printk(KERN_ERR IPWIRELESS_PCCARD_NAME + ": unable to register PPP channel\n"); + kfree(channel); + return; + } spin_lock_irqsave(&network->lock, flags); network->ppp_channel = channel; } diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index f8b5fa0..160f0ad 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -476,7 +476,7 @@ static int add_tty(int j, mutex_init(&ttys[j]->ipw_tty_mutex); tty_port_init(&ttys[j]->port); - tty_register_device(ipw_tty_driver, j, NULL); + tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL); ipwireless_associate_network_tty(network, channel_idx, ttys[j]); if (secondary_channel_idx != -1) diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index e1235ac..d7492e1 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -600,7 +600,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) port->status &= ~ISI_DCD; } - if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (header & ISI_CTS) { port->port.tty->hw_stopped = 0; @@ -702,7 +702,7 @@ static void isicom_config_port(struct tty_struct *tty) /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ if (baud < 1 || baud > 4) - tty->termios->c_cflag &= ~CBAUDEX; + tty->termios.c_cflag &= ~CBAUDEX; else baud += 15; } @@ -1196,8 +1196,8 @@ static void isicom_set_termios(struct tty_struct *tty, if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) return; - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) + if (tty->termios.c_cflag == old_termios->c_cflag && + tty->termios.c_iflag == old_termios->c_iflag) return; spin_lock_irqsave(&port->card->card_lock, flags); @@ -1205,7 +1205,7 @@ static void isicom_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&port->card->card_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; isicom_start(tty); } @@ -1611,7 +1611,8 @@ static int __devinit isicom_probe(struct pci_dev *pdev, goto errunri; for (index = 0; index < board->port_count; index++) - tty_register_device(isicom_normal, board->index * 16 + index, + tty_port_register_device(&board->ports[index].port, + isicom_normal, board->index * 16 + index, &pdev->dev); return 0; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 324467d..56e616b 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -169,6 +169,7 @@ static DEFINE_SPINLOCK(moxa_lock); static unsigned long baseaddr[MAX_BOARDS]; static unsigned int type[MAX_BOARDS]; static unsigned int numports[MAX_BOARDS]; +static struct tty_port moxa_service_port; MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); @@ -367,10 +368,10 @@ static int moxa_ioctl(struct tty_struct *tty, tmp.dcd = 1; ttyp = tty_port_tty_get(&p->port); - if (!ttyp || !ttyp->termios) + if (!ttyp) tmp.cflag = p->cflag; else - tmp.cflag = ttyp->termios->c_cflag; + tmp.cflag = ttyp->termios.c_cflag; tty_kref_put(ttyp); copy: if (copy_to_user(argm, &tmp, sizeof(tmp))) @@ -834,7 +835,7 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) const struct firmware *fw; const char *file; struct moxa_port *p; - unsigned int i; + unsigned int i, first_idx; int ret; brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports), @@ -887,6 +888,11 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) mod_timer(&moxaTimer, jiffies + HZ / 50); spin_unlock_bh(&moxa_lock); + first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD; + for (i = 0; i < brd->numPorts; i++) + tty_port_register_device(&brd->ports[i].port, moxaDriver, + first_idx + i, dev); + return 0; err_free: kfree(brd->ports); @@ -896,7 +902,7 @@ err: static void moxa_board_deinit(struct moxa_board_conf *brd) { - unsigned int a, opened; + unsigned int a, opened, first_idx; mutex_lock(&moxa_openlock); spin_lock_bh(&moxa_lock); @@ -925,6 +931,10 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) mutex_lock(&moxa_openlock); } + first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD; + for (a = 0; a < brd->numPorts; a++) + tty_unregister_device(moxaDriver, first_idx + a); + iounmap(brd->basemem); brd->basemem = NULL; kfree(brd->ports); @@ -967,6 +977,7 @@ static int __devinit moxa_pci_probe(struct pci_dev *pdev, board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000); if (board->basemem == NULL) { dev_err(&pdev->dev, "can't remap io space 2\n"); + retval = -ENOMEM; goto err_reg; } @@ -1031,9 +1042,14 @@ static int __init moxa_init(void) printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION); - moxaDriver = alloc_tty_driver(MAX_PORTS + 1); - if (!moxaDriver) - return -ENOMEM; + + tty_port_init(&moxa_service_port); + + moxaDriver = tty_alloc_driver(MAX_PORTS + 1, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(moxaDriver)) + return PTR_ERR(moxaDriver); moxaDriver->name = "ttyMX"; moxaDriver->major = ttymajor; @@ -1044,8 +1060,9 @@ static int __init moxa_init(void) moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; moxaDriver->init_termios.c_ispeed = 9600; moxaDriver->init_termios.c_ospeed = 9600; - moxaDriver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(moxaDriver, &moxa_ops); + /* Having one more port only for ioctls is ugly */ + tty_port_link_device(&moxa_service_port, moxaDriver, MAX_PORTS); if (tty_register_driver(moxaDriver)) { printk(KERN_ERR "can't register MOXA Smartio tty driver!\n"); @@ -1178,7 +1195,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) mutex_lock(&ch->port.mutex); if (!(ch->port.flags & ASYNC_INITIALIZED)) { ch->statusflags = 0; - moxa_set_tty_param(tty, tty->termios); + moxa_set_tty_param(tty, &tty->termios); MoxaPortLineCtrl(ch, 1, 1); MoxaPortEnable(ch); MoxaSetFifo(ch, ch->type == PORT_16550A); @@ -1193,7 +1210,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) static void moxa_close(struct tty_struct *tty, struct file *filp) { struct moxa_port *ch = tty->driver_data; - ch->cflag = tty->termios->c_cflag; + ch->cflag = tty->termios.c_cflag; tty_port_close(&ch->port, tty, filp); } @@ -1464,7 +1481,7 @@ static void moxa_poll(unsigned long ignored) static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios) { - register struct ktermios *ts = tty->termios; + register struct ktermios *ts = &tty->termios; struct moxa_port *ch = tty->driver_data; int rts, cts, txflow, rxflow, xany, baud; diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 90cc680..cfda47d 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -643,7 +643,7 @@ static int mxser_change_speed(struct tty_struct *tty, int ret = 0; unsigned char status; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; if (!info->ioaddr) return ret; @@ -830,7 +830,7 @@ static void mxser_check_modem_status(struct tty_struct *tty, wake_up_interruptible(&port->port.open_wait); } - if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&port->port)) { if (tty->hw_stopped) { if (status & UART_MSR_CTS) { tty->hw_stopped = 0; @@ -1520,10 +1520,10 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) tty = tty_port_tty_get(port); - if (!tty || !tty->termios) + if (!tty) ms.cflag = ip->normal_termios.c_cflag; else - ms.cflag = tty->termios->c_cflag; + ms.cflag = tty->termios.c_cflag; tty_kref_put(tty); spin_lock_irq(&ip->slock); status = inb(ip->ioaddr + UART_MSR); @@ -1589,13 +1589,13 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) tty = tty_port_tty_get(&ip->port); - if (!tty || !tty->termios) { + if (!tty) { cflag = ip->normal_termios.c_cflag; iflag = ip->normal_termios.c_iflag; me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios); } else { - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; me->baudrate[p] = tty_get_baud_rate(tty); } tty_kref_put(tty); @@ -1853,7 +1853,7 @@ static void mxser_stoprx(struct tty_struct *tty) } } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { info->MCR &= ~UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1890,7 +1890,7 @@ static void mxser_unthrottle(struct tty_struct *tty) } } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { info->MCR |= UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1939,14 +1939,14 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi spin_unlock_irqrestore(&info->slock, flags); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; mxser_start(tty); } /* Handle sw stopped */ if ((old_termios->c_iflag & IXON) && - !(tty->termios->c_iflag & IXON)) { + !(tty->termios.c_iflag & IXON)) { tty->stopped = 0; if (info->board->chip_flag) { @@ -2337,11 +2337,36 @@ static struct tty_port_operations mxser_port_ops = { * The MOXA Smartio/Industio serial driver boot-time initialization code! */ +static bool allow_overlapping_vector; +module_param(allow_overlapping_vector, bool, S_IRUGO); +MODULE_PARM_DESC(allow_overlapping_vector, "whether we allow ISA cards to be configured such that vector overlabs IO ports (default=no)"); + +static bool mxser_overlapping_vector(struct mxser_board *brd) +{ + return allow_overlapping_vector && + brd->vector >= brd->ports[0].ioaddr && + brd->vector < brd->ports[0].ioaddr + 8 * brd->info->nports; +} + +static int mxser_request_vector(struct mxser_board *brd) +{ + if (mxser_overlapping_vector(brd)) + return 0; + return request_region(brd->vector, 1, "mxser(vector)") ? 0 : -EIO; +} + +static void mxser_release_vector(struct mxser_board *brd) +{ + if (mxser_overlapping_vector(brd)) + return; + release_region(brd->vector, 1); +} + static void mxser_release_ISA_res(struct mxser_board *brd) { free_irq(brd->irq, brd); release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - release_region(brd->vector, 1); + mxser_release_vector(brd); } static int __devinit mxser_initbrd(struct mxser_board *brd, @@ -2396,7 +2421,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) { - int id, i, bits; + int id, i, bits, ret; unsigned short regs[16], irq; unsigned char scratch, scratch2; @@ -2492,13 +2517,15 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) 8 * brd->info->nports - 1); return -EIO; } - if (!request_region(brd->vector, 1, "mxser(vector)")) { + + ret = mxser_request_vector(brd); + if (ret) { release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); printk(KERN_ERR "mxser: can't request interrupt vector region: " "0x%.8lx-0x%.8lx\n", brd->ports[0].ioaddr, brd->ports[0].ioaddr + 8 * brd->info->nports - 1); - return -EIO; + return ret; } return brd->info->nports; @@ -2598,7 +2625,8 @@ static int __devinit mxser_probe(struct pci_dev *pdev, goto err_rel3; for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev); + tty_port_register_device(&brd->ports[i].port, mxvar_sdriver, + brd->idx + i, &pdev->dev); pci_set_drvdata(pdev, brd); @@ -2695,7 +2723,8 @@ static int __init mxser_module_init(void) brd->idx = m * MXSER_PORTS_PER_BOARD; for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, NULL); + tty_port_register_device(&brd->ports[i].port, + mxvar_sdriver, brd->idx + i, NULL); m++; } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c43b683..1e8e8ce 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -108,7 +108,7 @@ struct gsm_mux_net { */ struct gsm_msg { - struct gsm_msg *next; + struct list_head list; u8 addr; /* DLCI address + flags */ u8 ctrl; /* Control byte + flags */ unsigned int len; /* Length of data block (can be zero) */ @@ -245,8 +245,7 @@ struct gsm_mux { unsigned int tx_bytes; /* TX data outstanding */ #define TX_THRESH_HI 8192 #define TX_THRESH_LO 2048 - struct gsm_msg *tx_head; /* Pending data packets */ - struct gsm_msg *tx_tail; + struct list_head tx_list; /* Pending data packets */ /* Control messages */ struct timer_list t2_timer; /* Retransmit timer for commands */ @@ -489,7 +488,7 @@ static void gsm_print_packet(const char *hdr, int addr, int cr, default: if (!(control & 0x01)) { pr_cont("I N(S)%d N(R)%d", - (control & 0x0E) >> 1, (control & 0xE) >> 5); + (control & 0x0E) >> 1, (control & 0xE0) >> 5); } else switch (control & 0x0F) { case RR: pr_cont("RR(%d)", (control & 0xE0) >> 5); @@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, m->len = len; m->addr = addr; m->ctrl = ctrl; - m->next = NULL; + INIT_LIST_HEAD(&m->list); return m; } @@ -673,22 +672,21 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, * * The tty device has called us to indicate that room has appeared in * the transmit queue. Ram more data into the pipe if we have any + * If we have been flow-stopped by a CMD_FCOFF, then we can only + * send messages on DLCI0 until CMD_FCON * * FIXME: lock against link layer control transmissions */ static void gsm_data_kick(struct gsm_mux *gsm) { - struct gsm_msg *msg = gsm->tx_head; + struct gsm_msg *msg, *nmsg; int len; int skip_sof = 0; - /* FIXME: We need to apply this solely to data messages */ - if (gsm->constipated) - return; - - while (gsm->tx_head != NULL) { - msg = gsm->tx_head; + list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) { + if (gsm->constipated && msg->addr) + continue; if (gsm->encoding != 0) { gsm->txframe[0] = GSM1_SOF; len = gsm_stuff_frame(msg->data, @@ -711,14 +709,13 @@ static void gsm_data_kick(struct gsm_mux *gsm) len - skip_sof) < 0) break; /* FIXME: Can eliminate one SOF in many more cases */ - gsm->tx_head = msg->next; - if (gsm->tx_head == NULL) - gsm->tx_tail = NULL; gsm->tx_bytes -= msg->len; - kfree(msg); /* For a burst of frames skip the extra SOF within the burst */ skip_sof = 1; + + list_del(&msg->list); + kfree(msg); } } @@ -768,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) msg->data = dp; /* Add to the actual output queue */ - if (gsm->tx_tail) - gsm->tx_tail->next = msg; - else - gsm->tx_head = msg; - gsm->tx_tail = msg; + list_add_tail(&msg->list, &gsm->tx_list); gsm->tx_bytes += msg->len; gsm_data_kick(gsm); } @@ -875,7 +868,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, /* dlci->skb is locked by tx_lock */ if (dlci->skb == NULL) { - dlci->skb = skb_dequeue(&dlci->skb_list); + dlci->skb = skb_dequeue_tail(&dlci->skb_list); if (dlci->skb == NULL) return 0; first = 1; @@ -886,7 +879,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, if (len > gsm->mtu) { if (dlci->adaption == 3) { /* Over long frame, bin it */ - kfree_skb(dlci->skb); + dev_kfree_skb_any(dlci->skb); dlci->skb = NULL; return 0; } @@ -899,8 +892,11 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, /* FIXME: need a timer or something to kick this so it can't get stuck with no work outstanding and no buffer free */ - if (msg == NULL) + if (msg == NULL) { + skb_queue_tail(&dlci->skb_list, dlci->skb); + dlci->skb = NULL; return -ENOMEM; + } dp = msg->data; if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ @@ -912,7 +908,7 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, skb_pull(dlci->skb, len); __gsm_data_queue(dlci, msg); if (last) { - kfree_skb(dlci->skb); + dev_kfree_skb_any(dlci->skb); dlci->skb = NULL; } return size; @@ -971,16 +967,22 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm) static void gsm_dlci_data_kick(struct gsm_dlci *dlci) { unsigned long flags; + int sweep; + + if (dlci->constipated) + return; spin_lock_irqsave(&dlci->gsm->tx_lock, flags); /* If we have nothing running then we need to fire up */ + sweep = (dlci->gsm->tx_bytes < TX_THRESH_LO); if (dlci->gsm->tx_bytes == 0) { if (dlci->net) gsm_dlci_data_output_framed(dlci->gsm, dlci); else gsm_dlci_data_output(dlci->gsm, dlci); - } else if (dlci->gsm->tx_bytes < TX_THRESH_LO) - gsm_dlci_data_sweep(dlci->gsm); + } + if (sweep) + gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1027,6 +1029,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, { int mlines = 0; u8 brk = 0; + int fc; /* The modem status command can either contain one octet (v.24 signals) or two octets (v.24 signals + break signals). The length field will @@ -1038,19 +1041,21 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, else { brk = modem & 0x7f; modem = (modem >> 7) & 0x7f; - }; + } /* Flow control/ready to communicate */ - if (modem & MDM_FC) { + fc = (modem & MDM_FC) || !(modem & MDM_RTR); + if (fc && !dlci->constipated) { /* Need to throttle our output on this device */ dlci->constipated = 1; - } - if (modem & MDM_RTC) { - mlines |= TIOCM_DSR | TIOCM_DTR; + } else if (!fc && dlci->constipated) { dlci->constipated = 0; gsm_dlci_data_kick(dlci); } + /* Map modem bits */ + if (modem & MDM_RTC) + mlines |= TIOCM_DSR | TIOCM_DTR; if (modem & MDM_RTR) mlines |= TIOCM_RTS | TIOCM_CTS; if (modem & MDM_IC) @@ -1061,7 +1066,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, /* Carrier drop -> hangup */ if (tty) { if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) - if (!(tty->termios->c_cflag & CLOCAL)) + if (!(tty->termios.c_cflag & CLOCAL)) tty_hangup(tty); if (brk & 0x01) tty_insert_flip_char(tty, 0, TTY_BREAK); @@ -1190,6 +1195,8 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, u8 *data, int clen) { u8 buf[1]; + unsigned long flags; + switch (command) { case CMD_CLD: { struct gsm_dlci *dlci = gsm->dlci[0]; @@ -1206,16 +1213,18 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, gsm_control_reply(gsm, CMD_TEST, data, clen); break; case CMD_FCON: - /* Modem wants us to STFU */ - gsm->constipated = 1; - gsm_control_reply(gsm, CMD_FCON, NULL, 0); - break; - case CMD_FCOFF: /* Modem can accept data again */ gsm->constipated = 0; - gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); + gsm_control_reply(gsm, CMD_FCON, NULL, 0); /* Kick the link in case it is idling */ + spin_lock_irqsave(&gsm->tx_lock, flags); gsm_data_kick(gsm); + spin_unlock_irqrestore(&gsm->tx_lock, flags); + break; + case CMD_FCOFF: + /* Modem wants us to STFU */ + gsm->constipated = 1; + gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); break; case CMD_MSC: /* Out of band modem line change indicator for a DLCI */ @@ -1668,7 +1677,7 @@ static void gsm_dlci_free(struct kref *ref) dlci->gsm->dlci[dlci->addr] = NULL; kfifo_free(dlci->fifo); while ((dlci->skb = skb_dequeue(&dlci->skb_list))) - kfree_skb(dlci->skb); + dev_kfree_skb(dlci->skb); kfree(dlci); } @@ -2007,7 +2016,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) { int i; struct gsm_dlci *dlci = gsm->dlci[0]; - struct gsm_msg *txq; + struct gsm_msg *txq, *ntxq; struct gsm_control *gc; gsm->dead = 1; @@ -2042,11 +2051,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); /* Now wipe the queues */ - for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { - gsm->tx_head = txq->next; + list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); - } - gsm->tx_tail = NULL; + INIT_LIST_HEAD(&gsm->tx_list); } EXPORT_SYMBOL_GPL(gsm_cleanup_mux); @@ -2157,6 +2164,7 @@ struct gsm_mux *gsm_alloc_mux(void) } spin_lock_init(&gsm->lock); kref_init(&gsm->ref); + INIT_LIST_HEAD(&gsm->tx_list); gsm->t1 = T1; gsm->t2 = T2; @@ -2273,7 +2281,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, gsm->error(gsm, *dp, flags); break; default: - WARN_ONCE("%s: unknown flag %d\n", + WARN_ONCE(1, "%s: unknown flag %d\n", tty_name(tty, buf), flags); break; } @@ -2377,12 +2385,12 @@ static void gsmld_write_wakeup(struct tty_struct *tty) /* Queue poll */ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + spin_lock_irqsave(&gsm->tx_lock, flags); gsm_data_kick(gsm); if (gsm->tx_bytes < TX_THRESH_LO) { - spin_lock_irqsave(&gsm->tx_lock, flags); gsm_dlci_data_sweep(gsm); - spin_unlock_irqrestore(&gsm->tx_lock, flags); } + spin_unlock_irqrestore(&gsm->tx_lock, flags); } /** @@ -2868,14 +2876,14 @@ static const struct tty_port_operations gsm_port_ops = { .dtr_rts = gsm_dtr_rts, }; - -static int gsmtty_open(struct tty_struct *tty, struct file *filp) +static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) { struct gsm_mux *gsm; struct gsm_dlci *dlci; - struct tty_port *port; unsigned int line = tty->index; unsigned int mux = line >> 6; + bool alloc = false; + int ret; line = line & 0x3F; @@ -2889,14 +2897,35 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) gsm = gsm_mux[mux]; if (gsm->dead) return -EL2HLT; + /* If DLCI 0 is not yet fully open return an error. This is ok from a locking + perspective as we don't have to worry about this if DLCI0 is lost */ + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + return -EL2NSYNC; dlci = gsm->dlci[line]; - if (dlci == NULL) + if (dlci == NULL) { + alloc = true; dlci = gsm_dlci_alloc(gsm, line); + } if (dlci == NULL) return -ENOMEM; - port = &dlci->port; - port->count++; + ret = tty_port_install(&dlci->port, driver, tty); + if (ret) { + if (alloc) + dlci_put(dlci); + return ret; + } + tty->driver_data = dlci; + + return 0; +} + +static int gsmtty_open(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + struct tty_port *port = &dlci->port; + + port->count++; dlci_get(dlci); dlci_get(dlci->gsm->dlci[0]); mux_get(dlci->gsm); @@ -3043,13 +3072,13 @@ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) the RPN control message. This however rapidly gets nasty as we then have to remap modem signals each way according to whether our virtual cable is null modem etc .. */ - tty_termios_copy_hw(tty->termios, old); + tty_termios_copy_hw(&tty->termios, old); } static void gsmtty_throttle(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) dlci->modem_tx &= ~TIOCM_DTR; dlci->throttled = 1; /* Send an MSC with DTR cleared */ @@ -3059,7 +3088,7 @@ static void gsmtty_throttle(struct tty_struct *tty) static void gsmtty_unthrottle(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) dlci->modem_tx |= TIOCM_DTR; dlci->throttled = 0; /* Send an MSC with DTR set */ @@ -3085,6 +3114,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { + .install = gsmtty_install, .open = gsmtty_open, .close = gsmtty_close, .write = gsmtty_write, diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c314..1e64050 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ee1c268..8c0b7b4 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -92,10 +92,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + int left; int old_left; + /* tty->read_cnt is not read locked ? */ + if (I_PARMRK(tty)) { + /* Multiply read_cnt by 3, since each byte might take up to + * three times as many spaces when PARMRK is set (depending on + * its flags, e.g. parity error). */ + left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1; + } else + left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + /* * If we are doing input canonicalization, and there are no * pending newlines, let characters through without limit, so @@ -1432,6 +1440,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, */ if (tty->receive_room < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + /* FIXME: there is a tiny race here if the receive room check runs + before the other work executes and empties the buffer (upping + the receiving room and unthrottling. We then throttle and get + stuck. This has been observed and traced down by Vincent Pillet/ + We need to address this when we sort out out the rx path locking */ } int is_ignored(int sig) @@ -1460,7 +1474,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) BUG_ON(!tty); if (old) - canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; + canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { memset(&tty->read_flags, 0, sizeof tty->read_flags); tty->canon_head = tty->read_tail; @@ -1728,7 +1742,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, do_it_again: - BUG_ON(!tty->read_buf); + if (WARN_ON(!tty->read_buf)) + return -EAGAIN; c = job_control(tty, file); if (c < 0) @@ -1832,13 +1847,13 @@ do_it_again: if (tty->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ + spin_lock_irqsave(&tty->read_lock, flags); while (nr && tty->read_cnt) { int eol; eol = test_and_clear_bit(tty->read_tail, tty->read_flags); c = tty->read_buf[tty->read_tail]; - spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; @@ -1856,15 +1871,19 @@ do_it_again: if (tty_put_user(tty, c, b++)) { retval = -EFAULT; b--; + spin_lock_irqsave(&tty->read_lock, flags); break; } nr--; } if (eol) { tty_audit_push(tty); + spin_lock_irqsave(&tty->read_lock, flags); break; } + spin_lock_irqsave(&tty->read_lock, flags); } + spin_unlock_irqrestore(&tty->read_lock, flags); if (retval) break; } else { diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index e7592f9..b917c94 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1473,8 +1473,8 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, port->dc = dc; tty_port_init(&port->port); port->port.ops = &noz_tty_port_ops; - tty_dev = tty_register_device(ntty_driver, dc->index_start + i, - &pdev->dev); + tty_dev = tty_port_register_device(&port->port, ntty_driver, + dc->index_start + i, &pdev->dev); if (IS_ERR(tty_dev)) { ret = PTR_ERR(tty_dev); diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 5505ffc..a82b399 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(); + tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(); + tty_lock(tty); } } @@ -231,8 +232,8 @@ out: static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { - tty->termios->c_cflag &= ~(CSIZE | PARENB); - tty->termios->c_cflag |= (CS8 | CREAD); + tty->termios.c_cflag &= ~(CSIZE | PARENB); + tty->termios.c_cflag |= (CS8 | CREAD); } /** @@ -282,60 +283,110 @@ done: return 0; } -/* Traditional BSD devices */ -#ifdef CONFIG_LEGACY_PTYS - -static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +/** + * pty_common_install - set up the pty pair + * @driver: the pty driver + * @tty: the tty being instantiated + * @bool: legacy, true if this is BSD style + * + * Perform the initial set up for the tty/pty pair. Called from the + * tty layer when the port is first opened. + * + * Locking: the caller must hold the tty_mutex + */ +static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, + bool legacy) { struct tty_struct *o_tty; + struct tty_port *ports[2]; int idx = tty->index; - int retval; + int retval = -ENOMEM; o_tty = alloc_tty_struct(); if (!o_tty) - return -ENOMEM; + goto err; + ports[0] = kmalloc(sizeof **ports, GFP_KERNEL); + ports[1] = kmalloc(sizeof **ports, GFP_KERNEL); + if (!ports[0] || !ports[1]) + goto err_free_tty; if (!try_module_get(driver->other->owner)) { /* This cannot in fact currently happen */ - retval = -ENOMEM; goto err_free_tty; } initialize_tty_struct(o_tty, driver->other, idx); - /* We always use new tty termios data so we can do this - the easy way .. */ - retval = tty_init_termios(tty); - if (retval) - goto err_deinit_tty; - - retval = tty_init_termios(o_tty); - if (retval) - goto err_free_termios; + if (legacy) { + /* We always use new tty termios data so we can do this + the easy way .. */ + retval = tty_init_termios(tty); + if (retval) + goto err_deinit_tty; + + retval = tty_init_termios(o_tty); + if (retval) + goto err_free_termios; + + driver->other->ttys[idx] = o_tty; + driver->ttys[idx] = tty; + } else { + memset(&tty->termios_locked, 0, sizeof(tty->termios_locked)); + tty->termios = driver->init_termios; + memset(&o_tty->termios_locked, 0, sizeof(tty->termios_locked)); + o_tty->termios = driver->other->init_termios; + } /* * Everything allocated ... set up the o_tty structure. */ - driver->other->ttys[idx] = o_tty; tty_driver_kref_get(driver->other); if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; + tty_port_init(ports[0]); + tty_port_init(ports[1]); + o_tty->port = ports[0]; + tty->port = ports[1]; tty_driver_kref_get(driver); tty->count++; - driver->ttys[idx] = tty; return 0; err_free_termios: - tty_free_termios(tty); + if (legacy) + tty_free_termios(tty); err_deinit_tty: deinitialize_tty_struct(o_tty); module_put(o_tty->driver->owner); err_free_tty: + kfree(ports[0]); + kfree(ports[1]); free_tty_struct(o_tty); +err: return retval; } +static void pty_cleanup(struct tty_struct *tty) +{ + kfree(tty->port); +} + +/* Traditional BSD devices */ +#ifdef CONFIG_LEGACY_PTYS + +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + return pty_common_install(driver, tty, true); +} + +static void pty_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + struct tty_struct *pair = tty->link; + driver->ttys[tty->index] = NULL; + if (pair) + pair->driver->ttys[pair->index] = NULL; +} + static int pty_bsd_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -366,7 +417,9 @@ static const struct tty_operations master_pty_ops_bsd = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_bsd_ioctl, - .resize = pty_resize + .cleanup = pty_cleanup, + .resize = pty_resize, + .remove = pty_remove }; static const struct tty_operations slave_pty_ops_bsd = { @@ -379,7 +432,9 @@ static const struct tty_operations slave_pty_ops_bsd = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .resize = pty_resize + .cleanup = pty_cleanup, + .resize = pty_resize, + .remove = pty_remove }; static void __init legacy_pty_init(void) @@ -389,12 +444,18 @@ static void __init legacy_pty_init(void) if (legacy_count <= 0) return; - pty_driver = alloc_tty_driver(legacy_count); - if (!pty_driver) + pty_driver = tty_alloc_driver(legacy_count, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_ALLOC); + if (IS_ERR(pty_driver)) panic("Couldn't allocate pty driver"); - pty_slave_driver = alloc_tty_driver(legacy_count); - if (!pty_slave_driver) + pty_slave_driver = tty_alloc_driver(legacy_count, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_ALLOC); + if (IS_ERR(pty_slave_driver)) panic("Couldn't allocate pty slave driver"); pty_driver->driver_name = "pty_master"; @@ -410,7 +471,6 @@ static void __init legacy_pty_init(void) pty_driver->init_termios.c_lflag = 0; pty_driver->init_termios.c_ispeed = 38400; pty_driver->init_termios.c_ospeed = 38400; - pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_driver->other = pty_slave_driver; tty_set_operations(pty_driver, &master_pty_ops_bsd); @@ -424,8 +484,6 @@ static void __init legacy_pty_init(void) pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; pty_slave_driver->init_termios.c_ispeed = 38400; pty_slave_driver->init_termios.c_ospeed = 38400; - pty_slave_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW; pty_slave_driver->other = pty_driver; tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd); @@ -497,78 +555,22 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, return tty; } -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - tty_driver_remove_tty(tty->driver, tty); - /* We have our own method as we don't use the tty index */ - kfree(tty->termios); -} - /* We have no need to install and remove our tty objects as devpts does all the work for us */ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) { - struct tty_struct *o_tty; - int idx = tty->index; - - o_tty = alloc_tty_struct(); - if (!o_tty) - return -ENOMEM; - if (!try_module_get(driver->other->owner)) { - /* This cannot in fact currently happen */ - goto err_free_tty; - } - initialize_tty_struct(o_tty, driver->other, idx); - - tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (tty->termios == NULL) - goto err_free_mem; - *tty->termios = driver->init_termios; - tty->termios_locked = tty->termios + 1; - - o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (o_tty->termios == NULL) - goto err_free_mem; - *o_tty->termios = driver->other->init_termios; - o_tty->termios_locked = o_tty->termios + 1; - - tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; - /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - tty_driver_kref_get(driver); - tty->count++; - return 0; -err_free_mem: - deinitialize_tty_struct(o_tty); - kfree(o_tty->termios); - kfree(tty->termios); - module_put(o_tty->driver->owner); -err_free_tty: - free_tty_struct(o_tty); - return -ENOMEM; -} - -static void ptm_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) -{ + return pty_common_install(driver, tty, false); } -static void pts_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { } static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, - .remove = ptm_unix98_remove, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -578,14 +580,14 @@ static const struct tty_operations ptm_unix98_ops = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, - .shutdown = pty_unix98_shutdown, - .resize = pty_resize + .resize = pty_resize, + .cleanup = pty_cleanup }; static const struct tty_operations pty_unix98_ops = { .lookup = pts_unix98_lookup, .install = pty_unix98_install, - .remove = pts_unix98_remove, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -594,7 +596,7 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .shutdown = pty_unix98_shutdown + .cleanup = pty_cleanup, }; /** @@ -622,26 +624,28 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); if (index < 0) { retval = index; + mutex_unlock(&devpts_mutex); goto err_file; } + mutex_unlock(&devpts_mutex); + mutex_lock(&tty_mutex); - mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&devpts_mutex); - tty_lock(); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -654,15 +658,15 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: tty_free_file(filp); return retval; @@ -672,11 +676,21 @@ static struct file_operations ptmx_fops; static void __init unix98_pty_init(void) { - ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); - if (!ptm_driver) + ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_DEVPTS_MEM | + TTY_DRIVER_DYNAMIC_ALLOC); + if (IS_ERR(ptm_driver)) panic("Couldn't allocate Unix98 ptm driver"); - pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); - if (!pts_driver) + pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_DEVPTS_MEM | + TTY_DRIVER_DYNAMIC_ALLOC); + if (IS_ERR(pts_driver)) panic("Couldn't allocate Unix98 pts driver"); ptm_driver->driver_name = "pty_master"; @@ -692,8 +706,6 @@ static void __init unix98_pty_init(void) ptm_driver->init_termios.c_lflag = 0; ptm_driver->init_termios.c_ispeed = 38400; ptm_driver->init_termios.c_ospeed = 38400; - ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; tty_set_operations(ptm_driver, &ptm_unix98_ops); @@ -707,8 +719,6 @@ static void __init unix98_pty_init(void) pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; pts_driver->init_termios.c_ispeed = 38400; pts_driver->init_termios.c_ospeed = 38400; - pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; pts_driver->other = ptm_driver; tty_set_operations(pts_driver, &pty_unix98_ops); diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 777d5f9..9700d34 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -704,8 +704,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) spin_lock_init(&info->slock); mutex_init(&info->write_mtx); rp_table[line] = info; - tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev : - NULL); + tty_port_register_device(&info->port, rocket_driver, line, + pci_dev ? &pci_dev->dev : NULL); } /* @@ -720,7 +720,7 @@ static void configure_r_port(struct tty_struct *tty, struct r_port *info, unsigned rocketMode; int bits, baud, divisor; CHANNEL_t *cp; - struct ktermios *t = tty->termios; + struct ktermios *t = &tty->termios; cp = &info->channel; cflag = t->c_cflag; @@ -978,7 +978,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp) tty->alt_speed = 460800; configure_r_port(tty, info, NULL); - if (tty->termios->c_cflag & CBAUD) { + if (tty->termios.c_cflag & CBAUD) { sSetDTR(cp); sSetRTS(cp); } @@ -1089,35 +1089,35 @@ static void rp_set_termios(struct tty_struct *tty, if (rocket_paranoia_check(info, "rp_set_termios")) return; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* * This driver doesn't support CS5 or CS6 */ if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) - tty->termios->c_cflag = + tty->termios.c_cflag = ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); /* Or CMSPAR */ - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; configure_r_port(tty, info, old_termios); cp = &info->channel; /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { + if ((old_termios->c_cflag & CBAUD) && !(tty->termios.c_cflag & CBAUD)) { sClrDTR(cp); sClrRTS(cp); } /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { - if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) + if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) { + if (!tty->hw_stopped || !(tty->termios.c_cflag & CRTSCTS)) sSetRTS(cp); sSetDTR(cp); } - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rp_start(tty); } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 3ed20e4..66c38a3 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -515,7 +515,7 @@ static void change_speed(struct m68k_serial *info, struct tty_struct *tty) unsigned cflag; int i; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; if (!(port = info->port)) return; @@ -617,7 +617,7 @@ static void rs_set_ldisc(struct tty_struct *tty) if (serial_paranoia_check(info, tty->name, "rs_set_ldisc")) return; - info->is_cons = (tty->termios->c_line == N_TTY); + info->is_cons = (tty->termios.c_line == N_TTY); printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off"); } @@ -985,7 +985,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(info, tty); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); } @@ -1070,7 +1070,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; + tty->termios.c_line = N_TTY; if (tty->ldisc.open) (tty->ldisc.open)(tty); } @@ -1189,12 +1189,6 @@ rs68328_init(void) serial_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(serial_driver, &rs_ops); - if (tty_register_driver(serial_driver)) { - put_tty_driver(serial_driver); - printk(KERN_ERR "Couldn't register serial driver\n"); - return -ENOMEM; - } - local_irq_save(flags); for(i=0;i<NR_PORTS;i++) { @@ -1224,8 +1218,17 @@ rs68328_init(void) 0, "M68328_UART", info)) panic("Unable to attach 68328 serial interrupt\n"); + + tty_port_link_device(&info->tport, serial_driver, i); } local_irq_restore(flags); + + if (tty_register_driver(serial_driver)) { + put_tty_driver(serial_driver); + printk(KERN_ERR "Couldn't register serial driver\n"); + return -ENOMEM; + } + return 0; } diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 8123f78..3ba4234 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -290,6 +290,9 @@ static const struct serial8250_config uart_config[] = { UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, .flags = UART_CAP_FIFO, }, + [PORT_8250_CIR] = { + .name = "CIR port" + } }; /* Uart divisor latch read */ @@ -1037,6 +1040,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) unsigned char save_lcr, save_mcr; struct uart_port *port = &up->port; unsigned long flags; + unsigned int old_capabilities; if (!port->iobase && !port->mapbase && !port->membase) return; @@ -1087,6 +1091,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) /* * We failed; there's nothing here */ + spin_unlock_irqrestore(&port->lock, flags); DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", scratch2, scratch3); goto out; @@ -1110,6 +1115,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) status1 = serial_in(up, UART_MSR) & 0xF0; serial_out(up, UART_MCR, save_mcr); if (status1 != 0x90) { + spin_unlock_irqrestore(&port->lock, flags); DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); goto out; @@ -1132,8 +1138,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(up, UART_IIR) >> 6; - DEBUG_AUTOCONF("iir=%d ", scratch); - switch (scratch) { case 0: autoconfig_8250(up); @@ -1167,19 +1171,13 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) serial_out(up, UART_LCR, save_lcr); - if (up->capabilities != uart_config[port->type].flags) { - printk(KERN_WARNING - "ttyS%d: detected caps %08x should be %08x\n", - serial_index(port), up->capabilities, - uart_config[port->type].flags); - } - port->fifosize = uart_config[up->port.type].fifo_size; + old_capabilities = up->capabilities; up->capabilities = uart_config[port->type].flags; up->tx_loadsz = uart_config[port->type].tx_loadsz; if (port->type == PORT_UNKNOWN) - goto out; + goto out_lock; /* * Reset the UART. @@ -1196,8 +1194,16 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) else serial_out(up, UART_IER, 0); - out: +out_lock: spin_unlock_irqrestore(&port->lock, flags); + if (up->capabilities != old_capabilities) { + printk(KERN_WARNING + "ttyS%d: detected caps %08x should be %08x\n", + serial_index(port), old_capabilities, + up->capabilities); + } +out: + DEBUG_AUTOCONF("iir=%d ", scratch); DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name); } @@ -1897,6 +1903,9 @@ static int serial8250_startup(struct uart_port *port) unsigned char lsr, iir; int retval; + if (port->type == PORT_8250_CIR) + return -ENODEV; + port->fifosize = uart_config[up->port.type].fifo_size; up->tx_loadsz = uart_config[up->port.type].tx_loadsz; up->capabilities = uart_config[up->port.type].flags; @@ -2202,6 +2211,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; + int fifo_bug = 0; switch (termios->c_cflag & CSIZE) { case CS5: @@ -2221,8 +2231,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & CSTOPB) cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) + if (termios->c_cflag & PARENB) { cval |= UART_LCR_PARITY; + if (up->bugs & UART_BUG_PARITY) + fifo_bug = 1; + } if (!(termios->c_cflag & PARODD)) cval |= UART_LCR_EPAR; #ifdef CMSPAR @@ -2246,7 +2259,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { fcr = uart_config[port->type].fcr; - if (baud < 2400) { + if (baud < 2400 || fifo_bug) { fcr &= ~UART_FCR_TRIGGER_MASK; fcr |= UART_FCR_TRIGGER_1; } @@ -2336,7 +2349,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } -#ifdef CONFIG_ARCH_OMAP +#ifdef CONFIG_ARCH_OMAP1 /* Workaround to enable 115200 baud on OMAP1510 internal ports */ if (cpu_is_omap1510() && is_omap_port(up)) { if (baud == 115200) { @@ -2426,7 +2439,7 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) return 0x1000; -#ifdef CONFIG_ARCH_OMAP +#ifdef CONFIG_ARCH_OMAP1 if (is_omap_port(pt)) return 0x16 << pt->port.regshift; #endif @@ -2550,7 +2563,10 @@ static int serial8250_request_port(struct uart_port *port) { struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); - int ret = 0; + int ret; + + if (port->type == PORT_8250_CIR) + return -ENODEV; ret = serial8250_request_std_resource(up); if (ret == 0 && port->type == PORT_RSA) { @@ -2569,6 +2585,9 @@ static void serial8250_config_port(struct uart_port *port, int flags) int probeflags = PROBE_ANY; int ret; + if (port->type == PORT_8250_CIR) + return; + /* * Find the region that we can probe for. This in turn * tells us whether we can probe for the type of port. @@ -2668,6 +2687,9 @@ static void __init serial8250_isa_init_ports(void) return; first = 0; + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; struct uart_port *port = &up->port; @@ -2677,6 +2699,7 @@ static void __init serial8250_isa_init_ports(void) init_timer(&up->timer); up->timer.function = serial8250_timeout; + up->cur_iotype = 0xFF; /* * ALPHA_KLUDGE_MCR needs to be killed. @@ -2728,13 +2751,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; - up->cur_iotype = 0xFF; - } - - serial8250_isa_init_ports(); - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; + if (up->port.dev) + continue; up->port.dev = dev; @@ -2859,9 +2878,6 @@ static struct console serial8250_console = { static int __init serial8250_console_init(void) { - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - serial8250_isa_init_ports(); register_console(&serial8250_console); return 0; @@ -2979,36 +2995,36 @@ void serial8250_resume_port(int line) static int __devinit serial8250_probe(struct platform_device *dev) { struct plat_serial8250_port *p = dev->dev.platform_data; - struct uart_port port; + struct uart_8250_port uart; int ret, i, irqflag = 0; - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); if (share_irqs) irqflag = IRQF_SHARED; for (i = 0; p && p->flags != 0; p++, i++) { - port.iobase = p->iobase; - port.membase = p->membase; - port.irq = p->irq; - port.irqflags = p->irqflags; - port.uartclk = p->uartclk; - port.regshift = p->regshift; - port.iotype = p->iotype; - port.flags = p->flags; - port.mapbase = p->mapbase; - port.hub6 = p->hub6; - port.private_data = p->private_data; - port.type = p->type; - port.serial_in = p->serial_in; - port.serial_out = p->serial_out; - port.handle_irq = p->handle_irq; - port.handle_break = p->handle_break; - port.set_termios = p->set_termios; - port.pm = p->pm; - port.dev = &dev->dev; - port.irqflags |= irqflag; - ret = serial8250_register_port(&port); + uart.port.iobase = p->iobase; + uart.port.membase = p->membase; + uart.port.irq = p->irq; + uart.port.irqflags = p->irqflags; + uart.port.uartclk = p->uartclk; + uart.port.regshift = p->regshift; + uart.port.iotype = p->iotype; + uart.port.flags = p->flags; + uart.port.mapbase = p->mapbase; + uart.port.hub6 = p->hub6; + uart.port.private_data = p->private_data; + uart.port.type = p->type; + uart.port.serial_in = p->serial_in; + uart.port.serial_out = p->serial_out; + uart.port.handle_irq = p->handle_irq; + uart.port.handle_break = p->handle_break; + uart.port.set_termios = p->set_termios; + uart.port.pm = p->pm; + uart.port.dev = &dev->dev; + uart.port.irqflags |= irqflag; + ret = serial8250_register_8250_port(&uart); if (ret < 0) { dev_err(&dev->dev, "unable to register port at index %d " "(IO%lx MEM%llx IRQ%d): %d\n", i, @@ -3081,7 +3097,7 @@ static struct platform_driver serial8250_isa_driver = { static struct platform_device *serial8250_isa_devs; /* - * serial8250_register_port and serial8250_unregister_port allows for + * serial8250_register_8250_port and serial8250_unregister_port allows for * 16x50 serial ports to be configured at run-time, to support PCMCIA * modems and PCI multiport cards. */ @@ -3143,8 +3159,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up) mutex_lock(&serial_mutex); uart = serial8250_find_match_or_unused(&up->port); - if (uart) { - uart_remove_one_port(&serial8250_reg, &uart->port); + if (uart && uart->port.type != PORT_8250_CIR) { + if (uart->port.dev) + uart_remove_one_port(&serial8250_reg, &uart->port); uart->port.iobase = up->port.iobase; uart->port.membase = up->port.membase; @@ -3155,6 +3172,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.regshift = up->port.regshift; uart->port.iotype = up->port.iotype; uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF; + uart->bugs = up->bugs; uart->port.mapbase = up->port.mapbase; uart->port.private_data = up->port.private_data; if (up->port.dev) @@ -3198,29 +3216,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up) EXPORT_SYMBOL(serial8250_register_8250_port); /** - * serial8250_register_port - register a serial port - * @port: serial port template - * - * Configure the serial port specified by the request. If the - * port exists and is in use, it is hung up and unregistered - * first. - * - * The port is then probed and if necessary the IRQ is autodetected - * If this fails an error is returned. - * - * On success the port is ready to use and the line number is returned. - */ -int serial8250_register_port(struct uart_port *port) -{ - struct uart_8250_port up; - - memset(&up, 0, sizeof(up)); - memcpy(&up.port, port, sizeof(*port)); - return serial8250_register_8250_port(&up); -} -EXPORT_SYMBOL(serial8250_register_port); - -/** * serial8250_unregister_port - remove a 16x50 serial port at runtime * @line: serial line number * @@ -3250,8 +3245,7 @@ static int __init serial8250_init(void) { int ret; - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; + serial8250_isa_init_ports(); printk(KERN_INFO "Serial: 8250/16550 driver, " "%d ports, IRQ sharing %sabled\n", nr_uarts, @@ -3266,11 +3260,15 @@ static int __init serial8250_init(void) if (ret) goto out; + ret = serial8250_pnp_init(); + if (ret) + goto unreg_uart_drv; + serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); if (!serial8250_isa_devs) { ret = -ENOMEM; - goto unreg_uart_drv; + goto unreg_pnp; } ret = platform_device_add(serial8250_isa_devs); @@ -3286,6 +3284,8 @@ static int __init serial8250_init(void) platform_device_del(serial8250_isa_devs); put_dev: platform_device_put(serial8250_isa_devs); +unreg_pnp: + serial8250_pnp_exit(); unreg_uart_drv: #ifdef CONFIG_SPARC sunserial_unregister_minors(&serial8250_reg, UART_NR); @@ -3310,6 +3310,8 @@ static void __exit serial8250_exit(void) platform_driver_unregister(&serial8250_isa_driver); platform_device_unregister(isa_dev); + serial8250_pnp_exit(); + #ifdef CONFIG_SPARC sunserial_unregister_minors(&serial8250_reg, UART_NR); #else diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index f9719d1..5a76f9c 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -13,36 +13,6 @@ #include <linux/serial_8250.h> -struct uart_8250_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short capabilities; /* port capabilities */ - unsigned short bugs; /* port bugs */ - unsigned int tx_loadsz; /* transmit fifo load size */ - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char cur_iotype; /* Running I/O type */ - - /* - * Some bits in registers are cleared on a read, so they must - * be saved whenever the register is read but the bits will not - * be immediately processed. - */ -#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS - unsigned char lsr_saved_flags; -#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA - unsigned char msr_saved_flags; - - /* 8250 specific callbacks */ - int (*dl_read)(struct uart_8250_port *); - void (*dl_write)(struct uart_8250_port *, int); -}; - struct old_serial_port { unsigned int uart; unsigned int baud_base; @@ -56,9 +26,6 @@ struct old_serial_port { unsigned long irqflags; }; -/* - * This replaces serial_uart_config in include/linux/serial.h - */ struct serial8250_config { const char *name; unsigned short fifo_size; @@ -78,6 +45,7 @@ struct serial8250_config { #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ #define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ #define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ +#define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */ #define PROBE_RSA (1 << 0) #define PROBE_ANY (~0) @@ -129,3 +97,12 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) #else #define ALPHA_KLUDGE_MCR 0 #endif + +#ifdef CONFIG_SERIAL_8250_PNP +int serial8250_pnp_init(void); +void serial8250_pnp_exit(void); +#else +static inline int serial8250_pnp_init(void) { return 0; } +static inline void serial8250_pnp_exit(void) { } +#endif + diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index b0ce8c5..8574983 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -43,7 +43,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; struct serial_card_type *type = id->data; - struct uart_port port; + struct uart_8250_port uart; unsigned long bus_addr; unsigned int i; @@ -62,19 +62,19 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) ecard_set_drvdata(ec, info); - memset(&port, 0, sizeof(struct uart_port)); - port.irq = ec->irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - port.uartclk = type->uartclk; - port.iotype = UPIO_MEM; - port.regshift = 2; - port.dev = &ec->dev; + memset(&uart, 0, sizeof(struct uart_8250_port)); + uart.port.irq = ec->irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = type->uartclk; + uart.port.iotype = UPIO_MEM; + uart.port.regshift = 2; + uart.port.dev = &ec->dev; for (i = 0; i < info->num_ports; i ++) { - port.membase = info->vaddr + type->offset[i]; - port.mapbase = bus_addr + type->offset[i]; + uart.port.membase = info->vaddr + type->offset[i]; + uart.port.mapbase = bus_addr + type->offset[i]; - info->ports[i] = serial8250_register_port(&port); + info->ports[i] = serial8250_register_8250_port(&uart); } return 0; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f574eef..c3b2ec0 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -89,7 +89,7 @@ static int dw8250_handle_irq(struct uart_port *p) static int __devinit dw8250_probe(struct platform_device *pdev) { - struct uart_port port = {}; + struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct device_node *np = pdev->dev.of_node; @@ -104,28 +104,28 @@ static int __devinit dw8250_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - port.private_data = data; - - spin_lock_init(&port.lock); - port.mapbase = regs->start; - port.irq = irq->start; - port.handle_irq = dw8250_handle_irq; - port.type = PORT_8250; - port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | + uart.port.private_data = data; + + spin_lock_init(&uart.port.lock); + uart.port.mapbase = regs->start; + uart.port.irq = irq->start; + uart.port.handle_irq = dw8250_handle_irq; + uart.port.type = PORT_8250; + uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; - port.dev = &pdev->dev; + uart.port.dev = &pdev->dev; - port.iotype = UPIO_MEM; - port.serial_in = dw8250_serial_in; - port.serial_out = dw8250_serial_out; + uart.port.iotype = UPIO_MEM; + uart.port.serial_in = dw8250_serial_in; + uart.port.serial_out = dw8250_serial_out; if (!of_property_read_u32(np, "reg-io-width", &val)) { switch (val) { case 1: break; case 4: - port.iotype = UPIO_MEM32; - port.serial_in = dw8250_serial_in32; - port.serial_out = dw8250_serial_out32; + uart.port.iotype = UPIO_MEM32; + uart.port.serial_in = dw8250_serial_in32; + uart.port.serial_out = dw8250_serial_out32; break; default: dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n", @@ -135,15 +135,15 @@ static int __devinit dw8250_probe(struct platform_device *pdev) } if (!of_property_read_u32(np, "reg-shift", &val)) - port.regshift = val; + uart.port.regshift = val; if (of_property_read_u32(np, "clock-frequency", &val)) { dev_err(&pdev->dev, "no clock-frequency property set\n"); return -EINVAL; } - port.uartclk = val; + uart.port.uartclk = val; - data->line = serial8250_register_port(&port); + data->line = serial8250_register_8250_port(&uart); if (data->line < 0) return data->line; diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index d8c0ffb..097dff9 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -26,7 +26,7 @@ static int __init serial_init_chip(struct parisc_device *dev) { - struct uart_port port; + struct uart_8250_port uart; unsigned long address; int err; @@ -48,21 +48,21 @@ static int __init serial_init_chip(struct parisc_device *dev) if (dev->id.sversion != 0x8d) address += 0x800; - memset(&port, 0, sizeof(port)); - port.iotype = UPIO_MEM; + memset(&uart, 0, sizeof(uart)); + uart.port.iotype = UPIO_MEM; /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - port.uartclk = 7272727; - port.mapbase = address; - port.membase = ioremap_nocache(address, 16); - port.irq = dev->irq; - port.flags = UPF_BOOT_AUTOCONF; - port.dev = &dev->dev; - - err = serial8250_register_port(&port); + uart.port.uartclk = 7272727; + uart.port.mapbase = address; + uart.port.membase = ioremap_nocache(address, 16); + uart.port.irq = dev->irq; + uart.port.flags = UPF_BOOT_AUTOCONF; + uart.port.dev = &dev->dev; + + err = serial8250_register_8250_port(&uart); if (err < 0) { printk(KERN_WARNING - "serial8250_register_port returned error %d\n", err); - iounmap(port.membase); + "serial8250_register_8250_port returned error %d\n", err); + iounmap(uart.port.membase); return err; } diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index c13438c9..8f1dd2c 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -171,7 +171,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d, return 0; } #endif - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); /* Memory mapped I/O */ port.iotype = UPIO_MEM; @@ -182,7 +182,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d, port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); port.regshift = 1; port.dev = &d->dev; - line = serial8250_register_port(&port); + line = serial8250_register_8250_port(&uart); if (line < 0) { printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" @@ -210,7 +210,7 @@ static int __init hp300_8250_init(void) #ifdef CONFIG_HPAPCI int line; unsigned long base; - struct uart_port uport; + struct uart_8250_port uart; struct hp300_port *port; int i; #endif @@ -248,26 +248,26 @@ static int __init hp300_8250_init(void) if (!port) return -ENOMEM; - memset(&uport, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); /* Memory mapped I/O */ - uport.iotype = UPIO_MEM; - uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ | UPF_BOOT_AUTOCONF; /* XXX - no interrupt support yet */ - uport.irq = 0; - uport.uartclk = HPAPCI_BAUD_BASE * 16; - uport.mapbase = base; - uport.membase = (char *)(base + DIO_VIRADDRBASE); - uport.regshift = 2; + uart.port.irq = 0; + uart.port.uartclk = HPAPCI_BAUD_BASE * 16; + uart.port.mapbase = base; + uart.port.membase = (char *)(base + DIO_VIRADDRBASE); + uart.port.regshift = 2; - line = serial8250_register_port(&uport); + line = serial8250_register_8250_port(&uart); if (line < 0) { printk(KERN_NOTICE "8250_hp300: register_serial() APCI" - " %d irq %d failed\n", i, uport.irq); + " %d irq %d failed\n", i, uart.port.irq); kfree(port); continue; } diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 452278e..17b7d26 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -44,7 +44,7 @@ struct pci_serial_quirk { int (*init)(struct pci_dev *dev); int (*setup)(struct serial_private *, const struct pciserial_board *, - struct uart_port *, int); + struct uart_8250_port *, int); void (*exit)(struct pci_dev *dev); }; @@ -59,7 +59,7 @@ struct serial_private { }; static int pci_default_setup(struct serial_private*, - const struct pciserial_board*, struct uart_port*, int); + const struct pciserial_board*, struct uart_8250_port *, int); static void moan_device(const char *str, struct pci_dev *dev) { @@ -74,7 +74,7 @@ static void moan_device(const char *str, struct pci_dev *dev) } static int -setup_port(struct serial_private *priv, struct uart_port *port, +setup_port(struct serial_private *priv, struct uart_8250_port *port, int bar, int offset, int regshift) { struct pci_dev *dev = priv->dev; @@ -93,17 +93,17 @@ setup_port(struct serial_private *priv, struct uart_port *port, if (!priv->remapped_bar[bar]) return -ENOMEM; - port->iotype = UPIO_MEM; - port->iobase = 0; - port->mapbase = base + offset; - port->membase = priv->remapped_bar[bar] + offset; - port->regshift = regshift; + port->port.iotype = UPIO_MEM; + port->port.iobase = 0; + port->port.mapbase = base + offset; + port->port.membase = priv->remapped_bar[bar] + offset; + port->port.regshift = regshift; } else { - port->iotype = UPIO_PORT; - port->iobase = base + offset; - port->mapbase = 0; - port->membase = NULL; - port->regshift = 0; + port->port.iotype = UPIO_PORT; + port->port.iobase = base + offset; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; } return 0; } @@ -113,7 +113,7 @@ setup_port(struct serial_private *priv, struct uart_port *port, */ static int addidata_apci7800_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; bar = FL_GET_BASE(board->flags); @@ -140,7 +140,7 @@ static int addidata_apci7800_setup(struct serial_private *priv, */ static int afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -195,7 +195,7 @@ static int pci_hp_diva_init(struct pci_dev *dev) static int pci_hp_diva_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int offset = board->first_offset; unsigned int bar = FL_GET_BASE(board->flags); @@ -370,7 +370,7 @@ static void __devexit pci_ni8430_exit(struct pci_dev *dev) /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ static int sbs_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -525,7 +525,7 @@ static int pci_siig_init(struct pci_dev *dev) static int pci_siig_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; @@ -619,7 +619,7 @@ static int pci_timedia_init(struct pci_dev *dev) static int pci_timedia_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; @@ -653,7 +653,7 @@ pci_timedia_setup(struct serial_private *priv, static int titan_400l_800l_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -754,7 +754,7 @@ static int pci_ni8430_init(struct pci_dev *dev) static int pci_ni8430_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { void __iomem *p; unsigned long base, len; @@ -781,7 +781,7 @@ pci_ni8430_setup(struct serial_private *priv, static int pci_netmos_9900_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar; @@ -1032,10 +1032,17 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) return number_uarts; } -static int -pci_default_setup(struct serial_private *priv, +static int pci_asix_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + port->bugs |= UART_BUG_PARITY; + return pci_default_setup(priv, board, port, idx); +} + +static int pci_default_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset, maxnr; @@ -1057,15 +1064,15 @@ pci_default_setup(struct serial_private *priv, static int ce4100_serial_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { int ret; ret = setup_port(priv, port, 0, 0, board->reg_shift); - port->iotype = UPIO_MEM32; - port->type = PORT_XSCALE; - port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); - port->regshift = 2; + port->port.iotype = UPIO_MEM32; + port->port.type = PORT_XSCALE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->port.regshift = 2; return ret; } @@ -1073,16 +1080,16 @@ ce4100_serial_setup(struct serial_private *priv, static int pci_omegapci_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { return setup_port(priv, port, 2, idx * 8, 0); } static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_NO_TXEN_TEST; + port->port.flags |= UPF_NO_TXEN_TEST; printk(KERN_DEBUG "serial8250: skipping TxEn test for device " "[%04x:%04x] subsystem [%04x:%04x]\n", priv->dev->vendor, @@ -1131,11 +1138,11 @@ static unsigned int kt_serial_in(struct uart_port *p, int offset) static int kt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_BUG_THRE; - port->serial_in = kt_serial_in; - port->handle_break = kt_handle_break; + port->port.flags |= UPF_BUG_THRE; + port->port.serial_in = kt_serial_in; + port->port.handle_break = kt_handle_break; return skip_tx_en_setup(priv, board, port, idx); } @@ -1151,9 +1158,19 @@ static int pci_eg20t_init(struct pci_dev *dev) static int pci_xr17c154_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_EXAR_EFR; + port->port.flags |= UPF_EXAR_EFR; + return pci_default_setup(priv, board, port, idx); +} + +static int +pci_wch_ch353_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_16550A; return pci_default_setup(priv, board, port, idx); } @@ -1164,6 +1181,8 @@ pci_xr17c154_setup(struct serial_private *priv, #define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 #define PCI_SUBDEVICE_ID_POCTAL232 0x0308 #define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_00 0x2500 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530 #define PCI_VENDOR_ID_ADVANTECH 0x13fe #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 #define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 @@ -1187,6 +1206,13 @@ pci_xr17c154_setup(struct serial_private *priv, #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d +#define PCI_VENDOR_ID_WCH 0x4348 +#define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 +#define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 +#define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 +#define PCI_VENDOR_ID_AGESTAR 0x5372 +#define PCI_DEVICE_ID_AGESTAR_9375 0x6872 +#define PCI_VENDOR_ID_ASIX 0x9710 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1726,7 +1752,41 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_omegapci_setup, - }, + }, + /* WCH CH353 2S1P card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_2S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH353 4S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH353 2S1PF card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_2S1PF, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* + * ASIX devices with FIFO bug + */ + { + .vendor = PCI_VENDOR_ID_ASIX, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_asix_setup, + }, /* * Default "match everything" terminator entry */ @@ -1887,7 +1947,6 @@ enum pci_board_num_t { pbn_panacom, pbn_panacom2, pbn_panacom4, - pbn_exsys_4055, pbn_plx_romulus, pbn_oxsemi, pbn_oxsemi_1_4000000, @@ -2393,13 +2452,6 @@ static struct pciserial_board pci_boards[] __devinitdata = { .reg_shift = 7, }, - [pbn_exsys_4055] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - /* I think this entry is broken - the first_offset looks wrong --rmk */ [pbn_plx_romulus] = { .flags = FL_BASE2, @@ -2624,10 +2676,14 @@ static struct pciserial_board pci_boards[] __devinitdata = { }, }; -static const struct pci_device_id softmodem_blacklist[] = { +static const struct pci_device_id blacklist[] = { + /* softmodems */ { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ + + /* multi-io cards handled by parport_serial */ + { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ }; /* @@ -2638,7 +2694,7 @@ static const struct pci_device_id softmodem_blacklist[] = { static int __devinit serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) { - const struct pci_device_id *blacklist; + const struct pci_device_id *bldev; int num_iomem, num_port, first_port = -1, i; /* @@ -2655,13 +2711,13 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) /* * Do not access blacklisted devices that are known not to - * feature serial ports. + * feature serial ports or are handled by other modules. */ - for (blacklist = softmodem_blacklist; - blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist); - blacklist++) { - if (dev->vendor == blacklist->vendor && - dev->device == blacklist->device) + for (bldev = blacklist; + bldev < blacklist + ARRAY_SIZE(blacklist); + bldev++) { + if (dev->vendor == bldev->vendor && + dev->device == bldev->device) return -ENODEV; } @@ -2728,7 +2784,7 @@ serial_pci_matches(const struct pciserial_board *board, struct serial_private * pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) { - struct uart_port serial_port; + struct uart_8250_port uart; struct serial_private *priv; struct pci_serial_quirk *quirk; int rc, nr_ports, i; @@ -2768,22 +2824,22 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) priv->dev = dev; priv->quirk = quirk; - memset(&serial_port, 0, sizeof(struct uart_port)); - serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - serial_port.uartclk = board->base_baud * 16; - serial_port.irq = get_pci_irq(dev, board); - serial_port.dev = &dev->dev; + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = board->base_baud * 16; + uart.port.irq = get_pci_irq(dev, board); + uart.port.dev = &dev->dev; for (i = 0; i < nr_ports; i++) { - if (quirk->setup(priv, board, &serial_port, i)) + if (quirk->setup(priv, board, &uart, i)) break; #ifdef SERIAL_DEBUG_PCI printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", - serial_port.iobase, serial_port.irq, serial_port.iotype); + uart.port.iobase, uart.port.irq, uart.port.iotype); #endif - priv->line[i] = serial8250_register_port(&serial_port); + priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) { printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); break; @@ -3193,7 +3249,7 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, - pbn_exsys_4055 }, + pbn_b2_4_115200 }, /* * Megawolf Romulus PCI Serial Card, from Mike Hudson * (Exoray@isys.ca) @@ -3232,8 +3288,11 @@ static struct pci_device_id serial_pci_tbl[] = { * For now just used the hex ID 0x950a. */ { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, - pbn_b0_2_115200 }, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00, + 0, 0, pbn_b0_2_115200 }, + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30, + 0, 0, pbn_b0_2_115200 }, { PCI_VENDOR_ID_OXSEMI, 0x950a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_2_1130000 }, @@ -4179,6 +4238,25 @@ static struct pci_device_id serial_pci_tbl[] = { pbn_omegapci }, /* + * AgeStar as-prs2-009 + */ + { PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + + /* + * WCH CH353 series devices: The 2S1P is handled by parport_serial + * so not listed here. + */ + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_4S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_4_115200 }, + + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_2S1PF, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL */ diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index a2f2365..f8ee250 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -1,5 +1,5 @@ /* - * Probe module for 8250/16550-type ISAPNP serial ports. + * Probe for 8250/16550-type ISAPNP serial ports. * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * @@ -25,7 +25,7 @@ #include "8250.h" #define UNKNOWN_DEV 0x3000 - +#define CIR_PORT 0x0800 static const struct pnp_device_id pnp_dev_table[] = { /* Archtek America Corp. */ @@ -362,6 +362,9 @@ static const struct pnp_device_id pnp_dev_table[] = { { "PNPCXXX", UNKNOWN_DEV }, /* More unknown PnP modems */ { "PNPDXXX", UNKNOWN_DEV }, + /* Winbond CIR port, should not be probed. We should keep track + of it to prevent the legacy serial driver from probing it */ + { "WEC1022", CIR_PORT }, { "", 0 } }; @@ -409,7 +412,7 @@ static int __devinit check_resources(struct pnp_dev *dev) * PnP modems, alternatively we must hardcode all modems in pnp_devices[] * table. */ -static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) +static int __devinit serial_pnp_guess_board(struct pnp_dev *dev) { if (!(check_name(pnp_dev_name(dev)) || (dev->card && check_name(dev->card->name)))) @@ -424,42 +427,49 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) static int __devinit serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - struct uart_port port; + struct uart_8250_port uart; int ret, line, flags = dev_id->driver_data; if (flags & UNKNOWN_DEV) { - ret = serial_pnp_guess_board(dev, &flags); + ret = serial_pnp_guess_board(dev); if (ret < 0) return ret; } - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); if (pnp_irq_valid(dev, 0)) - port.irq = pnp_irq(dev, 0); - if (pnp_port_valid(dev, 0)) { - port.iobase = pnp_port_start(dev, 0); - port.iotype = UPIO_PORT; + uart.port.irq = pnp_irq(dev, 0); + if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { + uart.port.iobase = pnp_port_start(dev, 2); + uart.port.iotype = UPIO_PORT; + } else if (pnp_port_valid(dev, 0)) { + uart.port.iobase = pnp_port_start(dev, 0); + uart.port.iotype = UPIO_PORT; } else if (pnp_mem_valid(dev, 0)) { - port.mapbase = pnp_mem_start(dev, 0); - port.iotype = UPIO_MEM; - port.flags = UPF_IOREMAP; + uart.port.mapbase = pnp_mem_start(dev, 0); + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_IOREMAP; } else return -ENODEV; #ifdef SERIAL_DEBUG_PNP printk(KERN_DEBUG "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", - port.iobase, port.mapbase, port.irq, port.iotype); + uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype); #endif + if (flags & CIR_PORT) { + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.type = PORT_8250_CIR; + } - port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - port.flags |= UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &dev->dev; + uart.port.flags |= UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; - line = serial8250_register_port(&port); - if (line < 0) + line = serial8250_register_8250_port(&uart); + if (line < 0 || (flags & CIR_PORT)) return -ENODEV; pnp_set_drvdata(dev, (void *)((long)line + 1)); @@ -507,18 +517,13 @@ static struct pnp_driver serial_pnp_driver = { .id_table = pnp_dev_table, }; -static int __init serial8250_pnp_init(void) +int serial8250_pnp_init(void) { return pnp_register_driver(&serial_pnp_driver); } -static void __exit serial8250_pnp_exit(void) +void serial8250_pnp_exit(void) { pnp_unregister_driver(&serial_pnp_driver); } -module_init(serial8250_pnp_init); -module_exit(serial8250_pnp_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index a27dd05..f3d283f 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -33,6 +33,14 @@ config SERIAL_8250 Most people will say Y or M here, so that they can use serial mice, modems and similar devices connecting to the standard serial ports. +config SERIAL_8250_PNP + bool "8250/16550 PNP device support" if EXPERT + depends on SERIAL_8250 && PNP + default y + ---help--- + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y @@ -85,14 +93,6 @@ config SERIAL_8250_PCI disable this feature if you only need legacy serial support. Saves about 9K. -config SERIAL_8250_PNP - tristate "8250/16550 PNP device support" if EXPERT - depends on SERIAL_8250 && PNP - default SERIAL_8250 - help - This builds standard PNP serial support. You may be able to - disable this feature if you only need legacy serial support. - config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index d7533c7..108fe7f 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -2,8 +2,9 @@ # Makefile for the 8250 serial device drivers. # -obj-$(CONFIG_SERIAL_8250) += 8250.o -obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250) += 8250_core.o +8250_core-y := 8250.o +8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 29b695d..b7d48b3 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -73,7 +73,7 @@ struct serial_quirk { unsigned int prodid; int multi; /* 1 = multifunction, > 1 = # ports */ void (*config)(struct pcmcia_device *); - void (*setup)(struct pcmcia_device *, struct uart_port *); + void (*setup)(struct pcmcia_device *, struct uart_8250_port *); void (*wakeup)(struct pcmcia_device *); int (*post)(struct pcmcia_device *); }; @@ -105,9 +105,9 @@ struct serial_cfg_mem { * Elan VPU16551 UART with 14.7456MHz oscillator * manfid 0x015D, 0x4C45 */ -static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) +static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart) { - port->uartclk = 14745600; + uart->port.uartclk = 14745600; } static int quirk_post_ibm(struct pcmcia_device *link) @@ -343,25 +343,25 @@ static void serial_detach(struct pcmcia_device *link) static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, unsigned int iobase, int irq) { - struct uart_port port; + struct uart_8250_port uart; int line; - memset(&port, 0, sizeof (struct uart_port)); - port.iobase = iobase; - port.irq = irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &handle->dev; + memset(&uart, 0, sizeof(uart)); + uart.port.iobase = iobase; + uart.port.irq = irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &handle->dev; if (buggy_uart) - port.flags |= UPF_BUGGY_UART; + uart.port.flags |= UPF_BUGGY_UART; if (info->quirk && info->quirk->setup) - info->quirk->setup(handle, &port); + info->quirk->setup(handle, &uart); - line = serial8250_register_port(&port); + line = serial8250_register_8250_port(&uart); if (line < 0) { - printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " - "0x%04lx, irq %d failed\n", (u_long)iobase, irq); + pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n", + (unsigned long)iobase, irq); return -EINVAL; } diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4720b4b..233fbaa 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -141,6 +141,25 @@ config SERIAL_ATMEL_TTYAT Say Y if you have an external 8250/16C550 UART. If unsure, say N. +config SERIAL_KGDB_NMI + bool "Serial console over KGDB NMI debugger port" + depends on KGDB_SERIAL_CONSOLE + help + This special driver allows you to temporary use NMI debugger port + as a normal console (assuming that the port is attached to KGDB). + + Unlike KDB's disable_nmi command, with this driver you are always + able to go back to the debugger using KGDB escape sequence ($3#33). + This is because this console driver processes the input in NMI + context, and thus is able to intercept the magic sequence. + + Note that since the console interprets input and uses polling + communication methods, for things like PPP you still must fully + detach debugger port from the KGDB NMI (i.e. disable_nmi), and + use raw console. + + If unsure, say N. + config SERIAL_KS8695 bool "Micrel KS8695 (Centaur) serial port support" depends on ARCH_KS8695 @@ -257,12 +276,19 @@ config SERIAL_MAX3100 help MAX3100 chip support -config SERIAL_MAX3107 - tristate "MAX3107 support" +config SERIAL_MAX310X + bool "MAX310X support" depends on SPI select SERIAL_CORE + select REGMAP_SPI if SPI + default n help - MAX3107 chip support + This selects support for an advanced UART from Maxim (Dallas). + Supported ICs are MAX3107, MAX3108. + Each IC contains 128 words each of receive and transmit FIFO + that can be controlled through I2C or high-speed SPI. + + Say Y here if you want to support this ICs. config SERIAL_DZ bool "DECstation DZ serial driver" @@ -686,7 +712,7 @@ config SERIAL_SH_SCI_CONSOLE config SERIAL_SH_SCI_DMA bool "DMA support" - depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL + depends on SERIAL_SH_SCI && SH_DMAE config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" @@ -704,6 +730,25 @@ config SERIAL_PNX8XXX_CONSOLE If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 and you want to use serial console, say Y. Otherwise, say N. +config SERIAL_HS_LPC32XX + tristate "LPC32XX high speed serial port support" + depends on ARCH_LPC32XX && OF + select SERIAL_CORE + help + Support for the LPC32XX high speed serial ports (up to 900kbps). + Those are UARTs completely different from the Standard UARTs on the + LPC32XX SoC. + Choose M or Y here to build this driver. + +config SERIAL_HS_LPC32XX_CONSOLE + bool "Enable LPC32XX high speed UART serial console" + depends on SERIAL_HS_LPC32XX + select SERIAL_CORE_CONSOLE + help + If you would like to be able to use one of the high speed serial + ports on the LPC32XX as the console, you can do so by answering + Y to this option. + config SERIAL_CORE tristate @@ -1104,6 +1149,24 @@ config SERIAL_SC26XX_CONSOLE help Support for Console on SC2681/SC2692 serial ports. +config SERIAL_SCCNXP + bool "SCCNXP serial port support" + depends on !SERIAL_SC26XX + select SERIAL_CORE + default n + help + This selects support for an advanced UART from NXP (Philips). + Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92, + SC28L202, SCC68681 and SCC68692. + Positioned as a replacement for the driver SC26XX. + +config SERIAL_SCCNXP_CONSOLE + bool "Console on SCCNXP serial port" + depends on SERIAL_SCCNXP + select SERIAL_CORE_CONSOLE + help + Support for console on SCCNXP serial ports. + config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" depends on BLACKFIN @@ -1260,7 +1323,7 @@ config SERIAL_ALTERA_UART_CONSOLE config SERIAL_IFX6X60 tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" - depends on GPIOLIB && SPI && EXPERIMENTAL + depends on GPIOLIB && SPI help Support for the IFX6x60 modem devices on Intel MID platforms. diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7257c5d..4f694da 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,12 +28,13 @@ obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o -obj-$(CONFIG_SERIAL_MAX3107) += max3107.o +obj-$(CONFIG_SERIAL_MAX310X) += max310x.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o +obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o obj-$(CONFIG_SERIAL_DZ) += dz.o obj-$(CONFIG_SERIAL_ZS) += zs.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o @@ -47,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o +obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o @@ -59,6 +61,7 @@ obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o +obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 1f03309..15d80b9 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -591,7 +591,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) port->ops = &altera_uart_ops; port->flags = UPF_BOOT_AUTOCONF; - dev_set_drvdata(&pdev->dev, port); + platform_set_drvdata(pdev, port); uart_add_one_port(&altera_uart_driver, port); @@ -600,11 +600,11 @@ static int __devinit altera_uart_probe(struct platform_device *pdev) static int __devexit altera_uart_remove(struct platform_device *pdev) { - struct uart_port *port = dev_get_drvdata(&pdev->dev); + struct uart_port *port = platform_get_drvdata(pdev); if (port) { uart_remove_one_port(&altera_uart_driver, port); - dev_set_drvdata(&pdev->dev, NULL); + platform_set_drvdata(pdev, NULL); port->mapbase = 0; } diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 0d91a54..22317dd 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -312,16 +312,12 @@ static int pl010_startup(struct uart_port *port) struct uart_amba_port *uap = (struct uart_amba_port *)port; int retval; - retval = clk_prepare(uap->clk); - if (retval) - goto out; - /* * Try to enable the clock producer. */ - retval = clk_enable(uap->clk); + retval = clk_prepare_enable(uap->clk); if (retval) - goto clk_unprep; + goto out; uap->port.uartclk = clk_get_rate(uap->clk); @@ -346,9 +342,7 @@ static int pl010_startup(struct uart_port *port) return 0; clk_dis: - clk_disable(uap->clk); - clk_unprep: - clk_unprepare(uap->clk); + clk_disable_unprepare(uap->clk); out: return retval; } @@ -375,8 +369,7 @@ static void pl010_shutdown(struct uart_port *port) /* * Shut down the clock producer */ - clk_disable(uap->clk); - clk_unprepare(uap->clk); + clk_disable_unprepare(uap->clk); } static void diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d3553b5..d7e1ede 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -52,6 +52,8 @@ #include <linux/scatterlist.h> #include <linux/delay.h> #include <linux/types.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/sizes.h> @@ -75,7 +77,6 @@ struct vendor_data { unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; - bool interrupt_may_hang; /* vendor-specific */ bool dma_threshold; bool cts_event_workaround; }; @@ -96,7 +97,6 @@ static struct vendor_data vendor_st = { .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, - .interrupt_may_hang = true, .dma_threshold = true, .cts_event_workaround = true, }; @@ -147,7 +147,6 @@ struct uart_amba_port { unsigned int old_cr; /* state during shutdown */ bool autorts; char type[12]; - bool interrupt_may_hang; /* vendor-specific */ #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ bool using_tx_dma; @@ -1215,14 +1214,14 @@ static irqreturn_t pl011_int(int irq, void *dev_id) return IRQ_RETVAL(handled); } -static unsigned int pl01x_tx_empty(struct uart_port *port) +static unsigned int pl011_tx_empty(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status = readw(uap->port.membase + UART01x_FR); return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } -static unsigned int pl01x_get_mctrl(struct uart_port *port) +static unsigned int pl011_get_mctrl(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int result = 0; @@ -1285,11 +1284,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) } #ifdef CONFIG_CONSOLE_POLL -static int pl010_get_poll_char(struct uart_port *port) + +static void pl011_quiesce_irqs(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned char __iomem *regs = uap->port.membase; + + writew(readw(regs + UART011_MIS), regs + UART011_ICR); + /* + * There is no way to clear TXIM as this is "ready to transmit IRQ", so + * we simply mask it. start_tx() will unmask it. + * + * Note we can race with start_tx(), and if the race happens, the + * polling user might get another interrupt just after we clear it. + * But it should be OK and can happen even w/o the race, e.g. + * controller immediately got some new data and raised the IRQ. + * + * And whoever uses polling routines assumes that it manages the device + * (including tx queue), so we're also fine with start_tx()'s caller + * side. + */ + writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC); +} + +static int pl011_get_poll_char(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; + /* + * The caller might need IRQs lowered, e.g. if used with KDB NMI + * debugger. + */ + pl011_quiesce_irqs(port); + status = readw(uap->port.membase + UART01x_FR); if (status & UART01x_FR_RXFE) return NO_POLL_CHAR; @@ -1297,7 +1325,7 @@ static int pl010_get_poll_char(struct uart_port *port) return readw(uap->port.membase + UART01x_DR); } -static void pl010_put_poll_char(struct uart_port *port, +static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1310,10 +1338,9 @@ static void pl010_put_poll_char(struct uart_port *port, #endif /* CONFIG_CONSOLE_POLL */ -static int pl011_startup(struct uart_port *port) +static int pl011_hwinit(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; int retval; /* Optionaly enable pins to be muxed in and configured */ @@ -1324,16 +1351,12 @@ static int pl011_startup(struct uart_port *port) "could not set default pins\n"); } - retval = clk_prepare(uap->clk); - if (retval) - goto out; - /* * Try to enable the clock producer. */ - retval = clk_enable(uap->clk); + retval = clk_prepare_enable(uap->clk); if (retval) - goto clk_unprep; + goto out; uap->port.uartclk = clk_get_rate(uap->clk); @@ -1342,6 +1365,37 @@ static int pl011_startup(struct uart_port *port) 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 */ retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); @@ -1400,21 +1454,10 @@ static int pl011_startup(struct uart_port *port) writew(uap->im, uap->port.membase + UART011_IMSC); spin_unlock_irq(&uap->port.lock); - if (uap->port.dev->platform_data) { - struct amba_pl011_data *plat; - - plat = uap->port.dev->platform_data; - if (plat->init) - plat->init(); - } - return 0; clk_dis: - clk_disable(uap->clk); - clk_unprep: - clk_unprepare(uap->clk); - out: + clk_disable_unprepare(uap->clk); return retval; } @@ -1473,8 +1516,7 @@ static void pl011_shutdown(struct uart_port *port) /* * Shut down the clock producer */ - clk_disable(uap->clk); - clk_unprepare(uap->clk); + clk_disable_unprepare(uap->clk); /* Optionally let pins go into sleep states */ if (!IS_ERR(uap->pins_sleep)) { retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep); @@ -1603,13 +1645,26 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr &= ~ST_UART011_CR_OVSFACT; } + /* + * Workaround for the ST Micro oversampling variants to + * increase the bitrate slightly, by lowering the divisor, + * to avoid delayed sampling of start bit at high speeds, + * else we see data corruption. + */ + if (uap->vendor->oversampling) { + if ((baud >= 3000000) && (baud < 3250000) && (quot > 1)) + quot -= 1; + else if ((baud > 3250000) && (quot > 2)) + quot -= 2; + } /* Set baud rate */ writew(quot & 0x3f, port->membase + UART011_FBRD); writew(quot >> 6, port->membase + UART011_IBRD); /* * ----------v----------v----------v----------v----- - * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER + * UART011_FBRD & UART011_IBRD. * ----------^----------^----------^----------^----- */ writew(lcr_h, port->membase + uap->lcrh_rx); @@ -1637,7 +1692,7 @@ static const char *pl011_type(struct uart_port *port) /* * Release the memory region(s) being used by 'port' */ -static void pl010_release_port(struct uart_port *port) +static void pl011_release_port(struct uart_port *port) { release_mem_region(port->mapbase, SZ_4K); } @@ -1645,7 +1700,7 @@ static void pl010_release_port(struct uart_port *port) /* * Request the memory region(s) being used by 'port' */ -static int pl010_request_port(struct uart_port *port) +static int pl011_request_port(struct uart_port *port) { return request_mem_region(port->mapbase, SZ_4K, "uart-pl011") != NULL ? 0 : -EBUSY; @@ -1654,18 +1709,18 @@ static int pl010_request_port(struct uart_port *port) /* * Configure/autoconfigure the port. */ -static void pl010_config_port(struct uart_port *port, int flags) +static void pl011_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) { port->type = PORT_AMBA; - pl010_request_port(port); + pl011_request_port(port); } } /* * verify the new serial_struct (for TIOCSSERIAL). */ -static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) +static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) { int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) @@ -1678,9 +1733,9 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) } static struct uart_ops amba_pl011_pops = { - .tx_empty = pl01x_tx_empty, + .tx_empty = pl011_tx_empty, .set_mctrl = pl011_set_mctrl, - .get_mctrl = pl01x_get_mctrl, + .get_mctrl = pl011_get_mctrl, .stop_tx = pl011_stop_tx, .start_tx = pl011_start_tx, .stop_rx = pl011_stop_rx, @@ -1691,13 +1746,14 @@ static struct uart_ops amba_pl011_pops = { .flush_buffer = pl011_dma_flush_buffer, .set_termios = pl011_set_termios, .type = pl011_type, - .release_port = pl010_release_port, - .request_port = pl010_request_port, - .config_port = pl010_config_port, - .verify_port = pl010_verify_port, + .release_port = pl011_release_port, + .request_port = pl011_request_port, + .config_port = pl011_config_port, + .verify_port = pl011_verify_port, #ifdef CONFIG_CONSOLE_POLL - .poll_get_char = pl010_get_poll_char, - .poll_put_char = pl010_put_poll_char, + .poll_init = pl011_hwinit, + .poll_get_char = pl011_get_poll_char, + .poll_put_char = pl011_put_poll_char, #endif }; @@ -1869,6 +1925,38 @@ static struct uart_driver amba_reg = { .cons = AMBA_CONSOLE, }; +static int pl011_probe_dt_alias(int index, struct device *dev) +{ + struct device_node *np; + static bool seen_dev_with_alias = false; + static bool seen_dev_without_alias = false; + int ret = index; + + if (!IS_ENABLED(CONFIG_OF)) + return ret; + + np = dev->of_node; + if (!np) + return ret; + + ret = of_alias_get_id(np, "serial"); + if (IS_ERR_VALUE(ret)) { + seen_dev_without_alias = true; + ret = index; + } else { + seen_dev_with_alias = true; + if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret] != NULL) { + dev_warn(dev, "requested serial port %d not available.\n", ret); + ret = index; + } + } + + if (seen_dev_with_alias && seen_dev_without_alias) + dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n"); + + return ret; +} + static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; @@ -1891,6 +1979,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } + i = pl011_probe_dt_alias(i, &dev->dev); + base = ioremap(dev->res.start, resource_size(&dev->res)); if (!base) { ret = -ENOMEM; @@ -1923,7 +2013,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; uap->fifosize = vendor->fifosize; - uap->interrupt_may_hang = vendor->interrupt_may_hang; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index bd97db2..9242d56 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -182,7 +182,7 @@ static void bfin_serial_start_tx(struct uart_port *port) * To avoid losting RX interrupt, we reset IR function * before sending data. */ - if (tty->termios->c_line == N_IRDA) + if (tty->termios.c_line == N_IRDA) bfin_serial_reset_irda(port); #ifdef CONFIG_SERIAL_BFIN_DMA diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index b418947..d0dd919 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -71,7 +71,7 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo); /**************************************************************/ -#define HW_BUF_SPD_THRESHOLD 9600 +#define HW_BUF_SPD_THRESHOLD 2400 /* * Check, if transmit buffers are processed @@ -417,6 +417,7 @@ static int cpm_uart_startup(struct uart_port *port) clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR); clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); } + cpm_uart_initbd(pinfo); cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); } /* Install interrupt handler. */ @@ -500,16 +501,28 @@ static void cpm_uart_set_termios(struct uart_port *port, struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; smc_t __iomem *smcp = pinfo->smcp; scc_t __iomem *sccp = pinfo->sccp; + int maxidl; pr_debug("CPM uart[%d]:set_termios\n", port->line); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - if (baud <= HW_BUF_SPD_THRESHOLD || + if (baud < HW_BUF_SPD_THRESHOLD || (pinfo->port.state && pinfo->port.state->port.tty->low_latency)) pinfo->rx_fifosize = 1; else pinfo->rx_fifosize = RX_BUF_SIZE; + /* MAXIDL is the timeout after which a receive buffer is closed + * when not full if no more characters are received. + * We calculate it from the baudrate so that the duration is + * always the same at standard rates: about 4ms. + */ + maxidl = baud / 2400; + if (maxidl < 1) + maxidl = 1; + if (maxidl > 0x10) + maxidl = 0x10; + /* Character length programmed into the mode register is the * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, * 1 or 2 stop bits, minus 1. @@ -610,6 +623,7 @@ static void cpm_uart_set_termios(struct uart_port *port, * SMC/SCC receiver is disabled. */ out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize); + out_be16(&pinfo->smcup->smc_maxidl, maxidl); /* Set the mode register. We want to keep a copy of the * enables, because we want to put them back if they were @@ -622,6 +636,7 @@ static void cpm_uart_set_termios(struct uart_port *port, SMCMR_SM_UART | prev_mode); } else { out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); + out_be16(&pinfo->sccup->scc_maxidl, maxidl); out_be16(&sccp->scc_psmr, (sbits << 12) | scval); } @@ -798,7 +813,7 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo) cpm_set_scc_fcr(sup); out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); - out_be16(&sup->scc_maxidl, pinfo->rx_fifosize); + out_be16(&sup->scc_maxidl, 0x10); out_be16(&sup->scc_brkcr, 1); out_be16(&sup->scc_parec, 0); out_be16(&sup->scc_frmec, 0); @@ -872,7 +887,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) /* Using idle character time requires some additional tuning. */ out_be16(&up->smc_mrblr, pinfo->rx_fifosize); - out_be16(&up->smc_maxidl, pinfo->rx_fifosize); + out_be16(&up->smc_maxidl, 0x10); out_be16(&up->smc_brklen, 0); out_be16(&up->smc_brkec, 0); out_be16(&up->smc_brkcr, 1); diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 80b6b1b..35ee6a2 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -955,7 +955,7 @@ static const struct control_pins e100_modem_pins[NR_PORTS] = /* Calculate the chartime depending on baudrate, numbor of bits etc. */ static void update_char_time(struct e100_serial * info) { - tcflag_t cflags = info->port.tty->termios->c_cflag; + tcflag_t cflags = info->port.tty->termios.c_cflag; int bits; /* calc. number of bits / data byte */ @@ -1473,7 +1473,7 @@ rs_stop(struct tty_struct *tty) xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop); - if (tty->termios->c_iflag & IXON ) { + if (tty->termios.c_iflag & IXON ) { xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); } @@ -1496,7 +1496,7 @@ rs_start(struct tty_struct *tty) info->xmit.tail,SERIAL_XMIT_SIZE))); xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); - if (tty->termios->c_iflag & IXON ) { + if (tty->termios.c_iflag & IXON ) { xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); } @@ -2929,7 +2929,7 @@ shutdown(struct e100_serial * info) descr[i].buf = 0; } - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) { /* hang up DTR and RTS if HUPCL is enabled */ e100_dtr(info, 0); e100_rts(info, 0); /* could check CRTSCTS before doing this */ @@ -2953,12 +2953,12 @@ change_speed(struct e100_serial *info) unsigned long flags; /* first some safety checks */ - if (!info->port.tty || !info->port.tty->termios) + if (!info->port.tty) return; if (!info->ioport) return; - cflag = info->port.tty->termios->c_cflag; + cflag = info->port.tty->termios.c_cflag; /* possibly, the tx/rx should be disabled first to do this safely */ @@ -3088,7 +3088,7 @@ change_speed(struct e100_serial *info) info->ioport[REG_REC_CTRL] = info->rx_ctrl; xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); - if (info->port.tty->termios->c_iflag & IXON ) { + if (info->port.tty->termios.c_iflag & IXON ) { DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->port.tty))); xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); @@ -3355,7 +3355,7 @@ rs_throttle(struct tty_struct * tty) DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { /* Turn off RTS line */ e100_rts(info, 0); } @@ -3377,7 +3377,7 @@ rs_unthrottle(struct tty_struct * tty) DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count)); /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { /* Assert RTS line */ e100_rts(info, 1); } @@ -3748,7 +3748,7 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); } @@ -3815,7 +3815,7 @@ rs_close(struct tty_struct *tty, struct file * filp) * separate termios for callout and dialin. */ if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; + info->normal_termios = tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -3998,7 +3998,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } - if (tty->termios->c_cflag & CLOCAL) { + if (tty->termios.c_cflag & CLOCAL) { do_clocal = 1; } @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? @@ -4219,7 +4219,7 @@ rs_open(struct tty_struct *tty, struct file * filp) } if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - *tty->termios = info->normal_termios; + tty->termios = info->normal_termios; change_speed(info); } @@ -4443,14 +4443,12 @@ static int __init rs_init(void) B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ driver->init_termios.c_ispeed = 115200; driver->init_termios.c_ospeed = 115200; - driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(driver, &rs_ops); serial_driver = driver; - if (tty_register_driver(driver)) - panic("Couldn't register serial driver\n"); - /* do some initializing for the separate ports */ + /* do some initializing for the separate ports */ for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { if (info->enabled) { if (cris_request_io_interface(info->io_if, @@ -4502,7 +4500,12 @@ static int __init rs_init(void) printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n", serial_driver->name, info->line, info->ioport); } + tty_port_link_device(&info->port, driver, i); } + + if (tty_register_driver(driver)) + panic("Couldn't register serial driver\n"); + #ifdef CONFIG_ETRAX_FAST_TIMER #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER memset(fast_timers, 0, sizeof(fast_timers)); diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 3ad079f..5b9bc19 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -800,8 +800,8 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) tty_port_init(pport); pport->ops = &ifx_tty_port_ops; ifx_dev->minor = IFX_SPI_TTY_ID; - ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor, - &ifx_dev->spi_dev->dev); + ifx_dev->tty_dev = tty_port_register_device(pport, tty_drv, + ifx_dev->minor, &ifx_dev->spi_dev->dev); if (IS_ERR(ifx_dev->tty_dev)) { dev_dbg(&ifx_dev->spi_dev->dev, "%s: registering tty device failed", __func__); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e309e8b..efeb8be 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -207,7 +207,7 @@ struct imx_port { unsigned short trcv_delay; /* transceiver delay */ struct clk *clk_ipg; struct clk *clk_per; - struct imx_uart_data *devdata; + const struct imx_uart_data *devdata; }; struct imx_port_ucrs { @@ -1373,8 +1373,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) val |= UCR3_AWAKEN; writel(val, sport->port.membase + UCR3); - if (sport) - uart_suspend_port(&imx_reg, &sport->port); + uart_suspend_port(&imx_reg, &sport->port); return 0; } @@ -1389,8 +1388,7 @@ static int serial_imx_resume(struct platform_device *dev) val &= ~UCR3_AWAKEN; writel(val, sport->port.membase + UCR3); - if (sport) - uart_resume_port(&imx_reg, &sport->port); + uart_resume_port(&imx_reg, &sport->port); return 0; } @@ -1505,18 +1503,21 @@ static int serial_imx_probe(struct platform_device *pdev) pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { ret = PTR_ERR(pinctrl); + dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret); goto unmap; } sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk_ipg)) { ret = PTR_ERR(sport->clk_ipg); + dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); goto unmap; } sport->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(sport->clk_per)) { ret = PTR_ERR(sport->clk_per); + dev_err(&pdev->dev, "failed to get per clk: %d\n", ret); goto unmap; } @@ -1537,7 +1538,7 @@ static int serial_imx_probe(struct platform_device *pdev) ret = uart_add_one_port(&imx_reg, &sport->port); if (ret) goto deinit; - platform_set_drvdata(pdev, &sport->port); + platform_set_drvdata(pdev, sport); return 0; deinit: diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index 758ff310..5ac5289 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -1120,13 +1120,14 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len) struct ioc3_port *port = get_ioc3_port(the_port); struct ring *inring; struct ring_entry *entry; - struct port_hooks *hooks = port->ip_hooks; + struct port_hooks *hooks; int byte_num; char *sc; int loop_counter; BUG_ON(!(len >= 0)); BUG_ON(!port); + hooks = port->ip_hooks; /* There is a nasty timing issue in the IOC3. When the rx_timer * expires or the rx_high condition arises, we take an interrupt. diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index e16894f..3e7da10 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -1803,7 +1803,7 @@ static inline int ic4_startup_local(struct uart_port *the_port) ioc4_set_proto(port, the_port->mapbase); /* set the speed of the serial port */ - ioc4_change_speed(the_port, state->port.tty->termios, + ioc4_change_speed(the_port, &state->port.tty->termios, (struct ktermios *)0); return 0; @@ -2069,13 +2069,14 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf, struct ioc4_port *port = get_ioc4_port(the_port, 0); struct ring *inring; struct ring_entry *entry; - struct hooks *hooks = port->ip_hooks; + struct hooks *hooks; int byte_num; char *sc; int loop_counter; BUG_ON(!(len >= 0)); BUG_ON(!port); + hooks = port->ip_hooks; /* There is a nasty timing issue in the IOC4. When the rx_timer * expires or the rx_high condition arises, we take an interrupt. diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 434bd88..7139796 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -161,7 +161,7 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) struct ktermios *termios; spin_lock_irqsave(&port->lock, lock_flags); - termios = port->state->port.tty->termios; + termios = &port->state->port.tty->termios; if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); @@ -250,7 +250,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - termios = port->state->port.tty->termios; + termios = &port->state->port.tty->termios; channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; channel->ch_c_oflag = termios->c_oflag; @@ -283,7 +283,7 @@ static void jsm_tty_close(struct uart_port *port) jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; - ts = port->state->port.tty->termios; + ts = &port->state->port.tty->termios; channel->ch_flags &= ~(CH_STOPI); @@ -567,7 +567,7 @@ void jsm_input(struct jsm_channel *ch) *input data and return immediately. */ if (!tp || - !(tp->termios->c_cflag & CREAD) ) { + !(tp->termios.c_cflag & CREAD) ) { jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum); diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c new file mode 100644 index 0000000..d185247 --- /dev/null +++ b/drivers/tty/serial/kgdb_nmi.c @@ -0,0 +1,402 @@ +/* + * KGDB NMI serial console + * + * Copyright 2010 Google, Inc. + * Arve HjønnevÃ¥g <arve@android.com> + * Colin Cross <ccross@android.com> + * Copyright 2012 Linaro Ltd. + * Anton Vorontsov <anton.vorontsov@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/atomic.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/tick.h> +#include <linux/kfifo.h> +#include <linux/kgdb.h> +#include <linux/kdb.h> + +static int kgdb_nmi_knock = 1; +module_param_named(knock, kgdb_nmi_knock, int, 0600); +MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \ + "must be used to enter the debugger; when set to 0, " \ + "hitting return key is enough to enter the debugger; " \ + "when set to -1, the debugger is entered immediately " \ + "upon NMI"); + +static char *kgdb_nmi_magic = "$3#33"; +module_param_named(magic, kgdb_nmi_magic, charp, 0600); +MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)"); + +static bool kgdb_nmi_tty_enabled; + +static void kgdb_nmi_console_write(struct console *co, const char *s, uint c) +{ + int i; + + if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0) + return; + + for (i = 0; i < c; i++) + dbg_io_ops->write_char(s[i]); +} + +static struct tty_driver *kgdb_nmi_tty_driver; + +static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx) +{ + *idx = co->index; + return kgdb_nmi_tty_driver; +} + +static struct console kgdb_nmi_console = { + .name = "ttyNMI", + .write = kgdb_nmi_console_write, + .device = kgdb_nmi_console_device, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, + .index = -1, +}; + +/* + * This is usually the maximum rate on debug ports. We make fifo large enough + * to make copy-pasting to the terminal usable. + */ +#define KGDB_NMI_BAUD 115200 +#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ) + +struct kgdb_nmi_tty_priv { + struct tty_port port; + struct tasklet_struct tlet; + STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo; +}; + +static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port) +{ + return container_of(port, struct kgdb_nmi_tty_priv, port); +} + +/* + * Our debugging console is polled in a tasklet, so we'll check for input + * every tick. In HZ-less mode, we should program the next tick. We have + * to use the lowlevel stuff as no locks should be grabbed. + */ +#ifdef CONFIG_HIGH_RES_TIMERS +static void kgdb_tty_poke(void) +{ + tick_program_event(ktime_get(), 0); +} +#else +static inline void kgdb_tty_poke(void) {} +#endif + +static struct tty_port *kgdb_nmi_port; + +static void kgdb_tty_recv(int ch) +{ + struct kgdb_nmi_tty_priv *priv; + char c = ch; + + if (!kgdb_nmi_port || ch < 0) + return; + /* + * Can't use port->tty->driver_data as tty might be not there. Tasklet + * will check for tty and will get the ref, but here we don't have to + * do that, and actually, we can't: we're in NMI context, no locks are + * possible. + */ + priv = kgdb_nmi_port_to_priv(kgdb_nmi_port); + kfifo_in(&priv->fifo, &c, 1); + kgdb_tty_poke(); +} + +static int kgdb_nmi_poll_one_knock(void) +{ + static int n; + int c = -1; + const char *magic = kgdb_nmi_magic; + size_t m = strlen(magic); + bool printch = 0; + + c = dbg_io_ops->read_char(); + if (c == NO_POLL_CHAR) + return c; + + if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) { + return 1; + } else if (c == magic[n]) { + n = (n + 1) % m; + if (!n) + return 1; + printch = 1; + } else { + n = 0; + } + + if (kgdb_nmi_tty_enabled) { + kgdb_tty_recv(c); + return 0; + } + + if (printch) { + kdb_printf("%c", c); + return 0; + } + + kdb_printf("\r%s %s to enter the debugger> %*s", + kgdb_nmi_knock ? "Type" : "Hit", + kgdb_nmi_knock ? magic : "<return>", (int)m, ""); + while (m--) + kdb_printf("\b"); + return 0; +} + +/** + * kgdb_nmi_poll_knock - Check if it is time to enter the debugger + * + * "Serial ports are often noisy, especially when muxed over another port (we + * often use serial over the headset connector). Noise on the async command + * line just causes characters that are ignored, on a command line that blocked + * execution noise would be catastrophic." -- Colin Cross + * + * So, this function implements KGDB/KDB knocking on the serial line: we won't + * enter the debugger until we receive a known magic phrase (which is actually + * "$3#33", known as "escape to KDB" command. There is also a relaxed variant + * of knocking, i.e. just pressing the return key is enough to enter the + * debugger. And if knocking is disabled, the function always returns 1. + */ +bool kgdb_nmi_poll_knock(void) +{ + if (kgdb_nmi_knock < 0) + return 1; + + while (1) { + int ret; + + ret = kgdb_nmi_poll_one_knock(); + if (ret == NO_POLL_CHAR) + return 0; + else if (ret == 1) + break; + } + return 1; +} + +/* + * The tasklet is cheap, it does not cause wakeups when reschedules itself, + * instead it waits for the next tick. + */ +static void kgdb_nmi_tty_receiver(unsigned long data) +{ + struct kgdb_nmi_tty_priv *priv = (void *)data; + struct tty_struct *tty; + char ch; + + tasklet_schedule(&priv->tlet); + + if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo))) + return; + + /* Port is there, but tty might be hung up, check. */ + tty = tty_port_tty_get(kgdb_nmi_port); + if (!tty) + return; + + while (kfifo_out(&priv->fifo, &ch, 1)) + tty_insert_flip_char(priv->port.tty, ch, TTY_NORMAL); + tty_flip_buffer_push(priv->port.tty); + + tty_kref_put(tty); +} + +static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct kgdb_nmi_tty_priv *priv = tty->driver_data; + + kgdb_nmi_port = port; + tasklet_schedule(&priv->tlet); + return 0; +} + +static void kgdb_nmi_tty_shutdown(struct tty_port *port) +{ + struct kgdb_nmi_tty_priv *priv = port->tty->driver_data; + + tasklet_kill(&priv->tlet); + kgdb_nmi_port = NULL; +} + +static const struct tty_port_operations kgdb_nmi_tty_port_ops = { + .activate = kgdb_nmi_tty_activate, + .shutdown = kgdb_nmi_tty_shutdown, +}; + +static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) +{ + struct kgdb_nmi_tty_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_KFIFO(priv->fifo); + tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv); + tty_port_init(&priv->port); + priv->port.ops = &kgdb_nmi_tty_port_ops; + tty->driver_data = priv; + + ret = tty_port_install(&priv->port, drv, tty); + if (ret) { + pr_err("%s: can't install tty port: %d\n", __func__, ret); + goto err; + } + return 0; +err: + kfree(priv); + return ret; +} + +static void kgdb_nmi_tty_cleanup(struct tty_struct *tty) +{ + struct kgdb_nmi_tty_priv *priv = tty->driver_data; + + tty->driver_data = NULL; + kfree(priv); +} + +static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file) +{ + struct kgdb_nmi_tty_priv *priv = tty->driver_data; + + return tty_port_open(&priv->port, tty, file); +} + +static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file) +{ + struct kgdb_nmi_tty_priv *priv = tty->driver_data; + + tty_port_close(&priv->port, tty, file); +} + +static void kgdb_nmi_tty_hangup(struct tty_struct *tty) +{ + struct kgdb_nmi_tty_priv *priv = tty->driver_data; + + tty_port_hangup(&priv->port); +} + +static int kgdb_nmi_tty_write_room(struct tty_struct *tty) +{ + /* Actually, we can handle any amount as we use polled writes. */ + return 2048; +} + +static int kgdb_nmi_tty_write(struct tty_struct *tty, const unchar *buf, int c) +{ + int i; + + for (i = 0; i < c; i++) + dbg_io_ops->write_char(buf[i]); + return c; +} + +static const struct tty_operations kgdb_nmi_tty_ops = { + .open = kgdb_nmi_tty_open, + .close = kgdb_nmi_tty_close, + .install = kgdb_nmi_tty_install, + .cleanup = kgdb_nmi_tty_cleanup, + .hangup = kgdb_nmi_tty_hangup, + .write_room = kgdb_nmi_tty_write_room, + .write = kgdb_nmi_tty_write, +}; + +static int kgdb_nmi_enable_console(int argc, const char *argv[]) +{ + kgdb_nmi_tty_enabled = !(argc == 1 && !strcmp(argv[1], "off")); + return 0; +} + +int kgdb_register_nmi_console(void) +{ + int ret; + + if (!arch_kgdb_ops.enable_nmi) + return 0; + + kgdb_nmi_tty_driver = alloc_tty_driver(1); + if (!kgdb_nmi_tty_driver) { + pr_err("%s: cannot allocate tty\n", __func__); + return -ENOMEM; + } + kgdb_nmi_tty_driver->driver_name = "ttyNMI"; + kgdb_nmi_tty_driver->name = "ttyNMI"; + kgdb_nmi_tty_driver->num = 1; + kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL; + kgdb_nmi_tty_driver->flags = TTY_DRIVER_REAL_RAW; + kgdb_nmi_tty_driver->init_termios = tty_std_termios; + tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios, + KGDB_NMI_BAUD, KGDB_NMI_BAUD); + tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops); + + ret = tty_register_driver(kgdb_nmi_tty_driver); + if (ret) { + pr_err("%s: can't register tty driver: %d\n", __func__, ret); + goto err_drv_reg; + } + + ret = kdb_register("nmi_console", kgdb_nmi_enable_console, "[off]", + "switch to Linux NMI console", 0); + if (ret) { + pr_err("%s: can't register kdb command: %d\n", __func__, ret); + goto err_kdb_reg; + } + + register_console(&kgdb_nmi_console); + arch_kgdb_ops.enable_nmi(1); + + return 0; +err_kdb_reg: + tty_unregister_driver(kgdb_nmi_tty_driver); +err_drv_reg: + put_tty_driver(kgdb_nmi_tty_driver); + return ret; +} +EXPORT_SYMBOL_GPL(kgdb_register_nmi_console); + +int kgdb_unregister_nmi_console(void) +{ + int ret; + + if (!arch_kgdb_ops.enable_nmi) + return 0; + arch_kgdb_ops.enable_nmi(0); + + kdb_unregister("nmi_console"); + + ret = unregister_console(&kgdb_nmi_console); + if (ret) + return ret; + + ret = tty_unregister_driver(kgdb_nmi_tty_driver); + if (ret) + return ret; + put_tty_driver(kgdb_nmi_tty_driver); + + return 0; +} +EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console); diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 2b42a01..3f63d83 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -145,6 +145,8 @@ __setup("kgdboc=", kgdboc_option_setup); static void cleanup_kgdboc(void) { + if (kgdb_unregister_nmi_console()) + return; kgdboc_unregister_kbd(); if (configured == 1) kgdb_unregister_io_module(&kgdboc_io_ops); @@ -198,11 +200,18 @@ do_register: if (err) goto noconfig; + err = kgdb_register_nmi_console(); + if (err) + goto nmi_con_failed; + configured = 1; return 0; +nmi_con_failed: + kgdb_unregister_io_module(&kgdboc_io_ops); noconfig: + kgdboc_unregister_kbd(); config[0] = 0; configured = 0; cleanup_kgdboc(); diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c new file mode 100644 index 0000000..ba3af3b --- /dev/null +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -0,0 +1,823 @@ +/* + * High Speed Serial Ports on NXP LPC32xx SoC + * + * Authors: Kevin Wells <kevin.wells@nxp.com> + * Roland Stigge <stigge@antcom.de> + * + * Copyright (C) 2010 NXP Semiconductors + * Copyright (C) 2012 Roland Stigge + * + * 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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/nmi.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <mach/platform.h> +#include <mach/hardware.h> + +/* + * High Speed UART register offsets + */ +#define LPC32XX_HSUART_FIFO(x) ((x) + 0x00) +#define LPC32XX_HSUART_LEVEL(x) ((x) + 0x04) +#define LPC32XX_HSUART_IIR(x) ((x) + 0x08) +#define LPC32XX_HSUART_CTRL(x) ((x) + 0x0C) +#define LPC32XX_HSUART_RATE(x) ((x) + 0x10) + +#define LPC32XX_HSU_BREAK_DATA (1 << 10) +#define LPC32XX_HSU_ERROR_DATA (1 << 9) +#define LPC32XX_HSU_RX_EMPTY (1 << 8) + +#define LPC32XX_HSU_TX_LEV(n) (((n) >> 8) & 0xFF) +#define LPC32XX_HSU_RX_LEV(n) ((n) & 0xFF) + +#define LPC32XX_HSU_TX_INT_SET (1 << 6) +#define LPC32XX_HSU_RX_OE_INT (1 << 5) +#define LPC32XX_HSU_BRK_INT (1 << 4) +#define LPC32XX_HSU_FE_INT (1 << 3) +#define LPC32XX_HSU_RX_TIMEOUT_INT (1 << 2) +#define LPC32XX_HSU_RX_TRIG_INT (1 << 1) +#define LPC32XX_HSU_TX_INT (1 << 0) + +#define LPC32XX_HSU_HRTS_INV (1 << 21) +#define LPC32XX_HSU_HRTS_TRIG_8B (0x0 << 19) +#define LPC32XX_HSU_HRTS_TRIG_16B (0x1 << 19) +#define LPC32XX_HSU_HRTS_TRIG_32B (0x2 << 19) +#define LPC32XX_HSU_HRTS_TRIG_48B (0x3 << 19) +#define LPC32XX_HSU_HRTS_EN (1 << 18) +#define LPC32XX_HSU_TMO_DISABLED (0x0 << 16) +#define LPC32XX_HSU_TMO_INACT_4B (0x1 << 16) +#define LPC32XX_HSU_TMO_INACT_8B (0x2 << 16) +#define LPC32XX_HSU_TMO_INACT_16B (0x3 << 16) +#define LPC32XX_HSU_HCTS_INV (1 << 15) +#define LPC32XX_HSU_HCTS_EN (1 << 14) +#define LPC32XX_HSU_OFFSET(n) ((n) << 9) +#define LPC32XX_HSU_BREAK (1 << 8) +#define LPC32XX_HSU_ERR_INT_EN (1 << 7) +#define LPC32XX_HSU_RX_INT_EN (1 << 6) +#define LPC32XX_HSU_TX_INT_EN (1 << 5) +#define LPC32XX_HSU_RX_TL1B (0x0 << 2) +#define LPC32XX_HSU_RX_TL4B (0x1 << 2) +#define LPC32XX_HSU_RX_TL8B (0x2 << 2) +#define LPC32XX_HSU_RX_TL16B (0x3 << 2) +#define LPC32XX_HSU_RX_TL32B (0x4 << 2) +#define LPC32XX_HSU_RX_TL48B (0x5 << 2) +#define LPC32XX_HSU_TX_TLEMPTY (0x0 << 0) +#define LPC32XX_HSU_TX_TL0B (0x0 << 0) +#define LPC32XX_HSU_TX_TL4B (0x1 << 0) +#define LPC32XX_HSU_TX_TL8B (0x2 << 0) +#define LPC32XX_HSU_TX_TL16B (0x3 << 0) + +#define MODNAME "lpc32xx_hsuart" + +struct lpc32xx_hsuart_port { + struct uart_port port; +}; + +#define FIFO_READ_LIMIT 128 +#define MAX_PORTS 3 +#define LPC32XX_TTY_NAME "ttyTX" +static struct lpc32xx_hsuart_port lpc32xx_hs_ports[MAX_PORTS]; + +#ifdef CONFIG_SERIAL_HS_LPC32XX_CONSOLE +static void wait_for_xmit_empty(struct uart_port *port) +{ + unsigned int timeout = 10000; + + do { + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( + port->membase))) == 0) + break; + if (--timeout == 0) + break; + udelay(1); + } while (1); +} + +static void wait_for_xmit_ready(struct uart_port *port) +{ + unsigned int timeout = 10000; + + while (1) { + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( + port->membase))) < 32) + break; + if (--timeout == 0) + break; + udelay(1); + } +} + +static void lpc32xx_hsuart_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmit_ready(port); + writel((u32)ch, LPC32XX_HSUART_FIFO(port->membase)); +} + +static void lpc32xx_hsuart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct lpc32xx_hsuart_port *up = &lpc32xx_hs_ports[co->index]; + unsigned long flags; + int locked = 1; + + touch_nmi_watchdog(); + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar); + wait_for_xmit_empty(&up->port); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init lpc32xx_hsuart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= MAX_PORTS) + co->index = 0; + + port = &lpc32xx_hs_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver lpc32xx_hsuart_reg; +static struct console lpc32xx_hsuart_console = { + .name = LPC32XX_TTY_NAME, + .write = lpc32xx_hsuart_console_write, + .device = uart_console_device, + .setup = lpc32xx_hsuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpc32xx_hsuart_reg, +}; + +static int __init lpc32xx_hsuart_console_init(void) +{ + register_console(&lpc32xx_hsuart_console); + return 0; +} +console_initcall(lpc32xx_hsuart_console_init); + +#define LPC32XX_HSUART_CONSOLE (&lpc32xx_hsuart_console) +#else +#define LPC32XX_HSUART_CONSOLE NULL +#endif + +static struct uart_driver lpc32xx_hs_reg = { + .owner = THIS_MODULE, + .driver_name = MODNAME, + .dev_name = LPC32XX_TTY_NAME, + .nr = MAX_PORTS, + .cons = LPC32XX_HSUART_CONSOLE, +}; +static int uarts_registered; + +static unsigned int __serial_get_clock_div(unsigned long uartclk, + unsigned long rate) +{ + u32 div, goodrate, hsu_rate, l_hsu_rate, comprate; + u32 rate_diff; + + /* Find the closest divider to get the desired clock rate */ + div = uartclk / rate; + goodrate = hsu_rate = (div / 14) - 1; + if (hsu_rate != 0) + hsu_rate--; + + /* Tweak divider */ + l_hsu_rate = hsu_rate + 3; + rate_diff = 0xFFFFFFFF; + + while (hsu_rate < l_hsu_rate) { + comprate = uartclk / ((hsu_rate + 1) * 14); + if (abs(comprate - rate) < rate_diff) { + goodrate = hsu_rate; + rate_diff = abs(comprate - rate); + } + + hsu_rate++; + } + if (hsu_rate > 0xFF) + hsu_rate = 0xFF; + + return goodrate; +} + +static void __serial_uart_flush(struct uart_port *port) +{ + u32 tmp; + int cnt = 0; + + while ((readl(LPC32XX_HSUART_LEVEL(port->membase)) > 0) && + (cnt++ < FIFO_READ_LIMIT)) + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); +} + +static void __serial_lpc32xx_rx(struct uart_port *port) +{ + unsigned int tmp, flag; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) { + /* Discard data: no tty available */ + while (!(readl(LPC32XX_HSUART_FIFO(port->membase)) & + LPC32XX_HSU_RX_EMPTY)) + ; + + return; + } + + /* Read data from FIFO and push into terminal */ + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); + while (!(tmp & LPC32XX_HSU_RX_EMPTY)) { + flag = TTY_NORMAL; + port->icount.rx++; + + if (tmp & LPC32XX_HSU_ERROR_DATA) { + /* Framing error */ + writel(LPC32XX_HSU_FE_INT, + LPC32XX_HSUART_IIR(port->membase)); + port->icount.frame++; + flag = TTY_FRAME; + tty_insert_flip_char(tty, 0, TTY_FRAME); + } + + tty_insert_flip_char(tty, (tmp & 0xFF), flag); + + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); + } + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static void __serial_lpc32xx_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int tmp; + + if (port->x_char) { + writel((u32)port->x_char, LPC32XX_HSUART_FIFO(port->membase)); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + goto exit_tx; + + /* Transfer data */ + while (LPC32XX_HSU_TX_LEV(readl( + LPC32XX_HSUART_LEVEL(port->membase))) < 64) { + writel((u32) xmit->buf[xmit->tail], + LPC32XX_HSUART_FIFO(port->membase)); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + +exit_tx: + if (uart_circ_empty(xmit)) { + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + } +} + +static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + u32 status; + + spin_lock(&port->lock); + + /* Read UART status and clear latched interrupts */ + status = readl(LPC32XX_HSUART_IIR(port->membase)); + + if (status & LPC32XX_HSU_BRK_INT) { + /* Break received */ + writel(LPC32XX_HSU_BRK_INT, LPC32XX_HSUART_IIR(port->membase)); + port->icount.brk++; + uart_handle_break(port); + } + + /* Framing error */ + if (status & LPC32XX_HSU_FE_INT) + writel(LPC32XX_HSU_FE_INT, LPC32XX_HSUART_IIR(port->membase)); + + if (status & LPC32XX_HSU_RX_OE_INT) { + /* Receive FIFO overrun */ + writel(LPC32XX_HSU_RX_OE_INT, + LPC32XX_HSUART_IIR(port->membase)); + port->icount.overrun++; + if (tty) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_schedule_flip(tty); + } + } + + /* Data received? */ + if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) { + __serial_lpc32xx_rx(port); + if (tty) + tty_flip_buffer_push(tty); + } + + /* Transmit data request? */ + if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) { + writel(LPC32XX_HSU_TX_INT, LPC32XX_HSUART_IIR(port->membase)); + __serial_lpc32xx_tx(port); + } + + spin_unlock(&port->lock); + tty_kref_put(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int serial_lpc32xx_tx_empty(struct uart_port *port) +{ + unsigned int ret = 0; + + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(port->membase))) == 0) + ret = TIOCSER_TEMT; + + return ret; +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + /* No signals are supported on HS UARTs */ +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int serial_lpc32xx_get_mctrl(struct uart_port *port) +{ + /* No signals are supported on HS UARTs */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_stop_tx(struct uart_port *port) +{ + u32 tmp; + + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_start_tx(struct uart_port *port) +{ + u32 tmp; + + __serial_lpc32xx_tx(port); + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp |= LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_stop_rx(struct uart_port *port) +{ + u32 tmp; + + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + writel((LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT | + LPC32XX_HSU_FE_INT), LPC32XX_HSUART_IIR(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_enable_ms(struct uart_port *port) +{ + /* Modem status is not supported */ +} + +/* port->lock is not held. */ +static void serial_lpc32xx_break_ctl(struct uart_port *port, + int break_state) +{ + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&port->lock, flags); + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + if (break_state != 0) + tmp |= LPC32XX_HSU_BREAK; + else + tmp &= ~LPC32XX_HSU_BREAK; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */ +static void lpc32xx_loopback_set(resource_size_t mapbase, int state) +{ + int bit; + u32 tmp; + + switch (mapbase) { + case LPC32XX_HS_UART1_BASE: + bit = 0; + break; + case LPC32XX_HS_UART2_BASE: + bit = 1; + break; + case LPC32XX_HS_UART7_BASE: + bit = 6; + break; + default: + WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase); + return; + } + + tmp = readl(LPC32XX_UARTCTL_CLOOP); + if (state) + tmp |= (1 << bit); + else + tmp &= ~(1 << bit); + writel(tmp, LPC32XX_UARTCTL_CLOOP); +} + +/* port->lock is not held. */ +static int serial_lpc32xx_startup(struct uart_port *port) +{ + int retval; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&port->lock, flags); + + __serial_uart_flush(port); + + writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | + LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), + LPC32XX_HSUART_IIR(port->membase)); + + writel(0xFF, LPC32XX_HSUART_RATE(port->membase)); + + /* + * Set receiver timeout, HSU offset of 20, no break, no interrupts, + * and default FIFO trigger levels + */ + tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */ + + spin_unlock_irqrestore(&port->lock, flags); + + retval = request_irq(port->irq, serial_lpc32xx_interrupt, + 0, MODNAME, port); + if (!retval) + writel((tmp | LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN), + LPC32XX_HSUART_CTRL(port->membase)); + + return retval; +} + +/* port->lock is not held. */ +static void serial_lpc32xx_shutdown(struct uart_port *port) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + lpc32xx_loopback_set(port->mapbase, 1); /* go to loopback mode */ + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +/* port->lock is not held. */ +static void serial_lpc32xx_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, quot; + u32 tmp; + + /* Always 8-bit, no parity, 1 stop bit */ + termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); + termios->c_cflag |= CS8; + + termios->c_cflag &= ~(HUPCL | CMSPAR | CLOCAL | CRTSCTS); + + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / 14); + + quot = __serial_get_clock_div(port->uartclk, baud); + + spin_lock_irqsave(&port->lock, flags); + + /* Ignore characters? */ + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + if ((termios->c_cflag & CREAD) == 0) + tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); + else + tmp |= LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + writel(quot, LPC32XX_HSUART_RATE(port->membase)); + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *serial_lpc32xx_type(struct uart_port *port) +{ + return MODNAME; +} + +static void serial_lpc32xx_release_port(struct uart_port *port) +{ + if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, SZ_4K); + } +} + +static int serial_lpc32xx_request_port(struct uart_port *port) +{ + int ret = -ENODEV; + + if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { + ret = 0; + + if (!request_mem_region(port->mapbase, SZ_4K, MODNAME)) + ret = -EBUSY; + else if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, SZ_4K); + if (!port->membase) { + release_mem_region(port->mapbase, SZ_4K); + ret = -ENOMEM; + } + } + } + + return ret; +} + +static void serial_lpc32xx_config_port(struct uart_port *port, int uflags) +{ + int ret; + + ret = serial_lpc32xx_request_port(port); + if (ret < 0) + return; + port->type = PORT_UART00; + port->fifosize = 64; + + __serial_uart_flush(port); + + writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | + LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), + LPC32XX_HSUART_IIR(port->membase)); + + writel(0xFF, LPC32XX_HSUART_RATE(port->membase)); + + /* Set receiver timeout, HSU offset of 20, no break, no interrupts, + and default FIFO trigger levels */ + writel(LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B, + LPC32XX_HSUART_CTRL(port->membase)); +} + +static int serial_lpc32xx_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UART00) + ret = -EINVAL; + + return ret; +} + +static struct uart_ops serial_lpc32xx_pops = { + .tx_empty = serial_lpc32xx_tx_empty, + .set_mctrl = serial_lpc32xx_set_mctrl, + .get_mctrl = serial_lpc32xx_get_mctrl, + .stop_tx = serial_lpc32xx_stop_tx, + .start_tx = serial_lpc32xx_start_tx, + .stop_rx = serial_lpc32xx_stop_rx, + .enable_ms = serial_lpc32xx_enable_ms, + .break_ctl = serial_lpc32xx_break_ctl, + .startup = serial_lpc32xx_startup, + .shutdown = serial_lpc32xx_shutdown, + .set_termios = serial_lpc32xx_set_termios, + .type = serial_lpc32xx_type, + .release_port = serial_lpc32xx_release_port, + .request_port = serial_lpc32xx_request_port, + .config_port = serial_lpc32xx_config_port, + .verify_port = serial_lpc32xx_verify_port, +}; + +/* + * Register a set of serial devices attached to a platform device + */ +static int __devinit serial_hs_lpc32xx_probe(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = &lpc32xx_hs_ports[uarts_registered]; + int ret = 0; + struct resource *res; + + if (uarts_registered >= MAX_PORTS) { + dev_err(&pdev->dev, + "Error: Number of possible ports exceeded (%d)!\n", + uarts_registered + 1); + return -ENXIO; + } + + memset(p, 0, sizeof(*p)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Error getting mem resource for HS UART port %d\n", + uarts_registered); + return -ENXIO; + } + p->port.mapbase = res->start; + p->port.membase = NULL; + + p->port.irq = platform_get_irq(pdev, 0); + if (p->port.irq < 0) { + dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n", + uarts_registered); + return p->port.irq; + } + + p->port.iotype = UPIO_MEM32; + p->port.uartclk = LPC32XX_MAIN_OSC_FREQ; + p->port.regshift = 2; + p->port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP; + p->port.dev = &pdev->dev; + p->port.ops = &serial_lpc32xx_pops; + p->port.line = uarts_registered++; + spin_lock_init(&p->port.lock); + + /* send port to loopback mode by default */ + lpc32xx_loopback_set(p->port.mapbase, 1); + + ret = uart_add_one_port(&lpc32xx_hs_reg, &p->port); + + platform_set_drvdata(pdev, p); + + return ret; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial_hs_lpc32xx_remove(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_remove_one_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} + + +#ifdef CONFIG_PM +static int serial_hs_lpc32xx_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_suspend_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} + +static int serial_hs_lpc32xx_resume(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_resume_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} +#else +#define serial_hs_lpc32xx_suspend NULL +#define serial_hs_lpc32xx_resume NULL +#endif + +static const struct of_device_id serial_hs_lpc32xx_dt_ids[] = { + { .compatible = "nxp,lpc3220-hsuart" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); + +static struct platform_driver serial_hs_lpc32xx_driver = { + .probe = serial_hs_lpc32xx_probe, + .remove = __devexit_p(serial_hs_lpc32xx_remove), + .suspend = serial_hs_lpc32xx_suspend, + .resume = serial_hs_lpc32xx_resume, + .driver = { + .name = MODNAME, + .owner = THIS_MODULE, + .of_match_table = serial_hs_lpc32xx_dt_ids, + }, +}; + +static int __init lpc32xx_hsuart_init(void) +{ + int ret; + + ret = uart_register_driver(&lpc32xx_hs_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_hs_lpc32xx_driver); + if (ret) + uart_unregister_driver(&lpc32xx_hs_reg); + + return ret; +} + +static void __exit lpc32xx_hsuart_exit(void) +{ + platform_driver_unregister(&serial_hs_lpc32xx_driver); + uart_unregister_driver(&lpc32xx_hs_reg); +} + +module_init(lpc32xx_hsuart_init); +module_exit(lpc32xx_hsuart_exit); + +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("NXP LPC32XX High Speed UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index a070362..b13949a 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -44,8 +44,6 @@ #include <asm/io.h> #include <asm/irq.h> -#define PORT_M32R_BASE PORT_M32R_SIO -#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) #define BAUD_RATE 115200 #include <linux/serial_core.h> @@ -132,22 +130,6 @@ struct irq_info { static struct irq_info irq_lists[NR_IRQS]; -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, - [PORT_INDEX(PORT_M32R_SIO)] = { - .name = "M32RSIO", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, -}; - #ifdef CONFIG_SERIAL_M32R_PLDSIO #define __sio_in(x) inw((unsigned long)(x)) @@ -907,8 +889,7 @@ static void m32r_sio_config_port(struct uart_port *port, int unused) spin_lock_irqsave(&up->port.lock, flags); - up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + up->port.fifosize = 1; spin_unlock_irqrestore(&up->port.lock, flags); } @@ -916,23 +897,11 @@ static void m32r_sio_config_port(struct uart_port *port, int unused) static int m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser) { - if (ser->irq >= nr_irqs || ser->irq < 0 || - ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || - ser->type >= ARRAY_SIZE(uart_config)) + if (ser->irq >= nr_irqs || ser->irq < 0 || ser->baud_base < 9600) return -EINVAL; return 0; } -static const char * -m32r_sio_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - static struct uart_ops m32r_sio_pops = { .tx_empty = m32r_sio_tx_empty, .set_mctrl = m32r_sio_set_mctrl, @@ -946,7 +915,6 @@ static struct uart_ops m32r_sio_pops = { .shutdown = m32r_sio_shutdown, .set_termios = m32r_sio_set_termios, .pm = m32r_sio_pm, - .type = m32r_sio_type, .release_port = m32r_sio_release_port, .request_port = m32r_sio_request_port, .config_port = m32r_sio_config_port, diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index b4902b9..0f24486 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -827,14 +827,16 @@ static int __devexit max3100_remove(struct spi_device *spi) /* find out the index for the chip we are removing */ for (i = 0; i < MAX_MAX3100; i++) - if (max3100s[i] == s) + if (max3100s[i] == s) { + dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i); + uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port); + kfree(max3100s[i]); + max3100s[i] = NULL; break; + } - dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i); - uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port); - kfree(max3100s[i]); - max3100s[i] = NULL; - + WARN_ON(i == MAX_MAX3100); + /* check if this is the last chip we have */ for (i = 0; i < MAX_MAX3100; i++) if (max3100s[i]) { @@ -910,17 +912,7 @@ static struct spi_driver max3100_driver = { .resume = max3100_resume, }; -static int __init max3100_init(void) -{ - return spi_register_driver(&max3100_driver); -} -module_init(max3100_init); - -static void __exit max3100_exit(void) -{ - spi_unregister_driver(&max3100_driver); -} -module_exit(max3100_exit); +module_spi_driver(max3100_driver); MODULE_DESCRIPTION("MAX3100 driver"); MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c deleted file mode 100644 index 17c7ba8..0000000 --- a/drivers/tty/serial/max3107.c +++ /dev/null @@ -1,1215 +0,0 @@ -/* - * max3107.c - spi uart protocol driver for Maxim 3107 - * Based on max3100.c - * by Christian Pellegrin <chripell@evolware.org> - * and max3110.c - * by Feng Tang <feng.tang@intel.com> - * - * Copyright (C) Aavamobile 2009 - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - */ - -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/serial_core.h> -#include <linux/serial.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/gpio.h> -#include <linux/spi/spi.h> -#include <linux/freezer.h> -#include <linux/module.h> -#include "max3107.h" - -static const struct baud_table brg26_ext[] = { - { 300, MAX3107_BRG26_B300 }, - { 600, MAX3107_BRG26_B600 }, - { 1200, MAX3107_BRG26_B1200 }, - { 2400, MAX3107_BRG26_B2400 }, - { 4800, MAX3107_BRG26_B4800 }, - { 9600, MAX3107_BRG26_B9600 }, - { 19200, MAX3107_BRG26_B19200 }, - { 57600, MAX3107_BRG26_B57600 }, - { 115200, MAX3107_BRG26_B115200 }, - { 230400, MAX3107_BRG26_B230400 }, - { 460800, MAX3107_BRG26_B460800 }, - { 921600, MAX3107_BRG26_B921600 }, - { 0, 0 } -}; - -static const struct baud_table brg13_int[] = { - { 300, MAX3107_BRG13_IB300 }, - { 600, MAX3107_BRG13_IB600 }, - { 1200, MAX3107_BRG13_IB1200 }, - { 2400, MAX3107_BRG13_IB2400 }, - { 4800, MAX3107_BRG13_IB4800 }, - { 9600, MAX3107_BRG13_IB9600 }, - { 19200, MAX3107_BRG13_IB19200 }, - { 57600, MAX3107_BRG13_IB57600 }, - { 115200, MAX3107_BRG13_IB115200 }, - { 230400, MAX3107_BRG13_IB230400 }, - { 460800, MAX3107_BRG13_IB460800 }, - { 921600, MAX3107_BRG13_IB921600 }, - { 0, 0 } -}; - -static u32 get_new_brg(int baud, struct max3107_port *s) -{ - int i; - const struct baud_table *baud_tbl = s->baud_tbl; - - for (i = 0; i < 13; i++) { - if (baud == baud_tbl[i].baud) - return baud_tbl[i].new_brg; - } - - return 0; -} - -/* Perform SPI transfer for write/read of device register(s) */ -int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) -{ - struct spi_message spi_msg; - struct spi_transfer spi_xfer; - - /* Initialize SPI ,message */ - spi_message_init(&spi_msg); - - /* Initialize SPI transfer */ - memset(&spi_xfer, 0, sizeof spi_xfer); - spi_xfer.len = len; - spi_xfer.tx_buf = tx; - spi_xfer.rx_buf = rx; - spi_xfer.speed_hz = MAX3107_SPI_SPEED; - - /* Add SPI transfer to SPI message */ - spi_message_add_tail(&spi_xfer, &spi_msg); - -#ifdef DBG_TRACE_SPI_DATA - { - int i; - pr_info("tx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]); - pr_info("\n"); - } -#endif - - /* Perform synchronous SPI transfer */ - if (spi_sync(s->spi, &spi_msg)) { - dev_err(&s->spi->dev, "spi_sync failure\n"); - return -EIO; - } - -#ifdef DBG_TRACE_SPI_DATA - if (spi_xfer.rx_buf) { - int i; - pr_info("rx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]); - pr_info("\n"); - } -#endif - return 0; -} -EXPORT_SYMBOL_GPL(max3107_rw); - -/* Puts received data to circular buffer */ -static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, - int len) -{ - struct uart_port *port = &s->port; - struct tty_struct *tty; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Insert received data */ - tty_insert_flip_string(tty, data, len); - /* Update RX counter */ - port->icount.rx += len; -} - -/* Handle data receiving */ -static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) -{ - int i; - int j; - int len; /* SPI transfer buffer length */ - u16 *buf; - u8 *valid_str; - - if (!s->rx_enabled) - /* RX is disabled */ - return; - - if (rxlvl == 0) { - /* RX fifo is empty */ - return; - } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { - dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); - /* Ensure sanity of RX level */ - rxlvl = MAX3107_RX_FIFO_SIZE; - } - if ((s->rxbuf == 0) || (s->rxstr == 0)) { - dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n"); - return; - } - buf = s->rxbuf; - valid_str = s->rxstr; - while (rxlvl) { - pr_debug("rxlvl %d\n", rxlvl); - /* Clear buffer */ - memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2)); - len = 0; - if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { - /* First disable RX FIFO interrupt */ - pr_debug("Disabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - len++; - } - /* Just increase the length by amount of words in FIFO since - * buffer was zeroed and SPI transfer of 0x0000 means reading - * from RX FIFO - */ - len += rxlvl; - /* Append RX level query */ - buf[len] = MAX3107_RXFIFOLVL_REG; - len++; - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) { - dev_err(&s->spi->dev, "SPI transfer for RX h failed\n"); - return; - } - - /* Skip RX FIFO interrupt disabling word if it was added */ - j = ((len - 1) - rxlvl); - /* Read received words */ - for (i = 0; i < rxlvl; i++, j++) - valid_str[i] = (u8)buf[j]; - put_data_to_circ_buf(s, valid_str, rxlvl); - /* Get new RX level */ - rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK); - } - - if (s->rx_enabled) { - /* RX still enabled, re-enable RX FIFO interrupt */ - pr_debug("Enabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n"); - } - - /* Push the received data to receivers */ - if (s->port.state->port.tty) - tty_flip_buffer_push(s->port.state->port.tty); -} - - -/* Handle data sending */ -static void max3107_handletx(struct max3107_port *s) -{ - struct circ_buf *xmit = &s->port.state->xmit; - int i; - unsigned long flags; - int len; /* SPI transfer buffer length */ - u16 *buf; - - if (!s->tx_fifo_empty) - /* Don't send more data before previous data is sent */ - return; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) - /* No data to send or TX is stopped */ - return; - - if (!s->txbuf) { - dev_warn(&s->spi->dev, "Txbuf isn't ready\n"); - return; - } - buf = s->txbuf; - /* Get length of data pending in circular buffer */ - len = uart_circ_chars_pending(xmit); - if (len) { - /* Limit to size of TX FIFO */ - if (len > MAX3107_TX_FIFO_SIZE) - len = MAX3107_TX_FIFO_SIZE; - - pr_debug("txlen %d\n", len); - - /* Update TX counter */ - s->port.icount.tx += len; - - /* TX FIFO will no longer be empty */ - s->tx_fifo_empty = 0; - - i = 0; - if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { - /* First disable TX empty interrupt */ - pr_debug("Disabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - /* Add data to send */ - spin_lock_irqsave(&s->port.lock, flags); - for ( ; i < len ; i++) { - buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); - buf[i] |= ((u16)xmit->buf[xmit->tail] & - MAX3107_SPI_TX_DATA_MASK); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } - spin_unlock_irqrestore(&s->port.lock, flags); - if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { - /* Enable TX empty interrupt */ - pr_debug("Enabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - if (!s->tx_enabled) { - /* Enable TX */ - pr_debug("Enable TX\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; - buf[i] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - s->tx_enabled = 1; - i++; - len++; - } - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { - dev_err(&s->spi->dev, - "SPI transfer TX handling failed\n"); - return; - } - } - - /* Indicate wake up if circular buffer is getting low on data */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); - -} - -/* Handle interrupts - * Also reads and returns current RX FIFO level - */ -static u16 handle_interrupt(struct max3107_port *s) -{ - u16 buf[4]; /* Buffer for SPI transfers */ - u8 irq_status; - u16 rx_level; - unsigned long flags; - - /* Read IRQ status register */ - buf[0] = MAX3107_IRQSTS_REG; - /* Read status IRQ status register */ - buf[1] = MAX3107_STS_IRQSTS_REG; - /* Read LSR IRQ status register */ - buf[2] = MAX3107_LSR_IRQSTS_REG; - /* Query RX level */ - buf[3] = MAX3107_RXFIFOLVL_REG; - - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { - dev_err(&s->spi->dev, - "SPI transfer for INTR handling failed\n"); - return 0; - } - - irq_status = (u8)buf[0]; - pr_debug("IRQSTS %x\n", irq_status); - rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); - - if (irq_status & MAX3107_IRQ_LSR_BIT) { - /* LSR interrupt */ - if (buf[2] & MAX3107_LSR_RXTO_BIT) - /* RX timeout interrupt, - * handled by normal RX handling - */ - pr_debug("RX TO INT\n"); - } - - if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { - /* Tx empty interrupt, - * disable TX and set tx_fifo_empty flag - */ - pr_debug("TE INT, disabling TX\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer TX dis failed\n"); - s->tx_enabled = 0; - s->tx_fifo_empty = 1; - } - - if (irq_status & MAX3107_IRQ_RXFIFO_BIT) - /* RX FIFO interrupt, - * handled by normal RX handling - */ - pr_debug("RFIFO INT\n"); - - /* Return RX level */ - return rx_level; -} - -/* Trigger work thread*/ -static void max3107_dowork(struct max3107_port *s) -{ - if (!work_pending(&s->work) && !freezing(current) && !s->suspended) - queue_work(s->workqueue, &s->work); - else - dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n"); -} - -/* Work thread */ -static void max3107_work(struct work_struct *w) -{ - struct max3107_port *s = container_of(w, struct max3107_port, work); - u16 rxlvl = 0; - int len; /* SPI transfer buffer length */ - u16 buf[5]; /* Buffer for SPI transfers */ - unsigned long flags; - - /* Start by reading current RX FIFO level */ - buf[0] = MAX3107_RXFIFOLVL_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer RX lev failed\n"); - rxlvl = 0; - } else { - rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); - } - - do { - pr_debug("rxlvl %d\n", rxlvl); - - /* Handle RX */ - max3107_handlerx(s, rxlvl); - rxlvl = 0; - - if (s->handle_irq) { - /* Handle pending interrupts - * We also get new RX FIFO level since new data may - * have been received while pushing received data to - * receivers - */ - s->handle_irq = 0; - rxlvl = handle_interrupt(s); - } - - /* Handle TX */ - max3107_handletx(s); - - /* Handle configuration changes */ - len = 0; - spin_lock_irqsave(&s->data_lock, flags); - if (s->mode1_commit) { - pr_debug("mode1_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - buf[len++] |= s->mode1_reg; - s->mode1_commit = 0; - } - if (s->lcr_commit) { - pr_debug("lcr_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); - buf[len++] |= s->lcr_reg; - s->lcr_commit = 0; - } - if (s->brg_commit) { - pr_debug("brg_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); - buf[len++] |= ((s->brg_cfg >> 16) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); - buf[len++] |= ((s->brg_cfg >> 8) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); - buf[len++] |= ((s->brg_cfg) & 0xff); - s->brg_commit = 0; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - if (len > 0) { - if (max3107_rw(s, (u8 *)buf, NULL, len * 2)) - dev_err(&s->spi->dev, - "SPI transfer config failed\n"); - } - - /* Reloop if interrupt handling indicated data in RX FIFO */ - } while (rxlvl); - -} - -/* Set sleep mode */ -static void max3107_set_sleep(struct max3107_port *s, int mode) -{ - u16 buf[1]; /* Buffer for SPI transfer */ - unsigned long flags; - pr_debug("enter, mode %d\n", mode); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - switch (mode) { - case MAX3107_DISABLE_FORCED_SLEEP: - s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_ENABLE_FORCED_SLEEP: - s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_DISABLE_AUTOSLEEP: - s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; - break; - case MAX3107_ENABLE_AUTOSLEEP: - s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; - break; - default: - spin_unlock_irqrestore(&s->data_lock, flags); - dev_warn(&s->spi->dev, "invalid sleep mode\n"); - return; - } - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n"); - - if (mode == MAX3107_DISABLE_AUTOSLEEP || - mode == MAX3107_DISABLE_FORCED_SLEEP) - msleep(MAX3107_WAKEUP_DELAY); -} - -/* Perform full register initialization */ -static void max3107_register_init(struct max3107_port *s) -{ - u16 buf[11]; /* Buffer for SPI transfers */ - - /* 1. Configure baud rate, 9600 as default */ - s->baud = 9600; - /* the below is default*/ - if (s->ext_clk) { - s->brg_cfg = MAX3107_BRG26_B9600; - s->baud_tbl = (struct baud_table *)brg26_ext; - } else { - s->brg_cfg = MAX3107_BRG13_IB9600; - s->baud_tbl = (struct baud_table *)brg13_int; - } - - if (s->pdata->init) - s->pdata->init(s); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG) - | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); - buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG) - | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); - buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG) - | ((s->brg_cfg) & 0xff); - - /* 2. Configure LCR register, 8N1 mode by default */ - s->lcr_reg = MAX3107_LCR_WORD_LEN_8; - buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG) - | s->lcr_reg; - - /* 3. Configure MODE 1 register */ - s->mode1_reg = 0; - /* Enable IRQ pin */ - s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; - /* Disable TX */ - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - s->tx_enabled = 0; - /* RX is enabled */ - s->rx_enabled = 1; - buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG) - | s->mode1_reg; - - /* 4. Configure MODE 2 register */ - buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Enable loopback */ - buf[5] |= MAX3107_MODE2_LOOPBACK_BIT; - } - /* Reset FIFOs */ - buf[5] |= MAX3107_MODE2_FIFORST_BIT; - s->tx_fifo_empty = 1; - - /* 5. Configure FIFO trigger level register */ - buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); - /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ - buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); - - /* 6. Configure flow control levels */ - buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); - /* Flow control halt level 96, resume level 48 */ - buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); - - /* 7. Configure flow control */ - buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); - /* Enable auto CTS and auto RTS flow control */ - buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT); - - /* 8. Configure RX timeout register */ - buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); - /* Timeout after 48 character intervals */ - buf[9] |= 0x0030; - - /* 9. Configure LSR interrupt enable register */ - buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); - /* Enable RX timeout interrupt */ - buf[10] |= MAX3107_LSR_RXTO_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 22)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - - /* 10. Clear IRQ status register by reading it */ - buf[0] = MAX3107_IRQSTS_REG; - - /* 11. Configure interrupt enable register */ - /* Enable LSR interrupt */ - s->irqen_reg = MAX3107_IRQ_LSR_BIT; - /* Enable RX FIFO interrupt */ - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG) - | s->irqen_reg; - - /* 12. Clear FIFO reset that was set in step 6 */ - buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Keep loopback enabled */ - buf[2] |= MAX3107_MODE2_LOOPBACK_BIT; - } - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - -} - -/* IRQ handler */ -static irqreturn_t max3107_irq(int irqno, void *dev_id) -{ - struct max3107_port *s = dev_id; - - if (irqno != s->spi->irq) { - /* Unexpected IRQ */ - return IRQ_NONE; - } - - /* Indicate irq */ - s->handle_irq = 1; - - /* Trigger work thread */ - max3107_dowork(s); - - return IRQ_HANDLED; -} - -/* HW suspension function - * - * Currently autosleep is used to decrease current consumption, alternative - * approach would be to set the chip to reset mode if UART is not being - * used but that would mess the GPIOs - * - */ -void max3107_hw_susp(struct max3107_port *s, int suspend) -{ - pr_debug("enter, suspend %d\n", suspend); - - if (suspend) { - /* Suspend requested, - * enable autosleep to decrease current consumption - */ - s->suspended = 1; - max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); - } else { - /* Resume requested, - * disable autosleep - */ - s->suspended = 0; - max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); - } -} -EXPORT_SYMBOL_GPL(max3107_hw_susp); - -/* Modem status IRQ enabling */ -static void max3107_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ -} - -/* Data send function */ -static void max3107_start_tx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Trigger work thread for sending data */ - max3107_dowork(s); -} - -/* Function for checking that there is no pending transfers */ -static unsigned int max3107_tx_empty(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - pr_debug("returning %d\n", - (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); - return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit); -} - -/* Function for stopping RX */ -static void max3107_stop_rx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - unsigned long flags; - - /* Set RX disabled in MODE 1 register */ - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; - s->mode1_commit = 1; - spin_unlock_irqrestore(&s->data_lock, flags); - /* Set RX disabled */ - s->rx_enabled = 0; - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Function for returning control pin states */ -static unsigned int max3107_get_mctrl(struct uart_port *port) -{ - /* DCD and DSR are not wired and CTS/RTS is handled automatically - * so just indicate DSR and CAR asserted - */ - return TIOCM_DSR | TIOCM_CAR; -} - -/* Function for setting control pin states */ -static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* DCD and DSR are not wired and CTS/RTS is hadnled automatically - * so do nothing - */ -} - -/* Function for configuring UART parameters */ -static void max3107_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - struct tty_struct *tty; - int baud; - u16 new_lcr = 0; - u32 new_brg = 0; - unsigned long flags; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Get new LCR register values */ - /* Word size */ - if ((termios->c_cflag & CSIZE) == CS7) - new_lcr |= MAX3107_LCR_WORD_LEN_7; - else - new_lcr |= MAX3107_LCR_WORD_LEN_8; - - /* Parity */ - if (termios->c_cflag & PARENB) { - new_lcr |= MAX3107_LCR_PARITY_BIT; - if (!(termios->c_cflag & PARODD)) - new_lcr |= MAX3107_LCR_EVENPARITY_BIT; - } - - /* Stop bits */ - if (termios->c_cflag & CSTOPB) { - /* 2 stop bits */ - new_lcr |= MAX3107_LCR_STOPLEN_BIT; - } - - /* Mask termios capabilities we don't support */ - termios->c_cflag &= ~CMSPAR; - - /* Set status ignore mask */ - s->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; - - /* Set low latency to immediately handle pushed data */ - s->port.state->port.tty->low_latency = 1; - - /* Get new baud rate generator configuration */ - baud = tty_get_baud_rate(tty); - - spin_lock_irqsave(&s->data_lock, flags); - new_brg = get_new_brg(baud, s); - /* if can't find the corrent config, use previous */ - if (!new_brg) { - baud = s->baud; - new_brg = s->brg_cfg; - } - spin_unlock_irqrestore(&s->data_lock, flags); - tty_termios_encode_baud_rate(termios, baud, baud); - s->baud = baud; - - /* Update timeout according to new baud rate */ - uart_update_timeout(port, termios->c_cflag, baud); - - spin_lock_irqsave(&s->data_lock, flags); - if (s->lcr_reg != new_lcr) { - s->lcr_reg = new_lcr; - s->lcr_commit = 1; - } - if (s->brg_cfg != new_brg) { - s->brg_cfg = new_brg; - s->brg_commit = 1; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Port shutdown function */ -static void max3107_shutdown(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - if (s->suspended && s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Free the interrupt */ - free_irq(s->spi->irq, s); - - if (s->workqueue) { - /* Flush and destroy work queue */ - flush_workqueue(s->workqueue); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - } - - /* Suspend HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -} - -/* Port startup function */ -static int max3107_startup(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Initialize work queue */ - s->workqueue = create_freezable_workqueue("max3107"); - if (!s->workqueue) { - dev_err(&s->spi->dev, "Workqueue creation failed\n"); - return -EBUSY; - } - INIT_WORK(&s->work, max3107_work); - - /* Setup IRQ */ - if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, - "max3107", s)) { - dev_err(&s->spi->dev, "IRQ reguest failed\n"); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - return -EBUSY; - } - - /* Resume HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Init registers */ - max3107_register_init(s); - - return 0; -} - -/* Port type function */ -static const char *max3107_type(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - return s->spi->modalias; -} - -/* Port release function */ -static void max3107_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port request function */ -static int max3107_request_port(struct uart_port *port) -{ - /* Do nothing */ - return 0; -} - -/* Port config function */ -static void max3107_config_port(struct uart_port *port, int flags) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - s->port.type = PORT_MAX3107; -} - -/* Port verify function */ -static int max3107_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107) - return 0; - - return -EINVAL; -} - -/* Port stop TX function */ -static void max3107_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port break control function */ -static void max3107_break_ctl(struct uart_port *port, int break_state) -{ - /* We don't support break control, do nothing */ -} - - -/* Port functions */ -static struct uart_ops max3107_ops = { - .tx_empty = max3107_tx_empty, - .set_mctrl = max3107_set_mctrl, - .get_mctrl = max3107_get_mctrl, - .stop_tx = max3107_stop_tx, - .start_tx = max3107_start_tx, - .stop_rx = max3107_stop_rx, - .enable_ms = max3107_enable_ms, - .break_ctl = max3107_break_ctl, - .startup = max3107_startup, - .shutdown = max3107_shutdown, - .set_termios = max3107_set_termios, - .type = max3107_type, - .release_port = max3107_release_port, - .request_port = max3107_request_port, - .config_port = max3107_config_port, - .verify_port = max3107_verify_port, -}; - -/* UART driver data */ -static struct uart_driver max3107_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "ttyMAX", - .dev_name = "ttyMAX", - .nr = 1, -}; - -static int driver_registered = 0; - - - -/* 'Generic' platform data */ -static struct max3107_plat generic_plat_data = { - .loopback = 0, - .ext_clk = 1, - .hw_suspend = max3107_hw_susp, - .polled_mode = 0, - .poll_time = 0, -}; - - -/*******************************************************************/ - -/** - * max3107_probe - SPI bus probe entry point - * @spi: the spi device - * - * SPI wants us to probe this device and if appropriate claim it. - * Perform any platform specific requirements and then initialise - * the device. - */ - -int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata) -{ - struct max3107_port *s; - u16 buf[2]; /* Buffer for SPI transfers */ - int retval; - - pr_info("enter max3107 probe\n"); - - /* Allocate port structure */ - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - pr_err("Allocating port structure failed\n"); - return -ENOMEM; - } - - s->pdata = pdata; - - /* SPI Rx buffer - * +2 for RX FIFO interrupt - * disabling and RX level query - */ - s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL); - if (!s->rxbuf) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free4; - } - s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL); - if (!s->rxstr) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free3; - } - /* SPI Tx buffer - * SPI transfer buffer - * +3 for TX FIFO empty - * interrupt disabling and - * enabling and TX enabling - */ - s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL); - if (!s->txbuf) { - pr_err("Allocating TX buffer failed\n"); - retval = -ENOMEM; - goto err_free2; - } - /* Initialize shared data lock */ - spin_lock_init(&s->data_lock); - - /* SPI intializations */ - dev_set_drvdata(&spi->dev, s); - spi->mode = SPI_MODE_0; - spi->dev.platform_data = pdata; - spi->bits_per_word = 16; - s->ext_clk = pdata->ext_clk; - s->loopback = pdata->loopback; - spi_setup(spi); - s->spi = spi; - - /* Check REV ID to ensure we are talking to what we expect */ - buf[0] = MAX3107_REVID_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); - retval = -EIO; - goto err_free1; - } - if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && - (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { - dev_err(&s->spi->dev, "REVID %x does not match\n", - (buf[0] & MAX3107_SPI_RX_DATA_MASK)); - retval = -ENODEV; - goto err_free1; - } - - /* Disable all interrupts */ - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); - buf[0] |= 0x0000; - - /* Configure clock source */ - buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); - if (s->ext_clk) { - /* External clock */ - buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; - } - - /* PLL bypass ON */ - buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 4)) { - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - retval = -EIO; - goto err_free1; - } - - /* Register UART driver */ - if (!driver_registered) { - retval = uart_register_driver(&max3107_uart_driver); - if (retval) { - dev_err(&s->spi->dev, "Registering UART driver failed\n"); - goto err_free1; - } - driver_registered = 1; - } - - /* Initialize UART port data */ - s->port.fifosize = 128; - s->port.ops = &max3107_ops; - s->port.line = 0; - s->port.dev = &spi->dev; - s->port.uartclk = 9600; - s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - s->port.irq = s->spi->irq; - s->port.type = PORT_MAX3107; - - /* Add UART port */ - retval = uart_add_one_port(&max3107_uart_driver, &s->port); - if (retval < 0) { - dev_err(&s->spi->dev, "Adding UART port failed\n"); - goto err_free1; - } - - if (pdata->configure) { - retval = pdata->configure(s); - if (retval < 0) - goto err_free1; - } - - /* Go to suspend mode */ - if (pdata->hw_suspend) - pdata->hw_suspend(s, 1); - - return 0; - -err_free1: - kfree(s->txbuf); -err_free2: - kfree(s->rxstr); -err_free3: - kfree(s->rxbuf); -err_free4: - kfree(s); - return retval; -} -EXPORT_SYMBOL_GPL(max3107_probe); - -/* Driver remove function */ -int max3107_remove(struct spi_device *spi) -{ - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_info("enter max3107 remove\n"); - - /* Remove port */ - if (uart_remove_one_port(&max3107_uart_driver, &s->port)) - dev_warn(&s->spi->dev, "Removing UART port failed\n"); - - - /* Free TxRx buffer */ - kfree(s->rxbuf); - kfree(s->rxstr); - kfree(s->txbuf); - - /* Free port structure */ - kfree(s); - - return 0; -} -EXPORT_SYMBOL_GPL(max3107_remove); - -/* Driver suspend function */ -int max3107_suspend(struct spi_device *spi, pm_message_t state) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter suspend\n"); - - /* Suspend UART port */ - uart_suspend_port(&max3107_uart_driver, &s->port); - - /* Go to suspend mode */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_suspend); - -/* Driver resume function */ -int max3107_resume(struct spi_device *spi) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter resume\n"); - - /* Resume from suspend */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Resume UART port */ - uart_resume_port(&max3107_uart_driver, &s->port); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_resume); - -static int max3107_probe_generic(struct spi_device *spi) -{ - return max3107_probe(spi, &generic_plat_data); -} - -/* Spi driver data */ -static struct spi_driver max3107_driver = { - .driver = { - .name = "max3107", - .owner = THIS_MODULE, - }, - .probe = max3107_probe_generic, - .remove = __devexit_p(max3107_remove), - .suspend = max3107_suspend, - .resume = max3107_resume, -}; - -/* Driver init function */ -static int __init max3107_init(void) -{ - pr_info("enter max3107 init\n"); - return spi_register_driver(&max3107_driver); -} - -/* Driver exit function */ -static void __exit max3107_exit(void) -{ - pr_info("enter max3107 exit\n"); - /* Unregister UART driver */ - if (driver_registered) - uart_unregister_driver(&max3107_uart_driver); - spi_unregister_driver(&max3107_driver); -} - -module_init(max3107_init); -module_exit(max3107_exit); - -MODULE_DESCRIPTION("MAX3107 driver"); -MODULE_AUTHOR("Aavamobile"); -MODULE_ALIAS("spi:max3107"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/max3107.h b/drivers/tty/serial/max3107.h deleted file mode 100644 index 8415fc7..0000000 --- a/drivers/tty/serial/max3107.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - * max3107.h - spi uart protocol driver header for Maxim 3107 - * - * Copyright (C) Aavamobile 2009 - * Based on serial_max3100.h by Christian Pellegrin - * - * 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 of the License, or - * (at your option) any later version. - */ - -#ifndef _MAX3107_H -#define _MAX3107_H - -/* Serial error status definitions */ -#define MAX3107_PARITY_ERROR 1 -#define MAX3107_FRAME_ERROR 2 -#define MAX3107_OVERRUN_ERROR 4 -#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ - MAX3107_FRAME_ERROR | \ - MAX3107_OVERRUN_ERROR) - -/* GPIO definitions */ -#define MAX3107_GPIO_BASE 88 -#define MAX3107_GPIO_COUNT 4 - - -/* GPIO connected to chip's reset pin */ -#define MAX3107_RESET_GPIO 87 - - -/* Chip reset delay */ -#define MAX3107_RESET_DELAY 10 - -/* Chip wakeup delay */ -#define MAX3107_WAKEUP_DELAY 50 - - -/* Sleep mode definitions */ -#define MAX3107_DISABLE_FORCED_SLEEP 0 -#define MAX3107_ENABLE_FORCED_SLEEP 1 -#define MAX3107_DISABLE_AUTOSLEEP 2 -#define MAX3107_ENABLE_AUTOSLEEP 3 - - -/* Definitions for register access with SPI transfers - * - * SPI transfer format: - * - * Master to slave bits xzzzzzzzyyyyyyyy - * Slave to master bits aaaaaaaabbbbbbbb - * - * where: - * x = 0 for reads, 1 for writes - * z = register address - * y = new register value if write, 0 if read - * a = unspecified - * b = register value if read, unspecified if write - */ - -/* SPI speed */ -#define MAX3107_SPI_SPEED (3125000 * 2) - -/* Write bit */ -#define MAX3107_WRITE_BIT (1 << 15) - -/* SPI TX data mask */ -#define MAX3107_SPI_RX_DATA_MASK (0x00ff) - -/* SPI RX data mask */ -#define MAX3107_SPI_TX_DATA_MASK (0x00ff) - -/* Register access masks */ -#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ -#define MAX3107_THR_REG (0x0000) /* TX FIFO */ -#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ -#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ -#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ -#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ -#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ -#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ -#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ -#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ -#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ -#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ -#define MAX3107_LCR_REG (0x0b00) /* LCR */ -#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ -#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ -#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ -#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ -#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ -#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ -#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ -#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ -#define MAX3107_XON1_REG (0x1400) /* XON1 character */ -#define MAX3107_XON2_REG (0x1500) /* XON2 character */ -#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ -#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ -#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ -#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ -#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ -#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ -#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ -#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ -#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ -#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ - -/* IRQ register bits */ -#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ -#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ -#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ -#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ -#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ -#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ -#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ -#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ - -/* LSR register bits */ -#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ -#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ -#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ -#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ -#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ -#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ -#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ - -/* Special character register bits */ -#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ -#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ -#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ -#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ -#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ -#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ -#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Status register bits */ -#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ -#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ -#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ -#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ -#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ -#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ -#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ -#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* MODE1 register bits */ -#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ -#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ -#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ -#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ -#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ -#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ -#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ -#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ - -/* MODE2 register bits */ -#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ -#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ -#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ -#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ -#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ -#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ -#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ -#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ - -/* LCR register bits */ -#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ -#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 - * - * Word length bits table: - * 00 -> 5 bit words - * 01 -> 6 bit words - * 10 -> 7 bit words - * 11 -> 8 bit words - */ -#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit - * - * STOP length bit table: - * 0 -> 1 stop bit - * 1 -> 1-1.5 stop bits if - * word length is 5, - * 2 stop bits otherwise - */ -#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ -#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ -#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ -#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ -#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ -#define MAX3107_LCR_WORD_LEN_5 (0x0000) -#define MAX3107_LCR_WORD_LEN_6 (0x0001) -#define MAX3107_LCR_WORD_LEN_7 (0x0002) -#define MAX3107_LCR_WORD_LEN_8 (0x0003) - - -/* IRDA register bits */ -#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ -#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ -#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ -#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ -#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ -#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ -#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Flow control trigger level register masks */ -#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ -#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ -#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) -#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) - -/* FIFO interrupt trigger level register masks */ -#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) -#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) - -/* Flow control register bits */ -#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs - * are used in conjunction with - * XOFF2 for definition of - * special character */ -#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ -#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ -#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 - * - * SWFLOW bits 1 & 0 table: - * 00 -> no transmitter flow - * control - * 01 -> receiver compares - * XON2 and XOFF2 - * and controls - * transmitter - * 10 -> receiver compares - * XON1 and XOFF1 - * and controls - * transmitter - * 11 -> receiver compares - * XON1, XON2, XOFF1 and - * XOFF2 and controls - * transmitter - */ -#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ -#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 - * - * SWFLOW bits 3 & 2 table: - * 00 -> no received flow - * control - * 01 -> transmitter generates - * XON2 and XOFF2 - * 10 -> transmitter generates - * XON1 and XOFF1 - * 11 -> transmitter generates - * XON1, XON2, XOFF1 and - * XOFF2 - */ - -/* GPIO configuration register bits */ -#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ -#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ -#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ -#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ -#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ -#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ -#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ -#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ - -/* GPIO DATA register bits */ -#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ -#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ -#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ -#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ -#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ -#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ -#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ -#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ - -/* PLL configuration register masks */ -#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ -#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ - -/* Baud rate generator configuration register masks and bits */ -#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of - * Baud rate generator divisor - */ -#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ -#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ -#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Clock source register bits */ -#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ -#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ -#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ -#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ -#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ -#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ -#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ - - -/* HW definitions */ -#define MAX3107_RX_FIFO_SIZE 128 -#define MAX3107_TX_FIFO_SIZE 128 -#define MAX3107_REVID1 0x00a0 -#define MAX3107_REVID2 0x00a1 - - -/* Baud rate generator configuration values for external clock 13MHz */ -#define MAX3107_BRG13_B300 (0x0A9400 | 0x05) -#define MAX3107_BRG13_B600 (0x054A00 | 0x03) -#define MAX3107_BRG13_B1200 (0x02A500 | 0x01) -#define MAX3107_BRG13_B2400 (0x015200 | 0x09) -#define MAX3107_BRG13_B4800 (0x00A900 | 0x04) -#define MAX3107_BRG13_B9600 (0x005400 | 0x0A) -#define MAX3107_BRG13_B19200 (0x002A00 | 0x05) -#define MAX3107_BRG13_B38400 (0x001500 | 0x03) -#define MAX3107_BRG13_B57600 (0x000E00 | 0x02) -#define MAX3107_BRG13_B115200 (0x000700 | 0x01) -#define MAX3107_BRG13_B230400 (0x000300 | 0x08) -#define MAX3107_BRG13_B460800 (0x000100 | 0x0c) -#define MAX3107_BRG13_B921600 (0x000100 | 0x1c) - -/* Baud rate generator configuration values for external clock 26MHz */ -#define MAX3107_BRG26_B300 (0x152800 | 0x0A) -#define MAX3107_BRG26_B600 (0x0A9400 | 0x05) -#define MAX3107_BRG26_B1200 (0x054A00 | 0x03) -#define MAX3107_BRG26_B2400 (0x02A500 | 0x01) -#define MAX3107_BRG26_B4800 (0x015200 | 0x09) -#define MAX3107_BRG26_B9600 (0x00A900 | 0x04) -#define MAX3107_BRG26_B19200 (0x005400 | 0x0A) -#define MAX3107_BRG26_B38400 (0x002A00 | 0x05) -#define MAX3107_BRG26_B57600 (0x001C00 | 0x03) -#define MAX3107_BRG26_B115200 (0x000E00 | 0x02) -#define MAX3107_BRG26_B230400 (0x000700 | 0x01) -#define MAX3107_BRG26_B460800 (0x000300 | 0x08) -#define MAX3107_BRG26_B921600 (0x000100 | 0x0C) - -/* Baud rate generator configuration values for internal clock */ -#define MAX3107_BRG13_IB300 (0x008000 | 0x00) -#define MAX3107_BRG13_IB600 (0x004000 | 0x00) -#define MAX3107_BRG13_IB1200 (0x002000 | 0x00) -#define MAX3107_BRG13_IB2400 (0x001000 | 0x00) -#define MAX3107_BRG13_IB4800 (0x000800 | 0x00) -#define MAX3107_BRG13_IB9600 (0x000400 | 0x00) -#define MAX3107_BRG13_IB19200 (0x000200 | 0x00) -#define MAX3107_BRG13_IB38400 (0x000100 | 0x00) -#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B) -#define MAX3107_BRG13_IB115200 (0x000000 | 0x05) -#define MAX3107_BRG13_IB230400 (0x000000 | 0x03) -#define MAX3107_BRG13_IB460800 (0x000000 | 0x00) -#define MAX3107_BRG13_IB921600 (0x000000 | 0x00) - - -struct baud_table { - int baud; - u32 new_brg; -}; - -struct max3107_port { - /* UART port structure */ - struct uart_port port; - - /* SPI device structure */ - struct spi_device *spi; - -#if defined(CONFIG_GPIOLIB) - /* GPIO chip structure */ - struct gpio_chip chip; -#endif - - /* Workqueue that does all the magic */ - struct workqueue_struct *workqueue; - struct work_struct work; - - /* Lock for shared data */ - spinlock_t data_lock; - - /* Device configuration */ - int ext_clk; /* 1 if external clock used */ - int loopback; /* Current loopback mode state */ - int baud; /* Current baud rate */ - - /* State flags */ - int suspended; /* Indicates suspend mode */ - int tx_fifo_empty; /* Flag for TX FIFO state */ - int rx_enabled; /* Flag for receiver state */ - int tx_enabled; /* Flag for transmitter state */ - - u16 irqen_reg; /* Current IRQ enable register value */ - /* Shared data */ - u16 mode1_reg; /* Current mode1 register value*/ - int mode1_commit; /* Flag for setting new mode1 register value */ - u16 lcr_reg; /* Current LCR register value */ - int lcr_commit; /* Flag for setting new LCR register value */ - u32 brg_cfg; /* Current Baud rate generator config */ - int brg_commit; /* Flag for setting new baud rate generator - * config - */ - struct baud_table *baud_tbl; - int handle_irq; /* Indicates that IRQ should be handled */ - - /* Rx buffer and str*/ - u16 *rxbuf; - u8 *rxstr; - /* Tx buffer*/ - u16 *txbuf; - - struct max3107_plat *pdata; /* Platform data */ -}; - -/* Platform data structure */ -struct max3107_plat { - /* Loopback mode enable */ - int loopback; - /* External clock enable */ - int ext_clk; - /* Called during the register initialisation */ - void (*init)(struct max3107_port *s); - /* Called when the port is found and configured */ - int (*configure)(struct max3107_port *s); - /* HW suspend function */ - void (*hw_suspend) (struct max3107_port *s, int suspend); - /* Polling mode enable */ - int polled_mode; - /* Polling period if polling mode enabled */ - int poll_time; -}; - -extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len); -extern void max3107_hw_susp(struct max3107_port *s, int suspend); -extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata); -extern int max3107_remove(struct spi_device *spi); -extern int max3107_suspend(struct spi_device *spi, pm_message_t state); -extern int max3107_resume(struct spi_device *spi); - -#endif /* _LINUX_SERIAL_MAX3107_H */ diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c new file mode 100644 index 0000000..2bc28a5 --- /dev/null +++ b/drivers/tty/serial/max310x.c @@ -0,0 +1,1260 @@ +/* + * Maxim (Dallas) MAX3107/8 serial driver + * + * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * + * Based on max3100.c, by Christian Pellegrin <chripell@evolware.org> + * Based on max3110.c, by Feng Tang <feng.tang@intel.com> + * Based on max3107.c, by Aavamobile + * + * 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 of the License, or + * (at your option) any later version. + */ + +/* TODO: MAX3109 support (Dual) */ +/* TODO: MAX14830 support (Quad) */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/regmap.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/platform_data/max310x.h> + +#define MAX310X_MAJOR 204 +#define MAX310X_MINOR 209 + +/* MAX310X register definitions */ +#define MAX310X_RHR_REG (0x00) /* RX FIFO */ +#define MAX310X_THR_REG (0x00) /* TX FIFO */ +#define MAX310X_IRQEN_REG (0x01) /* IRQ enable */ +#define MAX310X_IRQSTS_REG (0x02) /* IRQ status */ +#define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */ +#define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */ +#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */ +#define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */ +#define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */ +#define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */ +#define MAX310X_MODE1_REG (0x09) /* MODE1 */ +#define MAX310X_MODE2_REG (0x0a) /* MODE2 */ +#define MAX310X_LCR_REG (0x0b) /* LCR */ +#define MAX310X_RXTO_REG (0x0c) /* RX timeout */ +#define MAX310X_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */ +#define MAX310X_IRDA_REG (0x0e) /* IRDA settings */ +#define MAX310X_FLOWLVL_REG (0x0f) /* Flow control levels */ +#define MAX310X_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */ +#define MAX310X_TXFIFOLVL_REG (0x11) /* TX FIFO level */ +#define MAX310X_RXFIFOLVL_REG (0x12) /* RX FIFO level */ +#define MAX310X_FLOWCTRL_REG (0x13) /* Flow control */ +#define MAX310X_XON1_REG (0x14) /* XON1 character */ +#define MAX310X_XON2_REG (0x15) /* XON2 character */ +#define MAX310X_XOFF1_REG (0x16) /* XOFF1 character */ +#define MAX310X_XOFF2_REG (0x17) /* XOFF2 character */ +#define MAX310X_GPIOCFG_REG (0x18) /* GPIO config */ +#define MAX310X_GPIODATA_REG (0x19) /* GPIO data */ +#define MAX310X_PLLCFG_REG (0x1a) /* PLL config */ +#define MAX310X_BRGCFG_REG (0x1b) /* Baud rate generator conf */ +#define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */ +#define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ +#define MAX310X_CLKSRC_REG (0x1e) /* Clock source */ +/* Only present in MAX3107 */ +#define MAX3107_REVID_REG (0x1f) /* Revision identification */ + +/* IRQ register bits */ +#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX310X_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX310X_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX310X_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX310X_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX310X_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX310X_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX310X_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX310X_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX310X_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX310X_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX310X_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX310X_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX310X_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX310X_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX310X_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX310X_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX310X_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX310X_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX310X_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX310X_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ + +/* Status register bits */ +#define MAX310X_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX310X_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX310X_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX310X_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX310X_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX310X_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ + +/* MODE1 register bits */ +#define MAX310X_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX310X_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX310X_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX310X_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX310X_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX310X_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX310X_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX310X_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX310X_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX310X_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX310X_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX310X_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX310X_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX310X_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX310X_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX310X_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX310X_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX310X_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX310X_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX310X_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX310X_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX310X_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX310X_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX310X_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX310X_LCR_WORD_LEN_5 (0x00) +#define MAX310X_LCR_WORD_LEN_6 (0x01) +#define MAX310X_LCR_WORD_LEN_7 (0x02) +#define MAX310X_LCR_WORD_LEN_8 (0x03) + +/* IRDA register bits */ +#define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX310X_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX310X_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX310X_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX310X_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ + +/* Flow control trigger level register masks */ +#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX310X_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX310X_FLOWLVL_HALT(words) ((words / 8) & 0x0f) +#define MAX310X_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX310X_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f) +#define MAX310X_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4) + +/* Flow control register bits */ +#define MAX310X_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX310X_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX310X_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX310X_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX310X_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX310X_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX310X_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX310X_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX310X_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX310X_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX310X_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX310X_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX310X_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX310X_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX310X_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX310X_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX310X_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX310X_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX310X_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX310X_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX310X_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX310X_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX310X_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX310X_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */ +#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register bits */ +#define MAX310X_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX310X_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ + +/* Clock source register bits */ +#define MAX310X_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX310X_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX310X_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + +/* Misc definitions */ +#define MAX310X_FIFO_SIZE (128) + +/* MAX3107 specific */ +#define MAX3107_REV_ID (0xa0) +#define MAX3107_REV_MASK (0xfe) + +/* IRQ status bits definitions */ +#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \ + MAX310X_IRQ_TXEMPTY_BIT) +#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \ + MAX310X_IRQ_RXEMPTY_BIT) + +/* Supported chip types */ +enum { + MAX310X_TYPE_MAX3107 = 3107, + MAX310X_TYPE_MAX3108 = 3108, +}; + +struct max310x_port { + struct uart_driver uart; + struct uart_port port; + + const char *name; + int uartclk; + + unsigned int nr_gpio; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; +#endif + + struct regmap *regmap; + struct regmap_config regcfg; + + struct workqueue_struct *wq; + struct work_struct tx_work; + + struct mutex max310x_mutex; + + struct max310x_pdata *pdata; +}; + +static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX310X_IRQSTS_REG: + case MAX310X_LSR_IRQSTS_REG: + case MAX310X_SPCHR_IRQSTS_REG: + case MAX310X_STS_IRQSTS_REG: + case MAX310X_TXFIFOLVL_REG: + case MAX310X_RXFIFOLVL_REG: + case MAX3107_REVID_REG: /* Only available on MAX3107 */ + return false; + default: + break; + } + + return true; +} + +static bool max310x_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX310X_RHR_REG: + case MAX310X_IRQSTS_REG: + case MAX310X_LSR_IRQSTS_REG: + case MAX310X_SPCHR_IRQSTS_REG: + case MAX310X_STS_IRQSTS_REG: + case MAX310X_TXFIFOLVL_REG: + case MAX310X_RXFIFOLVL_REG: + case MAX310X_GPIODATA_REG: + return true; + default: + break; + } + + return false; +} + +static bool max310x_reg_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX310X_RHR_REG: + case MAX310X_IRQSTS_REG: + case MAX310X_SPCHR_IRQSTS_REG: + case MAX310X_STS_IRQSTS_REG: + return true; + default: + break; + } + + return false; +} + +static void max310x_set_baud(struct max310x_port *s, int baud) +{ + unsigned int mode = 0, div = s->uartclk / baud; + + if (!(div / 16)) { + /* Mode x2 */ + mode = MAX310X_BRGCFG_2XMODE_BIT; + div = (s->uartclk * 2) / baud; + } + + if (!(div / 16)) { + /* Mode x4 */ + mode = MAX310X_BRGCFG_4XMODE_BIT; + div = (s->uartclk * 4) / baud; + } + + regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG, + ((div / 16) >> 8) & 0xff); + regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff); + regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode); +} + +static void max310x_wait_pll(struct max310x_port *s) +{ + int tryes = 1000; + + /* Wait for PLL only if crystal is used */ + if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) { + unsigned int sts = 0; + + while (tryes--) { + regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts); + if (sts & MAX310X_STS_CLKREADY_BIT) + break; + } + } +} + +static int __devinit max310x_update_best_err(unsigned long f, long *besterr) +{ + /* Use baudrate 115200 for calculate error */ + long err = f % (115200 * 16); + + if ((*besterr < 0) || (*besterr > err)) { + *besterr = err; + return 0; + } + + return 1; +} + +static int __devinit max310x_set_ref_clk(struct max310x_port *s) +{ + unsigned int div, clksrc, pllcfg = 0; + long besterr = -1; + unsigned long fdiv, fmul, bestfreq = s->pdata->frequency; + + /* First, update error without PLL */ + max310x_update_best_err(s->pdata->frequency, &besterr); + + /* Try all possible PLL dividers */ + for (div = 1; (div <= 63) && besterr; div++) { + fdiv = DIV_ROUND_CLOSEST(s->pdata->frequency, div); + + /* Try multiplier 6 */ + fmul = fdiv * 6; + if ((fdiv >= 500000) && (fdiv <= 800000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (0 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 48 */ + fmul = fdiv * 48; + if ((fdiv >= 850000) && (fdiv <= 1200000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (1 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 96 */ + fmul = fdiv * 96; + if ((fdiv >= 425000) && (fdiv <= 1000000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (2 << 6) | div; + bestfreq = fmul; + } + /* Try multiplier 144 */ + fmul = fdiv * 144; + if ((fdiv >= 390000) && (fdiv <= 667000)) + if (!max310x_update_best_err(fmul, &besterr)) { + pllcfg = (3 << 6) | div; + bestfreq = fmul; + } + } + + /* Configure clock source */ + if (s->pdata->driver_flags & MAX310X_EXT_CLK) + clksrc = MAX310X_CLKSRC_EXTCLK_BIT; + else + clksrc = MAX310X_CLKSRC_CRYST_BIT; + + /* Configure PLL */ + if (pllcfg) { + clksrc |= MAX310X_CLKSRC_PLL_BIT; + regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg); + } else + clksrc |= MAX310X_CLKSRC_PLLBYP_BIT; + + regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc); + + if (pllcfg) + max310x_wait_pll(s); + + dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq); + + return (int)bestfreq; +} + +static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) +{ + unsigned int sts = 0, ch = 0, flag; + struct tty_struct *tty = tty_port_tty_get(&s->port.state->port); + + if (!tty) + return; + + if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) { + dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + /* Ensure sanity of RX level */ + rxlen = MAX310X_FIFO_SIZE; + } + + dev_dbg(s->port.dev, "RX Len = %u\n", rxlen); + + while (rxlen--) { + regmap_read(s->regmap, MAX310X_RHR_REG, &ch); + regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts); + + sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT | + MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT; + + s->port.icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(sts)) { + if (sts & MAX310X_LSR_RXBRK_BIT) { + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + continue; + } else if (sts & MAX310X_LSR_RXPAR_BIT) + s->port.icount.parity++; + else if (sts & MAX310X_LSR_FRERR_BIT) + s->port.icount.frame++; + else if (sts & MAX310X_LSR_RXOVR_BIT) + s->port.icount.overrun++; + + sts &= s->port.read_status_mask; + if (sts & MAX310X_LSR_RXBRK_BIT) + flag = TTY_BREAK; + else if (sts & MAX310X_LSR_RXPAR_BIT) + flag = TTY_PARITY; + else if (sts & MAX310X_LSR_FRERR_BIT) + flag = TTY_FRAME; + else if (sts & MAX310X_LSR_RXOVR_BIT) + flag = TTY_OVERRUN; + } + + if (uart_handle_sysrq_char(s->port, ch)) + continue; + + if (sts & s->port.ignore_status_mask) + continue; + + uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT, + ch, flag); + } + + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void max310x_handle_tx(struct max310x_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + unsigned int txlen = 0, to_send; + + if (unlikely(s->port.x_char)) { + regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char); + s->port.icount.tx++; + s->port.x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + return; + + /* Get length of data pending in circular buffer */ + to_send = uart_circ_chars_pending(xmit); + if (likely(to_send)) { + /* Limit to size of TX FIFO */ + regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen); + txlen = MAX310X_FIFO_SIZE - txlen; + to_send = (to_send > txlen) ? txlen : to_send; + + dev_dbg(s->port.dev, "TX Len = %u\n", to_send); + + /* Add data to send */ + s->port.icount.tx += to_send; + while (to_send--) { + regmap_write(s->regmap, MAX310X_THR_REG, + xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + }; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); +} + +static irqreturn_t max310x_ist(int irq, void *dev_id) +{ + struct max310x_port *s = (struct max310x_port *)dev_id; + unsigned int ists = 0, lsr = 0, rxlen = 0; + + mutex_lock(&s->max310x_mutex); + + for (;;) { + /* Read IRQ status & RX FIFO level */ + regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists); + regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr); + regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen); + if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen) + break; + + dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists); + + if (rxlen) + max310x_handle_rx(s, rxlen); + if (ists & MAX310X_IRQ_TX) + max310x_handle_tx(s); + if (ists & MAX310X_IRQ_CTS_BIT) + uart_handle_cts_change(&s->port, + !!(lsr & MAX310X_LSR_CTS_BIT)); + } + + mutex_unlock(&s->max310x_mutex); + + return IRQ_HANDLED; +} + +static void max310x_wq_proc(struct work_struct *ws) +{ + struct max310x_port *s = container_of(ws, struct max310x_port, tx_work); + + mutex_lock(&s->max310x_mutex); + max310x_handle_tx(s); + mutex_unlock(&s->max310x_mutex); +} + +static void max310x_start_tx(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + queue_work(s->wq, &s->tx_work); +} + +static void max310x_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +static void max310x_stop_rx(struct uart_port *port) +{ + /* Do nothing */ +} + +static unsigned int max310x_tx_empty(struct uart_port *port) +{ + unsigned int val = 0; + struct max310x_port *s = container_of(port, struct max310x_port, port); + + mutex_lock(&s->max310x_mutex); + regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val); + mutex_unlock(&s->max310x_mutex); + + return val ? 0 : TIOCSER_TEMT; +} + +static void max310x_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +static unsigned int max310x_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +static void max310x_break_ctl(struct uart_port *port, int break_state) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + mutex_lock(&s->max310x_mutex); + regmap_update_bits(s->regmap, MAX310X_LCR_REG, + MAX310X_LCR_TXBREAK_BIT, + break_state ? MAX310X_LCR_TXBREAK_BIT : 0); + mutex_unlock(&s->max310x_mutex); +} + +static void max310x_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + unsigned int lcr, flow = 0; + int baud; + + mutex_lock(&s->max310x_mutex); + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + termios->c_iflag &= ~IXANY; + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = MAX310X_LCR_WORD_LEN_5; + break; + case CS6: + lcr = MAX310X_LCR_WORD_LEN_6; + break; + case CS7: + lcr = MAX310X_LCR_WORD_LEN_7; + break; + case CS8: + default: + lcr = MAX310X_LCR_WORD_LEN_8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= MAX310X_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= MAX310X_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Update LCR register */ + regmap_write(s->regmap, MAX310X_LCR_REG, lcr); + + /* Set read status mask */ + port->read_status_mask = MAX310X_LSR_RXOVR_BIT; + if (termios->c_iflag & INPCK) + port->read_status_mask |= MAX310X_LSR_RXPAR_BIT | + MAX310X_LSR_FRERR_BIT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= MAX310X_LSR_RXBRK_BIT; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= MAX310X_LSR_RXBRK_BIT; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= MAX310X_LSR_RXPAR_BIT | + MAX310X_LSR_RXOVR_BIT | + MAX310X_LSR_FRERR_BIT | + MAX310X_LSR_RXBRK_BIT; + + /* Configure flow control */ + regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]); + regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); + if (termios->c_cflag & CRTSCTS) + flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT | + MAX310X_FLOWCTRL_AUTORTS_BIT; + if (termios->c_iflag & IXON) + flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT | + MAX310X_FLOWCTRL_SWFLOWEN_BIT; + if (termios->c_iflag & IXOFF) + flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT | + MAX310X_FLOWCTRL_SWFLOWEN_BIT; + regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow); + + /* Get baud rate generator configuration */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 4); + + /* Setup baudrate generator */ + max310x_set_baud(s, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + mutex_unlock(&s->max310x_mutex); +} + +static int max310x_startup(struct uart_port *port) +{ + unsigned int val, line = port->line; + struct max310x_port *s = container_of(port, struct max310x_port, port); + + if (s->pdata->suspend) + s->pdata->suspend(0); + + mutex_lock(&s->max310x_mutex); + + /* Configure baud rate, 9600 as default */ + max310x_set_baud(s, 9600); + + /* Configure LCR register, 8N1 mode by default */ + val = MAX310X_LCR_WORD_LEN_8; + regmap_write(s->regmap, MAX310X_LCR_REG, val); + + /* Configure MODE1 register */ + regmap_update_bits(s->regmap, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) + ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); + + /* Configure MODE2 register */ + val = MAX310X_MODE2_RXEMPTINV_BIT; + if (s->pdata->uart_flags[line] & MAX310X_LOOPBACK) + val |= MAX310X_MODE2_LOOPBACK_BIT; + if (s->pdata->uart_flags[line] & MAX310X_ECHO_SUPRESS) + val |= MAX310X_MODE2_ECHOSUPR_BIT; + + /* Reset FIFOs */ + val |= MAX310X_MODE2_FIFORST_BIT; + regmap_write(s->regmap, MAX310X_MODE2_REG, val); + + /* Configure FIFO trigger level register */ + /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */ + val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64); + regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val); + + /* Configure flow control levels */ + /* Flow control halt level 96, resume level 48 */ + val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96); + regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val); + + /* Clear timeout register */ + regmap_write(s->regmap, MAX310X_RXTO_REG, 0); + + /* Configure LSR interrupt enable register */ + /* Enable RX timeout interrupt */ + val = MAX310X_LSR_RXTO_BIT; + regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val); + + /* Clear FIFO reset */ + regmap_update_bits(s->regmap, MAX310X_MODE2_REG, + MAX310X_MODE2_FIFORST_BIT, 0); + + /* Clear IRQ status register by reading it */ + regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val); + + /* Configure interrupt enable register */ + /* Enable CTS change interrupt */ + val = MAX310X_IRQ_CTS_BIT; + /* Enable RX, TX interrupts */ + val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX; + regmap_write(s->regmap, MAX310X_IRQEN_REG, val); + + mutex_unlock(&s->max310x_mutex); + + return 0; +} + +static void max310x_shutdown(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + /* Disable all interrupts */ + mutex_lock(&s->max310x_mutex); + regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); + mutex_unlock(&s->max310x_mutex); + + if (s->pdata->suspend) + s->pdata->suspend(1); +} + +static const char *max310x_type(struct uart_port *port) +{ + struct max310x_port *s = container_of(port, struct max310x_port, port); + + return (port->type == PORT_MAX310X) ? s->name : NULL; +} + +static int max310x_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void max310x_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +static void max310x_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_MAX310X; +} + +static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X)) + return 0; + if (ser->irq == port->irq) + return 0; + + return -EINVAL; +} + +static struct uart_ops max310x_ops = { + .tx_empty = max310x_tx_empty, + .set_mctrl = max310x_set_mctrl, + .get_mctrl = max310x_get_mctrl, + .stop_tx = max310x_stop_tx, + .start_tx = max310x_start_tx, + .stop_rx = max310x_stop_rx, + .enable_ms = max310x_enable_ms, + .break_ctl = max310x_break_ctl, + .startup = max310x_startup, + .shutdown = max310x_shutdown, + .set_termios = max310x_set_termios, + .type = max310x_type, + .request_port = max310x_request_port, + .release_port = max310x_release_port, + .config_port = max310x_config_port, + .verify_port = max310x_verify_port, +}; + +static int max310x_suspend(struct spi_device *spi, pm_message_t state) +{ + int ret; + struct max310x_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Suspend\n"); + + ret = uart_suspend_port(&s->uart, &s->port); + + mutex_lock(&s->max310x_mutex); + + /* Enable sleep mode */ + regmap_update_bits(s->regmap, MAX310X_MODE1_REG, + MAX310X_MODE1_FORCESLEEP_BIT, + MAX310X_MODE1_FORCESLEEP_BIT); + + mutex_unlock(&s->max310x_mutex); + + if (s->pdata->suspend) + s->pdata->suspend(1); + + return ret; +} + +static int max310x_resume(struct spi_device *spi) +{ + struct max310x_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "Resume\n"); + + if (s->pdata->suspend) + s->pdata->suspend(0); + + mutex_lock(&s->max310x_mutex); + + /* Disable sleep mode */ + regmap_update_bits(s->regmap, MAX310X_MODE1_REG, + MAX310X_MODE1_FORCESLEEP_BIT, + 0); + + max310x_wait_pll(s); + + mutex_unlock(&s->max310x_mutex); + + return uart_resume_port(&s->uart, &s->port); +} + +#ifdef CONFIG_GPIOLIB +static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int val = 0; + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val); + mutex_unlock(&s->max310x_mutex); + + return !!((val >> 4) & (1 << offset)); +} + +static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? + 1 << offset : 0); + mutex_unlock(&s->max310x_mutex); +} + +static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + + regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0); + + mutex_unlock(&s->max310x_mutex); + + return 0; +} + +static int max310x_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + + mutex_lock(&s->max310x_mutex); + + regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, + 1 << offset); + regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? + 1 << offset : 0); + + mutex_unlock(&s->max310x_mutex); + + return 0; +} +#endif + +/* Generic platform data */ +static struct max310x_pdata generic_plat_data = { + .driver_flags = MAX310X_EXT_CLK, + .uart_flags[0] = MAX310X_ECHO_SUPRESS, + .frequency = 26000000, +}; + +static int __devinit max310x_probe(struct spi_device *spi) +{ + struct max310x_port *s; + struct device *dev = &spi->dev; + int chiptype = spi_get_device_id(spi)->driver_data; + struct max310x_pdata *pdata = dev->platform_data; + unsigned int val = 0; + int ret; + + /* Check for IRQ */ + if (spi->irq <= 0) { + dev_err(dev, "No IRQ specified\n"); + return -ENOTSUPP; + } + + /* Alloc port structure */ + s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL); + if (!s) { + dev_err(dev, "Error allocating port structure\n"); + return -ENOMEM; + } + dev_set_drvdata(dev, s); + + if (!pdata) { + dev_warn(dev, "No platform data supplied, using defaults\n"); + pdata = &generic_plat_data; + } + s->pdata = pdata; + + /* Individual chip settings */ + switch (chiptype) { + case MAX310X_TYPE_MAX3107: + s->name = "MAX3107"; + s->nr_gpio = 4; + s->uart.nr = 1; + s->regcfg.max_register = 0x1f; + break; + case MAX310X_TYPE_MAX3108: + s->name = "MAX3108"; + s->nr_gpio = 4; + s->uart.nr = 1; + s->regcfg.max_register = 0x1e; + break; + default: + dev_err(dev, "Unsupported chip type %i\n", chiptype); + return -ENOTSUPP; + } + + /* Check input frequency */ + if ((pdata->driver_flags & MAX310X_EXT_CLK) && + ((pdata->frequency < 500000) || (pdata->frequency > 35000000))) + goto err_freq; + /* Check frequency for quartz */ + if (!(pdata->driver_flags & MAX310X_EXT_CLK) && + ((pdata->frequency < 1000000) || (pdata->frequency > 4000000))) + goto err_freq; + + mutex_init(&s->max310x_mutex); + + /* Setup SPI bus */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + spi->max_speed_hz = 26000000; + spi_setup(spi); + + /* Setup regmap */ + s->regcfg.reg_bits = 8; + s->regcfg.val_bits = 8; + s->regcfg.read_flag_mask = 0x00; + s->regcfg.write_flag_mask = 0x80; + s->regcfg.cache_type = REGCACHE_RBTREE; + s->regcfg.writeable_reg = max3107_8_reg_writeable; + s->regcfg.volatile_reg = max310x_reg_volatile; + s->regcfg.precious_reg = max310x_reg_precious; + s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + if (IS_ERR(s->regmap)) { + ret = PTR_ERR(s->regmap); + dev_err(dev, "Failed to initialize register map\n"); + goto err_out; + } + + /* Reset chip & check SPI function */ + ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); + if (ret) { + dev_err(dev, "SPI transfer failed\n"); + goto err_out; + } + /* Clear chip reset */ + regmap_write(s->regmap, MAX310X_MODE2_REG, 0); + + switch (chiptype) { + case MAX310X_TYPE_MAX3107: + /* Check REV ID to ensure we are talking to what we expect */ + regmap_read(s->regmap, MAX3107_REVID_REG, &val); + if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) { + dev_err(dev, "%s ID 0x%02x does not match\n", + s->name, val); + ret = -ENODEV; + goto err_out; + } + break; + case MAX310X_TYPE_MAX3108: + /* MAX3108 have not REV ID register, we just check default value + * from clocksource register to make sure everything works. + */ + regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); + if (val != (MAX310X_CLKSRC_EXTCLK_BIT | + MAX310X_CLKSRC_PLLBYP_BIT)) { + dev_err(dev, "%s not present\n", s->name); + ret = -ENODEV; + goto err_out; + } + break; + } + + /* Board specific configure */ + if (pdata->init) + pdata->init(); + if (pdata->suspend) + pdata->suspend(0); + + /* Calculate referecne clock */ + s->uartclk = max310x_set_ref_clk(s); + + /* Disable all interrupts */ + regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); + + /* Setup MODE1 register */ + val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */ + if (pdata->driver_flags & MAX310X_AUTOSLEEP) + val = MAX310X_MODE1_AUTOSLEEP_BIT; + regmap_write(s->regmap, MAX310X_MODE1_REG, val); + + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), s); + if (ret) { + dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq); + goto err_out; + } + + /* Register UART driver */ + s->uart.owner = THIS_MODULE; + s->uart.driver_name = dev_name(dev); + s->uart.dev_name = "ttyMAX"; + s->uart.major = MAX310X_MAJOR; + s->uart.minor = MAX310X_MINOR; + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(dev, "Registering UART driver failed\n"); + goto err_out; + } + + /* Initialize workqueue for start TX */ + s->wq = create_freezable_workqueue(dev_name(dev)); + INIT_WORK(&s->tx_work, max310x_wq_proc); + + /* Initialize UART port data */ + s->port.line = 0; + s->port.dev = dev; + s->port.irq = spi->irq; + s->port.type = PORT_MAX310X; + s->port.fifosize = MAX310X_FIFO_SIZE; + s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port.iotype = UPIO_PORT; + s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */ + s->port.uartclk = s->uartclk; + s->port.ops = &max310x_ops; + uart_add_one_port(&s->uart, &s->port); + +#ifdef CONFIG_GPIOLIB + /* Setup GPIO cotroller */ + if (pdata->gpio_base) { + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = max310x_gpio_direction_input; + s->gpio.get = max310x_gpio_get; + s->gpio.direction_output= max310x_gpio_direction_output; + s->gpio.set = max310x_gpio_set; + s->gpio.base = pdata->gpio_base; + s->gpio.ngpio = s->nr_gpio; + if (gpiochip_add(&s->gpio)) { + /* Indicate that we should not call gpiochip_remove */ + s->gpio.base = 0; + } + } else + dev_info(dev, "GPIO support not enabled\n"); +#endif + + /* Go to suspend mode */ + if (pdata->suspend) + pdata->suspend(1); + + return 0; + +err_freq: + dev_err(dev, "Frequency parameter incorrect\n"); + ret = -EINVAL; + +err_out: + dev_set_drvdata(dev, NULL); + + return ret; +} + +static int __devexit max310x_remove(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct max310x_port *s = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "Removing port\n"); + + devm_free_irq(dev, s->port.irq, s); + + destroy_workqueue(s->wq); + + uart_remove_one_port(&s->uart, &s->port); + + uart_unregister_driver(&s->uart); + +#ifdef CONFIG_GPIOLIB + if (s->pdata->gpio_base) { + ret = gpiochip_remove(&s->gpio); + if (ret) + dev_err(dev, "Failed to remove gpio chip: %d\n", ret); + } +#endif + + dev_set_drvdata(dev, NULL); + + if (s->pdata->suspend) + s->pdata->suspend(1); + if (s->pdata->exit) + s->pdata->exit(); + + return ret; +} + +static const struct spi_device_id max310x_id_table[] = { + { "max3107", MAX310X_TYPE_MAX3107 }, + { "max3108", MAX310X_TYPE_MAX3108 }, +}; +MODULE_DEVICE_TABLE(spi, max310x_id_table); + +static struct spi_driver max310x_driver = { + .driver = { + .name = "max310x", + .owner = THIS_MODULE, + }, + .probe = max310x_probe, + .remove = __devexit_p(max310x_remove), + .suspend = max310x_suspend, + .resume = max310x_resume, + .id_table = max310x_id_table, +}; +module_spi_driver(max310x_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("MAX310X serial driver"); diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index bedac0d..f19d04e 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -775,11 +775,15 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, } if (new->c_cflag & PARENB) { + if (new->c_cflag & CMSPAR) + mr1 |= MPC52xx_PSC_MODE_PARFORCE; + + /* With CMSPAR, PARODD also means high parity (same as termios) */ mr1 |= (new->c_cflag & PARODD) ? MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN; - } else + } else { mr1 |= MPC52xx_PSC_MODE_PARNONE; - + } mr2 = 0; diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 8131e2c..033e0bc 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -896,7 +896,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) return PTR_ERR(msm_port->clk); if (msm_port->is_uartdm) - clk_set_rate(msm_port->clk, 7372800); + clk_set_rate(msm_port->clk, 1843200); port->uartclk = clk_get_rate(msm_port->clk); printk(KERN_INFO "uartclk = %d\n", port->uartclk); diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c index b25e6ee..925d1fa 100644 --- a/drivers/tty/serial/msm_smd_tty.c +++ b/drivers/tty/serial/msm_smd_tty.c @@ -223,9 +223,11 @@ static int __init smd_tty_init(void) return ret; for (i = 0; i < smd_tty_channels_len; i++) { - tty_port_init(&smd_tty[smd_tty_channels[i].id].port); - smd_tty[smd_tty_channels[i].id].port.ops = &smd_tty_port_ops; - tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0); + struct tty_port *port = &smd_tty[smd_tty_channels[i].id].port; + tty_port_init(port); + port->ops = &smd_tty_port_ops; + tty_port_register_device(port, smd_tty_driver, + smd_tty_channels[i].id, NULL); } return 0; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 3a667ee..6db3baa 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -262,7 +262,7 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) ctrl &= ~AUART_CTRL2_RTSEN; if (mctrl & TIOCM_RTS) { - if (u->state->port.flags & ASYNC_CTS_FLOW) + if (tty_port_cts_enabled(&u->state->port)) ctrl |= AUART_CTRL2_RTSEN; } @@ -457,11 +457,11 @@ static void mxs_auart_shutdown(struct uart_port *u) writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); - writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET); - writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, u->membase + AUART_INTR_CLR); + writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET); + clk_disable_unprepare(s->clk); } @@ -781,6 +781,7 @@ out_free_irq: auart_port[pdev->id] = NULL; free_irq(s->irq, s); out_free_clk: + put_device(s->dev); clk_put(s->clk); out_free: kfree(s); @@ -796,6 +797,7 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev) auart_port[pdev->id] = NULL; + put_device(s->dev); clk_put(s->clk); free_irq(s->irq, s); kfree(s); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 34e7187..df443b9 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -105,6 +105,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; + + if (of_find_property(np, "no-loopback-test", NULL)) + port->flags |= UPF_SKIP_TEST; + port->dev = &ofdev->dev; if (type == PORT_TEGRA) @@ -144,8 +148,15 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev) switch (port_type) { #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: - ret = serial8250_register_port(&port); + { + /* For now the of bindings don't support the extra + 8250 specific bits */ + struct uart_8250_port port8250; + memset(&port8250, 0, sizeof(port8250)); + port8250.port = port; + ret = serial8250_register_8250_port(&port8250); break; + } #endif #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL case PORT_NWPSERIAL: diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d3cda0c..ccc2f35 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -32,16 +32,16 @@ #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/platform_device.h> #include <linux/io.h> -#include <linux/dma-mapping.h> #include <linux/clk.h> #include <linux/serial_core.h> #include <linux/irq.h> #include <linux/pm_runtime.h> #include <linux/of.h> +#include <linux/gpio.h> +#include <linux/pinctrl/consumer.h> -#include <plat/dma.h> -#include <plat/dmtimer.h> #include <plat/omap-serial.h> #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) @@ -57,8 +57,8 @@ #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) /* FCR register bitmasks */ -#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) +#define OMAP_UART_FCR_TX_FIFO_TRIG_MASK (0x3 << 4) /* MVR register bitmasks */ #define OMAP_UART_MVR_SCHEME_SHIFT 30 @@ -71,12 +71,52 @@ #define OMAP_UART_MVR_MAJ_SHIFT 8 #define OMAP_UART_MVR_MIN_MASK 0x3f +struct uart_omap_port { + struct uart_port port; + struct uart_omap_dma uart_dma; + struct device *dev; + + 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; + unsigned int irq_pending:1; + + int DTR_gpio; + int DTR_inverted; + int DTR_active; + + struct pm_qos_request pm_qos_request; + u32 latency; + u32 calc_latency; + struct work_struct qos_work; + struct pinctrl *pins; +}; + +#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) + static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ -static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); -static void serial_omap_rxdma_poll(unsigned long uart_no); -static int serial_omap_start_rxdma(struct uart_omap_port *up); static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1); static struct workqueue_struct *serial_omap_uart_wq; @@ -101,6 +141,46 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) serial_out(up, UART_FCR, 0); } +static int serial_omap_get_context_loss_count(struct uart_omap_port *up) +{ + struct omap_uart_port_info *pdata = up->dev->platform_data; + + if (!pdata || !pdata->get_context_loss_count) + return 0; + + return pdata->get_context_loss_count(up->dev); +} + +static void serial_omap_set_forceidle(struct uart_omap_port *up) +{ + struct omap_uart_port_info *pdata = up->dev->platform_data; + + if (!pdata || !pdata->set_forceidle) + return; + + pdata->set_forceidle(up->dev); +} + +static void serial_omap_set_noidle(struct uart_omap_port *up) +{ + struct omap_uart_port_info *pdata = up->dev->platform_data; + + if (!pdata || !pdata->set_noidle) + return; + + pdata->set_noidle(up->dev); +} + +static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) +{ + struct omap_uart_port_info *pdata = up->dev->platform_data; + + if (!pdata || !pdata->enable_wakeup) + return; + + pdata->enable_wakeup(up->dev, enable); +} + /* * serial_omap_get_divisor - calculate divisor value * @port: uart port info @@ -126,151 +206,55 @@ serial_omap_get_divisor(struct uart_port *port, unsigned int baud) return port->uartclk/(baud * divisor); } -static void serial_omap_stop_rxdma(struct uart_omap_port *up) -{ - if (up->uart_dma.rx_dma_used) { - del_timer(&up->uart_dma.rx_timer); - omap_stop_dma(up->uart_dma.rx_dma_channel); - omap_free_dma(up->uart_dma.rx_dma_channel); - up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; - up->uart_dma.rx_dma_used = false; - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); - } -} - static void serial_omap_enable_ms(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->port.line); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); up->ier |= UART_IER_MSI; serial_out(up, UART_IER, up->ier); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static void serial_omap_stop_tx(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; - struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; - - if (up->use_dma && - up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { - /* - * Check if dma is still active. If yes do nothing, - * return. Else stop dma - */ - if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) - return; - omap_stop_dma(up->uart_dma.tx_dma_channel); - omap_free_dma(up->uart_dma.tx_dma_channel); - up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); - } + struct uart_omap_port *up = to_uart_omap_port(port); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; serial_out(up, UART_IER, up->ier); } - if (!up->use_dma && pdata && pdata->set_forceidle) - pdata->set_forceidle(up->pdev); + serial_omap_set_forceidle(up); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static void serial_omap_stop_rx(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); - pm_runtime_get_sync(&up->pdev->dev); - if (up->use_dma) - serial_omap_stop_rxdma(up); + pm_runtime_get_sync(up->dev); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; serial_out(up, UART_IER, up->ier); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); -} - -static inline void receive_chars(struct uart_omap_port *up, - unsigned int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned int flag, lsr = *status; - unsigned char ch = 0; - int max_count = 256; - - do { - if (likely(lsr & UART_LSR_DR)) - ch = serial_in(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (lsr & UART_LSR_PE) { - up->port.icount.parity++; - } else if (lsr & UART_LSR_FE) { - up->port.icount.frame++; - } - - if (lsr & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - lsr &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_OMAP_CONSOLE - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - lsr |= up->lsr_break_flag; - } -#endif - if (lsr & UART_LSR_BI) - flag = TTY_BREAK; - else if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - else if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); -ignore_char: - lsr = serial_in(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } -static void transmit_chars(struct uart_omap_port *up) +static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) { struct circ_buf *xmit = &up->port.state->xmit; int count; + if (!(lsr & UART_LSR_THRE)) + return; + if (up->port.x_char) { serial_out(up, UART_TX, up->port.x_char); up->port.icount.tx++; @@ -290,8 +274,11 @@ static void transmit_chars(struct uart_omap_port *up) break; } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { + spin_unlock(&up->port.lock); uart_write_wakeup(&up->port); + spin_lock(&up->port.lock); + } if (uart_circ_empty(xmit)) serial_omap_stop_tx(&up->port); @@ -307,70 +294,13 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; - struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; - struct circ_buf *xmit; - unsigned int start; - int ret = 0; - - if (!up->use_dma) { - pm_runtime_get_sync(&up->pdev->dev); - serial_omap_enable_ier_thri(up); - if (pdata && pdata->set_noidle) - pdata->set_noidle(up->pdev); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); - return; - } - - if (up->uart_dma.tx_dma_used) - return; - - xmit = &up->port.state->xmit; - - if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { - pm_runtime_get_sync(&up->pdev->dev); - ret = omap_request_dma(up->uart_dma.uart_dma_tx, - "UART Tx DMA", - (void *)uart_tx_dma_callback, up, - &(up->uart_dma.tx_dma_channel)); - - if (ret < 0) { - serial_omap_enable_ier_thri(up); - return; - } - } - spin_lock(&(up->uart_dma.tx_lock)); - up->uart_dma.tx_dma_used = true; - spin_unlock(&(up->uart_dma.tx_lock)); + struct uart_omap_port *up = to_uart_omap_port(port); - start = up->uart_dma.tx_buf_dma_phys + - (xmit->tail & (UART_XMIT_SIZE - 1)); - - up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); - /* - * It is a circular buffer. See if the buffer has wounded back. - * If yes it will have to be transferred in two separate dma - * transfers - */ - if (start + up->uart_dma.tx_buf_size >= - up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - up->uart_dma.tx_buf_size = - (up->uart_dma.tx_buf_dma_phys + - UART_XMIT_SIZE) - start; - - omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, start, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.tx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_tx, 0); - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.tx_dma_channel); + pm_runtime_get_sync(up->dev); + serial_omap_enable_ier_thri(up); + serial_omap_set_noidle(up); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static unsigned int check_modem_status(struct uart_omap_port *up) @@ -401,76 +331,162 @@ static unsigned int check_modem_status(struct uart_omap_port *up) return status; } +static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr) +{ + unsigned int flag; + unsigned char ch = 0; + + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + + up->port.icount.rx++; + flag = TTY_NORMAL; + + if (lsr & UART_LSR_BI) { + flag = TTY_BREAK; + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + return; + + } + + if (lsr & UART_LSR_PE) { + flag = TTY_PARITY; + up->port.icount.parity++; + } + + if (lsr & UART_LSR_FE) { + flag = TTY_FRAME; + up->port.icount.frame++; + } + + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + lsr |= up->lsr_break_flag; + } +#endif + uart_insert_char(&up->port, lsr, UART_LSR_OE, 0, flag); +} + +static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr) +{ + unsigned char ch = 0; + unsigned int flag; + + if (!(lsr & UART_LSR_DR)) + return; + + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (uart_handle_sysrq_char(&up->port, ch)) + return; + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); +} + /** * serial_omap_irq() - This handles the interrupt from one port * @irq: uart port irq number * @dev_id: uart port info */ -static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) +static irqreturn_t serial_omap_irq(int irq, void *dev_id) { struct uart_omap_port *up = dev_id; + struct tty_struct *tty = up->port.state->port.tty; unsigned int iir, lsr; - unsigned long flags; + unsigned int type; + irqreturn_t ret = IRQ_NONE; + int max_count = 256; - pm_runtime_get_sync(&up->pdev->dev); - iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) { - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); - return IRQ_NONE; - } + spin_lock(&up->port.lock); + pm_runtime_get_sync(up->dev); - spin_lock_irqsave(&up->port.lock, flags); - lsr = serial_in(up, UART_LSR); - if (iir & UART_IIR_RLSI) { - if (!up->use_dma) { - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); - } else { - up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - if ((serial_omap_start_rxdma(up) != 0) && - (lsr & UART_LSR_DR)) - receive_chars(up, &lsr); + do { + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + break; + + ret = IRQ_HANDLED; + lsr = serial_in(up, UART_LSR); + + /* extract IRQ type from IIR register */ + type = iir & 0x3e; + + switch (type) { + case UART_IIR_MSI: + check_modem_status(up); + break; + case UART_IIR_THRI: + transmit_chars(up, lsr); + break; + case UART_IIR_RX_TIMEOUT: + /* FALLTHROUGH */ + case UART_IIR_RDI: + serial_omap_rdi(up, lsr); + break; + case UART_IIR_RLSI: + serial_omap_rlsi(up, lsr); + break; + case UART_IIR_CTS_RTS_DSR: + /* simply try again */ + break; + case UART_IIR_XOFF: + /* FALLTHROUGH */ + default: + break; } - } + } while (!(iir & UART_IIR_NO_INT) && max_count--); - check_modem_status(up); - if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) - transmit_chars(up); + spin_unlock(&up->port.lock); - spin_unlock_irqrestore(&up->port.lock, flags); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); + tty_flip_buffer_push(tty); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); up->port_activity = jiffies; - return IRQ_HANDLED; + + return ret; } static unsigned int serial_omap_tx_empty(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; unsigned int ret = 0; - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line); spin_lock_irqsave(&up->port.lock, flags); ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; spin_unlock_irqrestore(&up->port.lock, flags); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); return ret; } static unsigned int serial_omap_get_mctrl(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned int status; unsigned int ret = 0; - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); status = check_modem_status(up); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->port.line); @@ -487,7 +503,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port) static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned char mcr = 0; dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line); @@ -502,20 +518,31 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); up->mcr = serial_in(up, UART_MCR); up->mcr |= mcr; serial_out(up, UART_MCR, up->mcr); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); + + if (gpio_is_valid(up->DTR_gpio) && + !!(mctrl & TIOCM_DTR) != up->DTR_active) { + up->DTR_active = !up->DTR_active; + if (gpio_cansleep(up->DTR_gpio)) + schedule_work(&up->qos_work); + else + gpio_set_value(up->DTR_gpio, + up->DTR_active != up->DTR_inverted); + } } static void serial_omap_break_ctl(struct uart_port *port, int break_state) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); spin_lock_irqsave(&up->port.lock, flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; @@ -523,12 +550,13 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state) up->lcr &= ~UART_LCR_SBC; serial_out(up, UART_LCR, up->lcr); spin_unlock_irqrestore(&up->port.lock, flags); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static int serial_omap_startup(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; int retval; @@ -542,7 +570,7 @@ static int serial_omap_startup(struct uart_port *port) dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -573,20 +601,6 @@ static int serial_omap_startup(struct uart_port *port) spin_unlock_irqrestore(&up->port.lock, flags); up->msr_saved_flags = 0; - if (up->use_dma) { - free_page((unsigned long)up->port.state->xmit.buf); - up->port.state->xmit.buf = dma_alloc_coherent(NULL, - UART_XMIT_SIZE, - (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), - 0); - init_timer(&(up->uart_dma.rx_timer)); - up->uart_dma.rx_timer.function = serial_omap_rxdma_poll; - up->uart_dma.rx_timer.data = up->port.line; - /* Currently the buffer size is 4KB. Can increase it */ - up->uart_dma.rx_buf = dma_alloc_coherent(NULL, - up->uart_dma.rx_buf_size, - (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); - } /* * Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently @@ -598,20 +612,20 @@ static int serial_omap_startup(struct uart_port *port) /* Enable module level wake up */ serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); up->port_activity = jiffies; return 0; } static void serial_omap_shutdown(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->port.line); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); /* * Disable interrupts from this port */ @@ -634,19 +648,9 @@ static void serial_omap_shutdown(struct uart_port *port) */ if (serial_in(up, UART_LSR) & UART_LSR_DR) (void) serial_in(up, UART_RX); - if (up->use_dma) { - dma_free_coherent(up->port.dev, - UART_XMIT_SIZE, up->port.state->xmit.buf, - up->uart_dma.tx_buf_dma_phys); - up->port.state->xmit.buf = NULL; - serial_omap_stop_rx(port); - dma_free_coherent(up->port.dev, - up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, - up->uart_dma.rx_buf_dma_phys); - up->uart_dma.rx_buf = NULL; - } - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); free_irq(up->port.irq, up); } @@ -667,19 +671,19 @@ serial_omap_configure_xonxoff /* * IXON Flag: - * Enable XON/XOFF flow control on output. - * Transmit XON1, XOFF1 + * Flow control for OMAP.TX + * OMAP.RX should listen for XON/XOFF */ if (termios->c_iflag & IXON) - up->efr |= OMAP_UART_SW_TX; + up->efr |= OMAP_UART_SW_RX; /* * IXOFF Flag: - * Enable XON/XOFF flow control on input. - * Receiver compares XON1, XOFF1. + * Flow control for OMAP.RX + * OMAP.TX should send XON/XOFF */ if (termios->c_iflag & IXOFF) - up->efr |= OMAP_UART_SW_RX; + up->efr |= OMAP_UART_SW_TX; serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); @@ -715,13 +719,16 @@ static void serial_omap_uart_qos_work(struct work_struct *work) qos_work); pm_qos_update_request(&up->pm_qos_request, up->latency); + if (gpio_is_valid(up->DTR_gpio)) + gpio_set_value_cansleep(up->DTR_gpio, + up->DTR_active != up->DTR_inverted); } static void serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned char cval = 0; unsigned char efr = 0; unsigned long flags = 0; @@ -768,14 +775,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | UART_FCR_ENABLE_FIFO; - if (up->use_dma) - up->fcr |= UART_FCR_DMA_SELECT; /* * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); spin_lock_irqsave(&up->port.lock, flags); /* @@ -845,14 +850,13 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; - if (up->use_dma) { - serial_out(up, UART_TI752_TLR, 0); - up->scr |= UART_FCR_TRIGGER_4; - } else { - /* Set receive FIFO threshold to 1 byte */ - up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; - up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT); - } + /* Set receive FIFO threshold to 16 characters and + * transmit FIFO threshold to 16 spaces + */ + up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; + up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK; + up->fcr |= UART_FCR6_R_TRIGGER_16 | UART_FCR6_T_TRIGGER_24 | + UART_FCR_ENABLE_FIFO; serial_out(up, UART_FCR, up->fcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); @@ -924,20 +928,30 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_omap_configure_xonxoff(up, termios); spin_unlock_irqrestore(&up->port.lock, flags); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line); } +static int serial_omap_set_wake(struct uart_port *port, unsigned int state) +{ + struct uart_omap_port *up = to_uart_omap_port(port); + + serial_omap_enable_wakeup(up, state); + + return 0; +} + static void serial_omap_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned char efr; dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); efr = serial_in(up, UART_EFR); serial_out(up, UART_EFR, efr | UART_EFR_ECB); @@ -948,14 +962,15 @@ serial_omap_pm(struct uart_port *port, unsigned int state, serial_out(up, UART_EFR, efr); serial_out(up, UART_LCR, 0); - if (!device_may_wakeup(&up->pdev->dev)) { + if (!device_may_wakeup(up->dev)) { if (!state) - pm_runtime_forbid(&up->pdev->dev); + pm_runtime_forbid(up->dev); else - pm_runtime_allow(&up->pdev->dev); + pm_runtime_allow(up->dev); } - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static void serial_omap_release_port(struct uart_port *port) @@ -971,7 +986,7 @@ static int serial_omap_request_port(struct uart_port *port) static void serial_omap_config_port(struct uart_port *port, int flags) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", up->port.line); @@ -989,7 +1004,7 @@ serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) static const char * serial_omap_type(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->port.line); return up->name; @@ -1032,26 +1047,33 @@ static inline void wait_for_xmitr(struct uart_omap_port *up) static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); wait_for_xmitr(up); serial_out(up, UART_TX, ch); - pm_runtime_put(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); } static int serial_omap_poll_get_char(struct uart_port *port) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); unsigned int status; - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); status = serial_in(up, UART_LSR); - if (!(status & UART_LSR_DR)) - return NO_POLL_CHAR; + if (!(status & UART_LSR_DR)) { + status = NO_POLL_CHAR; + goto out; + } status = serial_in(up, UART_RX); - pm_runtime_put(&up->pdev->dev); + +out: + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); + return status; } @@ -1065,7 +1087,7 @@ static struct uart_driver serial_omap_reg; static void serial_omap_console_putchar(struct uart_port *port, int ch) { - struct uart_omap_port *up = (struct uart_omap_port *)port; + struct uart_omap_port *up = to_uart_omap_port(port); wait_for_xmitr(up); serial_out(up, UART_TX, ch); @@ -1080,7 +1102,7 @@ serial_omap_console_write(struct console *co, const char *s, unsigned int ier; int locked = 1; - pm_runtime_get_sync(&up->pdev->dev); + pm_runtime_get_sync(up->dev); local_irq_save(flags); if (up->port.sysrq) @@ -1114,8 +1136,8 @@ serial_omap_console_write(struct console *co, const char *s, if (up->msr_saved_flags) check_modem_status(up); - pm_runtime_mark_last_busy(&up->pdev->dev); - pm_runtime_put_autosuspend(&up->pdev->dev); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); if (locked) spin_unlock(&up->port.lock); local_irq_restore(flags); @@ -1179,6 +1201,7 @@ static struct uart_ops serial_omap_pops = { .shutdown = serial_omap_shutdown, .set_termios = serial_omap_set_termios, .pm = serial_omap_pm, + .set_wake = serial_omap_set_wake, .type = serial_omap_type, .release_port = serial_omap_release_port, .request_port = serial_omap_request_port, @@ -1203,10 +1226,8 @@ static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - if (up) { - uart_suspend_port(&serial_omap_reg, &up->port); - flush_work_sync(&up->qos_work); - } + uart_suspend_port(&serial_omap_reg, &up->port); + flush_work_sync(&up->qos_work); return 0; } @@ -1215,156 +1236,13 @@ static int serial_omap_resume(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - if (up) - uart_resume_port(&serial_omap_reg, &up->port); + uart_resume_port(&serial_omap_reg, &up->port); + return 0; } #endif -static void serial_omap_rxdma_poll(unsigned long uart_no) -{ - struct uart_omap_port *up = ui[uart_no]; - unsigned int curr_dma_pos, curr_transmitted_size; - int ret = 0; - - curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); - if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || - (curr_dma_pos == 0)) { - if (jiffies_to_msecs(jiffies - up->port_activity) < - up->uart_dma.rx_timeout) { - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_poll_rate)); - } else { - serial_omap_stop_rxdma(up); - up->ier |= (UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - } - return; - } - - curr_transmitted_size = curr_dma_pos - - up->uart_dma.prev_rx_dma_pos; - up->port.icount.rx += curr_transmitted_size; - tty_insert_flip_string(up->port.state->port.tty, - up->uart_dma.rx_buf + - (up->uart_dma.prev_rx_dma_pos - - up->uart_dma.rx_buf_dma_phys), - curr_transmitted_size); - tty_flip_buffer_push(up->port.state->port.tty); - up->uart_dma.prev_rx_dma_pos = curr_dma_pos; - if (up->uart_dma.rx_buf_size + - up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { - ret = serial_omap_start_rxdma(up); - if (ret < 0) { - serial_omap_stop_rxdma(up); - up->ier |= (UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - } - } else { - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_poll_rate)); - } - up->port_activity = jiffies; -} - -static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) -{ - return; -} - -static int serial_omap_start_rxdma(struct uart_omap_port *up) -{ - int ret = 0; - - if (up->uart_dma.rx_dma_channel == -1) { - pm_runtime_get_sync(&up->pdev->dev); - ret = omap_request_dma(up->uart_dma.uart_dma_rx, - "UART Rx DMA", - (void *)uart_rx_dma_callback, up, - &(up->uart_dma.rx_dma_channel)); - if (ret < 0) - return ret; - - omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, - up->uart_dma.rx_buf_dma_phys, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.rx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_rx, 0); - } - up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.rx_dma_channel); - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_poll_rate)); - up->uart_dma.rx_dma_used = true; - return ret; -} - -static void serial_omap_continue_tx(struct uart_omap_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - unsigned int start = up->uart_dma.tx_buf_dma_phys - + (xmit->tail & (UART_XMIT_SIZE - 1)); - - if (uart_circ_empty(xmit)) - return; - - up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); - /* - * It is a circular buffer. See if the buffer has wounded back. - * If yes it will have to be transferred in two separate dma - * transfers - */ - if (start + up->uart_dma.tx_buf_size >= - up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - up->uart_dma.tx_buf_size = - (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; - omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, start, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.tx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_tx, 0); - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.tx_dma_channel); -} - -static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) -{ - struct uart_omap_port *up = (struct uart_omap_port *)data; - struct circ_buf *xmit = &up->port.state->xmit; - - xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ - (UART_XMIT_SIZE - 1); - up->port.icount.tx += up->uart_dma.tx_buf_size; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) { - spin_lock(&(up->uart_dma.tx_lock)); - serial_omap_stop_tx(&up->port); - up->uart_dma.tx_dma_used = false; - spin_unlock(&(up->uart_dma.tx_lock)); - } else { - omap_stop_dma(up->uart_dma.tx_dma_channel); - serial_omap_continue_tx(up); - } - up->port_activity = jiffies; - return; -} - -static void omap_serial_fill_features_erratas(struct uart_omap_port *up) +static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *up) { u32 mvr, scheme; u16 revision, major, minor; @@ -1389,7 +1267,7 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) minor = (mvr & OMAP_UART_MVR_MIN_MASK); break; default: - dev_warn(&up->pdev->dev, + dev_warn(up->dev, "Unknown %s revision, defaulting to highest\n", up->name); /* highest possible revision */ @@ -1417,7 +1295,7 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) } } -static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) +static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) { struct omap_uart_port_info *omap_up_info; @@ -1430,12 +1308,12 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) return omap_up_info; } -static int serial_omap_probe(struct platform_device *pdev) +static int __devinit serial_omap_probe(struct platform_device *pdev) { struct uart_omap_port *up; - struct resource *mem, *irq, *dma_tx, *dma_rx; + struct resource *mem, *irq; struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; - int ret = -ENOSPC; + int ret; if (pdev->dev.of_node) omap_up_info = of_get_uart_port_info(&pdev->dev); @@ -1458,19 +1336,30 @@ static int serial_omap_probe(struct platform_device *pdev) return -EBUSY; } - dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!dma_rx) - return -ENXIO; - - dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!dma_tx) - return -ENXIO; + if (gpio_is_valid(omap_up_info->DTR_gpio) && + omap_up_info->DTR_present) { + ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial"); + if (ret < 0) + return ret; + ret = gpio_direction_output(omap_up_info->DTR_gpio, + omap_up_info->DTR_inverted); + if (ret < 0) + return ret; + } up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; - up->pdev = pdev; + if (gpio_is_valid(omap_up_info->DTR_gpio) && + omap_up_info->DTR_present) { + up->DTR_gpio = omap_up_info->DTR_gpio; + up->DTR_inverted = omap_up_info->DTR_inverted; + } else + up->DTR_gpio = -EINVAL; + up->DTR_active = 0; + + up->dev = &pdev->dev; up->port.dev = &pdev->dev; up->port.type = PORT_OMAP; up->port.iotype = UPIO_MEM; @@ -1492,6 +1381,13 @@ static int serial_omap_probe(struct platform_device *pdev) goto err_port_line; } + up->pins = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(up->pins)) { + dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n", + up->port.line, PTR_ERR(up->pins)); + up->pins = NULL; + } + sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; up->port.membase = devm_ioremap(&pdev->dev, mem->start, @@ -1509,20 +1405,6 @@ static int serial_omap_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "No clock speed specified: using default:" "%d\n", DEFAULT_CLK_SPEED); } - up->uart_dma.uart_base = mem->start; - - if (omap_up_info->dma_enabled) { - up->uart_dma.uart_dma_tx = dma_tx->start; - up->uart_dma.uart_dma_rx = dma_rx->start; - up->use_dma = 1; - up->uart_dma.rx_buf_size = omap_up_info->dma_rx_buf_size; - up->uart_dma.rx_timeout = omap_up_info->dma_rx_timeout; - up->uart_dma.rx_poll_rate = omap_up_info->dma_rx_poll_rate; - spin_lock_init(&(up->uart_dma.tx_lock)); - spin_lock_init(&(up->uart_dma.rx_lock)); - up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; - up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; - } up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; @@ -1531,12 +1413,13 @@ static int serial_omap_probe(struct platform_device *pdev) serial_omap_uart_wq = create_singlethread_workqueue(up->name); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); + platform_set_drvdata(pdev, up); + pm_runtime_enable(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, omap_up_info->autosuspend_timeout); pm_runtime_irq_safe(&pdev->dev); - pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(up); @@ -1548,8 +1431,8 @@ static int serial_omap_probe(struct platform_device *pdev) if (ret != 0) goto err_add_port; - pm_runtime_put(&pdev->dev); - platform_set_drvdata(pdev, up); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); return 0; err_add_port: @@ -1562,17 +1445,15 @@ err_port_line: return ret; } -static int serial_omap_remove(struct platform_device *dev) +static int __devexit serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); - if (up) { - pm_runtime_disable(&up->pdev->dev); - uart_remove_one_port(&serial_omap_reg, &up->port); - pm_qos_remove_request(&up->pm_qos_request); - } + pm_runtime_put_sync(up->dev); + pm_runtime_disable(up->dev); + uart_remove_one_port(&serial_omap_reg, &up->port); + pm_qos_remove_request(&up->pm_qos_request); - platform_set_drvdata(dev, NULL); return 0; } @@ -1602,7 +1483,7 @@ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1) timeout--; if (!timeout) { /* Should *never* happen. we warn and carry on */ - dev_crit(&up->pdev->dev, "Errata i202: timedout %x\n", + dev_crit(up->dev, "Errata i202: timedout %x\n", serial_in(up, UART_LSR)); break; } @@ -1648,29 +1529,23 @@ static int serial_omap_runtime_suspend(struct device *dev) if (!up) return -EINVAL; - if (!pdata || !pdata->enable_wakeup) + if (!pdata) return 0; - if (pdata->get_context_loss_count) - up->context_loss_cnt = pdata->get_context_loss_count(dev); + up->context_loss_cnt = serial_omap_get_context_loss_count(up); if (device_may_wakeup(dev)) { if (!up->wakeups_enabled) { - pdata->enable_wakeup(up->pdev, true); + serial_omap_enable_wakeup(up, true); up->wakeups_enabled = true; } } else { if (up->wakeups_enabled) { - pdata->enable_wakeup(up->pdev, false); + serial_omap_enable_wakeup(up, false); up->wakeups_enabled = false; } } - /* Errata i291 */ - if (up->use_dma && pdata->set_forceidle && - (up->errata & UART_ERRATA_i291_DMA_FORCEIDLE)) - pdata->set_forceidle(up->pdev); - up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; schedule_work(&up->qos_work); @@ -1680,24 +1555,14 @@ static int serial_omap_runtime_suspend(struct device *dev) static int serial_omap_runtime_resume(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - struct omap_uart_port_info *pdata = dev->platform_data; - - if (up && pdata) { - if (pdata->get_context_loss_count) { - u32 loss_cnt = pdata->get_context_loss_count(dev); - if (up->context_loss_cnt != loss_cnt) - serial_omap_restore_context(up); - } + u32 loss_cnt = serial_omap_get_context_loss_count(up); - /* Errata i291 */ - if (up->use_dma && pdata->set_noidle && - (up->errata & UART_ERRATA_i291_DMA_FORCEIDLE)) - pdata->set_noidle(up->pdev); + if (up->context_loss_cnt != loss_cnt) + serial_omap_restore_context(up); - up->latency = up->calc_latency; - schedule_work(&up->qos_work); - } + up->latency = up->calc_latency; + schedule_work(&up->qos_work); return 0; } @@ -1721,7 +1586,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match); static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, - .remove = serial_omap_remove, + .remove = __devexit_p(serial_omap_remove), .driver = { .name = DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 558ce85..4cd6c23 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -979,6 +979,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) priv->tx_dma_use = 1; priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC); + if (!priv->sg_tx_p) { + dev_err(priv->port.dev, "%s:kzalloc Failed\n", __func__); + return 0; + } sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */ sg = priv->sg_tx_p; diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 5847a4b..9033fc6 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -670,9 +670,19 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) { struct uart_pxa_port *up = serial_pxa_ports[co->index]; unsigned int ier; + unsigned long flags; + int locked = 1; clk_prepare_enable(up->clk); + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + /* * First save the IER then disable the interrupts */ @@ -688,6 +698,10 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) wait_for_xmitr(up); serial_out(up, UART_IER, ier); + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); + clk_disable_unprepare(up->clk); } diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 02d07bf..7f04717 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -82,7 +82,7 @@ static inline const char *s3c24xx_serial_portname(struct uart_port *port) static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) { - return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); + return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE; } /* @@ -268,7 +268,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) dbg("break!\n"); port->icount.brk++; if (uart_handle_break(port)) - goto ignore_char; + goto ignore_char; } if (uerstat & S3C2410_UERSTAT_FRAME) @@ -459,7 +459,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) s3c24xx_serial_portname(port), ourport); if (ret != 0) { - printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); + dev_err(port->dev, "cannot get irq %d\n", ourport->rx_irq); return ret; } @@ -473,7 +473,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) s3c24xx_serial_portname(port), ourport); if (ret) { - printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); + dev_err(port->dev, "cannot get irq %d\n", ourport->tx_irq); goto err; } @@ -502,7 +502,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, s3c24xx_serial_portname(port), ourport); if (ret) { - printk(KERN_ERR "cannot get irq %d\n", port->irq); + dev_err(port->dev, "cannot get irq %d\n", port->irq); return ret; } @@ -529,7 +529,7 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, switch (level) { case 3: - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + if (!IS_ERR(ourport->baudclk)) clk_disable(ourport->baudclk); clk_disable(ourport->clk); @@ -538,12 +538,12 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, case 0: clk_enable(ourport->clk); - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + if (!IS_ERR(ourport->baudclk)) clk_enable(ourport->baudclk); break; default: - printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level); + dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level); } } @@ -604,7 +604,6 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, char clkname[MAX_CLK_NAME_LENGTH]; int calc_deviation, deviation = (1 << 30) - 1; - *best_clk = NULL; clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel : ourport->info->def_clk_sel; for (cnt = 0; cnt < info->num_clks; cnt++) { @@ -613,7 +612,7 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, sprintf(clkname, "clk_uart_baud%d", cnt); clk = clk_get(ourport->port.dev, clkname); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) continue; rate = clk_get_rate(clk); @@ -684,7 +683,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, { struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); struct s3c24xx_uart_port *ourport = to_ourport(port); - struct clk *clk = NULL; + struct clk *clk = ERR_PTR(-EINVAL); unsigned long flags; unsigned int baud, quot, clk_sel = 0; unsigned int ulcon; @@ -705,7 +704,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel); if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; - if (!clk) + if (IS_ERR(clk)) return; /* check to see if we need to change clock source */ @@ -713,9 +712,9 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->baudclk != clk) { s3c24xx_serial_setsource(port, clk_sel); - if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { + if (!IS_ERR(ourport->baudclk)) { clk_disable(ourport->baudclk); - ourport->baudclk = NULL; + ourport->baudclk = ERR_PTR(-EINVAL); } clk_enable(clk); @@ -877,11 +876,24 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) static struct console s3c24xx_serial_console; +static int __init s3c24xx_serial_console_init(void) +{ + register_console(&s3c24xx_serial_console); + return 0; +} +console_initcall(s3c24xx_serial_console_init); + #define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console #else #define S3C24XX_SERIAL_CONSOLE NULL #endif +#ifdef CONFIG_CONSOLE_POLL +static int s3c24xx_serial_get_poll_char(struct uart_port *port); +static void s3c24xx_serial_put_poll_char(struct uart_port *port, + unsigned char c); +#endif + static struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, @@ -900,6 +912,10 @@ static struct uart_ops s3c24xx_serial_ops = { .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = s3c24xx_serial_get_poll_char, + .poll_put_char = s3c24xx_serial_put_poll_char, +#endif }; static struct uart_driver s3c24xx_uart_drv = { @@ -1036,10 +1052,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, if (tty == NULL) goto exit; - termios = tty->termios; + termios = &tty->termios; if (termios == NULL) { - printk(KERN_WARNING "%s: no termios?\n", __func__); + dev_warn(uport->dev, "%s: no termios?\n", __func__); goto exit; } @@ -1114,7 +1130,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, res = platform_get_resource(platdev, IORESOURCE_MEM, 0); if (res == NULL) { - printk(KERN_ERR "failed to find memory resource for uart\n"); + dev_err(port->dev, "failed to find memory resource for uart\n"); return -EINVAL; } @@ -1130,7 +1146,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->rx_irq = ret; ourport->tx_irq = ret + 1; } - + ret = platform_get_irq(platdev, 1); if (ret > 0) ourport->tx_irq = ret; @@ -1160,7 +1176,11 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); - return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name); + if (IS_ERR(ourport->baudclk)) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "* %s\n", + ourport->baudclk->name ?: "(null)"); } static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); @@ -1200,6 +1220,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) return -ENODEV; } + ourport->baudclk = ERR_PTR(-EINVAL); ourport->info = ourport->drv_data->info; ourport->cfg = (pdev->dev.platform_data) ? (struct s3c2410_uartcfg *)pdev->dev.platform_data : @@ -1312,6 +1333,36 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; } +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int s3c24xx_serial_get_poll_char(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + unsigned int ufstat; + + ufstat = rd_regl(port, S3C2410_UFSTAT); + if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) + return NO_POLL_CHAR; + + return rd_regb(port, S3C2410_URXH); +} + +static void s3c24xx_serial_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + + while (!s3c24xx_serial_console_txrdy(port, ufcon)) + cpu_relax(); + wr_regb(cons_uart, S3C2410_UTXH, c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { @@ -1387,7 +1438,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, sprintf(clk_name, "clk_uart_baud%d", clk_sel); clk = clk_get(port->dev, clk_name); - if (!IS_ERR(clk) && clk != NULL) + if (!IS_ERR(clk)) rate = clk_get_rate(clk); else rate = 1; @@ -1679,8 +1730,8 @@ static int __init s3c24xx_serial_modinit(void) ret = uart_register_driver(&s3c24xx_uart_drv); if (ret < 0) { - printk(KERN_ERR "failed to register UART driver\n"); - return -1; + pr_err("Failed to register Samsung UART driver\n"); + return ret; } return platform_driver_register(&samsung_serial_driver); diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c index e0b4b0a..9d66424 100644 --- a/drivers/tty/serial/sc26xx.c +++ b/drivers/tty/serial/sc26xx.c @@ -20,6 +20,10 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/irq.h> +#include <linux/io.h> + +#warning "Please try migrate to use new driver SCCNXP and report the status" \ + "in the linux-serial mailing list." #if defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c new file mode 100644 index 0000000..b7086d0 --- /dev/null +++ b/drivers/tty/serial/sccnxp.c @@ -0,0 +1,990 @@ +/* + * NXP (Philips) SCC+++(SCN+++) serial driver + * + * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * + * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de) + * + * 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 of the License, or + * (at your option) any later version. + */ + +#if defined(CONFIG_SERIAL_SCCNXP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/platform_device.h> +#include <linux/platform_data/sccnxp.h> + +#define SCCNXP_NAME "uart-sccnxp" +#define SCCNXP_MAJOR 204 +#define SCCNXP_MINOR 205 + +#define SCCNXP_MR_REG (0x00) +# define MR0_BAUD_NORMAL (0 << 0) +# define MR0_BAUD_EXT1 (1 << 0) +# define MR0_BAUD_EXT2 (5 << 0) +# define MR0_FIFO (1 << 3) +# define MR0_TXLVL (1 << 4) +# define MR1_BITS_5 (0 << 0) +# define MR1_BITS_6 (1 << 0) +# define MR1_BITS_7 (2 << 0) +# define MR1_BITS_8 (3 << 0) +# define MR1_PAR_EVN (0 << 2) +# define MR1_PAR_ODD (1 << 2) +# define MR1_PAR_NO (4 << 2) +# define MR2_STOP1 (7 << 0) +# define MR2_STOP2 (0xf << 0) +#define SCCNXP_SR_REG (0x01) +#define SCCNXP_CSR_REG SCCNXP_SR_REG +# define SR_RXRDY (1 << 0) +# define SR_FULL (1 << 1) +# define SR_TXRDY (1 << 2) +# define SR_TXEMT (1 << 3) +# define SR_OVR (1 << 4) +# define SR_PE (1 << 5) +# define SR_FE (1 << 6) +# define SR_BRK (1 << 7) +#define SCCNXP_CR_REG (0x02) +# define CR_RX_ENABLE (1 << 0) +# define CR_RX_DISABLE (1 << 1) +# define CR_TX_ENABLE (1 << 2) +# define CR_TX_DISABLE (1 << 3) +# define CR_CMD_MRPTR1 (0x01 << 4) +# define CR_CMD_RX_RESET (0x02 << 4) +# define CR_CMD_TX_RESET (0x03 << 4) +# define CR_CMD_STATUS_RESET (0x04 << 4) +# define CR_CMD_BREAK_RESET (0x05 << 4) +# define CR_CMD_START_BREAK (0x06 << 4) +# define CR_CMD_STOP_BREAK (0x07 << 4) +# define CR_CMD_MRPTR0 (0x0b << 4) +#define SCCNXP_RHR_REG (0x03) +#define SCCNXP_THR_REG SCCNXP_RHR_REG +#define SCCNXP_IPCR_REG (0x04) +#define SCCNXP_ACR_REG SCCNXP_IPCR_REG +# define ACR_BAUD0 (0 << 7) +# define ACR_BAUD1 (1 << 7) +# define ACR_TIMER_MODE (6 << 4) +#define SCCNXP_ISR_REG (0x05) +#define SCCNXP_IMR_REG SCCNXP_ISR_REG +# define IMR_TXRDY (1 << 0) +# define IMR_RXRDY (1 << 1) +# define ISR_TXRDY(x) (1 << ((x * 4) + 0)) +# define ISR_RXRDY(x) (1 << ((x * 4) + 1)) +#define SCCNXP_IPR_REG (0x0d) +#define SCCNXP_OPCR_REG SCCNXP_IPR_REG +#define SCCNXP_SOP_REG (0x0e) +#define SCCNXP_ROP_REG (0x0f) + +/* Route helpers */ +#define MCTRL_MASK(sig) (0xf << (sig)) +#define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0) +#define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0) + +/* Supported chip types */ +enum { + SCCNXP_TYPE_SC2681 = 2681, + SCCNXP_TYPE_SC2691 = 2691, + SCCNXP_TYPE_SC2692 = 2692, + SCCNXP_TYPE_SC2891 = 2891, + SCCNXP_TYPE_SC2892 = 2892, + SCCNXP_TYPE_SC28202 = 28202, + SCCNXP_TYPE_SC68681 = 68681, + SCCNXP_TYPE_SC68692 = 68692, +}; + +struct sccnxp_port { + struct uart_driver uart; + struct uart_port port[SCCNXP_MAX_UARTS]; + + const char *name; + int irq; + + u8 imr; + u8 addr_mask; + int freq_std; + + int flags; +#define SCCNXP_HAVE_IO 0x00000001 +#define SCCNXP_HAVE_MR0 0x00000002 + +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE + struct console console; +#endif + + struct mutex sccnxp_mutex; + + struct sccnxp_pdata pdata; +}; + +static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) +{ + return readb(base + (reg << shift)); +} + +static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v) +{ + writeb(v, base + (reg << shift)); +} + +static inline u8 sccnxp_read(struct uart_port *port, u8 reg) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + return sccnxp_raw_read(port->membase, reg & s->addr_mask, + port->regshift); +} + +static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v); +} + +static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) +{ + return sccnxp_read(port, (port->line << 3) + reg); +} + +static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) +{ + sccnxp_write(port, (port->line << 3) + reg, v); +} + +static int sccnxp_update_best_err(int a, int b, int *besterr) +{ + int err = abs(a - b); + + if ((*besterr < 0) || (*besterr > err)) { + *besterr = err; + return 0; + } + + return 1; +} + +struct baud_table { + u8 csr; + u8 acr; + u8 mr0; + int baud; +}; + +const struct baud_table baud_std[] = { + { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, + { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, + { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, + { 2, ACR_BAUD0, MR0_BAUD_NORMAL, 134, }, + { 3, ACR_BAUD1, MR0_BAUD_NORMAL, 150, }, + { 3, ACR_BAUD0, MR0_BAUD_NORMAL, 200, }, + { 4, ACR_BAUD0, MR0_BAUD_NORMAL, 300, }, + { 0, ACR_BAUD1, MR0_BAUD_EXT1, 450, }, + { 1, ACR_BAUD0, MR0_BAUD_EXT2, 880, }, + { 3, ACR_BAUD1, MR0_BAUD_EXT1, 900, }, + { 5, ACR_BAUD0, MR0_BAUD_NORMAL, 600, }, + { 7, ACR_BAUD0, MR0_BAUD_NORMAL, 1050, }, + { 2, ACR_BAUD0, MR0_BAUD_EXT2, 1076, }, + { 6, ACR_BAUD0, MR0_BAUD_NORMAL, 1200, }, + { 10, ACR_BAUD1, MR0_BAUD_NORMAL, 1800, }, + { 7, ACR_BAUD1, MR0_BAUD_NORMAL, 2000, }, + { 8, ACR_BAUD0, MR0_BAUD_NORMAL, 2400, }, + { 5, ACR_BAUD1, MR0_BAUD_EXT1, 3600, }, + { 9, ACR_BAUD0, MR0_BAUD_NORMAL, 4800, }, + { 10, ACR_BAUD0, MR0_BAUD_NORMAL, 7200, }, + { 11, ACR_BAUD0, MR0_BAUD_NORMAL, 9600, }, + { 8, ACR_BAUD0, MR0_BAUD_EXT1, 14400, }, + { 12, ACR_BAUD1, MR0_BAUD_NORMAL, 19200, }, + { 9, ACR_BAUD0, MR0_BAUD_EXT1, 28800, }, + { 12, ACR_BAUD0, MR0_BAUD_NORMAL, 38400, }, + { 11, ACR_BAUD0, MR0_BAUD_EXT1, 57600, }, + { 12, ACR_BAUD1, MR0_BAUD_EXT1, 115200, }, + { 12, ACR_BAUD0, MR0_BAUD_EXT1, 230400, }, + { 0, 0, 0, 0 } +}; + +static int sccnxp_set_baud(struct uart_port *port, int baud) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + int div_std, tmp_baud, bestbaud = baud, besterr = -1; + u8 i, acr = 0, csr = 0, mr0 = 0; + + /* Find best baud from table */ + for (i = 0; baud_std[i].baud && besterr; i++) { + if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0)) + continue; + div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud); + tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std); + if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) { + acr = baud_std[i].acr; + csr = baud_std[i].csr; + mr0 = baud_std[i].mr0; + bestbaud = tmp_baud; + } + } + + if (s->flags & SCCNXP_HAVE_MR0) { + /* Enable FIFO, set half level for TX */ + mr0 |= MR0_FIFO | MR0_TXLVL; + /* Update MR0 */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0); + sccnxp_port_write(port, SCCNXP_MR_REG, mr0); + } + + sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE); + sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr); + + if (baud != bestbaud) + dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n", + baud, bestbaud); + + return bestbaud; +} + +static void sccnxp_enable_irq(struct uart_port *port, int mask) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + s->imr |= mask << (port->line * 4); + sccnxp_write(port, SCCNXP_IMR_REG, s->imr); +} + +static void sccnxp_disable_irq(struct uart_port *port, int mask) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + s->imr &= ~(mask << (port->line * 4)); + sccnxp_write(port, SCCNXP_IMR_REG, s->imr); +} + +static void sccnxp_set_bit(struct uart_port *port, int sig, int state) +{ + u8 bitmask; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) { + bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig); + if (state) + sccnxp_write(port, SCCNXP_SOP_REG, bitmask); + else + sccnxp_write(port, SCCNXP_ROP_REG, bitmask); + } +} + +static void sccnxp_handle_rx(struct uart_port *port) +{ + u8 sr; + unsigned int ch, flag; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) + return; + + for (;;) { + sr = sccnxp_port_read(port, SCCNXP_SR_REG); + if (!(sr & SR_RXRDY)) + break; + sr &= SR_PE | SR_FE | SR_OVR | SR_BRK; + + ch = sccnxp_port_read(port, SCCNXP_RHR_REG); + + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(sr)) { + if (sr & SR_BRK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (sr & SR_PE) + port->icount.parity++; + else if (sr & SR_FE) + port->icount.frame++; + else if (sr & SR_OVR) + port->icount.overrun++; + + sr &= port->read_status_mask; + if (sr & SR_BRK) + flag = TTY_BREAK; + else if (sr & SR_PE) + flag = TTY_PARITY; + else if (sr & SR_FE) + flag = TTY_FRAME; + else if (sr & SR_OVR) + flag = TTY_OVERRUN; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (sr & port->ignore_status_mask) + continue; + + uart_insert_char(port, sr, SR_OVR, ch, flag); + } + + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void sccnxp_handle_tx(struct uart_port *port) +{ + u8 sr; + struct circ_buf *xmit = &port->state->xmit; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (unlikely(port->x_char)) { + sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + /* Disable TX if FIFO is empty */ + if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) { + sccnxp_disable_irq(port, IMR_TXRDY); + + /* Set direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 0); + } + return; + } + + while (!uart_circ_empty(xmit)) { + sr = sccnxp_port_read(port, SCCNXP_SR_REG); + if (!(sr & SR_TXRDY)) + break; + + sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t sccnxp_ist(int irq, void *dev_id) +{ + int i; + u8 isr; + struct sccnxp_port *s = (struct sccnxp_port *)dev_id; + + mutex_lock(&s->sccnxp_mutex); + + for (;;) { + isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); + isr &= s->imr; + if (!isr) + break; + + dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); + + for (i = 0; i < s->uart.nr; i++) { + if (isr & ISR_RXRDY(i)) + sccnxp_handle_rx(&s->port[i]); + if (isr & ISR_TXRDY(i)) + sccnxp_handle_tx(&s->port[i]); + } + } + + mutex_unlock(&s->sccnxp_mutex); + + return IRQ_HANDLED; +} + +static void sccnxp_start_tx(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + /* Set direction to output */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 1); + + sccnxp_enable_irq(port, IMR_TXRDY); + + mutex_unlock(&s->sccnxp_mutex); +} + +static void sccnxp_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +static void sccnxp_stop_rx(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); + mutex_unlock(&s->sccnxp_mutex); +} + +static unsigned int sccnxp_tx_empty(struct uart_port *port) +{ + u8 val; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + val = sccnxp_port_read(port, SCCNXP_SR_REG); + mutex_unlock(&s->sccnxp_mutex); + + return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; +} + +static void sccnxp_enable_ms(struct uart_port *port) +{ + /* Do nothing */ +} + +static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + if (!(s->flags & SCCNXP_HAVE_IO)) + return; + + mutex_lock(&s->sccnxp_mutex); + + sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); + sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); + + mutex_unlock(&s->sccnxp_mutex); +} + +static unsigned int sccnxp_get_mctrl(struct uart_port *port) +{ + u8 bitmask, ipr; + struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; + + if (!(s->flags & SCCNXP_HAVE_IO)) + return mctrl; + + mutex_lock(&s->sccnxp_mutex); + + ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); + + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + DSR_IP); + mctrl &= ~TIOCM_DSR; + mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + CTS_IP); + mctrl &= ~TIOCM_CTS; + mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + DCD_IP); + mctrl &= ~TIOCM_CAR; + mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0; + } + if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) { + bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line], + RNG_IP); + mctrl &= ~TIOCM_RNG; + mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; + } + + mutex_unlock(&s->sccnxp_mutex); + + return mctrl; +} + +static void sccnxp_break_ctl(struct uart_port *port, int break_state) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? + CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); + mutex_unlock(&s->sccnxp_mutex); +} + +static void sccnxp_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + u8 mr1, mr2; + int baud; + + mutex_lock(&s->sccnxp_mutex); + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Disable RX & TX, reset break condition, status and FIFOs */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET | + CR_RX_DISABLE | CR_TX_DISABLE); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET); + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + mr1 = MR1_BITS_5; + break; + case CS6: + mr1 = MR1_BITS_6; + break; + case CS7: + mr1 = MR1_BITS_7; + break; + case CS8: + default: + mr1 = MR1_BITS_8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + mr1 |= MR1_PAR_ODD; + } else + mr1 |= MR1_PAR_NO; + + /* Stop bits */ + mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1; + + /* Update desired format */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1); + sccnxp_port_write(port, SCCNXP_MR_REG, mr1); + sccnxp_port_write(port, SCCNXP_MR_REG, mr2); + + /* Set read status mask */ + port->read_status_mask = SR_OVR; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SR_PE | SR_FE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BRK; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= SR_BRK; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK; + + /* Setup baudrate */ + baud = uart_get_baud_rate(port, termios, old, 50, + (s->flags & SCCNXP_HAVE_MR0) ? + 230400 : 38400); + baud = sccnxp_set_baud(port, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* Enable RX & TX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); + + mutex_unlock(&s->sccnxp_mutex); +} + +static int sccnxp_startup(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + if (s->flags & SCCNXP_HAVE_IO) { + /* Outputs are controlled manually */ + sccnxp_write(port, SCCNXP_OPCR_REG, 0); + } + + /* Reset break condition, status and FIFOs */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET); + sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET); + + /* Enable RX & TX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); + + /* Enable RX interrupt */ + sccnxp_enable_irq(port, IMR_RXRDY); + + mutex_unlock(&s->sccnxp_mutex); + + return 0; +} + +static void sccnxp_shutdown(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + mutex_lock(&s->sccnxp_mutex); + + /* Disable interrupts */ + sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + + /* Disable TX & RX */ + sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE); + + /* Leave direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(port, DIR_OP, 0); + + mutex_unlock(&s->sccnxp_mutex); +} + +static const char *sccnxp_type(struct uart_port *port) +{ + struct sccnxp_port *s = dev_get_drvdata(port->dev); + + return (port->type == PORT_SC26XX) ? s->name : NULL; +} + +static void sccnxp_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +static int sccnxp_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void sccnxp_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SC26XX; +} + +static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s) +{ + if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX)) + return 0; + if (s->irq == port->irq) + return 0; + + return -EINVAL; +} + +static const struct uart_ops sccnxp_ops = { + .tx_empty = sccnxp_tx_empty, + .set_mctrl = sccnxp_set_mctrl, + .get_mctrl = sccnxp_get_mctrl, + .stop_tx = sccnxp_stop_tx, + .start_tx = sccnxp_start_tx, + .stop_rx = sccnxp_stop_rx, + .enable_ms = sccnxp_enable_ms, + .break_ctl = sccnxp_break_ctl, + .startup = sccnxp_startup, + .shutdown = sccnxp_shutdown, + .set_termios = sccnxp_set_termios, + .type = sccnxp_type, + .release_port = sccnxp_release_port, + .request_port = sccnxp_request_port, + .config_port = sccnxp_config_port, + .verify_port = sccnxp_verify_port, +}; + +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE +static void sccnxp_console_putchar(struct uart_port *port, int c) +{ + int tryes = 100000; + + while (tryes--) { + if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) { + sccnxp_port_write(port, SCCNXP_THR_REG, c); + break; + } + barrier(); + } +} + +static void sccnxp_console_write(struct console *co, const char *c, unsigned n) +{ + struct sccnxp_port *s = (struct sccnxp_port *)co->data; + struct uart_port *port = &s->port[co->index]; + + mutex_lock(&s->sccnxp_mutex); + uart_console_write(port, c, n, sccnxp_console_putchar); + mutex_unlock(&s->sccnxp_mutex); +} + +static int sccnxp_console_setup(struct console *co, char *options) +{ + struct sccnxp_port *s = (struct sccnxp_port *)co->data; + struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; + int baud = 9600, bits = 8, parity = 'n', flow = 'n'; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} +#endif + +static int __devinit sccnxp_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int chiptype = pdev->id_entry->driver_data; + struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); + int i, ret, fifosize, freq_min, freq_max; + struct sccnxp_port *s; + void __iomem *membase; + + if (!res) { + dev_err(&pdev->dev, "Missing memory resource data\n"); + return -EADDRNOTAVAIL; + } + + dev_set_name(&pdev->dev, SCCNXP_NAME); + + s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); + if (!s) { + dev_err(&pdev->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, s); + + mutex_init(&s->sccnxp_mutex); + + /* Individual chip settings */ + switch (chiptype) { + case SCCNXP_TYPE_SC2681: + s->name = "SC2681"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2691: + s->name = "SC2691"; + s->uart.nr = 1; + s->freq_std = 3686400; + s->addr_mask = 0x07; + s->flags = 0; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2692: + s->name = "SC2692"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC2891: + s->name = "SC2891"; + s->uart.nr = 1; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 16; + freq_min = 100000; + freq_max = 8000000; + break; + case SCCNXP_TYPE_SC2892: + s->name = "SC2892"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 16; + freq_min = 100000; + freq_max = 8000000; + break; + case SCCNXP_TYPE_SC28202: + s->name = "SC28202"; + s->uart.nr = 2; + s->freq_std = 14745600; + s->addr_mask = 0x7f; + s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; + fifosize = 256; + freq_min = 1000000; + freq_max = 50000000; + break; + case SCCNXP_TYPE_SC68681: + s->name = "SC68681"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + case SCCNXP_TYPE_SC68692: + s->name = "SC68692"; + s->uart.nr = 2; + s->freq_std = 3686400; + s->addr_mask = 0x0f; + s->flags = SCCNXP_HAVE_IO; + fifosize = 3; + freq_min = 1000000; + freq_max = 4000000; + break; + default: + dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype); + ret = -ENOTSUPP; + goto err_out; + } + + if (!pdata) { + dev_warn(&pdev->dev, + "No platform data supplied, using defaults\n"); + s->pdata.frequency = s->freq_std; + } else + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + + s->irq = platform_get_irq(pdev, 0); + if (s->irq <= 0) { + dev_err(&pdev->dev, "Missing irq resource data\n"); + ret = -ENXIO; + goto err_out; + } + + /* Check input frequency */ + if ((s->pdata.frequency < freq_min) || + (s->pdata.frequency > freq_max)) { + dev_err(&pdev->dev, "Frequency out of bounds\n"); + ret = -EINVAL; + goto err_out; + } + + membase = devm_request_and_ioremap(&pdev->dev, res); + if (!membase) { + dev_err(&pdev->dev, "Failed to ioremap\n"); + ret = -EIO; + goto err_out; + } + + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttySC"; + s->uart.major = SCCNXP_MAJOR; + s->uart.minor = SCCNXP_MINOR; +#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE + s->uart.cons = &s->console; + s->uart.cons->device = uart_console_device; + s->uart.cons->write = sccnxp_console_write; + s->uart.cons->setup = sccnxp_console_setup; + s->uart.cons->flags = CON_PRINTBUFFER; + s->uart.cons->index = -1; + s->uart.cons->data = s; + strcpy(s->uart.cons->name, "ttySC"); +#endif + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(&pdev->dev, "Registering UART driver failed\n"); + goto err_out; + } + + for (i = 0; i < s->uart.nr; i++) { + s->port[i].line = i; + s->port[i].dev = &pdev->dev; + s->port[i].irq = s->irq; + s->port[i].type = PORT_SC26XX; + s->port[i].fifosize = fifosize; + s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port[i].iotype = UPIO_MEM; + s->port[i].mapbase = res->start; + s->port[i].membase = membase; + s->port[i].regshift = s->pdata.reg_shift; + s->port[i].uartclk = s->pdata.frequency; + s->port[i].ops = &sccnxp_ops; + uart_add_one_port(&s->uart, &s->port[i]); + /* Set direction to input */ + if (s->flags & SCCNXP_HAVE_IO) + sccnxp_set_bit(&s->port[i], DIR_OP, 0); + } + + /* Disable interrupts */ + s->imr = 0; + sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); + + /* Board specific configure */ + if (s->pdata.init) + s->pdata.init(); + + ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&pdev->dev), s); + if (!ret) + return 0; + + dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + +err_out: + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int __devexit sccnxp_remove(struct platform_device *pdev) +{ + int i; + struct sccnxp_port *s = platform_get_drvdata(pdev); + + devm_free_irq(&pdev->dev, s->irq, s); + + for (i = 0; i < s->uart.nr; i++) + uart_remove_one_port(&s->uart, &s->port[i]); + + uart_unregister_driver(&s->uart); + platform_set_drvdata(pdev, NULL); + + if (s->pdata.exit) + s->pdata.exit(); + + return 0; +} + +static const struct platform_device_id sccnxp_id_table[] = { + { "sc2681", SCCNXP_TYPE_SC2681 }, + { "sc2691", SCCNXP_TYPE_SC2691 }, + { "sc2692", SCCNXP_TYPE_SC2692 }, + { "sc2891", SCCNXP_TYPE_SC2891 }, + { "sc2892", SCCNXP_TYPE_SC2892 }, + { "sc28202", SCCNXP_TYPE_SC28202 }, + { "sc68681", SCCNXP_TYPE_SC68681 }, + { "sc68692", SCCNXP_TYPE_SC68692 }, +}; +MODULE_DEVICE_TABLE(platform, sccnxp_id_table); + +static struct platform_driver sccnxp_uart_driver = { + .driver = { + .name = SCCNXP_NAME, + .owner = THIS_MODULE, + }, + .probe = sccnxp_probe, + .remove = __devexit_p(sccnxp_remove), + .id_table = sccnxp_id_table, +}; +module_platform_driver(sccnxp_uart_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("SCCNXP serial driver"); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a21dc8e..0fcfd98 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -159,7 +159,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, retval = uport->ops->startup(uport); if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { - tty->termios->c_cflag = uport->cons->cflag; + tty->termios.c_cflag = uport->cons->cflag; uport->cons->cflag = 0; } /* @@ -172,11 +172,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, * Setup the RTS and DTR signals once the * port is open and ready to respond. */ - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -240,7 +240,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (!tty || (tty->termios->c_cflag & HUPCL)) + if (!tty || (tty->termios.c_cflag & HUPCL)) uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); uart_port_shutdown(port); @@ -440,10 +440,10 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, * If we have no tty, termios, or the port does not exist, * then we can't set the parameters for this port. */ - if (!tty || !tty->termios || uport->type == PORT_UNKNOWN) + if (!tty || uport->type == PORT_UNKNOWN) return; - termios = tty->termios; + termios = &tty->termios; /* * Set flags based on termios cflag @@ -614,7 +614,7 @@ static void uart_throttle(struct tty_struct *tty) if (I_IXOFF(tty)) uart_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) uart_clear_mctrl(state->uart_port, TIOCM_RTS); } @@ -630,42 +630,48 @@ static void uart_unthrottle(struct tty_struct *tty) uart_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) + if (tty->termios.c_cflag & CRTSCTS) uart_set_mctrl(port, TIOCM_RTS); } -static int uart_get_info(struct uart_state *state, - struct serial_struct __user *retinfo) +static void uart_get_info(struct tty_port *port, + struct uart_state *state, + struct serial_struct *retinfo) { struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - struct serial_struct tmp; - memset(&tmp, 0, sizeof(tmp)); + memset(retinfo, 0, sizeof(*retinfo)); - /* Ensure the state we copy is consistent and no hardware changes - occur as we go */ - mutex_lock(&port->mutex); - - tmp.type = uport->type; - tmp.line = uport->line; - tmp.port = uport->iobase; + retinfo->type = uport->type; + retinfo->line = uport->line; + retinfo->port = uport->iobase; if (HIGH_BITS_OFFSET) - tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET; - tmp.irq = uport->irq; - tmp.flags = uport->flags; - tmp.xmit_fifo_size = uport->fifosize; - tmp.baud_base = uport->uartclk / 16; - tmp.close_delay = jiffies_to_msecs(port->close_delay) / 10; - tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? + retinfo->port_high = (long) uport->iobase >> HIGH_BITS_OFFSET; + retinfo->irq = uport->irq; + retinfo->flags = uport->flags; + retinfo->xmit_fifo_size = uport->fifosize; + retinfo->baud_base = uport->uartclk / 16; + retinfo->close_delay = jiffies_to_msecs(port->close_delay) / 10; + retinfo->closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : jiffies_to_msecs(port->closing_wait) / 10; - tmp.custom_divisor = uport->custom_divisor; - tmp.hub6 = uport->hub6; - tmp.io_type = uport->iotype; - tmp.iomem_reg_shift = uport->regshift; - tmp.iomem_base = (void *)(unsigned long)uport->mapbase; + retinfo->custom_divisor = uport->custom_divisor; + retinfo->hub6 = uport->hub6; + retinfo->io_type = uport->iotype; + retinfo->iomem_reg_shift = uport->regshift; + retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; +} + +static int uart_get_info_user(struct uart_state *state, + struct serial_struct __user *retinfo) +{ + struct tty_port *port = &state->port; + struct serial_struct tmp; + /* Ensure the state we copy is consistent and no hardware changes + occur as we go */ + mutex_lock(&port->mutex); + uart_get_info(port, state, &tmp); mutex_unlock(&port->mutex); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) @@ -673,42 +679,30 @@ static int uart_get_info(struct uart_state *state, return 0; } -static int uart_set_info(struct tty_struct *tty, struct uart_state *state, - struct serial_struct __user *newinfo) +static int uart_set_info(struct tty_struct *tty, struct tty_port *port, + struct uart_state *state, + struct serial_struct *new_info) { - struct serial_struct new_serial; struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor, close_delay; upf_t old_flags, new_flags; int retval = 0; - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - new_port = new_serial.port; + new_port = new_info->port; if (HIGH_BITS_OFFSET) - new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + new_port += (unsigned long) new_info->port_high << HIGH_BITS_OFFSET; - new_serial.irq = irq_canonicalize(new_serial.irq); - close_delay = msecs_to_jiffies(new_serial.close_delay * 10); - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + new_info->irq = irq_canonicalize(new_info->irq); + close_delay = msecs_to_jiffies(new_info->close_delay * 10); + closing_wait = new_info->closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : - msecs_to_jiffies(new_serial.closing_wait * 10); + msecs_to_jiffies(new_info->closing_wait * 10); - /* - * This semaphore protects port->count. It is also - * very useful to prevent opens. Also, take the - * port configuration semaphore to make sure that a - * module insertion/removal doesn't change anything - * under us. - */ - mutex_lock(&port->mutex); change_irq = !(uport->flags & UPF_FIXED_PORT) - && new_serial.irq != uport->irq; + && new_info->irq != uport->irq; /* * Since changing the 'type' of the port changes its resource @@ -717,29 +711,29 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, */ change_port = !(uport->flags & UPF_FIXED_PORT) && (new_port != uport->iobase || - (unsigned long)new_serial.iomem_base != uport->mapbase || - new_serial.hub6 != uport->hub6 || - new_serial.io_type != uport->iotype || - new_serial.iomem_reg_shift != uport->regshift || - new_serial.type != uport->type); + (unsigned long)new_info->iomem_base != uport->mapbase || + new_info->hub6 != uport->hub6 || + new_info->io_type != uport->iotype || + new_info->iomem_reg_shift != uport->regshift || + new_info->type != uport->type); old_flags = uport->flags; - new_flags = new_serial.flags; + new_flags = new_info->flags; old_custom_divisor = uport->custom_divisor; if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM; if (change_irq || change_port || - (new_serial.baud_base != uport->uartclk / 16) || + (new_info->baud_base != uport->uartclk / 16) || (close_delay != port->close_delay) || (closing_wait != port->closing_wait) || - (new_serial.xmit_fifo_size && - new_serial.xmit_fifo_size != uport->fifosize) || + (new_info->xmit_fifo_size && + new_info->xmit_fifo_size != uport->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) goto exit; uport->flags = ((uport->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); - uport->custom_divisor = new_serial.custom_divisor; + uport->custom_divisor = new_info->custom_divisor; goto check_and_exit; } @@ -747,10 +741,10 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, * Ask the low level driver to verify the settings. */ if (uport->ops->verify_port) - retval = uport->ops->verify_port(uport, &new_serial); + retval = uport->ops->verify_port(uport, new_info); - if ((new_serial.irq >= nr_irqs) || (new_serial.irq < 0) || - (new_serial.baud_base < 9600)) + if ((new_info->irq >= nr_irqs) || (new_info->irq < 0) || + (new_info->baud_base < 9600)) retval = -EINVAL; if (retval) @@ -790,11 +784,11 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, uport->ops->release_port(uport); uport->iobase = new_port; - uport->type = new_serial.type; - uport->hub6 = new_serial.hub6; - uport->iotype = new_serial.io_type; - uport->regshift = new_serial.iomem_reg_shift; - uport->mapbase = (unsigned long)new_serial.iomem_base; + uport->type = new_info->type; + uport->hub6 = new_info->hub6; + uport->iotype = new_info->io_type; + uport->regshift = new_info->iomem_reg_shift; + uport->mapbase = (unsigned long)new_info->iomem_base; /* * Claim and map the new regions @@ -835,16 +829,16 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, } if (change_irq) - uport->irq = new_serial.irq; + uport->irq = new_info->irq; if (!(uport->flags & UPF_FIXED_PORT)) - uport->uartclk = new_serial.baud_base * 16; + uport->uartclk = new_info->baud_base * 16; uport->flags = (uport->flags & ~UPF_CHANGE_MASK) | (new_flags & UPF_CHANGE_MASK); - uport->custom_divisor = new_serial.custom_divisor; + uport->custom_divisor = new_info->custom_divisor; port->close_delay = close_delay; port->closing_wait = closing_wait; - if (new_serial.xmit_fifo_size) - uport->fifosize = new_serial.xmit_fifo_size; + if (new_info->xmit_fifo_size) + uport->fifosize = new_info->xmit_fifo_size; if (port->tty) port->tty->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; @@ -873,6 +867,28 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state, } else retval = uart_startup(tty, state, 1); exit: + return retval; +} + +static int uart_set_info_user(struct tty_struct *tty, struct uart_state *state, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + struct tty_port *port = &state->port; + int retval; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + /* + * This semaphore protects port->count. It is also + * very useful to prevent opens. Also, take the + * port configuration semaphore to make sure that a + * module insertion/removal doesn't change anything + * under us. + */ + mutex_lock(&port->mutex); + retval = uart_set_info(tty, port, state, &new_serial); mutex_unlock(&port->mutex); return retval; } @@ -1115,11 +1131,11 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, */ switch (cmd) { case TIOCGSERIAL: - ret = uart_get_info(state, uarg); + ret = uart_get_info_user(state, uarg); break; case TIOCSSERIAL: - ret = uart_set_info(tty, state, uarg); + ret = uart_set_info_user(tty, state, uarg); break; case TIOCSERCONFIG: @@ -1187,7 +1203,7 @@ static void uart_set_ldisc(struct tty_struct *tty) struct uart_port *uport = state->uart_port; if (uport->ops->set_ldisc) - uport->ops->set_ldisc(uport, tty->termios->c_line); + uport->ops->set_ldisc(uport, tty->termios.c_line); } static void uart_set_termios(struct tty_struct *tty, @@ -1195,7 +1211,7 @@ static void uart_set_termios(struct tty_struct *tty, { struct uart_state *state = tty->driver_data; unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; /* @@ -1206,9 +1222,9 @@ static void uart_set_termios(struct tty_struct *tty, */ #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) if ((cflag ^ old_termios->c_cflag) == 0 && - tty->termios->c_ospeed == old_termios->c_ospeed && - tty->termios->c_ispeed == old_termios->c_ispeed && - RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) { + tty->termios.c_ospeed == old_termios->c_ospeed && + tty->termios.c_ispeed == old_termios->c_ispeed && + RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) { return; } @@ -1960,8 +1976,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) /* * If that's unset, use the tty termios setting. */ - if (port->tty && port->tty->termios && termios.c_cflag == 0) - termios = *(port->tty->termios); + if (port->tty && termios.c_cflag == 0) + termios = port->tty->termios; if (console_suspend_enabled) uart_change_pm(state, 0); @@ -2113,6 +2129,7 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; + int ret; if (!state || !state->uart_port) return -1; @@ -2121,6 +2138,22 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) if (!(port->ops->poll_get_char && port->ops->poll_put_char)) return -1; + if (port->ops->poll_init) { + struct tty_port *tport = &state->port; + + ret = 0; + mutex_lock(&tport->mutex); + /* + * We don't set ASYNCB_INITIALIZED as we only initialized the + * hw, e.g. state->xmit is still uninitialized. + */ + if (!test_bit(ASYNCB_INITIALIZED, &tport->flags)) + ret = port->ops->poll_init(port); + mutex_unlock(&tport->mutex); + if (ret) + return ret; + } + if (options) { uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(port, NULL, baud, parity, bits, flow); @@ -2293,6 +2326,36 @@ struct tty_driver *uart_console_device(struct console *co, int *index) return p->tty_driver; } +static ssize_t uart_get_attr_uartclk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + + mutex_lock(&state->port.mutex); + ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk); + mutex_unlock(&state->port.mutex); + + return ret; +} + +static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL); + +static struct attribute *tty_dev_attrs[] = { + &dev_attr_uartclk.attr, + NULL, + }; + +static const struct attribute_group tty_dev_attr_group = { + .attrs = tty_dev_attrs, + }; + +static const struct attribute_group *tty_dev_attr_groups[] = { + &tty_dev_attr_group, + NULL + }; + /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port @@ -2346,7 +2409,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ - tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); + tty_dev = tty_port_register_device_attr(port, drv->tty_driver, + uport->line, uport->dev, port, tty_dev_attr_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { @@ -2454,9 +2518,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { struct uart_state *state = uport->state; struct tty_port *port = &state->port; - struct tty_ldisc *ld = tty_ldisc_ref(port->tty); + struct tty_ldisc *ld = NULL; struct pps_event_time ts; + struct tty_struct *tty = port->tty; + if (tty) + ld = tty_ldisc_ref(tty); if (ld && ld->ops->dcd_change) pps_get_ts(&ts); @@ -2469,12 +2536,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) if (port->flags & ASYNC_CHECK_CD) { if (status) wake_up_interruptible(&port->open_wait); - else if (port->tty) - tty_hangup(port->tty); + else if (tty) + tty_hangup(tty); } if (ld && ld->ops->dcd_change) - ld->ops->dcd_change(port->tty, status, &ts); + ld->ops->dcd_change(tty, status, &ts); if (ld) tty_ldisc_deref(ld); } @@ -2492,7 +2559,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - if (port->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(port)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 5b3eda2..a9e2bd1 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -668,7 +668,7 @@ int sirfsoc_uart_probe(struct platform_device *pdev) if (res == NULL) { dev_err(&pdev->dev, "Insufficient resources.\n"); ret = -EFAULT; - goto irq_err; + goto err; } port->irq = res->start; @@ -676,7 +676,7 @@ int sirfsoc_uart_probe(struct platform_device *pdev) sirfport->p = pinctrl_get_select_default(&pdev->dev); ret = IS_ERR(sirfport->p); if (ret) - goto pin_err; + goto err; } port->ops = &sirfsoc_uart_ops; @@ -695,9 +695,6 @@ port_err: platform_set_drvdata(pdev, NULL); if (sirfport->hw_flow_ctrl) pinctrl_put(sirfport->p); -pin_err: -irq_err: - devm_iounmap(&pdev->dev, port->membase); err: return ret; } @@ -709,7 +706,6 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (sirfport->hw_flow_ctrl) pinctrl_put(sirfport->p); - devm_iounmap(&pdev->dev, port->membase); uart_remove_one_port(&sirfsoc_uart_drv, port); return 0; } diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 675303b..b97913d 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -58,10 +58,16 @@ enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT }; static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" }; +struct serial_uart_config { + char *name; + int dfl_xmit_fifo_size; + int flags; +}; + /* * Here we define the default xmit fifo size used for each type of UART. */ -static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { +static const struct serial_uart_config uart_config[] = { { "unknown", 1, 0 }, { "8250", 1, 0 }, { "16450", 1, 0 }, diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 593d40a..70e3a52 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1359,7 +1359,7 @@ static void mgsl_isr_io_pin( struct mgsl_struct *info ) } } - if ( (info->port.flags & ASYNC_CTS_FLOW) && + if (tty_port_cts_enabled(&info->port) && (status & MISCSTATUS_CTS_LATCHED) ) { if (info->port.tty->hw_stopped) { if (status & MISCSTATUS_CTS) { @@ -1840,22 +1840,22 @@ static void shutdown(struct mgsl_struct * info) usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); - + /* Disable DMAEN (Port 7, Bit 14) */ /* This disconnects the DMA request signal from the ISA bus */ /* on the ISA adapter. This has no effect for the PCI adapter */ usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14)); - + /* Disable INTEN (Port 6, Bit12) */ /* This disconnects the IRQ request signal to the ISA bus */ /* on the ISA adapter. This has no effect for the PCI adapter */ usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); - - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + + if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); usc_set_serial_signals(info); } - + spin_unlock_irqrestore(&info->irq_spinlock,flags); mgsl_release_resources(info); @@ -1895,7 +1895,7 @@ static void mgsl_program_hw(struct mgsl_struct *info) usc_EnableInterrupts(info, IO_PIN); usc_get_serial_signals(info); - if (info->netcount || info->port.tty->termios->c_cflag & CREAD) + if (info->netcount || info->port.tty->termios.c_cflag & CREAD) usc_start_receiver(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -1908,14 +1908,14 @@ static void mgsl_change_params(struct mgsl_struct *info) unsigned cflag; int bits_per_char; - if (!info->port.tty || !info->port.tty->termios) + if (!info->port.tty) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_change_params(%s)\n", __FILE__,__LINE__, info->device_name ); - cflag = info->port.tty->termios->c_cflag; + cflag = info->port.tty->termios.c_cflag; /* if B0 rate (hangup) specified then negate DTR and RTS */ /* otherwise assert DTR and RTS */ @@ -2367,8 +2367,8 @@ static void mgsl_throttle(struct tty_struct * tty) if (I_IXOFF(tty)) mgsl_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) { + + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->irq_spinlock,flags); info->serial_signals &= ~SerialSignal_RTS; usc_set_serial_signals(info); @@ -2401,8 +2401,8 @@ static void mgsl_unthrottle(struct tty_struct * tty) else mgsl_send_xchar(tty, START_CHAR(tty)); } - - if (tty->termios->c_cflag & CRTSCTS) { + + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->irq_spinlock,flags); info->serial_signals |= SerialSignal_RTS; usc_set_serial_signals(info); @@ -3045,7 +3045,7 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { + !(tty->termios.c_cflag & CBAUD)) { info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->irq_spinlock,flags); usc_set_serial_signals(info); @@ -3054,9 +3054,9 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { + tty->termios.c_cflag & CBAUD) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->serial_signals |= SerialSignal_RTS; } @@ -3067,7 +3067,7 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; mgsl_start(tty); } @@ -3287,7 +3287,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } - if (tty->termios->c_cflag & CLOCAL) + if (tty->termios.c_cflag & CLOCAL) do_clocal = true; /* Wait for carrier detect and the line to become @@ -3313,7 +3313,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open++; while (1) { - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); @@ -3362,6 +3362,29 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } /* end of block_til_ready() */ +static int mgsl_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct mgsl_struct *info; + int line = tty->index; + + /* verify range of specified line number */ + if (line >= mgsl_device_count) { + printk("%s(%d):mgsl_open with invalid line #%d.\n", + __FILE__, __LINE__, line); + return -ENODEV; + } + + /* find the info structure for the specified line */ + info = mgsl_device_list; + while (info && info->line != line) + info = info->next_device; + if (mgsl_paranoia_check(info, tty->name, "mgsl_open")) + return -ENODEV; + tty->driver_data = info; + + return tty_port_install(&info->port, driver, tty); +} + /* mgsl_open() * * Called when a port is opened. Init and enable port. @@ -3374,26 +3397,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ static int mgsl_open(struct tty_struct *tty, struct file * filp) { - struct mgsl_struct *info; - int retval, line; + struct mgsl_struct *info = tty->driver_data; unsigned long flags; + int retval; - /* verify range of specified line number */ - line = tty->index; - if (line >= mgsl_device_count) { - printk("%s(%d):mgsl_open with invalid line #%d.\n", - __FILE__,__LINE__,line); - return -ENODEV; - } - - /* find the info structure for the specified line */ - info = mgsl_device_list; - while(info && info->line != line) - info = info->next_device; - if (mgsl_paranoia_check(info, tty->name, "mgsl_open")) - return -ENODEV; - - tty->driver_data = info; info->port.tty = tty; if (debug_level >= DEBUG_LEVEL_INFO) @@ -4297,6 +4304,7 @@ static struct mgsl_struct* mgsl_allocate_device(void) } /* end of mgsl_allocate_device()*/ static const struct tty_operations mgsl_ops = { + .install = mgsl_install, .open = mgsl_open, .close = mgsl_close, .write = mgsl_write, diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa1debf..b38e954 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -785,7 +785,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { + !(tty->termios.c_cflag & CBAUD)) { info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -794,9 +794,9 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { + tty->termios.c_cflag & CBAUD) { info->signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->signals |= SerialSignal_RTS; } @@ -807,7 +807,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); } @@ -1372,7 +1372,7 @@ static void throttle(struct tty_struct * tty) DBGINFO(("%s throttle\n", info->device_name)); if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->signals &= ~SerialSignal_RTS; set_signals(info); @@ -1397,7 +1397,7 @@ static void unthrottle(struct tty_struct * tty) else send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->signals |= SerialSignal_RTS; set_signals(info); @@ -2053,7 +2053,7 @@ static void cts_change(struct slgt_info *info, unsigned short status) wake_up_interruptible(&info->event_wait_q); info->pending_bh |= BH_STATUS; - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&info->port)) { if (info->port.tty) { if (info->port.tty->hw_stopped) { if (info->signals & SerialSignal_CTS) { @@ -2493,7 +2493,7 @@ static void shutdown(struct slgt_info *info) slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); set_signals(info); } @@ -2534,7 +2534,7 @@ static void program_hw(struct slgt_info *info) get_signals(info); if (info->netcount || - (info->port.tty && info->port.tty->termios->c_cflag & CREAD)) + (info->port.tty && info->port.tty->termios.c_cflag & CREAD)) rx_start(info); spin_unlock_irqrestore(&info->lock,flags); @@ -2548,11 +2548,11 @@ static void change_params(struct slgt_info *info) unsigned cflag; int bits_per_char; - if (!info->port.tty || !info->port.tty->termios) + if (!info->port.tty) return; DBGINFO(("%s change_params\n", info->device_name)); - cflag = info->port.tty->termios->c_cflag; + cflag = info->port.tty->termios.c_cflag; /* if B0 rate (hangup) specified then negate DTR and RTS */ /* otherwise assert DTR and RTS */ @@ -3292,7 +3292,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } - if (tty->termios->c_cflag & CLOCAL) + if (tty->termios.c_cflag & CLOCAL) do_clocal = true; /* Wait for carrier detect and the line to become @@ -3314,7 +3314,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios->c_cflag & CBAUD)) + if ((tty->termios.c_cflag & CBAUD)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); @@ -3689,8 +3689,11 @@ static void device_init(int adapter_num, struct pci_dev *pdev) } } - for (i=0; i < port_count; ++i) - tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev)); + for (i = 0; i < port_count; ++i) { + struct slgt_info *info = port_array[i]; + tty_port_register_device(&info->port, serial_driver, info->line, + &info->pdev->dev); + } } static int __devinit init_one(struct pci_dev *dev, diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index a3dddc1..f17d9f3 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -711,15 +711,11 @@ static void ldisc_receive_buf(struct tty_struct *tty, /* tty callbacks */ -/* Called when a port is opened. Init and enable port. - */ -static int open(struct tty_struct *tty, struct file *filp) +static int install(struct tty_driver *driver, struct tty_struct *tty) { SLMP_INFO *info; - int retval, line; - unsigned long flags; + int line = tty->index; - line = tty->index; if (line >= synclinkmp_device_count) { printk("%s(%d): open with invalid line #%d.\n", __FILE__,__LINE__,line); @@ -727,17 +723,30 @@ static int open(struct tty_struct *tty, struct file *filp) } info = synclinkmp_device_list; - while(info && info->line != line) + while (info && info->line != line) info = info->next_device; if (sanity_check(info, tty->name, "open")) return -ENODEV; - if ( info->init_error ) { + if (info->init_error) { printk("%s(%d):%s device is not allocated, init error=%d\n", - __FILE__,__LINE__,info->device_name,info->init_error); + __FILE__, __LINE__, info->device_name, + info->init_error); return -ENODEV; } tty->driver_data = info; + + return tty_port_install(&info->port, driver, tty); +} + +/* Called when a port is opened. Init and enable port. + */ +static int open(struct tty_struct *tty, struct file *filp) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + int retval; + info->port.tty = tty; if (debug_level >= DEBUG_LEVEL_INFO) @@ -873,7 +882,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { + !(tty->termios.c_cflag & CBAUD)) { info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); @@ -882,9 +891,9 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { + tty->termios.c_cflag & CBAUD) { info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->serial_signals |= SerialSignal_RTS; } @@ -895,7 +904,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); } @@ -1473,7 +1482,7 @@ static void throttle(struct tty_struct * tty) if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals &= ~SerialSignal_RTS; set_signals(info); @@ -1502,7 +1511,7 @@ static void unthrottle(struct tty_struct * tty) send_xchar(tty, START_CHAR(tty)); } - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { spin_lock_irqsave(&info->lock,flags); info->serial_signals |= SerialSignal_RTS; set_signals(info); @@ -2491,7 +2500,7 @@ static void isr_io_pin( SLMP_INFO *info, u16 status ) } } - if ( (info->port.flags & ASYNC_CTS_FLOW) && + if (tty_port_cts_enabled(&info->port) && (status & MISCSTATUS_CTS_LATCHED) ) { if ( info->port.tty ) { if (info->port.tty->hw_stopped) { @@ -2708,7 +2717,7 @@ static void shutdown(SLMP_INFO * info) reset_port(info); - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); set_signals(info); } @@ -2749,7 +2758,7 @@ static void program_hw(SLMP_INFO *info) get_signals(info); - if (info->netcount || (info->port.tty && info->port.tty->termios->c_cflag & CREAD) ) + if (info->netcount || (info->port.tty && info->port.tty->termios.c_cflag & CREAD) ) rx_start(info); spin_unlock_irqrestore(&info->lock,flags); @@ -2762,14 +2771,14 @@ static void change_params(SLMP_INFO *info) unsigned cflag; int bits_per_char; - if (!info->port.tty || !info->port.tty->termios) + if (!info->port.tty) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s change_params()\n", __FILE__,__LINE__, info->device_name ); - cflag = info->port.tty->termios->c_cflag; + cflag = info->port.tty->termios.c_cflag; /* if B0 rate (hangup) specified then negate DTR and RTS */ /* otherwise assert DTR and RTS */ @@ -3306,7 +3315,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } - if (tty->termios->c_cflag & CLOCAL) + if (tty->termios.c_cflag & CLOCAL) do_clocal = true; /* Wait for carrier detect and the line to become @@ -3332,7 +3341,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3357,9 +3366,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); @@ -3881,6 +3890,7 @@ static void device_init(int adapter_num, struct pci_dev *pdev) } static const struct tty_operations ops = { + .install = install, .open = open, .close = close, .write = write, diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b425c79..8a5a8b0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -181,10 +181,13 @@ struct tty_struct *alloc_tty_struct(void) void free_tty_struct(struct tty_struct *tty) { + if (!tty) + return; if (tty->dev) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -573,7 +576,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -666,7 +669,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -1103,12 +1106,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1213,7 +1216,10 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) */ static void tty_line_name(struct tty_driver *driver, int index, char *p) { - sprintf(p, "%s%d", driver->name, index + driver->name_base); + if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE) + strcpy(p, driver->name); + else + sprintf(p, "%s%d", driver->name, index + driver->name_base); } /** @@ -1249,21 +1255,19 @@ int tty_init_termios(struct tty_struct *tty) struct ktermios *tp; int idx = tty->index; - tp = tty->driver->termios[idx]; - if (tp == NULL) { - tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (tp == NULL) - return -ENOMEM; - memcpy(tp, &tty->driver->init_termios, - sizeof(struct ktermios)); - tty->driver->termios[idx] = tp; + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) + tty->termios = tty->driver->init_termios; + else { + /* Check for lazy saved data */ + tp = tty->driver->termios[idx]; + if (tp != NULL) + tty->termios = *tp; + else + tty->termios = tty->driver->init_termios; } - tty->termios = tp; - tty->termios_locked = tp + 1; - /* Compatibility until drivers always set this */ - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); + tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); + tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); return 0; } EXPORT_SYMBOL_GPL(tty_init_termios); @@ -1403,10 +1407,18 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; + if (!tty->port) + tty->port = driver->ports[idx]; + + WARN_RATELIMIT(!tty->port, + "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", + __func__, tty->driver->name); + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1415,9 +1427,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1426,6 +1440,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1436,22 +1451,25 @@ void tty_free_termios(struct tty_struct *tty) { struct ktermios *tp; int idx = tty->index; - /* Kill this flag and push into drivers for locking etc */ - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* FIXME: Locking on ->termios array */ - tp = tty->termios; - tty->driver->termios[idx] = NULL; - kfree(tp); + + /* If the port is going to reset then it has no termios to save */ + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) + return; + + /* Stash the termios data */ + tp = tty->driver->termios[idx]; + if (tp == NULL) { + tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); + if (tp == NULL) { + pr_warn("tty: no memory to save termios state.\n"); + return; + } + tty->driver->termios[idx] = tp; } + *tp = tty->termios; } EXPORT_SYMBOL(tty_free_termios); -void tty_shutdown(struct tty_struct *tty) -{ - tty_driver_remove_tty(tty->driver, tty); - tty_free_termios(tty); -} -EXPORT_SYMBOL(tty_shutdown); /** * release_one_tty - release tty structure memory @@ -1462,7 +1480,6 @@ EXPORT_SYMBOL(tty_shutdown); * in use. It also gets called when setup of a device fails. * * Locking: - * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. * @@ -1495,11 +1512,6 @@ static void queue_release_one_tty(struct kref *kref) { struct tty_struct *tty = container_of(kref, struct tty_struct, kref); - if (tty->ops->shutdown) - tty->ops->shutdown(tty); - else - tty_shutdown(tty); - /* The hangup queue is now free so we can reuse it rather than waste a chunk of memory for each port */ INIT_WORK(&tty->hangup_work, release_one_tty); @@ -1528,16 +1540,20 @@ EXPORT_SYMBOL(tty_kref_put); * and decrement the refcount of the backing module. * * Locking: - * tty_mutex - sometimes only + * tty_mutex * takes the file list lock internally when working on the list * of ttys that the driver keeps. - * FIXME: should we require tty_mutex is held here ?? * */ static void release_tty(struct tty_struct *tty, int idx) { /* This should always be true but check for the moment */ WARN_ON(tty->index != idx); + WARN_ON(!mutex_is_locked(&tty_mutex)); + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + tty_free_termios(tty); + tty_driver_remove_tty(tty->driver, tty); if (tty->link) tty_kref_put(tty->link); @@ -1572,22 +1588,12 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, __func__, idx, tty->name); return -1; } - if (tty->termios != tty->driver->termios[idx]) { - printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n", - __func__, idx, tty->name); - return -1; - } if (tty->driver->other) { if (o_tty != tty->driver->other->ttys[idx]) { printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", __func__, idx, tty->name); return -1; } - if (o_tty->termios != tty->driver->other->termios[idx]) { - printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n", - __func__, idx, tty->name); - return -1; - } if (o_tty->link != tty) { printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); return -1; @@ -1628,7 +1634,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1637,10 +1643,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1652,7 +1659,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1675,7 +1682,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1706,7 +1713,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1715,6 +1722,9 @@ int tty_release(struct inode *inode, struct file *filp) * The closing flags are now consistent with the open counts on * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. + * + * We must *not* drop the tty_mutex until we ensure that a further + * entry into tty_open can not pick up this tty. */ if (pty_master) { if (--o_tty->count < 0) { @@ -1766,12 +1776,13 @@ int tty_release(struct inode *inode, struct file *filp) } mutex_unlock(&tty_mutex); + tty_unlock_pair(tty, o_tty); + /* At this point the TTY_CLOSING flag should ensure a dead tty + cannot be re-opened by a racing opener */ /* check whether both sides are closing ... */ - if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); + if (!tty_closing || (o_tty && !o_tty_closing)) return 0; - } #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); @@ -1782,14 +1793,17 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ + mutex_lock(&tty_mutex); release_tty(tty, idx); + mutex_unlock(&tty_mutex); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - tty_unlock(); return 0; } @@ -1893,6 +1907,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1916,8 +1933,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1938,17 +1954,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1977,7 +1995,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1989,17 +2007,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2007,11 +2023,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2094,10 +2109,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2756,7 +2774,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ld->ops->ioctl) { retval = ld->ops->ioctl(tty, file, cmd, arg); if (retval == -ENOIOCTLCMD) - retval = -EINVAL; + retval = -ENOTTY; } tty_ldisc_deref(ld); return retval; @@ -2934,6 +2952,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); @@ -2991,6 +3010,15 @@ EXPORT_SYMBOL_GPL(tty_put_char); struct class *tty_class; +static int tty_cdev_add(struct tty_driver *driver, dev_t dev, + unsigned int index, unsigned int count) +{ + /* init here, since reused cdevs cause crashes */ + cdev_init(&driver->cdevs[index], &tty_fops); + driver->cdevs[index].owner = driver->owner; + return cdev_add(&driver->cdevs[index], dev, count); +} + /** * tty_register_device - register a tty device * @driver: the tty driver that describes the tty device @@ -3013,8 +3041,46 @@ struct class *tty_class; struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *device) { + return tty_register_device_attr(driver, index, device, NULL, NULL); +} +EXPORT_SYMBOL(tty_register_device); + +static void tty_device_create_release(struct device *dev) +{ + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); + kfree(dev); +} + +/** + * tty_register_device_attr - register a tty device + * @driver: the tty driver that describes the tty device + * @index: the index in the tty driver for this tty device + * @device: a struct device that is associated with this tty device. + * This field is optional, if there is no known struct device + * for this tty device it can be set to NULL safely. + * @drvdata: Driver data to be set to device. + * @attr_grp: Attribute group to be set on device. + * + * Returns a pointer to the struct device for this tty device + * (or ERR_PTR(-EFOO) on error). + * + * This call is required to be made to register an individual tty device + * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If + * that bit is not set, this function should not be called by a tty + * driver. + * + * Locking: ?? + */ +struct device *tty_register_device_attr(struct tty_driver *driver, + unsigned index, struct device *device, + void *drvdata, + const struct attribute_group **attr_grp) +{ char name[64]; - dev_t dev = MKDEV(driver->major, driver->minor_start) + index; + dev_t devt = MKDEV(driver->major, driver->minor_start) + index; + struct device *dev = NULL; + int retval = -ENODEV; + bool cdev = false; if (index >= driver->num) { printk(KERN_ERR "Attempt to register invalid tty line number " @@ -3027,9 +3093,40 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, else tty_line_name(driver, index, name); - return device_create(tty_class, device, dev, NULL, name); + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + retval = tty_cdev_add(driver, devt, index, 1); + if (retval) + goto error; + cdev = true; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto error; + } + + dev->devt = devt; + dev->class = tty_class; + dev->parent = device; + dev->release = tty_device_create_release; + dev_set_name(dev, "%s", name); + dev->groups = attr_grp; + dev_set_drvdata(dev, drvdata); + + retval = device_register(dev); + if (retval) + goto error; + + return dev; + +error: + put_device(dev); + if (cdev) + cdev_del(&driver->cdevs[index]); + return ERR_PTR(retval); } -EXPORT_SYMBOL(tty_register_device); +EXPORT_SYMBOL_GPL(tty_register_device_attr); /** * tty_unregister_device - unregister a tty device @@ -3046,31 +3143,82 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) { device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) + cdev_del(&driver->cdevs[index]); } EXPORT_SYMBOL(tty_unregister_device); -struct tty_driver *__alloc_tty_driver(int lines, struct module *owner) +/** + * __tty_alloc_driver -- allocate tty driver + * @lines: count of lines this driver can handle at most + * @owner: module which is repsonsible for this driver + * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags + * + * This should not be called directly, some of the provided macros should be + * used instead. Use IS_ERR and friends on @retval. + */ +struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, + unsigned long flags) { struct tty_driver *driver; + unsigned int cdevs = 1; + int err; + + if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1)) + return ERR_PTR(-EINVAL); driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); - if (driver) { - kref_init(&driver->kref); - driver->magic = TTY_DRIVER_MAGIC; - driver->num = lines; - driver->owner = owner; - /* later we'll move allocation of tables here */ + if (!driver) + return ERR_PTR(-ENOMEM); + + kref_init(&driver->kref); + driver->magic = TTY_DRIVER_MAGIC; + driver->num = lines; + driver->owner = owner; + driver->flags = flags; + + if (!(flags & TTY_DRIVER_DEVPTS_MEM)) { + driver->ttys = kcalloc(lines, sizeof(*driver->ttys), + GFP_KERNEL); + driver->termios = kcalloc(lines, sizeof(*driver->termios), + GFP_KERNEL); + if (!driver->ttys || !driver->termios) { + err = -ENOMEM; + goto err_free_all; + } } + + if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + driver->ports = kcalloc(lines, sizeof(*driver->ports), + GFP_KERNEL); + if (!driver->ports) { + err = -ENOMEM; + goto err_free_all; + } + cdevs = lines; + } + + driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL); + if (!driver->cdevs) { + err = -ENOMEM; + goto err_free_all; + } + return driver; +err_free_all: + kfree(driver->ports); + kfree(driver->ttys); + kfree(driver->termios); + kfree(driver); + return ERR_PTR(err); } -EXPORT_SYMBOL(__alloc_tty_driver); +EXPORT_SYMBOL(__tty_alloc_driver); static void destruct_tty_driver(struct kref *kref) { struct tty_driver *driver = container_of(kref, struct tty_driver, kref); int i; struct ktermios *tp; - void *p; if (driver->flags & TTY_DRIVER_INSTALLED) { /* @@ -3087,13 +3235,14 @@ static void destruct_tty_driver(struct kref *kref) if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) tty_unregister_device(driver, i); } - p = driver->ttys; proc_tty_unregister_driver(driver); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - cdev_del(&driver->cdev); + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) + cdev_del(&driver->cdevs[0]); } + kfree(driver->cdevs); + kfree(driver->ports); + kfree(driver->termios); + kfree(driver->ttys); kfree(driver); } @@ -3124,15 +3273,8 @@ int tty_register_driver(struct tty_driver *driver) int error; int i; dev_t dev; - void **p = NULL; struct device *d; - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { - p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); - if (!p) - return -ENOMEM; - } - if (!driver->major) { error = alloc_chrdev_region(&dev, driver->minor_start, driver->num, driver->name); @@ -3144,28 +3286,13 @@ int tty_register_driver(struct tty_driver *driver) dev = MKDEV(driver->major, driver->minor_start); error = register_chrdev_region(dev, driver->num, driver->name); } - if (error < 0) { - kfree(p); - return error; - } + if (error < 0) + goto err; - if (p) { - driver->ttys = (struct tty_struct **)p; - driver->termios = (struct ktermios **)(p + driver->num); - } else { - driver->ttys = NULL; - driver->termios = NULL; - } - - cdev_init(&driver->cdev, &tty_fops); - driver->cdev.owner = driver->owner; - error = cdev_add(&driver->cdev, dev, driver->num); - if (error) { - unregister_chrdev_region(dev, driver->num); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - return error; + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { + error = tty_cdev_add(driver, dev, 0, driver->num); + if (error) + goto err_unreg_char; } mutex_lock(&tty_mutex); @@ -3177,7 +3304,7 @@ int tty_register_driver(struct tty_driver *driver) d = tty_register_device(driver, i, NULL); if (IS_ERR(d)) { error = PTR_ERR(d); - goto err; + goto err_unreg_devs; } } } @@ -3185,7 +3312,7 @@ int tty_register_driver(struct tty_driver *driver) driver->flags |= TTY_DRIVER_INSTALLED; return 0; -err: +err_unreg_devs: for (i--; i >= 0; i--) tty_unregister_device(driver, i); @@ -3193,13 +3320,11 @@ err: list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); +err_unreg_char: unregister_chrdev_region(dev, driver->num); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); +err: return error; } - EXPORT_SYMBOL(tty_register_driver); /* diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index a1b9a2f..12b1fa0 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -410,7 +410,7 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) { - tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); + tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud); } EXPORT_SYMBOL_GPL(tty_encode_baud_rate); @@ -427,7 +427,7 @@ EXPORT_SYMBOL_GPL(tty_encode_baud_rate); speed_t tty_get_baud_rate(struct tty_struct *tty) { - speed_t baud = tty_termios_baud_rate(tty->termios); + speed_t baud = tty_termios_baud_rate(&tty->termios); if (baud == 38400 && tty->alt_speed) { if (!tty->warned) { @@ -509,14 +509,14 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ mutex_lock(&tty->termios_mutex); - old_termios = *tty->termios; - *tty->termios = *new_termios; - unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); + old_termios = tty->termios; + tty->termios = *new_termios; + unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { int extproc = (old_termios.c_lflag & EXTPROC) | - (tty->termios->c_lflag & EXTPROC); + (tty->termios.c_lflag & EXTPROC); int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); @@ -542,7 +542,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) if (tty->ops->set_termios) (*tty->ops->set_termios)(tty, &old_termios); else - tty_termios_copy_hw(tty->termios, &old_termios); + tty_termios_copy_hw(&tty->termios, &old_termios); ld = tty_ldisc_ref(tty); if (ld != NULL) { @@ -578,7 +578,7 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) return retval; mutex_lock(&tty->termios_mutex); - memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); + tmp_termios = tty->termios; mutex_unlock(&tty->termios_mutex); if (opt & TERMIOS_TERMIO) { @@ -632,14 +632,14 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) { mutex_lock(&tty->termios_mutex); - memcpy(kterm, tty->termios, sizeof(struct ktermios)); + *kterm = tty->termios; mutex_unlock(&tty->termios_mutex); } static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) { mutex_lock(&tty->termios_mutex); - memcpy(kterm, tty->termios_locked, sizeof(struct ktermios)); + *kterm = tty->termios_locked; mutex_unlock(&tty->termios_mutex); } @@ -707,16 +707,16 @@ static int get_sgflags(struct tty_struct *tty) { int flags = 0; - if (!(tty->termios->c_lflag & ICANON)) { - if (tty->termios->c_lflag & ISIG) + if (!(tty->termios.c_lflag & ICANON)) { + if (tty->termios.c_lflag & ISIG) flags |= 0x02; /* cbreak */ else flags |= 0x20; /* raw */ } - if (tty->termios->c_lflag & ECHO) + if (tty->termios.c_lflag & ECHO) flags |= 0x08; /* echo */ - if (tty->termios->c_oflag & OPOST) - if (tty->termios->c_oflag & ONLCR) + if (tty->termios.c_oflag & OPOST) + if (tty->termios.c_oflag & ONLCR) flags |= 0x10; /* crmod */ return flags; } @@ -726,10 +726,10 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) struct sgttyb tmp; mutex_lock(&tty->termios_mutex); - tmp.sg_ispeed = tty->termios->c_ispeed; - tmp.sg_ospeed = tty->termios->c_ospeed; - tmp.sg_erase = tty->termios->c_cc[VERASE]; - tmp.sg_kill = tty->termios->c_cc[VKILL]; + tmp.sg_ispeed = tty->termios.c_ispeed; + tmp.sg_ospeed = tty->termios.c_ospeed; + tmp.sg_erase = tty->termios.c_cc[VERASE]; + tmp.sg_kill = tty->termios.c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); mutex_unlock(&tty->termios_mutex); @@ -787,7 +787,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) return -EFAULT; mutex_lock(&tty->termios_mutex); - termios = *tty->termios; + termios = tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; set_sgflags(&termios, tmp.sg_flags); @@ -808,12 +808,12 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) struct tchars tmp; mutex_lock(&tty->termios_mutex); - tmp.t_intrc = tty->termios->c_cc[VINTR]; - tmp.t_quitc = tty->termios->c_cc[VQUIT]; - tmp.t_startc = tty->termios->c_cc[VSTART]; - tmp.t_stopc = tty->termios->c_cc[VSTOP]; - tmp.t_eofc = tty->termios->c_cc[VEOF]; - tmp.t_brkc = tty->termios->c_cc[VEOL2]; /* what is brkc anyway? */ + tmp.t_intrc = tty->termios.c_cc[VINTR]; + tmp.t_quitc = tty->termios.c_cc[VQUIT]; + tmp.t_startc = tty->termios.c_cc[VSTART]; + tmp.t_stopc = tty->termios.c_cc[VSTOP]; + tmp.t_eofc = tty->termios.c_cc[VEOF]; + tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */ mutex_unlock(&tty->termios_mutex); return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -825,12 +825,12 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) if (copy_from_user(&tmp, tchars, sizeof(tmp))) return -EFAULT; mutex_lock(&tty->termios_mutex); - tty->termios->c_cc[VINTR] = tmp.t_intrc; - tty->termios->c_cc[VQUIT] = tmp.t_quitc; - tty->termios->c_cc[VSTART] = tmp.t_startc; - tty->termios->c_cc[VSTOP] = tmp.t_stopc; - tty->termios->c_cc[VEOF] = tmp.t_eofc; - tty->termios->c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ + tty->termios.c_cc[VINTR] = tmp.t_intrc; + tty->termios.c_cc[VQUIT] = tmp.t_quitc; + tty->termios.c_cc[VSTART] = tmp.t_startc; + tty->termios.c_cc[VSTOP] = tmp.t_stopc; + tty->termios.c_cc[VEOF] = tmp.t_eofc; + tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ mutex_unlock(&tty->termios_mutex); return 0; } @@ -842,14 +842,14 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) struct ltchars tmp; mutex_lock(&tty->termios_mutex); - tmp.t_suspc = tty->termios->c_cc[VSUSP]; + tmp.t_suspc = tty->termios.c_cc[VSUSP]; /* what is dsuspc anyway? */ - tmp.t_dsuspc = tty->termios->c_cc[VSUSP]; - tmp.t_rprntc = tty->termios->c_cc[VREPRINT]; + tmp.t_dsuspc = tty->termios.c_cc[VSUSP]; + tmp.t_rprntc = tty->termios.c_cc[VREPRINT]; /* what is flushc anyway? */ - tmp.t_flushc = tty->termios->c_cc[VEOL2]; - tmp.t_werasc = tty->termios->c_cc[VWERASE]; - tmp.t_lnextc = tty->termios->c_cc[VLNEXT]; + tmp.t_flushc = tty->termios.c_cc[VEOL2]; + tmp.t_werasc = tty->termios.c_cc[VWERASE]; + tmp.t_lnextc = tty->termios.c_cc[VLNEXT]; mutex_unlock(&tty->termios_mutex); return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -862,14 +862,14 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) return -EFAULT; mutex_lock(&tty->termios_mutex); - tty->termios->c_cc[VSUSP] = tmp.t_suspc; + tty->termios.c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */ - tty->termios->c_cc[VEOL2] = tmp.t_dsuspc; - tty->termios->c_cc[VREPRINT] = tmp.t_rprntc; + tty->termios.c_cc[VEOL2] = tmp.t_dsuspc; + tty->termios.c_cc[VREPRINT] = tmp.t_rprntc; /* what is flushc anyway? */ - tty->termios->c_cc[VEOL2] = tmp.t_flushc; - tty->termios->c_cc[VWERASE] = tmp.t_werasc; - tty->termios->c_cc[VLNEXT] = tmp.t_lnextc; + tty->termios.c_cc[VEOL2] = tmp.t_flushc; + tty->termios.c_cc[VWERASE] = tmp.t_werasc; + tty->termios.c_cc[VLNEXT] = tmp.t_lnextc; mutex_unlock(&tty->termios_mutex); return 0; } @@ -920,12 +920,12 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) struct ktermios old; mutex_lock(&tty->termios_mutex); - old = *tty->termios; - tty->termios->c_cflag &= ~CLOCAL; - tty->termios->c_cflag |= bit; + old = tty->termios; + tty->termios.c_cflag &= ~CLOCAL; + tty->termios.c_cflag |= bit; if (tty->ops->set_termios) tty->ops->set_termios(tty, &old); - if ((tty->termios->c_cflag & CLOCAL) != bit) + if ((tty->termios.c_cflag & CLOCAL) != bit) ret = -EINVAL; mutex_unlock(&tty->termios_mutex); return ret; @@ -1031,7 +1031,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, (struct termios __user *) arg)) return -EFAULT; mutex_lock(&real_tty->termios_mutex); - memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); + real_tty->termios_locked = kterm; mutex_unlock(&real_tty->termios_mutex); return 0; #else @@ -1048,7 +1048,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, (struct termios __user *) arg)) return -EFAULT; mutex_lock(&real_tty->termios_mutex); - memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); + real_tty->termios_locked = kterm; mutex_unlock(&real_tty->termios_mutex); return ret; #endif diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 6f99c99..4d7b562 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -413,7 +413,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { mutex_lock(&tty->termios_mutex); - tty->termios->c_line = num; + tty->termios.c_line = num; mutex_unlock(&tty->termios_mutex); } @@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -665,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -708,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -722,9 +722,9 @@ enable: static void tty_reset_termios(struct tty_struct *tty) { mutex_lock(&tty->termios_mutex); - *tty->termios = tty->driver->init_termios; - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); + tty->termios = tty->driver->init_termios; + tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); + tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); mutex_unlock(&tty->termios_mutex); } @@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -831,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -846,7 +846,7 @@ retry: if (reset == 0) { - if (!tty_ldisc_reinit(tty, tty->termios->c_line)) + if (!tty_ldisc_reinit(tty, tty->termios.c_line)) err = tty_ldisc_open(tty, tty->ldisc); else err = 1; @@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_enable(tty); return 0; } + +static void tty_ldisc_kill(struct tty_struct *tty) +{ + mutex_lock(&tty->ldisc_mutex); + /* + * Now kill off the ldisc + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; + + /* Ensure the next open requests the N_TTY ldisc */ + tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); +} + /** * tty_ldisc_release - release line discipline * @tty: tty being shut down @@ -912,28 +929,21 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock(); + tty_lock_pair(tty, o_tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); - - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; - - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); + if (o_tty) { + tty_ldisc_halt(o_tty); + tty_ldisc_flush_works(o_tty); + } /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); + if (o_tty) - tty_ldisc_release(o_tty, NULL); + tty_ldisc_kill(o_tty); + tty_unlock_pair(tty, o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ } diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c..67feac9 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,70 @@ #include <linux/semaphore.h> #include <linux/sched.h> -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ + +enum { + TTY_MUTEX_NORMAL, + TTY_MUTEX_NESTED, +}; /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +static void __lockfunc tty_lock_nested(struct tty_struct *tty, + unsigned int subclass) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock_nested(&tty->legacy_mutex, subclass); +} + +void __lockfunc tty_lock(struct tty_struct *tty) +{ + return tty_lock_nested(tty, TTY_MUTEX_NORMAL); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock_nested(tty2, TTY_MUTEX_NESTED); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock_nested(tty, TTY_MUTEX_NESTED); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index bf6e238..d7bdd8d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -33,6 +33,70 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +/** + * tty_port_link_device - link tty and tty_port + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * + * Provide the tty layer wit ha link from a tty (specified by @index) to a + * tty_port (@port). Use this only if neither tty_port_register_device nor + * tty_port_install is used in the driver. If used, this has to be called before + * tty_register_driver. + */ +void tty_port_link_device(struct tty_port *port, + struct tty_driver *driver, unsigned index) +{ + if (WARN_ON(index >= driver->num)) + return; + driver->ports[index] = port; +} +EXPORT_SYMBOL_GPL(tty_port_link_device); + +/** + * tty_port_register_device - register tty device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * @device: parent if exists, otherwise NULL + * + * It is the same as tty_register_device except the provided @port is linked to + * a concrete tty specified by @index. Use this or tty_port_install (or both). + * Call tty_port_link_device as a last resort. + */ +struct device *tty_port_register_device(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device) +{ + tty_port_link_device(port, driver, index); + return tty_register_device(driver, index, device); +} +EXPORT_SYMBOL_GPL(tty_port_register_device); + +/** + * tty_port_register_device_attr - register tty device + * @port: tty_port of the device + * @driver: tty_driver for this device + * @index: index of the tty + * @device: parent if exists, otherwise NULL + * @drvdata: Driver data to be set to device. + * @attr_grp: Attribute group to be set on device. + * + * It is the same as tty_register_device_attr except the provided @port is + * linked to a concrete tty specified by @index. Use this or tty_port_install + * (or both). Call tty_port_link_device as a last resort. + */ +struct device *tty_port_register_device_attr(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device, void *drvdata, + const struct attribute_group **attr_grp) +{ + tty_port_link_device(port, driver, index); + return tty_register_device_attr(driver, index, device, drvdata, + attr_grp); +} +EXPORT_SYMBOL_GPL(tty_port_register_device_attr); + int tty_port_alloc_xmit_buf(struct tty_port *port) { /* We may sleep in get_zeroed_page() */ @@ -230,7 +294,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -246,7 +310,7 @@ int tty_port_block_til_ready(struct tty_port *port, } if (filp->f_flags & O_NONBLOCK) { /* Indicate we are open */ - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) tty_port_raise_dtr_rts(port); port->flags |= ASYNC_NORMAL_ACTIVE; return 0; @@ -270,7 +334,7 @@ int tty_port_block_til_ready(struct tty_port *port, while (1) { /* Indicate we are open */ - if (tty->termios->c_cflag & CBAUD) + if (tty->termios.c_cflag & CBAUD) tty_port_raise_dtr_rts(port); prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); @@ -296,9 +360,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); @@ -369,7 +433,7 @@ int tty_port_close_start(struct tty_port *port, /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to hang up the line */ - if (tty->termios->c_cflag & HUPCL) + if (tty->termios.c_cflag & HUPCL) tty_port_lower_dtr_rts(port); /* Don't call port->drop for the last reference. Callers will want @@ -413,6 +477,24 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, } EXPORT_SYMBOL(tty_port_close); +/** + * tty_port_install - generic tty->ops->install handler + * @port: tty_port of the device + * @driver: tty_driver for this device + * @tty: tty to be installed + * + * It is the same as tty_standard_install except the provided @port is linked + * to a concrete tty specified by @tty. Use this or tty_port_register_device + * (or both). Call tty_port_link_device as a last resort. + */ +int tty_port_install(struct tty_port *port, struct tty_driver *driver, + struct tty_struct *tty) +{ + tty->port = port; + return tty_standard_install(driver, tty); +} +EXPORT_SYMBOL_GPL(tty_port_install); + int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp) { diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 48cc6f2..681765b 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -119,6 +119,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals); static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); +static DEFINE_SPINLOCK(led_lock); static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static bool dead_key_next; @@ -310,7 +311,7 @@ static void put_queue(struct vc_data *vc, int ch) if (tty) { tty_insert_flip_char(tty, ch, 0); - con_schedule_flip(tty); + tty_schedule_flip(tty); } } @@ -325,7 +326,7 @@ static void puts_queue(struct vc_data *vc, char *cp) tty_insert_flip_char(tty, *cp, 0); cp++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void applkey(struct vc_data *vc, int key, char mode) @@ -586,7 +587,7 @@ static void fn_send_intr(struct vc_data *vc) if (!tty) return; tty_insert_flip_char(tty, 0, TTY_BREAK); - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void fn_scroll_forw(struct vc_data *vc) @@ -984,7 +985,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) * or (ii) whatever pattern of lights people want to show using KDSETLED, * or (iii) specified bits of specified words in kernel memory. */ -unsigned char getledstate(void) +static unsigned char getledstate(void) { return ledstate; } @@ -992,7 +993,7 @@ unsigned char getledstate(void) void setledstate(struct kbd_struct *kbd, unsigned int led) { unsigned long flags; - spin_lock_irqsave(&kbd_event_lock, flags); + spin_lock_irqsave(&led_lock, flags); if (!(led & ~7)) { ledioctl = led; kbd->ledmode = LED_SHOW_IOCTL; @@ -1000,7 +1001,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) kbd->ledmode = LED_SHOW_FLAGS; set_leds(); - spin_unlock_irqrestore(&kbd_event_lock, flags); + spin_unlock_irqrestore(&led_lock, flags); } static inline unsigned char getleds(void) @@ -1049,13 +1050,13 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) */ int vt_get_leds(int console, int flag) { - unsigned long flags; struct kbd_struct * kbd = kbd_table + console; int ret; + unsigned long flags; - spin_lock_irqsave(&kbd_event_lock, flags); + spin_lock_irqsave(&led_lock, flags); ret = vc_kbd_led(kbd, flag); - spin_unlock_irqrestore(&kbd_event_lock, flags); + spin_unlock_irqrestore(&led_lock, flags); return ret; } @@ -1091,11 +1092,11 @@ void vt_set_led_state(int console, int leds) void vt_kbd_con_start(int console) { struct kbd_struct * kbd = kbd_table + console; -/* unsigned long flags; */ -/* spin_lock_irqsave(&kbd_event_lock, flags); */ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); clr_vc_kbd_led(kbd, VC_SCROLLOCK); set_leds(); -/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ + spin_unlock_irqrestore(&led_lock, flags); } /** @@ -1104,21 +1105,15 @@ void vt_kbd_con_start(int console) * * Handle console stop. This is a wrapper for the VT layer * so that we can keep kbd knowledge internal - * - * FIXME: We eventually need to hold the kbd lock here to protect - * the LED updating. We can't do it yet because fn_hold calls stop_tty - * and start_tty under the kbd_event_lock, while normal tty paths - * don't hold the lock. We probably need to split out an LED lock - * but not during an -rc release! */ void vt_kbd_con_stop(int console) { struct kbd_struct * kbd = kbd_table + console; -/* unsigned long flags; */ -/* spin_lock_irqsave(&kbd_event_lock, flags); */ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); set_vc_kbd_led(kbd, VC_SCROLLOCK); set_leds(); -/* spin_unlock_irqrestore(&kbd_event_lock, flags); */ + spin_unlock_irqrestore(&led_lock, flags); } /* @@ -1130,7 +1125,12 @@ void vt_kbd_con_stop(int console) */ static void kbd_bh(unsigned long dummy) { - unsigned char leds = getleds(); + unsigned char leds; + unsigned long flags; + + spin_lock_irqsave(&led_lock, flags); + leds = getleds(); + spin_unlock_irqrestore(&led_lock, flags); if (leds != ledstate) { input_handler_for_each_handle(&kbd_handler, &leds, @@ -2035,11 +2035,11 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) return -EPERM; if (arg & ~0x77) return -EINVAL; - spin_lock_irqsave(&kbd_event_lock, flags); + spin_lock_irqsave(&led_lock, flags); kbd->ledflagstate = (arg & 7); kbd->default_ledflagstate = ((arg >> 4) & 7); set_leds(); - spin_unlock_irqrestore(&kbd_event_lock, flags); + spin_unlock_irqrestore(&led_lock, flags); return 0; /* the ioctls below only set the lights, not the functions */ @@ -2134,8 +2134,10 @@ void vt_reset_keyboard(int console) clr_vc_kbd_mode(kbd, VC_CRLF); kbd->lockstate = 0; kbd->slockstate = 0; + spin_lock(&led_lock); kbd->ledmode = LED_SHOW_FLAGS; kbd->ledflagstate = kbd->default_ledflagstate; + spin_unlock(&led_lock); /* do not do set_leds here because this causes an endless tasklet loop when the keyboard hasn't been initialized yet */ spin_unlock_irqrestore(&kbd_event_lock, flags); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 84cbf29..999ca63 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -537,45 +537,27 @@ void complement_pos(struct vc_data *vc, int offset) static void insert_char(struct vc_data *vc, unsigned int nr) { - unsigned short *p, *q = (unsigned short *)vc->vc_pos; + unsigned short *p = (unsigned short *) vc->vc_pos; - p = q + vc->vc_cols - nr - vc->vc_x; - while (--p >= q) - scr_writew(scr_readw(p), p + nr); - scr_memsetw(q, vc->vc_video_erase_char, nr * 2); + scr_memmovew(p + nr, p, vc->vc_cols - vc->vc_x); + scr_memsetw(p, vc->vc_video_erase_char, nr * 2); vc->vc_need_wrap = 0; - if (DO_UPDATE(vc)) { - unsigned short oldattr = vc->vc_attr; - vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x, vc->vc_y, vc->vc_x + nr, 1, - vc->vc_cols - vc->vc_x - nr); - vc->vc_attr = vc->vc_video_erase_char >> 8; - while (nr--) - vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, vc->vc_x + nr); - vc->vc_attr = oldattr; - } + if (DO_UPDATE(vc)) + do_update_region(vc, (unsigned long) p, + (vc->vc_cols - vc->vc_x) / 2 + 1); } static void delete_char(struct vc_data *vc, unsigned int nr) { - unsigned int i = vc->vc_x; - unsigned short *p = (unsigned short *)vc->vc_pos; + unsigned short *p = (unsigned short *) vc->vc_pos; - while (++i <= vc->vc_cols - nr) { - scr_writew(scr_readw(p+nr), p); - p++; - } - scr_memsetw(p, vc->vc_video_erase_char, nr * 2); + scr_memcpyw(p, p + nr, vc->vc_cols - vc->vc_x - nr); + scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char, + nr * 2); vc->vc_need_wrap = 0; - if (DO_UPDATE(vc)) { - unsigned short oldattr = vc->vc_attr; - vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x + nr, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x - nr); - vc->vc_attr = vc->vc_video_erase_char >> 8; - while (nr--) - vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, - vc->vc_cols - 1 - nr); - vc->vc_attr = oldattr; - } + if (DO_UPDATE(vc)) + do_update_region(vc, (unsigned long) p, + (vc->vc_cols - vc->vc_x) / 2); } static int softcursor_original; @@ -1172,45 +1154,26 @@ static void csi_J(struct vc_data *vc, int vpar) case 0: /* erase from cursor to end of display */ count = (vc->vc_scr_end - vc->vc_pos) >> 1; start = (unsigned short *)vc->vc_pos; - if (DO_UPDATE(vc)) { - /* do in two stages */ - vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x); - vc->vc_sw->con_clear(vc, vc->vc_y + 1, 0, - vc->vc_rows - vc->vc_y - 1, - vc->vc_cols); - } break; case 1: /* erase from start to cursor */ count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; start = (unsigned short *)vc->vc_origin; - if (DO_UPDATE(vc)) { - /* do in two stages */ - vc->vc_sw->con_clear(vc, 0, 0, vc->vc_y, - vc->vc_cols); - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_x + 1); - } break; case 3: /* erase scroll-back buffer (and whole display) */ scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, vc->vc_screenbuf_size >> 1); set_origin(vc); - if (CON_IS_VISIBLE(vc)) - update_screen(vc); /* fall through */ case 2: /* erase whole display */ count = vc->vc_cols * vc->vc_rows; start = (unsigned short *)vc->vc_origin; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, 0, 0, - vc->vc_rows, - vc->vc_cols); break; default: return; } scr_memsetw(start, vc->vc_video_erase_char, 2 * count); + if (DO_UPDATE(vc)) + do_update_region(vc, (unsigned long) start, count); vc->vc_need_wrap = 0; } @@ -1223,29 +1186,22 @@ static void csi_K(struct vc_data *vc, int vpar) case 0: /* erase from cursor to end of line */ count = vc->vc_cols - vc->vc_x; start = (unsigned short *)vc->vc_pos; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x); break; case 1: /* erase from start of line to cursor */ start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); count = vc->vc_x + 1; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_x + 1); break; case 2: /* erase whole line */ start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); count = vc->vc_cols; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_cols); break; default: return; } scr_memsetw(start, vc->vc_video_erase_char, 2 * count); vc->vc_need_wrap = 0; + if (DO_UPDATE(vc)) + do_update_region(vc, (unsigned long) start, count); } static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar positions */ @@ -1380,7 +1336,7 @@ static void respond_string(const char *p, struct tty_struct *tty) tty_insert_flip_char(tty, *p, 0); p++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void cursor_report(struct vc_data *vc, struct tty_struct *tty) @@ -2792,41 +2748,52 @@ static void con_flush_chars(struct tty_struct *tty) /* * Allocate the console screen memory. */ -static int con_open(struct tty_struct *tty, struct file *filp) +static int con_install(struct tty_driver *driver, struct tty_struct *tty) { unsigned int currcons = tty->index; - int ret = 0; + struct vc_data *vc; + int ret; console_lock(); - if (tty->driver_data == NULL) { - ret = vc_allocate(currcons); - if (ret == 0) { - struct vc_data *vc = vc_cons[currcons].d; + ret = vc_allocate(currcons); + if (ret) + goto unlock; - /* Still being freed */ - if (vc->port.tty) { - console_unlock(); - return -ERESTARTSYS; - } - tty->driver_data = vc; - vc->port.tty = tty; + vc = vc_cons[currcons].d; - if (!tty->winsize.ws_row && !tty->winsize.ws_col) { - tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; - tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; - } - if (vc->vc_utf) - tty->termios->c_iflag |= IUTF8; - else - tty->termios->c_iflag &= ~IUTF8; - console_unlock(); - return ret; - } + /* Still being freed */ + if (vc->port.tty) { + ret = -ERESTARTSYS; + goto unlock; } + + ret = tty_port_install(&vc->port, driver, tty); + if (ret) + goto unlock; + + tty->driver_data = vc; + vc->port.tty = tty; + + if (!tty->winsize.ws_row && !tty->winsize.ws_col) { + tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; + tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; + } + if (vc->vc_utf) + tty->termios.c_iflag |= IUTF8; + else + tty->termios.c_iflag &= ~IUTF8; +unlock: console_unlock(); return ret; } +static int con_open(struct tty_struct *tty, struct file *filp) +{ + /* everything done in install */ + return 0; +} + + static void con_close(struct tty_struct *tty, struct file *filp) { /* Nothing to do - we defer to shutdown */ @@ -2839,7 +2806,6 @@ static void con_shutdown(struct tty_struct *tty) console_lock(); vc->port.tty = NULL; console_unlock(); - tty_shutdown(tty); } static int default_italic_color = 2; // green (ASCII) @@ -2947,6 +2913,7 @@ static int __init con_init(void) console_initcall(con_init); static const struct tty_operations con_ops = { + .install = con_install, .open = con_open, .close = con_close, .write = con_write, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f763ed7..ff7b5a8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -826,7 +826,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { struct acm *acm = tty->driver_data; - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; struct usb_cdc_line_coding newline; int newctrl = acm->ctrlout; @@ -1299,7 +1299,8 @@ skip_countries: usb_set_intfdata(data_interface, acm); usb_get_intf(control_interface); - tty_register_device(acm_tty_driver, minor, &control_interface->dev); + tty_port_register_device(&acm->port, acm_tty_driver, minor, + &control_interface->dev); return 0; alloc_fail7: diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index da6d479..f173952 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1133,7 +1133,8 @@ int gserial_setup(struct usb_gadget *g, unsigned count) for (i = 0; i < count; i++) { struct device *tty_dev; - tty_dev = tty_register_device(gs_tty_driver, i, &g->dev); + tty_dev = tty_port_register_device(&ports[i].port->port, + gs_tty_driver, i, &g->dev); if (IS_ERR(tty_dev)) pr_warning("%s: no classdev for port %d, err %ld\n", __func__, i, PTR_ERR(tty_dev)); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index f8ce97d..3b98fb7 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -215,7 +215,7 @@ static void ark3116_release(struct usb_serial *serial) static void ark3116_init_termios(struct tty_struct *tty) { - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; *termios = tty_std_termios; termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; @@ -229,7 +229,7 @@ static void ark3116_set_termios(struct tty_struct *tty, { struct usb_serial *serial = port->serial; struct ark3116_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; int bps = tty_get_baud_rate(tty); int quot; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 6b73656..a46df73 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -307,7 +307,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty, unsigned long control_state; int bad_flow_control; speed_t baud; - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; iflag = termios->c_iflag; cflag = termios->c_cflag; diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index b9cca6d..9a56428 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -165,8 +165,8 @@ static int usb_console_setup(struct console *co, char *options) } if (serial->type->set_termios) { - tty->termios->c_cflag = cflag; - tty_termios_encode_baud_rate(tty->termios, baud, baud); + tty->termios.c_cflag = cflag; + tty_termios_encode_baud_rate(&tty->termios, baud, baud); memset(&dummy, 0, sizeof(struct ktermios)); serial->type->set_termios(tty, port, &dummy); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 1e71079..ba5e07e1 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -469,7 +469,7 @@ static void cp210x_get_termios(struct tty_struct *tty, if (tty) { cp210x_get_termios_port(tty->driver_data, - &tty->termios->c_cflag, &baud); + &tty->termios.c_cflag, &baud); tty_encode_baud_rate(tty, baud, baud); } @@ -631,7 +631,7 @@ static void cp210x_change_speed(struct tty_struct *tty, { u32 baud; - baud = tty->termios->c_ospeed; + baud = tty->termios.c_ospeed; /* This maps the requested rate to a rate valid on cp2102 or cp2103, * or to an arbitrary rate in [1M,2M]. @@ -665,10 +665,10 @@ static void cp210x_set_termios(struct tty_struct *tty, if (!tty) return; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; old_cflag = old_termios->c_cflag; - if (tty->termios->c_ospeed != old_termios->c_ospeed) + if (tty->termios.c_ospeed != old_termios->c_ospeed) cp210x_change_speed(tty, port, old_termios); /* If the number of data bits is to be updated */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index b78c34e..be34f15 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -922,38 +922,38 @@ static void cypress_set_termios(struct tty_struct *tty, early enough */ if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 4800; - tty->termios->c_ospeed = 4800; + tty->termios.c_ispeed = 4800; + tty->termios.c_ospeed = 4800; } else if (priv->chiptype == CT_CYPHIDCOM) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; + tty->termios.c_ispeed = 9600; + tty->termios.c_ospeed = 9600; } else if (priv->chiptype == CT_CA42V2) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; + tty->termios.c_ispeed = 9600; + tty->termios.c_ospeed = 9600; } priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); /* Unsupported features need clearing */ - tty->termios->c_cflag &= ~(CMSPAR|CRTSCTS); + tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS); - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; /* check if there are new settings */ if (old_termios) { spin_lock_irqsave(&priv->lock, flags); - priv->tmp_termios = *(tty->termios); + priv->tmp_termios = tty->termios; spin_unlock_irqrestore(&priv->lock, flags); } @@ -1021,7 +1021,7 @@ static void cypress_set_termios(struct tty_struct *tty, "4800bps."); /* define custom termios settings for NMEA protocol */ - tty->termios->c_iflag /* input modes - */ + tty->termios.c_iflag /* input modes - */ &= ~(IGNBRK /* disable ignore break */ | BRKINT /* disable break causes interrupt */ | PARMRK /* disable mark parity errors */ @@ -1031,10 +1031,10 @@ static void cypress_set_termios(struct tty_struct *tty, | ICRNL /* disable translate CR to NL */ | IXON); /* disable enable XON/XOFF flow control */ - tty->termios->c_oflag /* output modes */ + tty->termios.c_oflag /* output modes */ &= ~OPOST; /* disable postprocess output char */ - tty->termios->c_lflag /* line discipline modes */ + tty->termios.c_lflag /* line discipline modes */ &= ~(ECHO /* disable echo input characters */ | ECHONL /* disable echo new line */ | ICANON /* disable erase, kill, werase, and rprnt @@ -1200,7 +1200,7 @@ static void cypress_read_int_callback(struct urb *urb) /* hangup, as defined in acm.c... this might be a bad place for it * though */ - if (tty && !(tty->termios->c_cflag & CLOCAL) && + if (tty && !(tty->termios.c_cflag & CLOCAL) && !(priv->current_status & UART_CD)) { dbg("%s - calling hangup", __func__); tty_hangup(tty); diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index b5cd838..afd9d2e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -687,8 +687,8 @@ static void digi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { struct digi_port *priv = usb_get_serial_port_data(port); - unsigned int iflag = tty->termios->c_iflag; - unsigned int cflag = tty->termios->c_cflag; + unsigned int iflag = tty->termios.c_iflag; + unsigned int cflag = tty->termios.c_cflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; unsigned char buf[32]; @@ -709,7 +709,7 @@ static void digi_set_termios(struct tty_struct *tty, /* don't set RTS if using hardware flow control */ /* and throttling input */ modem_signals = TIOCM_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) modem_signals |= TIOCM_RTS; digi_set_modem_signals(port, modem_signals, 1); @@ -748,7 +748,7 @@ static void digi_set_termios(struct tty_struct *tty, } } /* set parity */ - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; if ((cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD))) { if (cflag&PARENB) { @@ -1124,8 +1124,8 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) /* set termios settings */ if (tty) { - not_termios.c_cflag = ~tty->termios->c_cflag; - not_termios.c_iflag = ~tty->termios->c_iflag; + not_termios.c_cflag = ~tty->termios.c_cflag; + not_termios.c_iflag = ~tty->termios.c_iflag; digi_set_termios(tty, port, ¬_termios); } return 0; @@ -1500,7 +1500,7 @@ static int digi_read_oob_callback(struct urb *urb) rts = 0; if (tty) - rts = tty->termios->c_cflag & CRTSCTS; + rts = tty->termios.c_cflag & CRTSCTS; if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) { spin_lock(&priv->dp_port_lock); diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index cdf61dd..34e8638 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -87,7 +87,7 @@ static int empeg_startup(struct usb_serial *serial) static void empeg_init_termios(struct tty_struct *tty) { - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; /* * The empeg-car player wants these particular tty settings. diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 499b15f..79451ee 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -173,10 +173,11 @@ static void f81232_set_termios(struct tty_struct *tty, /* FIXME - Stubbed out for now */ /* Don't change anything if nothing has changed */ - if (!tty_termios_hw_change(tty->termios, old_termios)) + if (!tty_termios_hw_change(&tty->termios, old_termios)) return; /* Do the real work here... */ + tty_termios_copy_hw(&tty->termios, old_termios); } static int f81232_tiocmget(struct tty_struct *tty) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f906b3a..0c8d1c2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2102,7 +2102,7 @@ static void ftdi_set_termios(struct tty_struct *tty, { struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; __u16 urb_value; /* will hold the new flags */ diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index e1f5ccd..f435575 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1458,7 +1458,7 @@ static void edge_throttle(struct tty_struct *tty) } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { edge_port->shadowMCR &= ~MCR_RTS; status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); @@ -1497,7 +1497,7 @@ static void edge_unthrottle(struct tty_struct *tty) return; } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { edge_port->shadowMCR |= MCR_RTS; send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); @@ -1516,9 +1516,9 @@ static void edge_set_termios(struct tty_struct *tty, struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int cflag; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; dbg("%s - clfag %08x iflag %08x", __func__, - tty->termios->c_cflag, tty->termios->c_iflag); + tty->termios.c_cflag, tty->termios.c_iflag); dbg("%s - old clfag %08x old iflag %08x", __func__, old_termios->c_cflag, old_termios->c_iflag); @@ -1987,7 +1987,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, tty = tty_port_tty_get(&edge_port->port->port); if (tty) { change_port_settings(tty, - edge_port, tty->termios); + edge_port, &tty->termios); tty_kref_put(tty); } @@ -2570,7 +2570,7 @@ static void change_port_settings(struct tty_struct *tty, return; } - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; switch (cflag & CSIZE) { case CS5: diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 3936904..765978a 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1870,7 +1870,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) /* set up the port settings */ if (tty) - edge_set_termios(tty, port, tty->termios); + edge_set_termios(tty, port, &tty->termios); /* open up the port */ @@ -2272,13 +2272,13 @@ static void change_port_settings(struct tty_struct *tty, config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { - *tty->termios = *old_termios; + tty->termios = *old_termios; dev_err(&edge_port->port->dev, "%s - out of memory\n", __func__); return; } - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; config->wFlags = 0; @@ -2362,7 +2362,7 @@ static void change_port_settings(struct tty_struct *tty, } else dbg("%s - OUTBOUND XON/XOFF is disabled", __func__); - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; /* Round the baud rate */ baud = tty_get_baud_rate(tty); @@ -2408,10 +2408,10 @@ static void edge_set_termios(struct tty_struct *tty, struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int cflag; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; dbg("%s - clfag %08x iflag %08x", __func__, - tty->termios->c_cflag, tty->termios->c_iflag); + tty->termios.c_cflag, tty->termios.c_iflag); dbg("%s - old clfag %08x old iflag %08x", __func__, old_termios->c_cflag, old_termios->c_iflag); dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index fc09414..5a96692 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -381,7 +381,7 @@ static void ir_set_termios(struct tty_struct *tty, ir_xbof = ir_xbof_change(xbof) ; /* Only speed changes are supported */ - tty_termios_copy_hw(tty->termios, old_termios); + tty_termios_copy_hw(&tty->termios, old_termios); tty_encode_baud_rate(tty, baud, baud); /* diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 22b1eb5..bf38640 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -921,7 +921,7 @@ static void iuu_set_termios(struct tty_struct *tty, { const u32 supported_mask = CMSPAR|PARENB|PARODD; struct iuu_private *priv = usb_get_serial_port_data(port); - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; int status; u32 actual; u32 parity; @@ -930,7 +930,7 @@ static void iuu_set_termios(struct tty_struct *tty, u32 newval = cflag & supported_mask; /* Just use the ospeed. ispeed should be the same. */ - baud = tty->termios->c_ospeed; + baud = tty->termios.c_ospeed; dbg("%s - enter c_ospeed or baud=%d", __func__, baud); @@ -961,13 +961,13 @@ static void iuu_set_termios(struct tty_struct *tty, * settings back over and then adjust them */ if (old_termios) - tty_termios_copy_hw(tty->termios, old_termios); + tty_termios_copy_hw(&tty->termios, old_termios); if (status != 0) /* Set failed - return old bits */ return; /* Re-encode speed, parity and csize */ tty_encode_baud_rate(tty, baud, baud); - tty->termios->c_cflag &= ~(supported_mask|CSIZE); - tty->termios->c_cflag |= newval | csize; + tty->termios.c_cflag &= ~(supported_mask|CSIZE); + tty->termios.c_cflag |= newval | csize; } static void iuu_close(struct usb_serial_port *port) @@ -993,14 +993,14 @@ static void iuu_close(struct usb_serial_port *port) static void iuu_init_termios(struct tty_struct *tty) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + tty->termios = tty_std_termios; + tty->termios.c_cflag = CLOCAL | CREAD | CS8 | B9600 | TIOCM_CTS | CSTOPB | PARENB; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; - tty->termios->c_lflag = 0; - tty->termios->c_oflag = 0; - tty->termios->c_iflag = 0; + tty->termios.c_ispeed = 9600; + tty->termios.c_ospeed = 9600; + tty->termios.c_lflag = 0; + tty->termios.c_oflag = 0; + tty->termios.c_iflag = 0; } static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -1012,8 +1012,8 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) u32 actual; struct iuu_private *priv = usb_get_serial_port_data(port); - baud = tty->termios->c_ospeed; - tty->termios->c_ispeed = baud; + baud = tty->termios.c_ospeed; + tty->termios.c_ispeed = baud; /* Re-encode speed */ tty_encode_baud_rate(tty, baud, baud); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index af0b70e..7bcbb47 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -158,7 +158,7 @@ static void keyspan_set_termios(struct tty_struct *tty, p_priv = usb_get_serial_port_data(port); d_details = p_priv->device_details; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; device_port = port->number - port->serial->minor; /* Baud rate calculation takes baud rate as an integer @@ -179,7 +179,7 @@ static void keyspan_set_termios(struct tty_struct *tty, p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none; /* Mark/Space not supported */ - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; keyspan_send_setup(port, 0); } @@ -1086,7 +1086,7 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port) device_port = port->number - port->serial->minor; if (tty) { - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* Baud rate calculation takes baud rate as an integer so other rates can be generated if desired. */ baud_rate = tty_get_baud_rate(tty); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index a4ac3cf..dcada86 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -338,7 +338,7 @@ static void keyspan_pda_set_termios(struct tty_struct *tty, 7[EOMS]1: 10 bit, b0/b7 is parity 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) - HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS + HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS bit. For now, just do baud. */ @@ -353,7 +353,7 @@ static void keyspan_pda_set_termios(struct tty_struct *tty, } /* Only speed can change so copy the old h/w parameters then encode the new speed */ - tty_termios_copy_hw(tty->termios, old_termios); + tty_termios_copy_hw(&tty->termios, old_termios); tty_encode_baud_rate(tty, speed, speed); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 5bed59c..def9ad2 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -311,12 +311,12 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) /* set up termios structure */ spin_lock_irqsave(&priv->lock, flags); - priv->termios.c_iflag = tty->termios->c_iflag; - priv->termios.c_oflag = tty->termios->c_oflag; - priv->termios.c_cflag = tty->termios->c_cflag; - priv->termios.c_lflag = tty->termios->c_lflag; + priv->termios.c_iflag = tty->termios.c_iflag; + priv->termios.c_oflag = tty->termios.c_oflag; + priv->termios.c_cflag = tty->termios.c_cflag; + priv->termios.c_lflag = tty->termios.c_lflag; for (i = 0; i < NCCS; i++) - priv->termios.c_cc[i] = tty->termios->c_cc[i]; + priv->termios.c_cc[i] = tty->termios.c_cc[i]; priv->cfg.pktlen = cfg->pktlen; priv->cfg.baudrate = cfg->baudrate; priv->cfg.databits = cfg->databits; @@ -445,9 +445,9 @@ static void klsi_105_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct klsi_105_private *priv = usb_get_serial_port_data(port); - unsigned int iflag = tty->termios->c_iflag; + unsigned int iflag = tty->termios.c_iflag; unsigned int old_iflag = old_termios->c_iflag; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; unsigned int old_cflag = old_termios->c_cflag; struct klsi_105_port_settings *cfg; unsigned long flags; @@ -560,7 +560,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD)) || (cflag & CSTOPB) != (old_cflag & CSTOPB)) { /* Not currently supported */ - tty->termios->c_cflag &= ~(PARENB|PARODD|CSTOPB); + tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB); #if 0 priv->last_lcr = 0; @@ -587,7 +587,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, || (iflag & IXON) != (old_iflag & IXON) || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { /* Not currently supported */ - tty->termios->c_cflag &= ~CRTSCTS; + tty->termios.c_cflag &= ~CRTSCTS; /* Drop DTR/RTS if no flow control otherwise assert */ #if 0 if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index fafeabb..bf5c749 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -191,11 +191,11 @@ static void kobil_release(struct usb_serial *serial) static void kobil_init_termios(struct tty_struct *tty) { /* Default to echo off and other sane device settings */ - tty->termios->c_lflag = 0; - tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); - tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + tty->termios.c_lflag = 0; + tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); + tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF; /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ - tty->termios->c_oflag &= ~ONLCR; + tty->termios.c_oflag &= ~ONLCR; } static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -581,14 +581,14 @@ static void kobil_set_termios(struct tty_struct *tty, struct kobil_private *priv; int result; unsigned short urb_val = 0; - int c_cflag = tty->termios->c_cflag; + int c_cflag = tty->termios.c_cflag; speed_t speed; priv = usb_get_serial_port_data(port); if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ - *tty->termios = *old; + tty_termios_copy_hw(&tty->termios, old); return; } @@ -612,7 +612,7 @@ static void kobil_set_termios(struct tty_struct *tty, urb_val |= SUSBCR_SPASB_EvenParity; } else urb_val |= SUSBCR_SPASB_NoParity; - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; tty_encode_baud_rate(tty, speed, speed); result = usb_control_msg(port->serial->dev, diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index a71fa0a..df98cff 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -454,7 +454,7 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) * either. */ spin_lock_irqsave(&priv->lock, flags); - if (tty && (tty->termios->c_cflag & CBAUD)) + if (tty && (tty->termios.c_cflag & CBAUD)) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; @@ -634,7 +634,7 @@ static void mct_u232_set_termios(struct tty_struct *tty, { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index d47eb06..2b0627b 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -130,12 +130,6 @@ static void metrousb_read_int_callback(struct urb *urb) /* Set the data read from the usb port into the serial port buffer. */ tty = tty_port_tty_get(&port->port); - if (!tty) { - dev_err(&port->dev, "%s - bad tty pointer - exiting\n", - __func__); - return; - } - if (tty && urb->actual_length) { /* Loop through the data copying each byte to the tty layer. */ tty_insert_flip_string(tty, data, urb->actual_length); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index a07dd3c..012f67b 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1349,7 +1349,7 @@ static void mos7720_throttle(struct tty_struct *tty) } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { mos7720_port->shadowMCR &= ~UART_MCR_RTS; write_mos_reg(port->serial, port->number - port->serial->minor, MCR, mos7720_port->shadowMCR); @@ -1383,7 +1383,7 @@ static void mos7720_unthrottle(struct tty_struct *tty) } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { mos7720_port->shadowMCR |= UART_MCR_RTS; write_mos_reg(port->serial, port->number - port->serial->minor, MCR, mos7720_port->shadowMCR); @@ -1604,8 +1604,8 @@ static void change_port_settings(struct tty_struct *tty, lStop = 0x00; /* 1 stop bit */ lParity = 0x00; /* No parity */ - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; /* Change the number of bits */ switch (cflag & CSIZE) { @@ -1753,11 +1753,11 @@ static void mos7720_set_termios(struct tty_struct *tty, dbg("%s\n", "setting termios - ASPIRE"); - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; dbg("%s - cflag %08x iflag %08x", __func__, - tty->termios->c_cflag, - RELEVANT_IFLAG(tty->termios->c_iflag)); + tty->termios.c_cflag, + RELEVANT_IFLAG(tty->termios.c_iflag)); dbg("%s - old cflag %08x old iflag %08x", __func__, old_termios->c_cflag, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 2f6da1e..402c32d 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1651,7 +1651,7 @@ static void mos7840_throttle(struct tty_struct *tty) return; } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { mos7840_port->shadowMCR &= ~MCR_RTS; status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); @@ -1694,7 +1694,7 @@ static void mos7840_unthrottle(struct tty_struct *tty) } /* if we are implementing RTS/CTS, toggle that line */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { mos7840_port->shadowMCR |= MCR_RTS; status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); @@ -2000,8 +2000,8 @@ static void mos7840_change_port_settings(struct tty_struct *tty, lStop = LCR_STOP_1; lParity = LCR_PAR_NONE; - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; /* Change the number of bits */ if (cflag & CSIZE) { @@ -2161,10 +2161,10 @@ static void mos7840_set_termios(struct tty_struct *tty, dbg("%s", "setting termios - "); - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; dbg("%s - clfag %08x iflag %08x", __func__, - tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag)); + tty->termios.c_cflag, RELEVANT_IFLAG(tty->termios.c_iflag)); dbg("%s - old clfag %08x old iflag %08x", __func__, old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag)); dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 5976b65..9f55556 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -404,10 +404,10 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty) static void oti6858_init_termios(struct tty_struct *tty) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 38400; - tty->termios->c_ospeed = 38400; + tty->termios = tty_std_termios; + tty->termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios.c_ispeed = 38400; + tty->termios.c_ospeed = 38400; } static void oti6858_set_termios(struct tty_struct *tty, @@ -425,7 +425,7 @@ static void oti6858_set_termios(struct tty_struct *tty, return; } - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; spin_lock_irqsave(&priv->lock, flags); divisor = priv->pending_setup.divisor; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 13b8dd6..2b9108a 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -260,16 +260,16 @@ static void pl2303_set_termios(struct tty_struct *tty, serial settings even to the same values as before. Thus we actually need to filter in this specific case */ - if (!tty_termios_hw_change(tty->termios, old_termios)) + if (!tty_termios_hw_change(&tty->termios, old_termios)) return; - cflag = tty->termios->c_cflag; + cflag = tty->termios.c_cflag; buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __func__); /* Report back no change occurred */ - *tty->termios = *old_termios; + tty->termios = *old_termios; return; } diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 151670b..7df9cdb 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -275,7 +275,7 @@ static void qt2_set_termios(struct tty_struct *tty, { struct usb_device *dev = port->serial->dev; struct qt2_port_private *port_priv; - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; u16 baud; unsigned int cflag = termios->c_cflag; u16 new_lcr = 0; @@ -406,7 +406,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) port_priv->device_port = (u8) device_port; if (tty) - qt2_set_termios(tty, port, tty->termios); + qt2_set_termios(tty, port, &tty->termios); return 0; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 0274710..b14ebbd 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -382,7 +382,7 @@ static int sierra_send_setup(struct usb_serial_port *port) static void sierra_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - tty_termios_copy_hw(tty->termios, old_termios); + tty_termios_copy_hw(&tty->termios, old_termios); sierra_send_setup(port); } diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index cad6089..ab68a4d 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -316,10 +316,10 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) static void spcp8x5_init_termios(struct tty_struct *tty) { /* for the 1st time call this function */ - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 115200; - tty->termios->c_ospeed = 115200; + tty->termios = tty_std_termios; + tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios.c_ispeed = 115200; + tty->termios.c_ospeed = 115200; } /* set the serial param for transfer. we should check if we really need to @@ -330,7 +330,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned short uartdata; unsigned char buf[2] = {0, 0}; @@ -340,7 +340,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, /* check that they really want us to change something */ - if (!tty_termios_hw_change(tty->termios, old_termios)) + if (!tty_termios_hw_change(&tty->termios, old_termios)) return; /* set DTR/RTS active */ diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 3fee23b..cf2d30c 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -216,7 +216,7 @@ static void ssu100_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct usb_device *dev = port->serial->dev; - struct ktermios *termios = tty->termios; + struct ktermios *termios = &tty->termios; u16 baud, divisor, remainder; unsigned int cflag = termios->c_cflag; u16 urb_value = 0; /* will hold the new flags */ @@ -322,7 +322,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) dbg("%s - set uart failed", __func__); if (tty) - ssu100_set_termios(tty, port, tty->termios); + ssu100_set_termios(tty, port, &tty->termios); return usb_serial_generic_open(tty, port); } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a4404f5..f502a16 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -520,7 +520,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) } if (tty) - ti_set_termios(tty, port, tty->termios); + ti_set_termios(tty, port, &tty->termios); dbg("%s - sending TI_OPEN_PORT", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -562,7 +562,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(dev, port->read_urb->pipe); if (tty) - ti_set_termios(tty, port, tty->termios); + ti_set_termios(tty, port, &tty->termios); dbg("%s - sending TI_OPEN_PORT (2)", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -831,8 +831,8 @@ static void ti_set_termios(struct tty_struct *tty, int port_number = port->number - port->serial->minor; unsigned int mcr; - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; dbg("%s - cflag %08x, iflag %08x", __func__, cflag, iflag); dbg("%s - old clfag %08x, old iflag %08x", __func__, @@ -871,7 +871,7 @@ static void ti_set_termios(struct tty_struct *tty, } /* CMSPAR isn't supported by this driver */ - tty->termios->c_cflag &= ~CMSPAR; + tty->termios.c_cflag &= ~CMSPAR; if (cflag & PARENB) { if (cflag & PARODD) { diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 27483f9..aa4b0d7 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -207,7 +207,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_get_interface; - retval = tty_standard_install(driver, tty); + retval = tty_port_install(&port->port, driver, tty); if (retval) goto error_init_termios; @@ -305,8 +305,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp) * Do the resource freeing and refcount dropping for the port. * Avoid freeing the console. * - * Called asynchronously after the last tty kref is dropped, - * and the tty layer has already done the tty_shutdown(tty); + * Called asynchronously after the last tty kref is dropped. */ static void serial_cleanup(struct tty_struct *tty) { @@ -423,7 +422,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) if (port->serial->type->set_termios) port->serial->type->set_termios(tty, port, old); else - tty_termios_copy_hw(tty->termios, old); + tty_termios_copy_hw(&tty->termios, old); } static int serial_break(struct tty_struct *tty, int break_state) diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 6855d5e..72b678d 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -67,7 +67,7 @@ void usb_wwan_set_termios(struct tty_struct *tty, struct usb_wwan_intf_private *intfdata = port->serial->private; /* Doesn't support option setting */ - tty_termios_copy_hw(tty->termios, old_termios); + tty_termios_copy_hw(&tty->termios, old_termios); if (intfdata->send_setup) intfdata->send_setup(port); diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 473635e..b36077d 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -724,7 +724,7 @@ static void firm_setup_port(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; port_settings.port = port->number + 1; diff --git a/firmware/Makefile b/firmware/Makefile index 344713b..fdc9ff0 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -40,7 +40,6 @@ fw-shipped-$(CONFIG_BNX2) += bnx2/bnx2-mips-09-6.2.1a.fw \ bnx2/bnx2-mips-06-6.2.1.fw \ bnx2/bnx2-rv2p-06-6.0.15.fw fw-shipped-$(CONFIG_CASSINI) += sun/cassini.bin -fw-shipped-$(CONFIG_COMPUTONE) += intelliport2.bin fw-shipped-$(CONFIG_CHELSIO_T3) += cxgb3/t3b_psram-1.1.0.bin \ cxgb3/t3c_psram-1.1.0.bin \ cxgb3/t3fw-7.10.0.bin \ diff --git a/firmware/intelliport2.bin.ihex b/firmware/intelliport2.bin.ihex deleted file mode 100644 index e9cfe8c..0000000 --- a/firmware/intelliport2.bin.ihex +++ /dev/null @@ -1,2147 +0,0 @@ -:100000003C4237180201030000000000000000001D -:10001000576564204465632030312031323A3234F0 -:100020003A33302031393939000000000000000037 -:10003000E96C0F426547694E6E496E47206F462056 -:10004000634F6445CC135A15E8167618041A921BB0 -:10005000201DAE1E3C20CA215823E6247426022807 -:1000600090291E2BAC2C3A2EC82F5631E432723414 -:1000700000368E371C39AA3A383CC63D543FE24020 -:100080007042FE438C451A47A848364AC44B524D2D -:10009000E04E6E50FC518A531855A6563458C2593A -:1000A000505BDE5C6C5EFA5F88611663A464326646 -:1000B000C0674E69DC6A6A6CF86D866F1471A27253 -:1000C0003074BE754C776C778C77AC7733DB8ADC19 -:1000D0005333DB250700750A8A1E080183E30CEB06 -:1000E00020903C01750A8A1E080180E3C0EB129043 -:1000F0008A1E0D013C02750680E30CEB049080E340 -:10010000C053508B1EBA138EDBE86A65558BEC53D7 -:100110001E2BC08ED88B5E04C1E304035E06D1E3C0 -:100120002E8B9F44008D472A1E5A1F5B5DC3558B43 -:10013000EC531E2BC08ED88B5E04C1E304035E0615 -:10014000D1E32E8B9F44008D47341E5A1F5B5DC345 -:10015000FB558BEC53515256571E061E0733C08E6B -:10016000D88B5E04268A47592503008BF0D1E62EF2 -:100170008BB4C400C1E0042602471AD1E08BE82EFC -:100180008BAE4400892C268A471C88440F268A4758 -:100190001D884410268A471E884411268A471F88D6 -:1001A0004412268A4720884413268A472388441409 -:1001B000268A4724884415268A475A88440E33C025 -:1001C00089440689440888440B88440AB021B464F1 -:1001D000894404894402B05588440D88440CE86A77 -:1001E00000725BE8C900E8C110894408807C0F01F7 -:1001F0007429E82B02E87F02807C0F03741DE8A9B4 -:10020000108BF82B44083DA00F7210897C0833C076 -:1002100087440685C07504C6440AFF8A440A84C020 -:10022000750BB80800E86A4AE8A90173BFE84F01F6 -:100230008166487FFF83667ABFB002E8040E8A4475 -:100240000A98071F5F5E5A595B5DC3814E48800064 -:10025000B040E83D4AE88940732AE84D108BD8B099 -:1002600005E82E4AF6462702751AE83D102BC33DD5 -:10027000581B72EB8166487FFFB002E8C40DC6448C -:100280000A01F9C3834E7A40F8C3FBB001E8024A81 -:10029000FAE8991EE40A84C075F0B04EE60AFBB095 -:1002A00001E8EE49FAE8851EE40A84C075F0C3FA55 -:1002B000E87A1EE4EC884416E4E4884417E4F888FD -:1002C0004418E4F0884419E41088441AE41288447D -:1002D0001BE41488441CE43488441DE43688441E1E -:1002E000E4D824018AE0E4DA24020AC488441F8A9C -:1002F0004410E8CD1F8A4411E835218A4412E88968 -:10030000218A4413E84321C686A10000E414241086 -:10031000E614E412243DE6128A44153C01721E776D -:1003200016B011E634B013E636E4140C10E614E40B -:10033000120C40E612EB06E4120C02E6128A440F9D -:100340003C0174063C02740AEB0EE4120C08E6123F -:10035000EB06E4120C10E612E82FFF8A44143C026C -:100360007508B05588440C88440DB021B4648944A4 -:1003700004894402E40C0C10E60CE8ED39FBC3E8F8 -:100380005F3F7308FBB00AE80849EBF3FAE89D1DEC -:100390008A64168A441789869400E6E48AC4E6ECE7 -:1003A0008A64188A441989869600E6F08AC4E6F8B9 -:1003B0008A441AE6108A441BE6128A441CE6148A10 -:1003C000441DE6348A441EE6368A441FE6D8E6DA3F -:1003D000E9B7FE90FA8A440EE6FEE402A80175052C -:1003E00033C0FBF8C333C0E400FBF9C38A64148054 -:1003F000FC02742BFEC0FEC780FF4E721C74098085 -:10040000FF507308B00AEB17B00DEB1302DC32FF9C -:1004100080FB7F7C02B3218AC33C7F7C02B021C376 -:10042000FA807C0B047602FBC38B46243D080072E5 -:10043000F68E46028B7E228A440C8B5C02AAE8ABC5 -:10044000FFAAE8A7FFAAE8A3FFAAE89FFF88440C39 -:10045000895C0280440B04897E22836E24048346D7 -:100460001A04807E26027406806626FDFBC360B0F7 -:10047000FDE8023F61FBC3FA807C0F037509C644A7 -:100480000B00E8E538FBC3C47E148B4E3A85C97572 -:1004900035268B0D4747E3EA3B7E047622B80200FF -:1004A00039462E7707C7462E0000EB138B5E2C894A -:1004B0005E0426C70700004343895E2C29462E852B -:1004C000C978CE894E3A8A440D8B5C04268A25472A -:1004D0003AC47516FE4C0BFF4406E80FFFE2ED88A8 -:1004E000440D895C04894E3AEBA7C6440AFEE879BC -:1004F00038FBC390E8B30D8AE88A0ECB13B3078AA2 -:10050000C1EEEB00EC3AC1750902CDFECB75F0EB04 -:100510000C90880ECB138AE8BBFFFFF9C3880ECB83 -:1005200013F8C390BB3F3F8A8E9E00BAFE00EC8A50 -:10053000E832C122C37502F8C3F9C390E8E5FF733E -:1005400001C3BAD000BB03038A8E9F00EC8AE83255 -:10055000C122C37502F8C3F9C39033C08ED88EC0D0 -:10056000803EC813007507B00AE82647EBF2FB335C -:10057000DB8A1EC913434383FB7E760733DBB0025D -:10058000E80F472E8BAF4400837E080074E7881E77 -:10059000C913B002E8FB46FAF7463840007414E885 -:1005A000961BE87FFF721C33D28A969F0083C20E8F -:1005B000EB0C90E8771BE883FF7208BA4800E83339 -:1005C000FF73AB23CB898E9A0089969C00FE86B57B -:1005D00000C606C81300B00AE8670AFBEB891018CA -:1005E000082833C0A005018AC824407524C7067CAA -:1005F000128E45C70642120100C606541202B00808 -:10060000F6C1017402B004A34612A24C12A29412C5 -:10061000C3C7067C12B645A00F0184C0750E6A00E0 -:100620001FC60693121E9C0EE8B10C90C70644121A -:100630000100A342128BD8C1E304881E9412BEE2CB -:10064000052BF08BC833DB8BFB2EAC888548128AD8 -:10065000D80C05E6FE8AE0EB00E4FE32C4A83F7445 -:1006600003E99E00E400888550128AE02430BA1025 -:10067000FF3C30741280FC04740ABA0403F60608C6 -:1006800001FE7403BA080F88954C1202FA32C0F6C4 -:10069000C4087402B001888558128AC43C35745B62 -:1006A0003C3674573C3474533C04744F3C14744BC4 -:1006B0003C157447A8407425C685541204D1E7B48C -:1006C000038AC389855C128AC38AE380CC01898549 -:1006D0006412D1EF47E203EB1A90E96CFFC6855430 -:1006E0001202D1E78AE68AC30C0489855C12D1EF35 -:1006F00047E2E733C08AC7A34612C3C68554120631 -:10070000EBBBC68554120033C08885501288854CD7 -:100710001288855812EBA6C7462602128B461E8900 -:1007200046008946228B4620894624C7461A000087 -:10073000C3C7463C8000C7463801001E568B763042 -:100740008976048976148E5E0633C089044646890C -:10075000762C89463A8B4632484889462E5E1FC31E -:1007600033C089464889464AC74646AE0189464E47 -:100770008B46448946508B4642894640894608C389 -:1007800033C0894676894678C7467A1000561E8B54 -:10079000767089761089760C8E5E12C70400008B05 -:1007A00046728946741F5EC3895618895602895657 -:1007B0000689560A89560E8956128956168BD84BC9 -:1007C0004BC1E302BF0200897E1E03FB897E30031A -:1007D000FB897E4203FB897E7083EB08895E20895A -:1007E0005E32895E44895E7250E82BFFE871FFE853 -:1007F0003FFFE88BFF58C3B83075C1E8040E5B03B8 -:10080000C3A3BA13833E4212007407803E941200C1 -:10081000750E6A001FC60693121E9C0EE8BD0A9054 -:10082000B8307AC1E80440A3C0132B061201F7D8F0 -:1008300033D28BCA8A0E9412F7F13D8000770E6A8C -:10084000001FC6069312259C0EE8900A90483DFFB3 -:10085000077203B8FF07A3C21333C98A0E94123379 -:10086000F6B800092E8BAC440089464C404646E25F -:10087000F38A0E941233F68B16C013A1C2132E8B7B -:10088000AC4400E822FF03D04646E2F2C333C02E58 -:100890008BAD44008946084747E2F4C35133C00A90 -:1008A000C22E8BAD440089869E00814E38002047C1 -:1008B00047FEC480FC04720432E4FEC0E2E35983C4 -:1008C000E9107405F7D9E8C4FFC35133C00AC22E3A -:1008D0008BAD440089869E00834E3840474780C4D4 -:1008E00010790432E4FEC0E2E65983E9107405F79A -:1008F000D9E899FFC3E8D2FFC38D089C08CA08F560 -:10090000088B0E421233F6515633DB8BCB8A944858 -:10091000128A8C4C128A9C54128BFEC1E70585DB2F -:100920007502B1102EFF97F9085E5946E2D9C3014E -:10093000CC03D000E802D000E801D000E800D000ED -:10094000E804D0A8DA00DC00DE01D803CC03CC0335 -:10095000CC04D0A8DA20DC00DE03CC03CC03CC002E -:10096000D803CC03CC03CC03CC03CC03CC03CC0303 -:10097000CC03CC03CC03CC03CC03CC03CC03CC03FF -:10098000CC04D000DA20DC03DE01D803CC03CC0396 -:10099000CC03CC00D800CC00D0000056521E0E1F55 -:1009A000BE2F0933D2FCAD85C0740D8AD4EEAD855F -:1009B000C074058AD4EEEBEE1F5A5EC3E48084C097 -:1009C00074167814B027E6FCB011E634E4FC3C273A -:1009D0007506E4117502F8C3F9C383C206B0BFEE11 -:1009E00083EA02B010EE8886AF00B01183C204EE35 -:1009F00083C202EEB01383C202EE83C202EE2EA1C6 -:100A00004C2D8986940083EA0EEE83C2028AC4EEDE -:100A100083C204B003EE8886A80083EA0432C0EEE5 -:100A200083C202B089EE8886A6000C06EEB040B400 -:100A30003889461CC74636380083C20432C0EE8867 -:100A400086A700C383C206B0BFEE83EA02EC3A86F3 -:100A5000AF00752483C204EC3C11751C83C206EC04 -:100A60003C13751483EA088A86A800EE83EA02EC38 -:100A700024C03CC07502F8C3F9C333C98BD18BF1D4 -:100A80008A0E9412C1E9022E8BAC4400F74638005E -:100A900020740E8A869E00E6FE32C0E68042E8FAA6 -:100AA000FE83C608E2E185D27403E80508C333C9B2 -:100AB0008BF18A0E94122E8BAC4400F7463840001E -:100AC0007406E87316E812FF4646E2EAC333C98BA0 -:100AD000F18A0E9412C1E9022E8BAC4400F746381D -:100AE00000207416E84616E8D2FE730E6A001FC690 -:100AF0000693121C9C0EE8E3079083C608E2D9C354 -:100B000033C98BF18A0E94122E8BAC4400F7463811 -:100B100040007416E82116E82AFF730E6A001FC60B -:100B20000693121C9C0EE8B307904646E2DAC30C0B -:100B300000001000131200001400283C001B3E00AF -:100B4000002A00002C0000420014D80000DA000047 -:100B50003400113600133800113A001300005650CB -:100B600052BE2F0B2EAD85C07406922EACEEEBF468 -:100B70005A585EC3532EA16022E6E4E6F08AC4E62A -:100B8000ECE6F8E8D8FFB04BE610B050E612B0380B -:100B9000E614E8AE15B046E60AE8A715B01AE60A6C -:100BA000E8A015B022E60AE89915E8FD068BD8E41E -:100BB00016A8047518E8F2062BC33D320072F06ADD -:100BC000001FC6069312239C0EE8100790E8DA0671 -:100BD0002BC33D2400771BB031E6FC565155B910AC -:100BE000002E8BAC4400814E3880004646E2F25D18 -:100BF000595EE869FFE84B15B046E60AE844155B24 -:100C0000C333F68B0E42122E8BAC4400F7463800ED -:100C1000207406E81715E85BFF83C620E2E9C38B62 -:100C2000C20504008946282EA14C2D89868E008994 -:100C300086900089869200C686A3000AC686C300F5 -:100C4000035283C2048A86A6000C06EE5A83C202AF -:100C5000B005EE8886A500C3E803FFE8E514B042BE -:100C6000E60AF74638800074062EA19C22EB042E7B -:100C7000A16C22C7461C0C008986940089869600C8 -:100C800089868E008986900089869200E6F0E6E4E7 -:100C90008AC4E6F8E6ECC686C30003E8A514B01AD9 -:100CA000E60AB0108886A500E60CC333C98BF18A2A -:100CB0000E94122E8BAC4400F7463840007406E8C0 -:100CC0007614E85AFF4646E2EAC333C98BF18A0E2E -:100CD00094122E8BAC4400F7463800207406E84C82 -:100CE00014E874FF4646E2EAC390833E441200755E -:100CF00014B001BA0601EE2AC0EEB002EEB004EE66 -:100D0000B80002EB0FBA0601B040EEB801008A0E3F -:100D10000E01D3E0A38812C3A18812A384122D2050 -:100D200000A38A122D2000A38212C706861220007B -:100D3000C70680123200C3833E44120074768B0EC5 -:100D4000421233F68AA4541284E4745F8A844812EF -:100D50000C04E6FEF6C4047425B01BBA0000EEEBEA -:100D6000002AC0BA0200EEEB00B003EEEB0032C086 -:100D7000BA0200EEEB00BA0000B000EEEB2DB01F9F -:100D8000BA0000EEEB002AC0BA0200EEEB00B0039E -:100D9000EEEB00D1E68A845D12D1EEF6D0BA020005 -:100DA000EEEB00BA0000B00AEEEB00E404EB00E466 -:100DB0000446E290C390B81400BA3EFFEFB80600B4 -:100DC000BA32FFEFB80F00BA34FFEFBA36FFEF8345 -:100DD0003E4412007516B81100BA38FFEFB8120081 -:100DE000BA3AFFEFB81B00BA3CFFEFC3B81100BA24 -:100DF00038FFEFB81200BA3AFFEFB81B00BA3CFF59 -:100E0000EFC3B8FC00BA28FFEFFB833E4412007426 -:100E100007B8CC00BA28FFEFC300FFFF202428FF4B -:100E20002CFFFF303438FFFF3C903C0F770EBB198E -:100E30000E2ED73CFF74058AD8F8C3902ADBF9C37D -:100E4000833E4412007427A00601802606013080EC -:100E50003E0601307518B90200BFC413BA0601EC92 -:100E6000A82075F8BA0401EDABE2F1EB1690B904D5 -:100E700000BFC413BA0601ECA82075F8BA0401EC4F -:100E8000AAE2F1FA90BEC413AD80E43F80FC027484 -:100E90000E6A001FC60693120A9C0EE83E0490AD2F -:100EA0003C0F75ED8AC4E881FF72E6881E1A01C600 -:100EB000068E1200B0000A061A01BA0001EEC6063C -:100EC0008F1240833E4412007506B80C00EB04906C -:100ED000B84C00BA28FFEFC3833E4412007501C32B -:100EE000A150120B0652120AC4A80874F2A00F01F6 -:100EF0002AE450FF36BA131FE8505683C4026A0032 -:100F00001F33C0A3BC13A00F01A3BE138B1EBC13C1 -:100F10008A875012F687501208740D24078AE0BEA3 -:100F2000CC00A0BC13E8943DFF06BC13FF0EBE131B -:100F300075DAC3901E33C08ED8B001E8543D1FC38C -:100F400033C98BF18A0E94122E8BAC4400C74662D3 -:100F50003844C7467CFC3BC7467EE23BC7868000E0 -:100F6000EC3CE8AB16C686C00011837E080074070F -:100F70005156E833335E594646E2CDC333C98BF14F -:100F80008BF98A0E9412C1E902E3132E8BAC440054 -:100F90008A869E0088856C1283C60847E2EDC3FAF4 -:100FA000FCB0C0BA0001EE33C08ED88EC08ED0BF68 -:100FB0001601B9CC772BCFD1E9F3ABBC4012E8D9FD -:100FC00002E8703CBECC0FE8F23CF49033C08ED8FF -:100FD0008EC08ED0F6060A0180740BBE3555E8DB54 -:100FE0003CB001E8AC3CE8B300E8F6F5E808F8E806 -:100FF0000FF9E885FAE8B6FAE8EFFCE8C210E80372 -:101000003CE8B2FDE830FDE85402C6068F12C0E8A5 -:10101000BBFAE8EBFAE8E9FBE8AFFCE88DFCE81F77 -:10102000FFE858FFE8DBFDE816FE33C0BE5A05E8CE -:101030008A3CE8A3FEE8E0FCFBBEA444E87D3CE972 -:10104000CA2D56988BF08B425285C07527C74252E5 -:10105000010053368B9C2C01F6C301750C36896850 -:10106000523689AC2C015B5EC33689AC2C013689C3 -:10107000AC1C015B5EC356988BF033ED368B841C41 -:1010800001A80175158BE833C08742523689841C4C -:1010900001A80174053689842C015EC3565133F6CC -:1010A000B80100B9080089841C0189842C014646D6 -:1010B000E2F4595EC390BB01008BE8FF4E6E740AE8 -:1010C0008BDD8B4658A80174F0C38B4648A90800F5 -:1010D0007445F7463840007427E85C1080C2068AE1 -:1010E00086A80024BF8886A800EE60B0FEE886329D -:1010F00061B002E84CFF8B464824F7894648EB175D -:10110000E82A10814E2600408A86A5000C028886B7 -:10111000A500E60C8B4648A904007414B002E8212F -:10112000FF8B464824FB89464860B0DFE8473261C0 -:1011300033C0874658F6C301750B36894758A80156 -:10114000750DE974FFA32201A8017503E96AFF89FF -:101150001E3201C3BB01008BE8F74638400074150E -:10116000E8D50F80C20AECA840750A8BDD8B465685 -:10117000A80174E3C38B462680E4FE80CC02894636 -:1011800026B002E8BCFE33C0874656F6C301750A96 -:1011900036894756A801750BEBBDA32001A8017540 -:1011A00002EBB4891E3001C3601E062BC08ED8A08E -:1011B000901284C07549A12201A8017503E8F6FECA -:1011C000A12001A8017503E88AFFA1AC13487805A6 -:1011D0007445A3AC13A1AE134878057451A3AE13A4 -:1011E000A1B0134878057463A3B013A17E124078B0 -:1011F00003A37E12B80080BA22FFEF071F61CFA0C1 -:101200009112403C02720B33C0A29112FF167C1265 -:10121000EBA4A29112EB9FA08E1232068F12A28E27 -:10122000120A061A01BA0001EEB82C01EBA4833EA3 -:101230008412107211BA28FFED0C81EFE85337BA0F -:1012400028FFED247EEFB80400EB92C6068D120154 -:10125000E83F37C6068D1200A1B213EB8B908A1EB1 -:101260000B012AFF6BC319BA62FFEFB80A00BA601C -:10127000FFEFB801E0BA66FFEFB8FFFFBA52FFEF29 -:10128000B809C0BA56FFEFC706AC132C01C706AEAB -:10129000130400C606911200C3908A1E0B012AFF98 -:1012A0006BC305D1E8A31801C39052BA50FFED5AA1 -:1012B000C39053518B1E1801B9320590E2FE4B7555 -:1012C000F7595BC3B080BA00010A061A01EEC39059 -:1012D000B040EBF2B0C0EBEEB000EBEAFA60061EF5 -:1012E000162BDB8EDB2EA1BA4C2EA3924CA09312B0 -:1012F000988BE889262D7A803ECA13007403E96B27 -:1013000042E8C0FFE8ABFFE8A8FFB020C606901295 -:1013100000FF167C128BFD83FF0A7211E8B9FFE80B -:1013200090FFE8ABFFE88AFF83EF0AEBEA0BFF745C -:101330000FE8A4FFE87BFFE89AFFE875FF4F75F11F -:10134000E895FFE86CFFEBB98A86A50024FDEE88DE -:1013500086A500C38A86A6000C02EEC38B7638F7FA -:10136000C6010074EF8B4E368B462E3BC173028B49 -:10137000C82BC189462E014E34C47E0426010D8B34 -:101380007E2C83EA04F36C8EC1897E2C3B463C7232 -:1013900012F7C62000750B83CE20897638B000E89E -:1013A000A0FCC3F7C60400741B8BD883CE108976CB -:1013B000388A86A70024FE8886A70083C208EE83A9 -:1013C000EA088BC33D40007201C3814E380004839C -:1013D000C2028A86A50024FA8886A500EEC38A8602 -:1013E000A6000C02EEC3F74638010074F18B4E2EB6 -:1013F00032DB8ABEA30083C206C476048B7E2C83B4 -:10140000F908722CECA80174168AE083EA0AEC83CE -:10141000C20A84E77551AAFEC34983F90873E5320D -:10142000FF26011C015E34897604894E2E897E2CAC -:101430003B4E3C7211F64638207401C3834E38206F -:10144000B000E8FDFBC3F64638047415834E38102F -:101450008A86A70024FE8886A70083EA02EE83C25C -:10146000023D4000725DC332FF26031C85DB740918 -:1014700026891C8BF74747494980E41E80CCC0264B -:101480008904F6C41074278B7638F7C60010740BE5 -:1014900050FE86B200B00AE8A8FB58F7C6000174F7 -:1014A0000DE882268B76388B4E2E8B7E04AB8BF725 -:1014B00033C0AB32DB8ABEA300494983F9087217F7 -:1014C000E941FF814E38000483C2F88A86A50024D2 -:1014D000FA8886A500EEC3E945FF83C208EC88863A -:1014E000AA00C0E8048AE08AC88686A90032E08B98 -:1014F0005E3E84E3744F8AC18B4E26F6C504740C9D -:10150000A808740580E1BFEB0380C940F6C50874E4 -:101510000CA802740580E17FEB0380C980884E2609 -:101520008BF08A86A50084C97408A802741524FD6E -:10153000EB06A802750D0C028886A50083EA0AEE68 -:1015400083C20A8BC684E77501C3C686BA0001B0A0 -:101550000EE8EEFAF74638000274EE837E2E06722D -:10156000E88AA6AA00C45E048B7E2CB0FFAAB00253 -:10157000AB26830703836E2E03897E2CF646382024 -:101580007401C3834E3820B000E8B6FAC39083EAF2 -:1015900008E9B4FD83C2068B5E26F6C3C075EF8BE7 -:1015A0004E1CEC8886A40083EA0AA82075028ACD26 -:1015B00032ED8B461A3BC87318014E2A2BC189465F -:1015C0001AC57600F36E8ED98976003D2000723000 -:1015D000C385C074318BC801462AC57600F36E8E70 -:1015E000D980CB02895E26E832F1F6C701751683F1 -:1015F000C202E853FDF6C710750BB002E843FAC308 -:10160000F6C70174F0C380CB02895E26F6C7017469 -:10161000DE83C202E831FDF686A40040740B80E749 -:10162000FE80CF02895E26EBCCB004E814FAC3C07A -:10163000C2C8CAC4C6CCCED0D2D8DAD4D6DCDE90EA -:10164000E90E01E4C48AE0E4C48BD083F90872F0A7 -:1016500026833F0074048BDF49498BFB8ADE83E3DA -:101660000F2E8AA72F16ABF6C4107424F7C60010ED -:10167000740B50FE86B200B00AE8C6F958F7C600EF -:1016800001740DE8A0248B76388B4E2E8B7E04AB34 -:10169000897E0433C0AB4949894E2E897E2C8BC18B -:1016A000EB4E90EB9E90E4D684C07963E6D08AC876 -:1016B00025030003D8D1E32E8BAF4400888EAE0003 -:1016C0008B4E2EC45E048B7E2C8B7638E4862407EA -:1016D0003C0375CFE41C913BC173028BC82BC189BD -:1016E000462E014E3426010FBAC400F36C897E2CBD -:1016F0003B463C721CF7C62000750B83CE208976D2 -:1017000038B000E83CF98A86AE00243FE6D6C3F93B -:10171000C3F7C60A007435F7C61000752F83CE10C4 -:10172000897638F7C60200740E50E4D824FEE6D855 -:1017300058F7C6080074155051B9E803E40A84C08C -:10174000E0FA84C07504B024E60A59583D4000739D -:10175000B58A86A50024EF8886A500E60C81CE1008 -:1017600004897638EBA00008040C0109050D020A73 -:10177000060E030B070F004080C02060A0E0105051 -:1017800090D03070B0F0E4D2E6D08AC825030003D0 -:10179000D8D1E32E8BAF4400888EAE00E4D8C0E8E9 -:1017A000048BD82E8A8766178AE08AC88686A900A5 -:1017B00032E0E4988B5E3E84E374548AC18B4E26FB -:1017C000F6C504740CA808740580E1BFEB0380C95A -:1017D00040F6C508740CA802740580E17FEB038015 -:1017E000C980884E268BF08A86A500F6C1FD740854 -:1017F000A806741924F9EB0FA8067511F6C5017532 -:10180000040C04EB020C028886A500E60C8BC6844F -:10181000E775098A86AE00243FE6D2C3C686BA00C1 -:1018200001B00EE81CF8F74638000274E6837E2EFD -:101830000672E08A86A9008AE08686AA008AC832F3 -:10184000C480C90B22C1C0E4040AE0C45E048B7EDC -:101850002CB0FFAAB002AB26830703836E2E038948 -:101860007E2CF646382075AB834E3820B000E8D188 -:10187000F7EBA090E41224DFE61281E3FE9F895E7D -:1018800026836648F7EB7390F6C72075E7E4120CE1 -:1018900020E61232C0E6C6B083E6C680CF20895E5D -:1018A000268A86A5000C028886A500E60CEB7490BB -:1018B000F6C74075D3E4120C20E61232C0E6C6B07B -:1018C00081E6C680E7DF80CB01895E26B006E8713D -:1018D000F7908A86A50024F9E60C8886A500EB43DC -:1018E000E4D4E6D08BF825030003D8D1E32E8BAFE8 -:1018F00044008B5E26F6C76075B6F6C3C075D3BAD2 -:10190000C6008B4E1C8B461A3BC8731E014E2A2BF9 -:10191000C189461AC57600F36E8ED98976003D20BE -:1019200000723D8BC7243FE6D4C385C074398BC891 -:1019300001462AC57600F36E8ED983CB02895E26D6 -:10194000E8D9EDF6C70175398A86A50024F9E60CB9 -:101950008886A500F6C71075CAB002E8E4F6EBC3A6 -:10196000F6C70174EFEBBCF6C70174DC8A86A500EC -:10197000A802741181E3FFFE81CB0002895E26EB91 -:10198000C78A86A50024FB0C02E60C8886A500EB1E -:101990009290FDF7DF7FFEFBEFBF0004000405041B -:1019A00005040104000405040504060406040504F6 -:1019B00005040604060405040504020400040504E5 -:1019C00005040104000405040504060406040504D6 -:1019D00005040604060405040504070407040504B9 -:1019E00005040704070405040504060406040504A9 -:1019F0000504060406040504050407040704050499 -:101A00000504070407040504050406040604050488 -:101A10000504060406040504050403040004050483 -:101A20000504010400040504050406040604050475 -:101A30000504060406040504050402040004050464 -:101A40000504010400040504050406040604050455 -:101A50000504060406040504050407040704050438 -:101A60000504070407040504050406040604050428 -:101A70000504060406040504050407040704050418 -:101A80000504070407040504050406040604050408 -:101A90000504060406040504050433DB8AD88A8796 -:101AA0006C12E6FEC1E302E4CEA8047509A8027434 -:101AB00003E92CFEF9C35053E8CBFC5B58A8027431 -:101AC00003E91CFEF8C333DB8AD88A876C12E6FE72 -:101AD000C1E302E9D0FB9A1AC61A00000200040012 -:101AE00002000600020004000200080002000400D8 -:101AF000020006000200040002000A0002000400C6 -:101B000002000600020004000200080002000400B7 -:101B1000020006000200040002000C0002000400A3 -:101B20000200060002000400020008000200040097 -:101B3000020006000200040002000A000200040085 -:101B40000200060002000400020008000200040077 -:101B5000020006000200040002000E000200040061 -:101B60000200060002000400020008000200040057 -:101B7000020006000200040002000A000200040045 -:101B80000200060002000400020008000200040037 -:101B9000020006000200040002000C000200040023 -:101BA0000200060002000400020008000200040017 -:101BB000020006000200040002000A000200040005 -:101BC00002000600020004000200080002000400F7 -:101BD00002000600020004000200C390DA1494150B -:101BE0005C13E613DA1BDA1BE613DA1B8B94641220 -:101BF000C1E604A80174355033C08AC2E6FEE4A0F1 -:101C000085C074278BD82E8A9FDA1A52562E8BA83D -:101C100044008B5628ECA801750D8886AD00240E73 -:101C20008AD82EFF97DC1B5E5AEBCD58A80274367B -:101C300083C61033C08AC6E6FEE4A085C074278B35 -:101C4000D82E8A9FDA1A52562E8BA844008B56281B -:101C5000ECA801750D8886AD00240E8AD82EFF975A -:101C6000DC1B5E5AEBCDC39032E48BD88BD02E8A2E -:101C70009F9A192E2297921956528AC3240303C69B -:101C800080E304D0EB2EFF97D61A585EA955007555 -:101C9000D9C3601E062BC08ED8A15C12E6FEE400FC -:101CA00022C4740833F6E8BFFFEBEE90E40407E4C7 -:101CB000041FB80080BA22FFEF61CF90601E062B90 -:101CC000C08ED8A15E12E6FEE40022C47408BE04F1 -:101CD00000E894FFEBEDE40407E4041FB80080BAC9 -:101CE00022FFEF61CF90601E062BC08ED8A15C1240 -:101CF000E6FEE40022C4741833F6E86BFFA160121C -:101D0000E6FEE40022C474E5BE0800E85AFFEBDDFD -:101D1000A16012E6FEE40022C475EDE40407E404C9 -:101D2000A15C12E6FEE4041FE404B80080BA22FFBE -:101D3000EF61CF90601E062BC08ED8A15E12E6FE2A -:101D4000E40022C47419BE0400E81CFFA16212E67C -:101D5000FEE40022C474E4BE0C00E80BFFEBDCA13F -:101D60006212E6FEE40022C475EDE40407E404A177 -:101D70005E12E6FEE4041FE404B80080BA22FFEF1E -:101D800061CF601E062BC08ED8A15C12E6FEE480F7 -:101D900084C4740833F6E853FEEBEE90B80080BAC2 -:101DA00022FFEF071F61CF90601E062BC08ED8A1C7 -:101DB0005E12E6FEE48084C47408BE0200E82CFED5 -:101DC000EBEDB80080BA22FFEF071F61CF90601ED5 -:101DD000062BC08ED8A16012E6FEE48084C474088D -:101DE000BE0400E806FEEBEDB80080BA22FFEF0764 -:101DF0001F61CF90601E062BC08ED8A16212E6FE36 -:101E0000E48084C47408BE0600E8E0FDEBEDB80091 -:101E100080BA22FFEF071F61CF90601E062BC08E95 -:101E2000D8A15C12E6FEE40022C4741833F6E83749 -:101E3000FEA16012E6FEE48084C474E5BE0400E8FE -:101E4000AAFDEBDDA16012E6FEE48084C475EDA17D -:101E50005C12E6FEE40407E4041FB80080BA22FF27 -:101E6000EF61CF90601E062BC08ED8A15E12E6FEF9 -:101E7000E40022C47419BE0400E8ECFDA16212E67D -:101E8000FEE48084C474E4BE0600E85FFDEBDCA1E0 -:101E90006212E6FEE48084C475EDA15E12E6FEE403 -:101EA0000407E4041FB80080BA22FFEF61CF601E70 -:101EB000062BC08ED8A15C12E6FEE48084C47418A0 -:101EC00033F6E827FDA16012E6FEE40022C474E5C3 -:101ED000BE0800E892FDEBDDA16012E6FEE4002200 -:101EE000C475EDE40407E4041FB80080BA22FFEFD4 -:101EF00061CF601E062BC08ED8A15E12E6FEE48084 -:101F000084C47419BE0200E8E2FCA16212E6FEE499 -:101F10000022C474E4BE0C00E84DFDEBDCA16212AB -:101F2000E6FEE40022C475EDE40407E4041FB800F3 -:101F300080BA22FFEF61CF90601E062BC08ED8A121 -:101F40005C12E6FEE48084C4741833F6E89DFCA1BC -:101F50006012E6FEE48084C474E5BE0400E88CFCF4 -:101F6000EBDDA16012E6FEE48084C475ED071FB8C6 -:101F70000080BA22FFEF61CF601E062BC08ED8A171 -:101F80005E12E6FEE48084C47419BE0200E85CFCC4 -:101F9000A16212E6FEE48084C474E4BE0600E84B4D -:101FA000FCEBDCA16212E6FEE48084C475ED071F41 -:101FB000B80080BA22FFEF61CF90601E062BC08E62 -:101FC000D8902AC0E6FEE4CEA801741433DBE8D52D -:101FD000F6EBEF90B80080BA22FFEF071F61CF90B9 -:101FE000F60605010175EDB001E6FEE4CEA8017428 -:101FF000E3BB0400E8AFF6EBC990601E062BC08E71 -:10200000D890FB90FA2AC0E6FEE4CEA802741333FF -:10201000DBE8CCF8EBECB80080BA22FFEF071F61D9 -:10202000CF90A80474F033DBE85BF7EBD590601E2B -:10203000062BC08ED890FB90FAB001E6FEE4CEA845 -:10204000027415BB0400E897F8EBEB90B80080BA77 -:1020500022FFEF071F61CF90A80474F0BB0400E8D3 -:1020600024F7EBD26A001FC6069312099C0EE86B98 -:10207000F2906A001FC6069312299C0EE85DF2904A -:10208000722072207220CE1D921CE61C1A1E722035 -:10209000821DAE1E381F7220821D72207220381FD2 -:1020A000722072207220F41DBC1C341D641E72202C -:1020B000A81DF21E781F7220A81D72207220781FA2 -:1020C000FCB940008CCBB864202BFFAB93AB93E200 -:1020D000FAC7064C00A811833E4412007520C706BB -:1020E0003C00084BC7063000BA1FC7063400FA1F71 -:1020F000F6060501017506C70638002E20C3C7067F -:102100003C00564B33DB8A1E5412C1E302021E56BA -:10211000122E8B878020A330008A1E5512C1E30245 -:10212000021E57122E8B87A020A33400C38B869EDD -:1021300000E6FE86C4E6D0C38B869E00E6FE33D260 -:102140008AD4C351B91027E40A909084C07405E280 -:10215000F659F9C359F8C384C0781E518AE88AC871 -:10216000B80100D3E0098698003AAEA00059751076 -:10217000E8A9E5834E2602F9C39889869800EBF01A -:10218000F8C384C07812518AE08AC8B80100D3E04D -:1021900059F7D021869800C3C78698000000C383F2 -:1021A000C2048A86A6000C04EE83EA04C3E893FF07 -:1021B0007204B082E60AC38B4626A8FD74118A8693 -:1021C000A500A806740824F98886A500E60CC3F6C5 -:1021D000C401740A8A86A50024FB0C02EB0CA80239 -:1021E000750F8A86A50024FD0C043A86A50075D8D3 -:1021F000C38A86A500EBCFE4D833DB8AD8C0EB04D2 -:102200002E8A9F6617889EA9008B5E2680E33FF684 -:10221000C7047407A810750380CB40F6C70874077D -:10222000A880750380CB40885E268A86A500F6C309 -:10223000FD740DA806740824F98886A500E60CC371 -:10224000F6C70174040C02EBF0F6C30275E90C0446 -:10225000EBE7C404C4048504590448044104C303DF -:10226000820341038202570241028201410182003E -:1022700041004E02AD0157012D002B002700210027 -:102280001600F404F404A3046F045B045104F40383 -:10229000A3035103A3026D025102A3015101A30044 -:1022A00051006202D9016D01380036003100290069 -:1022B0001B005157BF0200EB0F905156BF0100EBBE -:1022C00007905156BF0300903C197602B017988BC7 -:1022D000F08A82C4002AE48BF083FE187346D1E6AC -:1022E0002E8B8C5222F74638800074052E8B8C8200 -:1022F00022F7C7020074123B8E9400740C898E94EE -:10230000008AC5E6EC8AC1E6E4F7C7010074123B17 -:102310008E9600740C898E96008AC5E6F88AC1E60E -:10232000F05E59C377068B8E8E00EBC58B8E9000C6 -:10233000EBBFD503F6003E0010000400CA043301D1 -:102340004D00140005000103050709000102030404 -:1023500080841E00A02526000000608BF033FF2E35 -:10236000A150232E8B165223BB3223F74638800010 -:10237000740C2EA154232E8B165623BB3C23B90577 -:10238000002E3B31730A4747E2F7B8FFFFEB1D9081 -:10239000D1EF2E8A8D46232AEDD1EAD1D8E2FAF781 -:1023A000F6050200C1E8022E8AA54B232EA358236E -:1023B000612EA15823C3080020008000000260099C -:1023C0000800200080000002000800000100020058 -:1023D0000300040052565785C074053D0109760379 -:1023E000B80109BF5B01F7463880007403BFB20132 -:1023F00033F62E3B84B62376044646EBF5F7E72EFC -:102400008BBCC02303C783D200D1E7F7F72E8AA481 -:10241000CA235F5E5AC3E43E80BEC30003750CF757 -:10242000467A200074050C80E63EC3247FE63EC356 -:1024300024038886C3008AE0E41024FC0AC4E61062 -:10244000808EA10042E8CEFFC390568BF083E60752 -:10245000D1E62EFFA458249068246C2470247424A0 -:102460007824872487248724B400EB0EB4C0EB0AB9 -:10247000B440EB06B420EB02B4A0E410241F0AC45D -:10248000E610808EA100425EC3903C0277128AE083 -:10249000E41024F3C0E4020AC4E610808EA10042D6 -:1024A000C3908B5E3884C0741F3C02742083CB08B9 -:1024B0008B462E3B463C770CE888FC7207B024E63E -:1024C0000A83CB10895E38C383E3F7EBF7F7C310B9 -:1024D0000074F5E86DFC72EC8A86C000E638B02323 -:1024E000E60AEBE08B5E388B462E3B463CE4D87721 -:1024F0000B24FE80CB12E6D8895E38C30C0180CB5A -:1025000002EBF35033DBC1E804250F0F8AD82E8A83 -:102510008766178ADC2E8AA7661709463E58C3507D -:1025200033DBC1E804250F0F8AD82E8A8766178A05 -:10253000DC2E8AA76617F7D021463E58C38B463E4D -:1025400033DB8AD80ADC2E8A877617E62C8AE0E409 -:102550002A240F0AC4E62A8A86A50084E4750DA8F9 -:10256000807411247F8886A500E60CC3A8807504BA -:102570000C80EBF1C31E6033C933D233F68ED98D94 -:10258000BEFD00578B0584C074168BD1428BFE4F65 -:10259000780938A3E40074084F79F788A2E400466C -:1025A0005F83C7094183F91072D989B6860089967D -:1025B0008400611FC353C7466600008B4664A94070 -:1025C00000740DB300A980007402B37F889EC1001F -:1025D00032DBA90200740380CB40A9004074038061 -:1025E000CB02A90080740380CB01A9301E74038044 -:1025F000CBBCA90020740380CB08A904017403801C -:10260000CB10A90800740380CB20889EC2005BC356 -:102610000651575016078DBEC400B91F0033C0AA1B -:1026200040E2FC8B86920089868E00898690005855 -:102630005F5907C3E4D8C0E80453250F008BD82E98 -:102640008A8766178886A9005AC30886AC00C686A2 -:10265000BA0001B00EE8EAE9C3AD36A3B413AD3653 -:10266000A3B613AD36A3B81383E90636F706B6133F -:102670000F00C38A4626F74648800074020C108873 -:1026800086BD0032C0837E1A00750E8B5E4043808B -:10269000E3FE3B5E0875020C01837E3A00750D1E59 -:1026A000C55E148B1F1F85DB75020C02F7463810C0 -:1026B0000074020C048B5E7AF7C3020074020C08EB -:1026C000F7C3040074020C10F7C3080074020C2056 -:1026D000F7C3400074020C408886BF00C3906A00B4 -:1026E0001FC60693120D9C0EE8F1EB90B002E6DADD -:1026F000F8C333C0E6DAF8C3B001E6D8F8C333C094 -:10270000E6D8F8C3B0FFE84EFAE8A1FAF8C3AC493E -:10271000E8AFFBF8C390AC49E815FDF8C390AC49AD -:10272000E867FDF8C390AC49E81FFDF8C390AC49D9 -:10273000E634F8C3AC49E636F8C3AC493C02771F2F -:1027400084C0751DE41424EFE614E412243FE6125D -:10275000E416A8047409E8EAF97204B018E60AF865 -:10276000C38AE0E4140C10E614E4120CC0F6C401B1 -:102770007402247FE612F8C3AC49E825FDF8C39043 -:10278000B80040E87DFDE8B4FDE8A8FEB001E8B976 -:10279000FEF8C390B80040E885FDE8A0FDF8C390BE -:1027A000B80010E85DFDE894FDE888FEB008E899FF -:1027B000FEF8C390B80010E865FDE880FDF8C3900E -:1027C000B80080E83DFDE874FDE868FEB002E879F5 -:1027D000FEF8C390B80080E845FDE860FDF8C390BE -:1027E000B80020E81DFDE854FDE848FEB004E859B3 -:1027F000FEF8C390B80020E825FDE840FDF8C3903E -:10280000AC49E84814E43C24E70AC4E63CF8C39029 -:10281000B8FC3B89467CE43C0C18E63CF8C3E41267 -:102820000C02E612F8C3E41224FDEBF6E8B5FCF85E -:10283000C390836638FDF8C3AC49A8017406834E83 -:102840007A20EB0483667ADFE8CBFBF8C3908A86B4 -:10285000A5000C0224FB8886A500E60C814E26010B -:1028600020AC4932E489466E834E48084946F9C394 -:102870008A86A5000C0224FB8886A500E60C814E02 -:10288000260120ACB40AF6E4EBD8E8FA13E43C24C1 -:10289000F80AC4E63CF8C390AD4949894664A901E9 -:1028A00000741B8BD883E3FA751AA90400740FE433 -:1028B0003E0C02E63EB83844894662F8C390E43ED6 -:1028C00024FCEBEFE43E24FCE63EE8E8FCB8AA403A -:1028D000EBE6E86EF87205B018E60AF8C390AC496A -:1028E000E8CFF9F8C390AC49E8CFF9F8C390E868AD -:1028F000FD750632C0E6DAF8C3B002E6DA36A0B4F7 -:102900001324103410E8160136A1B413A901007481 -:1029100005E8FCFEEB0EA90200740432C0EB02B025 -:1029200001E8DEFE36A1B413E8B513E43C24F80A4E -:10293000C4E63C36A1B413C1E805250100E8FAFE5F -:1029400036A0B5132410E859FB32C0368A26B513D9 -:10295000F6C4047409FEC0F6C4087402FEC0E8DBC5 -:10296000FD36A1B613250F00E857F936A1B613C1FD -:10297000E804250300E8B8FA36A1B613C1E8052536 -:102980000200E805FB36A1B613F6C401750432C097 -:10299000EB0980E402D0ECB0022AC4E8ACFA36F6C7 -:1029A00006B713407405E883FEEB03E884FE36F6B1 -:1029B00006B713207405E865FEEB03E868FEF8C36C -:1029C000E4120C01E612F8C3E41224FEEBF6E41460 -:1029D00024F00C05E614E42A24F00C06E62AF8C3D9 -:1029E000E42A24F0E62AE41424F00C07E614F8C3E1 -:1029F000AD4949E864F989868E00F8C3AD4949E8D4 -:102A000058F989869000F8C3834E2604E8A8F7F8A1 -:102A1000C390836626FBE89EF7F8C390AC4984C058 -:102A2000750DE41024EFE610808EA10042F8C3E497 -:102A3000100C10EBF190AC493C02760232C0C0E0C1 -:102A400004A82074020C0824188AE0E41224E70A7F -:102A5000C4E612808EA10044F8C3AC498886C00049 -:102A6000F8C3AC49E63AF8C3AC4984C07408E41230 -:102A70000C04E612F8C3E41224FBEBF6AC49E8D6EA -:102A8000F67303E827F7F8C3E412A802740424FDE0 -:102A9000E612B8F000E887FA816626FFF3E857F7F8 -:102AA000E89AFAF8C390B88000E857FA804E2708F1 -:102AB000E844F7E887FAF8C3B88000E861FA81666D -:102AC00026FFF7E831F7E874FAF8C390B81000E889 -:102AD00031FA804E2704E81EF7E861FAF8C3B8100F -:102AE00000E83BFA816626FFFBE80BF7E84EFAF8B0 -:102AF000C39033C0AC493C017304B001EB063C0CFD -:102B00007602B00C89461CF8C390814E2600208ABC -:102B100086A5000C0224FB8886A500E60C834E26C1 -:102B200001F8C390814E2600408A86A5000C0288D9 -:102B300086A500E60CF8C390AC4950E805F658723B -:102B400008E638B023E60AF8C3F9C390AC50ADE804 -:102B500082F85AF6C201741239869600740C89867E -:102B60009600E6F086E0E6F886E0F6C202741039D8 -:102B7000869400740A89869400E6E486E0E6EC8395 -:102B8000E903C390E4168886BC00E8E6FA33DBE488 -:102B90000CA806740380CB01A810740380CB02A894 -:102BA00080740380CB04E4128AE024180AD8E4DAA3 -:102BB000F6C4027407A840750380CB20A8027509EB -:102BC000E42AA80F740380CB40F74638020074094A -:102BD000E4D8A801750380CB80889EBE00FE86B431 -:102BE00000B00AE85CE4F8C3AC493C027441771FCA -:102BF00050E84FF558720C84C0740AB012E60A808F -:102C00004E3801F8C3B011E60A806638FEF8C38B6F -:102C1000463825FFF7894638A9000475E68A86A557 -:102C200000A81075DE0C108886A500E60CF8C3819C -:102C30004E3800088A86A500A81074C724EFEBE779 -:102C4000AD49493C0172113C0C770D508AE0E41407 -:102C500025F00F0AC4E614588AC484C07402E64200 -:102C6000F8C3E8CFF9FE86B900B00EE8D4E3F8C3A4 -:102C70003A86AF00741F8886AF008AE080C206B033 -:102C8000BFEE80EA028AC4EE8A86A80080C202EE05 -:102C900080EA068AC4C38B463E85C08A86A5007436 -:102CA00012A808750D0C088886A50080C202EE8067 -:102CB000EA02C3A80874FB24F7EBEC8B462684C019 -:102CC00074168A86A500A802740D24FD8886A500C6 -:102CD00083C202EE83EA02C38A86A500A80275F7C2 -:102CE0000C02EBE85283C20CECC0E8048886A90011 -:102CF0008B5E2680E33FF6C7047407A8087503803F -:102D0000CB40F6C7087407A802750380CB80885EA5 -:102D1000268A86A50084DB7410A802740A24FD8824 -:102D200086A50083EA0AEE5AC3A80275FA0C02EBE4 -:102D3000EE90FFFF00480030BA20C41A00180012BD -:102D4000000C0006000300028001C000600030009B -:102D50001800CD0100018000100010000E000C00D2 -:102D6000080000000000060004000300020001004B -:102D70005251563C1E7747988BF08A82C40032E449 -:102D800083FE18743D83FE19743E83FE1E772FD197 -:102D9000E62E8B8C322D3B8E94007422898E94000B -:102DA00083C2068A86A8008AE00C80EE83EA068A3F -:102DB000C1EE83C2028AC5EE83C2048AC4EE5E59A4 -:102DC0005AC38B8E8E00EBCE8B8E9000EBC8525187 -:102DD0003D05007703B805008BC8BA0200B800D0E3 -:102DE000F7F1050100D1E8595AC38B467AA820743F -:102DF0000B80BEC3000375040C01EB0224FE894660 -:102E00007AC324038886C3008AA6A8008ADC80E4EB -:102E1000FC0AC43AC3740B8886A80083C206EE83FA -:102E2000EA06E8C5FFC30008183828903C04772359 -:102E300032E48BD82E8A87262E8AA6A8008ADC80C8 -:102E4000E4C70AC43AC3740B8886A80083C206EE9E -:102E500083EA06C384C07402B0048AA6A8008ADC90 -:102E600080E4FB0AC43AC3740B8886A80083C206B8 -:102E7000EE83EA06C3908B5E3884C074343C0274DF -:102E80003B8A86AF000C04E8E6FD8B462E3B463CB1 -:102E9000771BF7C30004751581CB000483C2028A37 -:102EA00086A50024FA8886A500EE83EA02895E38AA -:102EB000C38A86AF0024FBE8B6FDEBF1F7C3100030 -:102EC00074EFEBED83C20CEC83EA0CC0E804888657 -:102ED000A900C3908A86A7000C018886A7008BDA18 -:102EE00080C208EE8BD3F8C38A86A70024FEEBEAE3 -:102EF0008A86A7000C02EBE28A86A70024FDEBDAA3 -:102F0000B0FFE852F2E897F2F8C3AC49E861FEF886 -:102F1000C390AC49E8EBFEF8C390AC49E835FFF844 -:102F2000C390AC49E805FFF8C3905283C206B0BF16 -:102F3000EE5283C202AC49EE5A8A86A800EE5AF8D5 -:102F4000C3905283C206B0BFEE5283C206EBE69036 -:102F5000AC493C02770D84C0750B8A86AF0024FD16 -:102F6000E80DFDF8C3508A86AF000C02E801FD5B56 -:102F700083C2088A86A700F6C301740C24DF888602 -:102F8000A700EE83EA08F8C30C20EBF2AC49E8E5B1 -:102F9000FEF8C390B80040E869F5E8F9FCE824FFC2 -:102FA000B001E8A5F6F8C390B80040E871F5E8E58F -:102FB000FCF8C390B80010E849F5E8D9FCE804FF34 -:102FC000B008E885F6F8C390B80010E851F5E8C5F8 -:102FD000FCF8C390B80080E829F5E8B9FCE8E4FE05 -:102FE000B002E865F6F8C390B80080E831F5E8A5CE -:102FF000FCF8C390B80020E809F5E899FCE8C4FEA5 -:10300000B004E845F6F8C390B80020E811F5E8856B -:10301000FCF8C390AC49E8340CF8C390B8FC3B8989 -:10302000467CF8C38A86AF000C80E843FCF8C39066 -:103030008A86AF00247FEBF28A86AF000C40E82F2F -:10304000FCF8C3908A86AF0024BFEBF2AC49A8011C -:103050007407834E7A20EB059083667ADFE88AFD59 -:10306000F8C383C2068A86A8000C408886A800EEB2 -:1030700083EA06AC4932E489466E834E2601834ECC -:103080004808B006E8BBDF4946F9C39083C2068A08 -:1030900086A8000C408886A800EE83EA06ACB40A35 -:1030A000F6E4EBD0E8E00BF8C390AD4949894664FB -:1030B000A9010074198BD883E3FA750AA904007476 -:1030C0000DB8E23FEB0BE8ECF4B8AA40EB03B838DC -:1030D00044894662F8C38A86AF00A802740A24FDB8 -:1030E000E88DFB0C02E888FBF8C3AC49E881FCF8EA -:1030F000C390AC49E879FCF8C390E85CF57505E845 -:10310000E6FDF8C3E8CDFD36A0B41324103410E872 -:10311000260136A1B413A901007405E8FEFEEB0EEA -:10312000A90200740432C0EB02B001E8E8FE36A147 -:10313000B413E8AB0B36A1B413C1E805250100E8D0 -:103140000CFF36A0B5132410E82BFD32C0368A26BA -:10315000B513F6C4047409FEC0F6C4087402FEC0B8 -:10316000E8EFFD36A1B613250F00E803FC36A1B643 -:1031700013C1E804250300E888FC36A1B613C1E8B2 -:1031800005250200E8CDFC36A1B613F6C40175048E -:1031900032C0EB0980E402D0ECB0022AC4E88CFC17 -:1031A00036F606B713407405E88DFEEB03E894FE8F -:1031B00036F606B713207405E869FEEB03E870FEE7 -:1031C000F8C3F8C38B4638A9040075230D040089A1 -:1031D000463883C2088B462E3B463C7314834E38D8 -:1031E000108A86A70024FE8886A700EE83EA08F8E6 -:1031F000C38A86A7000C01EBEE908B4638A9040029 -:10320000740625FBFF894638F8C3AD4949E8BEFB83 -:1032100089868E00F8C3AD4949E8B2FB89869000E3 -:10322000F8C3834E2604E892FAF8C390836626FB1F -:10323000E888FAF8C390AC4984C07507808EA30073 -:1032400004F8C380A6A300FBF8C3AC4983C2083CC2 -:1032500002760232C03C017412770B8A86A70024E2 -:10326000EF8886A700EE83EA08F8C38A86A7000CD9 -:1032700010EBEE905283C206B0BFEE5283C204AC94 -:1032800049EE5A8A86A800EE5AF8C3905283C206C5 -:10329000B0BFEE5283C208EBE690AC49F8C3AC492C -:1032A000E8B4EE7303E8F7EEF8C38A86AF00247F34 -:1032B000E8BDF9B8F000E866F2816626FFF3E8237E -:1032C000FAE8D2F9F8C3B88000E837F2804E270850 -:1032D000E811FAE8C0F9F8C3B88000E841F2816665 -:1032E00026FFF7E8FEF9E8ADF9F8C390B81000E85A -:1032F00011F2804E2704E8EBF9E89AF9F8C3B81008 -:1033000000E8FFF1816626FFFBE8D8F9F8C3AC4975 -:10331000F8C383C2068A86A8000C408886A800EEFF -:1033200083EA06F8C39083C2068A86A80024BFEB0E -:10333000EA90AC498AE080C20AEC80EA0AA82074CC -:10334000058AC4EEF8C30651578B4E24E3344989ED -:103350004E24FF461A8E46028B7E228AC4AA897E9C -:10336000228B462624FD89462675298A86A500A833 -:1033700002752180C2020C028886A500EE80EA0256 -:10338000EB12C47E003B7E1E760A4F268825897E7E -:1033900000FF461A5F5907F8C390ACAD83E9038577 -:1033A000C074053D00207205B8FFFFEB03C1E003C8 -:1033B0003B8694007426898694008BD85283C2067B -:1033C0008A86A8008AE00C80EE83EA068AC3EE8330 -:1033D000C2028AC7EE83C2048AC4EE5AF8C3B08818 -:1033E0008886BC00E88CF233DB8A86A500A80274CC -:1033F0000380CB01A805740380CB02A80874038066 -:10340000CB04F686A70010740380CB108A86A9002F -:10341000F6C304750A83C20CEC83EA0CC0E8048A84 -:10342000E08A86AF00A8807408F6C401750380CBDB -:1034300020F686A70002750AF74638040074038058 -:10344000CB40889EBE00FE86B400B00AE8F3DBF8ED -:10345000C3FE86B400B00AE8E8DBF8C3AC493C021E -:103460007437771084C07406804E3801F8C38066C4 -:1034700038FEF8C38B463825FFF7894638A9000483 -:1034800075EA8A86A500A80175E20C0583C2028848 -:1034900086A500EE83EA02F8C3814E3800088A86CA -:1034A000A500A80174C624FAEBE2AD4949F8C3901F -:1034B000E811FAFE86B900B00EE886DBF8C3B0FF6B -:1034C000E8BFECF8C39083667AFBB000E873DBF8E2 -:1034D000C390AC49E853D9721136881E1A0136A040 -:1034E0008E120AC352BA0001EE5AF8C3AC4932E454 -:1034F00036A38612050600368B1E88122BD8368915 -:103500001E8A12F8C390AD8BD8AD83E90403C32B98 -:103510004676894678F7467A0200740A83667AFD11 -:10352000B80000E81CDBF8C3061607AC49250F00FD -:103530006BC0098DBEFD0003F8AC49250F00AA85BC -:10354000C074082BC8518BC8F3A459E827F0E8448D -:103550000307F8C333C0AC4936A3B21336A3B01384 -:10356000F8C383667AEFE82C03F8C390834E7A1091 -:10357000EBF4E89BF0F8C390AD3C19770E3C19775B -:103580000A8BF881E7FF0088A6C400F8C390834E39 -:103590002620AC4932E4D1E08BD8C1E30203C389D1 -:1035A000466E834E4804B006E897DA4946F9C39060 -:1035B000FE86B300B00AE889DAF8C39033C0AC499C -:1035C0006BC00A89868A00F8C390AC4932E43D0A90 -:1035D000007705B80A00EB083D5A007203B85A009C -:1035E00051F7D80564008BC88B4644F7E1B96400F5 -:1035F000F7F189464659F8C3AC49E885EBF8C39022 -:10360000AC4984C07507816638FFFDF8C3814E3828 -:103610000002F74638400075088A86A9008886AA05 -:1036200000F8C3905156E87F0C5E59F8C390FE86AF -:10363000B600B00AE80BDAF8C390FE86B700B00A0D -:10364000E8FFD9F8C390FE86B800B00AE8F3D9F8CD -:10365000C39000905155AC2EA2523633C9AD8BF9B0 -:10366000C1E705A9010074232E8BAD4400837E08B9 -:103670000074182E803E523601740960B004E8BB15 -:103680000C61EB0760B0FBE8EC0C614747D1E875D3 -:10369000D24183F90472C65D5983E905F746384083 -:1036A000007405E887EAF8C3E88DEAF8C39036C6E7 -:1036B00006C81301F8C333C0AC4936A38012AC4925 -:1036C000362B068812F7D836A38212F8C390DE266E -:1036D000DE26EC26F226F826FE2604270E271627DD -:1036E0001E2726272E273427BE34C634D2343A2745 -:1036F000782780279427A027B427C027D427E0273E -:10370000F42700281028EC34DE261E2826282C2832 -:10371000322838284E288A28063528359828BE2889 -:10372000D228DE28E628543562356C35EE28C029CB -:10373000C829CE29E02972357835F029FC298E3543 -:10374000082A122A1C2AB035362ABC355A2A622A7F -:10375000682ACA357C2AF835882AA62AB82ACC2AAB -:10376000DE2AF22A00360A2B242B2436382B4C2B47 -:10377000842B2E363A3646365436E82BAE36402C5D -:10378000622CB6367028DE26DE26D42EE82EF02EE9 -:10379000F82E002F0A2F122F1A2F222F2A2F422FF6 -:1037A000BE34C634D234502F8C2F942FA82FB42F70 -:1037B000C82FD42FE82FF42F083014301C30EC34ED -:1037C000DE2624303030383044304C306230A43083 -:1037D00006352835AA30CE30D630EA30F2305435AE -:1037E00062356C35FA30C231C231C431FA317235CA -:1037F00078350A3216328E3522322C323632B035D6 -:103800004A32BC3574328C329A32CA359E32F8351F -:10381000AA32C632D832EC32FE320E3300361233C0 -:103820002633243632339A33DE332E363A36463652 -:1038300054365C34AE36AA34B034B6368C30E32815 -:10384000F7463840007532E8E3E833C0AC493D5BE9 -:103850000077198BD8D1E32EFF97CE36720B85C92E -:1038600075E88B4648E81A0CC34E41C36A001FC670 -:103870000693120C9C0EE863DAE8BCE833C0AC494E -:103880003D5B0077E78BD8D1E32EFF97863772D95F -:1038900085C975E8C3F7467A1000750F83BE8400AA -:1038A000007408B8483A89868000C381BE8000EC65 -:1038B0003C74F783BE8800007505B8EC3CEBE7F775 -:1038C000467A080075401E608B8E88003B4E7477E8 -:1038D000333B4E78772EC47E108BDF26033D47475F -:1038E00033C08ED88DB6F4008BC1F7467A010075CF -:1038F0001DF3A4260107294678014676294674B0AF -:103900000CE83ED7611FC78688000000EBACE3E3FC -:103910005090AC247FAAE2FA58EBD8908B8E8800A6 -:10392000E3468B9E8A0085DB743EBA50FFED2B8602 -:1039300082003BC372378DB6F400C47E108BDF2645 -:10394000033D47478BC1161FF7467A01007524F3E4 -:10395000A4260107294678014676294674C7868839 -:10396000000000B00CE8DAD683667AF7C3B000E84E -:10397000D0D6C3E3DC50AC247FAAE2FA58EBD29055 -:103980001E6033C08ED88DB6FD008B8688008B9666 -:1039900084003A0475108BDE468BC88DBEF400F3AC -:1039A000A674668BF39083C6094A75E68DB6FD0052 -:1039B0008B9684003A0473108BDE468BC88DBEF460 -:1039C00000F3A674768BF39083C6094A75E68DB62C -:1039D000F400ACF7467A01007402247F1EC55E1025 -:1039E0008B37884002468937FF4E78FF4676FF4E78 -:1039F000741F8B8E880049898E8800E3438DB6F44E -:103A0000008BFE46F3A4E97DFFC576108B1C85DB99 -:103A1000740803F383C60383E6FE8B8684002BC2FF -:103A2000B48089044646C7040000897610834E7A24 -:103A300004C78688000000611FF9C333C0611FC33B -:103A4000B08084C0611FC3908B4E782B8E88007627 -:103A50002789B68C008B5E743BCB72028BCB3BC844 -:103A600072028BC88BC1E34433D28EC28BD183BE2A -:103A70008800007406E98E0033C0C38B5E10031FFC -:103A8000434352F7467A0100752AAC8DBEE4008BA1 -:103A90008E8600F2AE74348807434A75ED588B5E0B -:103AA0001001072946780146762946748BC62B8675 -:103AB0008C00C390AC8DBEE4008B8E8600F2AE7499 -:103AC0000A247F8807434A75EBEBD28886F400C747 -:103AD0008688000100582BC2740E8B5E10010729E6 -:103AE000467801467629467440E894FE72BE4A75CF -:103AF0001583BE8A000074B4BA50FFED8986820037 -:103B0000834E7A08EBA68DBEF40003BE8800A4FFA6 -:103B1000868800E86AFE729479064A748FE95BFF32 -:103B20004A74CEEBE19050E811CC8B467439467262 -:103B300074271E565133C9C5760CAD74107809032D -:103B4000C805010024FE03F03B761076ED294E7681 -:103B5000014E78E837CC595E1F58C390C47E1026BA -:103B60008B1D83C30326891D4B03FBAB91AAB803AE -:103B700000294678014676294674C390C47E1026F3 -:103B80008B1D4326891D4303FBAAFF4E78FF467613 -:103B9000FF4E74C3E8E5FFC38081848582838687F6 -:103BA00050538ADC83E30ED1EB2E8A87983B08863C -:103BB000B000FE86B100B00AE887D45B58C3508AD3 -:103BC000C8B8FF00E895FF58C3908A86BB00E8ABF1 -:103BD000FFC3E8CBFFE8F2FFC390E8C3FFE8B4FF00 -:103BE000C39033C0E895FFC3B8FF0033C9E86CFF4A -:103BF000C390B8FF01B110E862FFC390C3FC3BE281 -:103C00003BF23BF23BFC3BE23BE83BE83BFC3BE26C -:103C10003BE83BE83BFC3BE23BE23BE23B00100085 -:103C20000000100000001000000010000000100054 -:103C3000000010000000100000001000000008004C -:103C40000000080000000800000008000051538B2D -:103C50004E3881E1FFEEA804740481C900018AE0B6 -:103C600080E4032418D0E40AC433DB8AD82E8B877F -:103C7000FD3B89467C2E0B8F1D3C894E38D1EB2EA7 -:103C80008AA73D3C5B59C3AC493C01721D74203C82 -:103C900003722374283C08722B74303C20723774F2 -:103CA0003ABBDA3B32E4895E7EC3BBA03BEBF5BB9B -:103CB000943BB401EBF0BBFC3BB402EBE9BBE23B51 -:103CC000B403EBE2BBBE3BB404EBDBBBCA3BAC4989 -:103CD0008886BB00EBCEBBD23BEBF3BBFC3BEBC41B -:103CE000A9040075D1A9080075DAEBD18B5E748B3D -:103CF0004E783BCB72028BCB3BC872028BC88BC118 -:103D0000E32CC47E108BDF26033D4747F7467A013C -:103D100000751CF7C70100740249A4D1E9F3A5732B -:103D200001A4260107294678014676294674C35026 -:103D300053BB7F7FF7C70100740549AC22C3AAD1EA -:103D4000E9E31D9CAD23C3AB497414AD23C3AB4958 -:103D5000740DAD23C3AB497406AD23C3ABE2E59D3F -:103D60007304AC22C3AB5B58EBB8E8CEC98B5E38AA -:103D7000F7C310047501C3F7C340007405E8B8E346 -:103D8000EB03E8A8E3816638EFFBF6C310743CF65A -:103D9000C3027406E4D80C01E6D8F6C30474118398 -:103DA000C2088A86A7000C01EE8886A70083EA086D -:103DB000F6C308740FE88BE3720A8A86C000E638FF -:103DC000B023E60AF7C300047501C3F7C300087502 -:103DD000F98A86A500F6C340750DA81075EC0C1085 -:103DE0008886A500E60CC3A80175DF83C2020C0516 -:103DF000EE8886A500C3B000E847D2EB0FB002E81A -:103E0000900EEB08836638DF834E7A0233C08ED87B -:103E1000FAA0921240A292123C05721EC60692129D -:103E200000FBB001E86B0EFAA1260123062A01A8C7 -:103E3000017507E8E207E8610990B000E837D2FBB6 -:103E400085ED74B9FAF7467A460075C08B46783D21 -:103E50000A0072B08B4E7483F950729A836638DF11 -:103E6000C576148B463A85C07558AD85C0750FE888 -:103E7000F8FEF7467A08007493E8A0FAEB8E3B76DA -:103E8000047621B90200394E2E7705C7462E000070 -:103E9000568B762C897604C7040000464689762C1A -:103EA000294E2E5E85C07917F6C4107405FF567C26 -:103EB000EB03FF567E897614B00CE885D1EB86893A -:103EC000463AFF96800029463A897614B00CE8718C -:103ED000D1E971FF0000000000000000080410029A -:103EE00001200000000000000000000000000000B1 -:103EF00000000000808080808080808080808080C2 -:103F000080808080808080808080808080808080B1 -:103F100080808080808080808080808080808080A1 -:103F20008080808080808080808080808080808091 -:103F30008080808080C0C0C0C0C0C0C0C0C0C0C0C1 -:103F4000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C080B1 -:103F500080808000808080808080808080808080E1 -:103F60008080808080808080808080808080808051 -:103F70008080808080808080808080808080808041 -:103F80008080808080808080808080808080808031 -:103F90008080808080808080808080808080808021 -:103FA0008080808080808080808080808080808011 -:103FB0008080808080808080808080808080808001 -:103FC00080808080808080808080808080808080F1 -:103FD000808080804E417841D041F44106421842B1 -:103FE000C3908E46028B7E22897E6C806627FD8B75 -:103FF000562483FA0472E983EA028BD93BCA76021B -:104000008BCAB00A57518BFEF2AE8BC1595F751E39 -:1040100050402BC874062BD12BD9F3A4594B4A4AD4 -:10402000B00DAAA43BCA76028BCAE313EBD42BD9FA -:10403000F7C601007402A449D1E9F3A57301A4896C -:104040007E222B7E6C297E24017E1A8BCB807E26DD -:10405000027405806626FDC360B0FDE8180361C3E5 -:10406000C390E87C0272F990834E26208B466A89C1 -:10407000466E8B46480D040025BFFF894648B006B2 -:10408000E8BFCFC3897E222B7E6C017E1A297E2455 -:10409000807E26027405836626FDC360B0FDE8D5E8 -:1040A0000261C3908ABEC200EB24F7464840007507 -:1040B000B18E46028B7E22897E6C8B562483EA0A5F -:1040C000789E03D7806627FD33C08ABEC200E3B462 -:1040D0003BFA77B0AC49932E8A87D43E9322DF75A2 -:1040E00017AAE3A03BFA779CAC49932E8A87D43E6B -:1040F0009322DF7503AAEBD6F6C37F7505FF4666EC -:10410000EBDFF6C340750C8BD883EB08D1E32EFFB1 -:10411000A7D43FFF46662C20EBC785C0742C894688 -:104120006A834E4840897E222B7E6C017E1A297E4E -:1041300024807E26027408836626FDE8A301C360FE -:10414000B0FDE8310261E89801C3E957FF908B5E4A -:10415000664B7803895E66AA8B5E64F7C3002075A0 -:1041600003E940FFF7C3400074088A86C100AAE94A -:1041700032FFB83200EBA3908B5E66895E6883C322 -:104180000880E3F8895E668B5E6481E3001881FB3A -:104190000018742DAA85DB7425F746644000751855 -:1041A00081FB0010740C8B46662B4668C1E004E965 -:1041B00068FFB86400E962FF8A86C100AAAAE9E341 -:1041C000FE518B4E662B4E68B020F3AA59E9D4FEFF -:1041D0008B5E66895E688B5E64F7C324007410C7CB -:1041E00046660000F7C304007405B00DAAB00AAA21 -:1041F000EB489090AAF7466400407406B8D007E9EF -:1042000018FFE99FFE90AAF7466400807406B8D0B4 -:1042100007E906FFE98DFE908B5E66895E6885DBA7 -:10422000750C8B5E64F7C310007406E976FE8B5E36 -:1042300064F7C308007427B00AAAF7C32000751FEB -:10424000F7C300017503E95BFEF7C340007506B8CC -:104250006400E9C5FE8A86C100AAAAE946FEAAC78B -:1042600046660000F7C3000674F1F7C340007419F6 -:104270008A86C10081E3000681FB00047206760293 -:10428000AAAAAAAAAAAAE91BFE81E3000681FB004A -:1042900004720E7606B89600E97FFEB86400E979EC -:1042A000FE8B4668E973FE90368B0EDA1283F93284 -:1042B000731D1E0633C08ED88EC08D764CBFDC12A7 -:1042C00003F9A5A5A583C106890EDA12071FC3B09D -:1042D00008E86ECDC390836648FEE893C4E8C8FF43 -:1042E000C3F6462702750F9CFA837E1A0074098074 -:1042F0004E27019DF9C3F8C35052F7463840007469 -:104300001DE834DE83C20AECA840752783EA088AD8 -:1043100086A5000C028886A500EE5A58EBD1E80C61 -:10432000DE8A86A50024FB0C028886A500E60C5ACE -:1043300058EBBC804E27025A589DF8C30846269C6D -:10434000FA8A8EA500F7463840007514F6C1067447 -:1043500023E8D9DD8AC124F98886A500E60C9DC32F -:10436000F6C102740FE8D0DD83C2028AC124FD8841 -:1043700086A500EE9DC38B5E2622C3884626740167 -:10438000C3806627FD9CFA8A8EA500F74638400058 -:104390007516F6C104750FE893DD8AC124FD0C047F -:1043A0008886A500E60C9DC3F6C10275F9E888DD94 -:1043B00083C20AECA820750E83EA088AC10C028821 -:1043C00086A500EE9DC383EA0A33C98A4E1C8B463C -:1043D0001A3BC8731B014E2A2BC189461A1EC5768B -:1043E00000F36E1F89760083C2028A86A500EBCD9A -:1043F00085C0741201462A8BC81EC57600F36E1F55 -:10440000897600894E1AF6C701752380CB02895E32 -:1044100026E808C383C2028A86A50024FDEE8886AA -:10442000A500F6C7107505B002E816CC9DC383C27F -:10443000028A86A500EB86908BD18B46243BC876FA -:10444000028BC82BD12BC18BD9E322806627FD8E2E -:1044500046028B7E22F7C601007402A449D1E9F31B -:10446000A57301A4897E22894624015E1A8BCA8025 -:104470007E26027405806626FDC360B0FDE8F6FE68 -:1044800061C350E40A84C0750A8686A10084C074A2 -:104490000AE60A580C20894648F9C35824DF8946A1 -:1044A00048F8C390FBB002E8E807FAE82E01FBB039 -:1044B00001E8DE07FAB002E8BCCBFB85ED74E5FA53 -:1044C0008E5E0AFB90FA8B46488B7640A88C75DE90 -:1044D000A820741A50E855DC58E8A6FF7310B00203 -:1044E000E85FCBEBC99025FF008BC8EB3690A801A5 -:1044F00075224683E6FE3B76087479AD8AFCB3F0FC -:1045000022FB3AFB74E03ABEA000742EE8D2FD73A1 -:1045100077EB9B908AE024FC8846488B4E4AF6C491 -:1045200002741DE8BBFD7286E813F3897640E393BD -:10453000834E4803894E4AE974FF25FF0F8BC890CC -:104540008B86980085C0741A518A8EA000C0E90439 -:10455000BA0100D3E25923C2740803F1897640E915 -:1045600061FFFF5662E3F5834E4801894E4A897622 -:1045700040E93AFF814E2600108B46503B46467775 -:1045800003E852FDE927FF9088BEA000EBAC0A06C5 -:1045900090128AE0BA0601B004EEEC84C07512B045 -:1045A00004EE8AC4EE32E4A8807406C706841200C2 -:1045B0000088269012C30A0690128AE0BA0601EC1F -:1045C000A80175EDBA08018AC4EE32E4A88074E14E -:1045D000C7068412000088269012C39036F706247E -:1045E0000101007530368B0EDA1280F936732633EE -:1045F000C08EC08ED8BFDC1203F9B008E877CA8538 -:10460000ED740E8D764CA5A5A580C10680F9367295 -:10461000E9890EDA12C3C390F7062601010075F688 -:104620008B0E201385C975EE33C08EC08ED8BF2483 -:1046300013B93600B00AE83DCA85ED7506E91201E6 -:10464000E90A0133DB8A464C8AA6B300FECC780E19 -:1046500088A6B3000ADCB40AAB83E90276E28AA634 -:10466000B200FECC780E88A6B2000ADCB408AB8398 -:10467000E90276CC8AA6B100FECC78188ABEB000DA -:10468000750488A6B00088A6B1000ADC8AE7AB836F -:10469000E90276AC8AA6B400FECC781F88A6B400E6 -:1046A0000ADCB40BAB8A86BC008AA6BD00AB8B8645 -:1046B000BE00AB83E90676888A464C8AA6B600FE21 -:1046C000CC781988A6B6000ADCB40CABE8DBCBAB1F -:1046D0008B462AAB83E90676748A464C8AA6B700D5 -:1046E000FECC781988A6B7000ADCB40DABE8BACBCB -:1046F000AB8B4634AB83E90676538A464C8AA6B820 -:1047000000FECC781988A6B8000ADCB40EABA15024 -:1047100012ABA15212AB83E90676328A464C8AA6C6 -:10472000B500FECC781888A6B5000ADCB40FAB8BB8 -:10473000869A00AB8B869C00AB83E906760F84DB00 -:104740007503E9EFFEB00AE8F8C8E9E7FEB00AE849 -:10475000F0C8F7D983C1368BC10D800086C4A3226F -:10476000134141890E2013C3A184122BC17211A3DE -:104770008412BE2213D1E9F36F90890E2013F8C37F -:10478000F9C3C381EF6A1374F98BC70D800086C427 -:10479000A368134747893E6613C3F7062A01010041 -:1047A00075E08B0E6613E30780F92077D54949330E -:1047B000C08EC08ED8BF6A138BF703F983C6343B13 -:1047C000FE77C0B00EE8AEC885ED74B78A464C8A55 -:1047D000B6B900FECE781588B6B9008AA6A90080C1 -:1047E000CCC0AB84F67405B00EE856C88AB6BA00E1 -:1047F000FECE78CB8A9EA9008ABEAB008A563F8A3D -:10480000F332F70AB6AC00C686AC000022F2744B55 -:10481000F6C608740FB402F6C3087502B403AB8081 -:10482000E6F77437F6C601740FB400F6C3017502DB -:10483000B401AB80E6FE7423F6C602740FB404F62E -:10484000C3027502B405AB80E6FD740FF6C60474AE -:104850000AB406F6C3047502B407ABC686BA0000F4 -:10486000889EAB00E958FF90A184122BC17211A35E -:104870008412BE6813D1E9F36F90890E6613F8C3F2 -:10488000F9C3A1841241412BC17223A384128BC1AD -:10489000484832E40C8086C4EF9090909090BEDC43 -:1048A000124949D1E9F36F90890EDA12F8C3F9C3BE -:1048B0008AC88A464CB40183EB06EF9090909090A2 -:1048C000B80100EF90909090908AC1EF90909090F6 -:1048D00090E99700E9AC0033C08ED8891E8412C3DA -:1048E000368B1E8412FB90FAB00CE889C785ED74F4 -:1048F000E6C5760C83FB1472DBFB90FAAD85C078BD -:10490000AF74E28BFE03F8368B0E86123BC1770242 -:104910008BC883EB043BD977028BCB33C08A464CE0 -:10492000EF90909090908BC1EF90909090904180FC -:10493000E1FE2BD951D1E9F36F90598BC74024FE8A -:104940003BC674272BFE4E4E538B5E103BF3721307 -:10495000031F83C30380E3FEC7070000836E740256 -:10496000895E105B893C89760CEB8989760C3976F7 -:104970001077817208833C007403E977FFE80DBE6D -:10498000E962FF36891E8412B00CE8B5C633C08ECA -:10499000D8C3A184123D10007277BA04013B068887 -:1049A000127506C7067E1200008B0EDA12E30BE8C2 -:1049B000D0FE7257C7067E12FF7F8B0E2013E30BCB -:1049C000E8A5FD7246C7067E12FF7F8B0E6613E3D5 -:1049D0000BE894FE7235C7067E12FF7FA12801A95D -:1049E00001007503E8F9FE803E8D1200751DA1845B -:1049F000123D200076153B0682127609A17E123BFD -:104A0000068012720C800E901280C3B080FF167C5C -:104A100012C3800E901240C36A001FC6069312177D -:104A20009C0EE8B7C86A001FC6069312209C0EE8C9 -:104A3000AAC86A001FC6069312169C0EE89DC8906D -:104A4000BA0601ECA82075CAFB90FABA0401ED90F1 -:104A5000909090903A06941277BE33DB8AD8D1E3D7 -:104A60002E8BAF4400C47E0885FF74B9F6C4C075B0 -:104A70005532C0C1E00280E4F08BF0ED9090909050 -:104A80009085C074BB8BC84180E1FE0BC68B5E5025 -:104A90004B4B2BD9789CAB8BC1404001464ED1E9A2 -:104AA000F36D90895E50897E088B462680E4EF89FD -:104AB0004626F6C401750CF746480C007505B00291 -:104AC000E87FC5E97AFF86C48BC883E13F4180E176 -:104AD000FEE30A3C807209243FB4F0EBB0E960FFCA -:104AE000253F0033FF8EC7BF96128BF7D1E9F36DD8 -:104AF000908BC8E848EDE947FF906A001FC606930F -:104B0000121B9C0EE8D5C790601E0633C08ED88E4F -:104B1000C0BA0601ECA80474E1B006EEECA28C1257 -:104B2000A8407411A18812A38412C6068D1200E851 -:104B300060FEA08C12A8807403E804FFB80080BA5D -:104B400022FFEF071F61CF906A001FC60693121B5A -:104B50009C0EE887C790601E0633C08ED88EC0BA00 -:104B60000601ECA80474E1BA0801ECA28C12A8407A -:104B70007411A18812A38412C6068D1200E812FED9 -:104B8000A08C12A8807403E8B6FEB80080BA22FF99 -:104B9000EF071F61CF90EE86E0EE86E0EC86E0EC5A -:104BA00086E080E1FEF36C9080E1FEF36E900500FC -:104BB0007547A84B05007548A84B0500A348A84BAE -:104BC00005003549A84B06009848964B0600BA48A0 -:104BD000964B0600C348964B0600CB48964B060002 -:104BE0002049964B06002849964B06004E4A9C4B9E -:104BF00006007B4A9C4B05009E4AA24B0500EC4AEE -:104C0000A24B00001E06833E4412007409A0060158 -:104C100024303C30741A8CC88ED88EC0BBAE4B8BFF -:104C20000FE30D8B7F028B7704F3A483C306EBEFB6 -:104C3000071FC39033C0A33E01B90C01BE40018BD6 -:104C4000FE81C6B40F89048BC62BF13BC777F6A350 -:104C50003C01C3901E0660368B2E3E018B5E003BEE -:104C6000EB742B8B7602891C89770236A13C018973 -:104C7000460036892E3C018BEBFF4E0674088B6E86 -:104C800000FF4E0675F836892E3E018B66046107DB -:104C90001FC31E0660368B2E3E0198894606896624 -:104CA000043B6E0074108B6E00FF4E0675F836895B -:104CB0002E3E018B660461071FC3C3901E06609CD5 -:104CC000FA33ED8EDD8B2E3C0185ED743D8B4E006D -:104CD000890E3C018BCC8DA60A01561E06608966A2 -:104CE00004C746080F1AC7460601008B1E3E018501 -:104CF000DB741D8BC58707894600895E028BD889C6 -:104D00006F028BE19D61071FF8C39D61071FF9C307 -:104D1000892E3E01896E00896E0287E19D8BE1EB51 -:104D2000E4000D0A5465726D696E616C73207375D1 -:104D300070706F727465643A0D0A312920414E53C8 -:104D40004920636F6D70617469626C650D0A322968 -:104D500020577973652033300D0A506C6561736597 -:104D60002073656C6563743A20000D0A636F646597 -:104D7000207365676D656E743D000D0A4D6F6E6939 -:104D8000746F722076322E350A0D0A3E000D0A50DD -:104D90006172646F6E3F000D0A4E6F206164647231 -:104DA00065737320737065636966696564000D0AD5 -:104DB0003A000D0A004C6F633D000D0A4641544114 -:104DC0004C204552524F523D000D0A4D6F6E697492 -:104DD0006F7220636F6D6D616E64733A2D0D0A20E2 -:104DE0002020442C645B5B787878783A5D7878781A -:104DF000785D202D2064756D70206D656D6F727902 -:104E00000D0A2020204C2C6C5B5B787878783A5D1A -:104E1000787878785D202D2064756D702073696EC8 -:104E2000676C65206C696E650D0A202020452C6535 -:104E30005B5B787878783A5D787878785D202D209B -:104E400065646974206D656D6F72790D0A2020208C -:104E5000462C665B5B78787878205D787878785D2A -:104E6000202D2066696C6C206D656D6F72792070E5 -:104E70006172616772617068730D0A202020495B5E -:104E8000787878785D202020202020202020202D78 -:104E900020776F726420696E7075742066726F6D12 -:104EA00020706F72740D0A202020695B7878787802 -:104EB0005D202020202020202020202D20627974B9 -:104EC0006520696E7075742066726F6D20706F72E8 -:104ED000740D0A2020204F78787878207878202068 -:104EE000202020202020202D206F757470757420C4 -:104EF000776F726420746F20706F72740D0A2020B7 -:104F0000206F787878782078782020202020202042 -:104F100020202D206F75747075742062797465205F -:104F2000746F20706F72740D0A202020475B5B78CD -:104F30007878783A5D787878785D2020202D206721 -:104F40006F746F20616464726573730D0A20202092 -:104F5000575B5B787878783A5D787878785D202050 -:104F6000202D207761746368206120776F72640D53 -:104F70000A20202043202020202020202020202024 -:104F800020202020202D20696E7465727275707447 -:104F900073206F66660D0A202020532020202020D9 -:104FA00020202020202020202020202D20696E7409 -:104FB00065727275707473206F6E0D0A20202073F5 -:104FC00020202020202020202020202020202020E1 -:104FD0002D2073696E676C6520737465700D0A20EF -:104FE000202042787878782020202020202020203F -:104FF0002020202D20627265616B706F696E7420B5 -:105000007365740D0A20202062202020202020209B -:105010002020202020202020202D20627265616B1E -:10502000706F696E7420636C6561720D0A202020B8 -:10503000522020202020202020202020202020203E -:10504000202D207265737461727420627265616BC9 -:10505000706F696E740D0A2020207220202020209D -:1050600020202020202020202020202D2072656755 -:105070006973746572732061742062726B70740D51 -:105080000A202020582C78206E202020202020204C -:1050900020202020202D206578616D696E652063B9 -:1050A00068616E6E656C206E0D0A202020482C3FD2 -:1050B00020202020202020202020202020202D20E3 -:1050C00074686973206D657373616765001B5B327B -:1050D0004A1B5B313B3148414E5349205465726D48 -:1050E000696E616C0D0A0A001B5B4B001B5B4A007A -:1050F0001B5B324A1B5B313B3148001B5B44201B6E -:105100005B44001B5B313B373248001B5B003B00BC -:1051100048001B5B73001B5B75001B7A2B0B7F1B0E -:105120007A2E0C7F1B7A2D087F1B7A2C0A7F1B7A24 -:1051300022087F1A57797365203330205465726DC9 -:10514000696E616C0D0A001B54001B59001A001E89 -:105150000008200800001B3D0000001B46000D0059 -:105160003F4464456546664767486849694F6F43F1 -:1051700063537342625272577758784C6C3C60D4D8 -:1051800057D45750585058D659D659B459B4593C99 -:10519000603C606C57485726570657905790579871 -:1051A00057485F0C5F585F335F405FA057A057FEC2 -:1051B00059FE59DC57DC5788619861C061CC61D8D1 -:1051C00061F66102622262F8564A625862605920B2 -:1051D00020666C6167733D00202061783D002020CF -:1051E00062783D00202063783D00202064783D00F7 -:1051F000202063733D00202064733D0020206573F0 -:105200003D00202073733D00202064693D00202074 -:1052100073693D00202062703D00202073703D00C6 -:10522000202069703D00206368616E656C3D002040 -:105230002020207365673D002074695F7374723DA0 -:10524000002074695F746F733D002074695F6D6145 -:10525000783D002074695F6261733D002074695F6E -:1052600073697A3D002074695F7374663D00207431 -:10527000695F726F6F3D002074695F666C673D0007 -:105280002074695F746F743D002072695F70636E93 -:105290003D002072695F7374723D002072695F7314 -:1052A00074663D002072695F726F6F3D0020726905 -:1052B0005F6261733D002072695F73697A3D00200F -:1052C00072695F746F743D002072695F6D696E3D35 -:1052D000002072695F666C673D002072695F746FC1 -:1052E000733D002072695F7468723D002074685FCE -:1052F0007374663D002074685F7374723D0020749F -:10530000685F6261733D002074685F73697A3D0075 -:105310002074685F7472673D002074685F666C6714 -:105320003D002074685F636E743D002072685F7397 -:1053300074723D002072685F7374663D002072686D -:105340005F6261733D002072685F73697A3D00207F -:1053500072685F7370613D002072685F61736F3DBA -:10536000002072685F726F6F3D002072685F666C2C -:10537000673D00206D5F636172653D002070745F62 -:10538000666C6F3D002061735F666C6F3D0020723C -:105390006D5F666C6F3D00202020715F696E3D007F -:1053A0002020715F6F75743D0020715F6472616EC3 -:1053B0003D002020715F74696D3D00202020715FE9 -:1053C00066633D0020715F737461743D0020715FFE -:1053D000646174613D0020715F6D6F646D3D0020FC -:1053E00068616E645F6F3D002068616E645F623D5E -:1053F000002068616E645F653D002068616E645FD7 -:10540000693D0020206F706F73743D002020746927 -:105410006D656F3D0020637573746D313D002063D1 -:105420007573746D323D0020637573746D643D0057 -:10543000207478726174653D0020727872617465C1 -:105440003D002020635F6D61703D0020635F6164FB -:1054500064723D0020635F616973723D0020635F89 -:10546000787461673D0020635F646566723D00206B -:10547000635F666C73683D002074786D6178733D7E -:10548000002072695F656D733D002020635F6C735F -:10549000723D002020635F6965723D002020635FDC -:1054A0006663723D002020635F6D63723D002020C3 -:1054B000635F6C63723D002020635F6473733D0023 -:1054C00020635F647373693D0020635F647373726C -:1054D0003D002020635F6973723D002020635F639D -:1054E00061723D002020635F6566723D0020635F4E -:1054F000657273743D0020635F65636E743D0020C8 -:10550000635F62726B633D0020635F626F6B633D3C -:105510000020635F7265706C3D0020635F6363739E -:10552000723D0020635F737474313D0020635F73CC -:105530007474323D002BC08ED88EC0E8C200E8E5FE -:1055400000FABF8400C705DC568C4D02BF0C00C7B3 -:10555000056E5E8C4D02BF0400C705BA5E8C4D021D -:10556000E8F10090E84901E81600F490E8E500BE93 -:10557000BA4DE8090CA09312E85D0CE8C209EBE40F -:10558000E8D50CE8C40C0AC074F68B1EF8793C0D03 -:10559000742E3C0874173C7F741383FB207FE188D2 -:1055A00087D67943891EF879E8770CEBD30BDB7447 -:1055B000CF4B891EF8798B36167AE8C10BEBC19078 -:1055C000E80200EBBBC687D679000BDB741EA0D6C1 -:1055D00079BF6051B91D008BD9060E07F2AE077571 -:1055E00017412BD9D1E32EFF977D519033C0A3F8FB -:1055F00079BE894DE8870BC3BE8D4DE8800BEBEC7F -:10560000BA0002B093EEB055EEBA1002B093EEB00D -:10561000AAEEBA0002EC3C557508BA1002EC3CAA9E -:105620007403E82FF6C3BA0402B01AEEB020EEB04D -:1056300030EEB040EEB080EEBA0002B013EEB0072C -:10564000EEBA0802B080EEBA0202B0BBEEBA0402B3 -:10565000B005EEC3C606CA1301C706F8790000C636 -:1056600006F67901C706D0790000C706D279000096 -:10567000C706D4790000C706FA790000C706FC798E -:105680000000C706FE790000C706007A0000C706C2 -:10569000027ACE598C0E047AC706067A0000C70635 -:1056A000277A0000C606297A00C6062A7A00C39027 -:1056B000BE224DE8C80AE83F002C313C0177F7E8EC -:1056C00081098B360C7AE8B50ABE6A4DE8AF0A0E3E -:1056D00058E8F80ABE7A4DE8A40AC39060D1E38383 -:1056E000FB1873111EBA00008EDA2EFF97B7518B8C -:1056F000EC8946101F61CF90E84F0B0AC07505E892 -:10570000560BEBF4C390833EF879017416BED7793B -:10571000E8310A8BD0AC3C2C74043C207505E8239E -:105720000AEEC3E9D2FE833EF8790174F6BED7795A -:10573000E8110A8BD0AC3C2C74083C207404E9B707 -:10574000FE90E8FF09EFC3908B16067A833EF87946 -:1057500001740BBED779E8EB098BD0A3067AB02091 -:10576000E8570B8B16067AECE86F0BC38B16067A9C -:10577000833EF87901740BBED779E8C7098BD0A3B3 -:10578000067AB020E8330B8B16067AEDE8670BC378 -:10579000FAC606F67900C390C606F67901FBC390F7 -:1057A00006E85809B020E8110B268B05E8470BB036 -:1057B00008E8060BE8030BE8000BE8FD0AB8010057 -:1057C000E8CFF4BA0202EC24017502EBDCBA06025F -:1057D000EC07C390C706087A1000EB06C706087AE4 -:1057E0000100068E06FC798B3EFA79E80E09E80B7B -:1057F00000893EFA798C06FC7907C390BEB24DE869 -:105800007C098B16087A52E82A09E80F0AE80C0A84 -:1058100033DBB9100090268A01E8BC09E8FD094392 -:10582000E2F4E8F709E8F40933DBB9100090268ABE -:10583000013C2072053C7E760390B02EE8E30943DC -:10584000E2ECBEB24DE8360983C7105A4A75B7C3B9 -:10585000068E06007A8B3EFE79E8A008893EFE7926 -:105860008C06007A578B360E7AE81209C706087A3A -:105870001000BA0002E8E800E881FF5FBA0000E823 -:10588000DE00BEB54DE8F6088CC0E83F09B03AE846 -:1058900090098BC7E83509E87E08E8C30090E8B7AF -:1058A00009E8A6090AC074F63C0B750683EF10EBF5 -:1058B00019903C0A750683C710EB0F903C0C7504D9 -:1058C00047EB07903C0875244F908B36FE798BC7C9 -:1058D0002BC63D000172A53D1001720483EE20909D -:1058E00083C6108936FE79578BFEEB803C2E7508F7 -:1058F000BA0113E86A0007C3C6060A7A0232C990E1 -:105900003C30724C3C39760C245F3C4172423C4640 -:10591000773E2C072C3050E8CC085802C8FE0E0AFF -:105920007A740FC0E104E82F09E81E090AC074F672 -:10593000EBCE26880DE8E0078AD0E823008AC13C38 -:105940002072053C7E760390B02EE8D508E970FF02 -:10595000E8C507E80A00268A05E87C08E91DFF90EB -:10596000F606267A02750286F2528B361A7AE80D0E -:10597000085A528AC60206247AF606267A01750665 -:10598000E89F08EB0D9032E4E80D088B361C7AE8AE -:10599000EC075A8AC20206257AF606267A017506AF -:1059A000E87F08EB069032E4E8ED078B361E7AE8D4 -:1059B000CC07C390068E06047A8B3E027AE83C0739 -:1059C000893E027A8C06047A07FF1E027AC3BE97CC -:1059D0004DE8AA07CB900657BED779E866078BD863 -:1059E000E861078BC82BCB78118EC3BF0000B8FFCE -:1059F000FF51B90800F3AB59E2F75F07C39006BE49 -:105A0000D779E83F078BD8D1E32E8B9F4400BE2681 -:105A100052E8F1088BC3E8DD08B80100E873F2E84A -:105A2000E008BE2F52E8DD088B4718E8C808BE77AB -:105A300052E8D1088B4726E8BC08BE5352E8C50897 -:105A40008B471EE8B008BE5C52E8B9088B4720E8D7 -:105A5000A408BE6E52E8AD088B4724E89808BE80C3 -:105A600052E8A1088B472AE88C08E89508BE38520E -:105A7000E892088B07E87E08BE4152E887088B470A -:105A80001AE87208BE4A52E87B088B471CE8660891 -:105A9000BE6552E86F088B4722E85A08E86308BEE3 -:105AA000D152E860088B4738E84B08BEAD52E85445 -:105AB000088B4730E83F08BEB652E848088B4732AB -:105AC000E83308BEA452E83C088B472EE82708BEFE -:105AD000BF52E830088B4734E81B08E82408BE8929 -:105AE00052E821088B4704E80C08BE9252E81508DA -:105AF0008B4714E80008BE9B52E809088B472CE846 -:105B0000F407BEC852E8FD078B4736E8E807BEDA5F -:105B100052E8F1078B473AE8DC07BEE352E8E507B5 -:105B20008B473CE8D007E8D907BE1953E8D6078B66 -:105B30004748E8C107BEFE52E8CA078B4742E8B5AE -:105B400007BE0753E8BE078B4744E8A907BE7C534E -:105B5000E8B2078B474CE89D07BE8553E8A6078B44 -:105B6000474EE89107BE8E53E89A078B4750E88569 -:105B700007E88E07BE2253E88B078B474AE8760773 -:105B8000BEEC52E87F078B4708E86A07BEF552E88B -:105B900073078B4740E85E07BE1053E867078B47E3 -:105BA00046E85207E85B07BE6A53E858078B477A16 -:105BB000E84307BE3D53E84C078B4770E83707BE04 -:105BC0004653E840078B4772E82B07BE4F53E83433 -:105BD000078B4774E81F07E82807BE2B53E8250703 -:105BE0008B470CE81007BE3453E819078B4710E8C1 -:105BF0000407BE5853E80D078B4776E8F806BE61E8 -:105C000053E801078B4778E8EC06BE7353E8F506C6 -:105C10008B473EE8E006E8E906BE9753E8E6068BC8 -:105C20004752E8D106BEA053E8DA068B4754E8C5D0 -:105C300006BEA953E8CE068B4756E8B906BEB25356 -:105C4000E8C2068B4758E8AD06BEBB53E8B6068BE4 -:105C5000475AE8A106BEC453E8AA068B475CE895FC -:105C600006E89E06BECD53E89B068B475EE8860697 -:105C7000BED653E88F068B4760E87A06BEDF53E84E -:105C800083068B4762E86E06BEE853E877068B47CB -:105C90007CE86206BEF153E86B068B477EE8560649 -:105CA000BEFA53E85F068B878000E84906E8520693 -:105CB000BE4254E84F068B879E00E83906BE035467 -:105CC000E842068B4764E82D06BE0C54E836068B86 -:105CD000476EE82106BE1554E82A068B878E00E839 -:105CE0001406BE1E54E81D068B879000E80706BE0A -:105CF0002754E810068B879200E8FA05E80306BEF1 -:105D00003054E800068B879400E8EA05BE3954E871 -:105D1000F3058B879600E8DD05BE6F54E8E6058B3A -:105D2000879800E8D005BE5D54E8D9058A87A000B1 -:105D3000E8A705BE5454E8CC058A4728E89B05BE71 -:105D40006654E8C0058A87A100E88E05E8B305BE61 -:105D50007854E8B0058A87A200E87E05BE8154E841 -:105D6000A3058A87A300E87105BE8A54E896058AD0 -:105D700087A400E86405BE9354E889058A87A500D6 -:105D8000E85705BE9C54E87C058A87A600E84A05CA -:105D9000BEA554E86F058A87A700E83D05BEAE544E -:105DA000E862058A87A800E83005E85505BEB754C3 -:105DB000E852058A87A900E82005BEC054E84505D9 -:105DC0008A87AA00E81305BEC954E838058A87AB5C -:105DD00000E80605BED254E82B058A87AD00E8F935 -:105DE00004BEDB54E81E058A87AE00E8EC04BEE47E -:105DF00054E811058A87AF00E8DF04BEED54E804DB -:105E0000058A87B000E8D204E8F704BEF654E8F447 -:105E1000048A87B100E8C204BEFF54E8E7048A8719 -:105E2000B200E8B504BE0855E8DA048A87B300E892 -:105E3000A804BE1155E8CD048A87BB00E89B04E89E -:105E4000C004BE1A55E8BD048A87BC00E88B04BEB6 -:105E50002355E8B0048A87BE00E87E04BE2C55E8CE -:105E6000A3048A87BF00E87104E8960407C36006AC -:105E70001E168BECFF4E16F7461A00027401FBB893 -:105E800000008ED88EC0892E2D7AE8CB0081661A4C -:105E9000FFFEC6062A7A00E8D800B8005FA32B7A76 -:105EA000E85D00803E2A7A00740A814E1A0001C61D -:105EB000062A7A00171F0761CF9060061E168BEC2A -:105EC000F7461A00027401FBB800008ED88EC08914 -:105ED0002E2D7A81661AFFFEC6062A7A00E8920005 -:105EE000B8005FA32B7AE81700803E2A7A00740A74 -:105EF000814E1A0001C6062A7A00171F0761CF904B -:105F0000B8F000E88CEDFF262B7AC390065356803C -:105F10003E297A007403E83F00BED779E825028B5A -:105F2000D8A3277A2E8A07A2297AB0CC2E88075EBA -:105F30005B07C3C6062A7A00B80A5FA32B7AC39010 -:105F40008B2E2D7AE82B00C3C6062A7A01E80800BA -:105F5000B80A5FA32B7AC39057803E297A00740F4A -:105F60008B3E277AA0297A2E8805C606297A005FFB -:105F7000C390BEB24DE80602BED851E80002FF76DB -:105F80001458E84702BEDE51E8F301FF760E58E8E8 -:105F90003A02BEE451E8E601FF761258E82D02BE4F -:105FA000EA51E8D901FF761058E82002BE1452E801 -:105FB000CC01FF760A58E81302BE1A52E8BF01FF6F -:105FC000760C58E80602BECF51E8B201FF761A58A7 -:105FD000E8F901BEB24DE8A501BEF051E89F01FF0E -:105FE000761858E8E601BEF651E89201FF760258AD -:105FF000E8D901BEFC51E88501FF760458E8CC01E0 -:10600000BE0252E87801FF760058E8BF01BE085290 -:10601000E86B01FF760658E8B201BE0E52E85E0159 -:10602000FF760858E8A501BE2052E85101FF761618 -:1060300058E89801BE894DE84401C390BEC94DE8B7 -:106040003C01C33C0074053C017459C3C7060C7A7B -:10605000CD50C7060E7AF050C706107AE850C70632 -:10606000127AEC50C706147AF450C706167AFB5021 -:10607000C706187A0351C7061A7A0B51C7061C7A4D -:106080000E51C7061E7A1051C706207A1251C70654 -:10609000227A1651C606247A01C606257A01C6065A -:1060A000267A03C3C7060C7A1A51C7060E7A4D51D9 -:1060B000C706107A4751C706127A4A51C706147AA2 -:1060C0004F51C706167A5151C706187A5551C7065F -:1060D0001A7A5651C7061C7A5951C7061E7A5A5168 -:1060E000C706207A5B51C706227A5E51C606247A1B -:1060F00020C606257A20C606267A02C3A1F879486A -:106100007414BED779E83C008BF8AC3C3A75078E26 -:10611000C7E830008BF8C3908BC72B06FE798AF056 -:10612000240F8AD002D002D080C20BC0EE0480C6F9 -:1061300003043DC38CC0E89300B03AE8E4008BC789 -:10614000E88900C35133C990AC3C2074FB900AC06D -:1061500074262C3072223C0976143C11721A2C07DA -:106160003C0F760A3C2A72102C203C0F770A98C10B -:10617000E10403C8ACEBD7904E8BC159C390068C99 -:10618000C88EC0E8020007C3268A04460AC0740607 -:10619000E88F00EBF390C3900BC0747A5133D2B9FF -:1061A000E803F7F18BCAE803008BC159BA6400F623 -:1061B000F2E80C008AC498B20AF6F2E802008AC437 -:1061C000500AF074050430E8580058C386C4E80744 -:1061D0000086C4E80200C390C1C804E80800C1C03A -:1061E00004E80200C3905350240FBBCA622ED7E8C4 -:1061F0003000585BC39086C4E8070086C4E80200FC -:10620000C39050B908008AE032C0D1C00430E81110 -:1062100000E2F558C390B030E80700C3B020E801B1 -:1062200000C3568B36D0798884D0774681E6FF014B -:10623000FF06D4798936D079813ED479FE0175087C -:1062400056E814005EEBF1905EC3BA0202EC240142 -:106250007404BA0602ECC390803EF67900740960BB -:10626000B80100E82CEA6190BA0202ECA804742894 -:106270008B36D279833ED47900741D8A84D07746D8 -:1062800081E6FF018936D279FF0ED479BA0602EE93 -:10629000BA0202ECA80475DCA1D479C352BA060292 -:1062A000EE5AC3905250BA0202ECA8047408585A2D -:1062B000E8E9FFF9C390585AF8C35250BA0202EC09 -:1062C000A80474FB585AE8D3FFC330313233343555 -:1062D0003637383941424344454653508AE080E4DA -:1062E0000FBBCA62C0E8042ED7E8CEFF8AC42ED7FF -:1062F000E8C7FF585BC386E0E8DFFF86E0E8DAFF27 -:10630000C390BEB24D502EAC3C007405E8ABFFEB21 -:10631000F558C390C808000056578B7604BF040098 -:10632000C746FC0000C746FA0000C746F8000083D5 -:106330007E0600750E56E8B60E590BC075058BC764 -:10634000E95B018B46FC8946FE0BFF7505B8010031 -:10635000EB0233C05056E8A40D5959B4008946FCED -:106360008B5EFC83FB087603E92B01D1E32EFFA7AC -:10637000B264B80300E92601837EFA007414C746AC -:10638000FA00008A445898508A44599850E8C20F3D -:106390005959837EF800740AC746F8000056E89BF6 -:1063A0000859837E060075058BC7E9F10083FF0459 -:1063B0007503E9E6008BC7E9E400837EFE00750300 -:1063C000BF0200E9D500837EFE007503BF0100E92E -:1063D000C9008B5EFE83FB077603E98600D1E32EBE -:1063E000FFA7A26433FFE97F00BF0400807C580F41 -:1063F0007422837EF800751CFE44586A0856E87EB5 -:106400000C59598A445804805056E8720C5959C79F -:1064100046FA0100837EF800740AC746F800005669 -:10642000E8190859EB42BF0400807C5800742283AD -:106430007EF800751CFE4C586A0856E8410C595904 -:106440008A445804805056E8350C5959C746FA0119 -:1064500000837EF800740AC746F8000056E8DC079F -:1064600059EB05BF0400EB00EB31BF0400EB2CC778 -:1064700046F801006A0856E8050C5959807C58090D -:106480007D04B00FEB02B00004805056E8F00B59C9 -:1064900059BF0400EB05BF0400EB00E9A5FE5F5EF9 -:1064A000C9C3E4636364636463646364E963266427 -:1064B00051647863BA63C6639664D2636A646A643B -:1064C0006F647263C808000056578B76048B7E0891 -:1064D0006A0156E8A90B59598A4606C0E0060480AD -:1064E0005056E89A0B5959C746FE0000897EF8EBD2 -:1064F00003FF46FE8B5EF8FF46F8803F0075F2838F -:106500007EFE107D25B810002B46FED1F88946FC92 -:10651000C746FA0000EB0B6A2056E8620B5959FF98 -:1065200046FA8B46FA3B46FC7CEDEB0C8BDF478A48 -:10653000075056E8490B5959803D0075EF6A0256DD -:10654000E83C0B5959EB005F5EC9C3C80400005614 -:10655000578B7E04C746FE0000BE1400E909018B7C -:106560005EFE83C3042BDF8A87AC0B88445AC64483 -:1065700058088A46FE884459C744060000C6441994 -:1065800000C6441A00C6441B00C6441D0DC6441E66 -:1065900003C6441F00C6442000C6442100C6445B15 -:1065A00000C6445D00C6445E00C6445F00C6446049 -:1065B00000C746FC0000EB0D8B5EFCD1E3C740300A -:1065C0000000FF46FC837EFC107CEDC746FC00000B -:1065D000EB0A8B5EFCC6405000FF46FC837EFC0449 -:1065E0007CF0C744540000C7445600008A445A98BF -:1065F000BAF80023D0B805000BC28946FC9CFA8A81 -:1066000046FCBAFE00EEBA0000EC9D24088846FC69 -:10661000837EFC007502EB4AFF76FEE87A0C59682F -:10662000350256E8320A59590BC07534683802569B -:10663000E8250A59590BC0752768420256E8180A1E -:1066400059590BC0751A684C0256E80B0A59590B78 -:10665000C0750D68560256E8FE0959590BC0740200 -:10666000EB00FF46FE83C662397EFE7D03E9EFFE46 -:10667000EB005F5EC9C3C808000056578B4604BADA -:106680006200F7EA0514008BF0837E06007405B8FB -:106690001000EB03B808008944048A460888445C6B -:1066A00056E85904598BF88BC78944568944548A53 -:1066B000445D88442F0BFF751D68C20F6A0156E8C0 -:1066C00002FE83C406EB006A0156E847FC59590BE9 -:1066D000C075F4BF0100897EFAB90500BBE96A2ED6 -:1066E0008B073B46FA74074343E2F4E9A4032EFF09 -:1066F000670AC744060200C74408F4088B5E04D149 -:10670000E38B87FC0889440A33C08BF8894454E939 -:10671000800356E8BB0559BF01008A445D88446088 -:10672000E96F03837C04087530807C5C0175158AF1 -:10673000445DB400D1E08BD8FFB7E40856E8F70811 -:106740005959EB138A445DB400D1E08BD8FFB7C42C -:106750000856E8E2085959EB2E807C5C0175158AD1 -:10676000445DB400D1E08BD8FFB7D40856E8C70821 -:106770005959EB138A445DB400D1E08BD8FFB7B40C -:106780000856E8B20859596A0156E887FB59598BEF -:10679000D883FB03772AD1E32EFFA7E16ABF01006C -:1067A0008A445D88445EEB188A445D04FF240788B0 -:1067B000445DEB0C8A445DFEC0240788445DEB0019 -:1067C000E9CF028A445DB400D1E08BD8FFB7FD0267 -:1067D00056E863085959681D0356E85A0859596A1A -:1067E0000156E82FFB59598BD883FB037736D1E349 -:1067F0002EFFA7D96ABF01008A445D88445FEB245D -:106800008A445D04FF8A540480C2FF22C288445D2A -:10681000EB128A445DFEC08A540480C2FF22C28803 -:10682000445DEB00E96B028B5C0683C3FED1E38B16 -:10683000400889048B1CFF77066A0056E885FC83B4 -:10684000C4068B5C064BD1E38B40088944028B5C09 -:1068500002FF77066A0156E86AFC83C4066A01569D -:10686000E8B1FA59598BD883FB037603E91F02D1AB -:10687000E32EFFA7D16A8B5C028B47048944028B0D -:106880005C02803F44750D8B5C028A4701B4003B7B -:1068900044047DE28B4604D1E08B1C03D88B440278 -:1068A0008947088B5C064BD1E38B4402894008E999 -:1068B000DE018B5C028B47028944028B5C02803FC5 -:1068C00044750D8B5C028A4701B4003B44047DE2B1 -:1068D0008B4604D1E08B1C03D88B44028947088B7C -:1068E0005C064BD1E38B4402894008E9A201BF0159 -:1068F00000E99C018B5C028A07B4008946F8B90C58 -:1069000000BBA16A2E8B073B46F874074343E2F4B1 -:10691000E977012EFF67188B4604D1E08B5C0203F8 -:10692000D88B47088B5C06FF4406D1E38940088B6F -:106930001C807F010074128B5C028A47018B1C8AC9 -:106940005701B6008BDA884018E94001FF4C06E990 -:106950003A018B5C028A47018B1C8A5701B6008B77 -:10696000DA884018E925018B5C028A47018B1C8A72 -:106970005701B6008BDA884018FF4C06E90D018BF1 -:106980005C028A47018B1C8A5701B6008BDA3040C3 -:1069900018E9F800B8F0108BF88944548A445F88ED -:1069A000445DE9E7008A441C983D020074073D03FA -:1069B000007402EB07C746FE0000EB2B8A441C98CC -:1069C000D1E08BD8FFB7690256E86B0659596A01C6 -:1069D00056E840F959598946FE837EFE00740683C5 -:1069E0007EFE0375E9EB00837EFE0374628A441C1D -:1069F00098D1E08BD8FFB76D0256E83A0659595640 -:106A0000E84D97598946FC8B5EFC83EBFE83FB03C4 -:106A10007733D1E32EFFA7996A68AC0256E81706D0 -:106A20005959EB23688F0256E80C065959EB186840 -:106A3000750256E801065959EB0D68C60256E8F68C -:106A4000055959EB02EB006A0156E8C7F85959BFDE -:106A50000100EB3868DD0256E8DC0559596A015639 -:106A6000E8B1F85959BF0100EB22B8D0308BF88952 -:106A700044548A446088445DEB12B8E0208BF88966 -:106A800044548A445E88445DEB02EB00EB02EB0069 -:106A9000EB00E941FC5F5EC9C3196A246A2F6A3AB8 -:106AA0006A0000010002000400410042004300446B -:106AB00000800081008200FF001769546A7A6AA58D -:106AC00069526994696A6A676952697F6967694C42 -:106AD00069F4687668B268EE68F56700681268F570 -:106AE000679D67A867B4679D6700000100F010E02C -:106AF00020D0302768F266C36723671267C8040096 -:106B00000056578B76048A4459988946FC6A098B4B -:106B100046FC05840150E8930859598BF88BC7252A -:106B200000F03D001075558BC725F0003DF0007555 -:106B30004B8BC725000FC1F8088946FE8B44043BE8 -:106B400046FE7D0533C0E9EF008BC7250F00BA0F65 -:106B5000002BD03B56FE740533C0E9DB00C744026E -:106B600004098A46FE88445F88445D8B5EFCD1E35D -:106B7000C787FC080409B8F010E9BC008BC72500E2 -:106B8000F03D002075528BC725F0003DE0007548B0 -:106B90008BC725000FC1F8088946FE837EFE087E5C -:106BA0000533C0E992008BC7250F00BA0F002BD028 -:106BB0003B56FE740533C0EB7F90C744020C098A34 -:106BC00046FE88445E88445D8B5EFCD1E3C787FC4B -:106BD000080C09B8E020EB608BC72500F03D0030C1 -:106BE00075528BC725F0003DD00075488BC7250036 -:106BF0000FC1F8088946FE8B44043B46FE7D0433F2 -:106C0000C0EB358BC7250F00BA0F002BD03B56FECB -:106C1000740433C0EB22C7440214098A46FE884438 -:106C20006088445D8B5EFCD1E3C787FC081409B81B -:106C3000D030EB0433C0EB005F5EC9C3C806000070 -:106C4000568B76046A0856E8350459598A44580424 -:106C5000805056E8290459598B44543B4456750AD0 -:106C60008A445D3A442F7502EB648B445489445640 -:106C70008B5C028A470188442F8A445DB400C1E0DE -:106C8000088B54540BD08A445DB400BB0F002BD842 -:106C90000BD38956FE6A108A445998050400990559 -:106CA000400183D2005250E8540883C4068956FC40 -:106CB0008946FA8B46FE0946FA834EFC006A19FFA4 -:106CC00076FCFF76FAE8730783C406E8FE075EC920 -:106CD000C3C81C000056578B5E048A4759988BF036 -:106CE0008B5E048A475DB4008946E6837EE6007DBC -:106CF0000A8B5E048B4704488946E68B5E048B470B -:106D0000043B46E67F05C746E600008B5E048A46E4 -:106D1000E688475D8BDED1E38B9F5902C647022090 -:106D20008BDED1E38B9F5902C64703308BDED1E364 -:106D30008B9F6102C64702208BDED1E38B9F6102ED -:106D4000C64703308B46E68946FA837EFA007418FC -:106D50008B46FABB0A0033D2F7F380C2308BDED108 -:106D6000E38B9F5902885703BB0A008B46FA33D244 -:106D7000F7F38946FA837EFA0074188B46FABB0A49 -:106D80000033D2F7F380C2308BDED1E38B9F590200 -:106D90008857028B46E68946FA837EFA0074188B80 -:106DA00046FABB0A0033D2F7F380C2308BDED1E360 -:106DB0008B9F6102885703BB0A008B46FA33D2F7D8 -:106DC000F38946FA837EFA0074188B46FABB0A00F0 -:106DD00033D2F7F380C2308BDED1E38B9F61028820 -:106DE00057028B5EE6D1E3FFB712026A00FF76041A -:106DF000E8D1F683C40668D30F6A01FF7604E8C3BE -:106E0000F683C406FF76E656E8019359598956F28F -:106E10008946F0FF76E656E8149359598956EE896B -:106E200046EC9CFAC45EF0268B078946EAC45EEC09 -:106E3000268B078946E8BA50FFED8946FE9DC74676 -:106E4000E40100E8EEA0BA50FFED8946FC8B46FC59 -:106E50002B46FE3DE8037303E980019CFABA50FF1C -:106E6000ED8946FC8B46FC2B46FE8946F8C45EF055 -:106E7000268B072B46EA8946F6C45EF0268B0789E7 -:106E800046EAC45EEC268B072B46E88946F4C45ECE -:106E9000EC268B078946E8BA50FFED8946FE9D81B6 -:106EA0007EF8E803761CFF76F8FF76F6E87601595F -:106EB000598946F6FF76F8FF76F4E8680159598952 -:106EC00046F4BF0E00EB178BDED1E38B9F5902C651 -:106ED00001208BDED1E38B9F6102C601204783FF37 -:106EE0001176E48BDED1E38B9F5902C6470D308BC0 -:106EF000DED1E38B9F6102C6470D30837EF60977B2 -:106F000005B80D00EB26837EF6637705B80E00EB1F -:106F10001B817EF6E7037705B80F00EB0F817EF645 -:106F20000F277705B81000EB03B811008BF8EB259D -:106F30008B46F6BB0A0033D2F7F380C2308BDED12A -:106F4000E38B9F590288114FBB0A008B46F633D260 -:106F5000F7F38946F6837EF60075D5837EF40977CC -:106F600005B80D00EB26837EF4637705B80E00EBC1 -:106F70001B817EF4E7037705B80F00EB0F817EF4E9 -:106F80000F277705B81000EB03B811008BF8EB253D -:106F90008B46F4BB0A0033D2F7F380C2308BDED1CC -:106FA000E38B9F610288114FBB0A008B46F433D2FA -:106FB000F7F38946F4837EF40075D58BDED1E3FFC9 -:106FC000B75902FF7604E86E0059598BDED1E3FF12 -:106FD000B76102FF7604E85E0059596A00FF760443 -:106FE000E831F359598BD883FB04771FD1E32EFF87 -:106FF000A71B70EB22C746E40000FF4EE6EB0CC770 -:1070000046E40000FF46E6EB02EB00837EE40074FA -:1070100003E92AFEE9D4FC5F5EC9C3F36FF56FFF95 -:107020006FF36F0970558BEC8B4604B9E803F7E1F9 -:107030008B4E06F7F15DC3558BEC568B7606EB0E47 -:107040008BDE468A0750FF7604E833005959803CAE -:107050000075EDEB005E5DC3558BEC568B7606EB51 -:10706000148BDE468A0750FF7604E8450059590B19 -:10707000C07402EB07803C0075E7EB005E5DC3C89F -:10708000020000568B76048A445A988946FE9CFA80 -:107090008A46FEBAFE00EEBA0200ECA80274069D13 -:1070A000E8919EEBE9BA00008A4606EE9DEB005E91 -:1070B000C9C3C8040000568B76048A445A9889468E -:1070C000FEE8E6A18946FCE8E0A12B46FC3DB80BB2 -:1070D0007605B80100EB239CFA8A46FEBAFE00EE64 -:1070E000BA0200ECA80274069DE8489EEBD9BA00EB -:1070F000008A4606EE9D33C0EB005EC9C3C804009B -:107100000056578B7604837E0600740756E8030109 -:1071100059EB0556E8A200598846FF807EFF0877A4 -:10712000068A46FFE98400807EFF0F7603EB7990A4 -:107130008A46FFB4002D0A008BD883FB047767D101 -:10714000E32EFFA7AF71B000EB6156E86B0059B4B6 -:1071500000250F008946FC56E85E0059B4008BF804 -:1071600056E8550059B400C1E0088BD703D08BFA1C -:107170008B5EFCD1E3897830EB2E56E83B005988D2 -:10718000445BEB2456E831005988445056E8290006 -:107190005988445156E821005988445256E819004C -:1071A00059884453EB02EB00E95BFF5F5EC9C346BD -:1071B00071A6714A717A718471C8040000568B7689 -:1071C000048A445A988946FE9CFA8A46FEBAFE0012 -:1071D000EEBA0200ECA80175069DE8579DEBE9BAEE -:1071E0000000EC8846FD9D8A46FDEB005EC9C3C8E1 -:1071F000020000568B76048A445A988946FE9CFA0F -:107200008A46FEBAFE00EEBA0200EC32E424019D8A -:107210005EC9C3C8060000568B76048A445A988912 -:1072200046FEE885A08946FAE87FA02B46FA3DB8DD -:107230000B7604B008EB249CFA8A46FEBAFE00EEF8 -:10724000BA0200ECA80175069DE8E89CEBDABA00EA -:1072500000EC8846FD9D8A46FDEB005EC9C3558B58 -:10726000EC568B56048A4606EE33F6EB035058462E -:1072700083FE147CF85E5DC3C8020000568B560482 -:10728000EC8846FF33F6EB0350584683FE147CF837 -:107290008A46FFEB005EC9C3C802000056578B76D2 -:1072A00004833EB00B00751FBA8801B000EEBA86A9 -:1072B00001B000EE6A096A00683001E87D0183C40C -:1072C00006C706B00B01006A098BC605800150E8AD -:1072D000DA0059598BF88BC7C1E80C250F00894695 -:1072E000FE8BC7C1E808250F008B56FE83F20C3BCE -:1072F000C275218BC7C1E804250F008B56FE83F2AF -:10730000063BC2750F8BC7250F008B56FE83F20913 -:107310003BC2740D6A0756E838005959C746FE0744 -:10732000008A46FE0480A233028BC6BA6200F7EAE6 -:107330008A56FE8BD888976C006832028BC6BA6278 -:1073400000F7EA05140050E80EFD5959EB005F5EA6 -:10735000C9C3C8020000568B760683E60F8BC6C1F0 -:10736000E00C8BD683F20CC1E2080BC28BD683F201 -:1073700006C1E2040BC28BD683F2090BC28946FE1A -:107380006A196A108B46049905400183D200525055 -:10739000E86B0183C4060B46FE83CA005250E89A8C -:1073A0000083C406E82501EB005EC9C3558BEC568B -:1073B0005733FF6A01688601E8A3FE5959B1102AC4 -:1073C0004E06D3660433F6EB2E817E0400807204F1 -:1073D000B001EB02B00050688801E881FE59596A9B -:1073E00003688601E877FE59596A01688601E86DED -:1073F000FE5959D16604463B76067CCD33F6EB2424 -:10740000D1E76A03688601E854FE59596A01688623 -:1074100001E84AFE5959688801E85CFE599825013F -:10742000000BF84683FE107CD76A00688601E82DC1 -:10743000FE59598BC7EB005F5E5DC3558BEC565709 -:107440008B7E086A01688601E813FE5959B820004E -:107450002BC750FF7606FF7604E8A20083C4068996 -:10746000560689460433F6EB47817E060080720C8F -:107470007506837E04007204B001EB02B000506810 -:107480008801E8D9FD59596A03688601E8CFFD599A -:10749000596A01688601E8C5FD59596A01FF7606F7 -:1074A000FF7604E8580083C40689560689460446D8 -:1074B0003BF77CB56A00688601E8A2FD59596A006D -:1074C000688601E898FD59595F5E5DC3558BEC569F -:1074D0006A01688601E886FD595933F6EB00688831 -:1074E00001E894FD59A80175088BC6463D64007CEF -:1074F000ED6A00688601E865FD59595E5DC3C80400 -:1075000000008B46048B56068B4E08E306D1E0D173 -:10751000D2E2FA8946FC8956FE8B56FE8B46FCEB7E -:1075200000C9C300000000000000000000000000CF -:1075300050726576696F7573204D656E7500426592 -:1075400067696E00000000000000000000000000FD -:10755000000000000000000000000000000000002B -:10756000000000000000000000000000000000001B -:10757000000000000000000000000000000000000B -:1075800000000000000000000000000000000000FB -:1075900000000000000000000000000000000000EB -:1075A00000000000000000000000000000000000DB -:1075B00000000000000000000000000000000000CB -:1075C00000000000000000000000000000000000BB -:1075D00000000000000000000000000000000000AB -:1075E000000000000000000000000000000000009B -:1075F000000000000000000000000000000000008B -:10760000000000000000000000000000000000007A -:10761000000000000000000000000000000000006A -:10762000000000000000000000000000000000005A -:10763000000000000000000000000000000000004A -:10764000000000000000000000000000000000003A -:10765000000000000000000000000000000000002A -:10766000000000000000000000000000000000001A -:10767000000000000000000000000000000000000A -:1076800000000000000000000000000000000000FA -:1076900000000000000000000000000000000000EA -:1076A00000000000000000000000000000000000DA -:1076B00000000000000000000000000000000000CA -:1076C000000000000000000000000000506F727415 -:1076D000203000506F7274203100506F727420326D -:1076E00000506F7274203300506F72742034005059 -:1076F0006F7274203500506F7274203600506F72B4 -:1077000074203700506F7274203800506F727420EC -:107710003900506F727420313000506F7274203114 -:107720003100506F727420313200506F727420310A -:107730003300506F727420313400506F72742031F6 -:1077400035009C01A301AA01B101B801BF01C60126 -:10775000CD01D401DB01E201EA01F201FA010202EA -:107760000A02080000078100038080809F919591A4 -:107770009F000381848E95848484840003828484A2 -:107780008484958E8400048800B20BC60BDA0BEE5D -:107790000B020C160C2A0C3E0C520C770C9C0CBEE7 -:1077A0000CE00C020D01802054657374205061734D -:1077B000736564201F20507265737320800200017E -:1077C00080204D697373696E67205278204461741C -:1077D000611F205072657373208002000180204277 -:1077E00061642052782044617461201F20507265CA -:1077F000737320800200018020586D7472204275DE -:1078000073791F20507265737320800200018020FD -:107810006E6F742063757272656E746C791F2020B0 -:10782000696D706C656D656E7465640200240D2F62 -:107830000D3A0D450D500D5B0D660D710D7C0D87DC -:107840000D920D9D0DA80DB30DBE0DC90D53802CCD -:107850003254442053862C334454522053822C33C8 -:10786000525453201F53812C3252442053852C32C2 -:1078700043442053832C334354532053842C3344A8 -:1078800053522053872C3252492702000180202076 -:10789000444344202D2070696E2032301F275385C9 -:1078A0002E31818263908081828384858687888956 -:1078B0008A8B8C8D8E8F27020001802020445352AA -:1078C000202D2070696E2031311F2753842E318185 -:1078D000826390808182838485868788898A8B8C65 -:1078E0008D8E8F27020001802020435453202D20AD -:1078F00070696E20341F2753832E318182639080FC -:107900008182838485868788898A8B8C8D8E8F2758 -:107910000200018020205249202D2070696E203203 -:10792000321F2753872E3181826390808182838426 -:1079300085868788898A8B8C8D8E8F2702000180AF -:107940002020445452202D2070696E20362F381F7D -:107950002753862E3181826390808182838485863D -:107960008788898A8B8C8D8E8F270200018020204A -:10797000525453202D2070696E20351F2753822EBC -:107980003181826390808182838485868788898A19 -:107990008B8C8D8E8F27020001802020527844200E -:1079A0002D2070696E20321F2753812E30534D8158 -:1079B000826390808182838485868788898A8B8C84 -:1079C0008D8E8F27020001802020547844202D20A6 -:1079D00070696E20331F2753802E30534D81826390 -:1079E00090808182838485868788898A8B8C8D8E1E -:1079F0008F27020001802020444344202D207069FD -:107A00006E20351F2753852E3181826390808182BD -:107A1000838485868788898A8B8C8D8E8F27020048 -:107A200001802020445352202D2070696E20351F84 -:107A30002753842E3181826390808182838485865E -:107A40008788898A8B8C8D8E8F2702000180202069 -:107A5000435453202D2070696E20311F2753832EED -:107A60003181826390808182838485868788898A38 -:107A70008B8C8D8E8F270200018020205249202D73 -:107A800020286E2E632E291F2753872E3181826373 -:107A900090808182838485868788898A8B8C8D8E6D -:107AA0008F27020001802020445452202D2070692D -:107AB0006E20321F2753862E31818263908081820F -:107AC000838485868788898A8B8C8D8E8F27020098 -:107AD00001802020525453202D2070696E20371FC2 -:107AE0002753822E318182639080818283848586B0 -:107AF0008788898A8B8C8D8E8F27020001802020B9 -:107B0000527844202D2070696E20361F2753812E15 -:107B100030534D81826390808182838485868788FB -:107B2000898A8B8C8D8E8F270200018020205478CB -:107B300044202D2070696E20331F2753802E305330 -:107B40004D81826390808182838485868788898A3B -:107B50008B8C8D8E8F27020001802020444344208F -:107B60002D2070696E20351F202020202753852E60 -:107B700031818263888081828384858687270200A1 -:107B800001802020445352202D2070696E20351F23 -:107B9000202020202753842E318182638880818297 -:107BA0008384858687270200018020204354532048 -:107BB0002D2070696E20311F202020202753832E16 -:107BC0003181826388808182838485868727020051 -:107BD000018020205249202D20286E2E632E291F3F -:107BE000202020202753872E318182638880818244 -:107BF00083848586872702000180202044545220F8 -:107C00002D2070696E20321F202020202753862EC1 -:107C10003181826388808182838485868727020000 -:107C200001802020525453202D2070696E20371F70 -:107C3000202020202753822E3181826388808182F8 -:107C40008384858687270200018020205278442083 -:107C50002D2070696E20361F202020202753812E72 -:107C600030534D8182638880818283848586872713 -:107C7000020001802020547844202D2070696E205D -:107C8000331F202020202753802E30534D818263C4 -:107C90008880818283848586872702000180202056 -:107CA000444344202D2070696E2032301F20202054 -:107CB000202753852E318182638880818283848549 -:107CC000868727020001802020445352202D2070F7 -:107CD000696E2031311F202020202753842E3181CE -:107CE0008263888081828384858687270200018061 -:107CF0002020435453202D2070696E20341F2020F3 -:107D000020202753832E318182638880818283845F -:107D1000858687270200018020205249202D20706F -:107D2000696E2032321F202020202753872E318178 -:107D30008263888081828384858687270200018010 -:107D40002020445452202D2070696E20362F381F79 -:107D5000202020202753862E3181826388808182D3 -:107D60008384858687270200018020205254532077 -:107D70002D2070696E20351F202020202753822E51 -:107D8000318182638880818283848586872702008F -:107D900001802020527844202D2070696E20321FEF -:107DA000202020202753812E30534D8182638880EC -:107DB0008182838485868727020001802020547871 -:107DC00044202D2070696E20331F2020202027534F -:107DD000802E30534D8182638880818283848586A2 -:107DE0008727020068049604B6033C040E04890346 -:107DF0005C03E20360088A08BE0738080E0895078E -:107E00006C07E6071C057405FA05C404F004CC05EC -:107E1000A00548057806C806420728065006180738 -:107E2000F006A0060000F408F408D40D04090409C3 -:107E30000409040942000C091C09E50D020014099B -:107E40000409F40D43001C090C09050E0004040983 -:107E50001409120E2C092C092C092C0900003C09CC -:107E60006C091E0E740974097409740900014C0927 -:107E70002C092D0E740974097409740900025C0937 -:107E80003C093D0E740974097409740900036C09F6 -:107E90004C094D0E7409740974097409FF002C090A -:107EA0005C09000000058409EC095E0EF409F40980 -:107EB000F409F409000694097409680EAC0AAC0AC6 -:107EC000AC0AAC0A0007A4098409720EBC0ABC0AF9 -:107ED000BC0ABC0A0008B40994097C0ED40AD40A6E -:107EE000D40AD40A000BC409A409830EFC0AFC0AB4 -:107EF000FC0AFC0A000CD409B409900E140B140BF4 -:107F0000140B140B0002E409C409A00E2C0B2C0B5B -:107F10002C0B2C0B0400EC09D4090E00FF00740993 -:107F2000E40900008201FC09A40AAC0E8202040AE2 -:107F3000F409AF0E82030C0AFC09B20E8204140A83 -:107F4000040AB60E82051C0A0C0ABC0E8206240A1C -:107F5000140AC00E82072C0A1C0AC40E8208340AB6 -:107F6000240AC80E82093C0A2C0ACC0E820A440A52 -:107F7000340AD10E82104C0A3C0AD60E820B540AE7 -:107F8000440ADB0E82115C0A4C0AE00E820C640A81 -:107F9000540AE50E82126C0A5C0AEA0E820D740A1B -:107FA000640AEF0E820E7C0A6C0AF40E820F840AB9 -:107FB000740AFB0E82138C0A7C0A020F8214940A44 -:107FC000840A090F82159C0A8C0A100F8216A40AD3 -:107FD000940A170F8217F4099C0A1E0F8202B40A32 -:107FE000B40A260F8203AC0AAC0A2D0F8200C40A21 -:107FF000CC0A340F8201CC0ABC0A3F0F8202BC0AB1 -:10800000C40A4D0F8200DC0AF40A590F8201E40A07 -:10801000D40A630F8202EC0ADC0A6E0F8203F40AB0 -:10802000E40A7A0F8204D40AEC0A870F8200040B58 -:108030000C0B930F82010C0BFC0A9B0F8202FC0AB3 -:10804000040BA70F82001C0B240BB00F8201240B22 -:10805000140BB50F8202140B1C0BBE0F4400340B23 -:10806000A40B9C0144013C0B2C0BA3014402440BC8 -:10807000340BAA0144034C0B3C0BB1014404540BD8 -:10808000440BB80144055C0B4C0BBF014406640B68 -:10809000540BC60144076C0B5C0BCD014408740BF8 -:1080A000640BD40144097C0B6C0BDB01440A840B88 -:1080B000740BE201440B8C0B7C0BEA01440C940B17 -:1080C000840BF201440D9C0B8C0BFA01440EA40BA3 -:1080D000940B0202440F2C0B9C0B0A02171F0F2F4C -:1080E0000000018078783A20747820637073202A29 -:1080F0002A2A2A2A0200018078783A20747820639C -:108100007073202A2A2A2A2A0200018078783A20CD -:10811000747820637073202A2A2A2A2A0200018098 -:1081200078783A20747820637073202A2A2A2A2AC1 -:10813000020001C078783A20726320637073202AAD -:108140002A2A2A2A020001C078783A207263206322 -:108150007073202A2A2A2A2A020001C078783A203D -:10816000726320637073202A2A2A2A2A020001C01F -:1081700078783A20726320637073202A2A2A2A2A88 -:1081800002000180496E7374616C6C204C6F6F70DB -:108190006261636B1F5072657373208020746F205F -:1081A000737461727402000180204361626C652007 -:1081B000746F2052656D6F74651F50726573732004 -:1081C0008020746F20737461727402000180204CEF -:1081D0006F63616C204C6F6F706261636B201F2056 -:1081E0002052756E6E696E67202E2E2E0200018061 -:1081F00052656D6F7465204C6F6F706261636B20A8 -:108200001F202052756E6E696E67202E2E2E020082 -:10821000018020496E74726E6C204C6F6F706261C9 -:10822000636B1F202052756E6E696E67202E2E2E96 -:10823000020001805472616E736D69742050617424 -:108240007465726E1F202052756E6E696E67202EE7 -:108250002E2E020001802020303A2027438000018A -:10826000802020313A202743810001802020323AAB -:10827000202743820001802020333A2027438300B7 -:1082800001802020343A20274384000180202035BB -:108290003A202743850001802020363A2027438654 -:1082A0000001802020373A202743870001802020CA -:1082B000383A202743880001802020393A2027437C -:1082C000890001802031303A2027438A0001802034 -:1082D00031313A2027438B0001802031323A202768 -:1082E000438C0001802031333A2027438D000180E8 -:1082F0002031343A2027438E0001802031353A2046 -:1083000027438F002A2A204D61696E20204D656E1B -:1083100075202A2A004D6F6E69746F72206120509B -:108320006F7274004D6F6E69746F722061205369B3 -:10833000676E616C00457374696D617465204350AC -:108340005300446961676E6F7374696373004C6FA7 -:1083500063616C204C6F6F706261636B0052656D7E -:108360006F7465204C6F6F706261636B00496E744F -:10837000726E6C204C6F6F706261636B005472613F -:108380006E736D6974205061747465726E00426121 -:10839000756420526174650044617461204269749F -:1083A000730053746F702042697473005061726976 -:1083B00074790044617461205061747465726E0058 -:1083C000547820466C6F7720436F6E74726F6C0028 -:1083D000506F7274204E756D6265720035300037D3 -:1083E0003500313130003133342E35003135300035 -:1083F00032303000333030003630300031323030FF -:10840000003138303000323030300032343030001B -:1084100033363030003438303000373230300039C5 -:108420003630300031392C3230300033382C343093 -:10843000300035362C3030300035372C36303000B7 -:1084400036342C3030300037362C38303000313173 -:10845000352C323030003720626974730038206266 -:1084600069747300312073746F7020626974003115 -:108470002E352073746F702062697473003220731C -:10848000746F702062697473006E6F20706172691E -:108490007479006F64642070617269747900657624 -:1084A000656E207061726974790073706163652014 -:1084B000706172697479006D61726B2070617269AC -:1084C000747900436F6C756D6E7300426172626502 -:1084D0007220506F6C650055555555552E2E2E0047 -:1084E0004E6F6E6500586F6E2F586F66660043546E -:1084F00053005072657373208020666F72206D6523 -:108500006E750028636F756E74696E672E2E2E2946 -:108510000000654E64204F6620436F4465000000F4 -:10852000000000000000000000000000000000004B -:10853000000000000000000000000000000000003B -:10854000000000000000000000000000000000002B -:10855000000000000000000000000000000000001B -:10856000000000000000000000000000000000000B -:1085700000000000000000000000000000000000FB -:1085800000000000000000000000000000000000EB -:1085900000000000000000000000000000000000DB -:1085A00000000000000000000000000000000000CB -:1085B00000000000000000000000000000000000BB -:1085C00000000000000000000000000000000000AB -:1085D000000000000000000000000000000000009B -:1085E000000000000000000000000000000000008B -:1085F000000000000000000000000000000000007B -:00000001FF -/* Intelliport II loadware */ -/* -31232 bytes read from ff.lod */ diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index debdfe0..59f8db4 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -866,6 +866,12 @@ COMPATIBLE_IOCTL(TIOCGPTN) COMPATIBLE_IOCTL(TIOCSPTLCK) COMPATIBLE_IOCTL(TIOCSERGETLSR) COMPATIBLE_IOCTL(TIOCSIG) +#ifdef TIOCSRS485 +COMPATIBLE_IOCTL(TIOCSRS485) +#endif +#ifdef TIOCGRS485 +COMPATIBLE_IOCTL(TIOCGRS485) +#endif #ifdef TCGETS2 COMPATIBLE_IOCTL(TCGETS2) COMPATIBLE_IOCTL(TCSETS2) diff --git a/include/linux/Kbuild b/include/linux/Kbuild index fa21760..c57e064 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -84,7 +84,6 @@ header-y += capability.h header-y += capi.h header-y += cciss_defs.h header-y += cciss_ioctl.h -header-y += cdk.h header-y += cdrom.h header-y += cgroupstats.h header-y += chio.h @@ -93,7 +92,6 @@ header-y += cn_proc.h header-y += coda.h header-y += coda_psdev.h header-y += coff.h -header-y += comstats.h header-y += connector.h header-y += const.h header-y += cramfs_fs.h @@ -140,7 +138,6 @@ header-y += fuse.h header-y += futex.h header-y += gameport.h header-y += gen_stats.h -header-y += generic_serial.h header-y += genetlink.h header-y += gfs2_ondisk.h header-y += gigaset_dev.h @@ -372,6 +369,7 @@ header-y += tipc.h header-y += tipc_config.h header-y += toshiba.h header-y += tty.h +header-y += tty_flags.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index d117b29..f612c78 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -205,7 +205,6 @@ struct amba_pl011_data { void *dma_tx_param; void (*init) (void); void (*exit) (void); - void (*reset) (void); }; #endif diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h deleted file mode 100644 index 1dc3ab0..0000000 --- a/include/linux/cd1400.h +++ /dev/null @@ -1,292 +0,0 @@ -/*****************************************************************************/ - -/* - * cd1400.h -- cd1400 UART hardware info. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _CD1400_H -#define _CD1400_H -/*****************************************************************************/ - -/* - * Define the number of async ports per cd1400 uart chip. - */ -#define CD1400_PORTS 4 - -/* - * Define the cd1400 uarts internal FIFO sizes. - */ -#define CD1400_TXFIFOSIZE 12 -#define CD1400_RXFIFOSIZE 12 - -/* - * Local RX FIFO thresh hold level. Also define the RTS thresh hold - * based on the RX thresh hold. - */ -#define FIFO_RXTHRESHOLD 6 -#define FIFO_RTSTHRESHOLD 7 - -/*****************************************************************************/ - -/* - * Define the cd1400 register addresses. These are all the valid - * registers with the cd1400. Some are global, some virtual, some - * per port. - */ -#define GFRCR 0x40 -#define CAR 0x68 -#define GCR 0x4b -#define SVRR 0x67 -#define RICR 0x44 -#define TICR 0x45 -#define MICR 0x46 -#define RIR 0x6b -#define TIR 0x6a -#define MIR 0x69 -#define PPR 0x7e - -#define RIVR 0x43 -#define TIVR 0x42 -#define MIVR 0x41 -#define TDR 0x63 -#define RDSR 0x62 -#define MISR 0x4c -#define EOSRR 0x60 - -#define LIVR 0x18 -#define CCR 0x05 -#define SRER 0x06 -#define COR1 0x08 -#define COR2 0x09 -#define COR3 0x0a -#define COR4 0x1e -#define COR5 0x1f -#define CCSR 0x0b -#define RDCR 0x0e -#define SCHR1 0x1a -#define SCHR2 0x1b -#define SCHR3 0x1c -#define SCHR4 0x1d -#define SCRL 0x22 -#define SCRH 0x23 -#define LNC 0x24 -#define MCOR1 0x15 -#define MCOR2 0x16 -#define RTPR 0x21 -#define MSVR1 0x6c -#define MSVR2 0x6d -#define PSVR 0x6f -#define RBPR 0x78 -#define RCOR 0x7c -#define TBPR 0x72 -#define TCOR 0x76 - -/*****************************************************************************/ - -/* - * Define the set of baud rate clock divisors. - */ -#define CD1400_CLK0 8 -#define CD1400_CLK1 32 -#define CD1400_CLK2 128 -#define CD1400_CLK3 512 -#define CD1400_CLK4 2048 - -#define CD1400_NUMCLKS 5 - -/*****************************************************************************/ - -/* - * Define the clock pre-scalar value to be a 5 ms clock. This should be - * OK for now. It would probably be better to make it 10 ms, but we - * can't fit that divisor into 8 bits! - */ -#define PPR_SCALAR 244 - -/*****************************************************************************/ - -/* - * Define values used to set character size options. - */ -#define COR1_CHL5 0x00 -#define COR1_CHL6 0x01 -#define COR1_CHL7 0x02 -#define COR1_CHL8 0x03 - -/* - * Define values used to set the number of stop bits. - */ -#define COR1_STOP1 0x00 -#define COR1_STOP15 0x04 -#define COR1_STOP2 0x08 - -/* - * Define values used to set the parity scheme in use. - */ -#define COR1_PARNONE 0x00 -#define COR1_PARFORCE 0x20 -#define COR1_PARENB 0x40 -#define COR1_PARIGNORE 0x10 - -#define COR1_PARODD 0x80 -#define COR1_PAREVEN 0x00 - -#define COR2_IXM 0x80 -#define COR2_TXIBE 0x40 -#define COR2_ETC 0x20 -#define COR2_LLM 0x10 -#define COR2_RLM 0x08 -#define COR2_RTSAO 0x04 -#define COR2_CTSAE 0x02 - -#define COR3_SCDRNG 0x80 -#define COR3_SCD34 0x40 -#define COR3_FCT 0x20 -#define COR3_SCD12 0x10 - -/* - * Define values used by COR4. - */ -#define COR4_BRKINT 0x08 -#define COR4_IGNBRK 0x18 - -/*****************************************************************************/ - -/* - * Define the modem control register values. - * Note that the actual hardware is a little different to the conventional - * pin names on the cd1400. - */ -#define MSVR1_DTR 0x01 -#define MSVR1_DSR 0x10 -#define MSVR1_RI 0x20 -#define MSVR1_CTS 0x40 -#define MSVR1_DCD 0x80 - -#define MSVR2_RTS 0x02 -#define MSVR2_DSR 0x10 -#define MSVR2_RI 0x20 -#define MSVR2_CTS 0x40 -#define MSVR2_DCD 0x80 - -#define MCOR1_DCD 0x80 -#define MCOR1_CTS 0x40 -#define MCOR1_RI 0x20 -#define MCOR1_DSR 0x10 - -#define MCOR2_DCD 0x80 -#define MCOR2_CTS 0x40 -#define MCOR2_RI 0x20 -#define MCOR2_DSR 0x10 - -/*****************************************************************************/ - -/* - * Define the bits used with the service (interrupt) enable register. - */ -#define SRER_NNDT 0x01 -#define SRER_TXEMPTY 0x02 -#define SRER_TXDATA 0x04 -#define SRER_RXDATA 0x10 -#define SRER_MODEM 0x80 - -/*****************************************************************************/ - -/* - * Define operational commands for the command register. - */ -#define CCR_RESET 0x80 -#define CCR_CORCHANGE 0x4e -#define CCR_SENDCH 0x20 -#define CCR_CHANCTRL 0x10 - -#define CCR_TXENABLE (CCR_CHANCTRL | 0x08) -#define CCR_TXDISABLE (CCR_CHANCTRL | 0x04) -#define CCR_RXENABLE (CCR_CHANCTRL | 0x02) -#define CCR_RXDISABLE (CCR_CHANCTRL | 0x01) - -#define CCR_SENDSCHR1 (CCR_SENDCH | 0x01) -#define CCR_SENDSCHR2 (CCR_SENDCH | 0x02) -#define CCR_SENDSCHR3 (CCR_SENDCH | 0x03) -#define CCR_SENDSCHR4 (CCR_SENDCH | 0x04) - -#define CCR_RESETCHAN (CCR_RESET | 0x00) -#define CCR_RESETFULL (CCR_RESET | 0x01) -#define CCR_TXFLUSHFIFO (CCR_RESET | 0x02) - -#define CCR_MAXWAIT 10000 - -/*****************************************************************************/ - -/* - * Define the valid acknowledgement types (for hw ack cycle). - */ -#define ACK_TYPMASK 0x07 -#define ACK_TYPTX 0x02 -#define ACK_TYPMDM 0x01 -#define ACK_TYPRXGOOD 0x03 -#define ACK_TYPRXBAD 0x07 - -#define SVRR_RX 0x01 -#define SVRR_TX 0x02 -#define SVRR_MDM 0x04 - -#define ST_OVERRUN 0x01 -#define ST_FRAMING 0x02 -#define ST_PARITY 0x04 -#define ST_BREAK 0x08 -#define ST_SCHAR1 0x10 -#define ST_SCHAR2 0x20 -#define ST_SCHAR3 0x30 -#define ST_SCHAR4 0x40 -#define ST_RANGE 0x70 -#define ST_SCHARMASK 0x70 -#define ST_TIMEOUT 0x80 - -#define MISR_DCD 0x80 -#define MISR_CTS 0x40 -#define MISR_RI 0x20 -#define MISR_DSR 0x10 - -/*****************************************************************************/ - -/* - * Defines for the CCSR status register. - */ -#define CCSR_RXENABLED 0x80 -#define CCSR_RXFLOWON 0x40 -#define CCSR_RXFLOWOFF 0x20 -#define CCSR_TXENABLED 0x08 -#define CCSR_TXFLOWON 0x04 -#define CCSR_TXFLOWOFF 0x02 - -/*****************************************************************************/ - -/* - * Define the embedded commands. - */ -#define ETC_CMD 0x00 -#define ETC_STARTBREAK 0x81 -#define ETC_DELAY 0x82 -#define ETC_STOPBREAK 0x83 - -/*****************************************************************************/ -#endif diff --git a/include/linux/cdk.h b/include/linux/cdk.h deleted file mode 100644 index 80093a8..0000000 --- a/include/linux/cdk.h +++ /dev/null @@ -1,486 +0,0 @@ -/*****************************************************************************/ - -/* - * cdk.h -- CDK interface definitions. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _CDK_H -#define _CDK_H -/*****************************************************************************/ - -#pragma pack(2) - -/* - * The following set of definitions is used to communicate with the - * shared memory interface of the Stallion intelligent multiport serial - * boards. The definitions in this file are taken directly from the - * document titled "Generic Stackable Interface, Downloader and - * Communications Development Kit". - */ - -/* - * Define the set of important shared memory addresses. These are - * required to initialize the board and get things started. All of these - * addresses are relative to the start of the shared memory. - */ -#define CDK_SIGADDR 0x200 -#define CDK_FEATADDR 0x280 -#define CDK_CDKADDR 0x300 -#define CDK_RDYADDR 0x262 - -#define CDK_ALIVEMARKER 13 - -/* - * On hardware power up the ROMs located on the EasyConnection 8/64 will - * fill out the following signature information into shared memory. This - * way the host system can quickly determine that the board is present - * and is operational. - */ -typedef struct cdkecpsig { - unsigned long magic; - unsigned short romver; - unsigned short cputype; - unsigned char panelid[8]; -} cdkecpsig_t; - -#define ECP_MAGIC 0x21504345 - -/* - * On hardware power up the ROMs located on the ONboard, Stallion and - * Brumbys will fill out the following signature information into shared - * memory. This way the host system can quickly determine that the board - * is present and is operational. - */ -typedef struct cdkonbsig { - unsigned short magic0; - unsigned short magic1; - unsigned short magic2; - unsigned short magic3; - unsigned short romver; - unsigned short memoff; - unsigned short memseg; - unsigned short amask0; - unsigned short pic; - unsigned short status; - unsigned short btype; - unsigned short clkticks; - unsigned short clkspeed; - unsigned short amask1; - unsigned short amask2; -} cdkonbsig_t; - -#define ONB_MAGIC0 0xf2a7 -#define ONB_MAGIC1 0xa149 -#define ONB_MAGIC2 0x6352 -#define ONB_MAGIC3 0xf121 - -/* - * Define the feature area structure. The feature area is the set of - * startup parameters used by the slave image when it starts executing. - * They allow for the specification of buffer sizes, debug trace, etc. - */ -typedef struct cdkfeature { - unsigned long debug; - unsigned long banner; - unsigned long etype; - unsigned long nrdevs; - unsigned long brdspec; - unsigned long txrqsize; - unsigned long rxrqsize; - unsigned long flags; -} cdkfeature_t; - -#define ETYP_DDK 0 -#define ETYP_CDK 1 - -/* - * Define the CDK header structure. This is the info that the slave - * environment sets up after it has been downloaded and started. It - * essentially provides a memory map for the shared memory interface. - */ -typedef struct cdkhdr { - unsigned short command; - unsigned short status; - unsigned short port; - unsigned short mode; - unsigned long cmd_buf[14]; - unsigned short alive_cnt; - unsigned short intrpt_mode; - unsigned char intrpt_id[8]; - unsigned char ver_release; - unsigned char ver_modification; - unsigned char ver_fix; - unsigned char deadman_restart; - unsigned short deadman; - unsigned short nrdevs; - unsigned long memp; - unsigned long hostp; - unsigned long slavep; - unsigned char hostreq; - unsigned char slavereq; - unsigned char cmd_reserved[30]; -} cdkhdr_t; - -#define MODE_DDK 0 -#define MODE_CDK 1 - -#define IMD_INTR 0x0 -#define IMD_PPINTR 0x1 -#define IMD_POLL 0xff - -/* - * Define the memory mapping structure. This structure is pointed to by - * the memp field in the stlcdkhdr struct. As many as these structures - * as required are laid out in shared memory to define how the rest of - * shared memory is divided up. There will be one for each port. - */ -typedef struct cdkmem { - unsigned short dtype; - unsigned long offset; -} cdkmem_t; - -#define TYP_UNDEFINED 0x0 -#define TYP_ASYNCTRL 0x1 -#define TYP_ASYNC 0x20 -#define TYP_PARALLEL 0x40 -#define TYP_SYNCX21 0x60 - -/*****************************************************************************/ - -/* - * Following is a set of defines and structures used to actually deal - * with the serial ports on the board. Firstly is the set of commands - * that can be applied to ports. - */ -#define ASYCMD (((unsigned long) 'a') << 8) - -#define A_NULL (ASYCMD | 0) -#define A_FLUSH (ASYCMD | 1) -#define A_BREAK (ASYCMD | 2) -#define A_GETPORT (ASYCMD | 3) -#define A_SETPORT (ASYCMD | 4) -#define A_SETPORTF (ASYCMD | 5) -#define A_SETPORTFTX (ASYCMD | 6) -#define A_SETPORTFRX (ASYCMD | 7) -#define A_GETSIGNALS (ASYCMD | 8) -#define A_SETSIGNALS (ASYCMD | 9) -#define A_SETSIGNALSF (ASYCMD | 10) -#define A_SETSIGNALSFTX (ASYCMD | 11) -#define A_SETSIGNALSFRX (ASYCMD | 12) -#define A_GETNOTIFY (ASYCMD | 13) -#define A_SETNOTIFY (ASYCMD | 14) -#define A_NOTIFY (ASYCMD | 15) -#define A_PORTCTRL (ASYCMD | 16) -#define A_GETSTATS (ASYCMD | 17) -#define A_RQSTATE (ASYCMD | 18) -#define A_FLOWSTATE (ASYCMD | 19) -#define A_CLEARSTATS (ASYCMD | 20) - -/* - * Define those arguments used for simple commands. - */ -#define FLUSHRX 0x1 -#define FLUSHTX 0x2 - -#define BREAKON -1 -#define BREAKOFF -2 - -/* - * Define the port setting structure, and all those defines that go along - * with it. Basically this structure defines the characteristics of this - * port: baud rate, chars, parity, input/output char cooking etc. - */ -typedef struct asyport { - unsigned long baudout; - unsigned long baudin; - unsigned long iflag; - unsigned long oflag; - unsigned long lflag; - unsigned long pflag; - unsigned long flow; - unsigned long spare1; - unsigned short vtime; - unsigned short vmin; - unsigned short txlo; - unsigned short txhi; - unsigned short rxlo; - unsigned short rxhi; - unsigned short rxhog; - unsigned short spare2; - unsigned char csize; - unsigned char stopbs; - unsigned char parity; - unsigned char stopin; - unsigned char startin; - unsigned char stopout; - unsigned char startout; - unsigned char parmark; - unsigned char brkmark; - unsigned char cc[11]; -} asyport_t; - -#define PT_STOP1 0x0 -#define PT_STOP15 0x1 -#define PT_STOP2 0x2 - -#define PT_NOPARITY 0x0 -#define PT_ODDPARITY 0x1 -#define PT_EVENPARITY 0x2 -#define PT_MARKPARITY 0x3 -#define PT_SPACEPARITY 0x4 - -#define F_NONE 0x0 -#define F_IXON 0x1 -#define F_IXOFF 0x2 -#define F_IXANY 0x4 -#define F_IOXANY 0x8 -#define F_RTSFLOW 0x10 -#define F_CTSFLOW 0x20 -#define F_DTRFLOW 0x40 -#define F_DCDFLOW 0x80 -#define F_DSROFLOW 0x100 -#define F_DSRIFLOW 0x200 - -#define FI_NORX 0x1 -#define FI_RAW 0x2 -#define FI_ISTRIP 0x4 -#define FI_UCLC 0x8 -#define FI_INLCR 0x10 -#define FI_ICRNL 0x20 -#define FI_IGNCR 0x40 -#define FI_IGNBREAK 0x80 -#define FI_DSCRDBREAK 0x100 -#define FI_1MARKBREAK 0x200 -#define FI_2MARKBREAK 0x400 -#define FI_XCHNGBREAK 0x800 -#define FI_IGNRXERRS 0x1000 -#define FI_DSCDRXERRS 0x2000 -#define FI_1MARKRXERRS 0x4000 -#define FI_2MARKRXERRS 0x8000 -#define FI_XCHNGRXERRS 0x10000 -#define FI_DSCRDNULL 0x20000 - -#define FO_OLCUC 0x1 -#define FO_ONLCR 0x2 -#define FO_OOCRNL 0x4 -#define FO_ONOCR 0x8 -#define FO_ONLRET 0x10 -#define FO_ONL 0x20 -#define FO_OBS 0x40 -#define FO_OVT 0x80 -#define FO_OFF 0x100 -#define FO_OTAB1 0x200 -#define FO_OTAB2 0x400 -#define FO_OTAB3 0x800 -#define FO_OCR1 0x1000 -#define FO_OCR2 0x2000 -#define FO_OCR3 0x4000 -#define FO_OFILL 0x8000 -#define FO_ODELL 0x10000 - -#define P_RTSLOCK 0x1 -#define P_CTSLOCK 0x2 -#define P_MAPRTS 0x4 -#define P_MAPCTS 0x8 -#define P_LOOPBACK 0x10 -#define P_DTRFOLLOW 0x20 -#define P_FAKEDCD 0x40 - -#define P_RXIMIN 0x10000 -#define P_RXITIME 0x20000 -#define P_RXTHOLD 0x40000 - -/* - * Define a structure to communicate serial port signal and data state - * information. - */ -typedef struct asysigs { - unsigned long data; - unsigned long signal; - unsigned long sigvalue; -} asysigs_t; - -#define DT_TXBUSY 0x1 -#define DT_TXEMPTY 0x2 -#define DT_TXLOW 0x4 -#define DT_TXHIGH 0x8 -#define DT_TXFULL 0x10 -#define DT_TXHOG 0x20 -#define DT_TXFLOWED 0x40 -#define DT_TXBREAK 0x80 - -#define DT_RXBUSY 0x100 -#define DT_RXEMPTY 0x200 -#define DT_RXLOW 0x400 -#define DT_RXHIGH 0x800 -#define DT_RXFULL 0x1000 -#define DT_RXHOG 0x2000 -#define DT_RXFLOWED 0x4000 -#define DT_RXBREAK 0x8000 - -#define SG_DTR 0x1 -#define SG_DCD 0x2 -#define SG_RTS 0x4 -#define SG_CTS 0x8 -#define SG_DSR 0x10 -#define SG_RI 0x20 - -/* - * Define the notification setting structure. This is used to tell the - * port what events we want to be informed about. Fields here use the - * same defines as for the asysigs structure above. - */ -typedef struct asynotify { - unsigned long ctrl; - unsigned long data; - unsigned long signal; - unsigned long sigvalue; -} asynotify_t; - -/* - * Define the port control structure. It is used to do fine grain - * control operations on the port. - */ -typedef struct { - unsigned long rxctrl; - unsigned long txctrl; - char rximdch; - char tximdch; - char spare1; - char spare2; -} asyctrl_t; - -#define CT_ENABLE 0x1 -#define CT_DISABLE 0x2 -#define CT_STOP 0x4 -#define CT_START 0x8 -#define CT_STARTFLOW 0x10 -#define CT_STOPFLOW 0x20 -#define CT_SENDCHR 0x40 - -/* - * Define the stats structure kept for each port. This is a useful set - * of data collected for each port on the slave. The A_GETSTATS command - * is used to retrieve this data from the slave. - */ -typedef struct asystats { - unsigned long opens; - unsigned long txchars; - unsigned long rxchars; - unsigned long txringq; - unsigned long rxringq; - unsigned long txmsgs; - unsigned long rxmsgs; - unsigned long txflushes; - unsigned long rxflushes; - unsigned long overruns; - unsigned long framing; - unsigned long parity; - unsigned long ringover; - unsigned long lost; - unsigned long rxstart; - unsigned long rxstop; - unsigned long txstart; - unsigned long txstop; - unsigned long dcdcnt; - unsigned long dtrcnt; - unsigned long ctscnt; - unsigned long rtscnt; - unsigned long dsrcnt; - unsigned long ricnt; - unsigned long txbreaks; - unsigned long rxbreaks; - unsigned long signals; - unsigned long state; - unsigned long hwid; -} asystats_t; - -/*****************************************************************************/ - -/* - * All command and control communication with a device on the slave is - * via a control block in shared memory. Each device has its own control - * block, defined by the following structure. The control block allows - * the host to open, close and control the device on the slave. - */ -typedef struct cdkctrl { - unsigned char open; - unsigned char close; - unsigned long openarg; - unsigned long closearg; - unsigned long cmd; - unsigned long status; - unsigned long args[32]; -} cdkctrl_t; - -/* - * Each device on the slave passes data to and from the host via a ring - * queue in shared memory. Define a ring queue structure to hold the - * vital information about each ring queue. Two ring queues will be - * allocated for each port, one for receive data and one for transmit - * data. - */ -typedef struct cdkasyrq { - unsigned long offset; - unsigned short size; - unsigned short head; - unsigned short tail; -} cdkasyrq_t; - -/* - * Each asynchronous port is defined in shared memory by the following - * structure. It contains a control block to command a device, and also - * the necessary data channel information as well. - */ -typedef struct cdkasy { - cdkctrl_t ctrl; - unsigned short notify; - asynotify_t changed; - unsigned short receive; - cdkasyrq_t rxq; - unsigned short transmit; - cdkasyrq_t txq; -} cdkasy_t; - -#pragma pack() - -/*****************************************************************************/ - -/* - * Define the set of ioctls used by the driver to do special things - * to the board. These include interrupting it, and initializing - * the driver after board startup and shutdown. - */ -#include <linux/ioctl.h> - -#define STL_BINTR _IO('s',20) -#define STL_BSTART _IO('s',21) -#define STL_BSTOP _IO('s',22) -#define STL_BRESET _IO('s',23) - -/* - * Define a set of ioctl extensions, used to get at special stuff. - */ -#define STL_GETPFLAG _IO('s',80) -#define STL_SETPFLAG _IO('s',81) - -/*****************************************************************************/ -#endif diff --git a/include/linux/comstats.h b/include/linux/comstats.h deleted file mode 100644 index 3f5ea8e..0000000 --- a/include/linux/comstats.h +++ /dev/null @@ -1,119 +0,0 @@ -/*****************************************************************************/ - -/* - * comstats.h -- Serial Port Stats. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _COMSTATS_H -#define _COMSTATS_H -/*****************************************************************************/ - -/* - * Serial port stats structure. The structure itself is UART - * independent, but some fields may be UART/driver specific (for - * example state). - */ - -typedef struct { - unsigned long brd; - unsigned long panel; - unsigned long port; - unsigned long hwid; - unsigned long type; - unsigned long txtotal; - unsigned long rxtotal; - unsigned long txbuffered; - unsigned long rxbuffered; - unsigned long rxoverrun; - unsigned long rxparity; - unsigned long rxframing; - unsigned long rxlost; - unsigned long txbreaks; - unsigned long rxbreaks; - unsigned long txxon; - unsigned long txxoff; - unsigned long rxxon; - unsigned long rxxoff; - unsigned long txctson; - unsigned long txctsoff; - unsigned long rxrtson; - unsigned long rxrtsoff; - unsigned long modem; - unsigned long state; - unsigned long flags; - unsigned long ttystate; - unsigned long cflags; - unsigned long iflags; - unsigned long oflags; - unsigned long lflags; - unsigned long signals; -} comstats_t; - - -/* - * Board stats structure. Returns useful info about the board. - */ - -#define COM_MAXPANELS 8 - -typedef struct { - unsigned long panel; - unsigned long type; - unsigned long hwid; - unsigned long nrports; -} companel_t; - -typedef struct { - unsigned long brd; - unsigned long type; - unsigned long hwid; - unsigned long state; - unsigned long ioaddr; - unsigned long ioaddr2; - unsigned long memaddr; - unsigned long irq; - unsigned long nrpanels; - unsigned long nrports; - companel_t panels[COM_MAXPANELS]; -} combrd_t; - - -/* - * Define the ioctl operations for stats stuff. - */ -#include <linux/ioctl.h> - -#define COM_GETPORTSTATS _IO('c',30) -#define COM_CLRPORTSTATS _IO('c',31) -#define COM_GETBRDSTATS _IO('c',32) - - -/* - * Define the set of ioctls that give user level access to the - * private port, panel and board structures. The argument required - * will be driver dependent! - */ -#define COM_READPORT _IO('c',40) -#define COM_READBOARD _IO('c',41) -#define COM_READPANEL _IO('c',42) - -/*****************************************************************************/ -#endif diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h deleted file mode 100644 index 79b3eb3..0000000 --- a/include/linux/generic_serial.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * generic_serial.h - * - * Copyright (C) 1998 R.E.Wolff@BitWizard.nl - * - * written for the SX serial driver. - * - * Version 0.1 -- December, 1998. - */ - -#ifndef GENERIC_SERIAL_H -#define GENERIC_SERIAL_H - -#warning Use of this header is deprecated. -#warning Since nobody sets the constants defined here for you, you should not, in any case, use them. Including the header is thus pointless. - -/* Flags */ -/* Warning: serial.h defines some ASYNC_ flags, they say they are "only" - used in serial.c, but they are also used in all other serial drivers. - Make sure they don't clash with these here... */ -#define GS_TX_INTEN 0x00800000 -#define GS_RX_INTEN 0x00400000 -#define GS_ACTIVE 0x00200000 - -#define GS_TYPE_NORMAL 1 - -#define GS_DEBUG_FLUSH 0x00000001 -#define GS_DEBUG_BTR 0x00000002 -#define GS_DEBUG_TERMIOS 0x00000004 -#define GS_DEBUG_STUFF 0x00000008 -#define GS_DEBUG_CLOSE 0x00000010 -#define GS_DEBUG_FLOW 0x00000020 -#define GS_DEBUG_WRITE 0x00000040 - -#endif diff --git a/include/linux/istallion.h b/include/linux/istallion.h deleted file mode 100644 index ad700a6..0000000 --- a/include/linux/istallion.h +++ /dev/null @@ -1,123 +0,0 @@ -/*****************************************************************************/ - -/* - * istallion.h -- stallion intelligent multiport serial driver. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _ISTALLION_H -#define _ISTALLION_H -/*****************************************************************************/ - -/* - * Define important driver constants here. - */ -#define STL_MAXBRDS 4 -#define STL_MAXPANELS 4 -#define STL_MAXPORTS 64 -#define STL_MAXCHANS (STL_MAXPORTS + 1) -#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS) - - -/* - * Define a set of structures to hold all the board/panel/port info - * for our ports. These will be dynamically allocated as required at - * driver initialization time. - */ - -/* - * Port and board structures to hold status info about each object. - * The board structure contains pointers to structures for each port - * connected to it. Panels are not distinguished here, since - * communication with the slave board will always be on a per port - * basis. - */ -struct stliport { - unsigned long magic; - struct tty_port port; - unsigned int portnr; - unsigned int panelnr; - unsigned int brdnr; - unsigned long state; - unsigned int devnr; - int baud_base; - int custom_divisor; - int closing_wait; - int rc; - int argsize; - void *argp; - unsigned int rxmarkmsk; - wait_queue_head_t raw_wait; - struct asysigs asig; - unsigned long addr; - unsigned long rxoffset; - unsigned long txoffset; - unsigned long sigs; - unsigned long pflag; - unsigned int rxsize; - unsigned int txsize; - unsigned char reqbit; - unsigned char portidx; - unsigned char portbit; -}; - -/* - * Use a structure of function pointers to do board level operations. - * These include, enable/disable, paging shared memory, interrupting, etc. - */ -struct stlibrd { - unsigned long magic; - unsigned int brdnr; - unsigned int brdtype; - unsigned long state; - unsigned int nrpanels; - unsigned int nrports; - unsigned int nrdevs; - unsigned int iobase; - int iosize; - unsigned long memaddr; - void __iomem *membase; - unsigned long memsize; - int pagesize; - int hostoffset; - int slaveoffset; - int bitsize; - int enabval; - unsigned int panels[STL_MAXPANELS]; - int panelids[STL_MAXPANELS]; - void (*init)(struct stlibrd *brdp); - void (*enable)(struct stlibrd *brdp); - void (*reenable)(struct stlibrd *brdp); - void (*disable)(struct stlibrd *brdp); - void __iomem *(*getmemptr)(struct stlibrd *brdp, unsigned long offset, int line); - void (*intr)(struct stlibrd *brdp); - void (*reset)(struct stlibrd *brdp); - struct stliport *ports[STL_MAXPORTS]; -}; - - -/* - * Define MAGIC numbers used for above structures. - */ -#define STLI_PORTMAGIC 0xe671c7a1 -#define STLI_BOARDMAGIC 0x4bc6c825 - -/*****************************************************************************/ -#endif diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index daf4a3a..b7c8cdc 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -65,7 +65,6 @@ struct kbd_struct { extern int kbd_init(void); -extern unsigned char getledstate(void); extern void setledstate(struct kbd_struct *kbd, unsigned int led); extern int do_poke_blanked_console; @@ -145,16 +144,4 @@ void compute_shiftstate(void); extern unsigned int keymap_count; -/* console.c */ - -static inline void con_schedule_flip(struct tty_struct *t) -{ - unsigned long flags; - spin_lock_irqsave(&t->buf.lock, flags); - if (t->buf.tail != NULL) - t->buf.tail->commit = t->buf.tail->used; - spin_unlock_irqrestore(&t->buf.lock, flags); - schedule_work(&t->buf.work); -} - #endif diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 42d9e86..7f6fe6e 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -13,6 +13,14 @@ * Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com> */ +typedef enum { + KDB_REPEAT_NONE = 0, /* Do not repeat this command */ + KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */ + KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */ +} kdb_repeat_t; + +typedef int (*kdb_func_t)(int, const char **); + #ifdef CONFIG_KGDB_KDB #include <linux/init.h> #include <linux/sched.h> @@ -32,14 +40,6 @@ extern atomic_t kdb_event; #define KDB_MAXARGS 16 /* Maximum number of arguments to a function */ -typedef enum { - KDB_REPEAT_NONE = 0, /* Do not repeat this command */ - KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */ - KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */ -} kdb_repeat_t; - -typedef int (*kdb_func_t)(int, const char **); - /* KDB return codes from a command or internal kdb function */ #define KDB_NOTFOUND (-1) #define KDB_ARGCOUNT (-2) @@ -149,11 +149,14 @@ extern int kdb_register_repeat(char *, kdb_func_t, char *, char *, short, kdb_repeat_t); extern int kdb_unregister(char *); #else /* ! CONFIG_KGDB_KDB */ -#define kdb_printf(...) -#define kdb_init(x) -#define kdb_register(...) -#define kdb_register_repeat(...) -#define kdb_uregister(x) +static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; } +static inline void kdb_init(int level) {} +static inline int kdb_register(char *cmd, kdb_func_t func, char *usage, + char *help, short minlen) { return 0; } +static inline int kdb_register_repeat(char *cmd, kdb_func_t func, char *usage, + char *help, short minlen, + kdb_repeat_t repeat) { return 0; } +static inline int kdb_unregister(char *cmd) { return 0; } #endif /* CONFIG_KGDB_KDB */ enum { KDB_NOT_INITIALIZED, diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index c4d2fc1..4dff0c6 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -240,6 +240,7 @@ extern void kgdb_arch_late(void); * hardware breakpoints. * @correct_hw_break: Allow an architecture to specify how to correct the * hardware debug registers. + * @enable_nmi: Manage NMI-triggered entry to KGDB */ struct kgdb_arch { unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE]; @@ -252,6 +253,8 @@ struct kgdb_arch { void (*disable_hw_break)(struct pt_regs *regs); void (*remove_all_hw_break)(void); void (*correct_hw_break)(void); + + void (*enable_nmi)(bool on); }; /** @@ -283,6 +286,16 @@ extern struct kgdb_arch arch_kgdb_ops; extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs); +#ifdef CONFIG_SERIAL_KGDB_NMI +extern int kgdb_register_nmi_console(void); +extern int kgdb_unregister_nmi_console(void); +extern bool kgdb_nmi_poll_knock(void); +#else +static inline int kgdb_register_nmi_console(void) { return 0; } +static inline int kgdb_unregister_nmi_console(void) { return 0; } +static inline bool kgdb_nmi_poll_knock(void) { return 1; } +#endif + extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops); extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops); extern struct kgdb_io *dbg_io_ops; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 6b4565c..8d3c427 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1847,7 +1847,6 @@ #define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 #define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 #define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 -#define PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL 0x2530 #define PCI_VENDOR_ID_RADISYS 0x1331 diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h new file mode 100644 index 0000000..91648bf --- /dev/null +++ b/include/linux/platform_data/max310x.h @@ -0,0 +1,67 @@ +/* + * Maxim (Dallas) MAX3107/8 serial driver + * + * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * + * Based on max3100.c, by Christian Pellegrin <chripell@evolware.org> + * Based on max3110.c, by Feng Tang <feng.tang@intel.com> + * Based on max3107.c, by Aavamobile + * + * 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 of the License, or + * (at your option) any later version. + */ + +#ifndef _MAX310X_H_ +#define _MAX310X_H_ + +/* + * Example board initialization data: + * + * static struct max310x_pdata max3107_pdata = { + * .driver_flags = MAX310X_EXT_CLK, + * .uart_flags[0] = MAX310X_ECHO_SUPRESS | MAX310X_AUTO_DIR_CTRL, + * .frequency = 3686400, + * .gpio_base = -1, + * }; + * + * static struct spi_board_info spi_device_max3107[] = { + * { + * .modalias = "max3107", + * .irq = IRQ_EINT3, + * .bus_num = 1, + * .chip_select = 1, + * .platform_data = &max3107_pdata, + * }, + * }; + */ + +#define MAX310X_MAX_UARTS 1 + +/* MAX310X platform data structure */ +struct max310x_pdata { + /* Flags global to driver */ + const u8 driver_flags:2; +#define MAX310X_EXT_CLK (0x00000001) /* External clock enable */ +#define MAX310X_AUTOSLEEP (0x00000002) /* Enable AutoSleep mode */ + /* Flags global to UART port */ + const u8 uart_flags[MAX310X_MAX_UARTS]; +#define MAX310X_LOOPBACK (0x00000001) /* Loopback mode enable */ +#define MAX310X_ECHO_SUPRESS (0x00000002) /* Enable echo supress */ +#define MAX310X_AUTO_DIR_CTRL (0x00000004) /* Enable Auto direction + * control (RS-485) + */ + /* Frequency (extrenal clock or crystal) */ + const int frequency; + /* GPIO base number (can be negative) */ + const int gpio_base; + /* Called during startup */ + void (*init)(void); + /* Called before finish */ + void (*exit)(void); + /* Suspend callback */ + void (*suspend)(int do_suspend); +}; + +#endif diff --git a/include/linux/platform_data/sccnxp.h b/include/linux/platform_data/sccnxp.h new file mode 100644 index 0000000..7311ccd --- /dev/null +++ b/include/linux/platform_data/sccnxp.h @@ -0,0 +1,93 @@ +/* + * NXP (Philips) SCC+++(SCN+++) serial driver + * + * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * + * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de) + * + * 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 of the License, or + * (at your option) any later version. + */ + +#ifndef __SCCNXP_H +#define __SCCNXP_H + +#define SCCNXP_MAX_UARTS 2 + +/* Output lines */ +#define LINE_OP0 1 +#define LINE_OP1 2 +#define LINE_OP2 3 +#define LINE_OP3 4 +#define LINE_OP4 5 +#define LINE_OP5 6 +#define LINE_OP6 7 +#define LINE_OP7 8 + +/* Input lines */ +#define LINE_IP0 9 +#define LINE_IP1 10 +#define LINE_IP2 11 +#define LINE_IP3 12 +#define LINE_IP4 13 +#define LINE_IP5 14 +#define LINE_IP6 15 + +/* Signals */ +#define DTR_OP 0 /* DTR */ +#define RTS_OP 4 /* RTS */ +#define DSR_IP 8 /* DSR */ +#define CTS_IP 12 /* CTS */ +#define DCD_IP 16 /* DCD */ +#define RNG_IP 20 /* RNG */ + +#define DIR_OP 24 /* Special signal for control RS-485. + * Goes high when transmit, + * then goes low. + */ + +/* Routing control signal 'sig' to line 'line' */ +#define MCTRL_SIG(sig, line) ((line) << (sig)) + +/* + * Example board initialization data: + * + * static struct resource sc2892_resources[] = { + * DEFINE_RES_MEM(UART_PHYS_START, 0x10), + * DEFINE_RES_IRQ(IRQ_EXT2), + * }; + * + * static struct sccnxp_pdata sc2892_info = { + * .frequency = 3686400, + * .mctrl_cfg[0] = MCTRL_SIG(DIR_OP, LINE_OP0), + * .mctrl_cfg[1] = MCTRL_SIG(DIR_OP, LINE_OP1), + * }; + * + * static struct platform_device sc2892 = { + * .name = "sc2892", + * .id = -1, + * .resource = sc2892_resources, + * .num_resources = ARRAY_SIZE(sc2892_resources), + * .dev = { + * .platform_data = &sc2892_info, + * }, + * }; + */ + +/* SCCNXP platform data structure */ +struct sccnxp_pdata { + /* Frequency (extrenal clock or crystal) */ + int frequency; + /* Shift for A0 line */ + const u8 reg_shift; + /* Modem control lines configuration */ + const u32 mctrl_cfg[SCCNXP_MAX_UARTS]; + /* Called during startup */ + void (*init)(void); + /* Called before finish */ + void (*exit)(void); +}; + +#endif diff --git a/include/linux/sc26198.h b/include/linux/sc26198.h deleted file mode 100644 index 7ca35ab..0000000 --- a/include/linux/sc26198.h +++ /dev/null @@ -1,533 +0,0 @@ -/*****************************************************************************/ - -/* - * sc26198.h -- SC26198 UART hardware info. - * - * Copyright (C) 1995-1998 Stallion Technologies - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _SC26198_H -#define _SC26198_H -/*****************************************************************************/ - -/* - * Define the number of async ports per sc26198 uart device. - */ -#define SC26198_PORTS 8 - -/* - * Baud rate timing clocks. All derived from a master 14.7456 MHz clock. - */ -#define SC26198_MASTERCLOCK 14745600L -#define SC26198_DCLK (SC26198_MASTERCLOCK) -#define SC26198_CCLK (SC26198_MASTERCLOCK / 2) -#define SC26198_BCLK (SC26198_MASTERCLOCK / 4) - -/* - * Define internal FIFO sizes for the 26198 ports. - */ -#define SC26198_TXFIFOSIZE 16 -#define SC26198_RXFIFOSIZE 16 - -/*****************************************************************************/ - -/* - * Global register definitions. These registers are global to each 26198 - * device, not specific ports on it. - */ -#define TSTR 0x0d -#define GCCR 0x0f -#define ICR 0x1b -#define WDTRCR 0x1d -#define IVR 0x1f -#define BRGTRUA 0x84 -#define GPOSR 0x87 -#define GPOC 0x8b -#define UCIR 0x8c -#define CIR 0x8c -#define BRGTRUB 0x8d -#define GRXFIFO 0x8e -#define GTXFIFO 0x8e -#define GCCR2 0x8f -#define BRGTRLA 0x94 -#define GPOR 0x97 -#define GPOD 0x9b -#define BRGTCR 0x9c -#define GICR 0x9c -#define BRGTRLB 0x9d -#define GIBCR 0x9d -#define GITR 0x9f - -/* - * Per port channel registers. These are the register offsets within - * the port address space, so need to have the port address (0 to 7) - * inserted in bit positions 4:6. - */ -#define MR0 0x00 -#define MR1 0x01 -#define IOPCR 0x02 -#define BCRBRK 0x03 -#define BCRCOS 0x04 -#define BCRX 0x06 -#define BCRA 0x07 -#define XONCR 0x08 -#define XOFFCR 0x09 -#define ARCR 0x0a -#define RXCSR 0x0c -#define TXCSR 0x0e -#define MR2 0x80 -#define SR 0x81 -#define SCCR 0x81 -#define ISR 0x82 -#define IMR 0x82 -#define TXFIFO 0x83 -#define RXFIFO 0x83 -#define IPR 0x84 -#define IOPIOR 0x85 -#define XISR 0x86 - -/* - * For any given port calculate the address to use to access a specified - * register. This is only used for unusual access, mostly this is done - * through the assembler access routines. - */ -#define SC26198_PORTREG(port,reg) ((((port) & 0x07) << 4) | (reg)) - -/*****************************************************************************/ - -/* - * Global configuration control register bit definitions. - */ -#define GCCR_NOACK 0x00 -#define GCCR_IVRACK 0x02 -#define GCCR_IVRCHANACK 0x04 -#define GCCR_IVRTYPCHANACK 0x06 -#define GCCR_ASYNCCYCLE 0x00 -#define GCCR_SYNCCYCLE 0x40 - -/*****************************************************************************/ - -/* - * Mode register 0 bit definitions. - */ -#define MR0_ADDRNONE 0x00 -#define MR0_AUTOWAKE 0x01 -#define MR0_AUTODOZE 0x02 -#define MR0_AUTOWAKEDOZE 0x03 -#define MR0_SWFNONE 0x00 -#define MR0_SWFTX 0x04 -#define MR0_SWFRX 0x08 -#define MR0_SWFRXTX 0x0c -#define MR0_TXMASK 0x30 -#define MR0_TXEMPTY 0x00 -#define MR0_TXHIGH 0x10 -#define MR0_TXHALF 0x20 -#define MR0_TXRDY 0x00 -#define MR0_ADDRNT 0x00 -#define MR0_ADDRT 0x40 -#define MR0_SWFNT 0x00 -#define MR0_SWFT 0x80 - -/* - * Mode register 1 bit definitions. - */ -#define MR1_CS5 0x00 -#define MR1_CS6 0x01 -#define MR1_CS7 0x02 -#define MR1_CS8 0x03 -#define MR1_PAREVEN 0x00 -#define MR1_PARODD 0x04 -#define MR1_PARENB 0x00 -#define MR1_PARFORCE 0x08 -#define MR1_PARNONE 0x10 -#define MR1_PARSPECIAL 0x18 -#define MR1_ERRCHAR 0x00 -#define MR1_ERRBLOCK 0x20 -#define MR1_ISRUNMASKED 0x00 -#define MR1_ISRMASKED 0x40 -#define MR1_AUTORTS 0x80 - -/* - * Mode register 2 bit definitions. - */ -#define MR2_STOP1 0x00 -#define MR2_STOP15 0x01 -#define MR2_STOP2 0x02 -#define MR2_STOP916 0x03 -#define MR2_RXFIFORDY 0x00 -#define MR2_RXFIFOHALF 0x04 -#define MR2_RXFIFOHIGH 0x08 -#define MR2_RXFIFOFULL 0x0c -#define MR2_AUTOCTS 0x10 -#define MR2_TXRTS 0x20 -#define MR2_MODENORM 0x00 -#define MR2_MODEAUTOECHO 0x40 -#define MR2_MODELOOP 0x80 -#define MR2_MODEREMECHO 0xc0 - -/*****************************************************************************/ - -/* - * Baud Rate Generator (BRG) selector values. - */ -#define BRG_50 0x00 -#define BRG_75 0x01 -#define BRG_150 0x02 -#define BRG_200 0x03 -#define BRG_300 0x04 -#define BRG_450 0x05 -#define BRG_600 0x06 -#define BRG_900 0x07 -#define BRG_1200 0x08 -#define BRG_1800 0x09 -#define BRG_2400 0x0a -#define BRG_3600 0x0b -#define BRG_4800 0x0c -#define BRG_7200 0x0d -#define BRG_9600 0x0e -#define BRG_14400 0x0f -#define BRG_19200 0x10 -#define BRG_28200 0x11 -#define BRG_38400 0x12 -#define BRG_57600 0x13 -#define BRG_115200 0x14 -#define BRG_230400 0x15 -#define BRG_GIN0 0x16 -#define BRG_GIN1 0x17 -#define BRG_CT0 0x18 -#define BRG_CT1 0x19 -#define BRG_RX2TX316 0x1b -#define BRG_RX2TX31 0x1c - -#define SC26198_MAXBAUD 921600 - -/*****************************************************************************/ - -/* - * Command register command definitions. - */ -#define CR_NULL 0x04 -#define CR_ADDRNORMAL 0x0c -#define CR_RXRESET 0x14 -#define CR_TXRESET 0x1c -#define CR_CLEARRXERR 0x24 -#define CR_BREAKRESET 0x2c -#define CR_TXSTARTBREAK 0x34 -#define CR_TXSTOPBREAK 0x3c -#define CR_RTSON 0x44 -#define CR_RTSOFF 0x4c -#define CR_ADDRINIT 0x5c -#define CR_RXERRBLOCK 0x6c -#define CR_TXSENDXON 0x84 -#define CR_TXSENDXOFF 0x8c -#define CR_GANGXONSET 0x94 -#define CR_GANGXOFFSET 0x9c -#define CR_GANGXONINIT 0xa4 -#define CR_GANGXOFFINIT 0xac -#define CR_HOSTXON 0xb4 -#define CR_HOSTXOFF 0xbc -#define CR_CANCELXOFF 0xc4 -#define CR_ADDRRESET 0xdc -#define CR_RESETALLPORTS 0xf4 -#define CR_RESETALL 0xfc - -#define CR_RXENABLE 0x01 -#define CR_TXENABLE 0x02 - -/*****************************************************************************/ - -/* - * Channel status register. - */ -#define SR_RXRDY 0x01 -#define SR_RXFULL 0x02 -#define SR_TXRDY 0x04 -#define SR_TXEMPTY 0x08 -#define SR_RXOVERRUN 0x10 -#define SR_RXPARITY 0x20 -#define SR_RXFRAMING 0x40 -#define SR_RXBREAK 0x80 - -#define SR_RXERRS (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN) - -/*****************************************************************************/ - -/* - * Interrupt status register and interrupt mask register bit definitions. - */ -#define IR_TXRDY 0x01 -#define IR_RXRDY 0x02 -#define IR_RXBREAK 0x04 -#define IR_XONXOFF 0x10 -#define IR_ADDRRECOG 0x20 -#define IR_RXWATCHDOG 0x40 -#define IR_IOPORT 0x80 - -/*****************************************************************************/ - -/* - * Interrupt vector register field definitions. - */ -#define IVR_CHANMASK 0x07 -#define IVR_TYPEMASK 0x18 -#define IVR_CONSTMASK 0xc0 - -#define IVR_RXDATA 0x10 -#define IVR_RXBADDATA 0x18 -#define IVR_TXDATA 0x08 -#define IVR_OTHER 0x00 - -/*****************************************************************************/ - -/* - * BRG timer control register bit definitions. - */ -#define BRGCTCR_DISABCLK0 0x00 -#define BRGCTCR_ENABCLK0 0x08 -#define BRGCTCR_DISABCLK1 0x00 -#define BRGCTCR_ENABCLK1 0x80 - -#define BRGCTCR_0SCLK16 0x00 -#define BRGCTCR_0SCLK32 0x01 -#define BRGCTCR_0SCLK64 0x02 -#define BRGCTCR_0SCLK128 0x03 -#define BRGCTCR_0X1 0x04 -#define BRGCTCR_0X12 0x05 -#define BRGCTCR_0IO1A 0x06 -#define BRGCTCR_0GIN0 0x07 - -#define BRGCTCR_1SCLK16 0x00 -#define BRGCTCR_1SCLK32 0x10 -#define BRGCTCR_1SCLK64 0x20 -#define BRGCTCR_1SCLK128 0x30 -#define BRGCTCR_1X1 0x40 -#define BRGCTCR_1X12 0x50 -#define BRGCTCR_1IO1B 0x60 -#define BRGCTCR_1GIN1 0x70 - -/*****************************************************************************/ - -/* - * Watch dog timer enable register. - */ -#define WDTRCR_ENABALL 0xff - -/*****************************************************************************/ - -/* - * XON/XOFF interrupt status register. - */ -#define XISR_TXCHARMASK 0x03 -#define XISR_TXCHARNORMAL 0x00 -#define XISR_TXWAIT 0x01 -#define XISR_TXXOFFPEND 0x02 -#define XISR_TXXONPEND 0x03 - -#define XISR_TXFLOWMASK 0x0c -#define XISR_TXNORMAL 0x00 -#define XISR_TXSTOPPEND 0x04 -#define XISR_TXSTARTED 0x08 -#define XISR_TXSTOPPED 0x0c - -#define XISR_RXFLOWMASK 0x30 -#define XISR_RXFLOWNONE 0x00 -#define XISR_RXXONSENT 0x10 -#define XISR_RXXOFFSENT 0x20 - -#define XISR_RXXONGOT 0x40 -#define XISR_RXXOFFGOT 0x80 - -/*****************************************************************************/ - -/* - * Current interrupt register. - */ -#define CIR_TYPEMASK 0xc0 -#define CIR_TYPEOTHER 0x00 -#define CIR_TYPETX 0x40 -#define CIR_TYPERXGOOD 0x80 -#define CIR_TYPERXBAD 0xc0 - -#define CIR_RXDATA 0x80 -#define CIR_RXBADDATA 0x40 -#define CIR_TXDATA 0x40 - -#define CIR_CHANMASK 0x07 -#define CIR_CNTMASK 0x38 - -#define CIR_SUBTYPEMASK 0x38 -#define CIR_SUBNONE 0x00 -#define CIR_SUBCOS 0x08 -#define CIR_SUBADDR 0x10 -#define CIR_SUBXONXOFF 0x18 -#define CIR_SUBBREAK 0x28 - -/*****************************************************************************/ - -/* - * Global interrupting channel register. - */ -#define GICR_CHANMASK 0x07 - -/*****************************************************************************/ - -/* - * Global interrupting byte count register. - */ -#define GICR_COUNTMASK 0x0f - -/*****************************************************************************/ - -/* - * Global interrupting type register. - */ -#define GITR_RXMASK 0xc0 -#define GITR_RXNONE 0x00 -#define GITR_RXBADDATA 0x80 -#define GITR_RXGOODDATA 0xc0 -#define GITR_TXDATA 0x20 - -#define GITR_SUBTYPEMASK 0x07 -#define GITR_SUBNONE 0x00 -#define GITR_SUBCOS 0x01 -#define GITR_SUBADDR 0x02 -#define GITR_SUBXONXOFF 0x03 -#define GITR_SUBBREAK 0x05 - -/*****************************************************************************/ - -/* - * Input port change register. - */ -#define IPR_CTS 0x01 -#define IPR_DTR 0x02 -#define IPR_RTS 0x04 -#define IPR_DCD 0x08 -#define IPR_CTSCHANGE 0x10 -#define IPR_DTRCHANGE 0x20 -#define IPR_RTSCHANGE 0x40 -#define IPR_DCDCHANGE 0x80 - -#define IPR_CHANGEMASK 0xf0 - -/*****************************************************************************/ - -/* - * IO port interrupt and output register. - */ -#define IOPR_CTS 0x01 -#define IOPR_DTR 0x02 -#define IOPR_RTS 0x04 -#define IOPR_DCD 0x08 -#define IOPR_CTSCOS 0x10 -#define IOPR_DTRCOS 0x20 -#define IOPR_RTSCOS 0x40 -#define IOPR_DCDCOS 0x80 - -/*****************************************************************************/ - -/* - * IO port configuration register. - */ -#define IOPCR_SETCTS 0x00 -#define IOPCR_SETDTR 0x04 -#define IOPCR_SETRTS 0x10 -#define IOPCR_SETDCD 0x00 - -#define IOPCR_SETSIGS (IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD) - -/*****************************************************************************/ - -/* - * General purpose output select register. - */ -#define GPORS_TXC1XA 0x08 -#define GPORS_TXC16XA 0x09 -#define GPORS_RXC16XA 0x0a -#define GPORS_TXC16XB 0x0b -#define GPORS_GPOR3 0x0c -#define GPORS_GPOR2 0x0d -#define GPORS_GPOR1 0x0e -#define GPORS_GPOR0 0x0f - -/*****************************************************************************/ - -/* - * General purpose output register. - */ -#define GPOR_0 0x01 -#define GPOR_1 0x02 -#define GPOR_2 0x04 -#define GPOR_3 0x08 - -/*****************************************************************************/ - -/* - * General purpose output clock register. - */ -#define GPORC_0NONE 0x00 -#define GPORC_0GIN0 0x01 -#define GPORC_0GIN1 0x02 -#define GPORC_0IO3A 0x02 - -#define GPORC_1NONE 0x00 -#define GPORC_1GIN0 0x04 -#define GPORC_1GIN1 0x08 -#define GPORC_1IO3C 0x0c - -#define GPORC_2NONE 0x00 -#define GPORC_2GIN0 0x10 -#define GPORC_2GIN1 0x20 -#define GPORC_2IO3E 0x20 - -#define GPORC_3NONE 0x00 -#define GPORC_3GIN0 0x40 -#define GPORC_3GIN1 0x80 -#define GPORC_3IO3G 0xc0 - -/*****************************************************************************/ - -/* - * General purpose output data register. - */ -#define GPOD_0MASK 0x03 -#define GPOD_0SET1 0x00 -#define GPOD_0SET0 0x01 -#define GPOD_0SETR0 0x02 -#define GPOD_0SETIO3B 0x03 - -#define GPOD_1MASK 0x0c -#define GPOD_1SET1 0x00 -#define GPOD_1SET0 0x04 -#define GPOD_1SETR0 0x08 -#define GPOD_1SETIO3D 0x0c - -#define GPOD_2MASK 0x30 -#define GPOD_2SET1 0x00 -#define GPOD_2SET0 0x10 -#define GPOD_2SETR0 0x20 -#define GPOD_2SETIO3F 0x30 - -#define GPOD_3MASK 0xc0 -#define GPOD_3SET1 0x00 -#define GPOD_3SET0 0x40 -#define GPOD_3SETR0 0x80 -#define GPOD_3SETIO3H 0xc0 - -/*****************************************************************************/ -#endif diff --git a/include/linux/serial.h b/include/linux/serial.h index 90e9f98..861e51d 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -12,9 +12,12 @@ #include <linux/types.h> +#include <linux/tty_flags.h> + #ifdef __KERNEL__ #include <asm/page.h> + /* * Counters of the input lines (CTS, DSR, RI, CD) interrupts */ @@ -83,89 +86,11 @@ struct serial_struct { #define SERIAL_IO_HUB6 1 #define SERIAL_IO_MEM 2 -struct serial_uart_config { - char *name; - int dfl_xmit_fifo_size; - int flags; -}; - #define UART_CLEAR_FIFO 0x01 #define UART_USE_FIFO 0x02 #define UART_STARTECH 0x04 #define UART_NATSEMI 0x08 -/* - * Definitions for async_struct (and serial_struct) flags field - * - * Define ASYNCB_* for convenient use with {test,set,clear}_bit. - */ -#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes - * on the callout port */ -#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ -#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ -#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ -#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ -#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ -#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ -#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during - * autoconfiguration */ -#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ -#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ -#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ -#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ -#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ -#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ -#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety - * checks. Note: can be dangerous! */ -#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ -#define ASYNCB_LAST_USER 15 - -/* Internal flags used only by kernel */ -#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ -#define ASYNCB_SUSPENDED 30 /* Serial port is suspended */ -#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */ -#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */ -#define ASYNCB_CLOSING 27 /* Serial port is closing */ -#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */ -#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */ -#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */ -#define ASYNCB_CONS_FLOW 23 /* flow control for console */ -#define ASYNCB_FIRST_KERNEL 22 - -#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) -#define ASYNC_SUSPENDED (1U << ASYNCB_SUSPENDED) -#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) -#define ASYNC_SAK (1U << ASYNCB_SAK) -#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) -#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) -#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) -#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) -#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) -#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) -#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) -#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) -#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD) -#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) -#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) -#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) -#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) - -#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) -#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ - ASYNC_LOW_LATENCY) -#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) -#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) -#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) - -#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED) -#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE) -#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF) -#define ASYNC_CLOSING (1U << ASYNCB_CLOSING) -#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW) -#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD) -#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ) -#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW) -#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1)) /* * Multiport serial configuration structure --- external structure diff --git a/include/linux/serial167.h b/include/linux/serial167.h deleted file mode 100644 index 59c81b7..0000000 --- a/include/linux/serial167.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * serial167.h - * - * Richard Hirst [richard@sleepie.demon.co.uk] - * - * Based on cyclades.h - */ - -struct cyclades_monitor { - unsigned long int_count; - unsigned long char_count; - unsigned long char_max; - unsigned long char_last; -}; - -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -struct cyclades_port { - int magic; - int type; - int card; - int line; - int flags; /* defined in tty.h */ - struct tty_struct *tty; - int read_status_mask; - int timeout; - int xmit_fifo_size; - int cor1,cor2,cor3,cor4,cor5,cor6,cor7; - int tbpr,tco,rbpr,rco; - int ignore_status_mask; - int close_delay; - int IER; /* Interrupt Enable Register */ - unsigned long last_active; - int count; /* # of fd on device */ - int x_char; /* to be pushed out ASAP */ - int x_break; - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - int default_threshold; - int default_timeout; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - struct cyclades_monitor mon; -}; - -#define CYCLADES_MAGIC 0x4359 - -#define CYGETMON 0x435901 -#define CYGETTHRESH 0x435902 -#define CYSETTHRESH 0x435903 -#define CYGETDEFTHRESH 0x435904 -#define CYSETDEFTHRESH 0x435905 -#define CYGETTIMEOUT 0x435906 -#define CYSETTIMEOUT 0x435907 -#define CYGETDEFTIMEOUT 0x435908 -#define CYSETDEFTIMEOUT 0x435909 - -#define CyMaxChipsPerCard 1 - -/**** cd2401 registers ****/ - -#define CyGFRCR (0x81) -#define CyCCR (0x13) -#define CyCLR_CHAN (0x40) -#define CyINIT_CHAN (0x20) -#define CyCHIP_RESET (0x10) -#define CyENB_XMTR (0x08) -#define CyDIS_XMTR (0x04) -#define CyENB_RCVR (0x02) -#define CyDIS_RCVR (0x01) -#define CyCAR (0xee) -#define CyIER (0x11) -#define CyMdmCh (0x80) -#define CyRxExc (0x20) -#define CyRxData (0x08) -#define CyTxMpty (0x02) -#define CyTxRdy (0x01) -#define CyLICR (0x26) -#define CyRISR (0x89) -#define CyTIMEOUT (0x80) -#define CySPECHAR (0x70) -#define CyOVERRUN (0x08) -#define CyPARITY (0x04) -#define CyFRAME (0x02) -#define CyBREAK (0x01) -#define CyREOIR (0x84) -#define CyTEOIR (0x85) -#define CyMEOIR (0x86) -#define CyNOTRANS (0x08) -#define CyRFOC (0x30) -#define CyRDR (0xf8) -#define CyTDR (0xf8) -#define CyMISR (0x8b) -#define CyRISR (0x89) -#define CyTISR (0x8a) -#define CyMSVR1 (0xde) -#define CyMSVR2 (0xdf) -#define CyDSR (0x80) -#define CyDCD (0x40) -#define CyCTS (0x20) -#define CyDTR (0x02) -#define CyRTS (0x01) -#define CyRTPRL (0x25) -#define CyRTPRH (0x24) -#define CyCOR1 (0x10) -#define CyPARITY_NONE (0x00) -#define CyPARITY_E (0x40) -#define CyPARITY_O (0xC0) -#define Cy_5_BITS (0x04) -#define Cy_6_BITS (0x05) -#define Cy_7_BITS (0x06) -#define Cy_8_BITS (0x07) -#define CyCOR2 (0x17) -#define CyETC (0x20) -#define CyCtsAE (0x02) -#define CyCOR3 (0x16) -#define Cy_1_STOP (0x02) -#define Cy_2_STOP (0x04) -#define CyCOR4 (0x15) -#define CyREC_FIFO (0x0F) /* Receive FIFO threshold */ -#define CyCOR5 (0x14) -#define CyCOR6 (0x18) -#define CyCOR7 (0x07) -#define CyRBPR (0xcb) -#define CyRCOR (0xc8) -#define CyTBPR (0xc3) -#define CyTCOR (0xc0) -#define CySCHR1 (0x1f) -#define CySCHR2 (0x1e) -#define CyTPR (0xda) -#define CyPILR1 (0xe3) -#define CyPILR2 (0xe0) -#define CyPILR3 (0xe1) -#define CyCMR (0x1b) -#define CyASYNC (0x02) -#define CyLICR (0x26) -#define CyLIVR (0x09) -#define CySCRL (0x23) -#define CySCRH (0x22) -#define CyTFTC (0x80) - - -/* max number of chars in the FIFO */ - -#define CyMAX_CHAR_FIFO 12 - -/***************************************************************************/ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index a416e92..c174c90 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -65,11 +65,38 @@ enum { * platform device. Using these will make your driver * dependent on the 8250 driver. */ -struct uart_port; -struct uart_8250_port; + +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); +}; int serial8250_register_8250_port(struct uart_8250_port *); -int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); void serial8250_suspend_port(int line); void serial8250_resume_port(int line); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 0253c20..f9b22ec 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -48,7 +48,8 @@ #define PORT_TEGRA 20 /* NVIDIA Tegra internal UART */ #define PORT_XR17D15X 21 /* Exar XR17D15x UART */ #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ -#define PORT_MAX_8250 22 /* max port ID */ +#define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */ +#define PORT_MAX_8250 23 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed @@ -193,8 +194,8 @@ /* SH-SCI */ #define PORT_SCIFB 93 -/* MAX3107 */ -#define PORT_MAX3107 94 +/* MAX310X */ +#define PORT_MAX310X 94 /* High Speed UART for Medfield */ #define PORT_MFD 95 @@ -274,6 +275,7 @@ struct uart_ops { int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsigned int, unsigned long); #ifdef CONFIG_CONSOLE_POLL + int (*poll_init)(struct uart_port *); void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); #endif diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h index 8ce70d7..5ed325e 100644 --- a/include/linux/serial_reg.h +++ b/include/linux/serial_reg.h @@ -40,6 +40,10 @@ #define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ +#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ +#define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ +#define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ + #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ #define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ diff --git a/include/linux/stallion.h b/include/linux/stallion.h deleted file mode 100644 index 336af33c..0000000 --- a/include/linux/stallion.h +++ /dev/null @@ -1,147 +0,0 @@ -/*****************************************************************************/ - -/* - * stallion.h -- stallion multiport serial driver. - * - * Copyright (C) 1996-1998 Stallion Technologies - * Copyright (C) 1994-1996 Greg Ungerer. - * - * 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 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; 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. - */ - -/*****************************************************************************/ -#ifndef _STALLION_H -#define _STALLION_H -/*****************************************************************************/ - -/* - * Define important driver constants here. - */ -#define STL_MAXBRDS 4 -#define STL_MAXPANELS 4 -#define STL_MAXBANKS 8 -#define STL_PORTSPERPANEL 16 -#define STL_MAXPORTS 64 -#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS) - - -/* - * Define a set of structures to hold all the board/panel/port info - * for our ports. These will be dynamically allocated as required. - */ - -/* - * Define a ring queue structure for each port. This will hold the - * TX data waiting to be output. Characters are fed into this buffer - * from the line discipline (or even direct from user space!) and - * then fed into the UARTs during interrupts. Will use a classic ring - * queue here for this. The good thing about this type of ring queue - * is that the head and tail pointers can be updated without interrupt - * protection - since "write" code only needs to change the head, and - * interrupt code only needs to change the tail. - */ -struct stlrq { - char *buf; - char *head; - char *tail; -}; - -/* - * Port, panel and board structures to hold status info about each. - * The board structure contains pointers to structures for each panel - * connected to it, and in turn each panel structure contains pointers - * for each port structure for each port on that panel. Note that - * the port structure also contains the board and panel number that it - * is associated with, this makes it (fairly) easy to get back to the - * board/panel info for a port. - */ -struct stlport { - unsigned long magic; - struct tty_port port; - unsigned int portnr; - unsigned int panelnr; - unsigned int brdnr; - int ioaddr; - int uartaddr; - unsigned int pagenr; - unsigned long istate; - int baud_base; - int custom_divisor; - int close_delay; - int closing_wait; - int openwaitcnt; - int brklen; - unsigned int sigs; - unsigned int rxignoremsk; - unsigned int rxmarkmsk; - unsigned int imr; - unsigned int crenable; - unsigned long clk; - unsigned long hwid; - void *uartp; - comstats_t stats; - struct stlrq tx; -}; - -struct stlpanel { - unsigned long magic; - unsigned int panelnr; - unsigned int brdnr; - unsigned int pagenr; - unsigned int nrports; - int iobase; - void *uartp; - void (*isr)(struct stlpanel *panelp, unsigned int iobase); - unsigned int hwid; - unsigned int ackmask; - struct stlport *ports[STL_PORTSPERPANEL]; -}; - -struct stlbrd { - unsigned long magic; - unsigned int brdnr; - unsigned int brdtype; - unsigned int state; - unsigned int nrpanels; - unsigned int nrports; - unsigned int nrbnks; - int irq; - int irqtype; - int (*isr)(struct stlbrd *brdp); - unsigned int ioaddr1; - unsigned int ioaddr2; - unsigned int iosize1; - unsigned int iosize2; - unsigned int iostatus; - unsigned int ioctrl; - unsigned int ioctrlval; - unsigned int hwid; - unsigned long clk; - unsigned int bnkpageaddr[STL_MAXBANKS]; - unsigned int bnkstataddr[STL_MAXBANKS]; - struct stlpanel *bnk2panel[STL_MAXBANKS]; - struct stlpanel *panels[STL_MAXPANELS]; -}; - - -/* - * Define MAGIC numbers used for above structures. - */ -#define STL_PORTMAGIC 0x5a7182c9 -#define STL_PANELMAGIC 0x7ef621a1 -#define STL_BOARDMAGIC 0xa2267f52 - -/*****************************************************************************/ -#endif diff --git a/include/linux/tty.h b/include/linux/tty.h index 9f47ab5..1509b86 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -43,6 +43,7 @@ #include <linux/tty_driver.h> #include <linux/tty_ldisc.h> #include <linux/mutex.h> +#include <linux/tty_flags.h> @@ -103,28 +104,28 @@ struct tty_bufhead { #define TTY_PARITY 3 #define TTY_OVERRUN 4 -#define INTR_CHAR(tty) ((tty)->termios->c_cc[VINTR]) -#define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT]) -#define ERASE_CHAR(tty) ((tty)->termios->c_cc[VERASE]) -#define KILL_CHAR(tty) ((tty)->termios->c_cc[VKILL]) -#define EOF_CHAR(tty) ((tty)->termios->c_cc[VEOF]) -#define TIME_CHAR(tty) ((tty)->termios->c_cc[VTIME]) -#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN]) -#define SWTC_CHAR(tty) ((tty)->termios->c_cc[VSWTC]) -#define START_CHAR(tty) ((tty)->termios->c_cc[VSTART]) -#define STOP_CHAR(tty) ((tty)->termios->c_cc[VSTOP]) -#define SUSP_CHAR(tty) ((tty)->termios->c_cc[VSUSP]) -#define EOL_CHAR(tty) ((tty)->termios->c_cc[VEOL]) -#define REPRINT_CHAR(tty) ((tty)->termios->c_cc[VREPRINT]) -#define DISCARD_CHAR(tty) ((tty)->termios->c_cc[VDISCARD]) -#define WERASE_CHAR(tty) ((tty)->termios->c_cc[VWERASE]) -#define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT]) -#define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2]) - -#define _I_FLAG(tty, f) ((tty)->termios->c_iflag & (f)) -#define _O_FLAG(tty, f) ((tty)->termios->c_oflag & (f)) -#define _C_FLAG(tty, f) ((tty)->termios->c_cflag & (f)) -#define _L_FLAG(tty, f) ((tty)->termios->c_lflag & (f)) +#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) +#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) +#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) +#define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL]) +#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) +#define TIME_CHAR(tty) ((tty)->termios.c_cc[VTIME]) +#define MIN_CHAR(tty) ((tty)->termios.c_cc[VMIN]) +#define SWTC_CHAR(tty) ((tty)->termios.c_cc[VSWTC]) +#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) +#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) +#define SUSP_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) +#define EOL_CHAR(tty) ((tty)->termios.c_cc[VEOL]) +#define REPRINT_CHAR(tty) ((tty)->termios.c_cc[VREPRINT]) +#define DISCARD_CHAR(tty) ((tty)->termios.c_cc[VDISCARD]) +#define WERASE_CHAR(tty) ((tty)->termios.c_cc[VWERASE]) +#define LNEXT_CHAR(tty) ((tty)->termios.c_cc[VLNEXT]) +#define EOL2_CHAR(tty) ((tty)->termios.c_cc[VEOL2]) + +#define _I_FLAG(tty, f) ((tty)->termios.c_iflag & (f)) +#define _O_FLAG(tty, f) ((tty)->termios.c_oflag & (f)) +#define _C_FLAG(tty, f) ((tty)->termios.c_cflag & (f)) +#define _L_FLAG(tty, f) ((tty)->termios.c_lflag & (f)) #define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK) #define I_BRKINT(tty) _I_FLAG((tty), BRKINT) @@ -268,10 +269,11 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ - struct ktermios *termios, *termios_locked; + struct ktermios termios, termios_locked; struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; struct pid *pgrp; /* Protected by ctrl lock */ @@ -410,6 +412,10 @@ extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); extern struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev); +extern struct device *tty_register_device_attr(struct tty_driver *driver, + unsigned index, struct device *device, + void *drvdata, + const struct attribute_group **attr_grp); extern void tty_unregister_device(struct tty_driver *driver, unsigned index); extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen); @@ -423,7 +429,6 @@ extern void tty_unthrottle(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); extern void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty); -extern void tty_shutdown(struct tty_struct *tty); extern void tty_free_termios(struct tty_struct *tty); extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); @@ -497,6 +502,15 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) extern void tty_port_init(struct tty_port *port); +extern void tty_port_link_device(struct tty_port *port, + struct tty_driver *driver, unsigned index); +extern struct device *tty_port_register_device(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device); +extern struct device *tty_port_register_device_attr(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device, void *drvdata, + const struct attribute_group **attr_grp); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); extern void tty_port_put(struct tty_port *port); @@ -508,6 +522,12 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return port; } +/* If the cts flow control is enabled, return true. */ +static inline bool tty_port_cts_enabled(struct tty_port *port) +{ + return port->flags & ASYNC_CTS_FLOW; +} + extern struct tty_struct *tty_port_tty_get(struct tty_port *port); extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_port_carrier_raised(struct tty_port *port); @@ -521,6 +541,8 @@ extern int tty_port_close_start(struct tty_port *port, extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); extern void tty_port_close(struct tty_port *port, struct tty_struct *tty, struct file *filp); +extern int tty_port_install(struct tty_port *port, struct tty_driver *driver, + struct tty_struct *tty); extern int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp); static inline int tty_port_users(struct tty_port *port) @@ -605,8 +627,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* tty_mutex.c */ /* functions for preparation of BKL removal */ -extern void __lockfunc tty_lock(void) __acquires(tty_lock); -extern void __lockfunc tty_unlock(void) __releases(tty_lock); +extern void __lockfunc tty_lock(struct tty_struct *tty); +extern void __lockfunc tty_unlock(struct tty_struct *tty); +extern void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2); +extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2); /* * this shall be called only from where BTM is held (like close) @@ -621,9 +647,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock); static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, long timeout) { - tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ tty_wait_until_sent(tty, timeout); - tty_lock(); + tty_lock(tty); } /* @@ -638,16 +664,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, * * Do not use in new code. */ -#define wait_event_interruptible_tty(wq, condition) \ +#define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) { \ - __wait_event_interruptible_tty(wq, condition, __ret); \ + __wait_event_interruptible_tty(tty, wq, condition, __ret); \ } \ __ret; \ }) -#define __wait_event_interruptible_tty(wq, condition, ret) \ +#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ \ @@ -656,9 +682,9 @@ do { \ if (condition) \ break; \ if (!signal_pending(current)) { \ - tty_unlock(); \ + tty_unlock(tty); \ schedule(); \ - tty_lock(); \ + tty_lock(tty); \ continue; \ } \ ret = -ERESTARTSYS; \ diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 6e6dbb7..dd976cf 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -45,14 +45,9 @@ * * void (*shutdown)(struct tty_struct * tty); * - * This routine is called synchronously when a particular tty device - * is closed for the last time freeing up the resources. - * Note that tty_shutdown() is not called if ops->shutdown is defined. - * This means one is responsible to take care of calling ops->remove (e.g. - * via tty_driver_remove_tty) and releasing tty->termios. - * Note that this hook may be called from *all* the contexts where one - * uses tty refcounting (e.g. tty_port_tty_get). - * + * This routine is called under the tty lock when a particular tty device + * is closed for the last time. It executes before the tty resources + * are freed so may execute while another function holds a tty kref. * * void (*cleanup)(struct tty_struct * tty); * @@ -294,18 +289,18 @@ struct tty_operations { struct tty_driver { int magic; /* magic number for this structure */ struct kref kref; /* Reference management */ - struct cdev cdev; + struct cdev *cdevs; struct module *owner; const char *driver_name; const char *name; int name_base; /* offset of printed name */ int major; /* major device number */ int minor_start; /* start of minor device number */ - int num; /* number of devices allocated */ + unsigned int num; /* number of devices allocated */ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ - int flags; /* tty driver flags */ + unsigned long flags; /* tty driver flags */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ @@ -313,6 +308,7 @@ struct tty_driver { * Pointer to the tty data structures */ struct tty_struct **ttys; + struct tty_port **ports; struct ktermios **termios; void *driver_state; @@ -326,7 +322,8 @@ struct tty_driver { extern struct list_head tty_drivers; -extern struct tty_driver *__alloc_tty_driver(int lines, struct module *owner); +extern struct tty_driver *__tty_alloc_driver(unsigned int lines, + struct module *owner, unsigned long flags); extern void put_tty_driver(struct tty_driver *driver); extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); @@ -334,7 +331,21 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line); extern void tty_driver_kref_put(struct tty_driver *driver); -#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE) +/* Use TTY_DRIVER_* flags below */ +#define tty_alloc_driver(lines, flags) \ + __tty_alloc_driver(lines, THIS_MODULE, flags) + +/* + * DEPRECATED Do not use this in new code, use tty_alloc_driver instead. + * (And change the return value checks.) + */ +static inline struct tty_driver *alloc_tty_driver(unsigned int lines) +{ + struct tty_driver *ret = tty_alloc_driver(lines, 0); + if (IS_ERR(ret)) + return NULL; + return ret; +} static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) { @@ -380,6 +391,14 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) * the requested timeout to the caller instead of using a simple * on/off interface. * + * TTY_DRIVER_DYNAMIC_ALLOC -- do not allocate structures which are + * needed per line for this driver as it would waste memory. + * The driver will take care. + * + * TTY_DRIVER_UNNUMBERED_NODE -- do not create numbered /dev nodes. In + * other words create /dev/ttyprintk and not /dev/ttyprintk0. + * Applicable only when a driver for a single tty device is + * being allocated. */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 @@ -387,6 +406,8 @@ static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) #define TTY_DRIVER_DYNAMIC_DEV 0x0008 #define TTY_DRIVER_DEVPTS_MEM 0x0010 #define TTY_DRIVER_HARDWARE_BREAK 0x0020 +#define TTY_DRIVER_DYNAMIC_ALLOC 0x0040 +#define TTY_DRIVER_UNNUMBERED_NODE 0x0080 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 diff --git a/include/linux/tty_flags.h b/include/linux/tty_flags.h new file mode 100644 index 0000000..eefcb48 --- /dev/null +++ b/include/linux/tty_flags.h @@ -0,0 +1,78 @@ +#ifndef _LINUX_TTY_FLAGS_H +#define _LINUX_TTY_FLAGS_H + +/* + * Definitions for async_struct (and serial_struct) flags field also + * shared by the tty_port flags structures. + * + * Define ASYNCB_* for convenient use with {test,set,clear}_bit. + */ +#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes + * on the callout port */ +#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ +#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ +#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ +#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ +#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ +#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ +#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during + * autoconfiguration */ +#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ +#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ +#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ +#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ +#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ +#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ +#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety + * checks. Note: can be dangerous! */ +#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ +#define ASYNCB_LAST_USER 15 + +/* Internal flags used only by kernel */ +#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ +#define ASYNCB_SUSPENDED 30 /* Serial port is suspended */ +#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */ +#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */ +#define ASYNCB_CLOSING 27 /* Serial port is closing */ +#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */ +#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */ +#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */ +#define ASYNCB_CONS_FLOW 23 /* flow control for console */ +#define ASYNCB_FIRST_KERNEL 22 + +#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) +#define ASYNC_SUSPENDED (1U << ASYNCB_SUSPENDED) +#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) +#define ASYNC_SAK (1U << ASYNCB_SAK) +#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) +#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) +#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) +#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) +#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) +#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) +#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) +#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) +#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD) +#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) +#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) +#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) +#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) + +#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) +#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ + ASYNC_LOW_LATENCY) +#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) +#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) +#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) + +#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED) +#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE) +#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF) +#define ASYNC_CLOSING (1U << ASYNCB_CLOSING) +#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW) +#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD) +#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ) +#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW) +#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1)) + +#endif diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 59ba38bc..80ffde3 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -52,21 +52,16 @@ /* Same for payload size. See qos.c for the smallest max data size */ #define IRCOMM_TTY_DATA_UNINITIALISED (64 - IRCOMM_TTY_HDR_UNINITIALISED) -/* Those are really defined in include/linux/serial.h - Jean II */ -#define ASYNC_B_INITIALIZED 31 /* Serial port was initialized */ -#define ASYNC_B_NORMAL_ACTIVE 29 /* Normal device is active */ -#define ASYNC_B_CLOSING 27 /* Serial port is closing */ - /* * IrCOMM TTY driver state */ struct ircomm_tty_cb { irda_queue_t queue; /* Must be first */ + struct tty_port port; magic_t magic; int state; /* Connect state */ - struct tty_struct *tty; struct ircomm_cb *ircomm; /* IrCOMM layer instance */ struct sk_buff *tx_skb; /* Transmit buffer */ @@ -80,7 +75,6 @@ struct ircomm_tty_cb { LOCAL_FLOW flow; /* IrTTP flow status */ int line; - unsigned long flags; __u8 dlsap_sel; __u8 slsap_sel; @@ -97,19 +91,10 @@ struct ircomm_tty_cb { void *skey; void *ckey; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; struct timer_list watchdog_timer; struct work_struct tqueue; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ - - int open_count; - int blocked_open; /* # of blocked opens */ - /* Protect concurent access to : - * o self->open_count * o self->ctrl_skb * o self->tx_skb * Maybe other things may gain to be protected as well... diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0557f24..17e073c 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -672,6 +672,10 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) { struct kgdb_state kgdb_var; struct kgdb_state *ks = &kgdb_var; + int ret = 0; + + if (arch_kgdb_ops.enable_nmi) + arch_kgdb_ops.enable_nmi(0); ks->cpu = raw_smp_processor_id(); ks->ex_vector = evector; @@ -681,11 +685,15 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) ks->linux_regs = regs; if (kgdb_reenter_check(ks)) - return 0; /* Ouch, double exception ! */ + goto out; /* Ouch, double exception ! */ if (kgdb_info[ks->cpu].enter_kgdb != 0) - return 0; + goto out; - return kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); + ret = kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); +out: + if (arch_kgdb_ops.enable_nmi) + arch_kgdb_ops.enable_nmi(1); + return ret; } int kgdb_nmicallback(int cpu, void *regs) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 31df170..1261dc7 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -21,6 +21,7 @@ #include <linux/smp.h> #include <linux/utsname.h> #include <linux/vmalloc.h> +#include <linux/atomic.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/init.h> @@ -2107,6 +2108,32 @@ static int kdb_dmesg(int argc, const char **argv) return 0; } #endif /* CONFIG_PRINTK */ + +/* Make sure we balance enable/disable calls, must disable first. */ +static atomic_t kdb_nmi_disabled; + +static int kdb_disable_nmi(int argc, const char *argv[]) +{ + if (atomic_read(&kdb_nmi_disabled)) + return 0; + atomic_set(&kdb_nmi_disabled, 1); + arch_kgdb_ops.enable_nmi(0); + return 0; +} + +static int kdb_param_enable_nmi(const char *val, const struct kernel_param *kp) +{ + if (!atomic_add_unless(&kdb_nmi_disabled, -1, 0)) + return -EINVAL; + arch_kgdb_ops.enable_nmi(1); + return 0; +} + +static const struct kernel_param_ops kdb_param_ops_enable_nmi = { + .set = kdb_param_enable_nmi, +}; +module_param_cb(enable_nmi, &kdb_param_ops_enable_nmi, NULL, 0600); + /* * kdb_cpu - This function implements the 'cpu' command. * cpu [<cpunum>] @@ -2851,6 +2878,10 @@ static void __init kdb_inittab(void) kdb_register_repeat("dmesg", kdb_dmesg, "[lines]", "Display syslog buffer", 0, KDB_REPEAT_NONE); #endif + if (arch_kgdb_ops.enable_nmi) { + kdb_register_repeat("disable_nmi", kdb_disable_nmi, "", + "Disable NMI entry to KDB", 0, KDB_REPEAT_NONE); + } kdb_register_repeat("defcmd", kdb_defcmd, "name \"usage\" \"help\"", "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE); kdb_register_repeat("kill", kdb_kill, "<-signal> <pid>", diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 56f1823..ccc2487 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -278,8 +278,8 @@ out: if (err < 0) goto free; - dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL); - + dev->tty_dev = tty_port_register_device(&dev->port, rfcomm_tty_driver, + dev->id, NULL); if (IS_ERR(dev->tty_dev)) { err = PTR_ERR(dev->tty_dev); list_del(&dev->list); @@ -705,9 +705,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); @@ -861,7 +861,7 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned l static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - struct ktermios *new = tty->termios; + struct ktermios *new = &tty->termios; int old_baud_rate = tty_termios_baud_rate(old); int new_baud_rate = tty_termios_baud_rate(new); diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c index 8b915f3..3089391 100644 --- a/net/irda/ircomm/ircomm_param.c +++ b/net/irda/ircomm/ircomm_param.c @@ -99,7 +99,6 @@ pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 }; */ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) { - struct tty_struct *tty; unsigned long flags; struct sk_buff *skb; int count; @@ -109,10 +108,6 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - tty = self->tty; - if (!tty) - return 0; - /* Make sure we don't send parameters for raw mode */ if (self->service_type == IRCOMM_3_WIRE_RAW) return 0; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 6b9d5a0..95a3a7a 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -52,6 +52,8 @@ #include <net/irda/ircomm_tty_attach.h> #include <net/irda/ircomm_tty.h> +static int ircomm_tty_install(struct tty_driver *driver, + struct tty_struct *tty); static int ircomm_tty_open(struct tty_struct *tty, struct file *filp); static void ircomm_tty_close(struct tty_struct * tty, struct file *filp); static int ircomm_tty_write(struct tty_struct * tty, @@ -82,6 +84,7 @@ static struct tty_driver *driver; static hashbin_t *ircomm_tty = NULL; static const struct tty_operations ops = { + .install = ircomm_tty_install, .open = ircomm_tty_open, .close = ircomm_tty_close, .write = ircomm_tty_write, @@ -104,6 +107,35 @@ static const struct tty_operations ops = { #endif /* CONFIG_PROC_FS */ }; +static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise) +{ + struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, + port); + /* + * Here, we use to lock those two guys, but as ircomm_param_request() + * does it itself, I don't see the point (and I see the deadlock). + * Jean II + */ + if (raise) + self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR; + else + self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR); + + ircomm_param_request(self, IRCOMM_DTE, TRUE); +} + +static int ircomm_port_carrier_raised(struct tty_port *port) +{ + struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, + port); + return self->settings.dce & IRCOMM_CD; +} + +static const struct tty_port_operations ircomm_port_ops = { + .dtr_rts = ircomm_port_raise_dtr_rts, + .carrier_raised = ircomm_port_carrier_raised, +}; + /* * Function ircomm_tty_init() * @@ -194,7 +226,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); /* Check if already open */ - if (test_and_set_bit(ASYNC_B_INITIALIZED, &self->flags)) { + if (test_and_set_bit(ASYNCB_INITIALIZED, &self->port.flags)) { IRDA_DEBUG(2, "%s(), already open so break out!\n", __func__ ); return 0; } @@ -231,7 +263,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) return 0; err: - clear_bit(ASYNC_B_INITIALIZED, &self->flags); + clear_bit(ASYNCB_INITIALIZED, &self->port.flags); return ret; } @@ -242,72 +274,62 @@ err: * */ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, - struct file *filp) + struct tty_struct *tty, struct file *filp) { + struct tty_port *port = &self->port; DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; - struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); - tty = self->tty; - /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ /* nonblock mode is set or port is not enabled */ - self->flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; IRDA_DEBUG(1, "%s(), O_NONBLOCK requested!\n", __func__ ); return 0; } - if (tty->termios->c_cflag & CLOCAL) { + if (tty->termios.c_cflag & CLOCAL) { IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __func__ ); do_clocal = 1; } /* Wait for carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, self->open_count is dropped by one, so that + * this loop, port->count is dropped by one, so that * mgsl_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&self->open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count ); + __FILE__, __LINE__, tty->driver->name, port->count); - /* As far as I can see, we protect open_count - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); + spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { extra_count = 1; - self->open_count--; + port->count--; } - spin_unlock_irqrestore(&self->spinlock, flags); - self->blocked_open++; + spin_unlock_irqrestore(&port->lock, flags); + port->blocked_open++; while (1) { - if (tty->termios->c_cflag & CBAUD) { - /* Here, we use to lock those two guys, but - * as ircomm_param_request() does it itself, - * I don't see the point (and I see the deadlock). - * Jean II */ - self->settings.dte |= IRCOMM_RTS + IRCOMM_DTR; - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } + if (tty->termios.c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || - !test_bit(ASYNC_B_INITIALIZED, &self->flags)) { - retval = (self->flags & ASYNC_HUP_NOTIFY) ? + !test_bit(ASYNCB_INITIALIZED, &port->flags)) { + retval = (port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; break; } @@ -317,8 +339,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, * specified, we cannot return before the IrCOMM link is * ready */ - if (!test_bit(ASYNC_B_CLOSING, &self->flags) && - (do_clocal || (self->settings.dce & IRCOMM_CD)) && + if (!test_bit(ASYNCB_CLOSING, &port->flags) && + (do_clocal || tty_port_carrier_raised(port)) && self->state == IRCOMM_TTY_READY) { break; @@ -330,46 +352,36 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, } IRDA_DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count ); + __FILE__, __LINE__, tty->driver->name, port->count); schedule(); } __set_current_state(TASK_RUNNING); - remove_wait_queue(&self->open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (extra_count) { /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); - self->open_count++; - spin_unlock_irqrestore(&self->spinlock, flags); + spin_lock_irqsave(&port->lock, flags); + port->count++; + spin_unlock_irqrestore(&port->lock, flags); } - self->blocked_open--; + port->blocked_open--; IRDA_DEBUG(1, "%s(%d):block_til_ready after blocking on %s open_count=%d\n", - __FILE__,__LINE__, tty->driver->name, self->open_count); + __FILE__, __LINE__, tty->driver->name, port->count); if (!retval) - self->flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return retval; } -/* - * Function ircomm_tty_open (tty, filp) - * - * This routine is called when a particular tty device is opened. This - * routine is mandatory; if this routine is not filled in, the attempted - * open will fail with ENODEV. - */ -static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) + +static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) { struct ircomm_tty_cb *self; unsigned int line = tty->index; - unsigned long flags; - int ret; - - IRDA_DEBUG(2, "%s()\n", __func__ ); /* Check if instance already exists */ self = hashbin_lock_find(ircomm_tty, line, NULL); @@ -381,6 +393,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) return -ENOMEM; } + tty_port_init(&self->port); + self->port.ops = &ircomm_port_ops; self->magic = IRCOMM_TTY_MAGIC; self->flow = FLOW_STOP; @@ -388,13 +402,9 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) INIT_WORK(&self->tqueue, ircomm_tty_do_softint); self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED; self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED; - self->close_delay = 5*HZ/10; - self->closing_wait = 30*HZ; /* Init some important stuff */ init_timer(&self->watchdog_timer); - init_waitqueue_head(&self->open_wait); - init_waitqueue_head(&self->close_wait); spin_lock_init(&self->spinlock); /* @@ -404,31 +414,48 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) * * Note this is completely usafe and doesn't work properly */ - tty->termios->c_iflag = 0; - tty->termios->c_oflag = 0; + tty->termios.c_iflag = 0; + tty->termios.c_oflag = 0; /* Insert into hash */ hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); } - /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); - self->open_count++; - tty->driver_data = self; - self->tty = tty; - spin_unlock_irqrestore(&self->spinlock, flags); + return tty_port_install(&self->port, driver, tty); +} + +/* + * Function ircomm_tty_open (tty, filp) + * + * This routine is called when a particular tty device is opened. This + * routine is mandatory; if this routine is not filled in, the attempted + * open will fail with ENODEV. + */ +static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct ircomm_tty_cb *self = tty->driver_data; + unsigned long flags; + int ret; + + IRDA_DEBUG(2, "%s()\n", __func__ ); + + /* ++ is not atomic, so this should be protected - Jean II */ + spin_lock_irqsave(&self->port.lock, flags); + self->port.count++; + spin_unlock_irqrestore(&self->port.lock, flags); + tty_port_tty_set(&self->port, tty); IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, - self->line, self->open_count); + self->line, self->port.count); /* Not really used by us, but lets do it anyway */ - self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - test_bit(ASYNC_B_CLOSING, &self->flags)) { + test_bit(ASYNCB_CLOSING, &self->port.flags)) { /* Hm, why are we blocking on ASYNC_CLOSING if we * do return -EAGAIN/-ERESTARTSYS below anyway? @@ -438,14 +465,15 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) * probably better sleep uninterruptible? */ - if (wait_event_interruptible(self->close_wait, !test_bit(ASYNC_B_CLOSING, &self->flags))) { + if (wait_event_interruptible(self->port.close_wait, + !test_bit(ASYNCB_CLOSING, &self->port.flags))) { IRDA_WARNING("%s - got signal while blocking on ASYNC_CLOSING!\n", __func__); return -ERESTARTSYS; } #ifdef SERIAL_DO_RESTART - return (self->flags & ASYNC_HUP_NOTIFY) ? + return (self->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; #else return -EAGAIN; @@ -453,7 +481,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) } /* Check if this is a "normal" ircomm device, or an irlpt device */ - if (line < 0x10) { + if (self->line < 0x10) { self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE; self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */ /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */ @@ -469,7 +497,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) if (ret) return ret; - ret = ircomm_tty_block_til_ready(self, filp); + ret = ircomm_tty_block_til_ready(self, tty, filp); if (ret) { IRDA_DEBUG(2, "%s(), returning after block_til_ready with %d\n", __func__ , @@ -489,81 +517,22 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; + struct tty_port *port = &self->port; IRDA_DEBUG(0, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - spin_lock_irqsave(&self->spinlock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&self->spinlock, flags); - - IRDA_DEBUG(0, "%s(), returning 1\n", __func__ ); - return; - } - - if ((tty->count == 1) && (self->open_count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - IRDA_DEBUG(0, "%s(), bad serial port count; " - "tty->count is 1, state->count is %d\n", __func__ , - self->open_count); - self->open_count = 1; - } - - if (--self->open_count < 0) { - IRDA_ERROR("%s(), bad serial port count for ttys%d: %d\n", - __func__, self->line, self->open_count); - self->open_count = 0; - } - if (self->open_count) { - spin_unlock_irqrestore(&self->spinlock, flags); - - IRDA_DEBUG(0, "%s(), open count > 0\n", __func__ ); + if (tty_port_close_start(port, tty, filp) == 0) return; - } - - /* Hum... Should be test_and_set_bit ??? - Jean II */ - set_bit(ASYNC_B_CLOSING, &self->flags); - - /* We need to unlock here (we were unlocking at the end of this - * function), because tty_wait_until_sent() may schedule. - * I don't know if the rest should be protected somehow, - * so someone should check. - Jean II */ - spin_unlock_irqrestore(&self->spinlock, flags); - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, self->closing_wait); ircomm_tty_shutdown(self); tty_driver_flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - self->tty = NULL; - if (self->blocked_open) { - if (self->close_delay) - schedule_timeout_interruptible(self->close_delay); - wake_up_interruptible(&self->open_wait); - } - - self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&self->close_wait); + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); } /* @@ -606,7 +575,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) if (!self || self->magic != IRCOMM_TTY_MAGIC) return; - tty = self->tty; + tty = tty_port_tty_get(&self->port); if (!tty) return; @@ -627,7 +596,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) } if (tty->hw_stopped) - return; + goto put; /* Unlink transmit buffer */ spin_lock_irqsave(&self->spinlock, flags); @@ -646,6 +615,8 @@ static void ircomm_tty_do_softint(struct work_struct *work) /* Check if user (still) wants to be waken up */ tty_wakeup(tty); +put: + tty_kref_put(tty); } /* @@ -880,7 +851,7 @@ static void ircomm_tty_throttle(struct tty_struct *tty) ircomm_tty_send_xchar(tty, STOP_CHAR(tty)); /* Hardware flow control? */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { self->settings.dte &= ~IRCOMM_RTS; self->settings.dte |= IRCOMM_DELTA_RTS; @@ -912,7 +883,7 @@ static void ircomm_tty_unthrottle(struct tty_struct *tty) } /* Using hardware flow control? */ - if (tty->termios->c_cflag & CRTSCTS) { + if (tty->termios.c_cflag & CRTSCTS) { self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS); ircomm_param_request(self, IRCOMM_DTE, TRUE); @@ -955,7 +926,7 @@ static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) IRDA_DEBUG(0, "%s()\n", __func__ ); - if (!test_and_clear_bit(ASYNC_B_INITIALIZED, &self->flags)) + if (!test_and_clear_bit(ASYNCB_INITIALIZED, &self->port.flags)) return; ircomm_tty_detach_cable(self); @@ -994,6 +965,7 @@ static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) static void ircomm_tty_hangup(struct tty_struct *tty) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + struct tty_port *port = &self->port; unsigned long flags; IRDA_DEBUG(0, "%s()\n", __func__ ); @@ -1004,14 +976,17 @@ static void ircomm_tty_hangup(struct tty_struct *tty) /* ircomm_tty_flush_buffer(tty); */ ircomm_tty_shutdown(self); - /* I guess we need to lock here - Jean II */ - spin_lock_irqsave(&self->spinlock, flags); - self->flags &= ~ASYNC_NORMAL_ACTIVE; - self->tty = NULL; - self->open_count = 0; - spin_unlock_irqrestore(&self->spinlock, flags); + spin_lock_irqsave(&port->lock, flags); + port->flags &= ~ASYNC_NORMAL_ACTIVE; + if (port->tty) { + set_bit(TTY_IO_ERROR, &port->tty->flags); + tty_kref_put(port->tty); + } + port->tty = NULL; + port->count = 0; + spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&port->open_wait); } /* @@ -1071,20 +1046,20 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - tty = self->tty; + tty = tty_port_tty_get(&self->port); status = self->settings.dce; if (status & IRCOMM_DCE_DELTA_ANY) { /*wake_up_interruptible(&self->delta_msr_wait);*/ } - if ((self->flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) { + if ((self->port.flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) { IRDA_DEBUG(2, "%s(), ircomm%d CD now %s...\n", __func__ , self->line, (status & IRCOMM_CD) ? "on" : "off"); if (status & IRCOMM_CD) { - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } else { IRDA_DEBUG(2, "%s(), Doing serial hangup..\n", __func__ ); @@ -1092,10 +1067,10 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) tty_hangup(tty); /* Hangup will remote the tty, so better break out */ - return; + goto put; } } - if (self->flags & ASYNC_CTS_FLOW) { + if (tty && tty_port_cts_enabled(&self->port)) { if (tty->hw_stopped) { if (status & IRCOMM_CTS) { IRDA_DEBUG(2, @@ -1103,10 +1078,10 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) tty->hw_stopped = 0; /* Wake up processes blocked on open */ - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); schedule_work(&self->tqueue); - return; + goto put; } } else { if (!(status & IRCOMM_CTS)) { @@ -1116,6 +1091,8 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) } } } +put: + tty_kref_put(tty); } /* @@ -1128,6 +1105,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); @@ -1135,7 +1113,8 @@ static int ircomm_tty_data_indication(void *instance, void *sap, IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); - if (!self->tty) { + tty = tty_port_tty_get(&self->port); + if (!tty) { IRDA_DEBUG(0, "%s(), no tty!\n", __func__ ); return 0; } @@ -1146,7 +1125,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * Devices like WinCE can do this, and since they don't send any * params, we can just as well declare the hardware for running. */ - if (self->tty->hw_stopped && (self->flow == FLOW_START)) { + if (tty->hw_stopped && (self->flow == FLOW_START)) { IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ ); ircomm_param_request(self, IRCOMM_POLL, TRUE); @@ -1159,8 +1138,9 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * Use flip buffer functions since the code may be called from interrupt * context */ - tty_insert_flip_string(self->tty, skb->data, skb->len); - tty_flip_buffer_push(self->tty); + tty_insert_flip_string(tty, skb->data, skb->len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); /* No need to kfree_skb - see ircomm_ttp_data_indication() */ @@ -1211,12 +1191,13 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - tty = self->tty; + tty = tty_port_tty_get(&self->port); switch (cmd) { case FLOW_START: IRDA_DEBUG(2, "%s(), hw start!\n", __func__ ); - tty->hw_stopped = 0; + if (tty) + tty->hw_stopped = 0; /* ircomm_tty_do_softint will take care of the rest */ schedule_work(&self->tqueue); @@ -1224,15 +1205,19 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, default: /* If we get here, something is very wrong, better stop */ case FLOW_STOP: IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ ); - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; break; } + + tty_kref_put(tty); self->flow = cmd; } #ifdef CONFIG_PROC_FS static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) { + struct tty_struct *tty; char sep; seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); @@ -1328,40 +1313,43 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) seq_puts(m, "Flags:"); sep = ' '; - if (self->flags & ASYNC_CTS_FLOW) { + if (tty_port_cts_enabled(&self->port)) { seq_printf(m, "%cASYNC_CTS_FLOW", sep); sep = '|'; } - if (self->flags & ASYNC_CHECK_CD) { + if (self->port.flags & ASYNC_CHECK_CD) { seq_printf(m, "%cASYNC_CHECK_CD", sep); sep = '|'; } - if (self->flags & ASYNC_INITIALIZED) { + if (self->port.flags & ASYNC_INITIALIZED) { seq_printf(m, "%cASYNC_INITIALIZED", sep); sep = '|'; } - if (self->flags & ASYNC_LOW_LATENCY) { + if (self->port.flags & ASYNC_LOW_LATENCY) { seq_printf(m, "%cASYNC_LOW_LATENCY", sep); sep = '|'; } - if (self->flags & ASYNC_CLOSING) { + if (self->port.flags & ASYNC_CLOSING) { seq_printf(m, "%cASYNC_CLOSING", sep); sep = '|'; } - if (self->flags & ASYNC_NORMAL_ACTIVE) { + if (self->port.flags & ASYNC_NORMAL_ACTIVE) { seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep); sep = '|'; } seq_putc(m, '\n'); seq_printf(m, "Role: %s\n", self->client ? "client" : "server"); - seq_printf(m, "Open count: %d\n", self->open_count); + seq_printf(m, "Open count: %d\n", self->port.count); seq_printf(m, "Max data size: %d\n", self->max_data_size); seq_printf(m, "Max header size: %d\n", self->max_header_size); - if (self->tty) + tty = tty_port_tty_get(&self->port); + if (tty) { seq_printf(m, "Hardware: %s\n", - self->tty->hw_stopped ? "Stopped" : "Running"); + tty->hw_stopped ? "Stopped" : "Running"); + tty_kref_put(tty); + } } static int ircomm_tty_proc_show(struct seq_file *m, void *v) diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index b65d66e..edab393 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -130,6 +130,8 @@ static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, */ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) { + struct tty_struct *tty; + IRDA_DEBUG(0, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return -1;); @@ -142,7 +144,11 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) } /* Make sure nobody tries to write before the link is up */ - self->tty->hw_stopped = 1; + tty = tty_port_tty_get(&self->port); + if (tty) { + tty->hw_stopped = 1; + tty_kref_put(tty); + } ircomm_tty_ias_register(self); @@ -398,23 +404,26 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - if (!self->tty) + tty = tty_port_tty_get(&self->port); + if (!tty) return; /* This will stop control data transfers */ self->flow = FLOW_STOP; /* Stop data transfers */ - self->tty->hw_stopped = 1; + tty->hw_stopped = 1; ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, NULL); + tty_kref_put(tty); } /* @@ -550,12 +559,15 @@ void ircomm_tty_connect_indication(void *instance, void *sap, */ void ircomm_tty_link_established(struct ircomm_tty_cb *self) { + struct tty_struct *tty; + IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - if (!self->tty) + tty = tty_port_tty_get(&self->port); + if (!tty) return; del_timer(&self->watchdog_timer); @@ -566,19 +578,22 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) * will have to wait for the peer device (DCE) to raise the CTS * line. */ - if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) { + if (tty_port_cts_enabled(&self->port) && + ((self->settings.dce & IRCOMM_CTS) == 0)) { IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); - return; + goto put; } else { IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ ); - self->tty->hw_stopped = 0; + tty->hw_stopped = 0; /* Wake up processes blocked on open */ - wake_up_interruptible(&self->open_wait); + wake_up_interruptible(&self->port.open_wait); } schedule_work(&self->tqueue); +put: + tty_kref_put(tty); } /* @@ -977,14 +992,17 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); ircomm_tty_start_watchdog_timer(self, 3*HZ); - if (self->flags & ASYNC_CHECK_CD) { + if (self->port.flags & ASYNC_CHECK_CD) { /* Drop carrier */ self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); } else { + struct tty_struct *tty = tty_port_tty_get(&self->port); IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); - if (self->tty) - tty_hangup(self->tty); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } } break; default: diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index d0667d6..b343f50 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -52,17 +52,18 @@ * Change speed of the driver. If the remote device is a DCE, then this * should make it change the speed of its serial port */ -static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) +static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, + struct tty_struct *tty) { unsigned int cflag, cval; int baud; IRDA_DEBUG(2, "%s()\n", __func__ ); - if (!self->tty || !self->tty->termios || !self->ircomm) + if (!self->ircomm) return; - cflag = self->tty->termios->c_cflag; + cflag = tty->termios.c_cflag; /* byte size and parity */ switch (cflag & CSIZE) { @@ -81,7 +82,7 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) cval |= IRCOMM_PARITY_EVEN; /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(self->tty); + baud = tty_get_baud_rate(tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ @@ -90,19 +91,19 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { - self->flags |= ASYNC_CTS_FLOW; + self->port.flags |= ASYNC_CTS_FLOW; self->settings.flow_control |= IRCOMM_RTS_CTS_IN; /* This got me. Bummer. Jean II */ if (self->service_type == IRCOMM_3_WIRE_RAW) IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __func__); } else { - self->flags &= ~ASYNC_CTS_FLOW; + self->port.flags &= ~ASYNC_CTS_FLOW; self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; } if (cflag & CLOCAL) - self->flags &= ~ASYNC_CHECK_CD; + self->port.flags &= ~ASYNC_CHECK_CD; else - self->flags |= ASYNC_CHECK_CD; + self->port.flags |= ASYNC_CHECK_CD; #if 0 /* * Set up parity check flag @@ -148,18 +149,18 @@ void ircomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned int cflag = tty->termios->c_cflag; + unsigned int cflag = tty->termios.c_cflag; IRDA_DEBUG(2, "%s()\n", __func__ ); if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(tty->termios->c_iflag) == + (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { return; } - ircomm_tty_change_speed(self); + ircomm_tty_change_speed(self, tty); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && @@ -172,7 +173,7 @@ void ircomm_tty_set_termios(struct tty_struct *tty, if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { self->settings.dte |= IRCOMM_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || + if (!(tty->termios.c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { self->settings.dte |= IRCOMM_RTS; } @@ -181,7 +182,7 @@ void ircomm_tty_set_termios(struct tty_struct *tty, /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; ircomm_tty_start(tty); @@ -270,10 +271,10 @@ static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, memset(&info, 0, sizeof(info)); info.line = self->line; - info.flags = self->flags; + info.flags = self->port.flags; info.baud_base = self->settings.data_rate; - info.close_delay = self->close_delay; - info.closing_wait = self->closing_wait; + info.close_delay = self->port.close_delay; + info.closing_wait = self->port.closing_wait; /* For compatibility */ info.type = PORT_16550A; |