]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.2.14pre17 2.2.14pre17
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:34 +0000 (15:20 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:34 +0000 (15:20 -0500)
o Fix the IDE problem (Mark Lord)
o Clean the AHA1542 scribble ptr on free (Steven S Dick)
o Fix bridge for non x86 platforms (Jack Howarth)
o SIS900 update (Ollie Lho)
o Fix missing argument (Willy Tarreau)
o Update tulip fixes (add debug only checks) (Wolfgan Walter)
o Fix technical posix violation in sigprocmask (Cristian Gafton)
handling
o Fix AF_UNIX sysctl bug (Rusty Russel[l])
o NCPfs time stamp handling fixes (Wolfram Pienkoss)
o VFAT bogus EINVAL cases (Wolfram Pienkoss)
o Moxa serial drivers (Moxa)

26 files changed:
Documentation/README.moxa [new file with mode: 0644]
Documentation/devices.txt
Documentation/knfsd.txt [new file with mode: 0644]
Documentation/moxa-smartio [new file with mode: 0644]
Documentation/networking/sis900.txt
Makefile
drivers/block/ide-probe.c
drivers/block/ide.c
drivers/char/Config.in
drivers/char/Makefile
drivers/char/moxa.c [new file with mode: 0644]
drivers/char/mxser.c [new file with mode: 0644]
drivers/char/tty_io.c
drivers/net/sis900.c
drivers/net/sis900.h
drivers/net/tulip.c
drivers/scsi/aha1542.c
drivers/telephony/ixj.h
drivers/telephony/ixjuser.h [deleted file]
fs/ncpfs/inode.c
fs/vfat/namei.c
include/linux/ixjuser.h [new file with mode: 0644]
include/linux/tty.h
kernel/signal.c
net/bridge/br.c
net/unix/sysctl_net_unix.c

diff --git a/Documentation/README.moxa b/Documentation/README.moxa
new file mode 100644 (file)
index 0000000..20600ad
--- /dev/null
@@ -0,0 +1,18 @@
+ ===================================================================   
+ Release Note of Linux Driver for Moxa's C104/C168/CI-104J
+ ===================================================================   
+
+ -------------------------------------------------------------------
+ Ver. 1.1                                             Sep.  1, 1999
+ -------------------------------------------------------------------
+ 1. Improved:
+   a. Static driver (kernel) and dynamic driver (loadable module) 
+      modes are supported.
+   b. Multiple Smartio PCI series boards sharing the same IRQ 
+      supported.
+
+ -------------------------------------------------------------------
+ Ver. 1.0                                             Feb  17, 1997
+ -------------------------------------------------------------------
+ 1. Newly release.
+
index 6b51fc50a8ec6e4fbcd38deeeccd97ca8ff8a1bd..8255edec4dd8cae0f054a61bda3603196a6f1b1d 100644 (file)
@@ -1,8 +1,7 @@
-
                       LINUX ALLOCATED DEVICES
             Maintained by H. Peter Anvin <hpa@zytor.com>
 
-                  Last revised: December 16, 1999
+                  Last revised: December 21, 1999
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
@@ -15,7 +14,7 @@ ftp://ftp.kernel.org/pub/linux/docs/device-list/.  The LaTeX version
 of this document is no longer maintained.
 
 This document is included by reference into the Filesystem Hierarchy
-Standard (FHS).  The FHS is available from http://www.pathname.com/fhs/.
+Standard (FHS).         The FHS is available from http://www.pathname.com/fhs/.
 
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
 platform only. Allocations marked (68k/Atari) apply to Linux/68k on
@@ -199,7 +198,7 @@ Your cooperation is appreciated.
 
                Older versions of the Linux kernel used this major
                number for BSD PTY devices.  As of Linux 2.1.115, this
-               is no longer supported.  Use major numbers 2 and 3.
+               is no longer supported.  Use major numbers 2 and 3.
 
   5 char       Alternate TTY devices
                  0 = /dev/tty          Current TTY device
@@ -305,6 +304,8 @@ Your cooperation is appreciated.
                  7 = /dev/amigamouse1  Second Amiga mouse
                  8 = /dev/smouse       Simple serial mouse driver
                  9 = /dev/pc110pad     IBM PC-110 digitizer pad
+                10 = /dev/adbmouse     Apple Desktop Bus mouse
+                11 = /dev/vrtpanel     Vr41xx embedded touch panel
                128 = /dev/beep         Fancy beep device
                129 = /dev/modreq       Kernel module load request
                130 = /dev/watchdog     Watchdog timer port
@@ -353,6 +354,7 @@ Your cooperation is appreciated.
                177 = /dev/cbm          Serial CBM bus
                178 = /dev/jsflash      JavaStation OS flash SIMM
                179 = /dev/xsvc         High-speed shared-mem/semaphore service
+               180 = /dev/vrbuttons    Vr41xx button input device
                240-255                 Reserved for local use
 
  11 char       Raw keyboard device
@@ -570,7 +572,7 @@ Your cooperation is appreciated.
                 16 = /dev/zqft0        Unit 0, rewind-on-close, compression
                 17 = /dev/zqft1        Unit 1, rewind-on-close, compression
                 18 = /dev/zqft2        Unit 2, rewind-on-close, compression
-                19 = /dev/zqtf3        Unit 3, rewind-on-close, compression
+                19 = /dev/zqft3        Unit 3, rewind-on-close, compression
                 20 = /dev/nzqft0       Unit 0, no rewind-on-close, compression
                 21 = /dev/nzqft1       Unit 1, no rewind-on-close, compression
                 22 = /dev/nzqft2       Unit 2, no rewind-on-close, compression
@@ -1356,8 +1358,8 @@ Your cooperation is appreciated.
 
 
  89 char       I2C bus interface
-                 0 = /dev/i2c        First I2C adapter
-                 1 = /dev/i2c        Second I2C adapter
+                 0 = /dev/i2c-0        First I2C adapter
+                 1 = /dev/i2c-1        Second I2C adapter
                    ...
 
     block      Eighth IDE hard disk/CD-ROM interface
@@ -1735,9 +1737,9 @@ Your cooperation is appreciated.
                 63 = /dev/ttyCH63      AT/PCI-Fast board 3, port 15
 
 165 char       Chase Research AT/PCI-Fast serial card - alternate devices
-                 0 = /dev/cuch0        Callout device corresponding to ttyCH0
+                 0 = /dev/cuch0        Callout device for ttyCH0
                    ...
-                63 = /dev/cuch63       Callout device corresponding to ttyCH63
+                63 = /dev/cuch63       Callout device for ttyCH63
 
 166 char       ACM USB modems
                  0 = /dev/ttyACM0      First ACM modem
@@ -1796,13 +1798,13 @@ Your cooperation is appreciated.
                    ...
 
 177 char       TI PCILynx memory spaces
-                 0 = /dev/pcilynx/aux0  AUX space of first PCILynx card
+                 0 = /dev/pcilynx/aux0  AUX space of first PCILynx card
                    ...
                 15 = /dev/pcilynx/aux15 AUX space of 16th PCILynx card
-                16 = /dev/pcilynx/rom0  ROM space of first PCILynx card
+                16 = /dev/pcilynx/rom0  ROM space of first PCILynx card
                    ...
                 31 = /dev/pcilynx/rom15 ROM space of 16th PCILynx card
-                32 = /dev/pcilynx/ram0  RAM space of first PCILynx card
+                32 = /dev/pcilynx/ram0  RAM space of first PCILynx card
                    ...
                 47 = /dev/pcilynx/ram15 RAM space of 16th PCILynx card
 
@@ -1836,7 +1838,7 @@ Your cooperation is appreciated.
                    ...
 
 182 char       Picture Elements THR2 binarizer
-                 0 = /dev/pethr0       First THR2 board
+                 0 = /dev/pethr0       First THR2 board
                  1 = /dev/pethr1       Second THR2 board
                    ...
 
@@ -1870,11 +1872,16 @@ Your cooperation is appreciated.
                    ...
 
 189 char       USB serial converters - alternate devices
-                 0 = /dev/cuusb0       Callout device corresponding to ttyUSB0
-                 1 = /dev/cuusb1       Callout device corresponding to ttyUSB1
+                 0 = /dev/cuusb0       Callout device for ttyUSB0
+                 1 = /dev/cuusb1       Callout device for ttyUSB1
+                   ...
+
+190 char       Kansas City tracker/tuner card
+                 0 = /dev/kctt0        First KCT/T card
+                 1 = /dev/kctt1        Second KCT/T card
                    ...
 
-190-239                UNALLOCATED
+191-239                UNALLOCATED
 
 240-254                LOCAL/EXPERIMENTAL USE
 
@@ -1983,7 +1990,7 @@ monitor.  Virtual consoles are named /dev/tty#, with numbering
 starting at /dev/tty1; /dev/tty0 is the current virtual console.
 /dev/tty0 is the device that should be used to access the system video
 card on those architectures for which the frame buffer devices
-(/dev/fb*) are not applicable.  Do not use /dev/console
+(/dev/fb*) are not applicable. Do not use /dev/console
 for this purpose.
 
 The console device, /dev/console, is the device to which system
@@ -2000,10 +2007,10 @@ Serial ports are RS-232 serial ports and any device which simulates
 one, either in hardware (such as internal modems) or in software (such
 as the ISDN driver.)  Under Linux, each serial ports has two device
 names, the primary or callin device and the alternate or callout one.
-Each kind of device is indicated by a different letter.  For any
+Each kind of device is indicated by a different letter.         For any
 letter X, the names of the devices are /dev/ttyX# and /dev/cux#,
 respectively; for historical reasons, /dev/ttyS# and /dev/ttyC#
-correspond to /dev/cua# and /dev/cub#.  In the future, it should be
+correspond to /dev/cua# and /dev/cub#. In the future, it should be
 expected that multiple letters will be used; all letters will be upper
 case for the "tty" device (e.g. /dev/ttyDP#) and lower case for the
 "cu" device (e.g. /dev/cudp#).
@@ -2017,7 +2024,7 @@ support for serial ports.  Their use is deprecated, and they may be
 removed from a future version of Linux.
 
 Arbitration of serial ports is provided by the use of lock files with
-the names /var/lock/LCK..ttyX#.  The contents of the lock file should
+the names /var/lock/LCK..ttyX#.         The contents of the lock file should
 be the PID of the locking process as an ASCII number.
 
 It is common practice to install links such as /dev/modem
@@ -2025,7 +2032,7 @@ which point to serial ports.  In order to ensure proper locking in the
 presence of these links, it is recommended that software chase
 symlinks and lock all possible names; additionally, it is recommended
 that a lock file be installed with the corresponding alternate
-device.  In order to avoid deadlocks, it is recommended that the locks
+device.         In order to avoid deadlocks, it is recommended that the locks
 are acquired in the following order, and released in the reverse:
 
        1. The symbolic link name, if any (/var/lock/LCK..modem)
@@ -2045,7 +2052,7 @@ port TTY, for which no alternate device would exist.
 
 Pseudoterminals, or PTYs, are used to create login sessions or provide
 other capabilities requiring a TTY line dicipline (including SLIP or
-PPP capability) to arbitrary data-generation processes.  Each PTY has
+PPP capability) to arbitrary data-generation processes.         Each PTY has
 a master side, named /dev/pty[p-za-e][0-9a-f], and a slave side, named
 /dev/tty[p-za-e][0-9a-f].  The kernel arbitrates the use of PTYs by
 allowing each master side to be opened only once.
diff --git a/Documentation/knfsd.txt b/Documentation/knfsd.txt
new file mode 100644 (file)
index 0000000..ad66745
--- /dev/null
@@ -0,0 +1,98 @@
+This is the Linux NFS utility package version 0.1.5. It is based on
+knfsd 1.4.7.
+
+WARNING: The NFS servers in Linux 2.2 to 2.2.13 are not compatible with
+other NFS client implemenations. If you plan to use Linux 2.2.x as an
+NFS server for non-Linux NFS clients, you should get the Linux NFS
+kernel from the Linux NFS CVS server:
+
+1. Set the environment variable, CVS_RSH, to ssh.
+2. Login to the Linux NFS CVS server:
+
+# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs login
+
+without password if it is your first time.
+
+3. Check out the current Linux 2.2 NFS kernel:
+
+a. From the NFS V2 branch:
+
+# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co -r linux-2-2-nfsv2 linux-2.2
+
+b. From the main trunk:
+
+# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co linux-2.2
+
+4. If you don't want to use the current NFS kernel, you can find out
+for which kernels the NFS patch is available:
+
+# cd linux-2.2
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs status -v Makefile
+
+Then generate the kernel patch:
+
+# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs rdiff -ko -u -r linux-2-2-xx -r linux-2-2-xx-nfsv2-xxxxx linux-2.2
+
+If there is no NFS patch for the kernel you are interested in, you have
+to make a patch closest to your kernel version and apply it by hand.
+
+There is a Linux NFS kernel source tree for Linux 2.3, linux-2.3, on
+the Linux NFS CVS server. We will need all the help we can get. To
+contribute to the Linux NFS project, please go to
+
+http://www.linuxnfs.sourceforge.org
+
+You register yourself. Please send an email to
+nfs-admin@linuxnfs.sourceforge.org with
+
+1. Your user id on www.linuxnfs.sourceforge.org.
+2. The area in NFS you'd like to work on.
+
+You will be notified when it is done.
+
+There is a Linux NFS mailing list at
+
+http://lists.sourceforge.net/mailman/listinfo/nfs/                                                               
+You can subscribe it and search the mailing list archive via a web
+browser.
+
+The nfs-utils package is available from the CVS server:
+
+# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co nfs-utils
+
+will get the latest version.
+
+The files are
+
+ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/nfs-utils-0.1.5.tar.gz
+ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/nfs-utils-0.1.4-0.1.5.diff.gz
+
+To compile, just do
+
+# ./configure
+# make
+
+# make install
+
+will install the nfs-utils binaries. You have to install the NFS
+service scripts. There are 2 in etc/redhat provided for RedHat 6.x.
+They are tested on RedHat 6.1.
+
+On RedHat 6.1, you can use
+
+# rpm -ta nfs-utils-0.1.5.tar.gz
+
+to build the source and binary RPMs.
+
+If your mount from util-linux is too old, you will need 2 patches:
+
+ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/util-linux-2.9o-mount-nfsv3.patch
+ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/util-linux-2.9w-mount-nfsv3try.patch
+
+Thanks.
+
+
+H.J.
+hjl@lucon.org
+12/19/99
+
diff --git a/Documentation/moxa-smartio b/Documentation/moxa-smartio
new file mode 100644 (file)
index 0000000..4357e67
--- /dev/null
@@ -0,0 +1,412 @@
+=============================================================================
+
+       MOXA Smartio Family Device Driver Ver 1.1 Installation Guide
+                   for Linux Kernel 2.2.x and 2.0.3x
+              Copyright (C) 1999, Moxa Technologies Co, Ltd.
+=============================================================================
+Content
+
+1. Introduction
+2. System Requirement
+3. Installation
+4. Utilities
+5. Setserial
+6. Troubleshooting
+
+-----------------------------------------------------------------------------
+1. Introduction
+
+   The Smartio family Linux driver, Ver. 1.1, supports following multiport
+   boards.
+
+    -C104P/H/HS, C104H/PCI, C104HS/PCI, CI-104J 4 port multiport board.
+    -C168P/H/HS, C168H/PCI 8 port multiport board.
+
+   This driver has been modified a little and cleaned up from the Moxa
+   contributed driver code and merged into Linux 2.2.14pre. In paticular
+   official major/minor numbers have been assigned which are different to
+   those the original Moxa supplied driver used.
+
+   This driver and installation procedure have been developed upon Linux Kernel
+   2.2.5 and backward compatible to 2.0.3x. This driver supports Intel x86 and
+   Alpha hardware platform. In order to maintain compatibility, this version
+   has also been properly tested with RedHat, OpenLinux, TurboLinux and
+   S.u.S.E Linux. However, if compatibility problem occurs, please contact
+   Moxa at support@moxa.com.tw.
+
+   In addition to device driver, useful utilities are also provided in this
+   version. They are
+    - msdiag     Diagnostic program for detecting installed Moxa Smartio boards.
+    - msmon      Monitor program to observe data count and line status signals.
+    - msterm     A simple terminal program which is useful in testing serial
+                ports.
+    - io-irq.exe Configuration program to setup ISA boards. Please note that
+                 this program can only be executed under DOS.
+
+   All the drivers and utilities are published in form of source code under
+   GNU General Public License in this version. Please refer to GNU General
+   Public License announcement in each source code file for more detail.
+
+   In Moxa's ftp sites, you may always find latest driver at
+   ftp://ftp.moxa.com  or ftp://ftp.moxa.com.tw.
+
+   This version of driver can be installed as Loadable Module (Module driver)
+   or built-in into kernel (Static driver). You may refer to following
+   installation procedure for suitable one. Before you install the driver,
+   please refer to hardware installation procedure in the User's Manual.
+
+   We assume the user should be familiar with following documents.
+   - Serial-HOWTO
+   - Kernel-HOWTO
+
+-----------------------------------------------------------------------------
+2. System Requirement
+   - Hardware platform: Intel x86 or Alpha machine
+   - Kernel version: 2.0.3x or 2.2.x
+   - gcc version 2.72 or later
+   - Maximum 4 boards can be installed in combination
+
+-----------------------------------------------------------------------------
+3. Installation
+
+   3.1 Hardware installation
+
+       There are two types of buses, ISA and PCI, for Smartio family multiport
+       board.
+
+       ISA board
+       ---------
+       You'll have to configure CAP address, I/O address, Interrupt Vector
+       as well as IRQ before installing this driver. Please refer to hardware
+       installation procedure in User's Manual before proceed any further.
+       Please make sure the JP1 is open after the ISA board is set properly.
+
+       PCI board
+       ---------
+       You may need to adjust IRQ useage in BIOS to avoid from IRQ conflict
+       with other ISA devices. Please refer to hardware installation
+       procedure in User's Manual in advance.
+
+       IRQ Sharing
+       -----------
+       Each port within the same multiport board shares the same IRQ. Up to
+       4 Moxa Smartio Family multiport boards can be installed together on
+       one system and they can share the same IRQ.
+
+   3.2 Driver files and device naming convention
+
+       The driver file may be obtained from ftp, CD-ROM or floppy disk. The
+       first step, anyway, is to copy driver file "mxser.tgz" into specified
+       directory. e.g. /moxa. The execute commands as below.
+
+       # cd /moxa
+       # tar xvf /dev/fd0 
+       or
+       # cd /moxa
+       # cp /mnt/cdrom/<driver directory>/mxser.tgz .
+       # tar xvfz mxser.tgz
+
+       You may find all the driver and utilities files in /moxa/mxser.
+       Following installation procedure depends on the model you'd like to
+       run the driver. If you prefer module driver, please refer to 3.3.
+       If static driver is required, please refer to 3.4.
+
+       Dialin and callout port
+       -----------------------
+       This driver remains traditional serial device properties. There're
+       two special file name for each serial port. One is dial-in port
+       which is named "ttyMxx". For callout port, the naming convention
+       is "cumxx".
+
+       Device naming when more than 2 boards installed
+       -----------------------------------------------
+       Naming convention for each Smartio multiport board is pre-defined
+       as below.
+
+       Board Num.       Dial-in Port         Callout port
+       1st board       ttyM0  - ttyM7        cum0  - cum7
+       2nd board       ttyM8  - ttyM15       cum8  - cum15
+       3rd board       ttyM16 - ttyM23       cum16 - cum23
+       4th board       ttyM24 - ttym31       cum24 - cum31
+
+       Board sequence
+       --------------
+       This driver will activate ISA boards according to the parameter set
+       in the driver. After all specified ISA board activated, PCI board
+       will be installed in the system automatically driven.
+       Therefore the board number is sorted by the CAP address of ISA boards.
+       For PCI boards, their sequence will be after ISA boards and C168H/PCI
+       has higher priority than C104H/PCI boards.
+
+   3.3 Module driver configuration
+       Module driver is easiest way to install. If you prefer static driver
+       installation, please skip this paragraph.
+       1. Find "Makefile" in /moxa/mxser, then run
+
+         # make install
+
+         The driver files "mxser.o" and utilities will be properly compiled
+         and copied to system directories respectively.Then run
+
+         # insmod mxser
+
+         to activate the moduler driver. You may run "lsmod" to check
+         if "mxser.o" is activated.
+
+       2. Create special files by executing "msmknod".
+         # cd /moxa/mxser/driver
+         # ./msmknod
+
+         Default major numbers for dial-in device and callout device are
+         174, 175. Msmknod will delete any special files occuping the same
+         device naming.
+
+       3. Up to now, you may manually execute "insmod mxser" to activate
+         this driver and run "rmmod mxser" to remove it. However, it's
+         better to have a boot time configuration to eliminate manual
+         operation.
+         Boot time configuration can be achieved by rc file. Run following
+         command for setting rc files.
+
+         # cd /moxa/mxser/driver
+         # cp ./rc.mxser /etc/rc.d
+         # cd /etc/rc.d
+
+         You may have to modify part of the content in rc.mxser to specify
+          parameters for ISA board. Please refer to rc.mxser for more detail.
+          Find "rc.serial". If "rc.serial" doesn't exist, create it by vi.
+         Add "rc.mxser" in last line. Next, open rc.local by vi
+         and append following content.
+
+         if [ -f /etc/rc.d/rc.serial ]; then
+            sh /etc/rc.d/rc.serial
+         fi
+
+       4. Reboot and check if mxser.o activated by "lsmod" command.
+       5. If you'd like to drive Smartio ISA boards in the system, you'll
+         have to add parameter to specify CAP address of given board while
+          activating "mxser.o". The format for parameters are as follows.
+
+         insmod mxser ioaddr=0x???,0x???,0x???,0x???
+                               |      |     |    |
+                               |      |     |    +- 4th ISA board
+                               |      |     +------ 3rd ISA board
+                               |      +------------ 2nd ISA board
+                               +------------------- 1st ISA board
+
+   3.4 Static driver configuration
+
+       1. Create link
+         # cd /usr/src/linux/drivers/char
+         # ln -s /moxa/mxser/driver/mxser.c mxser.c
+
+       2. Add CAP address list for ISA boards
+         In module mode, the CAP address for ISA board is given by
+         parameter. In static driver configuration, you'll have to
+         assign it within driver's source code. If you will not
+         install any ISA boards, you may skip to next portion.
+         The instructions to modify driver source code are as
+         below.
+         a. # cd /moxa/mxser/driver
+            # vi mxser.c
+         b. Find the array mxserBoardCAP[] as belows.
+
+            static int mxserBoardCAP[]
+            = {0x00, 0x00, 0x00, 0x00};
+
+         c. Change the address within this array using vi. For
+            example, to driver 2 ISA boards with CAP address
+            0x280 and 0x180 as 1st and 2nd board. Just to change
+            the source code as follows.
+
+            static int mxserBoardCAP[]
+            = {0x280, 0x180, 0x00, 0x00};
+
+       3. Modify tty_io.c
+         # cd /usr/src/linux/drivers/char/
+         # vi tty_io.c
+           Find pty_init(), insert "mxser_init()" as
+
+           pty_init();
+           mxser_init();
+
+       4. Modify tty.h
+         # cd /usr/src/linux/include/linux
+         # vi tty.h
+           Find extern int tty_init(void), insert "mxser_init()" as
+
+           extern int tty_init(void);
+           extern int mxser_init(void);
+     
+       5. Modify Makefile
+         # cd /usr/src/linux/drivers/char
+         # vi Makefile
+           Find L_OBJS := tty_io.o ...... random.o, add
+           "mxser.o" at last of this line as
+           L_OBJS := tty_io.o ....... mxser.o
+
+       6. Rebuild kernel
+         The following are for Linux kernel rebuilding,for your reference only.
+         For appropriate details, please refer to the Linux document.
+
+         If 'lilo' utility is installed, please use 'make zlilo' to rebuild
+         kernel. If 'lilo' is not installed, please follow the following steps.
+
+          a. cd /usr/src/linux
+          b. make clean                             /* take a few minutes */
+          c. make dep                               /* take a few minutes */
+          d. make bzImage                 /* take probably 10-20 minutes */
+          e. Backup original boot kernel.                /* optional step */
+          f. cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz
+          g. Please make sure the boot kernel (vmlinuz) is in the
+             correct position. If you use 'lilo' utility, you should
+             check /etc/lilo.conf 'image' item specifiedd the path
+             which is the 'vmlinuz' path, or you will load wrong
+             (or old) boot kernel image (vmlinuz).
+          h. chmod 400 /vmlinuz
+          i. lilo
+          j. rdev -R /vmlinuz 1
+          k. sync
+
+         Note that if the result of "make zImage" is ERROR, then you have to
+         go back to Linux configuration Setup. Type "make config" in directory
+         /usr/src/linux or "setup".
+
+         Since system include file, /usr/src/linux/include/linux/interrupt.h,
+         is modified each time the MOXA driver is installed, kernel rebuilding
+         is inevitable. And it takes about 10 to 20 minutes depends on the
+         machine.
+
+       7. Make utility
+         # cd /moxa/mxser/utility
+         # make install
+       
+       8. Make special file
+          # cd /moxa/mxser/driver
+          # ./msmknod
+
+       9. Reboot
+
+   3.5 Custom configuration
+       Although this driver already provides you default configuration, you
+       still can change the device name and major number.The instruction to
+       change these parameters are shown as below.
+
+       Change Device name
+       ------------------
+       If you'd like to use other device names instead of default naming
+       convention, all you have to do is to modify the internal code
+       within the shell script "msmknod". First, you have to open "msmknod"
+       by vi. Locate each line contains "ttyM" and "cum" and change them
+       to the device name you desired. "msmknod" creates the device names
+       you need next time executed.
+
+       Change Major number
+       -------------------
+       If major number 30 and 35 had been occupied, you may have to select
+       2 free major numbers for this driver. There are 3 steps to change
+       major numbers.
+
+       1. Find free major numbers
+         In /proc/devices, you may find all the major numbers occupied
+         in the system. Please select 2 major numbers that are available.
+         e.g. 40, 45.
+       2. Create special files
+         Run /moxa/mxser/driver/msmknod to create special files with
+         specified major numbers.
+       3. Modify driver with new major number
+         Run vi to open /moxa/mxser/driver/mxser.c. Locate the line
+         contains "MXSERMAJOR". Change the content as below.
+         #define         MXSERMAJOR              40
+         #define         MXSERCUMAJOR            45
+       4. Run # make install in /moxa/mxser/driver.
+
+   3.6 Verify driver installation
+       You may refer to /var/log/messages to check the latest status
+       log reported by this driver whenever it's activated.
+-----------------------------------------------------------------------------
+4. Utilities
+   There are 3 utilities contained in this driver. They are msdiag, msmon and
+   msterm. These 3 utilities are released in form of source code. They should
+   be compiled into executable file and copied into /usr/bin.
+
+   msdiag - Diagnostic
+   --------------------
+   This utility provides the function to detect what Moxa Smartio multiport
+   board exists in the system.
+
+   msmon - Port Monitoring
+   -----------------------
+   This utility gives the user a quick view about all the MOXA ports'
+   activities. One can easily learn each port's total received/transmitted
+   (Rx/Tx) character count since the time when the monitoring is started.
+   Rx/Tx throughputs per second are also reported in interval basis (e.g.
+   the last 5 seconds) and in average basis (since the time the monitoring
+   is started). You can reset all ports' count by <HOME> key. <+> <->
+   (plus/minus) keys to change the displaying time interval. Press <ENTER>
+   on the port, that cursor stay, to view the port's communication
+   parameters, signal status, and input/output queue.
+
+   msterm - Terminal Emulation
+   ---------------------------
+   This utility provides data sending and receiving ability of all tty ports,
+   especially for MOXA ports. It is quite useful for testing simple
+   application, for example, sending AT command to a modem connected to the
+   port or used as a terminal for login purpose. Note that this is only a
+   dumb terminal emulation without handling full screen operation.
+-----------------------------------------------------------------------------
+5. Setserial
+
+   Supported Setserial parameters are listed as below.
+
+   uart          set UART type(16450-->disable FIFO, 16550A-->enable FIFO)
+   close_delay   set the amount of time(in 1/100 of a second) that DTR
+                 should be kept low while being closed.
+   closing_wait   set the amount of time(in 1/100 of a second) that the
+                 serial port should wait for data to be drained while
+                 being closed, before the receiver is disable.
+   spd_hi        Use  57.6kb  when  the application requests 38.4kb.
+   spd_vhi       Use  115.2kb  when  the application requests 38.4kb.
+   spd_normal    Use  38.4kb  when  the application requests 38.4kb.
+
+-----------------------------------------------------------------------------
+6. Troubleshooting
+
+   The boot time error mesages and solutions are stated as clearly as
+   possible. If all the possible solutions fail, please contact our technical
+   support team to get more help.
+
+   Error msg: More than 4 Moxa Smartio family boards found. Fifth board and
+             after are ignored.
+   Solution:
+   To avoid this problem, please unplug fifth and after board, because Moxa
+   driver supports up to 4 boards.
+
+   Error msg: Request_irq fail, IRQ(?) may be conflict with another device.
+   Solution:
+   Other PCI or ISA devices occupy the assigned IRQ. If you are not sure
+   which device causes the situation,please check /proc/interrupts to find
+   free IRQ and simply change another free IRQ for Moxa board.
+
+   Error msg: Board #: C1xx Series(CAP=xxx) interupt number invalid.
+   Solution:
+   Each port within the same multiport board shares the same IRQ. Please set
+   one IRQ (IRQ doesn't equal to zero) for one Moxa board.
+
+   Error msg: No interrupt vector be set for Moxa ISA board(CAP=xxx).
+   Solution:
+   Moxa ISA board needs an interrupt vector.Please refer to user's manual
+   "Hardware Installation" chapter to set interrupt vector.
+
+   Error msg: Couldn't install MOXA Smartio family driver!
+   Solution:
+   Load Moxa driver fail, the major number may conflict with other devices.
+   Please refer to previous section 3.5 to change a free major number for
+   Moxa driver.
+
+   Error msg: Couldn't install MOXA Smartio family callout driver!
+   Solution:
+   Load Moxa callout driver fail, the callout device major number may
+   conflict with other devices. Please refer to previous section 3.5 to
+   change a free callout device major number for Moxa driver.
+-----------------------------------------------------------------------------
index 474d3c081287a1b902cd99dd46d766899f096d32..b353eaa3be791df5414383216559e13584c3184b 100644 (file)
@@ -1,6 +1,6 @@
   SiS 900/7016 Fast Ethernet Device Driver
   by Ollie Lho (ollie@sis.com.tw)
-  November 4, 1999. Document Revision: 0.1
+  November 4, 1999. Document Revision: 0.2
 
   This document gives some information on installation and usage of SiS
   900/7016 device driver under Linux.
@@ -17,7 +17,7 @@
 
   4. Tested Environment
 
-  5. Files in This Rackage
+  5. Files in This Package
 
   6. Installation
 
 
   9. Names of variables were changed to be more consistent.
 
- 10. Clean up of auo-negotiation and timer code.
+  10.
+     Clean up of auo-negotiation and timer code.
 
- 11. Automatic detection and change of PHY on the fly.
+  11.
+     Automatic detection and change of PHY on the fly.
 
 
   4.  Tested Environment
   o  Samba version 2.0.3
 
 
-  5.  Files in This Rackage
+  5.  Files in This Package
 
   In the package you can find these files:
 
 
   6.  Installation
 
-  Before trying to install the driver, be sure to get the latest
-  revision from SiS' Home Page.  If you have no prior experience in
-  networking under Linux, please read Ethernet HOWTO and Networking
-  HOWTO available from Linux Documentation Project (LDP).
+  Silicon Integrated System Corp. is cooperating closely with core Linux
+  Kernel developers. The revisions of SiS 900 driver are distributed by
+  the usuall channels for kernel tar files and patches. Those kernel tar
+  files for official kernel and patches for kernel pre-release can be
+  download at official kernel ftp site
+  <http://ftp.kernel.org/pub/linux/kernel/> and its mirrors.  The 1.06
+  revision can be found in kernel version later than 2.3.15 and
+  pre-2.2.14.  If you have no prior experience in networking under
+  Linux, please read Ethernet HOWTO and Networking HOWTO available from
+  Linux Documentation Project (LDP).
 
   The installation procedure are different according to your kernel
   versions.
        sis900.c: v1.06  11/04/99
        eth0: SiS 900 PCI Fast Ethernet at 0xd000, IRQ 10, 00:00:e8:83:7f:a4.
        eth0: SiS 900 Internal MII PHY transceiver found at address 1.
+       eth0: Using SiS 900 Internal MII PHY as default
 
 
 
 
 
 
-       eth0: Using SiS 900 Internal MII PHY as default
        eth0: Media Link On 100mbps full-duplex
 
 
index 7462f7826dd2d678c056f5f2c5527e42d55c7328..8ab0486223da549203464b7f8c8267fdbd5b4871 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 14
-EXTRAVERSION = pre16
+EXTRAVERSION = pre17
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
index ed5c7b1ca622d2145cd4dbf8c9b8e30bf8c453dc..0f41b4da2b03b3096b7d282ad8404bd922d21bb1 100644 (file)
@@ -399,7 +399,7 @@ static void probe_cmos_for_drives (ide_hwif_t *hwif)
                                drive->present = 1;
                        } else
                                printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n",
-                                      cyl, head, sect);
+                                      unit, cyl, head, sect);
                }
                BIOS += 16;
        }
index e744368422c42283edd2e01c46adc5aabe7f29f7..27a889b14f723098dccb08c5ed82071c43291b28 100644 (file)
@@ -1146,7 +1146,7 @@ repeat:
  * the driver.  This makes the driver much more friendlier to shared IRQs
  * than previous designs, while remaining 100% (?) SMP safe and capable.
  */
-static void ide_do_request (ide_hwgroup_t *hwgroup)
+static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
 {
        struct blk_dev_struct *bdev;
        ide_drive_t     *drive;
@@ -1204,11 +1204,15 @@ static void ide_do_request (ide_hwgroup_t *hwgroup)
                if (bdev->current_request == &bdev->plug)       /* FIXME: paranoia */
                        printk("%s: Huh? nuking plugged queue\n", drive->name);
                bdev->current_request = hwgroup->rq = drive->queue;
+               if (hwif->irq != masked_irq)
+                       disable_irq_nosync(hwif->irq);
                spin_unlock(&io_request_lock);
                if (!hwif->serialized)  /* play it safe with buggy hardware */
                        ide__sti();
                startstop = start_request(drive);
                spin_lock_irq(&io_request_lock);
+               if (hwif->irq != masked_irq)
+                       enable_irq(hwif->irq);
                if (startstop == ide_stopped)
                        hwgroup->busy = 0;
        }
@@ -1226,41 +1230,41 @@ struct request **ide_get_queue (kdev_t dev)
 
 void do_ide0_request (void)
 {
-       ide_do_request (ide_hwifs[0].hwgroup);
+       ide_do_request (ide_hwifs[0].hwgroup, 0);
 }
 
 #if MAX_HWIFS > 1
 void do_ide1_request (void)
 {
-       ide_do_request (ide_hwifs[1].hwgroup);
+       ide_do_request (ide_hwifs[1].hwgroup, 0);
 }
 #endif /* MAX_HWIFS > 1 */
 
 #if MAX_HWIFS > 2
 void do_ide2_request (void)
 {
-       ide_do_request (ide_hwifs[2].hwgroup);
+       ide_do_request (ide_hwifs[2].hwgroup, 0);
 }
 #endif /* MAX_HWIFS > 2 */
 
 #if MAX_HWIFS > 3
 void do_ide3_request (void)
 {
-       ide_do_request (ide_hwifs[3].hwgroup);
+       ide_do_request (ide_hwifs[3].hwgroup, 0);
 }
 #endif /* MAX_HWIFS > 3 */
 
 #if MAX_HWIFS > 4
 void do_ide4_request (void)
 {
-       ide_do_request (ide_hwifs[4].hwgroup);
+       ide_do_request (ide_hwifs[4].hwgroup, 0);
 }
 #endif /* MAX_HWIFS > 4 */
 
 #if MAX_HWIFS > 5
 void do_ide5_request (void)
 {
-       ide_do_request (ide_hwifs[5].hwgroup);
+       ide_do_request (ide_hwifs[5].hwgroup, 0);
 }
 #endif /* MAX_HWIFS > 5 */
 
@@ -1328,7 +1332,7 @@ void ide_timer_expiry (unsigned long data)
                                hwgroup->busy = 0;
                }
        }
-       ide_do_request(hwgroup);
+       ide_do_request(hwgroup, 0);
        spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
@@ -1471,7 +1475,7 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
        if (startstop == ide_stopped) {
                if (hwgroup->handler == NULL) { /* paranoia */
                        hwgroup->busy = 0;
-                       ide_do_request(hwgroup);
+                       ide_do_request(hwgroup, hwif->irq);
                } else {
                        printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
                }
@@ -1574,7 +1578,7 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
                rq->next = cur_rq->next;
                cur_rq->next = rq;
        }
-       ide_do_request(hwgroup);
+       ide_do_request(hwgroup, 0);
        spin_unlock_irqrestore(&io_request_lock, flags);
        if (action == ide_wait) {
                down(&sem);                     /* wait for it to be serviced */
index 6a9a257c67114738a7492ef86f33232958218dc8..34db2b57d9565286b144b05bd9ae97032357eb66 100644 (file)
@@ -22,30 +22,32 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
+   tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE
    tristate 'Comtrol Rocketport support' CONFIG_ROCKETPORT
-   tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA
-   if [ "$CONFIG_DIGIEPCA" = "n" ]; then
-      tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
-   fi
    tristate 'Cyclades async mux support' CONFIG_CYCLADES
    if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_CYCLADES" != "n" ]; then
       bool '  Cyclades-Z interrupt mode operation (EXPERIMENTAL)' CONFIG_CYZ_INTR
    fi
-   bool 'Stallion multiport serial support' CONFIG_STALDRV
-   if [ "$CONFIG_STALDRV" = "y" ]; then
-     tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
-     tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+   tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA
+   if [ "$CONFIG_DIGIEPCA" = "n" ]; then
+      tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
+   fi
+   tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
+   tristate 'Moxa Intellio support' CONFIG_MOXA_INTELLIO
+   tristate 'Moxa SmartIO support' CONFIG_MOXA_SMARTIO
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+       dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m
    fi
    tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
-   tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE
    tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX
    if [ "$CONFIG_SPECIALIX" != "n" ]; then
      bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS
    fi 
    tristate 'Specialix SX (and SI) card support' CONFIG_SX
-   tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
-   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-       dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m
+   bool 'Stallion multiport serial support' CONFIG_STALDRV
+   if [ "$CONFIG_STALDRV" = "y" ]; then
+     tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
+     tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
    fi
    dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m
    dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m
index 734e83ba1ec908547066eb7a9d3a3247f7bd4dd2..dcf48b06e97ae3eb05c4acfa1934521d7ce1043a 100644 (file)
@@ -94,6 +94,22 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_MOXA_SMARTIO),y)
+L_OBJS += mxser.o
+else
+  ifeq ($(CONFIG_MOXA_SMARTIO),m)
+  M_OBJS += mxser.o
+  endif
+endif
+
+ifeq ($(CONFIG_MOXA_INTELLIO),y)
+L_OBJS += moxa.o
+else
+  ifeq ($(CONFIG_MOXA_INTELLIO),m)
+  M_OBJS += moxa.o
+  endif
+endif
+
 ifeq ($(CONFIG_DIGI),y)
 L_OBJS += pcxx.o
 else
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
new file mode 100644 (file)
index 0000000..b99e2b0
--- /dev/null
@@ -0,0 +1,3319 @@
+/*****************************************************************************/
+/*
+ *           moxa.c  -- MOXA Intellio family multiport serial driver.
+ *
+ *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com.tw).
+ *
+ *      This code is loosely based on the Linux serial driver, written by
+ *      Linus Torvalds, Theodore T'so and others.
+ *
+ *      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.
+ */
+
+/*
+ *    MOXA Intellio Series Driver
+ *      for             : LINUX
+ *      date            : 1999/1/7
+ *      version         : 5.1
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/serial.h>
+#include <linux/tty_driver.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#define                MOXA_VERSION            "5.1k"
+
+#define MOXAMAJOR       172
+#define MOXACUMAJOR     173
+
+#define put_to_user(arg1, arg2) put_user(arg1, (unsigned long *)arg2)
+#define get_from_user(arg1, arg2) get_user(arg1, (unsigned int *)arg2)
+
+#define MAX_BOARDS             4       /* Don't change this value */
+#define MAX_PORTS_PER_BOARD    32      /* Don't change this value */
+#define MAX_PORTS              128     /* Don't change this value */
+
+/*
+ *    Define the Moxa PCI vendor and device IDs.
+ */
+#define MOXA_BUS_TYPE_ISA              0
+#define MOXA_BUS_TYPE_PCI              1
+
+#ifndef        PCI_VENDOR_ID_MOXA
+#define        PCI_VENDOR_ID_MOXA      0x1393
+#endif
+#ifndef PCI_DEVICE_ID_CP204J
+#define PCI_DEVICE_ID_CP204J   0x2040
+#endif
+#ifndef PCI_DEVICE_ID_C218
+#define PCI_DEVICE_ID_C218     0x2180
+#endif
+#ifndef PCI_DEVICE_ID_C320
+#define PCI_DEVICE_ID_C320     0x3200
+#endif
+
+enum {
+       MOXA_BOARD_C218_PCI = 1,
+       MOXA_BOARD_C218_ISA,
+       MOXA_BOARD_C320_PCI,
+       MOXA_BOARD_C320_ISA,
+       MOXA_BOARD_CP204J,
+};
+
+static char *moxa_brdname[] =
+{
+       "C218 Turbo PCI series",
+       "C218 Turbo ISA series",
+       "C320 Turbo PCI series",
+       "C320 Turbo ISA series",
+       "CP-204J series",
+};
+
+typedef struct {
+       unsigned short vendor_id;
+       unsigned short device_id;
+       unsigned short board_type;
+} moxa_pciinfo;
+
+static moxa_pciinfo moxa_pcibrds[] =
+{
+       {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C218, MOXA_BOARD_C218_PCI},
+       {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C320, MOXA_BOARD_C320_PCI},
+       {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP204J, MOXA_BOARD_CP204J},
+};
+
+typedef struct _moxa_isa_board_conf {
+       int boardType;
+       int numPorts;
+       unsigned long baseAddr;
+} moxa_isa_board_conf;
+
+static moxa_isa_board_conf moxa_isa_boards[] =
+{
+/*       {MOXA_BOARD_C218_ISA,8,0xDC000}, */
+};
+
+typedef struct _moxa_pci_devinfo {
+       ushort busNum;
+       ushort devNum;
+} moxa_pci_devinfo;
+
+typedef struct _moxa_board_conf {
+       int boardType;
+       int numPorts;
+       unsigned long baseAddr;
+       int busType;
+       moxa_pci_devinfo pciInfo;
+} moxa_board_conf;
+
+static moxa_board_conf moxa_boards[MAX_BOARDS];
+static unsigned long moxaBaseAddr[MAX_BOARDS];
+
+struct moxa_str {
+       int type;
+       int port;
+       int close_delay;
+       unsigned short closing_wait;
+       int count;
+       int blocked_open;
+       int event;
+       int asyncflags;
+       long session;
+       long pgrp;
+       unsigned long statusflags;
+       struct tty_struct *tty;
+       struct termios normal_termios;
+       struct termios callout_termios;
+       struct wait_queue *open_wait;
+       struct wait_queue *close_wait;
+       struct tq_struct tqueue;
+};
+
+struct mxser_mstatus {
+       tcflag_t cflag;
+       int cts;
+       int dsr;
+       int ri;
+       int dcd;
+};
+
+static struct mxser_mstatus GMStatus[MAX_PORTS];
+
+/* statusflags */
+#define TXSTOPPED      0x1
+#define LOWWAIT        0x2
+#define EMPTYWAIT      0x4
+#define THROTTLE       0x8
+
+/* event */
+#define MOXA_EVENT_HANGUP      1
+
+#define SERIAL_DO_RESTART
+
+
+#define SERIAL_TYPE_NORMAL     1
+#define SERIAL_TYPE_CALLOUT    2
+
+#define WAKEUP_CHARS           256
+
+#define PORTNO(x)              (MINOR((x)->device) - (x)->driver.minor_start)
+
+static int verbose = 0;
+static int ttymajor = MOXAMAJOR;
+static int calloutmajor = MOXACUMAJOR;
+#ifdef MODULE
+/* Variables for insmod */
+static int baseaddr[]  =       {0, 0, 0, 0};
+static int type[]      =       {0, 0, 0, 0};
+static int numports[]  =       {0, 0, 0, 0};
+
+MODULE_AUTHOR("William Chen");
+MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
+MODULE_PARM(type, "1-4i");
+MODULE_PARM(baseaddr, "1-4i");
+MODULE_PARM(numports, "1-4i");
+MODULE_PARM(ttymajor, "i");
+MODULE_PARM(calloutmajor, "i");
+MODULE_PARM(verbose, "i");
+
+#endif                         //MODULE
+
+static struct tty_driver moxaDriver;
+static struct tty_driver moxaCallout;
+static struct tty_struct *moxaTable[MAX_PORTS + 1];
+static struct termios *moxaTermios[MAX_PORTS + 1];
+static struct termios *moxaTermiosLocked[MAX_PORTS + 1];
+static struct moxa_str moxaChannels[MAX_PORTS];
+static int moxaRefcount;
+unsigned char *moxaXmitBuff;
+static int moxaTimer_on;
+struct timer_list moxaTimer;
+static int moxaEmptyTimer_on[MAX_PORTS];
+struct timer_list moxaEmptyTimer[MAX_PORTS];
+struct semaphore moxaBuffSem = MUTEX;
+
+int moxa_init(void);
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+/*
+ * static functions:
+ */
+static int moxa_get_PCI_conf(struct pci_dev *, int, moxa_board_conf *);
+static void do_moxa_softint(void *);
+static int moxa_open(struct tty_struct *, struct file *);
+static void moxa_close(struct tty_struct *, struct file *);
+static int moxa_write(struct tty_struct *, int, const unsigned char *, int);
+static int moxa_write_room(struct tty_struct *);
+static void moxa_flush_buffer(struct tty_struct *);
+static int moxa_chars_in_buffer(struct tty_struct *);
+static void moxa_flush_chars(struct tty_struct *);
+static void moxa_put_char(struct tty_struct *, unsigned char);
+static int moxa_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long);
+static void moxa_throttle(struct tty_struct *);
+static void moxa_unthrottle(struct tty_struct *);
+static void moxa_set_termios(struct tty_struct *, struct termios *);
+static void moxa_stop(struct tty_struct *);
+static void moxa_start(struct tty_struct *);
+static void moxa_hangup(struct tty_struct *);
+static void moxa_poll(unsigned long);
+static void set_tty_param(struct tty_struct *);
+static int block_till_ready(struct tty_struct *, struct file *,
+                           struct moxa_str *);
+static void setup_empty_event(struct tty_struct *);
+static void check_xmit_empty(unsigned long);
+static void shut_down(struct moxa_str *);
+static void receive_data(struct moxa_str *);
+/*
+ * moxa board interface functions:
+ */
+static void MoxaDriverInit(void);
+static int MoxaDriverIoctl(unsigned int, unsigned long, int);
+static int MoxaDriverPoll(void);
+static int MoxaPortsOfCard(int);
+static int MoxaPortIsValid(int);
+static void MoxaPortEnable(int);
+static void MoxaPortDisable(int);
+static long MoxaPortGetMaxBaud(int);
+static long MoxaPortSetBaud(int, long);
+static int MoxaPortSetTermio(int, struct termios *);
+static int MoxaPortGetLineOut(int, int *, int *);
+static void MoxaPortLineCtrl(int, int, int);
+static void MoxaPortFlowCtrl(int, int, int, int, int, int);
+static int MoxaPortLineStatus(int);
+static int MoxaPortDCDChange(int);
+static int MoxaPortDCDON(int);
+static void MoxaPortFlushData(int, int);
+static int MoxaPortWriteData(int, unsigned char *, int);
+static int MoxaPortReadData(int, unsigned char *, int);
+static int MoxaPortTxQueue(int);
+static int MoxaPortRxQueue(int);
+static int MoxaPortTxFree(int);
+static void MoxaPortTxDisable(int);
+static void MoxaPortTxEnable(int);
+static int MoxaPortResetBrkCnt(int);
+static void MoxaPortSendBreak(int, int);
+static int moxa_get_serial_info(struct moxa_str *, struct serial_struct *);
+static int moxa_set_serial_info(struct moxa_str *, struct serial_struct *);
+static void MoxaSetFifo(int port, int enable);
+
+#ifdef MODULE
+int init_module(void)
+{
+       int ret;
+
+       if (verbose)
+               printk("Loading module moxa ...\n");
+       ret = moxa_init();
+       if (verbose)
+               printk("Done\n");
+       return (ret);
+}
+
+void cleanup_module(void)
+{
+       int i;
+
+       if (verbose)
+               printk("Unloading module moxa ...\n");
+
+       if (moxaTimer_on)
+               del_timer(&moxaTimer);
+
+       for (i = 0; i < MAX_PORTS; i++)
+               if (moxaEmptyTimer_on[i])
+                       del_timer(&moxaEmptyTimer[i]);
+
+       if (tty_unregister_driver(&moxaCallout))
+               printk("Couldn't unregister MOXA Intellio family callout driver\n");
+       if (tty_unregister_driver(&moxaDriver))
+               printk("Couldn't unregister MOXA Intellio family serial driver\n");
+       if (verbose)
+               printk("Done\n");
+
+}
+#endif
+
+int moxa_init(void)
+{
+       int i, n, numBoards;
+       struct moxa_str *ch;
+       int ret1, ret2;
+
+       printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION);
+
+       memset(&moxaDriver, 0, sizeof(struct tty_driver));
+       memset(&moxaCallout, 0, sizeof(struct tty_driver));
+       moxaDriver.magic = TTY_DRIVER_MAGIC;
+       moxaDriver.name = "ttya";
+       moxaDriver.major = ttymajor;
+       moxaDriver.minor_start = 0;
+       moxaDriver.num = MAX_PORTS + 1;
+       moxaDriver.type = TTY_DRIVER_TYPE_SERIAL;
+       moxaDriver.subtype = SERIAL_TYPE_NORMAL;
+       moxaDriver.init_termios = tty_std_termios;
+       moxaDriver.init_termios.c_iflag = 0;
+       moxaDriver.init_termios.c_oflag = 0;
+       moxaDriver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
+       moxaDriver.init_termios.c_lflag = 0;
+       moxaDriver.flags = TTY_DRIVER_REAL_RAW;
+       moxaDriver.refcount = &moxaRefcount;
+       moxaDriver.table = moxaTable;
+       moxaDriver.termios = moxaTermios;
+       moxaDriver.termios_locked = moxaTermiosLocked;
+
+       moxaDriver.open = moxa_open;
+       moxaDriver.close = moxa_close;
+       moxaDriver.write = moxa_write;
+       moxaDriver.write_room = moxa_write_room;
+       moxaDriver.flush_buffer = moxa_flush_buffer;
+       moxaDriver.chars_in_buffer = moxa_chars_in_buffer;
+       moxaDriver.flush_chars = moxa_flush_chars;
+       moxaDriver.put_char = moxa_put_char;
+       moxaDriver.ioctl = moxa_ioctl;
+       moxaDriver.throttle = moxa_throttle;
+       moxaDriver.unthrottle = moxa_unthrottle;
+       moxaDriver.set_termios = moxa_set_termios;
+       moxaDriver.stop = moxa_stop;
+       moxaDriver.start = moxa_start;
+       moxaDriver.hangup = moxa_hangup;
+
+       moxaCallout = moxaDriver;
+       moxaCallout.name = "ttyA";
+       moxaCallout.major = calloutmajor;
+       moxaCallout.subtype = SERIAL_TYPE_CALLOUT;
+
+       moxaXmitBuff = 0;
+
+       for (i = 0, ch = moxaChannels; i < MAX_PORTS; i++, ch++) {
+               ch->type = PORT_16550A;
+               ch->port = i;
+               ch->tqueue.routine = do_moxa_softint;
+               ch->tqueue.data = ch;
+               ch->tty = 0;
+               ch->close_delay = 5 * HZ / 10;
+               ch->closing_wait = 30 * HZ;
+               ch->count = 0;
+               ch->blocked_open = 0;
+               ch->callout_termios = moxaCallout.init_termios;
+               ch->normal_termios = moxaDriver.init_termios;
+               ch->open_wait = 0;
+               ch->close_wait = 0;
+       }
+
+       for (i = 0; i < MAX_BOARDS; i++) {
+               moxa_boards[i].boardType = 0;
+               moxa_boards[i].numPorts = 0;
+               moxa_boards[i].baseAddr = 0;
+               moxa_boards[i].busType = 0;
+               moxa_boards[i].pciInfo.busNum = 0;
+               moxa_boards[i].pciInfo.devNum = 0;
+       }
+       MoxaDriverInit();
+       printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor);
+
+       ret1 = 0;
+       ret2 = 0;
+       if ((ret1 = tty_register_driver(&moxaDriver))) {
+               printk(KERN_ERR "Couldn't install MOXA Smartio family driver !\n");
+       } else if ((ret2 = tty_register_driver(&moxaCallout))) {
+               tty_unregister_driver(&moxaDriver);
+               printk(KERN_ERR "Couldn't install MOXA Smartio family callout driver !\n");
+       }
+       if (ret1 || ret2) {
+               return -1;
+       }
+       for (i = 0; i < MAX_PORTS; i++) {
+               init_timer(&moxaEmptyTimer[i]);
+               moxaEmptyTimer[i].function = check_xmit_empty;
+               moxaEmptyTimer[i].data = (unsigned long) & moxaChannels[i];
+               moxaEmptyTimer_on[i] = 0;
+       }
+
+       init_timer(&moxaTimer);
+       moxaTimer.function = moxa_poll;
+       moxaTimer.expires = jiffies + (HZ / 50);
+       moxaTimer_on = 1;
+       add_timer(&moxaTimer);
+
+       /* Find the boards defined in source code */
+       numBoards = 0;
+       for (i = 0; i < MAX_BOARDS; i++) {
+               if ((moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) ||
+                (moxa_isa_boards[i].boardType == MOXA_BOARD_C320_ISA)) {
+                       moxa_boards[numBoards].boardType = moxa_isa_boards[i].boardType;
+                       if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA)
+                               moxa_boards[numBoards].numPorts = 8;
+                       else
+                               moxa_boards[numBoards].numPorts = moxa_isa_boards[i].numPorts;
+                       moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA;
+                       moxa_boards[numBoards].baseAddr = moxa_isa_boards[i].baseAddr;
+                       if (verbose)
+                               printk("Board %2d: %s board(baseAddr=%lx)\n",
+                                      numBoards + 1,
+                                      moxa_brdname[moxa_boards[numBoards].boardType - 1],
+                                      moxa_boards[numBoards].baseAddr);
+                       numBoards++;
+               }
+       }
+       /* Find the boards defined form module args. */
+#ifdef MODULE
+       for (i = 0; i < MAX_BOARDS; i++) {
+               if ((type[i] == MOXA_BOARD_C218_ISA) ||
+                   (type[i] == MOXA_BOARD_C320_ISA)) {
+                       if (verbose)
+                               printk("Board %2d: %s board(baseAddr=%lx)\n",
+                                      numBoards + 1,
+                                      moxa_brdname[type[i] - 1],
+                                      (unsigned long) baseaddr[i]);
+                       if (numBoards >= MAX_BOARDS) {
+                               if (verbose)
+                                       printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS);
+                               continue;
+                       }
+                       moxa_boards[numBoards].boardType = type[i];
+                       if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA)
+                               moxa_boards[numBoards].numPorts = 8;
+                       else
+                               moxa_boards[numBoards].numPorts = numports[i];
+                       moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA;
+                       moxa_boards[numBoards].baseAddr = baseaddr[i];
+                       numBoards++;
+               }
+       }
+#endif
+       /* Find PCI boards here */
+#ifdef CONFIG_PCI
+       if (pci_present()) {
+               struct pci_dev *p = NULL;
+               n = sizeof(moxa_pcibrds) / sizeof(moxa_pciinfo);
+               i = 0;
+               while (i < n) {
+                       while((p = pci_find_device(moxa_pcibrds[i].vendor_id, moxa_pcibrds[i].device_id, p))!=NULL)
+                       {
+                               if (numBoards >= MAX_BOARDS) {
+                                       if (verbose)
+                                               printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS);
+                               } else {
+                                       moxa_get_PCI_conf(p, moxa_pcibrds[i].board_type,
+                                               &moxa_boards[numBoards]);
+                                       numBoards++;
+                               }
+                       }
+                       i++;
+               }
+       }
+#endif
+       for (i = 0; i < numBoards; i++) {
+               moxaBaseAddr[i] = (unsigned long) ioremap((unsigned long) moxa_boards[i].baseAddr, 0x4000);
+       }
+
+       return (0);
+}
+
+static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board)
+{
+       unsigned int val;
+
+       board->baseAddr = p->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK;
+       board->boardType = board_type;
+       switch (board_type) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               board->numPorts = 8;
+               break;
+
+       case MOXA_BOARD_CP204J:
+               board->numPorts = 4;
+               break;
+       default:
+               board->numPorts = 0;
+               break;
+       }
+       board->busType = MOXA_BUS_TYPE_PCI;
+       board->pciInfo.busNum = p->bus->number;
+       board->pciInfo.devNum = p->devfn >> 3;
+
+       return (0);
+}
+
+static void do_moxa_softint(void *private_)
+{
+       struct moxa_str *ch = (struct moxa_str *) private_;
+       struct tty_struct *tty;
+
+       if (!ch || !(tty = ch->tty))
+               return;
+       if (test_and_clear_bit(MOXA_EVENT_HANGUP, &ch->event)) {
+               tty_hangup(tty);
+               wake_up_interruptible(&ch->open_wait);
+               ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+       }
+}
+
+static int moxa_open(struct tty_struct *tty, struct file *filp)
+{
+       struct moxa_str *ch;
+       int port;
+       int retval;
+       unsigned long page;
+
+       port = PORTNO(tty);
+       if (port == MAX_PORTS) {
+               MOD_INC_USE_COUNT;
+               return (0);
+       }
+       if (!MoxaPortIsValid(port)) {
+               tty->driver_data = NULL;
+               return (-ENODEV);
+       }
+       down(&moxaBuffSem);
+       if (!moxaXmitBuff) {
+               page = get_free_page(GFP_KERNEL);
+               if (!page) {
+                       up(&moxaBuffSem);
+                       return (-ENOMEM);
+               }
+               if (moxaXmitBuff)
+                       free_page(page);
+               else
+                       moxaXmitBuff = (unsigned char *) page;
+       }
+       up(&moxaBuffSem);
+
+       MOD_INC_USE_COUNT;
+       ch = &moxaChannels[port];
+       ch->count++;
+       tty->driver_data = ch;
+       ch->tty = tty;
+       if (ch->count == 1 && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = ch->normal_termios;
+               else
+                       *tty->termios = ch->callout_termios;
+       }
+       ch->session = current->session;
+       ch->pgrp = current->pgrp;
+       if (!(ch->asyncflags & ASYNC_INITIALIZED)) {
+               ch->statusflags = 0;
+               set_tty_param(tty);
+               MoxaPortLineCtrl(ch->port, 1, 1);
+               MoxaPortEnable(ch->port);
+               ch->asyncflags |= ASYNC_INITIALIZED;
+       }
+       retval = block_till_ready(tty, filp, ch);
+
+       moxa_unthrottle(tty);
+
+       if (ch->type == PORT_16550A) {
+               MoxaSetFifo(ch->port, 1);
+       } else {
+               MoxaSetFifo(ch->port, 0);
+       }
+
+       return (retval);
+}
+
+static void moxa_close(struct tty_struct *tty, struct file *filp)
+{
+       struct moxa_str *ch;
+       int port;
+
+       port = PORTNO(tty);
+       if (port == MAX_PORTS) {
+               MOD_DEC_USE_COUNT;
+               return;
+       }
+       if (!MoxaPortIsValid(port)) {
+#ifdef SERIAL_DEBUG_CLOSE
+               printk("Invalid portno in moxa_close\n");
+#endif
+               tty->driver_data = NULL;
+               return;
+       }
+       if (tty->driver_data == NULL) {
+               return;
+       }
+       if (tty_hung_up_p(filp)) {
+               MOD_DEC_USE_COUNT;
+               return;
+       }
+       ch = (struct moxa_str *) tty->driver_data;
+
+       if ((tty->count == 1) && (ch->count != 1)) {
+               printk("moxa_close: bad serial port count; tty->count is 1, "
+                      "ch->count is %d\n", ch->count);
+               ch->count = 1;
+       }
+       if (--ch->count < 0) {
+               printk("moxa_close: bad serial port count, minor=%d\n",
+                      MINOR(tty->device));
+               ch->count = 0;
+       }
+       if (ch->count) {
+               MOD_DEC_USE_COUNT;
+               return;
+       }
+       ch->asyncflags |= ASYNC_CLOSING;
+
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (ch->asyncflags & ASYNC_NORMAL_ACTIVE)
+               ch->normal_termios = *tty->termios;
+       if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE)
+               ch->callout_termios = *tty->termios;
+       if (ch->asyncflags & ASYNC_INITIALIZED) {
+               setup_empty_event(tty);
+               tty_wait_until_sent(tty, 30 * HZ);      /* 30 seconds timeout */
+               moxaEmptyTimer_on[ch->port] = 0;
+               del_timer(&moxaEmptyTimer[ch->port]);
+       }
+       shut_down(ch);
+       MoxaPortFlushData(port, 2);
+
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       ch->event = 0;
+       ch->tty = 0;
+       if (ch->blocked_open) {
+               if (ch->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(ch->close_delay);
+               }
+               wake_up_interruptible(&ch->open_wait);
+       }
+       ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE |
+                           ASYNC_CLOSING);
+       wake_up_interruptible(&ch->close_wait);
+       MOD_DEC_USE_COUNT;
+}
+
+static int moxa_write(struct tty_struct *tty, int from_user,
+                     const unsigned char *buf, int count)
+{
+       struct moxa_str *ch;
+       int len, port;
+       unsigned long flags;
+       unsigned char *temp;
+
+       ch = (struct moxa_str *) tty->driver_data;
+       if (ch == NULL)
+               return (0);
+       port = ch->port;
+       save_flags(flags);
+       cli();
+       if (from_user) {
+               copy_from_user(moxaXmitBuff, buf, count);
+               temp = moxaXmitBuff;
+       } else
+               temp = (unsigned char *) buf;
+       len = MoxaPortWriteData(port, temp, count);
+       restore_flags(flags);
+       /*********************************************
+       if ( !(ch->statusflags & LOWWAIT) &&
+            ((len != count) || (MoxaPortTxFree(port) <= 100)) )
+       ************************************************/
+       ch->statusflags |= LOWWAIT;
+       return (len);
+}
+
+static int moxa_write_room(struct tty_struct *tty)
+{
+       struct moxa_str *ch;
+
+       if (tty->stopped)
+               return (0);
+       ch = (struct moxa_str *) tty->driver_data;
+       if (ch == NULL)
+               return (0);
+       return (MoxaPortTxFree(ch->port));
+}
+
+static void moxa_flush_buffer(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       MoxaPortFlushData(ch->port, 1);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup) (tty);
+       wake_up_interruptible(&tty->write_wait);
+}
+
+static int moxa_chars_in_buffer(struct tty_struct *tty)
+{
+       int chars;
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       /*
+        * Sigh...I have to check if driver_data is NULL here, because
+        * if an open() fails, the TTY subsystem eventually calls
+        * tty_wait_until_sent(), which calls the driver's chars_in_buffer()
+        * routine.  And since the open() failed, we return 0 here.  TDJ
+        */
+       if (ch == NULL)
+               return (0);
+       chars = MoxaPortTxQueue(ch->port);
+       if (chars) {
+               /*
+                * Make it possible to wakeup anything waiting for output
+                * in tty_ioctl.c, etc.
+                */
+               if (!(ch->statusflags & EMPTYWAIT))
+                       setup_empty_event(tty);
+       }
+       return (chars);
+}
+
+static void moxa_flush_chars(struct tty_struct *tty)
+{
+       /*
+        * Don't think I need this, because this is called to empty the TX
+        * buffer for the 16450, 16550, etc.
+        */
+}
+
+static void moxa_put_char(struct tty_struct *tty, unsigned char c)
+{
+       struct moxa_str *ch;
+       int port;
+       unsigned long flags;
+
+       ch = (struct moxa_str *) tty->driver_data;
+       if (ch == NULL)
+               return;
+       port = ch->port;
+       save_flags(flags);
+       cli();
+       moxaXmitBuff[0] = c;
+       MoxaPortWriteData(port, moxaXmitBuff, 1);
+       restore_flags(flags);
+       /************************************************
+       if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) )
+       *************************************************/
+       ch->statusflags |= LOWWAIT;
+}
+
+static int moxa_ioctl(struct tty_struct *tty, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+       register int port;
+       int retval, dtr, rts;
+       unsigned long flag;
+
+       port = PORTNO(tty);
+       if ((port != MAX_PORTS) && (!ch))
+               return (-EINVAL);
+
+       switch (cmd) {
+       case TCSBRK:            /* SVID version: non-zero arg --> no break */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return (retval);
+               setup_empty_event(tty);
+               tty_wait_until_sent(tty, 0);
+               if (!arg)
+                       MoxaPortSendBreak(ch->port, 0);
+               return (0);
+       case TCSBRKP:           /* support for POSIX tcsendbreak() */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return (retval);
+               setup_empty_event(tty);
+               tty_wait_until_sent(tty, 0);
+               MoxaPortSendBreak(ch->port, arg);
+               return (0);
+       case TIOCGSOFTCAR:
+               return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+       case TIOCSSOFTCAR:
+               if(get_user(retval, (unsigned long *) arg))
+                       return -EFAULT;
+               arg = retval;
+               tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |
+                                        (arg ? CLOCAL : 0));
+               if (C_CLOCAL(tty))
+                       ch->asyncflags &= ~ASYNC_CHECK_CD;
+               else
+                       ch->asyncflags |= ASYNC_CHECK_CD;
+               return (0);
+       case TIOCMGET:
+               flag = 0;
+               MoxaPortGetLineOut(ch->port, &dtr, &rts);
+               if (dtr)
+                       flag |= TIOCM_DTR;
+               if (rts)
+                       flag |= TIOCM_RTS;
+               dtr = MoxaPortLineStatus(ch->port);
+               if (dtr & 1)
+                       flag |= TIOCM_CTS;
+               if (dtr & 2)
+                       flag |= TIOCM_DSR;
+               if (dtr & 4)
+                       flag |= TIOCM_CD;
+               return put_user(flag, (unsigned int *) arg);
+       case TIOCMBIS:
+               if(get_user(retval, (unsigned int *) arg))
+                       return -EFAULT;
+               MoxaPortGetLineOut(ch->port, &dtr, &rts);
+               if (retval & TIOCM_RTS)
+                       rts = 1;
+               if (retval & TIOCM_DTR)
+                       dtr = 1;
+               MoxaPortLineCtrl(ch->port, dtr, rts);
+               return (0);
+       case TIOCMBIC:
+               if(get_user(retval, (unsigned int *) arg))
+                       return -EFAULT;
+               MoxaPortGetLineOut(ch->port, &dtr, &rts);
+               if (retval & TIOCM_RTS)
+                       rts = 0;
+               if (retval & TIOCM_DTR)
+                       dtr = 0;
+               MoxaPortLineCtrl(ch->port, dtr, rts);
+               return (0);
+       case TIOCMSET:
+               if(get_user(retval, (unsigned long *) arg))
+                       return -EFAULT;
+               dtr = rts = 0;
+               if (retval & TIOCM_RTS)
+                       rts = 1;
+               if (retval & TIOCM_DTR)
+                       dtr = 1;
+               MoxaPortLineCtrl(ch->port, dtr, rts);
+               return (0);
+       case TIOCGSERIAL:
+               return (moxa_get_serial_info(ch, (struct serial_struct *) arg));
+
+       case TIOCSSERIAL:
+               return (moxa_set_serial_info(ch, (struct serial_struct *) arg));
+       default:
+               retval = MoxaDriverIoctl(cmd, arg, port);
+       }
+       return (retval);
+}
+
+static void moxa_throttle(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       ch->statusflags |= THROTTLE;
+}
+
+static void moxa_unthrottle(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       ch->statusflags &= ~THROTTLE;
+}
+
+static void moxa_set_termios(struct tty_struct *tty,
+                            struct termios *old_termios)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       set_tty_param(tty);
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&ch->open_wait);
+}
+
+static void moxa_stop(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       MoxaPortTxDisable(ch->port);
+       ch->statusflags |= TXSTOPPED;
+}
+
+
+static void moxa_start(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       if (ch == NULL)
+               return;
+
+       if (!(ch->statusflags & TXSTOPPED))
+               return;
+
+       MoxaPortTxEnable(ch->port);
+       ch->statusflags &= ~TXSTOPPED;
+}
+
+static void moxa_hangup(struct tty_struct *tty)
+{
+       struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
+
+       moxa_flush_buffer(tty);
+       shut_down(ch);
+       ch->event = 0;
+       ch->count = 0;
+       ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+       ch->tty = 0;
+       wake_up_interruptible(&ch->open_wait);
+}
+
+static void moxa_poll(unsigned long ignored)
+{
+       register int card;
+       struct moxa_str *ch;
+       struct tty_struct *tp;
+       int i, ports;
+
+       moxaTimer_on = 0;
+       del_timer(&moxaTimer);
+
+       if (MoxaDriverPoll() < 0) {
+               moxaTimer.function = moxa_poll;
+               moxaTimer.expires = jiffies + (HZ / 50);
+               moxaTimer_on = 1;
+               add_timer(&moxaTimer);
+               return;
+       }
+       for (card = 0; card < MAX_BOARDS; card++) {
+               if ((ports = MoxaPortsOfCard(card)) <= 0)
+                       continue;
+               ch = &moxaChannels[card * MAX_PORTS_PER_BOARD];
+               for (i = 0; i < ports; i++, ch++) {
+                       if ((ch->asyncflags & ASYNC_INITIALIZED) == 0)
+                               continue;
+                       if (!(ch->statusflags & THROTTLE) &&
+                           (MoxaPortRxQueue(ch->port) > 0))
+                               receive_data(ch);
+                       if ((tp = ch->tty) == 0)
+                               continue;
+                       if (ch->statusflags & LOWWAIT) {
+                               if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) {
+                                       if (!tp->stopped) {
+                                               ch->statusflags &= ~LOWWAIT;
+                                               if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                                                 tp->ldisc.write_wakeup)
+                                                       (tp->ldisc.write_wakeup) (tp);
+                                               wake_up_interruptible(&tp->write_wait);
+                                       }
+                               }
+                       }
+                       if (!I_IGNBRK(tp) && (MoxaPortResetBrkCnt(ch->port) > 0)) {
+                               tty_insert_flip_char(tp, 0, TTY_BREAK);
+                               tty_schedule_flip(tp);
+                       }
+                       if (MoxaPortDCDChange(ch->port)) {
+                               if (ch->asyncflags & ASYNC_CHECK_CD) {
+                                       if (MoxaPortDCDON(ch->port))
+                                               wake_up_interruptible(&ch->open_wait);
+                                       else {
+                                               set_bit(MOXA_EVENT_HANGUP, &ch->event);
+                                               queue_task(&ch->tqueue, &tq_scheduler);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       moxaTimer.function = moxa_poll;
+       moxaTimer.expires = jiffies + (HZ / 50);
+       moxaTimer_on = 1;
+       add_timer(&moxaTimer);
+}
+
+/******************************************************************************/
+
+static void set_tty_param(struct tty_struct *tty)
+{
+       register struct termios *ts;
+       struct moxa_str *ch;
+       int rts, cts, txflow, rxflow, xany;
+
+       ch = (struct moxa_str *) tty->driver_data;
+       ts = tty->termios;
+       if (ts->c_cflag & CLOCAL)
+               ch->asyncflags &= ~ASYNC_CHECK_CD;
+       else
+               ch->asyncflags |= ASYNC_CHECK_CD;
+       rts = cts = txflow = rxflow = xany = 0;
+       if (ts->c_cflag & CRTSCTS)
+               rts = cts = 1;
+       if (ts->c_iflag & IXON)
+               txflow = 1;
+       if (ts->c_iflag & IXOFF)
+               rxflow = 1;
+       if (ts->c_iflag & IXANY)
+               xany = 1;
+       MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany);
+       MoxaPortSetTermio(ch->port, ts);
+}
+
+static int block_till_ready(struct tty_struct *tty, struct file *filp,
+                           struct moxa_str *ch)
+{
+       struct wait_queue wait = {current, NULL};
+       unsigned long flags;
+       int retval;
+       int do_clocal = C_CLOCAL(tty);
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING)) {
+               if (ch->asyncflags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&ch->close_wait);
+#ifdef SERIAL_DO_RESTART
+               if (ch->asyncflags & ASYNC_HUP_NOTIFY)
+                       return (-EAGAIN);
+               else
+                       return (-ERESTARTSYS);
+#else
+               return (-EAGAIN);
+#endif
+       }
+       /*
+        * 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 (ch->asyncflags & ASYNC_NORMAL_ACTIVE)
+                       return (-EBUSY);
+               if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
+                   (ch->asyncflags & ASYNC_SESSION_LOCKOUT) &&
+                   (ch->session != current->session))
+                       return (-EBUSY);
+               if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
+                   (ch->asyncflags & ASYNC_PGRP_LOCKOUT) &&
+                   (ch->pgrp != current->pgrp))
+                       return (-EBUSY);
+               ch->asyncflags |= ASYNC_CALLOUT_ACTIVE;
+               return (0);
+       }
+       /*
+        * If non-blocking mode is set, then make the check up front
+        * and then exit.
+        */
+       if (filp->f_flags & O_NONBLOCK) {
+               if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE)
+                       return (-EBUSY);
+               ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
+               return (0);
+       }
+       /*
+        * Block waiting for the carrier detect and the line to become free
+        */
+       retval = 0;
+       add_wait_queue(&ch->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready before block: ttys%d, count = %d\n",
+              ch->line, ch->count);
+#endif
+       save_flags(flags);
+       cli();
+       if (!tty_hung_up_p(filp))
+               ch->count--;
+       restore_flags(flags);
+       ch->blocked_open++;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               if (tty_hung_up_p(filp) ||
+                   !(ch->asyncflags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (ch->asyncflags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) &&
+                   !(ch->asyncflags & ASYNC_CLOSING) && (do_clocal ||
+                                               MoxaPortDCDON(ch->port)))
+                       break;
+
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&ch->open_wait, &wait);
+       if (!tty_hung_up_p(filp))
+               ch->count++;
+       ch->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttys%d, count = %d\n",
+              ch->line, ch->count);
+#endif
+       if (retval)
+               return (retval);
+       ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
+       return (0);
+}
+
+static void setup_empty_event(struct tty_struct *tty)
+{
+       struct moxa_str *ch = tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       ch->statusflags |= EMPTYWAIT;
+       moxaEmptyTimer_on[ch->port] = 0;
+       del_timer(&moxaEmptyTimer[ch->port]);
+       moxaEmptyTimer[ch->port].expires = jiffies + HZ;
+       moxaEmptyTimer_on[ch->port] = 1;
+       add_timer(&moxaEmptyTimer[ch->port]);
+       restore_flags(flags);
+}
+
+static void check_xmit_empty(unsigned long data)
+{
+       struct moxa_str *ch;
+
+       ch = (struct moxa_str *) data;
+       moxaEmptyTimer_on[ch->port] = 0;
+       del_timer(&moxaEmptyTimer[ch->port]);
+       if (ch->tty && (ch->statusflags & EMPTYWAIT)) {
+               if (MoxaPortTxQueue(ch->port) == 0) {
+                       ch->statusflags &= ~EMPTYWAIT;
+                       if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                           ch->tty->ldisc.write_wakeup)
+                               (ch->tty->ldisc.write_wakeup) (ch->tty);
+                       wake_up_interruptible(&ch->tty->write_wait);
+                       return;
+               }
+               moxaEmptyTimer[ch->port].expires = jiffies + HZ;
+               moxaEmptyTimer_on[ch->port] = 1;
+               add_timer(&moxaEmptyTimer[ch->port]);
+       } else
+               ch->statusflags &= ~EMPTYWAIT;
+}
+
+static void shut_down(struct moxa_str *ch)
+{
+       struct tty_struct *tp;
+
+       if (!(ch->asyncflags & ASYNC_INITIALIZED))
+               return;
+
+       tp = ch->tty;
+
+       MoxaPortDisable(ch->port);
+
+       /*
+        * If we're a modem control device and HUPCL is on, drop RTS & DTR.
+        */
+       if (tp->termios->c_cflag & HUPCL)
+               MoxaPortLineCtrl(ch->port, 0, 0);
+
+       ch->asyncflags &= ~ASYNC_INITIALIZED;
+}
+
+static void receive_data(struct moxa_str *ch)
+{
+       struct tty_struct *tp;
+       struct termios *ts;
+       int i, count, rc, space;
+       unsigned char *charptr, *flagptr;
+       unsigned long flags;
+
+       ts = 0;
+       tp = ch->tty;
+       if (tp)
+               ts = tp->termios;
+       /**************************************************
+       if ( !tp || !ts || !(ts->c_cflag & CREAD) ) {
+       *****************************************************/
+       if (!tp || !ts) {
+               MoxaPortFlushData(ch->port, 0);
+               return;
+       }
+       space = TTY_FLIPBUF_SIZE - tp->flip.count;
+       if (space <= 0)
+               return;
+       charptr = tp->flip.char_buf_ptr;
+       flagptr = tp->flip.flag_buf_ptr;
+       rc = tp->flip.count;
+       save_flags(flags);
+       cli();
+       count = MoxaPortReadData(ch->port, charptr, space);
+       restore_flags(flags);
+       for (i = 0; i < count; i++)
+               *flagptr++ = 0;
+       charptr += count;
+       rc += count;
+       tp->flip.count = rc;
+       tp->flip.char_buf_ptr = charptr;
+       tp->flip.flag_buf_ptr = flagptr;
+       tty_schedule_flip(ch->tty);
+}
+
+#define Magic_code     0x404
+
+/*
+ *    System Configuration
+ */
+/*
+ *    for C218 BIOS initialization
+ */
+#define C218_ConfBase  0x800
+#define C218_status    (C218_ConfBase + 0)     /* BIOS running status    */
+#define C218_diag      (C218_ConfBase + 2)     /* diagnostic status      */
+#define C218_key       (C218_ConfBase + 4)     /* WORD (0x218 for C218) */
+#define C218DLoad_len  (C218_ConfBase + 6)     /* WORD           */
+#define C218check_sum  (C218_ConfBase + 8)     /* BYTE           */
+#define C218chksum_ok  (C218_ConfBase + 0x0a)  /* BYTE (1:ok)            */
+#define C218_TestRx    (C218_ConfBase + 0x10)  /* 8 bytes for 8 ports    */
+#define C218_TestTx    (C218_ConfBase + 0x18)  /* 8 bytes for 8 ports    */
+#define C218_RXerr     (C218_ConfBase + 0x20)  /* 8 bytes for 8 ports    */
+#define C218_ErrFlag   (C218_ConfBase + 0x28)  /* 8 bytes for 8 ports    */
+
+#define C218_LoadBuf   0x0F00
+#define C218_KeyCode   0x218
+#define CP204J_KeyCode 0x204
+
+/*
+ *    for C320 BIOS initialization
+ */
+#define C320_ConfBase  0x800
+#define C320_LoadBuf   0x0f00
+#define STS_init       0x05    /* for C320_status        */
+
+#define C320_status    C320_ConfBase + 0       /* BIOS running status    */
+#define C320_diag      C320_ConfBase + 2       /* diagnostic status      */
+#define C320_key       C320_ConfBase + 4       /* WORD (0320H for C320) */
+#define C320DLoad_len  C320_ConfBase + 6       /* WORD           */
+#define C320check_sum  C320_ConfBase + 8       /* WORD           */
+#define C320chksum_ok  C320_ConfBase + 0x0a    /* WORD (1:ok)            */
+#define C320bapi_len   C320_ConfBase + 0x0c    /* WORD           */
+#define C320UART_no    C320_ConfBase + 0x0e    /* WORD           */
+
+#define C320_KeyCode   0x320
+
+#define FixPage_addr   0x0000  /* starting addr of static page  */
+#define DynPage_addr   0x2000  /* starting addr of dynamic page */
+#define C218_start     0x3000  /* starting addr of C218 BIOS prg */
+#define Control_reg    0x1ff0  /* select page and reset control */
+#define HW_reset       0x80
+
+/*
+ *    Function Codes
+ */
+#define FC_CardReset   0x80
+#define FC_ChannelReset 1      /* C320 firmware not supported */
+#define FC_EnableCH    2
+#define FC_DisableCH   3
+#define FC_SetParam    4
+#define FC_SetMode     5
+#define FC_SetRate     6
+#define FC_LineControl 7
+#define FC_LineStatus  8
+#define FC_XmitControl 9
+#define FC_FlushQueue  10
+#define FC_SendBreak   11
+#define FC_StopBreak   12
+#define FC_LoopbackON  13
+#define FC_LoopbackOFF 14
+#define FC_ClrIrqTable 15
+#define FC_SendXon     16
+#define FC_SetTermIrq  17      /* C320 firmware not supported */
+#define FC_SetCntIrq   18      /* C320 firmware not supported */
+#define FC_SetBreakIrq 19
+#define FC_SetLineIrq  20
+#define FC_SetFlowCtl  21
+#define FC_GenIrq      22
+#define FC_InCD180     23
+#define FC_OutCD180    24
+#define FC_InUARTreg   23
+#define FC_OutUARTreg  24
+#define FC_SetXonXoff  25
+#define FC_OutCD180CCR 26
+#define FC_ExtIQueue   27
+#define FC_ExtOQueue   28
+#define FC_ClrLineIrq  29
+#define FC_HWFlowCtl   30
+#define FC_GetClockRate 35
+#define FC_SetBaud     36
+#define FC_SetDataMode  41
+#define FC_GetCCSR      43
+#define FC_GetDataError 45
+#define FC_RxControl   50
+#define FC_ImmSend     51
+#define FC_SetXonState 52
+#define FC_SetXoffState        53
+#define FC_SetRxFIFOTrig 54
+#define FC_SetTxFIFOCnt 55
+#define FC_UnixRate    56
+#define FC_UnixResetTimer 57
+
+#define        RxFIFOTrig1     0
+#define        RxFIFOTrig4     1
+#define        RxFIFOTrig8     2
+#define        RxFIFOTrig14    3
+
+/*
+ *    Dual-Ported RAM
+ */
+#define DRAM_global    0
+#define INT_data       (DRAM_global + 0)
+#define Config_base    (DRAM_global + 0x108)
+
+#define IRQindex       (INT_data + 0)
+#define IRQpending     (INT_data + 4)
+#define IRQtable       (INT_data + 8)
+
+/*
+ *    Interrupt Status
+ */
+#define IntrRx         0x01    /* receiver data O.K.             */
+#define IntrTx         0x02    /* transmit buffer empty  */
+#define IntrFunc       0x04    /* function complete              */
+#define IntrBreak      0x08    /* received break         */
+#define IntrLine       0x10    /* line status change
+                                  for transmitter                */
+#define IntrIntr       0x20    /* received INTR code             */
+#define IntrQuit       0x40    /* received QUIT code             */
+#define IntrEOF        0x80    /* received EOF code              */
+
+#define IntrRxTrigger  0x100   /* rx data count reach tigger value */
+#define IntrTxTrigger  0x200   /* tx data count below trigger value */
+
+#define Magic_no       (Config_base + 0)
+#define Card_model_no  (Config_base + 2)
+#define Total_ports    (Config_base + 4)
+#define Module_cnt     (Config_base + 8)
+#define Module_no      (Config_base + 10)
+#define Timer_10ms     (Config_base + 14)
+#define Disable_IRQ    (Config_base + 20)
+#define TMS320_PORT1   (Config_base + 22)
+#define TMS320_PORT2   (Config_base + 24)
+#define TMS320_CLOCK   (Config_base + 26)
+
+/*
+ *    DATA BUFFER in DRAM
+ */
+#define Extern_table   0x400   /* Base address of the external table
+                                  (24 words *    64) total 3K bytes
+                                  (24 words * 128) total 6K bytes */
+#define Extern_size    0x60    /* 96 bytes                       */
+#define RXrptr         0x00    /* read pointer for RX buffer     */
+#define RXwptr         0x02    /* write pointer for RX buffer    */
+#define TXrptr         0x04    /* read pointer for TX buffer     */
+#define TXwptr         0x06    /* write pointer for TX buffer    */
+#define HostStat       0x08    /* IRQ flag and general flag      */
+#define FlagStat       0x0A
+#define FlowControl    0x0C    /* B7 B6 B5 B4 B3 B2 B1 B0              */
+                                       /*  x  x  x  x  |  |  |  |            */
+                                       /*              |  |  |  + CTS flow   */
+                                       /*              |  |  +--- RTS flow   */
+                                       /*              |  +------ TX Xon/Xoff */
+                                       /*              +--------- RX Xon/Xoff */
+#define Break_cnt      0x0E    /* received break count   */
+#define CD180TXirq     0x10    /* if non-0: enable TX irq        */
+#define RX_mask        0x12
+#define TX_mask        0x14
+#define Ofs_rxb        0x16
+#define Ofs_txb        0x18
+#define Page_rxb       0x1A
+#define Page_txb       0x1C
+#define EndPage_rxb    0x1E
+#define EndPage_txb    0x20
+#define Data_error     0x22
+#define RxTrigger      0x28
+#define TxTrigger      0x2a
+
+#define rRXwptr        0x34
+#define Low_water      0x36
+
+#define FuncCode       0x40
+#define FuncArg        0x42
+#define FuncArg1       0x44
+
+#define C218rx_size    0x2000  /* 8K bytes */
+#define C218tx_size    0x8000  /* 32K bytes */
+
+#define C218rx_mask    (C218rx_size - 1)
+#define C218tx_mask    (C218tx_size - 1)
+
+#define C320p8rx_size  0x2000
+#define C320p8tx_size  0x8000
+#define C320p8rx_mask  (C320p8rx_size - 1)
+#define C320p8tx_mask  (C320p8tx_size - 1)
+
+#define C320p16rx_size 0x2000
+#define C320p16tx_size 0x4000
+#define C320p16rx_mask (C320p16rx_size - 1)
+#define C320p16tx_mask (C320p16tx_size - 1)
+
+#define C320p24rx_size 0x2000
+#define C320p24tx_size 0x2000
+#define C320p24rx_mask (C320p24rx_size - 1)
+#define C320p24tx_mask (C320p24tx_size - 1)
+
+#define C320p32rx_size 0x1000
+#define C320p32tx_size 0x1000
+#define C320p32rx_mask (C320p32rx_size - 1)
+#define C320p32tx_mask (C320p32tx_size - 1)
+
+#define Page_size      0x2000
+#define Page_mask      (Page_size - 1)
+#define C218rx_spage   3
+#define C218tx_spage   4
+#define C218rx_pageno  1
+#define C218tx_pageno  4
+#define C218buf_pageno 5
+
+#define C320p8rx_spage 3
+#define C320p8tx_spage 4
+#define C320p8rx_pgno  1
+#define C320p8tx_pgno  4
+#define C320p8buf_pgno 5
+
+#define C320p16rx_spage 3
+#define C320p16tx_spage 4
+#define C320p16rx_pgno 1
+#define C320p16tx_pgno 2
+#define C320p16buf_pgno 3
+
+#define C320p24rx_spage 3
+#define C320p24tx_spage 4
+#define C320p24rx_pgno 1
+#define C320p24tx_pgno 1
+#define C320p24buf_pgno 2
+
+#define C320p32rx_spage 3
+#define C320p32tx_ofs  C320p32rx_size
+#define C320p32tx_spage 3
+#define C320p32buf_pgno 1
+
+/*
+ *    Host Status
+ */
+#define WakeupRx       0x01
+#define WakeupTx       0x02
+#define WakeupBreak    0x08
+#define WakeupLine     0x10
+#define WakeupIntr     0x20
+#define WakeupQuit     0x40
+#define WakeupEOF      0x80    /* used in VTIME control */
+#define WakeupRxTrigger        0x100
+#define WakeupTxTrigger        0x200
+/*
+ *    Flag status
+ */
+#define Rx_over                0x01
+#define Xoff_state     0x02
+#define Tx_flowOff     0x04
+#define Tx_enable      0x08
+#define CTS_state      0x10
+#define DSR_state      0x20
+#define DCD_state      0x80
+/*
+ *    FlowControl
+ */
+#define CTS_FlowCtl    1
+#define RTS_FlowCtl    2
+#define Tx_FlowCtl     4
+#define Rx_FlowCtl     8
+#define IXM_IXANY      0x10
+
+#define LowWater       128
+
+#define DTR_ON         1
+#define RTS_ON         2
+#define CTS_ON         1
+#define DSR_ON         2
+#define DCD_ON         8
+
+/* mode definition */
+#define        MX_CS8          0x03
+#define        MX_CS7          0x02
+#define        MX_CS6          0x01
+#define        MX_CS5          0x00
+
+#define        MX_STOP1        0x00
+#define        MX_STOP15       0x04
+#define        MX_STOP2        0x08
+
+#define        MX_PARNONE      0x00
+#define        MX_PAREVEN      0x40
+#define        MX_PARODD       0xC0
+
+/*
+ *    Query
+ */
+#define QueryPort      MAX_PORTS
+
+
+
+struct mon_str {
+       int tick;
+       int rxcnt[MAX_PORTS];
+       int txcnt[MAX_PORTS];
+};
+typedef struct mon_str mon_st;
+
+#define        DCD_changed     0x01
+#define        DCD_oldstate    0x80
+
+static unsigned char moxaBuff[10240];
+static unsigned long moxaIntNdx[MAX_BOARDS];
+static unsigned long moxaIntPend[MAX_BOARDS];
+static unsigned long moxaIntTable[MAX_BOARDS];
+static char moxaChkPort[MAX_PORTS];
+static char moxaLineCtrl[MAX_PORTS];
+static unsigned long moxaTableAddr[MAX_PORTS];
+static long moxaCurBaud[MAX_PORTS];
+static char moxaDCDState[MAX_PORTS];
+static char moxaLowChkFlag[MAX_PORTS];
+static int moxaLowWaterChk;
+static int moxaCard;
+static mon_st moxaLog;
+static int moxaFuncTout;
+static ushort moxaBreakCnt[MAX_PORTS];
+
+static void moxadelay(int);
+static void moxafunc(unsigned long, int, ushort);
+static void wait_finish(unsigned long);
+static void low_water_check(unsigned long);
+static int moxaloadbios(int, unsigned char *, int);
+static int moxafindcard(int);
+static int moxaload320b(int, unsigned char *, int);
+static int moxaloadcode(int, unsigned char *, int);
+static int moxaloadc218(int, unsigned long, int);
+static int moxaloadc320(int, unsigned long, int, int *);
+
+/*****************************************************************************
+ *     Driver level functions:                                              *
+ *     1. MoxaDriverInit(void);                                             *
+ *     2. MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);   *
+ *     3. MoxaDriverPoll(void);                                             *
+ *****************************************************************************/
+void MoxaDriverInit(void)
+{
+       int i;
+
+       moxaFuncTout = HZ / 2;  /* 500 mini-seconds */
+       moxaCard = 0;
+       moxaLog.tick = 0;
+       moxaLowWaterChk = 0;
+       for (i = 0; i < MAX_PORTS; i++) {
+               moxaChkPort[i] = 0;
+               moxaLowChkFlag[i] = 0;
+               moxaLineCtrl[i] = 0;
+               moxaLog.rxcnt[i] = 0;
+               moxaLog.txcnt[i] = 0;
+       }
+}
+
+#define        MOXA            0x400
+#define MOXA_GET_IQUEUE        (MOXA + 1)      /* get input buffered count */
+#define MOXA_GET_OQUEUE        (MOXA + 2)      /* get output buffered count */
+#define MOXA_INIT_DRIVER       (MOXA + 6)      /* moxaCard=0 */
+#define MOXA_LOAD_BIOS         (MOXA + 9)      /* download BIOS */
+#define MOXA_FIND_BOARD                (MOXA + 10)     /* Check if MOXA card exist? */
+#define MOXA_LOAD_C320B                (MOXA + 11)     /* download 320B firmware */
+#define MOXA_LOAD_CODE         (MOXA + 12)     /* download firmware */
+#define MOXA_GETDATACOUNT       (MOXA + 23)
+#define MOXA_GET_IOQUEUE       (MOXA + 27)
+#define MOXA_FLUSH_QUEUE       (MOXA + 28)
+#define MOXA_GET_CONF          (MOXA + 35)     /* configuration */
+#define MOXA_GET_MAJOR          (MOXA + 63)
+#define MOXA_GET_CUMAJOR        (MOXA + 64)
+#define MOXA_GETMSTATUS         (MOXA + 65)
+
+
+struct moxaq_str {
+       int inq;
+       int outq;
+};
+
+struct dl_str {
+       char *buf;
+       int len;
+       int cardno;
+};
+
+static struct moxaq_str temp_queue[MAX_PORTS];
+static struct dl_str dltmp;
+
+void MoxaPortFlushData(int port, int mode)
+{
+       unsigned long ofsAddr;
+       if ((mode < 0) || (mode > 2))
+               return;
+       ofsAddr = moxaTableAddr[port];
+       moxafunc(ofsAddr, FC_FlushQueue, mode);
+       if (mode != 1) {
+               moxaLowChkFlag[port] = 0;
+               low_water_check(ofsAddr);
+       }
+}
+
+int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port)
+{
+       int i;
+       int status;
+       int MoxaPortTxQueue(int), MoxaPortRxQueue(int);
+
+       if (port == QueryPort) {
+               if ((cmd != MOXA_GET_CONF) && (cmd != MOXA_INIT_DRIVER) &&
+                   (cmd != MOXA_LOAD_BIOS) && (cmd != MOXA_FIND_BOARD) && (cmd != MOXA_LOAD_C320B) &&
+                (cmd != MOXA_LOAD_CODE) && (cmd != MOXA_GETDATACOUNT) &&
+                 (cmd != MOXA_GET_IOQUEUE) && (cmd != MOXA_GET_MAJOR) &&
+                   (cmd != MOXA_GET_CUMAJOR) && (cmd != MOXA_GETMSTATUS))
+                       return (-EINVAL);
+       }
+       switch (cmd) {
+       case MOXA_GET_CONF:
+               if(copy_to_user((void *)arg, &moxa_boards, MAX_BOARDS * sizeof(moxa_board_conf)))
+                       return -EFAULT;
+               return (0);
+       case MOXA_INIT_DRIVER:
+               if ((int) arg == 0x404)
+                       MoxaDriverInit();
+               return (0);
+       case MOXA_GETDATACOUNT:
+               moxaLog.tick = jiffies;
+               if(copy_to_user((void *)arg, &moxaLog, sizeof(mon_st)))
+                       return -EFAULT;
+               return (0);
+       case MOXA_FLUSH_QUEUE:
+               MoxaPortFlushData(port, arg);
+               return (0);
+       case MOXA_GET_IOQUEUE:
+               for (i = 0; i < MAX_PORTS; i++) {
+                       if (moxaChkPort[i]) {
+                               temp_queue[i].inq = MoxaPortRxQueue(i);
+                               temp_queue[i].outq = MoxaPortTxQueue(i);
+                       }
+               }
+               if(copy_to_user((void *)arg, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS))
+                       return -EFAULT;
+               return (0);
+       case MOXA_LOAD_BIOS:
+               if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str)))
+                       return -EFAULT;
+               i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len);
+               return (i);
+       case MOXA_FIND_BOARD:
+               if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str)))
+                       return -EFAULT;
+               return moxafindcard(dltmp.cardno);
+       case MOXA_LOAD_C320B:
+               if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str)))
+                       return -EFAULT;
+               moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len);
+               return (0);
+       case MOXA_LOAD_CODE:
+               if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str)))
+                       return -EFAULT; 
+               i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len);
+               if (i == -1)
+                       return (-EFAULT);
+               return (i);
+       case MOXA_GET_OQUEUE:
+               i = MoxaPortTxQueue(port);
+               return put_user(i, (unsigned long *) arg);
+       case MOXA_GET_IQUEUE:
+               i = MoxaPortRxQueue(port);
+               return put_user(i, (unsigned long *) arg);
+       case MOXA_GET_MAJOR:
+               if(copy_to_user((void *)arg, &ttymajor, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+       case MOXA_GET_CUMAJOR:
+               if(copy_to_user((void *)arg, &calloutmajor, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+       case MOXA_GETMSTATUS:
+               for (i = 0; i < MAX_PORTS; i++) {
+                       GMStatus[i].ri = 0;
+                       GMStatus[i].dcd = 0;
+                       GMStatus[i].dsr = 0;
+                       GMStatus[i].cts = 0;
+                       if (!moxaChkPort[i]) {
+                               continue;
+                       } else {
+                               status = MoxaPortLineStatus(moxaChannels[i].port);
+                               if (status & 1)
+                                       GMStatus[i].cts = 1;
+                               if (status & 2)
+                                       GMStatus[i].dsr = 1;
+                               if (status & 4)
+                                       GMStatus[i].dcd = 1;
+                       }
+
+                       if (!moxaChannels[i].tty || !moxaChannels[i].tty->termios)
+                               GMStatus[i].cflag = moxaChannels[i].normal_termios.c_cflag;
+                       else
+                               GMStatus[i].cflag = moxaChannels[i].tty->termios->c_cflag;
+               }
+               if(copy_to_user((void *)arg, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS))
+                       return -EFAULT;
+               return 0;
+
+       }
+       return (-ENOIOCTLCMD);
+}
+
+int MoxaDriverPoll(void)
+{
+       register ushort temp;
+       register int card;
+       unsigned long ip, ofsAddr;
+       int port, p, ports;
+
+       if (moxaCard == 0)
+               return (-1);
+       for (card = 0; card < MAX_BOARDS; card++) {
+               if ((ports = moxa_boards[card].numPorts) == 0)
+                       continue;
+               if (readb(moxaIntPend[card]) == 0xff) {
+                       ip = moxaIntTable[card] + readb(moxaIntNdx[card]);
+                       p = card * MAX_PORTS_PER_BOARD;
+                       ports <<= 1;
+                       for (port = 0; port < ports; port += 2, p++) {
+                               if ((temp = readw(ip + port)) != 0) {
+                                       writew(0, ip + port);
+                                       ofsAddr = moxaTableAddr[p];
+                                       if (temp & IntrTx)
+                                               writew(readw(ofsAddr + HostStat) & ~WakeupTx, ofsAddr + HostStat);
+                                       if (temp & IntrBreak) {
+                                               moxaBreakCnt[p]++;
+                                       }
+                                       if (temp & IntrLine) {
+                                               if (readb(ofsAddr + FlagStat) & DCD_state) {
+                                                       if ((moxaDCDState[p] & DCD_oldstate) == 0)
+                                                               moxaDCDState[p] = (DCD_oldstate |
+                                                                                  DCD_changed);
+                                               } else {
+                                                       if (moxaDCDState[p] & DCD_oldstate)
+                                                               moxaDCDState[p] = DCD_changed;
+                                               }
+                                       }
+                               }
+                       }
+                       writeb(0, moxaIntPend[card]);
+               }
+               if (moxaLowWaterChk) {
+                       p = card * MAX_PORTS_PER_BOARD;
+                       for (port = 0; port < ports; port++, p++) {
+                               if (moxaLowChkFlag[p]) {
+                                       moxaLowChkFlag[p] = 0;
+                                       ofsAddr = moxaTableAddr[p];
+                                       low_water_check(ofsAddr);
+                               }
+                       }
+               }
+       }
+       moxaLowWaterChk = 0;
+       return (0);
+}
+
+/*****************************************************************************
+ *     Card level function:                                                 *
+ *     1. MoxaPortsOfCard(int cardno);                                      *
+ *****************************************************************************/
+int MoxaPortsOfCard(int cardno)
+{
+
+       if (moxa_boards[cardno].boardType == 0)
+               return (0);
+       return (moxa_boards[cardno].numPorts);
+}
+
+/*****************************************************************************
+ *     Port level functions:                                                *
+ *     1.  MoxaPortIsValid(int port);                                       *
+ *     2.  MoxaPortEnable(int port);                                        *
+ *     3.  MoxaPortDisable(int port);                                       *
+ *     4.  MoxaPortGetMaxBaud(int port);                                    *
+ *     5.  MoxaPortGetCurBaud(int port);                                    *
+ *     6.  MoxaPortSetBaud(int port, long baud);                            *
+ *     7.  MoxaPortSetMode(int port, int databit, int stopbit, int parity); *
+ *     8.  MoxaPortSetTermio(int port, unsigned char *termio);              *
+ *     9.  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);      *
+ *     10. MoxaPortLineCtrl(int port, int dtrState, int rtsState);          *
+ *     11. MoxaPortFlowCtrl(int port, int rts, int cts, int rx, int tx,int xany);    *
+ *     12. MoxaPortLineStatus(int port);                                    *
+ *     13. MoxaPortDCDChange(int port);                                     *
+ *     14. MoxaPortDCDON(int port);                                         *
+ *     15. MoxaPortFlushData(int port, int mode);                           *
+ *     16. MoxaPortWriteData(int port, unsigned char * buffer, int length); *
+ *     17. MoxaPortReadData(int port, unsigned char * buffer, int length);  *
+ *     18. MoxaPortTxBufSize(int port);                                     *
+ *     19. MoxaPortRxBufSize(int port);                                     *
+ *     20. MoxaPortTxQueue(int port);                                       *
+ *     21. MoxaPortTxFree(int port);                                        *
+ *     22. MoxaPortRxQueue(int port);                                       *
+ *     23. MoxaPortRxFree(int port);                                        *
+ *     24. MoxaPortTxDisable(int port);                                     *
+ *     25. MoxaPortTxEnable(int port);                                      *
+ *     26. MoxaPortGetBrkCnt(int port);                                     *
+ *     27. MoxaPortResetBrkCnt(int port);                                   *
+ *     28. MoxaPortSetXonXoff(int port, int xonValue, int xoffValue);       *
+ *     29. MoxaPortIsTxHold(int port);                                      *
+ *     30. MoxaPortSendBreak(int port, int ticks);                          *
+ *****************************************************************************/
+/*
+ *    Moxa Port Number Description:
+ *
+ *      MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And,
+ *      the port number using in MOXA driver functions will be 0 to 31 for
+ *      first MOXA board, 32 to 63 for second, 64 to 95 for third and 96
+ *      to 127 for fourth. For example, if you setup three MOXA boards,
+ *      first board is C218, second board is C320-16 and third board is
+ *      C320-32. The port number of first board (C218 - 8 ports) is from
+ *      0 to 7. The port number of second board (C320 - 16 ports) is form
+ *      32 to 47. The port number of third board (C320 - 32 ports) is from
+ *      64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to
+ *      127 will be invalid.
+ *
+ *
+ *      Moxa Functions Description:
+ *
+ *      Function 1:     Driver initialization routine, this routine must be
+ *                      called when initialized driver.
+ *      Syntax:
+ *      void MoxaDriverInit();
+ *
+ *
+ *      Function 2:     Moxa driver private IOCTL command processing.
+ *      Syntax:
+ *      int  MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);
+ *
+ *           unsigned int cmd   : IOCTL command
+ *           unsigned long arg  : IOCTL argument
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0  (OK)
+ *                      -EINVAL
+ *                      -ENOIOCTLCMD
+ *
+ *
+ *      Function 3:     Moxa driver polling process routine.
+ *      Syntax:
+ *      int  MoxaDriverPoll(void);
+ *
+ *           return:    0       ; polling O.K.
+ *                      -1      : no any Moxa card.             
+ *
+ *
+ *      Function 4:     Get the ports of this card.
+ *      Syntax:
+ *      int  MoxaPortsOfCard(int cardno);
+ *
+ *           int cardno         : card number (0 - 3)
+ *
+ *           return:    0       : this card is invalid
+ *                      8/16/24/32
+ *
+ *
+ *      Function 5:     Check this port is valid or invalid
+ *      Syntax:
+ *      int  MoxaPortIsValid(int port);
+ *           int port           : port number (0 - 127, ref port description)
+ *
+ *           return:    0       : this port is invalid
+ *                      1       : this port is valid
+ *
+ *
+ *      Function 6:     Enable this port to start Tx/Rx data.
+ *      Syntax:
+ *      void MoxaPortEnable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 7:     Disable this port
+ *      Syntax:
+ *      void MoxaPortDisable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 8:     Get the maximun available baud rate of this port.
+ *      Syntax:
+ *      long MoxaPortGetMaxBaud(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0       : this port is invalid
+ *                      38400/57600/115200 bps
+ *
+ *
+ *      Function 9:     Get the current baud rate of this port.
+ *      Syntax:
+ *      long MoxaPortGetCurBaud(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0       : this port is invalid
+ *                      50 - 115200 bps
+ *
+ *
+ *      Function 10:    Setting baud rate of this port.
+ *      Syntax:
+ *      long MoxaPortSetBaud(int port, long baud);
+ *           int port           : port number (0 - 127)
+ *           long baud          : baud rate (50 - 115200)
+ *
+ *           return:    0       : this port is invalid or baud < 50
+ *                      50 - 115200 : the real baud rate set to the port, if
+ *                                    the argument baud is large than maximun
+ *                                    available baud rate, the real setting
+ *                                    baud rate will be the maximun baud rate.
+ *
+ *
+ *      Function 11:    Setting the data-bits/stop-bits/parity of this port
+ *      Syntax:
+ *      int  MoxaPortSetMode(int port, int databits, int stopbits, int parity);
+ *           int port           : port number (0 - 127)
+ *           int databits       : data bits (8/7/6/5)
+ *           int stopbits       : stop bits (2/1/0, 0 show 1.5 stop bits)
+ int parity     : parity (0:None,1:Odd,2:Even,3:Mark,4:Space)
+ *
+ *           return:    -1      : invalid parameter
+ *                      0       : setting O.K.
+ *
+ *
+ *      Function 12:    Configure the port.
+ *      Syntax:
+ *      int  MoxaPortSetTermio(int port, struct termios *termio);
+ *           int port           : port number (0 - 127)
+ *           struct termios * termio : termio structure pointer
+ *
+ *           return:    -1      : this port is invalid or termio == NULL
+ *                      0       : setting O.K.
+ *
+ *
+ *      Function 13:    Get the DTR/RTS state of this port.
+ *      Syntax:
+ *      int  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);
+ *           int port           : port number (0 - 127)
+ *           int * dtrState     : pointer to INT to receive the current DTR
+ *                                state. (if NULL, this function will not
+ *                                write to this address)
+ *           int * rtsState     : pointer to INT to receive the current RTS
+ *                                state. (if NULL, this function will not
+ *                                write to this address)
+ *
+ *           return:    -1      : this port is invalid
+ *                      0       : O.K.
+ *
+ *
+ *      Function 14:    Setting the DTR/RTS output state of this port.
+ *      Syntax:
+ *      void MoxaPortLineCtrl(int port, int dtrState, int rtsState);
+ *           int port           : port number (0 - 127)
+ *           int dtrState       : DTR output state (0: off, 1: on)
+ *           int rtsState       : RTS output state (0: off, 1: on)
+ *
+ *
+ *      Function 15:    Setting the flow control of this port.
+ *      Syntax:
+ *      void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow,
+ *                            int txFlow,int xany);
+ *           int port           : port number (0 - 127)
+ *           int rtsFlow        : H/W RTS flow control (0: no, 1: yes)
+ *           int ctsFlow        : H/W CTS flow control (0: no, 1: yes)
+ *           int rxFlow         : S/W Rx XON/XOFF flow control (0: no, 1: yes)
+ *           int txFlow         : S/W Tx XON/XOFF flow control (0: no, 1: yes)
+ *           int xany           : S/W XANY flow control (0: no, 1: yes)
+ *
+ *
+ *      Function 16:    Get ths line status of this port
+ *      Syntax:
+ *      int  MoxaPortLineStatus(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    Bit 0 - CTS state (0: off, 1: on)
+ *                      Bit 1 - DSR state (0: off, 1: on)
+ *                      Bit 2 - DCD state (0: off, 1: on)
+ *
+ *
+ *      Function 17:    Check the DCD state has changed since the last read
+ *                      of this function.
+ *      Syntax:
+ *      int  MoxaPortDCDChange(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0       : no changed
+ *                      1       : DCD has changed
+ *
+ *
+ *      Function 18:    Check ths current DCD state is ON or not.
+ *      Syntax:
+ *      int  MoxaPortDCDON(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0       : DCD off
+ *                      1       : DCD on
+ *
+ *
+ *      Function 19:    Flush the Rx/Tx buffer data of this port.
+ *      Syntax:
+ *      void MoxaPortFlushData(int port, int mode);
+ *           int port           : port number (0 - 127)
+ *           int mode    
+ *                      0       : flush the Rx buffer 
+ *                      1       : flush the Tx buffer 
+ *                      2       : flush the Rx and Tx buffer 
+ *
+ *
+ *      Function 20:    Write data.
+ *      Syntax:
+ *      int  MoxaPortWriteData(int port, unsigned char * buffer, int length);
+ *           int port           : port number (0 - 127)
+ *           unsigned char * buffer     : pointer to write data buffer.
+ *           int length         : write data length
+ *
+ *           return:    0 - length      : real write data length
+ *
+ *
+ *      Function 21:    Read data.
+ *      Syntax:
+ *      int  MoxaPortReadData(int port, unsigned char * buffer, int length);
+ *           int port           : port number (0 - 127)
+ *           unsigned char * buffer     : pointer to read data buffer.
+ *           int length         : read data buffer length
+ *
+ *           return:    0 - length      : real read data length
+ *
+ *
+ *      Function 22:    Get the Tx buffer size of this port
+ *      Syntax:
+ *      int  MoxaPortTxBufSize(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Tx buffer size
+ *
+ *
+ *      Function 23:    Get the Rx buffer size of this port
+ *      Syntax:
+ *      int  MoxaPortRxBufSize(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Rx buffer size
+ *
+ *
+ *      Function 24:    Get the Tx buffer current queued data bytes
+ *      Syntax:
+ *      int  MoxaPortTxQueue(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Tx buffer current queued data bytes
+ *
+ *
+ *      Function 25:    Get the Tx buffer current free space
+ *      Syntax:
+ *      int  MoxaPortTxFree(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Tx buffer current free space
+ *
+ *
+ *      Function 26:    Get the Rx buffer current queued data bytes
+ *      Syntax:
+ *      int  MoxaPortRxQueue(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Rx buffer current queued data bytes
+ *
+ *
+ *      Function 27:    Get the Rx buffer current free space
+ *      Syntax:
+ *      int  MoxaPortRxFree(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Rx buffer current free space
+ *
+ *
+ *      Function 28:    Disable port data transmission.
+ *      Syntax:
+ *      void MoxaPortTxDisable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 29:    Enable port data transmission.
+ *      Syntax:
+ *      void MoxaPortTxEnable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 30:    Get the received BREAK signal count.
+ *      Syntax:
+ *      int  MoxaPortGetBrkCnt(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0 - ..  : BREAK signal count
+ *
+ *
+ *      Function 31:    Get the received BREAK signal count and reset it.
+ *      Syntax:
+ *      int  MoxaPortResetBrkCnt(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0 - ..  : BREAK signal count
+ *
+ *
+ *      Function 32:    Set the S/W flow control new XON/XOFF value, default
+ *                      XON is 0x11 & XOFF is 0x13.
+ *      Syntax:
+ *      void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue);
+ *           int port           : port number (0 - 127)
+ *           int xonValue       : new XON value (0 - 255)
+ *           int xoffValue      : new XOFF value (0 - 255)
+ *
+ *
+ *      Function 33:    Check this port's transmission is hold by remote site
+ *                      because the flow control.
+ *      Syntax:
+ *      int  MoxaPortIsTxHold(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0       : normal
+ *                      1       : hold by remote site
+ *
+ *
+ *      Function 34:    Send out a BREAK signal.
+ *      Syntax:
+ *      void MoxaPortSendBreak(int port, int ms100);
+ *           int port           : port number (0 - 127)
+ *           int ms100          : break signal time interval.
+ *                                unit: 100 mini-second. if ms100 == 0, it will
+ *                                send out a about 250 ms BREAK signal.
+ *
+ */
+int MoxaPortIsValid(int port)
+{
+
+       if (moxaCard == 0)
+               return (0);
+       if (moxaChkPort[port] == 0)
+               return (0);
+       return (1);
+}
+
+void MoxaPortEnable(int port)
+{
+       unsigned long ofsAddr;
+       int MoxaPortLineStatus(int);
+       short lowwater = 512;
+
+       ofsAddr = moxaTableAddr[port];
+       writew(lowwater, ofsAddr + Low_water);
+       moxaBreakCnt[port] = 0;
+       if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
+           (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
+               moxafunc(ofsAddr, FC_SetBreakIrq, 0);
+       } else {
+               writew(readw(ofsAddr + HostStat) | WakeupBreak, ofsAddr + HostStat);
+       }
+
+       moxafunc(ofsAddr, FC_SetLineIrq, Magic_code);
+       moxafunc(ofsAddr, FC_FlushQueue, 2);
+
+       moxafunc(ofsAddr, FC_EnableCH, Magic_code);
+       MoxaPortLineStatus(port);
+}
+
+void MoxaPortDisable(int port)
+{
+       unsigned long ofsAddr;
+
+       ofsAddr = moxaTableAddr[port];
+       moxafunc(ofsAddr, FC_SetFlowCtl, 0);    /* disable flow control */
+       moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code);
+       writew(0, ofsAddr + HostStat);
+       moxafunc(ofsAddr, FC_DisableCH, Magic_code);
+}
+
+long MoxaPortGetMaxBaud(int port)
+{
+       if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
+           (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI))
+               return (460800L);
+       else
+               return (921600L);
+}
+
+
+long MoxaPortSetBaud(int port, long baud)
+{
+       unsigned long ofsAddr;
+       long max, clock;
+       unsigned int val;
+
+       if ((baud < 50L) || ((max = MoxaPortGetMaxBaud(port)) == 0))
+               return (0);
+       ofsAddr = moxaTableAddr[port];
+       if (baud > max)
+               baud = max;
+       if (max == 38400L)
+               clock = 614400L;        /* for 9.8304 Mhz : max. 38400 bps */
+       else if (max == 57600L)
+               clock = 691200L;        /* for 11.0592 Mhz : max. 57600 bps */
+       else
+               clock = 921600L;        /* for 14.7456 Mhz : max. 115200 bps */
+       val = clock / baud;
+       moxafunc(ofsAddr, FC_SetBaud, val);
+       baud = clock / val;
+       moxaCurBaud[port] = baud;
+       return (baud);
+}
+
+int MoxaPortSetTermio(int port, struct termios *termio)
+{
+       unsigned long ofsAddr;
+       tcflag_t cflag;
+       long baud;
+       tcflag_t mode = 0;
+
+       if (moxaChkPort[port] == 0 || termio == 0)
+               return (-1);
+       ofsAddr = moxaTableAddr[port];
+       cflag = termio->c_cflag;        /* termio->c_cflag */
+
+       mode = termio->c_cflag & CSIZE;
+       if (mode == CS5)
+               mode = MX_CS5;
+       else if (mode == CS6)
+               mode = MX_CS6;
+       else if (mode == CS7)
+               mode = MX_CS7;
+       else if (mode == CS8)
+               mode = MX_CS8;
+
+       if (termio->c_cflag & CSTOPB) {
+               if (mode == MX_CS5)
+                       mode |= MX_STOP15;
+               else
+                       mode |= MX_STOP2;
+       } else
+               mode |= MX_STOP1;
+
+       if (termio->c_cflag & PARENB) {
+               if (termio->c_cflag & PARODD)
+                       mode |= MX_PARODD;
+               else
+                       mode |= MX_PAREVEN;
+       } else
+               mode |= MX_PARNONE;
+
+       moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode);
+
+       cflag &= (CBAUD | CBAUDEX);
+#ifndef B921600
+#define        B921600 (B460800+1)
+#endif
+       switch (cflag) {
+       case B921600:
+               baud = 921600L;
+               break;
+       case B460800:
+               baud = 460800L;
+               break;
+       case B230400:
+               baud = 230400L;
+               break;
+       case B115200:
+               baud = 115200L;
+               break;
+       case B57600:
+               baud = 57600L;
+               break;
+       case B38400:
+               baud = 38400L;
+               break;
+       case B19200:
+               baud = 19200L;
+               break;
+       case B9600:
+               baud = 9600L;
+               break;
+       case B4800:
+               baud = 4800L;
+               break;
+       case B2400:
+               baud = 2400L;
+               break;
+       case B1800:
+               baud = 1800L;
+               break;
+       case B1200:
+               baud = 1200L;
+               break;
+       case B600:
+               baud = 600L;
+               break;
+       case B300:
+               baud = 300L;
+               break;
+       case B200:
+               baud = 200L;
+               break;
+       case B150:
+               baud = 150L;
+               break;
+       case B134:
+               baud = 134L;
+               break;
+       case B110:
+               baud = 110L;
+               break;
+       case B75:
+               baud = 75L;
+               break;
+       case B50:
+               baud = 50L;
+               break;
+       default:
+               baud = 0;
+       }
+       if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
+           (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
+               if (baud == 921600L)
+                       return (-1);
+       }
+       MoxaPortSetBaud(port, baud);
+
+       if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
+               writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
+               writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
+               writeb(FC_SetXonXoff, ofsAddr + FuncCode);
+               wait_finish(ofsAddr);
+
+       }
+       return (0);
+}
+
+int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState)
+{
+
+       if (!MoxaPortIsValid(port))
+               return (-1);
+       if (dtrState) {
+               if (moxaLineCtrl[port] & DTR_ON)
+                       *dtrState = 1;
+               else
+                       *dtrState = 0;
+       }
+       if (rtsState) {
+               if (moxaLineCtrl[port] & RTS_ON)
+                       *rtsState = 1;
+               else
+                       *rtsState = 0;
+       }
+       return (0);
+}
+
+void MoxaPortLineCtrl(int port, int dtr, int rts)
+{
+       unsigned long ofsAddr;
+       int mode;
+
+       ofsAddr = moxaTableAddr[port];
+       mode = 0;
+       if (dtr)
+               mode |= DTR_ON;
+       if (rts)
+               mode |= RTS_ON;
+       moxaLineCtrl[port] = mode;
+       moxafunc(ofsAddr, FC_LineControl, mode);
+}
+
+void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow, int txany)
+{
+       unsigned long ofsAddr;
+       int mode;
+
+       ofsAddr = moxaTableAddr[port];
+       mode = 0;
+       if (rts)
+               mode |= RTS_FlowCtl;
+       if (cts)
+               mode |= CTS_FlowCtl;
+       if (txflow)
+               mode |= Tx_FlowCtl;
+       if (rxflow)
+               mode |= Rx_FlowCtl;
+       if (txany)
+               mode |= IXM_IXANY;
+       moxafunc(ofsAddr, FC_SetFlowCtl, mode);
+}
+
+int MoxaPortLineStatus(int port)
+{
+       unsigned long ofsAddr;
+       int val;
+
+       ofsAddr = moxaTableAddr[port];
+       if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
+           (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
+               moxafunc(ofsAddr, FC_LineStatus, 0);
+               val = readw(ofsAddr + FuncArg);
+       } else {
+               val = readw(ofsAddr + FlagStat) >> 4;
+       }
+       val &= 0x0B;
+       if (val & 8) {
+               val |= 4;
+               if ((moxaDCDState[port] & DCD_oldstate) == 0)
+                       moxaDCDState[port] = (DCD_oldstate | DCD_changed);
+       } else {
+               if (moxaDCDState[port] & DCD_oldstate)
+                       moxaDCDState[port] = DCD_changed;
+       }
+       val &= 7;
+       return (val);
+}
+
+int MoxaPortDCDChange(int port)
+{
+       int n;
+
+       if (moxaChkPort[port] == 0)
+               return (0);
+       n = moxaDCDState[port];
+       moxaDCDState[port] &= ~DCD_changed;
+       n &= DCD_changed;
+       return (n);
+}
+
+int MoxaPortDCDON(int port)
+{
+       int n;
+
+       if (moxaChkPort[port] == 0)
+               return (0);
+       if (moxaDCDState[port] & DCD_oldstate)
+               n = 1;
+       else
+               n = 0;
+       return (n);
+}
+
+
+/*
+   int MoxaDumpMem(int port, unsigned char * buffer, int len)
+   {
+   int          i;
+   unsigned long                baseAddr,ofsAddr,ofs;
+
+   baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
+   ofs = baseAddr + DynPage_addr + pageofs;
+   if (len > 0x2000L)
+   len = 0x2000L;
+   for (i = 0; i < len; i++)
+   buffer[i] = readb(ofs+i);
+   }
+ */
+
+
+int MoxaPortWriteData(int port, unsigned char * buffer, int len)
+{
+       int c, total, i;
+       ushort tail;
+       int cnt;
+       ushort head, tx_mask, spage, epage;
+       ushort pageno, pageofs, bufhead;
+       unsigned long baseAddr, ofsAddr, ofs;
+
+       ofsAddr = moxaTableAddr[port];
+       baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
+       tx_mask = readw(ofsAddr + TX_mask);
+       spage = readw(ofsAddr + Page_txb);
+       epage = readw(ofsAddr + EndPage_txb);
+       tail = readw(ofsAddr + TXwptr);
+       head = readw(ofsAddr + TXrptr);
+       c = (head > tail) ? (head - tail - 1)
+           : (head - tail + tx_mask);
+       if (c > len)
+               c = len;
+       moxaLog.txcnt[port] += c;
+       total = c;
+       if (spage == epage) {
+               bufhead = readw(ofsAddr + Ofs_txb);
+               writew(spage, baseAddr + Control_reg);
+               while (c > 0) {
+                       if (head > tail)
+                               len = head - tail - 1;
+                       else
+                               len = tx_mask + 1 - tail;
+                       len = (c > len) ? len : c;
+                       ofs = baseAddr + DynPage_addr + bufhead + tail;
+                       for (i = 0; i < len; i++)
+                               writeb(*buffer++, ofs + i);
+                       tail = (tail + len) & tx_mask;
+                       c -= len;
+               }
+               writew(tail, ofsAddr + TXwptr);
+       } else {
+               len = c;
+               pageno = spage + (tail >> 13);
+               pageofs = tail & Page_mask;
+               do {
+                       cnt = Page_size - pageofs;
+                       if (cnt > c)
+                               cnt = c;
+                       c -= cnt;
+                       writeb(pageno, baseAddr + Control_reg);
+                       ofs = baseAddr + DynPage_addr + pageofs;
+                       for (i = 0; i < cnt; i++)
+                               writeb(*buffer++, ofs + i);
+                       if (c == 0) {
+                               writew((tail + len) & tx_mask, ofsAddr + TXwptr);
+                               break;
+                       }
+                       if (++pageno == epage)
+                               pageno = spage;
+                       pageofs = 0;
+               } while (1);
+       }
+       writeb(1, ofsAddr + CD180TXirq);        /* start to send */
+       return (total);
+}
+
+int MoxaPortReadData(int port, unsigned char * buffer, int space)
+{
+       register ushort head, pageofs;
+       int i, count, cnt, len, total, remain;
+       ushort tail, rx_mask, spage, epage;
+       ushort pageno, bufhead;
+       unsigned long baseAddr, ofsAddr, ofs;
+
+       ofsAddr = moxaTableAddr[port];
+       baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD];
+       head = readw(ofsAddr + RXrptr);
+       tail = readw(ofsAddr + RXwptr);
+       rx_mask = readw(ofsAddr + RX_mask);
+       spage = readw(ofsAddr + Page_rxb);
+       epage = readw(ofsAddr + EndPage_rxb);
+       count = (tail >= head) ? (tail - head)
+           : (tail - head + rx_mask + 1);
+       if (count == 0)
+               return (0);
+
+       total = (space > count) ? count : space;
+       remain = count - total;
+       moxaLog.rxcnt[port] += total;
+       count = total;
+       if (spage == epage) {
+               bufhead = readw(ofsAddr + Ofs_rxb);
+               writew(spage, baseAddr + Control_reg);
+               while (count > 0) {
+                       if (tail >= head)
+                               len = tail - head;
+                       else
+                               len = rx_mask + 1 - head;
+                       len = (count > len) ? len : count;
+                       ofs = baseAddr + DynPage_addr + bufhead + head;
+                       for (i = 0; i < len; i++)
+                               *buffer++ = readb(ofs + i);
+                       head = (head + len) & rx_mask;
+                       count -= len;
+               }
+               writew(head, ofsAddr + RXrptr);
+       } else {
+               len = count;
+               pageno = spage + (head >> 13);
+               pageofs = head & Page_mask;
+               do {
+                       cnt = Page_size - pageofs;
+                       if (cnt > count)
+                               cnt = count;
+                       count -= cnt;
+                       writew(pageno, baseAddr + Control_reg);
+                       ofs = baseAddr + DynPage_addr + pageofs;
+                       for (i = 0; i < cnt; i++)
+                               *buffer++ = readb(ofs + i);
+                       if (count == 0) {
+                               writew((head + len) & rx_mask, ofsAddr + RXrptr);
+                               break;
+                       }
+                       if (++pageno == epage)
+                               pageno = spage;
+                       pageofs = 0;
+               } while (1);
+       }
+       if ((readb(ofsAddr + FlagStat) & Xoff_state) && (remain < LowWater)) {
+               moxaLowWaterChk = 1;
+               moxaLowChkFlag[port] = 1;
+       }
+       return (total);
+}
+
+
+int MoxaPortTxQueue(int port)
+{
+       unsigned long ofsAddr;
+       ushort rptr, wptr, mask;
+       int len;
+
+       ofsAddr = moxaTableAddr[port];
+       rptr = readw(ofsAddr + TXrptr);
+       wptr = readw(ofsAddr + TXwptr);
+       mask = readw(ofsAddr + TX_mask);
+       len = (wptr - rptr) & mask;
+       return (len);
+}
+
+int MoxaPortTxFree(int port)
+{
+       unsigned long ofsAddr;
+       ushort rptr, wptr, mask;
+       int len;
+
+       ofsAddr = moxaTableAddr[port];
+       rptr = readw(ofsAddr + TXrptr);
+       wptr = readw(ofsAddr + TXwptr);
+       mask = readw(ofsAddr + TX_mask);
+       len = mask - ((wptr - rptr) & mask);
+       return (len);
+}
+
+int MoxaPortRxQueue(int port)
+{
+       unsigned long ofsAddr;
+       ushort rptr, wptr, mask;
+       int len;
+
+       ofsAddr = moxaTableAddr[port];
+       rptr = readw(ofsAddr + RXrptr);
+       wptr = readw(ofsAddr + RXwptr);
+       mask = readw(ofsAddr + RX_mask);
+       len = (wptr - rptr) & mask;
+       return (len);
+}
+
+
+void MoxaPortTxDisable(int port)
+{
+       unsigned long ofsAddr;
+
+       ofsAddr = moxaTableAddr[port];
+       moxafunc(ofsAddr, FC_SetXoffState, Magic_code);
+}
+
+void MoxaPortTxEnable(int port)
+{
+       unsigned long ofsAddr;
+
+       ofsAddr = moxaTableAddr[port];
+       moxafunc(ofsAddr, FC_SetXonState, Magic_code);
+}
+
+
+int MoxaPortResetBrkCnt(int port)
+{
+       ushort cnt;
+       cnt = moxaBreakCnt[port];
+       moxaBreakCnt[port] = 0;
+       return (cnt);
+}
+
+
+void MoxaPortSendBreak(int port, int ms100)
+{
+       unsigned long ofsAddr;
+
+       ofsAddr = moxaTableAddr[port];
+       if (ms100) {
+               moxafunc(ofsAddr, FC_SendBreak, Magic_code);
+               moxadelay(ms100 * (HZ / 10));
+       } else {
+               moxafunc(ofsAddr, FC_SendBreak, Magic_code);
+               moxadelay(HZ / 4);      /* 250 ms */
+       }
+       moxafunc(ofsAddr, FC_StopBreak, Magic_code);
+}
+
+static int moxa_get_serial_info(struct moxa_str *info,
+                               struct serial_struct *retinfo)
+{
+       struct serial_struct tmp;
+
+       if (!retinfo)
+               return (-EFAULT);
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = info->type;
+       tmp.line = info->port;
+       tmp.port = 0;
+       tmp.irq = 0;
+       tmp.flags = info->asyncflags;
+       tmp.baud_base = 921600;
+       tmp.close_delay = info->close_delay;
+       tmp.closing_wait = info->closing_wait;
+       tmp.custom_divisor = 0;
+       tmp.hub6 = 0;
+       if(copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return (0);
+}
+
+
+static int moxa_set_serial_info(struct moxa_str *info,
+                               struct serial_struct *new_info)
+{
+       struct serial_struct new_serial;
+
+       if(copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       if ((new_serial.irq != 0) ||
+           (new_serial.port != 0) ||
+//           (new_serial.type != info->type) ||
+           (new_serial.custom_divisor != 0) ||
+           (new_serial.baud_base != 921600))
+               return (-EPERM);
+
+       if (!suser()) {
+               if (((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (info->asyncflags & ~ASYNC_USR_MASK)))
+                       return (-EPERM);
+       } else {
+               info->close_delay = new_serial.close_delay * HZ / 100;
+               info->closing_wait = new_serial.closing_wait * HZ / 100;
+       }
+
+       new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS);
+       new_serial.flags |= (info->asyncflags & ASYNC_FLAGS);
+
+       if (new_serial.type == PORT_16550A) {
+               MoxaSetFifo(info->port, 1);
+       } else {
+               MoxaSetFifo(info->port, 0);
+       }
+
+       info->type = new_serial.type;
+       return (0);
+}
+
+
+
+/*****************************************************************************
+ *     Static local functions:                                              *
+ *****************************************************************************/
+/*
+ * moxadelay - delays a specified number ticks
+ */
+static void moxadelay(int tick)
+{
+       unsigned long st, et;
+
+       st = jiffies;
+       et = st + tick;
+       while (jiffies < et);
+}
+
+static void moxafunc(unsigned long ofsAddr, int cmd, ushort arg)
+{
+
+       writew(arg, ofsAddr + FuncArg);
+       writew(cmd, ofsAddr + FuncCode);
+       wait_finish(ofsAddr);
+}
+
+static void wait_finish(unsigned long ofsAddr)
+{
+       unsigned long i, j;
+
+       i = jiffies;
+       while (readw(ofsAddr + FuncCode) != 0) {
+               j = jiffies;
+               if ((j - i) > moxaFuncTout) {
+                       return;
+               }
+       }
+}
+
+static void low_water_check(unsigned long ofsAddr)
+{
+       int len;
+       ushort rptr, wptr, mask;
+
+       if (readb(ofsAddr + FlagStat) & Xoff_state) {
+               rptr = readw(ofsAddr + RXrptr);
+               wptr = readw(ofsAddr + RXwptr);
+               mask = readw(ofsAddr + RX_mask);
+               len = (wptr - rptr) & mask;
+               if (len <= Low_water)
+                       moxafunc(ofsAddr, FC_SendXon, 0);
+       }
+}
+
+static int moxaloadbios(int cardno, unsigned char *tmp, int len)
+{
+       unsigned long baseAddr;
+       int i;
+
+       if(copy_from_user(moxaBuff, tmp, len))
+               return -EFAULT;
+       baseAddr = moxaBaseAddr[cardno];
+       writeb(HW_reset, baseAddr + Control_reg);       /* reset */
+       moxadelay(1);           /* delay 10 ms */
+       for (i = 0; i < 4096; i++)
+               writeb(0, baseAddr + i);        /* clear fix page */
+       for (i = 0; i < len; i++)
+               writeb(moxaBuff[i], baseAddr + i);      /* download BIOS */
+       writeb(0, baseAddr + Control_reg);      /* restart */
+       return (0);
+}
+
+static int moxafindcard(int cardno)
+{
+       unsigned long baseAddr;
+       ushort tmp;
+
+       baseAddr = moxaBaseAddr[cardno];
+       switch (moxa_boards[cardno].boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               if ((tmp = readw(baseAddr + C218_key)) != C218_KeyCode) {
+                       return (-1);
+               }
+               break;
+       case MOXA_BOARD_CP204J:
+               if ((tmp = readw(baseAddr + C218_key)) != CP204J_KeyCode) {
+                       return (-1);
+               }
+               break;
+       default:
+               if ((tmp = readw(baseAddr + C320_key)) != C320_KeyCode) {
+                       return (-1);
+               }
+               if ((tmp = readw(baseAddr + C320_status)) != STS_init) {
+                       return (-2);
+               }
+       }
+       return (0);
+}
+
+static int moxaload320b(int cardno, unsigned char * tmp, int len)
+{
+       unsigned long baseAddr;
+       int i;
+
+       if(copy_from_user(moxaBuff, tmp, len))
+               return -EFAULT;
+       baseAddr = moxaBaseAddr[cardno];
+       writew(len - 7168 - 2, baseAddr + C320bapi_len);
+       writeb(1, baseAddr + Control_reg);      /* Select Page 1 */
+       for (i = 0; i < 7168; i++)
+               writeb(moxaBuff[i], baseAddr + DynPage_addr + i);
+       writeb(2, baseAddr + Control_reg);      /* Select Page 2 */
+       for (i = 0; i < (len - 7168); i++)
+               writeb(moxaBuff[i + 7168], baseAddr + DynPage_addr + i);
+       return (0);
+}
+
+static int moxaloadcode(int cardno, unsigned char * tmp, int len)
+{
+       unsigned long baseAddr, ofsAddr;
+       int retval, port, i;
+
+       if(copy_from_user(moxaBuff, tmp, len))
+               return -EFAULT;
+       baseAddr = moxaBaseAddr[cardno];
+       switch (moxa_boards[cardno].boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+       case MOXA_BOARD_CP204J:
+               retval = moxaloadc218(cardno, baseAddr, len);
+               if (retval)
+                       return (retval);
+               port = cardno * MAX_PORTS_PER_BOARD;
+               for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) {
+                       moxaChkPort[port] = 1;
+                       moxaCurBaud[port] = 9600L;
+                       moxaDCDState[port] = 0;
+                       moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i;
+                       ofsAddr = moxaTableAddr[port];
+                       writew(C218rx_mask, ofsAddr + RX_mask);
+                       writew(C218tx_mask, ofsAddr + TX_mask);
+                       writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb);
+                       writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb);
+
+                       writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb);
+                       writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb);
+
+               }
+               break;
+       default:
+               retval = moxaloadc320(cardno, baseAddr, len,
+                                     &moxa_boards[cardno].numPorts);
+               if (retval)
+                       return (retval);
+               port = cardno * MAX_PORTS_PER_BOARD;
+               for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) {
+                       moxaChkPort[port] = 1;
+                       moxaCurBaud[port] = 9600L;
+                       moxaDCDState[port] = 0;
+                       moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i;
+                       ofsAddr = moxaTableAddr[port];
+                       if (moxa_boards[cardno].numPorts == 8) {
+                               writew(C320p8rx_mask, ofsAddr + RX_mask);
+                               writew(C320p8tx_mask, ofsAddr + TX_mask);
+                               writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb);
+
+                       } else if (moxa_boards[cardno].numPorts == 16) {
+                               writew(C320p16rx_mask, ofsAddr + RX_mask);
+                               writew(C320p16tx_mask, ofsAddr + TX_mask);
+                               writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb);
+
+                       } else if (moxa_boards[cardno].numPorts == 24) {
+                               writew(C320p24rx_mask, ofsAddr + RX_mask);
+                               writew(C320p24tx_mask, ofsAddr + TX_mask);
+                               writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
+                       } else if (moxa_boards[cardno].numPorts == 32) {
+                               writew(C320p32rx_mask, ofsAddr + RX_mask);
+                               writew(C320p32tx_mask, ofsAddr + TX_mask);
+                               writew(C320p32tx_ofs, ofsAddr + Ofs_txb);
+                               writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb);
+                               writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb);
+                               writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
+                       }
+               }
+               break;
+       }
+       return (0);
+}
+
+static int moxaloadc218(int cardno, unsigned long baseAddr, int len)
+{
+       char retry;
+       int i, j, len1, len2;
+       ushort usum, *ptr, keycode;
+
+       if (moxa_boards[cardno].boardType == MOXA_BOARD_CP204J)
+               keycode = CP204J_KeyCode;
+       else
+               keycode = C218_KeyCode;
+       usum = 0;
+       len1 = len >> 1;
+       ptr = (ushort *) moxaBuff;
+       for (i = 0; i < len1; i++)
+               usum += *(ptr + i);
+       retry = 0;
+       do {
+               len1 = len >> 1;
+               j = 0;
+               while (len1) {
+                       len2 = (len1 > 2048) ? 2048 : len1;
+                       len1 -= len2;
+                       for (i = 0; i < len2 << 1; i++)
+                               writeb(moxaBuff[i + j], baseAddr + C218_LoadBuf + i);
+                       j += i;
+
+                       writew(len2, baseAddr + C218DLoad_len);
+                       writew(0, baseAddr + C218_key);
+                       for (i = 0; i < 100; i++) {
+                               if (readw(baseAddr + C218_key) == keycode)
+                                       break;
+                               moxadelay(1);   /* delay 10 ms */
+                       }
+                       if (readw(baseAddr + C218_key) != keycode) {
+                               return (-1);
+                       }
+               }
+               writew(0, baseAddr + C218DLoad_len);
+               writew(usum, baseAddr + C218check_sum);
+               writew(0, baseAddr + C218_key);
+               for (i = 0; i < 100; i++) {
+                       if (readw(baseAddr + C218_key) == keycode)
+                               break;
+                       moxadelay(1);   /* delay 10 ms */
+               }
+               retry++;
+       } while ((readb(baseAddr + C218chksum_ok) != 1) && (retry < 3));
+       if (readb(baseAddr + C218chksum_ok) != 1) {
+               return (-1);
+       }
+       writew(0, baseAddr + C218_key);
+       for (i = 0; i < 100; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               moxadelay(1);   /* delay 10 ms */
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code) {
+               return (-1);
+       }
+       writew(1, baseAddr + Disable_IRQ);
+       writew(0, baseAddr + Magic_no);
+       for (i = 0; i < 100; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               moxadelay(1);   /* delay 10 ms */
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code) {
+               return (-1);
+       }
+       moxaCard = 1;
+       moxaIntNdx[cardno] = baseAddr + IRQindex;
+       moxaIntPend[cardno] = baseAddr + IRQpending;
+       moxaIntTable[cardno] = baseAddr + IRQtable;
+       return (0);
+}
+
+static int moxaloadc320(int cardno, unsigned long baseAddr, int len, int *numPorts)
+{
+       ushort usum;
+       int i, j, wlen, len2, retry;
+       ushort *uptr;
+
+       usum = 0;
+       wlen = len >> 1;
+       uptr = (ushort *) moxaBuff;
+       for (i = 0; i < wlen; i++)
+               usum += uptr[i];
+       retry = 0;
+       j = 0;
+       do {
+               while (wlen) {
+                       if (wlen > 2048)
+                               len2 = 2048;
+                       else
+                               len2 = wlen;
+                       wlen -= len2;
+                       len2 <<= 1;
+                       for (i = 0; i < len2; i++)
+                               writeb(moxaBuff[j + i], baseAddr + C320_LoadBuf + i);
+                       len2 >>= 1;
+                       j += i;
+                       writew(len2, baseAddr + C320DLoad_len);
+                       writew(0, baseAddr + C320_key);
+                       for (i = 0; i < 10; i++) {
+                               if (readw(baseAddr + C320_key) == C320_KeyCode)
+                                       break;
+                               moxadelay(1);
+                       }
+                       if (readw(baseAddr + C320_key) != C320_KeyCode)
+                               return (-1);
+               }
+               writew(0, baseAddr + C320DLoad_len);
+               writew(usum, baseAddr + C320check_sum);
+               writew(0, baseAddr + C320_key);
+               for (i = 0; i < 10; i++) {
+                       if (readw(baseAddr + C320_key) == C320_KeyCode)
+                               break;
+                       moxadelay(1);
+               }
+               retry++;
+       } while ((readb(baseAddr + C320chksum_ok) != 1) && (retry < 3));
+       if (readb(baseAddr + C320chksum_ok) != 1)
+               return (-1);
+       writew(0, baseAddr + C320_key);
+       for (i = 0; i < 600; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               moxadelay(1);
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code)
+               return (-100);
+
+       if (moxa_boards[cardno].busType == MOXA_BUS_TYPE_PCI) {         /* ASIC board */
+               writew(0x3800, baseAddr + TMS320_PORT1);
+               writew(0x3900, baseAddr + TMS320_PORT2);
+               writew(28499, baseAddr + TMS320_CLOCK);
+       } else {
+               writew(0x3200, baseAddr + TMS320_PORT1);
+               writew(0x3400, baseAddr + TMS320_PORT2);
+               writew(19999, baseAddr + TMS320_CLOCK);
+       }
+       writew(1, baseAddr + Disable_IRQ);
+       writew(0, baseAddr + Magic_no);
+       for (i = 0; i < 500; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               moxadelay(1);
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code)
+               return (-102);
+
+       j = readw(baseAddr + Module_cnt);
+       if (j <= 0)
+               return (-101);
+       *numPorts = j * 8;
+       writew(j, baseAddr + Module_no);
+       writew(0, baseAddr + Magic_no);
+       for (i = 0; i < 600; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               moxadelay(1);
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code)
+               return (-102);
+       moxaCard = 1;
+       moxaIntNdx[cardno] = baseAddr + IRQindex;
+       moxaIntPend[cardno] = baseAddr + IRQpending;
+       moxaIntTable[cardno] = baseAddr + IRQtable;
+       return (0);
+}
+
+long MoxaPortGetCurBaud(int port)
+{
+
+       if (moxaChkPort[port] == 0)
+               return (0);
+       return (moxaCurBaud[port]);
+}
+
+static void MoxaSetFifo(int port, int enable)
+{
+       unsigned long ofsAddr = moxaTableAddr[port];
+
+       if (!enable) {
+               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0);
+               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1);
+       } else {
+               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3);
+               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16);
+       }
+}
+
+#if 0
+int MoxaPortSetMode(int port, int databits, int stopbits, int parity)
+{
+       unsigned long ofsAddr;
+       int val;
+
+       val = 0;
+       switch (databits) {
+       case 5:
+               val |= 0;
+               break;
+       case 6:
+               val |= 1;
+               break;
+       case 7:
+               val |= 2;
+               break;
+       case 8:
+               val |= 3;
+               break;
+       default:
+               return (-1);
+       }
+       switch (stopbits) {
+       case 0:
+               val |= 0;
+               break;          /* stop bits 1.5 */
+       case 1:
+               val |= 0;
+               break;
+       case 2:
+               val |= 4;
+               break;
+       default:
+               return (-1);
+       }
+       switch (parity) {
+       case 0:
+               val |= 0x00;
+               break;          /* None  */
+       case 1:
+               val |= 0x08;
+               break;          /* Odd   */
+       case 2:
+               val |= 0x18;
+               break;          /* Even  */
+       case 3:
+               val |= 0x28;
+               break;          /* Mark  */
+       case 4:
+               val |= 0x38;
+               break;          /* Space */
+       default:
+               return (-1);
+       }
+       ofsAddr = moxaTableAddr[port];
+       moxafunc(ofsAddr, FC_SetMode, val);
+       return (0);
+}
+
+int MoxaPortTxBufSize(int port)
+{
+       unsigned long ofsAddr;
+       int size;
+
+       ofsAddr = moxaTableAddr[port];
+       size = readw(ofsAddr + TX_mask);
+       return (size);
+}
+
+int MoxaPortRxBufSize(int port)
+{
+       unsigned long ofsAddr;
+       int size;
+
+       ofsAddr = moxaTableAddr[port];
+       size = readw(ofsAddr + RX_mask);
+       return (size);
+}
+
+int MoxaPortRxFree(int port)
+{
+       unsigned long ofsAddr;
+       ushort rptr, wptr, mask;
+       int len;
+
+       ofsAddr = moxaTableAddr[port];
+       rptr = readw(ofsAddr + RXrptr);
+       wptr = readw(ofsAddr + RXwptr);
+       mask = readw(ofsAddr + RX_mask);
+       len = mask - ((wptr - rptr) & mask);
+       return (len);
+}
+int MoxaPortGetBrkCnt(int port)
+{
+       return (moxaBreakCnt[port]);
+}
+
+void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue)
+{
+       unsigned long ofsAddr;
+
+       ofsAddr = moxaTableAddr[port];
+       writew(xonValue, ofsAddr + FuncArg);
+       writew(xoffValue, ofsAddr + FuncArg1);
+       writew(FC_SetXonXoff, ofsAddr + FuncCode);
+       wait_finish(ofsAddr);
+}
+
+int MoxaPortIsTxHold(int port)
+{
+       unsigned long ofsAddr;
+       int val;
+
+       ofsAddr = moxaTableAddr[port];
+       if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
+           (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
+               moxafunc(ofsAddr, FC_GetCCSR, 0);
+               val = readw(ofsAddr + FuncArg);
+               if (val & 0x04)
+                       return (1);
+       } else {
+               if (readw(ofsAddr + FlagStat) & Tx_flowOff)
+                       return (1);
+       }
+       return (0);
+}
+#endif
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
new file mode 100644 (file)
index 0000000..74a3da5
--- /dev/null
@@ -0,0 +1,2449 @@
+/*****************************************************************************/
+/*
+ *          mxser.c  -- MOXA Smartio family multiport serial driver.
+ *
+ *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com.tw).
+ *
+ *      This code is loosely based on the Linux serial driver, written by
+ *      Linus Torvalds, Theodore T'so and others.
+ *
+ *      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.
+ */
+
+/*
+ *    MOXA Smartio Family Serial Driver
+ *
+ *      Copyright (C) 1999,2000  Moxa Technologies Co., LTD.
+ *
+ *      for             : LINUX 2.0.X, 2.2.X
+ *      date            : 1999/07/22
+ *      version         : 1.1 
+ *      
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#define                MXSER_VERSION                   "1.1kern"
+
+#define                MXSERMAJOR              174
+#define                MXSERCUMAJOR            175
+
+
+#define        MXSER_EVENT_TXLOW        1
+#define        MXSER_EVENT_HANGUP       2
+
+
+#define        SERIAL_DO_RESTART
+
+#define        MXSER_BOARDS            4       /* Max. boards */
+#define        MXSER_PORTS             32      /* Max. ports */
+#define        MXSER_PORTS_PER_BOARD   8       /* Max. ports per board */
+#define        MXSER_ISR_PASS_LIMIT    256
+
+#define                MXSER_ERR_IOADDR        -1
+#define                MXSER_ERR_IRQ           -2
+#define                MXSER_ERR_IRQ_CONFLIT   -3
+#define                MXSER_ERR_VECTOR        -4
+
+#define        SERIAL_TYPE_NORMAL      1
+#define        SERIAL_TYPE_CALLOUT     2
+
+#define        WAKEUP_CHARS            256
+
+#define        UART_MCR_AFE            0x20
+#define        UART_LSR_SPECIAL        0x1E
+
+#define PORTNO(x)              (MINOR((x)->device) - (x)->driver.minor_start)
+
+#define RELEVANT_IFLAG(iflag)  (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
+
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ *    Define the Moxa PCI vendor and device IDs.
+ */
+
+#ifndef        PCI_VENDOR_ID_MOXA
+#define        PCI_VENDOR_ID_MOXA      0x1393
+#endif
+#ifndef PCI_DEVICE_ID_C168
+#define PCI_DEVICE_ID_C168     0x1680
+#endif
+#ifndef PCI_DEVICE_ID_C104
+#define PCI_DEVICE_ID_C104     0x1040
+#endif
+
+#define C168_ASIC_ID    1
+#define C104_ASIC_ID    2
+#define CI104J_ASIC_ID  5
+
+enum {
+       MXSER_BOARD_C168_ISA = 1,
+       MXSER_BOARD_C104_ISA,
+       MXSER_BOARD_CI104J,
+       MXSER_BOARD_C168_PCI,
+       MXSER_BOARD_C104_PCI,
+};
+
+static char *mxser_brdname[] =
+{
+       "C168 series",
+       "C104 series",
+       "CI-104J series",
+       "C168H/PCI series",
+       "C104H/PCI series",
+};
+
+static int mxser_numports[] =
+{
+       8,
+       4,
+       4,
+       8,
+       4,
+};
+
+/*
+ *    MOXA ioctls
+ */
+#define        MOXA            0x400
+#define        MOXA_GETDATACOUNT     (MOXA + 23)
+#define                MOXA_GET_CONF         (MOXA + 35)
+#define        MOXA_DIAGNOSE         (MOXA + 50)
+#define        MOXA_CHKPORTENABLE    (MOXA + 60)
+#define        MOXA_HighSpeedOn      (MOXA + 61)
+#define         MOXA_GET_MAJOR        (MOXA + 63)
+#define         MOXA_GET_CUMAJOR      (MOXA + 64)
+#define         MOXA_GETMSTATUS       (MOXA + 65)
+
+typedef struct {
+       unsigned short vendor_id;
+       unsigned short device_id;
+       unsigned short board_type;
+} mxser_pciinfo;
+
+static mxser_pciinfo mxser_pcibrds[] =
+{
+       {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C168, MXSER_BOARD_C168_PCI},
+       {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, MXSER_BOARD_C104_PCI},
+};
+
+typedef struct _moxa_pci_info {
+       unsigned short busNum;
+       unsigned short devNum;
+} moxa_pci_info;
+
+static int ioaddr[MXSER_BOARDS] = {0, 0, 0, 0};
+static int ttymajor = MXSERMAJOR;
+static int calloutmajor = MXSERCUMAJOR;
+static int verbose = 0;
+
+#ifdef MODULE
+/* Variables for insmod */
+
+MODULE_AUTHOR("William Chen");
+MODULE_DESCRIPTION("MOXA Smartio Family Multiport Board Device Driver");
+MODULE_PARM(ioaddr, "1-4i");
+MODULE_PARM(ttymajor, "i");
+MODULE_PARM(calloutmajor, "i");
+MODULE_PARM(verbose, "i");
+
+#endif /* MODULE */
+
+struct mxser_hwconf {
+       int board_type;
+       int ports;
+       int irq;
+       int vector;
+       int vector_mask;
+       int uart_type;
+       int ioaddr[MXSER_PORTS_PER_BOARD];
+       int baud_base[MXSER_PORTS_PER_BOARD];
+       moxa_pci_info pciInfo;
+};
+
+struct mxser_struct {
+       int port;
+       int base;               /* port base address */
+       int irq;                /* port using irq no. */
+       int vector;             /* port irq vector */
+       int vectormask;         /* port vector mask */
+       int rx_trigger;         /* Rx fifo trigger level */
+       int baud_base;          /* max. speed */
+       int flags;              /* defined in tty.h */
+       int type;               /* UART type */
+       struct tty_struct *tty;
+       int read_status_mask;
+       int ignore_status_mask;
+       int xmit_fifo_size;
+       int custom_divisor;
+       int x_char;             /* xon/xoff character */
+       int close_delay;
+       unsigned short closing_wait;
+       int IER;                /* Interrupt Enable Register */
+       int MCR;                /* Modem control register */
+       unsigned long event;
+       int count;              /* # of fd on device */
+       int blocked_open;       /* # of blocked opens */
+       long session;           /* Session of opening process */
+       long pgrp;              /* pgrp of opening process */
+       unsigned char *xmit_buf;
+       int xmit_head;
+       int xmit_tail;
+       int xmit_cnt;
+       struct tq_struct tqueue;
+       struct termios normal_termios;
+       struct termios callout_termios;
+       struct wait_queue *open_wait;
+       struct wait_queue *close_wait;
+       struct wait_queue *delta_msr_wait;
+       struct async_icount icount;     /* kernel counters for the 4 input interrupts */
+};
+
+struct mxser_log {
+       int tick;
+       int rxcnt[MXSER_PORTS];
+       int txcnt[MXSER_PORTS];
+};
+
+struct mxser_mstatus {
+       tcflag_t cflag;
+       int cts;
+       int dsr;
+       int ri;
+       int dcd;
+};
+
+static struct mxser_mstatus GMStatus[MXSER_PORTS];
+
+static int mxserBoardCAP[MXSER_BOARDS] =
+{
+       0, 0, 0, 0
+       /*  0x180, 0x280, 0x200, 0x320   */
+};
+
+
+static struct tty_driver mxvar_sdriver, mxvar_cdriver;
+static int mxvar_refcount;
+static struct mxser_struct mxvar_table[MXSER_PORTS];
+static struct tty_struct *mxvar_tty[MXSER_PORTS + 1];
+static struct termios *mxvar_termios[MXSER_PORTS + 1];
+static struct termios *mxvar_termios_locked[MXSER_PORTS + 1];
+static struct mxser_log mxvar_log;
+static int mxvar_diagflag;
+/*
+ * mxvar_tmp_buf is used as a temporary buffer by serial_write. We need
+ * to lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *mxvar_tmp_buf = 0;
+static struct semaphore mxvar_tmp_buf_sem = MUTEX;
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int mxvar_baud_table[] =
+{
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0};
+
+struct mxser_hwconf mxsercfg[MXSER_BOARDS];
+
+/*
+ * static functions:
+ */
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+
+static void mxser_getcfg(int board, struct mxser_hwconf *hwconf);
+int mxser_init(void);
+static int mxser_get_ISA_conf(int, struct mxser_hwconf *);
+static int mxser_get_PCI_conf(int, int, int, struct mxser_hwconf *);
+static void mxser_do_softint(void *);
+static int mxser_open(struct tty_struct *, struct file *);
+static void mxser_close(struct tty_struct *, struct file *);
+static int mxser_write(struct tty_struct *, int, const unsigned char *, int);
+static int mxser_write_room(struct tty_struct *);
+static void mxser_flush_buffer(struct tty_struct *);
+static int mxser_chars_in_buffer(struct tty_struct *);
+static void mxser_flush_chars(struct tty_struct *);
+static void mxser_put_char(struct tty_struct *, unsigned char);
+static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong);
+static int mxser_ioctl_special(unsigned int, unsigned long);
+static void mxser_throttle(struct tty_struct *);
+static void mxser_unthrottle(struct tty_struct *);
+static void mxser_set_termios(struct tty_struct *, struct termios *);
+static void mxser_stop(struct tty_struct *);
+static void mxser_start(struct tty_struct *);
+static void mxser_hangup(struct tty_struct *);
+static void mxser_interrupt(int, void *, struct pt_regs *);
+static inline void mxser_receive_chars(struct mxser_struct *, int *);
+static inline void mxser_transmit_chars(struct mxser_struct *);
+static inline void mxser_check_modem_status(struct mxser_struct *, int);
+static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *);
+static int mxser_startup(struct mxser_struct *);
+static void mxser_shutdown(struct mxser_struct *);
+static int mxser_change_speed(struct mxser_struct *, struct termios *old_termios);
+static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct *);
+static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct *);
+static int mxser_get_lsr_info(struct mxser_struct *, unsigned int *);
+static void mxser_send_break(struct mxser_struct *, int);
+static int mxser_get_modem_info(struct mxser_struct *, unsigned int *);
+static int mxser_set_modem_info(struct mxser_struct *, unsigned int, unsigned int *);
+
+/*
+ * The MOXA C168/C104 serial driver boot-time initialization code!
+ */
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       int ret;
+
+       if (verbose)
+               printk("Loading module mxser ...\n");
+       ret = mxser_init();
+       if (verbose)
+               printk("Done.\n");
+       return (ret);
+}
+
+void cleanup_module(void)
+{
+       int i, err = 0;
+
+
+       if (verbose)
+               printk("Unloading module mxser ...\n");
+       if ((err |= tty_unregister_driver(&mxvar_cdriver)))
+               printk("Couldn't unregister MOXA Smartio family callout driver\n");
+       if ((err |= tty_unregister_driver(&mxvar_sdriver)))
+               printk("Couldn't unregister MOXA Smartio family serial driver\n");
+
+       for (i = 0; i < MXSER_BOARDS; i++) {
+               if (mxsercfg[i].board_type == -1)
+                       continue;
+               else {
+                       free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]);
+               }
+       }
+
+       if (verbose)
+               printk("Done.\n");
+
+}
+#endif
+
+
+int mxser_initbrd(int board, struct mxser_hwconf *hwconf)
+{
+       struct mxser_struct *info;
+       unsigned long flags;
+       int retval;
+       int i, n;
+
+       n = board * MXSER_PORTS_PER_BOARD;
+       info = &mxvar_table[n];
+       for (i = 0; i < hwconf->ports; i++, n++, info++) {
+               if (verbose) {
+                       printk("        ttyM%d/cum%d at 0x%04x ", n, n, hwconf->ioaddr[i]);
+                       if (hwconf->baud_base[i] == 115200)
+                               printk(" max. baud rate up to 115200 bps.\n");
+                       else
+                               printk(" max. baud rate up to 921600 bps.\n");
+               }
+               info->port = n;
+               info->base = hwconf->ioaddr[i];
+               info->irq = hwconf->irq;
+               info->vector = hwconf->vector;
+               info->vectormask = hwconf->vector_mask;
+               info->rx_trigger = 14;
+               info->baud_base = hwconf->baud_base[i];
+               info->flags = ASYNC_SHARE_IRQ;
+               info->type = hwconf->uart_type;
+               if ((info->type == PORT_16450) || (info->type == PORT_8250))
+                       info->xmit_fifo_size = 1;
+               else
+                       info->xmit_fifo_size = 16;
+               info->custom_divisor = hwconf->baud_base[i] * 16;
+               info->close_delay = 5 * HZ / 10;
+               info->closing_wait = 30 * HZ;
+               info->tqueue.routine = mxser_do_softint;
+               info->tqueue.data = info;
+               info->callout_termios = mxvar_cdriver.init_termios;
+               info->normal_termios = mxvar_sdriver.init_termios;
+       }
+
+       /*
+        * Allocate the IRQ if necessary
+        */
+       save_flags(flags);
+
+       n = board * MXSER_PORTS_PER_BOARD;
+       info = &mxvar_table[n];
+
+       cli();
+       retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info),
+                            "mxser", info);
+       if (retval) {
+               restore_flags(flags);
+               printk("Board %d: %s", board, mxser_brdname[hwconf->board_type - 1]);
+               printk("  Request irq fail,IRQ (%d) may be conflit with another device.\n", info->irq);
+               return (retval);
+       }
+       restore_flags(flags);
+
+       return 0;
+}
+
+
+static void mxser_getcfg(int board, struct mxser_hwconf *hwconf)
+{
+       mxsercfg[board] = *hwconf;
+}
+
+static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf)
+{
+       int i;
+       unsigned int val, ioaddress;
+
+       hwconf->board_type = board_type;
+       hwconf->ports = mxser_numports[board_type - 1];
+       pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_2, &val);
+       if (val == 0xffffffff)
+               return (MXSER_ERR_IOADDR);
+       else
+               ioaddress = val & 0xffffffc;
+       for (i = 0; i < hwconf->ports; i++)
+               hwconf->ioaddr[i] = ioaddress + 8 * i;
+
+       pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_3, &val);
+       if (val == 0xffffffff)
+               return (MXSER_ERR_VECTOR);
+       else
+               ioaddress = val & 0xffffffc;
+       hwconf->vector = ioaddress;
+
+       pcibios_read_config_dword(busnum, devnum, PCI_INTERRUPT_LINE, &val);
+       if (val == 0xffffffff)
+               return (MXSER_ERR_IRQ);
+       else
+               hwconf->irq = val & 0xff;
+
+       hwconf->uart_type = PORT_16550A;
+       hwconf->vector_mask = 0;
+       for (i = 0; i < hwconf->ports; i++) {
+               hwconf->vector_mask |= (1 << i);
+               hwconf->baud_base[i] = 921600;
+       }
+       return (0);
+}
+
+int mxser_init(void)
+{
+       int i, m, retval, b;
+       int n, index;
+       int ret1, ret2;
+       unsigned char busnum, devnum;
+       struct mxser_hwconf hwconf;
+
+       printk("MOXA Smartio family driver version %s\n", MXSER_VERSION);
+
+       /* Initialize the tty_driver structure */
+
+       memset(&mxvar_sdriver, 0, sizeof(struct tty_driver));
+       mxvar_sdriver.magic = TTY_DRIVER_MAGIC;
+       mxvar_sdriver.name = "ttyM";
+       mxvar_sdriver.major = ttymajor;
+       mxvar_sdriver.minor_start = 0;
+       mxvar_sdriver.num = MXSER_PORTS + 1;
+       mxvar_sdriver.type = TTY_DRIVER_TYPE_SERIAL;
+       mxvar_sdriver.subtype = SERIAL_TYPE_NORMAL;
+       mxvar_sdriver.init_termios = tty_std_termios;
+       mxvar_sdriver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       mxvar_sdriver.flags = TTY_DRIVER_REAL_RAW;
+       mxvar_sdriver.refcount = &mxvar_refcount;
+       mxvar_sdriver.table = mxvar_tty;
+       mxvar_sdriver.termios = mxvar_termios;
+       mxvar_sdriver.termios_locked = mxvar_termios_locked;
+
+       mxvar_sdriver.open = mxser_open;
+       mxvar_sdriver.close = mxser_close;
+       mxvar_sdriver.write = mxser_write;
+       mxvar_sdriver.put_char = mxser_put_char;
+       mxvar_sdriver.flush_chars = mxser_flush_chars;
+       mxvar_sdriver.write_room = mxser_write_room;
+       mxvar_sdriver.chars_in_buffer = mxser_chars_in_buffer;
+       mxvar_sdriver.flush_buffer = mxser_flush_buffer;
+       mxvar_sdriver.ioctl = mxser_ioctl;
+       mxvar_sdriver.throttle = mxser_throttle;
+       mxvar_sdriver.unthrottle = mxser_unthrottle;
+       mxvar_sdriver.set_termios = mxser_set_termios;
+       mxvar_sdriver.stop = mxser_stop;
+       mxvar_sdriver.start = mxser_start;
+       mxvar_sdriver.hangup = mxser_hangup;
+
+       /*
+        * The callout device is just like normal device except for
+        * major number and the subtype code.
+        */
+       mxvar_cdriver = mxvar_sdriver;
+       mxvar_cdriver.name = "cum";
+       mxvar_cdriver.major = calloutmajor;
+       mxvar_cdriver.subtype = SERIAL_TYPE_CALLOUT;
+
+       printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor);
+
+       mxvar_diagflag = 0;
+       memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct));
+       memset(&mxvar_log, 0, sizeof(struct mxser_log));
+
+
+       m = 0;
+       /* Start finding ISA boards here */
+       for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) {
+               int cap;
+               if (!(cap = mxserBoardCAP[b]))
+                       continue;
+
+               retval = mxser_get_ISA_conf(cap, &hwconf);
+
+               if (retval != 0)
+                       printk("Found MOXA %s board (CAP=0x%x)\n",
+                              mxser_brdname[hwconf.board_type - 1],
+                              ioaddr[b]);
+
+               if (retval <= 0) {
+                       if (retval == MXSER_ERR_IRQ)
+                               printk("Invalid interrupt number,board not configured\n");
+                       else if (retval == MXSER_ERR_IRQ_CONFLIT)
+                               printk("Invalid interrupt number,board not configured\n");
+                       else if (retval == MXSER_ERR_VECTOR)
+                               printk("Invalid interrupt vector,board not configured\n");
+                       else if (retval == MXSER_ERR_IOADDR)
+                               printk("Invalid I/O address,board not configured\n");
+
+                       continue;
+               }
+               hwconf.pciInfo.busNum = 0;
+               hwconf.pciInfo.devNum = 0;
+
+               if (mxser_initbrd(m, &hwconf) < 0)
+                       continue;
+
+               mxser_getcfg(m, &hwconf);
+
+               m++;
+       }
+
+       /* Start finding ISA boards from module arg */
+       for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) {
+               int cap;
+               if (!(cap = ioaddr[b]))
+                       continue;
+
+               retval = mxser_get_ISA_conf(cap, &hwconf);
+
+               if (retval != 0)
+                       printk("Found MOXA %s board (CAP=0x%x)\n",
+                              mxser_brdname[hwconf.board_type - 1],
+                              ioaddr[b]);
+
+               if (retval <= 0) {
+                       if (retval == MXSER_ERR_IRQ)
+                               printk("Invalid interrupt number,board not configured\n");
+                       else if (retval == MXSER_ERR_IRQ_CONFLIT)
+                               printk("Invalid interrupt number,board not configured\n");
+                       else if (retval == MXSER_ERR_VECTOR)
+                               printk("Invalid interrupt vector,board not configured\n");
+                       else if (retval == MXSER_ERR_IOADDR)
+                               printk("Invalid I/O address,board not configured\n");
+
+                       continue;
+               }
+               hwconf.pciInfo.busNum = 0;
+               hwconf.pciInfo.devNum = 0;
+
+               if (mxser_initbrd(m, &hwconf) < 0)
+                       continue;
+
+               mxser_getcfg(m, &hwconf);
+
+               m++;
+       }
+
+       /* start finding PCI board here */
+
+#ifdef CONFIG_PCI
+       if (pci_present()) 
+       {
+               n = sizeof(mxser_pcibrds) / sizeof(mxser_pciinfo);
+               index = 0;
+               b = 0;
+               while (b < n) {
+                       if (pcibios_find_device(mxser_pcibrds[b].vendor_id,
+                                             mxser_pcibrds[b].device_id,
+                                               index,
+                                               &busnum,
+                                               &devnum) != 0) {
+                               b++;
+                               index = 0;
+                               continue;
+                       }
+                       hwconf.pciInfo.busNum = busnum;
+                       hwconf.pciInfo.devNum = devnum;
+                       printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n", mxser_brdname[mxser_pcibrds[b].board_type - 1], busnum, devnum >> 3);
+                       index++;
+                       if (m >= MXSER_BOARDS) {
+                               printk("Too many Smartio family boards find (maximum %d),board not configured\n", MXSER_BOARDS);
+                       } else {
+                               retval = mxser_get_PCI_conf(busnum, devnum,
+                                  mxser_pcibrds[b].board_type, &hwconf);
+                               if (retval < 0) {
+                                       if (retval == MXSER_ERR_IRQ)
+                                               printk("Invalid interrupt number,board not configured\n");
+                                       else if (retval == MXSER_ERR_IRQ_CONFLIT)
+                                               printk("Invalid interrupt number,board not configured\n");
+                                       else if (retval == MXSER_ERR_VECTOR)
+                                               printk("Invalid interrupt vector,board not configured\n");
+                                       else if (retval == MXSER_ERR_IOADDR)
+                                               printk("Invalid I/O address,board not configured\n");
+                                       continue;
+
+                               }
+                               if (mxser_initbrd(m, &hwconf) < 0)
+                                       continue;
+                               mxser_getcfg(m, &hwconf);
+                               m++;
+
+                       }
+
+               }
+       }
+#endif
+
+       for (i = m; i < MXSER_BOARDS; i++) {
+               mxsercfg[i].board_type = -1;
+       }
+
+
+       ret1 = 0;
+       ret2 = 0;
+       if (!(ret1 = tty_register_driver(&mxvar_sdriver))) {
+               if (!(ret2 = tty_register_driver(&mxvar_cdriver))) {
+                       return 0;
+               } else {
+                       tty_unregister_driver(&mxvar_sdriver);
+                       printk("Couldn't install MOXA Smartio family callout driver !\n");
+               }
+       } else
+               printk("Couldn't install MOXA Smartio family driver !\n");
+
+
+       if (ret1 || ret2) {
+               for (i = 0; i < MXSER_BOARDS; i++) {
+                       if (mxsercfg[i].board_type == -1)
+                               continue;
+                       else {
+                               free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]);
+                       }
+               }
+               return -1;
+       }
+       return (0);
+}
+
+static void mxser_do_softint(void *private_)
+{
+       struct mxser_struct *info = (struct mxser_struct *) private_;
+       struct tty_struct *tty;
+
+       tty = info->tty;
+       if (!tty)
+               return;
+       if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup) (tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+       if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) {
+               tty_hangup(tty);
+       }
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+
+static int mxser_open(struct tty_struct *tty, struct file *filp)
+{
+       struct mxser_struct *info;
+       int retval, line;
+       unsigned long page;
+
+       line = PORTNO(tty);
+       if (line == MXSER_PORTS)
+               return (0);
+       if ((line < 0) || (line > MXSER_PORTS))
+               return (-ENODEV);
+       info = mxvar_table + line;
+       if (!info->base)
+               return (-ENODEV);
+
+       info->count++;
+       tty->driver_data = info;
+       info->tty = tty;
+
+       if (!mxvar_tmp_buf) {
+               page = get_free_page(GFP_KERNEL);
+               if (!page)
+                       return (-ENOMEM);
+               if (mxvar_tmp_buf)
+                       free_page(page);
+               else
+                       mxvar_tmp_buf = (unsigned char *) page;
+       }
+       /*
+        * Start up serial port
+        */
+       retval = mxser_startup(info);
+       if (retval)
+               return (retval);
+
+       retval = mxser_block_til_ready(tty, filp, info);
+       if (retval)
+               return (retval);
+
+       if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->normal_termios;
+               else
+                       *tty->termios = info->callout_termios;
+               mxser_change_speed(info, 0);
+       }
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+
+       MOD_INC_USE_COUNT;
+
+       return (0);
+}
+
+/*
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ */
+
+static void mxser_close(struct tty_struct *tty, struct file *filp)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+       unsigned long timeout;
+
+       if (PORTNO(tty) == MXSER_PORTS)
+               return;
+       if (!info)
+               return;
+
+       save_flags(flags);
+       cli();
+
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               MOD_DEC_USE_COUNT;
+               return;
+       }
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.        tty->count is 1, which means that the tty
+                * structure will be freed.  Info->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.
+                */
+               printk("mxser_close: bad serial port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+       if (--info->count < 0) {
+               printk("mxser_close: bad serial port count for ttys%d: %d\n",
+                      info->port, info->count);
+               info->count = 0;
+       }
+       if (info->count) {
+               restore_flags(flags);
+               MOD_DEC_USE_COUNT;
+               return;
+       }
+       info->flags |= ASYNC_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ASYNC_NORMAL_ACTIVE)
+               info->normal_termios = *tty->termios;
+       if (info->flags & ASYNC_CALLOUT_ACTIVE)
+               info->callout_termios = *tty->termios;
+       /*
+        * 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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receive line status interrupts, and tell the
+        * interrupt driver to stop checking the data ready bit in the
+        * line status register.
+        */
+       info->IER &= ~UART_IER_RLSI;
+       /* by William
+          info->read_status_mask &= ~UART_LSR_DR;
+        */
+       if (info->flags & ASYNC_INITIALIZED) {
+               outb(info->IER, info->base + UART_IER);
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important if there is a transmit FIFO!
+                */
+               timeout = jiffies + HZ;
+               while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(5);
+                       if (jiffies > timeout)
+                               break;
+               }
+       }
+       mxser_shutdown(info);
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(info->close_delay);
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE |
+                        ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       restore_flags(flags);
+
+       MOD_DEC_USE_COUNT;
+}
+
+static int mxser_write(struct tty_struct *tty, int from_user,
+                      const unsigned char *buf, int count)
+{
+       int c, total = 0;
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       if (!tty || !info->xmit_buf || !mxvar_tmp_buf)
+               return (0);
+
+       if (from_user)
+               down(&mxvar_tmp_buf_sem);
+       save_flags(flags);
+       while (1) {
+               cli();
+               c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                  SERIAL_XMIT_SIZE - info->xmit_head));
+               if (c <= 0)
+                       break;
+
+               if (from_user) {
+                       copy_from_user(mxvar_tmp_buf, buf, c);
+                       c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                   SERIAL_XMIT_SIZE - info->xmit_head));
+                       memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c);
+               } else
+                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
+               info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);
+               info->xmit_cnt += c;
+               restore_flags(flags);
+               buf += c;
+               count -= c;
+               total += c;
+       }
+       if (from_user)
+               up(&mxvar_tmp_buf_sem);
+       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+           !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);
+       }
+       restore_flags(flags);
+       return (total);
+}
+
+static void mxser_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       if (!tty || !info->xmit_buf)
+               return;
+
+       save_flags(flags);
+       cli();
+       if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+               restore_flags(flags);
+               return;
+       }
+       info->xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= SERIAL_XMIT_SIZE - 1;
+       info->xmit_cnt++;
+       /********************************************** why ??? ***********
+       if ( !tty->stopped && !tty->hw_stopped &&
+            !(info->IER & UART_IER_THRI) ) {
+           info->IER |= UART_IER_THRI;
+           outb(info->IER, info->base + UART_IER);
+       }
+       *****************************************************************/
+       restore_flags(flags);
+}
+
+static void mxser_flush_chars(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+           !info->xmit_buf)
+               return;
+
+       save_flags(flags);
+       cli();
+       info->IER |= UART_IER_THRI;
+       outb(info->IER, info->base + UART_IER);
+       restore_flags(flags);
+}
+
+static int mxser_write_room(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       int ret;
+
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+       return (ret);
+}
+
+static int mxser_chars_in_buffer(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+
+       return (info->xmit_cnt);
+}
+
+static void mxser_flush_buffer(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       restore_flags(flags);
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup) (tty);
+}
+
+static int mxser_ioctl(struct tty_struct *tty, struct file *file,
+                      unsigned int cmd, unsigned long arg)
+{
+       unsigned long flags;
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       int retval;
+       struct async_icount cprev, cnow;        /* kernel counter temps */
+       struct serial_icounter_struct *p_cuser;         /* user space */
+       unsigned long templ;
+
+       if (PORTNO(tty) == MXSER_PORTS)
+               return (mxser_ioctl_special(cmd, arg));
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) &&
+           (cmd != TIOCGICOUNT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                       return (-EIO);
+       }
+       switch (cmd) {
+       case TCSBRK:            /* SVID version: non-zero arg --> no break */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return (retval);
+               tty_wait_until_sent(tty, 0);
+               if (!arg)
+                       mxser_send_break(info, HZ / 4);         /* 1/4 second */
+               return (0);
+       case TCSBRKP:           /* support for POSIX tcsendbreak() */
+               retval = tty_check_change(tty);
+               if (retval)
+                       return (retval);
+               tty_wait_until_sent(tty, 0);
+               mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4);
+               return (0);
+       case TIOCGSOFTCAR:
+               return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+       case TIOCSSOFTCAR:
+               if(get_user(templ, (unsigned long *) arg))
+                       return -EFAULT;
+               arg = templ;
+               tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |
+                                        (arg ? CLOCAL : 0));
+               return (0);
+       case TIOCMGET:
+               return (mxser_get_modem_info(info, (unsigned int *) arg));
+       case TIOCMBIS:
+       case TIOCMBIC:
+       case TIOCMSET:
+               return (mxser_set_modem_info(info, cmd, (unsigned int *) arg));
+       case TIOCGSERIAL:
+               return (mxser_get_serial_info(info, (struct serial_struct *) arg));
+       case TIOCSSERIAL:
+               return (mxser_set_serial_info(info, (struct serial_struct *) arg));
+       case TIOCSERGETLSR:     /* Get line status register */
+               return (mxser_get_lsr_info(info, (unsigned int *) arg));
+               /*
+                * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+                * - mask passed in arg for lines of interest
+                *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+                * Caller should use TIOCGICOUNT to see which one it was
+                */
+       case TIOCMIWAIT:
+               save_flags(flags);
+               cli();
+               cprev = info->icount;   /* note the counters on entry */
+               restore_flags(flags);
+               while (1) {
+                       interruptible_sleep_on(&info->delta_msr_wait);
+                       /* see if a signal did it */
+                       if (signal_pending(current))
+                               return (-ERESTARTSYS);
+                       save_flags(flags);
+                       cli();
+                       cnow = info->icount;    /* atomic copy */
+                       restore_flags(flags);
+                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                         cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                               return (-EIO);  /* no change => error */
+                       if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                       ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                        ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+                       ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                               return (0);
+                       }
+                       cprev = cnow;
+               }
+               /* NOTREACHED */
+               /*
+                * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+                * Return: write counters to the user passed counter struct
+                * NB: both 1->0 and 0->1 transitions are counted except for
+                *     RI where only 0->1 is counted.
+                */
+       case TIOCGICOUNT:
+               save_flags(flags);
+               cli();
+               cnow = info->icount;
+               restore_flags(flags);
+               p_cuser = (struct serial_icounter_struct *) arg;
+               if(put_user(cnow.cts, &p_cuser->cts))
+                       return -EFAULT;
+               if(put_user(cnow.dsr, &p_cuser->dsr))
+                       return -EFAULT;
+               if(put_user(cnow.rng, &p_cuser->rng))
+                       return -EFAULT;
+               return put_user(cnow.dcd, &p_cuser->dcd);
+       case MOXA_HighSpeedOn:
+               return put_user(info->baud_base != 115200 ? 1 : 0, (int *) arg);
+       default:
+               return (-ENOIOCTLCMD);
+       }
+       return (0);
+}
+
+static int mxser_ioctl_special(unsigned int cmd, unsigned long arg)
+{
+       int i, result, status;
+
+       switch (cmd) {
+       case MOXA_GET_CONF:
+               if(copy_to_user((struct mxser_hwconf *) arg, mxsercfg,
+                            sizeof(struct mxser_hwconf) * 4))
+                               return -EFAULT;
+               return 0;
+       case MOXA_GET_MAJOR:
+               if(copy_to_user((int *) arg, &ttymajor, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+
+       case MOXA_GET_CUMAJOR:
+               if(copy_to_user((int *) arg, &calloutmajor, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+
+       case MOXA_CHKPORTENABLE:
+               result = 0;
+               for (i = 0; i < MXSER_PORTS; i++) {
+                       if (mxvar_table[i].base)
+                               result |= (1 << i);
+               }
+               return put_user(result, (unsigned long *) arg);
+       case MOXA_GETDATACOUNT:
+               if(copy_to_user((struct mxser_log *) arg, &mxvar_log, sizeof(mxvar_log)))
+                       return -EFAULT;
+               return (0);
+       case MOXA_GETMSTATUS:
+               for (i = 0; i < MXSER_PORTS; i++) {
+                       GMStatus[i].ri = 0;
+                       if (!mxvar_table[i].base) {
+                               GMStatus[i].dcd = 0;
+                               GMStatus[i].dsr = 0;
+                               GMStatus[i].cts = 0;
+                               continue;
+                       }
+                       if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios)
+                               GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag;
+                       else
+                               GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag;
+
+                       status = inb(mxvar_table[i].base + UART_MSR);
+                       if (status & 0x80 /*UART_MSR_DCD */ )
+                               GMStatus[i].dcd = 1;
+                       else
+                               GMStatus[i].dcd = 0;
+
+                       if (status & 0x20 /*UART_MSR_DSR */ )
+                               GMStatus[i].dsr = 1;
+                       else
+                               GMStatus[i].dsr = 0;
+
+
+                       if (status & 0x10 /*UART_MSR_CTS */ )
+                               GMStatus[i].cts = 1;
+                       else
+                               GMStatus[i].cts = 0;
+               }
+               if(copy_to_user((struct mxser_mstatus *) arg, GMStatus,
+                            sizeof(struct mxser_mstatus) * MXSER_PORTS))
+                       return -EFAULT;
+               return 0;
+       default:
+               return (-ENOIOCTLCMD);
+       }
+       return (0);
+}
+
+/*
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ */
+static void mxser_throttle(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       if (I_IXOFF(tty)) {
+               info->x_char = STOP_CHAR(tty);
+               save_flags(flags);
+               cli();
+               outb(info->IER, 0);
+               info->IER |= UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);         /* force Tx interrupt */
+               restore_flags(flags);
+       }
+       if (info->tty->termios->c_cflag & CRTSCTS) {
+               info->MCR &= ~UART_MCR_RTS;
+               save_flags(flags);
+               cli();
+               outb(info->MCR, info->base + UART_MCR);
+               restore_flags(flags);
+       }
+}
+
+static void mxser_unthrottle(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else {
+                       info->x_char = START_CHAR(tty);
+                       save_flags(flags);
+                       cli();
+                       outb(info->IER, 0);
+                       info->IER |= UART_IER_THRI;     /* force Tx interrupt */
+                       outb(info->IER, info->base + UART_IER);
+                       restore_flags(flags);
+               }
+       }
+       if (info->tty->termios->c_cflag & CRTSCTS) {
+               info->MCR |= UART_MCR_RTS;
+               save_flags(flags);
+               cli();
+               outb(info->MCR, info->base + UART_MCR);
+               restore_flags(flags);
+       }
+}
+
+static void mxser_set_termios(struct tty_struct *tty,
+                             struct termios *old_termios)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+
+/* 8-2-99 by William
+   if ( (tty->termios->c_cflag == old_termios->c_cflag) &&
+   (RELEVANT_IFLAG(tty->termios->c_iflag) ==
+   RELEVANT_IFLAG(old_termios->c_iflag)) )
+   return;
+
+   mxser_change_speed(info, old_termios);
+
+   if ( (old_termios->c_cflag & CRTSCTS) &&
+   !(tty->termios->c_cflag & CRTSCTS) ) {
+   tty->hw_stopped = 0;
+   mxser_start(tty);
+   }
+ */
+       if ((tty->termios->c_cflag != old_termios->c_cflag) ||
+           (RELEVANT_IFLAG(tty->termios->c_iflag) !=
+            RELEVANT_IFLAG(old_termios->c_iflag))) {
+
+               mxser_change_speed(info, old_termios);
+
+               if ((old_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->stopped = 0;
+               mxser_start(tty);
+       }
+}
+
+/*
+ * mxser_stop() and mxser_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ */
+static void mxser_stop(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if (info->IER & UART_IER_THRI) {
+               info->IER &= ~UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);
+       }
+       restore_flags(flags);
+}
+
+static void mxser_start(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if (info->xmit_cnt && info->xmit_buf &&
+           !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);
+       }
+       restore_flags(flags);
+}
+
+/*
+ * This routine is called by tty_hangup() when a hangup is signaled.
+ */
+void mxser_hangup(struct tty_struct *tty)
+{
+       struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;
+
+       mxser_flush_buffer(tty);
+       mxser_shutdown(info);
+       info->event = 0;
+       info->count = 0;
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+       info->tty = 0;
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void mxser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int status, i;
+       struct mxser_struct *info;
+       struct mxser_struct *port;
+       int max, irqbits, bits, msr;
+       int pass_counter = 0;
+
+       port = 0;
+       for (i = 0; i < MXSER_BOARDS; i++) {
+               if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) {
+                       port = dev_id;
+                       break;
+               }
+       }
+
+       if (i == MXSER_BOARDS)
+               return;
+       if (port == 0)
+               return;
+       max = mxser_numports[mxsercfg[i].board_type - 1];
+
+       while (1) {
+               irqbits = inb(port->vector) & port->vectormask;
+               if (irqbits == port->vectormask)
+                       break;
+               for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) {
+                       if (irqbits == port->vectormask)
+                               break;
+                       if (bits & irqbits)
+                               continue;
+                       info = port + i;
+                       if (!info->tty ||
+                         (inb(info->base + UART_IIR) & UART_IIR_NO_INT))
+                               continue;
+                       status = inb(info->base + UART_LSR) & info->read_status_mask;
+                       if (status & UART_LSR_DR)
+                               mxser_receive_chars(info, &status);
+                       msr = inb(info->base + UART_MSR);
+                       if (msr & UART_MSR_ANY_DELTA)
+                               mxser_check_modem_status(info, msr);
+                       if (status & UART_LSR_THRE) {
+/* 8-2-99 by William
+   if ( info->x_char || (info->xmit_cnt > 0) )
+ */
+                               mxser_transmit_chars(info);
+                       }
+               }
+               if (pass_counter++ > MXSER_ISR_PASS_LIMIT) {
+#if 0
+                       printk("MOXA Smartio/Indusrtio family driver interrupt loop break\n");
+#endif
+                       break;  /* Prevent infinite loops */
+               }
+       }
+}
+
+static inline void mxser_receive_chars(struct mxser_struct *info,
+                                        int *status)
+{
+       struct tty_struct *tty = info->tty;
+       unsigned char ch;
+       int ignored = 0;
+       int cnt = 0;
+
+       do {
+               ch = inb(info->base + UART_RX);
+               if (*status & info->ignore_status_mask) {
+                       if (++ignored > 100)
+                               break;
+               } else {
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                               break;
+                       tty->flip.count++;
+                       if (*status & UART_LSR_SPECIAL) {
+                               if (*status & UART_LSR_BI) {
+                                       *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+                                       if (info->flags & ASYNC_SAK)
+                                               do_SAK(tty);
+                               } else if (*status & UART_LSR_PE) {
+                                       *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+                               } else if (*status & UART_LSR_FE) {
+                                       *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+                               } else if (*status & UART_LSR_OE) {
+                                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+                               } else
+                                       *tty->flip.flag_buf_ptr++ = 0;
+                       } else
+                               *tty->flip.flag_buf_ptr++ = 0;
+                       *tty->flip.char_buf_ptr++ = ch;
+                       cnt++;
+               }
+               *status = inb(info->base + UART_LSR) & info->read_status_mask;
+       } while (*status & UART_LSR_DR);
+       mxvar_log.rxcnt[info->port] += cnt;
+       queue_task(&tty->flip.tqueue, &tq_timer);
+
+}
+
+static inline void mxser_transmit_chars(struct mxser_struct *info)
+{
+       int count, cnt;
+
+       if (info->x_char) {
+               outb(info->x_char, info->base + UART_TX);
+               info->x_char = 0;
+               mxvar_log.txcnt[info->port]++;
+               return;
+       }
+       if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+           info->tty->hw_stopped) {
+               info->IER &= ~UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);
+               return;
+       }
+       cnt = info->xmit_cnt;
+       count = info->xmit_fifo_size;
+       do {
+               outb(info->xmit_buf[info->xmit_tail++], info->base + UART_TX);
+               info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+               if (--info->xmit_cnt <= 0)
+                       break;
+       } while (--count > 0);
+       mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt);
+
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+               set_bit(MXSER_EVENT_TXLOW, &info->event);
+               queue_task(&info->tqueue, &tq_scheduler);
+       }
+       if (info->xmit_cnt <= 0) {
+               info->IER &= ~UART_IER_THRI;
+               outb(info->IER, info->base + UART_IER);
+       }
+}
+
+static inline void mxser_check_modem_status(struct mxser_struct *info,
+                                             int status)
+{
+
+       /* update input line counters */
+       if (status & UART_MSR_TERI)
+               info->icount.rng++;
+       if (status & UART_MSR_DDSR)
+               info->icount.dsr++;
+       if (status & UART_MSR_DDCD)
+               info->icount.dcd++;
+       if (status & UART_MSR_DCTS)
+               info->icount.cts++;
+       wake_up_interruptible(&info->delta_msr_wait);
+
+       if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+               if (status & UART_MSR_DCD)
+                       wake_up_interruptible(&info->open_wait);
+               else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                          (info->flags & ASYNC_CALLOUT_NOHUP)))
+                       set_bit(MXSER_EVENT_HANGUP, &info->event);
+               queue_task(&info->tqueue, &tq_scheduler);
+
+       }
+       if (info->flags & ASYNC_CTS_FLOW) {
+               if (info->tty->hw_stopped) {
+                       if (status & UART_MSR_CTS) {
+                               info->tty->hw_stopped = 0;
+                               info->IER |= UART_IER_THRI;
+                               outb(info->IER, info->base + UART_IER);
+
+                               set_bit(MXSER_EVENT_TXLOW, &info->event);
+                               queue_task(&info->tqueue, &tq_scheduler);
+                       }
+               } else {
+                       if (!(status & UART_MSR_CTS)) {
+                               info->tty->hw_stopped = 1;
+                               info->IER &= ~UART_IER_THRI;
+                               outb(info->IER, info->base + UART_IER);
+                       }
+               }
+       }
+}
+
+static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp,
+                                struct mxser_struct *info)
+{
+       struct wait_queue wait = {current, NULL};
+       unsigned long flags;
+       int retval;
+       int do_clocal = 0;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               if (info->flags & ASYNC_HUP_NOTIFY)
+                       return (-EAGAIN);
+               else
+                       return (-ERESTARTSYS);
+#else
+               return (-EAGAIN);
+#endif
+       }
+       /*
+        * 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 (info->flags & ASYNC_NORMAL_ACTIVE)
+                       return (-EBUSY);
+               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ASYNC_SESSION_LOCKOUT) &&
+                   (info->session != current->session))
+                       return (-EBUSY);
+               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ASYNC_PGRP_LOCKOUT) &&
+                   (info->pgrp != current->pgrp))
+                       return (-EBUSY);
+               info->flags |= ASYNC_CALLOUT_ACTIVE;
+               return (0);
+       }
+       /*
+        * 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))) {
+               if (info->flags & ASYNC_CALLOUT_ACTIVE)
+                       return (-EBUSY);
+               info->flags |= ASYNC_NORMAL_ACTIVE;
+               return (0);
+       }
+       if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+               if (info->normal_termios.c_cflag & CLOCAL)
+                       do_clocal = 1;
+       } else {
+               if (tty->termios->c_cflag & CLOCAL)
+                       do_clocal = 1;
+       }
+
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, info->count is dropped by one, so that
+        * mxser_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+       save_flags(flags);
+       cli();
+       if (!tty_hung_up_p(filp))
+               info->count--;
+       restore_flags(flags);
+       info->blocked_open++;
+       while (1) {
+               save_flags(flags);
+               cli();
+               if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+                       outb(inb(info->base + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS,
+                            info->base + UART_MCR);
+               restore_flags(flags);
+               current->state = TASK_INTERRUPTIBLE;
+               if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (info->flags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   !(info->flags & ASYNC_CLOSING) &&
+                   (do_clocal || (inb(info->base + UART_MSR) & UART_MSR_DCD)))
+                       break;
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->open_wait, &wait);
+       if (!tty_hung_up_p(filp))
+               info->count++;
+       info->blocked_open--;
+       if (retval)
+               return (retval);
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return (0);
+}
+
+static int mxser_startup(struct mxser_struct *info)
+{
+       unsigned long flags;
+       unsigned long page;
+
+       page = get_free_page(GFP_KERNEL);
+       if (!page)
+               return (-ENOMEM);
+
+       save_flags(flags);
+       cli();
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               free_page(page);
+               restore_flags(flags);
+               return (0);
+       }
+       if (!info->base || !info->type) {
+               if (info->tty)
+                       set_bit(TTY_IO_ERROR, &info->tty->flags);
+               free_page(page);
+               restore_flags(flags);
+               return (0);
+       }
+       if (info->xmit_buf)
+               free_page(page);
+       else
+               info->xmit_buf = (unsigned char *) page;
+
+       /*
+        * Clear the FIFO buffers and disable them
+        * (they will be reenabled in mxser_change_speed())
+        */
+       if (info->xmit_fifo_size == 16)
+               outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
+                    info->base + UART_FCR);
+
+       /*
+        * At this point there's no way the LSR could still be 0xFF;
+        * if it is, then bail out, because there's likely no UART
+        * here.
+        */
+       if (inb(info->base + UART_LSR) == 0xff) {
+               restore_flags(flags);
+               if (suser()) {
+                       if (info->tty)
+                               set_bit(TTY_IO_ERROR, &info->tty->flags);
+                       return (0);
+               } else
+                       return (-ENODEV);
+       }
+       /*
+        * Clear the interrupt registers.
+        */
+       (void) inb(info->base + UART_LSR);
+       (void) inb(info->base + UART_RX);
+       (void) inb(info->base + UART_IIR);
+       (void) inb(info->base + UART_MSR);
+
+       /*
+        * Now, initialize the UART
+        */
+       outb(UART_LCR_WLEN8, info->base + UART_LCR);    /* reset DLAB */
+       info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+       outb(info->MCR, info->base + UART_MCR);
+
+       /*
+        * Finally, enable interrupts
+        */
+       info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+       outb(info->IER, info->base + UART_IER);         /* enable interrupts */
+
+       /*
+        * And clear the interrupt registers again for luck.
+        */
+       (void) inb(info->base + UART_LSR);
+       (void) inb(info->base + UART_RX);
+       (void) inb(info->base + UART_IIR);
+       (void) inb(info->base + UART_MSR);
+
+       if (info->tty)
+               test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       /*
+        * and set the speed of the serial port
+        */
+       mxser_change_speed(info, 0);
+
+       info->flags |= ASYNC_INITIALIZED;
+       restore_flags(flags);
+       return (0);
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts maybe disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mxser_shutdown(struct mxser_struct *info)
+{
+       unsigned long flags;
+
+       if (!(info->flags & ASYNC_INITIALIZED))
+               return;
+
+       save_flags(flags);
+       cli();                  /* Disable interrupts */
+
+       /*
+        * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+        * here so the queue might never be waken up
+        */
+       wake_up_interruptible(&info->delta_msr_wait);
+
+       /*
+        * Free the IRQ, if necessary
+        */
+       if (info->xmit_buf) {
+               free_page((unsigned long) info->xmit_buf);
+               info->xmit_buf = 0;
+       }
+       info->IER = 0;
+       outb(0x00, info->base + UART_IER);      /* disable all intrs */
+
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+               info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
+       outb(info->MCR, info->base + UART_MCR);
+
+       /* clear Rx/Tx FIFO's */
+       outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR);
+       /* read data port to reset things */
+       (void) inb(info->base + UART_RX);
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ASYNC_INITIALIZED;
+       restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static int mxser_change_speed(struct mxser_struct *info,
+                             struct termios *old_termios)
+{
+       int quot = 0;
+       unsigned cflag, cval, fcr;
+       int i;
+       int ret = 0;
+       unsigned long flags;
+
+       if (!info->tty || !info->tty->termios)
+               return ret;
+       cflag = info->tty->termios->c_cflag;
+       if (!(info->base))
+               return ret;
+
+#ifndef B921600
+#define B921600 (B460800 +1)
+#endif
+       switch (cflag & (CBAUD | CBAUDEX)) {
+       case B921600:
+               i = 20;
+               break;
+       case B460800:
+               i = 19;
+               break;
+       case B230400:
+               i = 18;
+               break;
+       case B115200:
+               i = 17;
+               break;
+       case B57600:
+               i = 16;
+               break;
+       case B38400:
+               i = 15;
+               break;
+       case B19200:
+               i = 14;
+               break;
+       case B9600:
+               i = 13;
+               break;
+       case B4800:
+               i = 12;
+               break;
+       case B2400:
+               i = 11;
+               break;
+       case B1800:
+               i = 10;
+               break;
+       case B1200:
+               i = 9;
+               break;
+       case B600:
+               i = 8;
+               break;
+       case B300:
+               i = 7;
+               break;
+       case B200:
+               i = 6;
+               break;
+       case B150:
+               i = 5;
+               break;
+       case B134:
+               i = 4;
+               break;
+       case B110:
+               i = 3;
+               break;
+       case B75:
+               i = 2;
+               break;
+       case B50:
+               i = 1;
+               break;
+       default:
+               i = 0;
+               break;
+       }
+
+       if (i == 15) {
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       i = 16; /* 57600 bps */
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       i = 17; /* 115200 bps */
+
+#ifdef ASYNC_SPD_SHI
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       i = 18;
+#endif
+
+#ifdef ASYNC_SPD_WARP
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       i = 19;
+#endif
+       }
+       if (mxvar_baud_table[i] == 134) {
+               quot = (2 * info->baud_base / 269);
+       } else if (mxvar_baud_table[i]) {
+               quot = info->baud_base / mxvar_baud_table[i];
+               if (!quot && old_termios) {
+                       /* re-calculate */
+                       info->tty->termios->c_cflag &= ~CBAUD;
+                       info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+                       switch (info->tty->termios->c_cflag & (CBAUD | CBAUDEX)) {
+                       case B921600:
+                               i = 20;
+                               break;
+                       case B460800:
+                               i = 19;
+                               break;
+                       case B230400:
+                               i = 18;
+                               break;
+                       case B115200:
+                               i = 17;
+                               break;
+                       case B57600:
+                               i = 16;
+                               break;
+                       case B38400:
+                               i = 15;
+                               break;
+                       case B19200:
+                               i = 14;
+                               break;
+                       case B9600:
+                               i = 13;
+                               break;
+                       case B4800:
+                               i = 12;
+                               break;
+                       case B2400:
+                               i = 11;
+                               break;
+                       case B1800:
+                               i = 10;
+                               break;
+                       case B1200:
+                               i = 9;
+                               break;
+                       case B600:
+                               i = 8;
+                               break;
+                       case B300:
+                               i = 7;
+                               break;
+                       case B200:
+                               i = 6;
+                               break;
+                       case B150:
+                               i = 5;
+                               break;
+                       case B134:
+                               i = 4;
+                               break;
+                       case B110:
+                               i = 3;
+                               break;
+                       case B75:
+                               i = 2;
+                               break;
+                       case B50:
+                               i = 1;
+                               break;
+                       default:
+                               i = 0;
+                               break;
+                       }
+                       if (i == 15) {
+                               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                                       i = 16;         /* 57600 bps */
+                               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                                       i = 17;         /* 115200 bps */
+#ifdef ASYNC_SPD_SHI
+                               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                                       i = 18;
+#endif
+#ifdef ASYNC_SPD_WARP
+                               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                                       i = 19;
+#endif
+                       }
+                       if (mxvar_baud_table[i] == 134) {
+                               quot = (2 * info->baud_base / 269);
+                       } else if (mxvar_baud_table[i]) {
+                               quot = info->baud_base / mxvar_baud_table[i];
+                               if (quot == 0)
+                                       quot = 1;
+                       } else {
+                               quot = 0;
+                       }
+               } else if (quot == 0)
+                       quot = 1;
+       } else {
+               quot = 0;
+       }
+
+       if (quot) {
+               info->MCR |= UART_MCR_DTR;
+               save_flags(flags);
+               cli();
+               outb(info->MCR, info->base + UART_MCR);
+               restore_flags(flags);
+       } else {
+               info->MCR &= ~UART_MCR_DTR;
+               save_flags(flags);
+               cli();
+               outb(info->MCR, info->base + UART_MCR);
+               restore_flags(flags);
+               return ret;
+       }
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               cval = 0x00;
+               break;
+       case CS6:
+               cval = 0x01;
+               break;
+       case CS7:
+               cval = 0x02;
+               break;
+       case CS8:
+               cval = 0x03;
+               break;
+       default:
+               cval = 0x00;
+               break;          /* too keep GCC shut... */
+       }
+       if (cflag & CSTOPB)
+               cval |= 0x04;
+       if (cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+       if ((info->type == PORT_8250) || (info->type == PORT_16450)) {
+               fcr = 0;
+       } else {
+               fcr = UART_FCR_ENABLE_FIFO;
+               switch (info->rx_trigger) {
+               case 1:
+                       fcr |= UART_FCR_TRIGGER_1;
+                       break;
+               case 4:
+                       fcr |= UART_FCR_TRIGGER_4;
+                       break;
+               case 8:
+                       fcr |= UART_FCR_TRIGGER_8;
+                       break;
+               default:
+                       fcr |= UART_FCR_TRIGGER_14;
+               }
+       }
+
+       /* CTS flow control flag and modem status interrupts */
+       info->IER &= ~UART_IER_MSI;
+       info->MCR &= ~UART_MCR_AFE;
+       if (cflag & CRTSCTS) {
+               info->flags |= ASYNC_CTS_FLOW;
+               info->IER |= UART_IER_MSI;
+               if (info->type == PORT_16550A)
+                       info->MCR |= UART_MCR_AFE;
+       } else {
+               info->flags &= ~ASYNC_CTS_FLOW;
+       }
+       outb(info->MCR, info->base + UART_MCR);
+       if (cflag & CLOCAL)
+               info->flags &= ~ASYNC_CHECK_CD;
+       else {
+               info->flags |= ASYNC_CHECK_CD;
+               info->IER |= UART_IER_MSI;
+       }
+       outb(info->IER, info->base + UART_IER);
+
+       /*
+        * Set up parity check flag
+        */
+       info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (I_INPCK(info->tty))
+               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+               info->read_status_mask |= UART_LSR_BI;
+
+       info->ignore_status_mask = 0;
+#if 0
+       /* This should be safe, but for some broken bits of hardware... */
+       if (I_IGNPAR(info->tty)) {
+               info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+               info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       }
+#endif
+       if (I_IGNBRK(info->tty)) {
+               info->ignore_status_mask |= UART_LSR_BI;
+               info->read_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignore parity and break indicators, ignore
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->tty)) {
+                       info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE;
+                       info->read_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE;
+               }
+       }
+       save_flags(flags);
+       cli();
+       outb(cval | UART_LCR_DLAB, info->base + UART_LCR);      /* set DLAB */
+       outb(quot & 0xff, info->base + UART_DLL);       /* LS of divisor */
+       outb(quot >> 8, info->base + UART_DLM);         /* MS of divisor */
+       outb(cval, info->base + UART_LCR);      /* reset DLAB */
+       outb(fcr, info->base + UART_FCR);       /* set fcr */
+       restore_flags(flags);
+
+       return ret;
+}
+
+/*
+ * ------------------------------------------------------------
+ * friends of mxser_ioctl()
+ * ------------------------------------------------------------
+ */
+static int mxser_get_serial_info(struct mxser_struct *info,
+                                struct serial_struct *retinfo)
+{
+       struct serial_struct tmp;
+
+       if (!retinfo)
+               return (-EFAULT);
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = info->type;
+       tmp.line = info->port;
+       tmp.port = info->base;
+       tmp.irq = info->irq;
+       tmp.flags = info->flags;
+       tmp.baud_base = info->baud_base;
+       tmp.close_delay = info->close_delay;
+       tmp.closing_wait = info->closing_wait;
+       tmp.custom_divisor = info->custom_divisor;
+       tmp.hub6 = 0;
+       copy_to_user(retinfo, &tmp, sizeof(*retinfo));
+       return (0);
+}
+
+static int mxser_set_serial_info(struct mxser_struct *info,
+                                struct serial_struct *new_info)
+{
+       struct serial_struct new_serial;
+       unsigned int flags;
+       int retval = 0;
+
+       if (!new_info || !info->base)
+               return (-EFAULT);
+       copy_from_user(&new_serial, new_info, sizeof(new_serial));
+
+       if ((new_serial.irq != info->irq) ||
+           (new_serial.port != info->base) ||
+           (new_serial.type != info->type) ||
+           (new_serial.custom_divisor != info->custom_divisor) ||
+           (new_serial.baud_base != info->baud_base))
+               return (-EPERM);
+
+       flags = info->flags & ASYNC_SPD_MASK;
+
+       if (!suser()) {
+               if ((new_serial.baud_base != info->baud_base) ||
+                   (new_serial.close_delay != info->close_delay) ||
+                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (info->flags & ~ASYNC_USR_MASK)))
+                       return (-EPERM);
+               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+       } else {
+               /*
+                * OK, past this point, all the error checking has been done.
+                * At this point, we start making changes.....
+                */
+               info->flags = ((info->flags & ~ASYNC_FLAGS) |
+                              (new_serial.flags & ASYNC_FLAGS));
+               info->close_delay = new_serial.close_delay * HZ / 100;
+               info->closing_wait = new_serial.closing_wait * HZ / 100;
+       }
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               if (flags != (info->flags & ASYNC_SPD_MASK)) {
+                       mxser_change_speed(info, 0);
+               }
+       } else
+               retval = mxser_startup(info);
+       return (retval);
+}
+
+/*
+ * mxser_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows an RS485 driver to be written in user space.
+ */
+static int mxser_get_lsr_info(struct mxser_struct *info, unsigned int *value)
+{
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       status = inb(info->base + UART_LSR);
+       restore_flags(flags);
+       result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+       put_user(result, value);
+       return (0);
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void mxser_send_break(struct mxser_struct *info, int duration)
+{
+       unsigned long flags;
+       if (!info->base)
+               return;
+       current->state = TASK_INTERRUPTIBLE;
+       save_flags(flags);
+       cli();
+       outb(inb(info->base + UART_LCR) | UART_LCR_SBC, info->base + UART_LCR);
+       schedule_timeout(duration);
+       outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, info->base + UART_LCR);
+       restore_flags(flags);
+}
+
+static int mxser_get_modem_info(struct mxser_struct *info,
+                               unsigned int *value)
+{
+       unsigned char control, status;
+       unsigned int result;
+       unsigned long flags;
+
+       control = info->MCR;
+       save_flags(flags);
+       cli();
+       status = inb(info->base + UART_MSR);
+       if (status & UART_MSR_ANY_DELTA)
+               mxser_check_modem_status(info, status);
+       restore_flags(flags);
+       result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
+           ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
+           ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) |
+           ((status & UART_MSR_RI) ? TIOCM_RNG : 0) |
+           ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) |
+           ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+       put_user(result, value);
+       return (0);
+}
+
+static int mxser_set_modem_info(struct mxser_struct *info, unsigned int cmd,
+                               unsigned int *value)
+{
+       unsigned int arg;
+       unsigned long flags;
+
+       if(get_user(arg, value))
+               return -EFAULT;
+       switch (cmd) {
+       case TIOCMBIS:
+               if (arg & TIOCM_RTS)
+                       info->MCR |= UART_MCR_RTS;
+               if (arg & TIOCM_DTR)
+                       info->MCR |= UART_MCR_DTR;
+               break;
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS)
+                       info->MCR &= ~UART_MCR_RTS;
+               if (arg & TIOCM_DTR)
+                       info->MCR &= ~UART_MCR_DTR;
+               break;
+       case TIOCMSET:
+               info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) |
+                            ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) |
+                            ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+               break;
+       default:
+               return (-EINVAL);
+       }
+       save_flags(flags);
+       cli();
+       outb(info->MCR, info->base + UART_MCR);
+       restore_flags(flags);
+       return (0);
+}
+
+static int mxser_read_register(int, unsigned short *);
+static int mxser_program_mode(int);
+static void mxser_normal_mode(int);
+
+static int mxser_get_ISA_conf(int cap, struct mxser_hwconf *hwconf)
+{
+       int id, i, bits;
+       unsigned short regs[16], irq;
+       unsigned char scratch, scratch2;
+
+       id = mxser_read_register(cap, regs);
+       if (id == C168_ASIC_ID)
+               hwconf->board_type = MXSER_BOARD_C168_ISA;
+       else if (id == C104_ASIC_ID)
+               hwconf->board_type = MXSER_BOARD_C104_ISA;
+       else if (id == CI104J_ASIC_ID)
+               hwconf->board_type = MXSER_BOARD_CI104J;
+       else
+               return (0);
+       irq = regs[9] & 0x0F;
+       irq = irq | (irq << 4);
+       irq = irq | (irq << 8);
+       if ((irq != regs[9]) || ((id == 1) && (irq != regs[10]))) {
+               return (MXSER_ERR_IRQ_CONFLIT);
+       }
+       if (!irq) {
+               return (MXSER_ERR_IRQ);
+       }
+       for (i = 0; i < 8; i++)
+               hwconf->ioaddr[i] = (int) regs[i + 1] & 0xFFF8;
+       hwconf->irq = (int) (irq & 0x0F);
+       if ((regs[12] & 0x80) == 0) {
+               return (MXSER_ERR_VECTOR);
+       }
+       hwconf->vector = (int) regs[11];        /* interrupt vector */
+       if (id == 1)
+               hwconf->vector_mask = 0x00FF;
+       else
+               hwconf->vector_mask = 0x000F;
+       for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) {
+               if (regs[12] & bits)
+                       hwconf->baud_base[i] = 921600;
+               else
+                       hwconf->baud_base[i] = 115200;
+       }
+       scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB);
+       outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR);
+       outb(0, cap + UART_EFR);        /* EFR is the same as FCR */
+       outb(scratch2, cap + UART_LCR);
+       outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR);
+       scratch = inb(cap + UART_IIR);
+       if (scratch & 0xC0)
+               hwconf->uart_type = PORT_16550A;
+       else
+               hwconf->uart_type = PORT_16450;
+       if (id == 1)
+               hwconf->ports = 8;
+       else
+               hwconf->ports = 4;
+       return (hwconf->ports);
+}
+
+#define CHIP_SK        0x01    /* Serial Data Clock  in Eprom */
+#define CHIP_DO        0x02    /* Serial Data Output in Eprom */
+#define CHIP_CS        0x04    /* Serial Chip Select in Eprom */
+#define CHIP_DI        0x08    /* Serial Data Input  in Eprom */
+#define EN_CCMD        0x000   /* Chip's command register     */
+#define EN0_RSARLO     0x008   /* Remote start address reg 0  */
+#define EN0_RSARHI     0x009   /* Remote start address reg 1  */
+#define EN0_RCNTLO     0x00A   /* Remote byte count reg WR    */
+#define EN0_RCNTHI     0x00B   /* Remote byte count reg WR    */
+#define EN0_DCFG       0x00E   /* Data configuration reg WR   */
+#define EN0_PORT       0x010   /* Rcv missed frame error counter RD */
+#define ENC_PAGE0      0x000   /* Select page 0 of chip registers   */
+#define ENC_PAGE3      0x0C0   /* Select page 3 of chip registers   */
+static int mxser_read_register(int port, unsigned short *regs)
+{
+       int i, k, value, id;
+       unsigned int j;
+
+       id = mxser_program_mode(port);
+       if (id < 0)
+               return (id);
+       for (i = 0; i < 14; i++) {
+               k = (i & 0x3F) | 0x180;
+               for (j = 0x100; j > 0; j >>= 1) {
+                       outb(CHIP_CS, port);
+                       if (k & j) {
+                               outb(CHIP_CS | CHIP_DO, port);
+                               outb(CHIP_CS | CHIP_DO | CHIP_SK, port);        /* A? bit of read */
+                       } else {
+                               outb(CHIP_CS, port);
+                               outb(CHIP_CS | CHIP_SK, port);  /* A? bit of read */
+                       }
+               }
+               (void) inb(port);
+               value = 0;
+               for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) {
+                       outb(CHIP_CS, port);
+                       outb(CHIP_CS | CHIP_SK, port);
+                       if (inb(port) & CHIP_DI)
+                               value |= j;
+               }
+               regs[i] = value;
+               outb(0, port);
+       }
+       mxser_normal_mode(port);
+       return (id);
+}
+
+static int mxser_program_mode(int port)
+{
+       int id, i, j, n;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       outb(0, port);
+       outb(0, port);
+       outb(0, port);
+       (void) inb(port);
+       (void) inb(port);
+       outb(0, port);
+       (void) inb(port);
+       restore_flags(flags);
+       id = inb(port + 1) & 0x1F;
+       if ((id != C168_ASIC_ID) && (id != C104_ASIC_ID) && (id != CI104J_ASIC_ID))
+               return (-1);
+       for (i = 0, j = 0; i < 4; i++) {
+               n = inb(port + 2);
+               if (n == 'M') {
+                       j = 1;
+               } else if ((j == 1) && (n == 1)) {
+                       j = 2;
+                       break;
+               } else
+                       j = 0;
+       }
+       if (j != 2)
+               id = -2;
+       return (id);
+}
+
+static void mxser_normal_mode(int port)
+{
+       int i, n;
+
+       outb(0xA5, port + 1);
+       outb(0x80, port + 3);
+       outb(12, port + 0);     /* 9600 bps */
+       outb(0, port + 1);
+       outb(0x03, port + 3);   /* 8 data bits */
+       outb(0x13, port + 4);   /* loop back mode */
+       for (i = 0; i < 16; i++) {
+               n = inb(port + 5);
+               if ((n & 0x61) == 0x60)
+                       break;
+               if ((n & 1) == 1)
+                       (void) inb(port);
+       }
+       outb(0x00, port + 4);
+}
index b1ec779cf23c81f139e8d417d22963f348d8bc79..c753ae1866df8cdfc7b3afdce531760b4665a77e 100644 (file)
@@ -2209,6 +2209,12 @@ __initfunc(int tty_init(void))
         rs_8xx_init();
 #endif /* CONFIG_8xx */
        pty_init();
+#ifdef CONFIG_MOXA_SMARTIO
+       mxser_init();
+#endif 
+#ifdef CONFIG_MOXA_INTELLIO
+       moxa_init();
+#endif 
 #ifdef CONFIG_VT
        vcs_init();
 #endif
index 57a6a0c224a435070e595ea3c4aa32360b5d3c9a..161a299e6516cb5fb64ee824185cbb22f2f17a4d 100644 (file)
@@ -1,5 +1,5 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
-   Silicon Integrated System Corporation 
+   Copyright 1999 Silicon Integrated System Corporation 
    Revision:   1.05    Aug 7 1999
    
    Modified from the driver which is originally written by Donald Becker. 
@@ -18,6 +18,8 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
    
+   Rev 1.06.02 Nov. 23 1999 Ollie Lho 
+   Rev 1.06.01 Nov. 16 1999 Ollie Lho CRC calculation provide by Joseph Zbiciak (im14u2c@primenet.com)
    Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
    Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx  
    Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
@@ -47,7 +49,7 @@
 #include "sis900.h"
 
 static const char *version =
-"sis900.c: v1.06  11/04/99\n";
+"sis900.c: v1.06.03  12/23/99\n";
 
 static int max_interrupt_work = 20;
 #define sis900_debug debug
@@ -221,17 +223,18 @@ static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_
        if (did_version++ == 0)
                printk(KERN_INFO "%s", version);
 
+       if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
+               return NULL;
+
        /* check to see if we have sane EEPROM */
        signature = (u16) read_eeprom(ioaddr, EEPROMSignature);    
        if (signature == 0xffff || signature == 0x0000) {
-               printk (KERN_INFO "%s: Error EERPOM read %x\n", 
+               printk (KERN_INFO "%s: Error EEPROM read: %x\n",
                        net_dev->name, signature);
+               unregister_netdevice(net_dev);
                return NULL;
        }
 
-       if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
-               return NULL;
-
        printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name,
               ioaddr, irq);
 
@@ -509,7 +512,7 @@ sis900_open(struct device *net_dev)
         net_dev->start = 1;
 
         /* Enable all known interrupts by setting the interrupt mask. */
-       outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+       outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
         outl(RxENA, ioaddr + cr);
         outl(IE, ioaddr + ier);
 
@@ -770,7 +773,6 @@ static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed
                               "full" : "half");
                else
                        printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-                       
        }
        else {
                /* HomePNA */
@@ -787,6 +789,7 @@ static void sis900_tx_timeout(struct device *net_dev)
 {
         struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
         long ioaddr = net_dev->base_addr;
+       int i;
 
        printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
               net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
@@ -794,14 +797,25 @@ static void sis900_tx_timeout(struct device *net_dev)
         /* Disable interrupts by clearing the interrupt mask. */
         outl(0x0000, ioaddr + imr);
 
-        sis_priv->cur_rx = 0;
+       /* discard unsent packets, should this code section be protected by
+          cli(), sti() ?? */
+       sis_priv->dirty_tx = sis_priv->cur_tx = 0;
+       for (i = 0; i < NUM_TX_DESC; i++) {
+               if (sis_priv->tx_skbuff[i] != NULL) {
+                   dev_kfree_skb(sis_priv->tx_skbuff[i]);
+                   sis_priv->tx_skbuff[i] = 0;
+                       sis_priv->tx_ring[i].cmdsts = 0;
+                       sis_priv->tx_ring[i].bufptr = 0;
+                       sis_priv->stats.tx_dropped++;
+               }
+       }
         net_dev->trans_start = jiffies;
-        sis_priv->stats.tx_errors++;
+       net_dev->tbusy = sis_priv->tx_full = 0;
 
        /* FIXME: Should we restart the transmission thread here  ?? */
 
         /* Enable all known interrupts by setting the interrupt mask. */
-        outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+       outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
         return;
 }
 
@@ -876,13 +890,7 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
         do {
                 status = inl(ioaddr + isr);
                
-                if (sis900_debug > 3)
-                       printk(KERN_INFO "%s: entering interrupt, "
-                              "original status = %#8.8x, "
-                              "new status = %#8.8x.\n",
-                              net_dev->name, status, inl(ioaddr + isr));
-
-                if ((status & (HIBERR|TxURN|TxERR|TxOK|RxORN|RxERR|RxOK)) == 0)
+               if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0)
                        /* nothing intresting happened */
                         break;
 
@@ -891,7 +899,7 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                        /* Rx interrupt */
                         sis900_rx(net_dev);
 
-                if (status & (TxURN | TxERR | TxOK))
+               if (status & (TxURN | TxERR | TxIDLE))
                        /* Tx interrupt */
                        sis900_finish_xmit(net_dev);
 
@@ -961,6 +969,9 @@ static int sis900_rx(struct device *net_dev)
                 } else {
                        struct sk_buff * skb;
                        
+                       /* This situation should never happen, but due to
+                          some unknow bugs, it is possible that
+                          we are working on NULL sk_buff :-( */
                        if (sis_priv->rx_skbuff[entry] == NULL) {
                                printk(KERN_INFO "%s: NULL pointer " 
                                       "encountered in Rx ring, skipping\n",
@@ -1033,7 +1044,9 @@ static void sis900_finish_xmit (struct device *net_dev)
                tx_status = sis_priv->tx_ring[entry].cmdsts;
                
                if (tx_status & OWN) {
-                       /* The packet is not transmited yet (owned by hardware) ! */
+                       /* The packet is not transmited yet (owned by hardware) !
+                          Note: the interrupt is generated only when Tx Machine
+                          is idle, so this is an almost impossible case */
                        break;
                }
                
@@ -1054,8 +1067,6 @@ static void sis900_finish_xmit (struct device *net_dev)
                                sis_priv->stats.tx_window_errors++;
                } else {
                        /* packet successfully transmited */
-                       if (sis900_debug > 3)
-                               printk(KERN_INFO "Tx Transmit OK\n");
                        sis_priv->stats.collisions += (tx_status & COLCNT) >> 16;
                        sis_priv->stats.tx_bytes += tx_status & DSIZE;
                        sis_priv->stats.tx_packets++;
@@ -1069,8 +1080,8 @@ static void sis900_finish_xmit (struct device *net_dev)
        
        if (sis_priv->tx_full && net_dev->tbusy && 
            sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
-               /* The ring is no longer full, clear tbusy, tx_full and schedule 
-                  more transmission by marking NET_BH */
+               /* The ring is no longer full, clear tbusy, tx_full and 
+                  schedule more transmission by marking NET_BH */
                sis_priv->tx_full = 0;
                clear_bit(0, (void *)&net_dev->tbusy);
                mark_bh(NET_BH);
@@ -1153,11 +1164,13 @@ static u16 sis900_compute_hashtable_index(u8 *addr)
 {
 
 /* what is the correct value of the POLYNOMIAL ??
-   Donald Becker use 0x04C11DB7U */
-#define POLYNOMIAL 0x04C11DB6L
+   Donald Becker use 0x04C11DB7U
+   Joseph Zbiciak im14u2c@primenet.com gives me the
+   correct answer, thank you Joe !! */
+#define POLYNOMIAL 0x04C11DB7L
        u32 crc = 0xffffffff, msb;
        int  i, j;
-       u byte;
+       u32 byte;
 
        for (i = 0; i < 6; i++) {
                byte = *addr++;
@@ -1166,7 +1179,6 @@ static u16 sis900_compute_hashtable_index(u8 *addr)
                        crc <<= 1;
                        if (msb ^ (byte & 1)) {
                                crc ^= POLYNOMIAL;
-                               crc |= 1;
                        }
                        byte >>= 1;
                }
@@ -1189,13 +1201,13 @@ static void set_rx_mode(struct device *net_dev)
                         mc_filter[i] = 0xffff;
         } else if ((net_dev->mc_count > multicast_filter_limit) ||
                   (net_dev->flags & IFF_ALLMULTI)) {
-               /* too many multicast addresses or accept all multicast packet */
+               /* too many multicast addresses or accept all multicast packets */
                 rx_mode = RFAAB | RFAAM;
                 for (i = 0; i < 8; i++)
                         mc_filter[i] = 0xffff;
         } else {
-               /* Accept Broadcast packet, destination address matchs our MAC address,
-                  use Receive Filter to reject unwanted MCAST packet */
+               /* Accept Broadcast packets, destination addresses match our MAC address,
+                  use Receive Filter to reject unwanted MCAST packets */
                 struct dev_mc_list *mclist;
                 rx_mode = RFAAB;
                 for (i = 0; i < 8; i++)
@@ -1208,7 +1220,7 @@ static void set_rx_mode(struct device *net_dev)
 
        /* update Multicast Hash Table in Receive Filter */
         for (i = 0; i < 8; i++) {
-               /* why plus 0x04 ??, I don't know, UNDOCUMENT FEATURE ?? */
+               /* why plus 0x04 ??, That makes the correct value for hash table. */
                 outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
                 outl(mc_filter[i], ioaddr + rfdr);
         }
index 818e193322b8da2fb689f5c2cdb7ed581a53cebc..f52bcc1b4c29196fccce30b5c7f31cf764d622e2 100644 (file)
@@ -92,8 +92,8 @@ enum sis900_transmit_config_register_bits {
 
 /* recevie FIFO thresholds */
 #define RxDRNT_shift     1
-#define RxDRNT_100     24      /* 3/4 FIFO size */
-#define RxDRNT_10              16      /* 1/2 FIFO size */
+#define RxDRNT_100     16      /* 1/2 FIFO size */
+#define RxDRNT_10              24      /* 3/4 FIFO size */
 
 enum sis900_reveive_config_register_bits {
        RxAEP  = 0x80000000, RxARP = 0x40000000, RxATX = 0x10000000,
index c2f27761b365be102bbcf1b653b969408d0c39d5..4d1f812cc45142d7d720158738d1e610971cfb3b 100644 (file)
@@ -2746,9 +2746,7 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                        oi++;
                }
                if (tx > maxtx || rx > maxrx || oi > maxoi) {
-#if 0
                        if (tulip_debug > 1)
-#endif
                                printk(KERN_WARNING "%s: Too much work during an interrupt, "
                                           "csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi);
                        /* Acknowledge all interrupt sources. */
@@ -2768,9 +2766,11 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
        /* check if we card is in suspend mode */
        entry = tp->dirty_rx % RX_RING_SIZE;
        if (tp->rx_skbuff[entry] == NULL) {
-               printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
+               if (tulip_debug > 1)
+                       printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
                if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) {
-                       printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir);
+                       if (tulip_debug > 1)
+                               printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir);
                        outl(tulip_tbl[tp->chip_id].valid_intrs | TimerInt,
                                ioaddr + CSR7);
                        outl(TimerInt, ioaddr + CSR5);
index eff5851acab67e0b76198a70106b22f89be3124b..2c89f66752c22361e544c4c54e77023181e9e94b 100644 (file)
@@ -482,7 +482,11 @@ static void aha1542_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
       }
       
       my_done = SCtmp->scsi_done;
-      if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+      if (SCtmp->host_scribble)
+      {
+       scsi_free(SCtmp->host_scribble, 512);
+       SCtmp->host_scribble = 0;
+      }
       
       /* Fetch the sense data, and tuck it away, in the required slot.  The
         Adaptec automatically fetches it, and there is no guarantee that
index 438adc07a05bb733b4b4b32d738b1edbbcc87fab..4a928c1803f6ab914ac61ea8ebf2dc0d0aab3c20 100644 (file)
@@ -29,7 +29,7 @@ static char ixj_h_rcsid[] = "$Id: ixj.h,v 3.4 1999/12/16 22:18:36 root Exp root
 #include <asm/types.h>
 #endif
 
-#include "ixjuser.h"
+#include <linux/ixjuser.h>
 #include <linux/phonedev.h>
 
 typedef __u16 WORD;
diff --git a/drivers/telephony/ixjuser.h b/drivers/telephony/ixjuser.h
deleted file mode 100644 (file)
index a701953..0000000
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- *    ixjuser.h
- *
- *    User-space include file for the Internet PhoneJACK and
- *    Internet LineJACK Telephony Cards.
- *
- *    (c) Copyright 1999 Quicknet Technologies, Inc.
- *
- *    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.
- *
- * Author:          Ed Okerson, <eokerson@quicknet.net>
- *    
- * Contributors:    Greg Herlein, <gherlein@quicknet.net>
- *                  David W. Erhart, <derhart@quicknet.net>
- *                  John Sellers, <jsellers@quicknet.net>
- *                  Mike Preston, <mpreston@quicknet.net>
- *
- * More information about the hardware related to this driver can be found
- * at our website:    http://www.quicknet.net
- *
- * Fixes:
- */
-
-static char ixjuser_h_rcsid[] = "$Id: ixjuser.h,v 3.4 1999/12/16 22:18:36 root Exp root $";
-
-#include <linux/telephony.h>
-
-/***************************************************************************
-
-  If you use the IXJCTL_TESTRAM command, the card must be power
-  cycled to reset the SRAM values before futher use.
-
-***************************************************************************/
-#define IXJCTL_DSP_RESET               _IO  ('q', 0xC0)
-
-#define IXJCTL_RING         PHONE_RING
-#define IXJCTL_HOOKSTATE    PHONE_HOOKSTATE
-#define IXJCTL_MAXRINGS                        PHONE_MAXRINGS
-#define IXJCTL_RING_CADENCE    PHONE_RING_CADENCE
-#define IXJCTL_RING_START              PHONE_RING_START
-#define IXJCTL_RING_STOP               PHONE_RING_STOP
-
-#define IXJCTL_CARDTYPE                        _IOR ('q', 0xC1, int)
-#define IXJCTL_SERIAL                    _IOR ('q', 0xC2, int)
-#define IXJCTL_DSP_TYPE     _IOR ('q', 0xC3, int)
-#define IXJCTL_DSP_VERSION  _IOR ('q', 0xC4, int)
-#define IXJCTL_DSP_IDLE                        _IO  ('q', 0xC5)
-#define IXJCTL_TESTRAM                 _IO  ('q', 0xC6)
-
-/******************************************************************************
-*
-* This group of IOCTLs deal with the record settings of the DSP
-*
-* The IXJCTL_REC_DEPTH command sets the internal buffer depth of the DSP.
-* Setting a lower depth reduces latency, but increases the demand of the
-* application to service the driver without frame loss.  The DSP has 480
-* bytes of physical buffer memory for the record channel so the true
-* maximum limit is determined by how many frames will fit in the buffer.
-*
-* 1 uncompressed (480 byte) 16-bit linear frame.
-* 2 uncompressed (240 byte) 8-bit A-law/mu-law frames.
-* 15 TrueSpeech 8.5 frames.
-* 20 TrueSpeech 6.3,5.3,4.8 or 4.1 frames.
-*
-* The default in the driver is currently set to 2 frames.
-*
-* The IXJCTL_REC_VOLUME and IXJCTL_PLAY_VOLUME commands both use a Q8
-* number as a parameter, 0x100 scales the signal by 1.0, 0x200 scales the
-* signal by 2.0, 0x80 scales the signal by 0.5.  No protection is given
-* against over-scaling, if the multiplication factor times the input
-* signal exceeds 16 bits, overflow distortion will occur.  The default
-* setting is 0x100 (1.0).
-*
-* The IXJCTL_REC_LEVEL returns the average signal level (not r.m.s.) on
-* the most recently recorded frame as a 16 bit value.
-******************************************************************************/
-
-#define IXJCTL_REC_CODEC                PHONE_REC_CODEC
-#define IXJCTL_REC_START                PHONE_REC_START
-#define IXJCTL_REC_STOP                 PHONE_REC_STOP
-#define IXJCTL_REC_DEPTH               PHONE_REC_DEPTH
-#define IXJCTL_FRAME                   PHONE_FRAME
-#define IXJCTL_REC_VOLUME              PHONE_REC_VOLUME
-#define IXJCTL_REC_LEVEL               PHONE_REC_LEVEL
-
-typedef enum {
-       f300_640 = 4, f300_500, f1100, f350, f400, f480, f440, f620, f20_50,
-       f133_200, f300, f300_420, f330, f300_425, f330_440, f340, f350_400,
-       f350_440, f350_450, f360, f380_420, f392, f400_425, f400_440, f400_450,
-       f420, f425, f425_450, f425_475, f435, f440_450, f440_480, f445, f450,
-       f452, f475, f480_620, f494, f500, f520, f523, f525, f540_660, f587,
-       f590, f600, f660, f700, f740, f750, f750_1450, f770, f800, f816, f850,
-       f857_1645, f900, f900_1300, f935_1215, f941_1477, f942, f950, f950_1400,
-       f975, f1000, f1020, f1050, f1100_1750, f1140, f1200, f1209, f1330, f1336,
-       lf1366, f1380, f1400, f1477, f1600, f1633_1638, f1800, f1860
-} IXJ_FILTER_FREQ;
-
-typedef struct {
-       unsigned int filter;
-       IXJ_FILTER_FREQ freq;
-       char enable;
-} IXJ_FILTER;
-
-#define IXJCTL_SET_FILTER              _IOW ('q', 0xC7, IXJ_FILTER *)
-#define IXJCTL_GET_FILTER_HIST         _IOW ('q', 0xC8, int)
-/******************************************************************************
-*
-* This IOCTL allows you to reassign values in the tone index table.  The
-* tone table has 32 entries (0 - 31), but the driver only allows entries
-* 13 - 27 to be modified, entry 0 is reserved for silence and 1 - 12 are
-* the standard DTMF digits and 28 - 31 are the DTMF tones for A, B, C & D.
-* The positions used internally for Call Progress Tones are as follows:
-*    Dial Tone   - 25
-*    Ring Back   - 26
-*    Busy Signal - 27
-*
-* The freq values are calculated as:
-* freq = cos(2 * PI * frequency / 8000)
-*
-* The most commonly needed values are already calculated and listed in the
-* enum IXJ_TONE_FREQ.  Each tone index can have two frequencies with
-* different gains, if you are only using a single frequency set the unused
-* one to 0.
-*
-* The gain values range from 0 to 15 indicating +6dB to -24dB in 2dB
-* increments.
-*
-******************************************************************************/
-
-typedef enum {
-       hz20 = 0x7ffa,
-       hz50 = 0x7fe5,
-       hz133 = 0x7f4c,
-       hz200 = 0x7e6b,
-       hz261 = 0x7d50,         /* .63 C1  */
-       hz277 = 0x7cfa,         /* .18 CS1 */
-       hz293 = 0x7c9f,         /* .66 D1  */
-       hz300 = 0x7c75,
-       hz311 = 0x7c32,         /* .13 DS1 */
-       hz329 = 0x7bbf,         /* .63 E1  */
-       hz330 = 0x7bb8,
-       hz340 = 0x7b75,
-       hz349 = 0x7b37,         /* .23 F1  */
-       hz350 = 0x7b30,
-       hz360 = 0x7ae9,
-       hz369 = 0x7aa8,         /* .99 FS1 */
-       hz380 = 0x7a56,
-       hz392 = 0x79fa,         /* .00 G1  */
-       hz400 = 0x79bb,
-       hz415 = 0x7941,         /* .30 GS1 */
-       hz420 = 0x7918,
-       hz425 = 0x78ee,
-       hz435 = 0x7899,
-       hz440 = 0x786d,         /* .00 A1  */
-       hz445 = 0x7842,
-       hz450 = 0x7815,
-       hz452 = 0x7803,
-       hz466 = 0x7784,         /* .16 AS1 */
-       hz475 = 0x7731,
-       hz480 = 0x7701,
-       hz493 = 0x7685,         /* .88 B1  */
-       hz494 = 0x767b,
-       hz500 = 0x7640,
-       hz520 = 0x7578,
-       hz523 = 0x7559,         /* .25 C2  */
-       hz525 = 0x7544,
-       hz540 = 0x74a7,
-       hz554 = 0x7411,         /* .37 CS2 */
-       hz587 = 0x72a1,         /* .33 D2  */
-       hz590 = 0x727f,
-       hz600 = 0x720b,
-       hz620 = 0x711e,
-       hz622 = 0x7106,         /* .25 DS2 */
-       hz659 = 0x6f3b,         /* .26 E2  */
-       hz660 = 0x6f2e,
-       hz698 = 0x6d3d,         /* .46 F2  */
-       hz700 = 0x6d22,
-       hz739 = 0x6b09,         /* .99 FS2 */
-       hz740 = 0x6afa,
-       hz750 = 0x6a6c,
-       hz770 = 0x694b,
-       hz783 = 0x688b,         /* .99 G2  */
-       hz800 = 0x678d,
-       hz816 = 0x6698,
-       hz830 = 0x65bf,         /* .61 GS2 */
-       hz850 = 0x6484,
-       hz857 = 0x6414,
-       hz880 = 0x629f,         /* .00 A2  */
-       hz900 = 0x6154,
-       hz932 = 0x5f35,         /* .33 AS2 */
-       hz935 = 0x5f01,
-       hz941 = 0x5e9a,
-       hz942 = 0x5e88,
-       hz950 = 0x5dfd,
-       hz975 = 0x5c44,
-       hz1000 = 0x5a81,
-       hz1020 = 0x5912,
-       hz1050 = 0x56e2,
-       hz1100 = 0x5320,
-       hz1140 = 0x5007,
-       hz1200 = 0x4b3b,
-       hz1209 = 0x4a80,
-       hz1215 = 0x4a02,
-       hz1250 = 0x471c,
-       hz1300 = 0x42e0,
-       hz1330 = 0x4049,
-       hz1336 = 0x3fc4,
-       hz1366 = 0x3d22,
-       hz1380 = 0x3be4,
-       hz1400 = 0x3a1b,
-       hz1450 = 0x3596,
-       hz1477 = 0x331c,
-       hz1500 = 0x30fb,
-       hz1600 = 0x278d,
-       hz1633 = 0x2462,
-       hz1638 = 0x23e7,
-       hz1645 = 0x233a,
-       hz1750 = 0x18f8,
-       hz1800 = 0x1405,
-       hz1860 = 0xe0b,
-       hz2100 = 0xf5f6,
-       hz2450 = 0xd3b3
-} IXJ_FREQ;
-
-typedef enum {
-       C1 = hz261,
-       CS1 = hz277,
-       D1 = hz293,
-       DS1 = hz311,
-       E1 = hz329,
-       F1 = hz349,
-       FS1 = hz369,
-       G1 = hz392,
-       GS1 = hz415,
-       A1 = hz440,
-       AS1 = hz466,
-       B1 = hz493,
-       C2 = hz523,
-       CS2 = hz554,
-       D2 = hz587,
-       DS2 = hz622,
-       E2 = hz659,
-       F2 = hz698,
-       FS2 = hz739,
-       G2 = hz783,
-       GS2 = hz830,
-       A2 = hz880,
-       AS2 = hz932,
-} IXJ_NOTE;
-
-typedef struct {
-       int tone_index;
-       int freq0;
-       int gain0;
-       int freq1;
-       int gain1;
-} IXJ_TONE;
-
-#define IXJCTL_INIT_TONE               _IOW ('q', 0xC9, IXJ_TONE *)
-
-/******************************************************************************
-*
-* The IXJCTL_TONE_CADENCE ioctl defines tone sequences used for various
-* Call Progress Tones (CPT).  This is accomplished by setting up an array of
-* IXJ_CADENCE_ELEMENT structures that sequentially define the states of
-* the tone sequence.  The tone_on_time and tone_off time are in
-* 250 microsecond intervals.  A pointer to this array is passed to the
-* driver as the ce element of an IXJ_CADENCE structure.  The elements_used
-* must be set to the number of IXJ_CADENCE_ELEMENTS in the array.  The
-* termination variable defines what to do at the end of a cadence, the
-* options are to play the cadence once and stop, to repeat the last
-* element of the cadence indefinatly, or to repeat the entire cadence
-* indefinatly.  The ce variable is a pointer to the array of IXJ_TONE
-* structures.  If the freq0 variable is non-zero, the tone table contents
-* for the tone_index are updated to the frequencies and gains defined.  It
-* should be noted that DTMF tones cannot be reassigned, so if DTMF tone
-* table indexs are used in a cadence the frequency and gain variables will
-* be ignored.
-*
-* If the array elements contain frequency parameters the driver will
-* initialize the needed tone table elements and begin playing the tone,
-* there is no preset limit on the number of elements in the cadence.  If
-* there is more than one frequency used in the cadence, sequential elements
-* of different frequencies MUST use different tone table indexes.  Only one
-* cadence can be played at a time.  It is possible to build complex
-* cadences with multiple frequencies using 2 tone table indexes by
-* alternating between them.
-*
-******************************************************************************/
-
-typedef struct {
-       int index;
-       int tone_on_time;
-       int tone_off_time;
-       int freq0;
-       int gain0;
-       int freq1;
-       int gain1;
-} IXJ_CADENCE_ELEMENT;
-
-typedef enum {
-       PLAY_ONCE,
-       REPEAT_LAST_ELEMENT,
-       REPEAT_ALL
-} IXJ_CADENCE_TERM;
-
-typedef struct {
-       int elements_used;
-       IXJ_CADENCE_TERM termination;
-       IXJ_CADENCE_ELEMENT *ce;
-} IXJ_CADENCE;
-
-#define IXJCTL_TONE_CADENCE            _IOW ('q', 0xCA, IXJ_CADENCE *)
-/******************************************************************************
-*
-* This group of IOCTLs deal with the playback settings of the DSP
-*
-******************************************************************************/
-
-#define IXJCTL_PLAY_CODEC               PHONE_PLAY_CODEC
-#define IXJCTL_PLAY_START               PHONE_PLAY_START
-#define IXJCTL_PLAY_STOP                PHONE_PLAY_STOP
-#define IXJCTL_PLAY_DEPTH              PHONE_PLAY_DEPTH
-#define IXJCTL_PLAY_VOLUME             PHONE_PLAY_VOLUME
-#define IXJCTL_PLAY_LEVEL              PHONE_PLAY_LEVEL
-
-/******************************************************************************
-*
-* This group of IOCTLs deal with the Acoustic Echo Cancellation settings
-* of the DSP
-*
-* Issueing the IXJCTL_AEC_START command with a value of AEC_OFF has the
-* same effect as IXJCTL_AEC_STOP.  This is to simplify slider bar
-* controls.  IXJCTL_AEC_GET_LEVEL returns the current setting of the AEC.
-******************************************************************************/
-#define IXJCTL_AEC_START               _IOW ('q', 0xCB, int)
-#define IXJCTL_AEC_STOP                        _IO  ('q', 0xCC)
-#define IXJCTL_AEC_GET_LEVEL           _IO  ('q', 0xCD)
-
-#define AEC_OFF   0
-#define AEC_LOW   1
-#define AEC_MED   2
-#define AEC_HIGH  3
-/******************************************************************************
-*
-* Call Progress Tones, DTMF, etc.
-* IXJCTL_DTMF_OOB determines if dtmf signaling is sent as Out-Of-Band
-* only.  If you pass a 1, dtmf is suppressed from the audio stream.
-* Tone on and off times are in 250 microsecond intervals so
-* ioctl(ixj1, IXJCTL_SET_TONE_ON_TIME, 360);
-* will set the tone on time of board ixj1 to 360 * 250us = 90ms
-* the default values of tone on and off times is 840 or 210ms
-******************************************************************************/
-
-#define IXJCTL_DTMF_READY              PHONE_DTMF_READY
-#define IXJCTL_GET_DTMF                 PHONE_GET_DTMF
-#define IXJCTL_GET_DTMF_ASCII           PHONE_GET_DTMF_ASCII
-#define IXJCTL_DTMF_OOB                        PHONE_DTMF_OOB
-#define IXJCTL_EXCEPTION               PHONE_EXCEPTION
-#define IXJCTL_PLAY_TONE               PHONE_PLAY_TONE
-#define IXJCTL_SET_TONE_ON_TIME                PHONE_SET_TONE_ON_TIME
-#define IXJCTL_SET_TONE_OFF_TIME       PHONE_SET_TONE_OFF_TIME
-#define IXJCTL_GET_TONE_ON_TIME                PHONE_GET_TONE_ON_TIME
-#define IXJCTL_GET_TONE_OFF_TIME       PHONE_GET_TONE_OFF_TIME
-#define IXJCTL_GET_TONE_STATE          PHONE_GET_TONE_STATE
-#define IXJCTL_BUSY                    PHONE_BUSY
-#define IXJCTL_RINGBACK                        PHONE_RINGBACK
-#define IXJCTL_DIALTONE                        PHONE_DIALTONE
-#define IXJCTL_CPT_STOP                        PHONE_CPT_STOP
-
-/******************************************************************************
-* LineJack specific IOCTLs
-*
-* The lsb 4 bits of the LED argument represent the state of each of the 4
-* LED's on the LineJack
-******************************************************************************/
-
-#define IXJCTL_SET_LED                 _IOW ('q', 0xCE, int)
-#define IXJCTL_MIXER                   _IOW ('q', 0xCF, int)
-
-/******************************************************************************
-* 
-* The master volume controls use attenuation with 32 levels from 0 to -62dB
-* with steps of 2dB each, the defines should be OR'ed together then sent
-* as the parameter to the mixer command to change the mixer settings.
-* 
-******************************************************************************/
-#define MIXER_MASTER_L         0x0100
-#define MIXER_MASTER_R         0x0200
-#define ATT00DB                        0x00
-#define ATT02DB                        0x01
-#define ATT04DB                        0x02
-#define ATT06DB                        0x03
-#define ATT08DB                        0x04
-#define ATT10DB                        0x05
-#define ATT12DB                        0x06
-#define ATT14DB                        0x07
-#define ATT16DB                        0x08
-#define ATT18DB                        0x09
-#define ATT20DB                        0x0A
-#define ATT22DB                        0x0B
-#define ATT24DB                        0x0C
-#define ATT26DB                        0x0D
-#define ATT28DB                        0x0E
-#define ATT30DB                        0x0F
-#define ATT32DB                        0x10
-#define ATT34DB                        0x11
-#define ATT36DB                        0x12
-#define ATT38DB                        0x13
-#define ATT40DB                        0x14
-#define ATT42DB                        0x15
-#define ATT44DB                        0x16
-#define ATT46DB                        0x17
-#define ATT48DB                        0x18
-#define ATT50DB                        0x19
-#define ATT52DB                        0x1A
-#define ATT54DB                        0x1B
-#define ATT56DB                        0x1C
-#define ATT58DB                        0x1D
-#define ATT60DB                        0x1E
-#define ATT62DB                        0x1F
-#define MASTER_MUTE            0x80
-
-/******************************************************************************
-* 
-* The input volume controls use gain with 32 levels from +12dB to -50dB
-* with steps of 2dB each, the defines should be OR'ed together then sent
-* as the parameter to the mixer command to change the mixer settings.
-* 
-******************************************************************************/
-#define MIXER_PORT_CD_L                0x0600
-#define MIXER_PORT_CD_R                0x0700
-#define MIXER_PORT_LINE_IN_L   0x0800
-#define MIXER_PORT_LINE_IN_R   0x0900
-#define MIXER_PORT_POTS_REC    0x0C00
-#define MIXER_PORT_MIC         0x0E00
-
-#define GAIN12DB               0x00
-#define GAIN10DB               0x01
-#define GAIN08DB               0x02
-#define GAIN06DB               0x03
-#define GAIN04DB               0x04
-#define GAIN02DB               0x05
-#define GAIN00DB               0x06
-#define GAIN_02DB              0x07
-#define GAIN_04DB              0x08
-#define GAIN_06DB              0x09
-#define GAIN_08DB              0x0A
-#define GAIN_10DB              0x0B
-#define GAIN_12DB              0x0C
-#define GAIN_14DB              0x0D
-#define GAIN_16DB              0x0E
-#define GAIN_18DB              0x0F
-#define GAIN_20DB              0x10
-#define GAIN_22DB              0x11
-#define GAIN_24DB              0x12
-#define GAIN_26DB              0x13
-#define GAIN_28DB              0x14
-#define GAIN_30DB              0x15
-#define GAIN_32DB              0x16
-#define GAIN_34DB              0x17
-#define GAIN_36DB              0x18
-#define GAIN_38DB              0x19
-#define GAIN_40DB              0x1A
-#define GAIN_42DB              0x1B
-#define GAIN_44DB              0x1C
-#define GAIN_46DB              0x1D
-#define GAIN_48DB              0x1E
-#define GAIN_50DB              0x1F
-#define INPUT_MUTE             0x80
-
-/******************************************************************************
-* 
-* The POTS volume control use attenuation with 8 levels from 0dB to -28dB
-* with steps of 4dB each, the defines should be OR'ed together then sent
-* as the parameter to the mixer command to change the mixer settings.
-* 
-******************************************************************************/
-#define MIXER_PORT_POTS_PLAY   0x0F00
-
-#define POTS_ATT_00DB          0x00
-#define POTS_ATT_04DB          0x01
-#define POTS_ATT_08DB          0x02
-#define POTS_ATT_12DB          0x03
-#define POTS_ATT_16DB          0x04
-#define POTS_ATT_20DB          0x05
-#define POTS_ATT_24DB          0x06
-#define POTS_ATT_28DB          0x07
-#define POTS_MUTE              0x80
-
-/******************************************************************************
-* 
-* The DAA controls the interface to the PSTN port.  The driver loads the
-* US coefficients by default, so if you live in a different country you
-* need to load the set for your countries phone system.
-* 
-******************************************************************************/
-#define IXJCTL_DAA_COEFF_SET           _IOW ('q', 0xD0, int)
-
-#define DAA_US                 1       //PITA 8kHz
-#define DAA_UK                 2       //ISAR34 8kHz
-#define DAA_FRANCE     3       //
-#define DAA_GERMANY    4
-#define DAA_AUSTRALIA  5
-#define DAA_JAPAN      6
-
-/******************************************************************************
-* 
-* Use IXJCTL_PORT to set or query the port the card is set to.  If the
-* argument is set to PORT_QUERY, the return value of the ioctl will
-* indicate which port is currently in use, otherwise it will change the
-* port.
-* 
-******************************************************************************/
-#define IXJCTL_PORT                    _IOW ('q', 0xD1, int)
-
-#define PORT_QUERY     0
-#define PORT_POTS      1
-#define PORT_PSTN      2
-#define PORT_SPEAKER   3
-#define PORT_HANDSET   4
-
-#define IXJCTL_PSTN_SET_STATE          PHONE_PSTN_SET_STATE
-#define IXJCTL_PSTN_GET_STATE          PHONE_PSTN_GET_STATE
-
-#define PSTN_ON_HOOK   0
-#define PSTN_RINGING   1
-#define PSTN_OFF_HOOK  2
-#define PSTN_PULSE_DIAL        3
-
-/******************************************************************************
-* 
-* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR), 
-* and the transmit gain (AGX).  OR together the components and pass them
-* as the parameter to IXJCTL_DAA_AGAIN.  The default setting is both at 0dB.
-* 
-******************************************************************************/
-#define IXJCTL_DAA_AGAIN               _IOW ('q', 0xD2, int)
-
-#define AGRR00DB       0x00    // Analog gain in receive direction 0dB
-#define AGRR3_5DB      0x10    // Analog gain in receive direction 3.5dB
-#define AGRR06DB       0x30    // Analog gain in receive direction 6dB
-
-#define AGX00DB                0x00    // Analog gain in transmit direction 0dB
-#define AGX_6DB                0x04    // Analog gain in transmit direction -6dB
-#define AGX3_5DB       0x08    // Analog gain in transmit direction 3.5dB
-#define AGX_2_5B       0x0C    // Analog gain in transmit direction -2.5dB
-
-#define IXJCTL_PSTN_LINETEST           _IO  ('q', 0xD3)
-
-typedef struct {
-       char month[3];
-       char day[3];
-       char hour[3];
-       char min[3];
-       int numlen;
-       char number[11];
-       int namelen;
-       char name[80];
-} IXJ_CID;
-
-#define IXJCTL_CID                     _IOR ('q', 0xD4, IXJ_CID *)
-/******************************************************************************
-* 
-* The wink duration is tunable with this ioctl.  The default wink duration  
-* is 320ms.  You do not need to use this ioctl if you do not require a
-* different wink duration.
-* 
-******************************************************************************/
-#define IXJCTL_WINK_DURATION           PHONE_WINK_DURATION
-
-/******************************************************************************
-* 
-* This ioctl will connect the POTS port to the PSTN port on the LineJACK
-* In order for this to work properly the port selection should be set to
-* the PSTN port with IXJCTL_PORT prior to calling this ioctl.  This will
-* enable conference calls between PSTN callers and network callers.
-* Passing a 1 to this ioctl enables the POTS<->PSTN connection while
-* passing a 0 turns it back off.
-* 
-******************************************************************************/
-#define IXJCTL_POTS_PSTN               _IOW ('q', 0xD5, int)
-
-/******************************************************************************
-*
-* IOCTLs added by request.
-*
-* IXJCTL_HZ sets the value your Linux kernel uses for HZ as defined in
-*           /usr/include/asm/param.h, this determines the fundamental
-*           frequency of the clock ticks on your Linux system.  The kernel
-*           must be rebuilt if you change this value, also all modules you
-*           use (except this one) must be recompiled.  The default value
-*           is 100, and you only need to use this IOCTL if you use some
-*           other value.
-*
-*
-* IXJCTL_RATE sets the number of times per second that the driver polls
-*             the DSP.  This value cannot be larger than HZ.  By
-*             increasing both of these values, you may be able to reduce
-*             latency because the max hang time that can exist between the
-*             driver and the DSP will be reduced.
-*
-******************************************************************************/
-
-#define IXJCTL_HZ                       _IOW ('q', 0xE0, int)
-#define IXJCTL_RATE                     _IOW ('q', 0xE1, int)
-#define IXJCTL_FRAMES_READ             _IOR ('q', 0xE2, unsigned long)
-#define IXJCTL_FRAMES_WRITTEN          _IOR ('q', 0xE3, unsigned long)
-#define IXJCTL_READ_WAIT               _IOR ('q', 0xE4, unsigned long)
-#define IXJCTL_WRITE_WAIT              _IOR ('q', 0xE5, unsigned long)
-#define IXJCTL_DRYBUFFER_READ          _IOR ('q', 0xE6, unsigned long)
-#define IXJCTL_DRYBUFFER_CLEAR         _IO  ('q', 0xE7)
-
-/******************************************************************************
-*
-* The intercom IOCTL's short the output from one card to the input of the
-* other and vice versa (actually done in the DSP read function).  It is only
-* necessary to execute the IOCTL on one card, but it is necessary to have
-* both devices open to be able to detect hook switch changes.  The record
-* codec and rate of each card must match the playback codec and rate of
-* the other card for this to work properly.
-*
-******************************************************************************/
-
-#define IXJCTL_INTERCOM_START          _IOW ('q', 0xFD, int)
-#define IXJCTL_INTERCOM_STOP           _IOW ('q', 0xFE, int)
index 0d216ae8bcdca9ecca0b5a19534cf65cb639fcfc..6027c3b84c1c0a5e964fe790b09fe60aa4823e4e 100644 (file)
@@ -134,10 +134,16 @@ void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo)
        if ((inode->i_size)&&(inode->i_blksize)) {
                inode->i_blocks = (inode->i_size-1)/(inode->i_blksize)+1;
        }
-       /* TODO: times? I'm not sure... */
-       NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
-       NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
-       NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+
+       inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
+                                          le16_to_cpu(nwi->modifyDate));
+       inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
+                                          le16_to_cpu(nwi->creationDate));
+       inode->i_atime = ncp_date_dos2unix(0, le16_to_cpu(nwi->lastAccessDate));
+
+       NCP_FINFO(inode)->DosDirNum = nwi->DosDirNum;
+       NCP_FINFO(inode)->dirEntNum = nwi->dirEntNum;
+       NCP_FINFO(inode)->volNumber = nwi->volNumber;
 }
 
 /*
index 64467ba083661c337b0131c8254954fba146bc3a..70b04c14db31129ca24371a7497cd4183d281e8f 100644 (file)
@@ -661,7 +661,7 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen,
                op = outname;
                if (nls) {
                        for (i = 0, ip = name, op = outname, *outlen = 0;
-                            i < len && *outlen <= 260; i++, *outlen += 1)
+                            i < len && *outlen <= 260; *outlen += 1)
                        {
                                if (escape && (*ip == ':')) {
                                        if (i > len - 4) return -EINVAL;
@@ -673,10 +673,12 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen,
                                        *op++ = (c1 << 4) + (c2 >> 2);
                                        *op++ = ((c2 & 0x3) << 6) + c3;
                                        ip += 4;
+                                       i += 4;
                                } else {
                                        *op++ = nls->charset2uni[*ip].uni1;
                                        *op++ = nls->charset2uni[*ip].uni2;
                                        ip++;
+                                       i++;
                                }
                        }
                } else {
diff --git a/include/linux/ixjuser.h b/include/linux/ixjuser.h
new file mode 100644 (file)
index 0000000..a701953
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ *    ixjuser.h
+ *
+ *    User-space include file for the Internet PhoneJACK and
+ *    Internet LineJACK Telephony Cards.
+ *
+ *    (c) Copyright 1999 Quicknet Technologies, Inc.
+ *
+ *    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.
+ *
+ * Author:          Ed Okerson, <eokerson@quicknet.net>
+ *    
+ * Contributors:    Greg Herlein, <gherlein@quicknet.net>
+ *                  David W. Erhart, <derhart@quicknet.net>
+ *                  John Sellers, <jsellers@quicknet.net>
+ *                  Mike Preston, <mpreston@quicknet.net>
+ *
+ * More information about the hardware related to this driver can be found
+ * at our website:    http://www.quicknet.net
+ *
+ * Fixes:
+ */
+
+static char ixjuser_h_rcsid[] = "$Id: ixjuser.h,v 3.4 1999/12/16 22:18:36 root Exp root $";
+
+#include <linux/telephony.h>
+
+/***************************************************************************
+
+  If you use the IXJCTL_TESTRAM command, the card must be power
+  cycled to reset the SRAM values before futher use.
+
+***************************************************************************/
+#define IXJCTL_DSP_RESET               _IO  ('q', 0xC0)
+
+#define IXJCTL_RING         PHONE_RING
+#define IXJCTL_HOOKSTATE    PHONE_HOOKSTATE
+#define IXJCTL_MAXRINGS                        PHONE_MAXRINGS
+#define IXJCTL_RING_CADENCE    PHONE_RING_CADENCE
+#define IXJCTL_RING_START              PHONE_RING_START
+#define IXJCTL_RING_STOP               PHONE_RING_STOP
+
+#define IXJCTL_CARDTYPE                        _IOR ('q', 0xC1, int)
+#define IXJCTL_SERIAL                    _IOR ('q', 0xC2, int)
+#define IXJCTL_DSP_TYPE     _IOR ('q', 0xC3, int)
+#define IXJCTL_DSP_VERSION  _IOR ('q', 0xC4, int)
+#define IXJCTL_DSP_IDLE                        _IO  ('q', 0xC5)
+#define IXJCTL_TESTRAM                 _IO  ('q', 0xC6)
+
+/******************************************************************************
+*
+* This group of IOCTLs deal with the record settings of the DSP
+*
+* The IXJCTL_REC_DEPTH command sets the internal buffer depth of the DSP.
+* Setting a lower depth reduces latency, but increases the demand of the
+* application to service the driver without frame loss.  The DSP has 480
+* bytes of physical buffer memory for the record channel so the true
+* maximum limit is determined by how many frames will fit in the buffer.
+*
+* 1 uncompressed (480 byte) 16-bit linear frame.
+* 2 uncompressed (240 byte) 8-bit A-law/mu-law frames.
+* 15 TrueSpeech 8.5 frames.
+* 20 TrueSpeech 6.3,5.3,4.8 or 4.1 frames.
+*
+* The default in the driver is currently set to 2 frames.
+*
+* The IXJCTL_REC_VOLUME and IXJCTL_PLAY_VOLUME commands both use a Q8
+* number as a parameter, 0x100 scales the signal by 1.0, 0x200 scales the
+* signal by 2.0, 0x80 scales the signal by 0.5.  No protection is given
+* against over-scaling, if the multiplication factor times the input
+* signal exceeds 16 bits, overflow distortion will occur.  The default
+* setting is 0x100 (1.0).
+*
+* The IXJCTL_REC_LEVEL returns the average signal level (not r.m.s.) on
+* the most recently recorded frame as a 16 bit value.
+******************************************************************************/
+
+#define IXJCTL_REC_CODEC                PHONE_REC_CODEC
+#define IXJCTL_REC_START                PHONE_REC_START
+#define IXJCTL_REC_STOP                 PHONE_REC_STOP
+#define IXJCTL_REC_DEPTH               PHONE_REC_DEPTH
+#define IXJCTL_FRAME                   PHONE_FRAME
+#define IXJCTL_REC_VOLUME              PHONE_REC_VOLUME
+#define IXJCTL_REC_LEVEL               PHONE_REC_LEVEL
+
+typedef enum {
+       f300_640 = 4, f300_500, f1100, f350, f400, f480, f440, f620, f20_50,
+       f133_200, f300, f300_420, f330, f300_425, f330_440, f340, f350_400,
+       f350_440, f350_450, f360, f380_420, f392, f400_425, f400_440, f400_450,
+       f420, f425, f425_450, f425_475, f435, f440_450, f440_480, f445, f450,
+       f452, f475, f480_620, f494, f500, f520, f523, f525, f540_660, f587,
+       f590, f600, f660, f700, f740, f750, f750_1450, f770, f800, f816, f850,
+       f857_1645, f900, f900_1300, f935_1215, f941_1477, f942, f950, f950_1400,
+       f975, f1000, f1020, f1050, f1100_1750, f1140, f1200, f1209, f1330, f1336,
+       lf1366, f1380, f1400, f1477, f1600, f1633_1638, f1800, f1860
+} IXJ_FILTER_FREQ;
+
+typedef struct {
+       unsigned int filter;
+       IXJ_FILTER_FREQ freq;
+       char enable;
+} IXJ_FILTER;
+
+#define IXJCTL_SET_FILTER              _IOW ('q', 0xC7, IXJ_FILTER *)
+#define IXJCTL_GET_FILTER_HIST         _IOW ('q', 0xC8, int)
+/******************************************************************************
+*
+* This IOCTL allows you to reassign values in the tone index table.  The
+* tone table has 32 entries (0 - 31), but the driver only allows entries
+* 13 - 27 to be modified, entry 0 is reserved for silence and 1 - 12 are
+* the standard DTMF digits and 28 - 31 are the DTMF tones for A, B, C & D.
+* The positions used internally for Call Progress Tones are as follows:
+*    Dial Tone   - 25
+*    Ring Back   - 26
+*    Busy Signal - 27
+*
+* The freq values are calculated as:
+* freq = cos(2 * PI * frequency / 8000)
+*
+* The most commonly needed values are already calculated and listed in the
+* enum IXJ_TONE_FREQ.  Each tone index can have two frequencies with
+* different gains, if you are only using a single frequency set the unused
+* one to 0.
+*
+* The gain values range from 0 to 15 indicating +6dB to -24dB in 2dB
+* increments.
+*
+******************************************************************************/
+
+typedef enum {
+       hz20 = 0x7ffa,
+       hz50 = 0x7fe5,
+       hz133 = 0x7f4c,
+       hz200 = 0x7e6b,
+       hz261 = 0x7d50,         /* .63 C1  */
+       hz277 = 0x7cfa,         /* .18 CS1 */
+       hz293 = 0x7c9f,         /* .66 D1  */
+       hz300 = 0x7c75,
+       hz311 = 0x7c32,         /* .13 DS1 */
+       hz329 = 0x7bbf,         /* .63 E1  */
+       hz330 = 0x7bb8,
+       hz340 = 0x7b75,
+       hz349 = 0x7b37,         /* .23 F1  */
+       hz350 = 0x7b30,
+       hz360 = 0x7ae9,
+       hz369 = 0x7aa8,         /* .99 FS1 */
+       hz380 = 0x7a56,
+       hz392 = 0x79fa,         /* .00 G1  */
+       hz400 = 0x79bb,
+       hz415 = 0x7941,         /* .30 GS1 */
+       hz420 = 0x7918,
+       hz425 = 0x78ee,
+       hz435 = 0x7899,
+       hz440 = 0x786d,         /* .00 A1  */
+       hz445 = 0x7842,
+       hz450 = 0x7815,
+       hz452 = 0x7803,
+       hz466 = 0x7784,         /* .16 AS1 */
+       hz475 = 0x7731,
+       hz480 = 0x7701,
+       hz493 = 0x7685,         /* .88 B1  */
+       hz494 = 0x767b,
+       hz500 = 0x7640,
+       hz520 = 0x7578,
+       hz523 = 0x7559,         /* .25 C2  */
+       hz525 = 0x7544,
+       hz540 = 0x74a7,
+       hz554 = 0x7411,         /* .37 CS2 */
+       hz587 = 0x72a1,         /* .33 D2  */
+       hz590 = 0x727f,
+       hz600 = 0x720b,
+       hz620 = 0x711e,
+       hz622 = 0x7106,         /* .25 DS2 */
+       hz659 = 0x6f3b,         /* .26 E2  */
+       hz660 = 0x6f2e,
+       hz698 = 0x6d3d,         /* .46 F2  */
+       hz700 = 0x6d22,
+       hz739 = 0x6b09,         /* .99 FS2 */
+       hz740 = 0x6afa,
+       hz750 = 0x6a6c,
+       hz770 = 0x694b,
+       hz783 = 0x688b,         /* .99 G2  */
+       hz800 = 0x678d,
+       hz816 = 0x6698,
+       hz830 = 0x65bf,         /* .61 GS2 */
+       hz850 = 0x6484,
+       hz857 = 0x6414,
+       hz880 = 0x629f,         /* .00 A2  */
+       hz900 = 0x6154,
+       hz932 = 0x5f35,         /* .33 AS2 */
+       hz935 = 0x5f01,
+       hz941 = 0x5e9a,
+       hz942 = 0x5e88,
+       hz950 = 0x5dfd,
+       hz975 = 0x5c44,
+       hz1000 = 0x5a81,
+       hz1020 = 0x5912,
+       hz1050 = 0x56e2,
+       hz1100 = 0x5320,
+       hz1140 = 0x5007,
+       hz1200 = 0x4b3b,
+       hz1209 = 0x4a80,
+       hz1215 = 0x4a02,
+       hz1250 = 0x471c,
+       hz1300 = 0x42e0,
+       hz1330 = 0x4049,
+       hz1336 = 0x3fc4,
+       hz1366 = 0x3d22,
+       hz1380 = 0x3be4,
+       hz1400 = 0x3a1b,
+       hz1450 = 0x3596,
+       hz1477 = 0x331c,
+       hz1500 = 0x30fb,
+       hz1600 = 0x278d,
+       hz1633 = 0x2462,
+       hz1638 = 0x23e7,
+       hz1645 = 0x233a,
+       hz1750 = 0x18f8,
+       hz1800 = 0x1405,
+       hz1860 = 0xe0b,
+       hz2100 = 0xf5f6,
+       hz2450 = 0xd3b3
+} IXJ_FREQ;
+
+typedef enum {
+       C1 = hz261,
+       CS1 = hz277,
+       D1 = hz293,
+       DS1 = hz311,
+       E1 = hz329,
+       F1 = hz349,
+       FS1 = hz369,
+       G1 = hz392,
+       GS1 = hz415,
+       A1 = hz440,
+       AS1 = hz466,
+       B1 = hz493,
+       C2 = hz523,
+       CS2 = hz554,
+       D2 = hz587,
+       DS2 = hz622,
+       E2 = hz659,
+       F2 = hz698,
+       FS2 = hz739,
+       G2 = hz783,
+       GS2 = hz830,
+       A2 = hz880,
+       AS2 = hz932,
+} IXJ_NOTE;
+
+typedef struct {
+       int tone_index;
+       int freq0;
+       int gain0;
+       int freq1;
+       int gain1;
+} IXJ_TONE;
+
+#define IXJCTL_INIT_TONE               _IOW ('q', 0xC9, IXJ_TONE *)
+
+/******************************************************************************
+*
+* The IXJCTL_TONE_CADENCE ioctl defines tone sequences used for various
+* Call Progress Tones (CPT).  This is accomplished by setting up an array of
+* IXJ_CADENCE_ELEMENT structures that sequentially define the states of
+* the tone sequence.  The tone_on_time and tone_off time are in
+* 250 microsecond intervals.  A pointer to this array is passed to the
+* driver as the ce element of an IXJ_CADENCE structure.  The elements_used
+* must be set to the number of IXJ_CADENCE_ELEMENTS in the array.  The
+* termination variable defines what to do at the end of a cadence, the
+* options are to play the cadence once and stop, to repeat the last
+* element of the cadence indefinatly, or to repeat the entire cadence
+* indefinatly.  The ce variable is a pointer to the array of IXJ_TONE
+* structures.  If the freq0 variable is non-zero, the tone table contents
+* for the tone_index are updated to the frequencies and gains defined.  It
+* should be noted that DTMF tones cannot be reassigned, so if DTMF tone
+* table indexs are used in a cadence the frequency and gain variables will
+* be ignored.
+*
+* If the array elements contain frequency parameters the driver will
+* initialize the needed tone table elements and begin playing the tone,
+* there is no preset limit on the number of elements in the cadence.  If
+* there is more than one frequency used in the cadence, sequential elements
+* of different frequencies MUST use different tone table indexes.  Only one
+* cadence can be played at a time.  It is possible to build complex
+* cadences with multiple frequencies using 2 tone table indexes by
+* alternating between them.
+*
+******************************************************************************/
+
+typedef struct {
+       int index;
+       int tone_on_time;
+       int tone_off_time;
+       int freq0;
+       int gain0;
+       int freq1;
+       int gain1;
+} IXJ_CADENCE_ELEMENT;
+
+typedef enum {
+       PLAY_ONCE,
+       REPEAT_LAST_ELEMENT,
+       REPEAT_ALL
+} IXJ_CADENCE_TERM;
+
+typedef struct {
+       int elements_used;
+       IXJ_CADENCE_TERM termination;
+       IXJ_CADENCE_ELEMENT *ce;
+} IXJ_CADENCE;
+
+#define IXJCTL_TONE_CADENCE            _IOW ('q', 0xCA, IXJ_CADENCE *)
+/******************************************************************************
+*
+* This group of IOCTLs deal with the playback settings of the DSP
+*
+******************************************************************************/
+
+#define IXJCTL_PLAY_CODEC               PHONE_PLAY_CODEC
+#define IXJCTL_PLAY_START               PHONE_PLAY_START
+#define IXJCTL_PLAY_STOP                PHONE_PLAY_STOP
+#define IXJCTL_PLAY_DEPTH              PHONE_PLAY_DEPTH
+#define IXJCTL_PLAY_VOLUME             PHONE_PLAY_VOLUME
+#define IXJCTL_PLAY_LEVEL              PHONE_PLAY_LEVEL
+
+/******************************************************************************
+*
+* This group of IOCTLs deal with the Acoustic Echo Cancellation settings
+* of the DSP
+*
+* Issueing the IXJCTL_AEC_START command with a value of AEC_OFF has the
+* same effect as IXJCTL_AEC_STOP.  This is to simplify slider bar
+* controls.  IXJCTL_AEC_GET_LEVEL returns the current setting of the AEC.
+******************************************************************************/
+#define IXJCTL_AEC_START               _IOW ('q', 0xCB, int)
+#define IXJCTL_AEC_STOP                        _IO  ('q', 0xCC)
+#define IXJCTL_AEC_GET_LEVEL           _IO  ('q', 0xCD)
+
+#define AEC_OFF   0
+#define AEC_LOW   1
+#define AEC_MED   2
+#define AEC_HIGH  3
+/******************************************************************************
+*
+* Call Progress Tones, DTMF, etc.
+* IXJCTL_DTMF_OOB determines if dtmf signaling is sent as Out-Of-Band
+* only.  If you pass a 1, dtmf is suppressed from the audio stream.
+* Tone on and off times are in 250 microsecond intervals so
+* ioctl(ixj1, IXJCTL_SET_TONE_ON_TIME, 360);
+* will set the tone on time of board ixj1 to 360 * 250us = 90ms
+* the default values of tone on and off times is 840 or 210ms
+******************************************************************************/
+
+#define IXJCTL_DTMF_READY              PHONE_DTMF_READY
+#define IXJCTL_GET_DTMF                 PHONE_GET_DTMF
+#define IXJCTL_GET_DTMF_ASCII           PHONE_GET_DTMF_ASCII
+#define IXJCTL_DTMF_OOB                        PHONE_DTMF_OOB
+#define IXJCTL_EXCEPTION               PHONE_EXCEPTION
+#define IXJCTL_PLAY_TONE               PHONE_PLAY_TONE
+#define IXJCTL_SET_TONE_ON_TIME                PHONE_SET_TONE_ON_TIME
+#define IXJCTL_SET_TONE_OFF_TIME       PHONE_SET_TONE_OFF_TIME
+#define IXJCTL_GET_TONE_ON_TIME                PHONE_GET_TONE_ON_TIME
+#define IXJCTL_GET_TONE_OFF_TIME       PHONE_GET_TONE_OFF_TIME
+#define IXJCTL_GET_TONE_STATE          PHONE_GET_TONE_STATE
+#define IXJCTL_BUSY                    PHONE_BUSY
+#define IXJCTL_RINGBACK                        PHONE_RINGBACK
+#define IXJCTL_DIALTONE                        PHONE_DIALTONE
+#define IXJCTL_CPT_STOP                        PHONE_CPT_STOP
+
+/******************************************************************************
+* LineJack specific IOCTLs
+*
+* The lsb 4 bits of the LED argument represent the state of each of the 4
+* LED's on the LineJack
+******************************************************************************/
+
+#define IXJCTL_SET_LED                 _IOW ('q', 0xCE, int)
+#define IXJCTL_MIXER                   _IOW ('q', 0xCF, int)
+
+/******************************************************************************
+* 
+* The master volume controls use attenuation with 32 levels from 0 to -62dB
+* with steps of 2dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+* 
+******************************************************************************/
+#define MIXER_MASTER_L         0x0100
+#define MIXER_MASTER_R         0x0200
+#define ATT00DB                        0x00
+#define ATT02DB                        0x01
+#define ATT04DB                        0x02
+#define ATT06DB                        0x03
+#define ATT08DB                        0x04
+#define ATT10DB                        0x05
+#define ATT12DB                        0x06
+#define ATT14DB                        0x07
+#define ATT16DB                        0x08
+#define ATT18DB                        0x09
+#define ATT20DB                        0x0A
+#define ATT22DB                        0x0B
+#define ATT24DB                        0x0C
+#define ATT26DB                        0x0D
+#define ATT28DB                        0x0E
+#define ATT30DB                        0x0F
+#define ATT32DB                        0x10
+#define ATT34DB                        0x11
+#define ATT36DB                        0x12
+#define ATT38DB                        0x13
+#define ATT40DB                        0x14
+#define ATT42DB                        0x15
+#define ATT44DB                        0x16
+#define ATT46DB                        0x17
+#define ATT48DB                        0x18
+#define ATT50DB                        0x19
+#define ATT52DB                        0x1A
+#define ATT54DB                        0x1B
+#define ATT56DB                        0x1C
+#define ATT58DB                        0x1D
+#define ATT60DB                        0x1E
+#define ATT62DB                        0x1F
+#define MASTER_MUTE            0x80
+
+/******************************************************************************
+* 
+* The input volume controls use gain with 32 levels from +12dB to -50dB
+* with steps of 2dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+* 
+******************************************************************************/
+#define MIXER_PORT_CD_L                0x0600
+#define MIXER_PORT_CD_R                0x0700
+#define MIXER_PORT_LINE_IN_L   0x0800
+#define MIXER_PORT_LINE_IN_R   0x0900
+#define MIXER_PORT_POTS_REC    0x0C00
+#define MIXER_PORT_MIC         0x0E00
+
+#define GAIN12DB               0x00
+#define GAIN10DB               0x01
+#define GAIN08DB               0x02
+#define GAIN06DB               0x03
+#define GAIN04DB               0x04
+#define GAIN02DB               0x05
+#define GAIN00DB               0x06
+#define GAIN_02DB              0x07
+#define GAIN_04DB              0x08
+#define GAIN_06DB              0x09
+#define GAIN_08DB              0x0A
+#define GAIN_10DB              0x0B
+#define GAIN_12DB              0x0C
+#define GAIN_14DB              0x0D
+#define GAIN_16DB              0x0E
+#define GAIN_18DB              0x0F
+#define GAIN_20DB              0x10
+#define GAIN_22DB              0x11
+#define GAIN_24DB              0x12
+#define GAIN_26DB              0x13
+#define GAIN_28DB              0x14
+#define GAIN_30DB              0x15
+#define GAIN_32DB              0x16
+#define GAIN_34DB              0x17
+#define GAIN_36DB              0x18
+#define GAIN_38DB              0x19
+#define GAIN_40DB              0x1A
+#define GAIN_42DB              0x1B
+#define GAIN_44DB              0x1C
+#define GAIN_46DB              0x1D
+#define GAIN_48DB              0x1E
+#define GAIN_50DB              0x1F
+#define INPUT_MUTE             0x80
+
+/******************************************************************************
+* 
+* The POTS volume control use attenuation with 8 levels from 0dB to -28dB
+* with steps of 4dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+* 
+******************************************************************************/
+#define MIXER_PORT_POTS_PLAY   0x0F00
+
+#define POTS_ATT_00DB          0x00
+#define POTS_ATT_04DB          0x01
+#define POTS_ATT_08DB          0x02
+#define POTS_ATT_12DB          0x03
+#define POTS_ATT_16DB          0x04
+#define POTS_ATT_20DB          0x05
+#define POTS_ATT_24DB          0x06
+#define POTS_ATT_28DB          0x07
+#define POTS_MUTE              0x80
+
+/******************************************************************************
+* 
+* The DAA controls the interface to the PSTN port.  The driver loads the
+* US coefficients by default, so if you live in a different country you
+* need to load the set for your countries phone system.
+* 
+******************************************************************************/
+#define IXJCTL_DAA_COEFF_SET           _IOW ('q', 0xD0, int)
+
+#define DAA_US                 1       //PITA 8kHz
+#define DAA_UK                 2       //ISAR34 8kHz
+#define DAA_FRANCE     3       //
+#define DAA_GERMANY    4
+#define DAA_AUSTRALIA  5
+#define DAA_JAPAN      6
+
+/******************************************************************************
+* 
+* Use IXJCTL_PORT to set or query the port the card is set to.  If the
+* argument is set to PORT_QUERY, the return value of the ioctl will
+* indicate which port is currently in use, otherwise it will change the
+* port.
+* 
+******************************************************************************/
+#define IXJCTL_PORT                    _IOW ('q', 0xD1, int)
+
+#define PORT_QUERY     0
+#define PORT_POTS      1
+#define PORT_PSTN      2
+#define PORT_SPEAKER   3
+#define PORT_HANDSET   4
+
+#define IXJCTL_PSTN_SET_STATE          PHONE_PSTN_SET_STATE
+#define IXJCTL_PSTN_GET_STATE          PHONE_PSTN_GET_STATE
+
+#define PSTN_ON_HOOK   0
+#define PSTN_RINGING   1
+#define PSTN_OFF_HOOK  2
+#define PSTN_PULSE_DIAL        3
+
+/******************************************************************************
+* 
+* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR), 
+* and the transmit gain (AGX).  OR together the components and pass them
+* as the parameter to IXJCTL_DAA_AGAIN.  The default setting is both at 0dB.
+* 
+******************************************************************************/
+#define IXJCTL_DAA_AGAIN               _IOW ('q', 0xD2, int)
+
+#define AGRR00DB       0x00    // Analog gain in receive direction 0dB
+#define AGRR3_5DB      0x10    // Analog gain in receive direction 3.5dB
+#define AGRR06DB       0x30    // Analog gain in receive direction 6dB
+
+#define AGX00DB                0x00    // Analog gain in transmit direction 0dB
+#define AGX_6DB                0x04    // Analog gain in transmit direction -6dB
+#define AGX3_5DB       0x08    // Analog gain in transmit direction 3.5dB
+#define AGX_2_5B       0x0C    // Analog gain in transmit direction -2.5dB
+
+#define IXJCTL_PSTN_LINETEST           _IO  ('q', 0xD3)
+
+typedef struct {
+       char month[3];
+       char day[3];
+       char hour[3];
+       char min[3];
+       int numlen;
+       char number[11];
+       int namelen;
+       char name[80];
+} IXJ_CID;
+
+#define IXJCTL_CID                     _IOR ('q', 0xD4, IXJ_CID *)
+/******************************************************************************
+* 
+* The wink duration is tunable with this ioctl.  The default wink duration  
+* is 320ms.  You do not need to use this ioctl if you do not require a
+* different wink duration.
+* 
+******************************************************************************/
+#define IXJCTL_WINK_DURATION           PHONE_WINK_DURATION
+
+/******************************************************************************
+* 
+* This ioctl will connect the POTS port to the PSTN port on the LineJACK
+* In order for this to work properly the port selection should be set to
+* the PSTN port with IXJCTL_PORT prior to calling this ioctl.  This will
+* enable conference calls between PSTN callers and network callers.
+* Passing a 1 to this ioctl enables the POTS<->PSTN connection while
+* passing a 0 turns it back off.
+* 
+******************************************************************************/
+#define IXJCTL_POTS_PSTN               _IOW ('q', 0xD5, int)
+
+/******************************************************************************
+*
+* IOCTLs added by request.
+*
+* IXJCTL_HZ sets the value your Linux kernel uses for HZ as defined in
+*           /usr/include/asm/param.h, this determines the fundamental
+*           frequency of the clock ticks on your Linux system.  The kernel
+*           must be rebuilt if you change this value, also all modules you
+*           use (except this one) must be recompiled.  The default value
+*           is 100, and you only need to use this IOCTL if you use some
+*           other value.
+*
+*
+* IXJCTL_RATE sets the number of times per second that the driver polls
+*             the DSP.  This value cannot be larger than HZ.  By
+*             increasing both of these values, you may be able to reduce
+*             latency because the max hang time that can exist between the
+*             driver and the DSP will be reduced.
+*
+******************************************************************************/
+
+#define IXJCTL_HZ                       _IOW ('q', 0xE0, int)
+#define IXJCTL_RATE                     _IOW ('q', 0xE1, int)
+#define IXJCTL_FRAMES_READ             _IOR ('q', 0xE2, unsigned long)
+#define IXJCTL_FRAMES_WRITTEN          _IOR ('q', 0xE3, unsigned long)
+#define IXJCTL_READ_WAIT               _IOR ('q', 0xE4, unsigned long)
+#define IXJCTL_WRITE_WAIT              _IOR ('q', 0xE5, unsigned long)
+#define IXJCTL_DRYBUFFER_READ          _IOR ('q', 0xE6, unsigned long)
+#define IXJCTL_DRYBUFFER_CLEAR         _IO  ('q', 0xE7)
+
+/******************************************************************************
+*
+* The intercom IOCTL's short the output from one card to the input of the
+* other and vice versa (actually done in the DSP read function).  It is only
+* necessary to execute the IOCTL on one card, but it is necessary to have
+* both devices open to be able to detect hook switch changes.  The record
+* codec and rate of each card must match the playback codec and rate of
+* the other card for this to work properly.
+*
+******************************************************************************/
+
+#define IXJCTL_INTERCOM_START          _IOW ('q', 0xFD, int)
+#define IXJCTL_INTERCOM_STOP           _IOW ('q', 0xFE, int)
index d215a2703023483644a2c866e759e846e287d1c9..3112c34900fda53350a2a1cca5f1eeae3d9fe817 100644 (file)
@@ -348,6 +348,8 @@ extern int rs_init(void);
 extern int lp_init(void);
 extern int pty_init(void);
 extern int tty_init(void);
+extern int mxser_init(void);
+extern int moxa_init(void);
 extern int ip2_init(void);
 extern int pcxe_init(void);
 extern int pc_init(void);
index d2ded68379bf8de39d9ef53fc97befd54136c298..545c63b70815856bfbb4bff48a5fb125cfd36d32 100644 (file)
@@ -680,7 +680,8 @@ sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize)
                        break;
                }
 
-               current->blocked = new_set;
+               if (!error)
+                   current->blocked = new_set;
                recalc_sigpending(current);
                spin_unlock_irq(&current->sigmask_lock);
                if (error)
index aac8a9260d24fbd18a25a60cbab160ec932970e5..e54cd4075589773e0b45428fece9d9a0ceb840df 100644 (file)
@@ -88,6 +88,7 @@
 #include <linux/rtnetlink.h>
 #include <net/br.h>
 #include <linux/proc_fs.h>
+#include <linux/delay.h>
 
 #ifndef min
 #define min(a, b) (((a) <= (b)) ? (a) : (b))
@@ -2438,65 +2439,63 @@ struct net_local
  
 __initfunc(int brg_probe(struct device *dev))
 {
-  unsigned int bogomips;
-  struct timeval utime;
+       unsigned int bogomips;
+       struct timeval utime;
 
-  printk(KERN_INFO "%s: network interface for Ethernet Bridge 007/NET4.0\n", dev->name);
+       printk(KERN_INFO "%s: network interface for Ethernet Bridge 007/NET4.0\n", dev->name);
 
-  /*
-   *   Initialize the device structure.
-   */
+       /*
+        *      Initialize the device structure.
+        */
   
-  dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
-  if (dev->priv == NULL)
-    return -ENOMEM;
-  memset(dev->priv, 0, sizeof(struct net_local));
-
-  /* Set up MAC address based on BogoMIPs figure for first CPU and time
-   */ 
-  bogomips = (boot_cpu_data.loops_per_sec+2500)/500000 ;
-  get_fast_time(&utime);
-
-  /* Ummmm....  YES! */
-  dev->dev_addr[0] = '\xFE';
-  dev->dev_addr[1] = '\xFD';
-  dev->dev_addr[2] = (bridge_info.instance & 0x0F) << 4;
-  dev->dev_addr[2] |= ((utime.tv_sec & 0x000F0000) >> 16);
-  dev->dev_addr[3] = bogomips & 0xFF;
-  dev->dev_addr[4] = (utime.tv_sec & 0x0000FF00) >> 8;
-  dev->dev_addr[5] = (utime.tv_sec & 0x000000FF);
+       dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+       if (dev->priv == NULL)
+               return -ENOMEM;
+       memset(dev->priv, 0, sizeof(struct net_local));
+
+       /* Set up MAC address based on BogoMIPs figure for first CPU and time
+        */ 
+       bogomips = (loops_per_sec+2500)/500000 ;
+       get_fast_time(&utime);
+
+       /* Ummmm....  YES! */
+       dev->dev_addr[0] = '\xFE';
+       dev->dev_addr[1] = '\xFD';
+       dev->dev_addr[2] = (bridge_info.instance & 0x0F) << 4;
+       dev->dev_addr[2] |= ((utime.tv_sec & 0x000F0000) >> 16);
+       dev->dev_addr[3] = bogomips & 0xFF;
+       dev->dev_addr[4] = (utime.tv_sec & 0x0000FF00) >> 8;
+       dev->dev_addr[5] = (utime.tv_sec & 0x000000FF);
   
-  printk(KERN_INFO "%s: generated MAC address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", 
-        dev->name,
-        dev->dev_addr[0],
-        dev->dev_addr[1],
-        dev->dev_addr[2],
-        dev->dev_addr[3],
-        dev->dev_addr[4],
-        dev->dev_addr[5]);
+       printk(KERN_INFO "%s: generated MAC address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", 
+               dev->name,
+               dev->dev_addr[0],
+               dev->dev_addr[1],
+               dev->dev_addr[2],
+               dev->dev_addr[3],
+               dev->dev_addr[4],
+               dev->dev_addr[5]);
 
   
-  printk(KERN_INFO "%s: attached to bridge instance %lu\n", dev->name, dev->base_addr);
-
-  /*
-   *   The brg specific entries in the device structure.
-   */
-
-  dev->open = brg_open;
-  dev->hard_start_xmit = brg_start_xmit;
-  dev->stop = brg_close;
-  dev->get_stats = brg_get_stats;
-  dev->set_multicast_list = brg_set_multicast_list;
+       printk(KERN_INFO "%s: attached to bridge instance %lu\n", dev->name, dev->base_addr);
 
-  /*
-   *   Setup the generic properties
-   */
+       /*
+        *      The brg specific entries in the device structure.
+        */
 
-  ether_setup(dev);
+       dev->open = brg_open;
+       dev->hard_start_xmit = brg_start_xmit;
+       dev->stop = brg_close;
+       dev->get_stats = brg_get_stats;
+       dev->set_multicast_list = brg_set_multicast_list;
 
-  dev->tx_queue_len = 0;
+       /*
+        *      Setup the generic properties
+        */
 
-  return 0;
+       ether_setup(dev);
+       dev->tx_queue_len = 0;
+       return 0;
 }
 
 /*
index 2f06a3643aa76489ac01cbc6c078529c14e851f7..ce2a1524ccbc58066e8d25c06076e424f481d75a 100644 (file)
@@ -30,7 +30,7 @@ ctl_table unix_table[] = {
         &proc_dointvec_jiffies},
        {NET_UNIX_MAX_DGRAM_QLEN, "max_dgram_qlen",
        &sysctl_unix_max_dgram_qlen, sizeof(int), 0600, NULL, 
-        &proc_dointvec_jiffies},
+        &proc_dointvec},
        {0}
 };