]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.27 1.3.27
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:14 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:14 +0000 (15:10 -0500)
33 files changed:
Makefile
arch/i386/config.in
drivers/block/README.sbpcd
drivers/block/sbpcd.c
drivers/char/Makefile
drivers/char/atixlmouse.c
drivers/char/busmouse.c
drivers/char/istallion.c [new file with mode: 0644]
drivers/char/mouse.c
drivers/char/mouse.h [deleted file]
drivers/char/msbusmouse.c
drivers/char/psaux.c
drivers/char/stallion.c [new file with mode: 0644]
drivers/char/tty_io.c
fs/buffer.c
fs/isofs/inode.c
fs/proc/array.c
include/linux/busmouse.h
include/linux/cd1400.h [new file with mode: 0644]
include/linux/cdk.h [new file with mode: 0644]
include/linux/cdrom.h
include/linux/hdreg.h
include/linux/major.h
include/linux/mouse.h
include/linux/sbpcd.h
include/linux/sched.h
include/linux/tty.h
ipc/shm.c
kernel/exit.c
kernel/fork.c
kernel/sys.c
mm/memory.c
mm/swap.c

index a80c05708b276bf8524c9067b980bada41d08ddd..63b44596dc5cb8eb9c3dd172a8e88e067ad590b8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 26
+SUBLEVEL = 27
 
 ARCH = i386
 
index eecf7e2956d981a9e232e249b3eb5b9f36aa773d..beb7ee41d64eda7399eb738beac8e2c45e5d12e2 100644 (file)
@@ -226,13 +226,13 @@ if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then
   tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n
   tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n
   tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n
-  tristate 'Matsushita/Panasonic CDROM support' CONFIG_SBPCD n
+  tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD n
   if [ "$CONFIG_SBPCD" = "y" ]; then
-    bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
+    bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2 n
     if [ "$CONFIG_SBPCD2" = "y" ]; then
-      bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
+      bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3 n
       if [ "$CONFIG_SBPCD3" = "y" ]; then
-        bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
+        bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4 n
       fi
     fi
   fi
@@ -266,6 +266,11 @@ tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n
 comment 'character devices'
 
 bool 'Cyclades async mux support' CONFIG_CYCLADES n
+bool 'Stallion multiport serial support' CONFIG_STALDRV n
+if [ "$CONFIG_STALDRV" = "y" ]; then
+  tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION n
+  tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION n
+fi
 tristate 'Parallel printer support' CONFIG_PRINTER n
 tristate 'Logitech busmouse support' CONFIG_BUSMOUSE n
 tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
index 6a40abfb0634a8b51a6ad93c62c34d3f9642ca3c..f0ad67bcf9554237a4bee13cf827a66959182474 100644 (file)
@@ -1,14 +1,33 @@
-This README belongs to release 3.7 or newer of the SoundBlaster Pro
+This README belongs to release 3.9 or newer of the SoundBlaster Pro
 (Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
 CD-ROM driver for Linux.
 
-The driver is able to drive the whole family of "traditional" IDE-style (that
+Sbpcd really, really is NOT for ANY IDE/ATAPI drive!
+Not even if you have an "original" SoundBlaster card with an IDE interface!
+So, you better have a look into README.ide if your port address is 0x1F0,
+0x170, 0x1E8, 0x168 or similar.
+I get tons of mails from IDE/ATAPI drive users - I really can't continue
+any more to answer them all. So, if your drive/interface information sheets
+mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver
+invoking line within your CONFIG.SYS is using an address below 0x230:
+DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15
+(that is the "secondary IDE" configuration), set your drive to "master" and
+use ide-cd as your driver. If you do not have a second IDE hard disk, use the
+LILO commands
+   hdb=noprobe hdc=cdrom
+and get lucky.
+To make it fully clear to you: if you mail me about IDE/ATAPI drive problems,
+my answer is above, and I simply will discard your mail, hoping to stop the
+flood and to find time to lead my 12-years old son towards happy computing.
+
+The driver is able to drive the whole family of "traditional" AT-style (that
 is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita,
 Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
 well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
+CR-574 is an IDE/ATAPI drive.
 
 The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It is supported now - with help by Serge Robyns.
+Matsushita command set. It is supported - with help by Serge Robyns.
 
 There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
 with a special controller board. This drive is supported (the interface is
@@ -16,39 +35,72 @@ of the "LaserMate" type), and it is possibly the best buy today (cheaper than
 an internal drive, and you can use it as an internal, too - f.e. plug it into
 a soundcard).
 
-CreativeLabs has a new drive "CD-200". Support is under construction.
-Drive detection and playing audio should already work. I need qualified
-feedback about the bugs within the data functions or a drive (I never saw
-a CD200).
-
-The quad-speed TEAC CD-55A drive is supported. The routines may still be
-a little bit buggy, but the data rate already reaches 500 kB/sec if you
-set SBP_BUFFER_FRAMES to 64. The drive is able to deliver 600 kB/sec, so
-this has to get a point of work.
+CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter
+is made by Funai and sometimes named "E2550UA". Support is under construction
+ - CD200F should work, CD200 is still giving problems.
+Drive detection and playing audio should work. I need qualified feedback
+about the bugs within the data functions or a drive (I never saw a CD200).
+
+The quad-speed TEAC CD-55A drive is supported, but still does not reach "full
+speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES
+to 64 (it is not recommended to do that for normal "file access" usage, but it
+can speed up things a lot if you use something like "dd" to read from the
+drive; I use it for verifying self-written CDs this way).
+The drive itself is able to deliver 600 kB/sec, so this has to get a point of
+work; with the normal setup, the performance currently is not even as good as
+double-speed.
 
 This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
-and this driver is in no way usable for any new IDE ATAPI drive.
+and again: this driver is in no way usable for any IDE/ATAPI drive. If you 
+think your drive should work and it doesn't: send me the DOS driver for your
+beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help,
+and include an original log message excerpt, and try to give all information
+a complete idiot needs to understand your hassle already with your first
+mail. And if you want to say "as I have mailed you before", be sure that I
+don't remember your "case" by such remarks; at the moment, I have some 
+hundreds open correspondences about Linux CDROM questions (hope to reduce if
+the IDE/ATAPI user questions disappear). 
 
-This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
-SoundFX, ...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
-WDH-7001C, Longshine LCS-6853, older Aztech cards, ...).
 
-It should work too now with the "configurable" interface "Sequoia S-1000",
-which is found on the Spea Media FX sound card. I still need feedback about
-this, or such a card. Anyways, the procedure "boot DOS and wait until 
-CONFIG.SYS is done, then use CTL-ALT-DEL to boot Linux" should make it
-work.
+This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
+SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P,
+LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
+
+It finally works now with the "configurable" interface "Sequoia S-1000", too,
+which is found on the Spea Media FX and Ensonic Soundscape sound cards. You
+have to specify the type "SBPRO 2" and the true CDROM port address with it,
+not the "configuration port" address.
+
+If you have a sound card which needs a "configuration driver" instead of
+jumpers for interface types and addresses (like Mozart cards) - those
+drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical
+names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the
+CDROM port configuration (the leading comments within 
+linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c
+code is able to set up my Mozart card - I simply had to add
+   #define MAD16_CONF 0x06
+   #define MAD16_CDSEL 0x03
+to configure the CDROM interface for type "Panasonic" (LaserMate) and address
+0x340.
 
 The interface type has to get configured in /usr/include/linux/sbpcd.h, 
-because the behavior of some sound card interfaces is different.
+because the register layout is different between the "SoundBlaster" and the
+"LaserMate" type.
 
-With some TEAC drives I have seen interface cards which seem to lack the
-"drive select" lines; always drive 0 gets addressed. To avoid "mirror drives"
-with such interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0.
+I got a report that the TEAC interface card "I/F E117098" is of type
+"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses
+0x300 and above. This is unusual, and it can't get covered by the auto
+probing scheme.
+If auto-probing found the drive, the address is correct. The reported type
+may be wrong. A "mount" will give success only if the interface type is set
+right. Playing audio should work with a wrong set interface type, too.
+
+With some TEAC and some CD200 drives I have seen interface cards which seem
+to lack the "drive select" lines; always drive 0 gets addressed. To avoid
+"mirror drives" (four drives detected where you only have one) with such
+interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if
+possible).
 
-The driver respects all known drive firmware releases - my old drive is a 2.11,
-but it should work with CR-52x drives <2.01 ... >3.00 and with CR-56x drives
-<0.75 .. 5.00.
 
 Up to 4 drives per interface card, and up to 4 interface cards are supported.
 All supported drive families can be mixed, but the CR-521 drives are 
@@ -104,13 +156,15 @@ session of a photoCD).
 At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
 convert photo CD image files and Gerd Knorr's viewing utility.
 
-The transfer rate will reach 150 kB/sec with "old" drives, 300 kB/sec with
-double-speed drives, and about 500 kB/sec with quad speed drives.
+The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with
+CR-56x drives, and currently not more than 500 kB/sec (usually less than
+250 kB/sec) with the TEAC quad speed drives.
 XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
 
-This release is part of the standard kernel and consists of
+This release consists of
 - this README file
 - the driver file linux/drivers/block/sbpcd.c
+- the stub files linux/drivers/block/sbpcd[234].c
 - the header file linux/include/linux/sbpcd.h.
 
 
@@ -119,21 +173,27 @@ To install:
 
 1. Setup your hardware parameters. Though the driver does "auto-probing" at a
    lot of (not all possible!) addresses, this step is recommended for
-   every-day use.
+   every-day use. You should let sbpcd auto-probe once and use the reported
+   address if a drive got found. The reported type may be incorrect; it is
+   correct if you can mount a data CD. There is no choice for you with the
+   type; only one is the right, the others are deadly wrong.
+
    a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your
       hardware (near the beginning):
       a1. Set it up for the appropriate type of interface board.
           "Original" CreativeLabs sound cards need "SBPRO 1".
-          Most "compatible" sound cards (for example "Highscreen", "SoundFX"
-          and "Galaxy") need "SBPRO 0".
+          Most "compatible" sound cards (almost all "non-CreativeLabs" cards)
+          need "SBPRO 0".
           The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
           All other "no-sound" boards need the "SBPRO 0" setup.
+          Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup.
           The Spea Media FX sound card needs "SBPRO 2".
           sbpcd.c holds some examples in its auto-probe list.
           If you configure "SBPRO" wrong, the playing of audio CDs will work,
           but you will not be able to mount a data CD.
       a2. Tell the address of your CDROM_PORT (not of the sound port).
-      a3. Set DISTRIBUTION to 0.
+      a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1.
+      a4. Set DISTRIBUTION to 0.
    b. Additionally for 2.a1 and 2.a2, the setup may be done during
       boot time (via the "kernel command line" or "LILO option"):
           sbpcd=0x230,SoundBlaster
@@ -142,16 +202,23 @@ To install:
       or
           sbpcd=0x330,SPEA
       This is especially useful if you install a fresh distribution.
+      If the second parameter is a number, it gets taken as the type
+      setting; 0 is "LaserMate", 1 is "SoundBlaster".
+      So, for example
+          sbpcd=0x230,1
+      is equivalent to
+          sbpcd=0x230,SoundBlaster
+
 2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
    CD-ROM support and for ISO9660 FileSystem support. If you do not have a
    second, third, or fourth controller installed, do not say "y" to the 
    secondary Matsushita CD-ROM questions.
-   SCSI and/or SCSI CD-ROM support is not needed.
 
 3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
 
-4. Make the device file(s). The driver uses definitely and exclusive the
-   MAJOR 25, so do
+4. Make the device file(s). This step usually already has been done by the
+   MAKEDEV script.
+   The driver uses MAJOR 25, so, if necessary, do
         mknod /dev/sbpcd  b 25 0       (if you have only one drive)
    and/or
         mknod /dev/sbpcd0 b 25 0
@@ -160,10 +227,8 @@ To install:
         mknod /dev/sbpcd3 b 25 3
    to make the node(s).
 
-   The driver no longer uses the "AT bus style" device numbering; the SCSI
-   scheme is used now; that means, the "first found" drive gets MINOR 0
-   (regardless to its jumpered ID), the "next found" (at the same cable)
-   gets MINOR 1, ...
+   The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the
+   "next found" (at the same cable) gets MINOR 1, ...
    
    For a second interface board, you have to make nodes like
         mknod /dev/sbpcd4 b 26 0
@@ -179,11 +244,12 @@ To install:
 You should now be able to do
               mkdir /CD
 and 
-              mount -t iso9660 -o ro /dev/sbpcd /CD
+              mount -rt iso9660 /dev/sbpcd /CD
 or
-              mount -t iso9660 -o ro,block=2048 /dev/sbpcd /CD
-and see the contents of your CD in the /CD directory, and/or hear music with
-"workman -c /dev/sbpcd &".
+              mount -rt iso9660 -o block=2048 /dev/sbpcd /CD
+and see the contents of your CD in the /CD directory.
+To use audio CDs, a mounting is not recommended (and it would fail if the
+first track is not a data track).
 
 
 Using sbpcd as a "loadable module":
@@ -214,6 +280,8 @@ No DMA and no IRQ is used.
 
 To reduce or increase the amount of kernel messages, edit sbpcd.c and play
 with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
+Don't forget to reflect what you do; enabling all DBG_xxx switches at once
+may crash your system.
 
 The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
 specify "block=2048" as a mount option. Doing this will disable the direct
@@ -225,13 +293,13 @@ distribution) which MUST get handled with a block_size of 1024. Generally,
 one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
 do not use block=2048 with those.
 
-At the beginning of sbpcd.c, you will find some "#define"s (f.e. EJECT and
-JUKEBOX). With that, you can configure the driver for some special things.
+Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With
+that, you can configure the driver for some special things.
 You can use the appended program "cdtester" to set the auto-eject feature
 during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
 for you.
 
-There is a new ioctl CDROMMULTISESSION to obtain with a user program if
+There is an ioctl CDROMMULTISESSION to obtain with a user program if
 the CD is an XA disk and - if it is - where the last session starts. The
 "cdtester" program illustrates how to call it.
 
@@ -284,8 +352,8 @@ If your I/O port address is not 0x340, you have to look for the #defines near
 the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
 change CDROM_PORT to the address of your CDROM I/O port.
 
-Most of the "SoundBlaster compatible" cards behave like the no-sound
-interfaces! 
+Almost all of the "SoundBlaster compatible" cards behave like the no-sound
+interfaces, i.e. need SBPRO 0
 
 With "original" SB Pro cards, an initial setting of CD_volume through the
 sound cards MIXER register gets done.
@@ -790,6 +858,21 @@ main(int argc, char *argv[])
                        rc=ioctl(drive,CDROMPLAYMSF,&msf);
                        if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
                        break;
+               case 'V':
+                       rc=ioctl(drive,CDROMVOLREAD,&volctrl);
+                       if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+                       printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
+                       break;  
+               case 'R':
+                       rc=ioctl(drive,CDROMRESET);
+                       if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
+                       break;
+               case 'B': /* set the driver's (?) read ahead value */
+                       printf("enter read-ahead size: ? ");
+                       scanf("%d",&i);
+                       rc=ioctl(drive,BLKRASET,i);
+                       if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
+                       break;
 #ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
                case 'd':
                        printf("Address (min:sec:frm)  ");
@@ -955,21 +1038,6 @@ main(int argc, char *argv[])
                        rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
                        printf("%d frames granted.\n",rc);
                        break;
-               case 'V':
-                       rc=ioctl(drive,CDROMVOLREAD,&volctrl);
-                       if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
-                       printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
-                       break;  
-               case 'R':
-                       rc=ioctl(drive,CDROMRESET);
-                       if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
-                       break;
-               case 'B': /* set the driver's (?) read ahead value */
-                       printf("enter read-ahead size: ? ");
-                       scanf("%d",&i);
-                       rc=ioctl(drive,BLKRASET,i);
-                       if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
-                       break;
 #endif SBP_PRIVATE_IOCTLS
                default:
                        printf("unknown command: \"%s\".\n",command);
@@ -978,20 +1046,4 @@ main(int argc, char *argv[])
        }
 }
 /*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file. 
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
 
index 6888eec89b89ef29c6bf19fe810f750e265dc8b1..2cff9a5125041c3fffd3fdcdd51b6376e9459edd 100644 (file)
@@ -10,9 +10,9 @@
  *            detailed bug reports).
  *            Also for the TEAC CD-55A drive.
  *            Not for Sanyo drives (but sjcd is there...).
- *            Not for Funai drives.
+ *            Not for any other Funai drives than E2550UA (="CD200" with "F").
  *
- *  NOTE:     This is release 3.8.
+ *  NOTE:     This is release 3.9.
  *
  *  VERSION HISTORY
  *
  *
  *  3.8  Elongated max_latency for CR-56x drives.
  *
+ *  3.9  Finally fixed the long-known SoundScape/SPEA/Sequoia S-1000 interface
+ *       configuration bug.
+ *       Now Corey, Heiko, Ken, Leo, Vadim/Eric & Werner are invited to copy
+ *       the config_spea() routine into their drivers. ;-)
+ *
+ *
  *  TODO
  *
  *     disk change detection
- *     allow & synchronize multi-activity
+ *     synchronize multi-activity
  *        (data + audio + ioctl + disk change, multiple drives)
  *     implement "read all subchannel data" (96 bytes per frame)
+ *     check if CDROMPLAYMSF can cause a hang
  *
  *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
  *     elaborated speed-up experiments (and the fabulous results!), for
@@ -298,7 +305,7 @@ char kernel_version[]=UTS_RELEASE;
 
 #include "blk.h"
 
-#define VERSION "v3.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v3.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*==========================================================================*/
 /*
@@ -364,9 +371,9 @@ static int sbpcd[] =
        0x270, 1, /* Soundblaster 16 */
        0x670, 0, /* "sound card #9" */
        0x690, 0, /* "sound card #9" */
-       0x330, 2, /* SPEA Media FX (default) */
-       0x320, 2, /* SPEA Media FX */
-       0x340, 2, /* SPEA Media FX */
+       0x338, 2, /* SPEA Media FX, Ensonic SoundScape (default) */
+       0x328, 2, /* SPEA Media FX */
+       0x348, 2, /* SPEA Media FX */
        0x634, 0, /* some newer sound cards */
        0x638, 0, /* some newer sound cards */
        0x230, 1, /* some newer sound cards */
@@ -380,7 +387,7 @@ static int sbpcd[] =
         */
        0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
        0x350, 0, /* Lasermate, CI-101P */
-       0x350, 2, /* SPEA Media FX */
+       0x358, 2, /* SPEA Media FX */
        0x370, 0, /* Lasermate, CI-101P */
        0x290, 1, /* Soundblaster 16 */
        0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
@@ -499,6 +506,8 @@ static const char *str_sb_l = "soundblaster";
 static const char *str_lm = "LaserMate";
 static const char *str_sp = "SPEA";
 static const char *str_sp_l = "spea";
+static const char *str_ss = "SoundScape";
+static const char *str_ss_l = "soundscape";
 const char *type;
 
 #if !(SBPCD_ISSUE-1)
@@ -608,6 +617,7 @@ static struct {
        u_int lba_multi;
        int first_session;
        int last_session;
+       int track_of_last_session;
        
        u_char audio_state;
        u_int pos_audio_start;
@@ -634,7 +644,6 @@ static struct {
        u_char UPC_ctl_adr;
        u_char UPC_buf[7];
        
-       int CDsize_blk;
        int frame_size;
        int CDsize_frm;
        
@@ -730,20 +739,26 @@ static int sbpcd_dbg_ioctl(unsigned long arg, int level)
 static void mark_timeout_delay(u_long i)
 {
        timed_out_delay=1;
+#if 0
        msg(DBG_TIM,"delay timer expired.\n");
+#endif
 }
 /*==========================================================================*/
 static void mark_timeout_data(u_long i)
 {
        timed_out_data=1;
+#if 0
        msg(DBG_TIM,"data timer expired.\n");
+#endif
 }
 /*==========================================================================*/
 #if 0
 static void mark_timeout_audio(u_long i)
 {
        timed_out_audio=1;
+#if 0
        msg(DBG_TIM,"audio timer expired.\n");
+#endif
 }
 #endif
 /*==========================================================================*/
@@ -775,7 +790,7 @@ static void sbp_sleep(u_int time)
  */
 static INLINE void lba2msf(int lba, u_char *msf)
 {
-       lba += CD_BLOCK_OFFSET;
+       lba += CD_MSF_OFFSET;
        msf[0] = lba / (CD_SECS*CD_FRAMES);
        lba %= CD_SECS*CD_FRAMES;
        msf[1] = lba / CD_FRAMES;
@@ -797,8 +812,8 @@ static INLINE u_int blk2msf(u_int blk)
        u_int mm;
        
        msf.c[3] = 0;
-       msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
-       mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
+       msf.c[2] = (blk + CD_MSF_OFFSET) / (CD_SECS * CD_FRAMES);
+       mm = (blk + CD_MSF_OFFSET) % (CD_SECS * CD_FRAMES);
        msf.c[1] = mm / CD_FRAMES;
        msf.c[0] = mm % CD_FRAMES;
        return (msf.n);
@@ -835,7 +850,7 @@ static INLINE int msf2blk(int msfx)
        int i;
        
        msf.n=msfx;
-       i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
+       i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_MSF_OFFSET;
        if (i<0) return (0);
        return (i);
 }
@@ -847,7 +862,7 @@ static INLINE int msf2lba(u_char *msf)
 {
        int i;
        
-       i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
+       i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_MSF_OFFSET;
        if (i<0) return (0);
        return (i);
 }
@@ -928,7 +943,7 @@ static int CDi_stat_loop(void)
                        if (!(j&s_not_result_ready)) return (j);
                        if (fam0L_drive) if (j&s_attention) return (j);
                }
-               sbp_sleep(HZ/100);
+               sbp_sleep(1);
                i = 1;
        }
 #else
@@ -950,7 +965,7 @@ static int CDi_stat_loop(void)
                                if (!(j&s_not_result_ready)) return (j);
                                if (fam0L_drive) if (j&s_attention) return (j);
                        }
-                       sbp_sleep(HZ/100);
+                       sbp_sleep(1);
                        i = 1;
                }
 #endif MODULE
@@ -1025,7 +1040,7 @@ static int ResponseInfo(void)
                                        if (!(st&s_not_result_ready)) break;
                                }
                                if ((j!=0)||(timeout<=jiffies)) break;
-                               sbp_sleep(HZ/100);
+                               sbp_sleep(1);
                                j = 1;
                        }
                        if (timeout<=jiffies) break;
@@ -1181,7 +1196,7 @@ static int ResponseStatus(void)
                                if (!(i&s_not_result_ready)) break;
                        }
                        if ((j!=0)||(timeout<jiffies)) break;
-                       sbp_sleep(HZ/100);
+                       sbp_sleep(1);
                        j = 1;
                }
                while (1);
@@ -1358,7 +1373,7 @@ static int cmd_out_T(void)
                                drvcmd[0]=CMDT_READ_ERR;
                                j=cmd_out_T(); /* !!! recursive here !!! */
                                --recursion;
-                               sbp_sleep(HZ/100);
+                               sbp_sleep(1);
                        }
                        while (j<0);
                        D_S[d].error_state=infobuf[2];
@@ -1917,7 +1932,7 @@ static int DriveReset(void)
                i=GetStatus();
                if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
                if (!st_caddy_in) break;
-               sbp_sleep(HZ/100);
+               sbp_sleep(1);
        }
        while (!st_diskok);
 #if 000
@@ -2075,7 +2090,7 @@ static int UnLockDoor(void)
        {
                i=cc_LockDoor(0);
                --j;
-               sbp_sleep(HZ/100);
+               sbp_sleep(1);
        }
        while ((i<0)&&(j));
        if (i<0)
@@ -2095,7 +2110,7 @@ static int LockDoor(void)
        {
                i=cc_LockDoor(1);
                --j;
-               sbp_sleep(HZ/100);
+               sbp_sleep(1);
        }
        while ((i<0)&&(j));
        if (j==0)
@@ -2106,7 +2121,7 @@ static int LockDoor(void)
                {
                        i=cc_LockDoor(1);
                        --j;
-                       sbp_sleep(HZ/100);
+                       sbp_sleep(1);
                }
                while ((i<0)&&(j));
        }
@@ -2256,12 +2271,12 @@ static int cc_ModeSense(void)
        i=cmd_out();
        if (i<0) return (i);
        i=0;
+       D_S[d].sense_byte=0;
        if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
-       else if (fam0L_drive) D_S[d].sense_byte=0;
        else if (famT_drive)
        {
-               D_S[d].sense_byte=0;
-               if (infobuf[4]==0x01) D_S[d].xa_byte=0x20; /* wrong!!!! */
+               if (infobuf[4]==0x01) D_S[d].xa_byte=0x20;
+               else D_S[d].xa_byte=0;
                i=2;
        }
        D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
@@ -2459,7 +2474,8 @@ static int cc_ReadCapacity(void)
 {
        int i, j;
        
-       if (famL_drive) return (0);
+       if (famL_drive) return (0); /* some firmware lacks this command */
+       if (famT_drive) return (0); /* done with cc_ReadTocDescr() */
        D_S[d].diskstate_flags &= ~cd_size_bit;
        for (j=3;j>0;j--)
        {
@@ -2482,31 +2498,15 @@ static int cc_ReadCapacity(void)
                        response_count=5;
                        flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
                }
-               else if (famT_drive)
-               {
-                       response_count=12;
-                       drvcmd[0]=CMDT_DISKINFO;
-                       drvcmd[1]=0x02;
-                       drvcmd[6]=CDROM_LEADOUT;
-                       drvcmd[8]=response_count;
-                       drvcmd[9]=0x00;
-               }
                i=cmd_out();
                if (i>=0) break;
                msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
                cc_ReadError();
        }
        if (j==0) return (i);
-       if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_BLOCK_OFFSET;
+       if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_MSF_OFFSET;
        else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
        else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
-       else if (famT_drive)
-       {
-               D_S[d].CDsize_frm=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
-               D_S[d].n_first_track=infobuf[2];
-               D_S[d].n_last_track=infobuf[3];
-
-       }
        D_S[d].diskstate_flags |= cd_size_bit;
        msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
        return (0);
@@ -2605,6 +2605,7 @@ static int cc_ReadTocDescr(void)
        {
                D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
                D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+               D_S[d].CDsize_frm=D_S[d].size_blk+1;
                D_S[d].n_first_track=infobuf[2];
                D_S[d].n_last_track=infobuf[3];
        }
@@ -2759,7 +2760,7 @@ static int cc_ReadUPC(void)
        
        D_S[d].diskstate_flags &= ~upc_bit;
 #if TEST_UPC
-       for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
+       for (block=CD_MSF_OFFSET+1;block<CD_MSF_OFFSET+200;block++)
        {
 #endif TEST_UPC
                clr_cmdbuf();
@@ -2888,7 +2889,14 @@ static int cc_CheckMultiSession(void)
                drvcmd[9]=0x40;
                i=cmd_out();
                if (i<0) return (i);
-               D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+               D_S[d].first_session=infobuf[2];
+               D_S[d].last_session=infobuf[3];
+               D_S[d].track_of_last_session=infobuf[6];
+               if (D_S[d].first_session!=D_S[d].last_session)
+               {
+                       D_S[d].f_multisession=1;
+                       D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+               }
        }
        for (i=0;i<response_count;i++)
                sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
@@ -2947,7 +2955,9 @@ static void check_datarate(void)
        delay_timer.expires=jiffies+11*HZ/10;
        timed_out_delay=0;
        add_timer(&delay_timer);
+#if 0
        msg(DBG_TIM,"delay timer started (11*HZ/10).\n");
+#endif
        do
        {
                i=inb(CDi_status);
@@ -2958,7 +2968,9 @@ static void check_datarate(void)
        }
        while (!timed_out_delay);
        del_timer(&delay_timer);
+#if 0
        msg(DBG_TIM,"datarate: %04X\n", datarate);
+#endif
        if (datarate<65536) datarate=65536;
        maxtim16=datarate*16;
        maxtim04=datarate*4;
@@ -2969,8 +2981,9 @@ static void check_datarate(void)
 #else
        maxtim_data=datarate/300;
 #endif LONG_TIMING
-       msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n",
-           maxtim_8, maxtim_data);
+#if 0
+       msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n", maxtim_8, maxtim_data);
+#endif
 }
 /*==========================================================================*/
 #if 0
@@ -3263,14 +3276,14 @@ static int check_version(void)
                        else
                        {
                                D_S[d].drv_type=drv_100;
-                               if ((j!=500)||(j!=102)) ask_mail();
+                               if ((j!=500)&&(j!=102)) ask_mail();
                        }
                }
                else if (fam2_drive)
                {
                        msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
-                       msg(DBG_INF,"support is not fulfilled yet - audio should work.\n");
-                       if ((j!=101)&&(j!=35)) ask_mail(); /* only 1.01 and 0.35 known at time */
+                       msg(DBG_INF,"CD200 is not fully supported yet - CD200F should work.\n");
+                       if ((j!=1)&&(j!=101)&&(j!=35)) ask_mail(); /* unknown version at time */
                }
        }
        msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
@@ -3654,7 +3667,7 @@ static int DiskInfo(void)
        if ((fam0L_drive) && (D_S[d].xa_byte==0x20))
        {
                /* XA disk with old drive */
-               cc_ModeSelect(CD_FRAMESIZE_XA);
+               cc_ModeSelect(CD_FRAMESIZE_RAW1);
                cc_ModeSense();
        }
        if (famT_drive) cc_prep_mode_T();
@@ -4081,7 +4094,7 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
                
        case CDROMREADMODE2: /* not usable at the moment */
                msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
-               cc_ModeSelect(CD_FRAMESIZE_XA);
+               cc_ModeSelect(CD_FRAMESIZE_RAW1);
                cc_ModeSense();
                D_S[d].mode=READ_M2;
                return (0);
@@ -4156,7 +4169,7 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
                                flags_cmd_out |= f_respo3;
                                cc_ReadStatus();
                                if (sbp_status() != 0) break;
-                               sbp_sleep(HZ/100);    /* wait a bit, try again */
+                               sbp_sleep(1);    /* wait a bit, try again */
                        }
                        if (status_tries == 0)
                        {
@@ -4211,7 +4224,7 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
                                        if (try != 0 || timeout <= jiffies) break;
                                        if (data_retrying == 0) data_waits++;
                                        data_retrying = 1;
-                                       sbp_sleep(HZ/100);
+                                       sbp_sleep(1);
                                        try = 1;
                                }
                                if (try==0)
@@ -4971,12 +4984,15 @@ static struct file_operations sbpcd_fops =
  *             or
  *                 sbpcd=0x300,LaserMate
  *             or
- *                 sbpcd=0x330,SPEA
+ *                 sbpcd=0x330,SoundScape
  *
- * (upper/lower case sensitive here!!!).
+ * (upper/lower case sensitive here - but all-lowercase is ok!!!).
  *
- * the address value has to be the TRUE CDROM PORT ADDRESS -
+ * the address value has to be the CDROM PORT ADDRESS -
  * not the soundcard base address.
+ * For the SPEA/SoundScape setup, DO NOT specify the "configuration port"
+ * address, but the address which is really used for the CDROM (usually 8
+ * bytes above).
  *
  */
 #if (SBPCD_ISSUE-1)
@@ -4986,11 +5002,14 @@ void sbpcd_setup(const char *s, int *p)
 {
        setup_done++;
        msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
-       sbpro_type=0;
+       sbpro_type=0; /* default: "LaserMate" */
+       if (p[0]>1) sbpro_type=p[2];
        if (!strcmp(s,str_sb)) sbpro_type=1;
        else if (!strcmp(s,str_sb_l)) sbpro_type=1;
        else if (!strcmp(s,str_sp)) sbpro_type=2;
        else if (!strcmp(s,str_sp_l)) sbpro_type=2;
+       else if (!strcmp(s,str_ss)) sbpro_type=2;
+       else if (!strcmp(s,str_ss_l)) sbpro_type=2;
        if (p[0]>0) sbpcd_ioaddr=p[1];
        
        CDo_command=sbpcd_ioaddr;
@@ -5010,24 +5029,32 @@ void sbpcd_setup(const char *s, int *p)
 /*==========================================================================*/
 /*
  * Sequoia S-1000 CD-ROM Interface Configuration
- * as used within SPEA Media FX card
- * The SPEA soundcard has to get configured for 
- *     -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi)
- *     -> I/O base address (0x320, 0x330, 0x340, 0x350)
+ * as used within SPEA Media FX, Ensonic SoundScape and some Reveal cards
+ * The soundcard has to get jumpered for the interface type "Panasonic"
+ * (not Sony or Mitsumi) and to get soft-configured for
+ *     -> configuration port address
+ *     -> CDROM port offset (num_ports): has to be 8 here. Possibly this
+ *        offset value determines the interface type (none, Panasonic,
+ *        Mitsumi, Sony).
+ *        The interface uses a configuration port (0x320, 0x330, 0x340, 0x350)
+ *        some bytes below the real CDROM address.
+ *         
+ *        For the Panasonic style (LaserMate) interface and the configuration
+ *        port 0x330, we have to use an offset of 8; so, the real CDROM port
+ *        address is 0x338.
  */
 static int config_spea(void)
 {
        int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
-       /* What is n_ports? Number of addresses or base address offset? */
-       int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
-       int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */
+       /* base address offset between configuration port and CDROM port */
+       int irq_number=0; /* off:0x00, 2:0x01, 7:0x03, 12:0x05, 15:0x07 */
+       int dma_channel=0; /* off: 0x00, 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68 */
        int dack_polarity=0; /* L:0x00, H:0x80 */
        int drq_polarity=0x40; /* L:0x00, H:0x40 */
-       
        int i;
-       
-#define SPEA_REG_1 sbpcd_ioaddr+4
-#define SPEA_REG_2 sbpcd_ioaddr+5
+
+#define SPEA_REG_1 sbpcd_ioaddr-0x08+4
+#define SPEA_REG_2 sbpcd_ioaddr-0x08+5
        
        OUT(SPEA_REG_1,0xFF);
        i=inb(SPEA_REG_1);
@@ -5056,7 +5083,7 @@ static int config_spea(void)
        OUT(SPEA_REG_2,i);
        
        sbpro_type = 0; /* acts like a LaserMate interface now */
-       msg(DBG_SEQ,"found SPEA interface at %04X.\n", sbpcd_ioaddr);
+       msg(DBG_SEQ,"found SoundScape interface at %04X.\n", sbpcd_ioaddr);
        return (0);
 }
 /*==========================================================================*/
@@ -5080,18 +5107,18 @@ int init_module(void)
 #if DISTRIBUTION
        if (!setup_done)
        {
-               msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, IBM, Longshine, TEAC CD-ROM drives\n");
-               msg(DBG_INF,"\n= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
-               msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an ethernet card).\n");
+               msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, Longshine, TEAC CD-ROM drives\n");
+               msg(DBG_INF,"= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
+               msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an NE2000 card).\n");
                msg(DBG_INF,"If that happens, you have to reboot and use the\n");
                msg(DBG_INF,"LILO (kernel) command line feature like:\n");
                msg(DBG_INF,"   LILO boot: ... sbpcd=0x230,SoundBlaster\n");
                msg(DBG_INF,"or like:\n");
                msg(DBG_INF,"   LILO boot: ... sbpcd=0x300,LaserMate\n");
                msg(DBG_INF,"or like:\n");
-               msg(DBG_INF,"   LILO boot: ... sbpcd=0x330,SPEA\n");
+               msg(DBG_INF,"   LILO boot: ... sbpcd=0x338,SoundScape\n");
                msg(DBG_INF,"with your REAL address.\n");
-               msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = = = = = =\n\n");
+               msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = == = = =\n");
        }
 #endif DISTRIBUTION
        sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
index c01464a31bb22bf82d2b28b09bb591e24c006fb4..cf31434ec4af25911e8845eb0628790392bff492 100644 (file)
@@ -24,6 +24,22 @@ ifdef CONFIG_CYCLADES
 L_OBJS += cyclades.o
 endif
 
+ifeq ($(CONFIG_STALLION),y)
+L_OBJS += stallion.o
+else
+  ifeq ($(CONFIG_STALLION),m)
+  M_OBJS += stallion.o
+  endif
+endif
+
+ifeq ($(CONFIG_ISTALLION),y)
+L_OBJS += istallion.o
+else
+  ifeq ($(CONFIG_ISTALLION),m)
+  M_OBJS += istallion.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
index 8a806ecdd35a42d61b90914fb29dc844b8dab4ab..bce7ee7edb5613ba8ca16da1c310009168e75aed 100644 (file)
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <linux/errno.h>
+#include <linux/mouse.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 
-#include "mouse.h"
-
 #define ATIXL_MOUSE_IRQ                5 /* H/W interrupt # set up on ATIXL board */
 #define ATIXL_BUSMOUSE         3 /* Minor device # (mknod c 10 3 /dev/bm) */
 
index 4a53f1c3ca09e9bf8449b52637ced9d2bae23db7..ddbc99e17c37b4af82cdfc0263ce0a87be3f35a9 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/signal.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
+#include <linux/mouse.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -243,7 +244,7 @@ struct file_operations bus_mouse_fops = {
        fasync_mouse,
 };
 
-static struct mouse bus_mous = {
+static struct mouse bus_mouse = {
        LOGITECH_BUSMOUSE, "busmouse", &bus_mouse_fops
 };
 
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
new file mode 100644 (file)
index 0000000..277914c
--- /dev/null
@@ -0,0 +1,4096 @@
+/*****************************************************************************/
+
+/*
+ *     istallion.c  -- stallion intelligent multiport serial driver.
+ *
+ *     Copyright (C) 1994,1995  Greg Ungerer (gerg@stallion.oz.au).
+ *
+ *     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.
+ */
+
+/*****************************************************************************/
+
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/cdk.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+/*****************************************************************************/
+
+/*
+ *     Define different board types. Not all of the following board types
+ *     are supported by this driver. But I will use the standard "assigned"
+ *     board numbers. Currently supported boards are abbreviated as:
+ *     ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
+ *     STAL = Stallion.
+ */
+#define        BRD_UNKNOWN     0
+#define        BRD_STALLION    1
+#define        BRD_BRUMBY4     2
+#define        BRD_ONBOARD2    3
+#define        BRD_ONBOARD     4
+#define        BRD_BRUMBY8     5
+#define        BRD_BRUMBY16    6
+#define        BRD_ONBOARDE    7
+#define        BRD_ONBOARD32   9
+#define        BRD_ONBOARD2_32 10
+#define        BRD_ONBOARDRS   11
+#define        BRD_EASYIO      20
+#define        BRD_ECH         21
+#define        BRD_ECHMC       22
+#define        BRD_ECP         23
+#define BRD_ECPE       24
+#define        BRD_ECPMC       25
+#define        BRD_ECHPCI      26
+
+#define        BRD_BRUMBY      BRD_BRUMBY4
+
+/*
+ *     Define a configuration structure to hold the board configuration.
+ *     Need to set this up in the code (for now) with the boards that are
+ *     to be configured into the system. This is what needs to be modified
+ *     when adding/removing/modifying boards. Each line entry in the
+ *     stli_brdconf[] array is a board. Each line contains io/irq/memory
+ *     ranges for that board (as well as what type of board it is).
+ *     Some examples:
+ *             { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+ *     This line will configure an EasyConnection 8/64 at io address 2a0,
+ *     and shared memory address of cc000. Multiple EasyConnection 8/64
+ *     boards can share the same shared memory address space. No interrupt
+ *     is required for this board type.
+ *     Another example:
+ *             { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
+ *     This line will configure an ONboard (ISA type) at io address 240,
+ *     and shared memory address of d0000. Multiple ONboards can share
+ *     the same shared memory address space. No interrupt required.
+ *     Another example:
+ *             { BRD_BRUMBY4, 0x360, 0, 0xc8000, 0, 0 },
+ *     This line will configure a Brumby board (any number of ports!) at
+ *     io address 360 and shared memory address of c8000. All Brumby boards
+ *     configured into a system must have their own separate io and memory
+ *     addresses. No interrupt is required.
+ *     Another example:
+ *             { BRD_STALLION, 0x330, 0, 0xd0000, 0, 0 },
+ *     This line will configure an original Stallion board at io address 330
+ *     and shared memory address d0000 (this would only be valid for a "V4.0"
+ *     or Rev.O Stallion board). All Stallion boards configured into the
+ *     system must have their own separate io and memory addresses. No
+ *     interrupt is required.
+ */
+
+typedef struct {
+       int             brdtype;
+       int             ioaddr1;
+       int             ioaddr2;
+       unsigned long   memaddr;
+       int             irq;
+       int             irqtype;
+} stlconf_t;
+
+static stlconf_t       stli_brdconf[] = {
+       { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+};
+
+static int     stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t);
+
+/*
+ *     Code support is offered for boards to use the above 1Mb memory
+ *     ranges for those boards which support this (supported on the ONboard
+ *     and ECP-EI hardware). The following switch should be enabled. The only
+ *     catch is that the kernel functions required to do this are not
+ *     normally exported symbols, so you will have to do some extra work
+ *     for this to be used in the loadable module form of the driver.
+ *     Unfortunately this doesn't work either if you linke the driver into
+ *     the kernel, sincethe memory management code is not set up early
+ *     enough (before our initialization routine is run).
+ */
+#define        STLI_HIMEMORY   0
+
+#if STLI_HIMEMORY
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Define some important driver characteristics. Device major numbers
+ *     allocated as per Linux Device Registery.
+ */
+#ifndef        STL_SIOMEMMAJOR
+#define        STL_SIOMEMMAJOR         28
+#endif
+#ifndef        STL_SERIALMAJOR
+#define        STL_SERIALMAJOR         24
+#endif
+#ifndef        STL_CALLOUTMAJOR
+#define        STL_CALLOUTMAJOR        25
+#endif
+
+#define        STL_DRVTYPSERIAL        1
+#define        STL_DRVTYPCALLOUT       2
+
+#define        STL_MAXBRDS             4
+#define        STL_MAXPANELS           4
+#define        STL_MAXPORTS            64
+#define        STL_MAXCHANS            (STL_MAXPORTS + 1)
+#define        STL_MAXDEVS             (STL_MAXBRDS * STL_MAXPORTS)
+
+/*****************************************************************************/
+
+/*
+ *     Define our local driver identity first. Set up stuff to deal with
+ *     all the local structures required by a serial tty driver.
+ */
+static char    *stli_drvname = "Stallion Intelligent Multiport Serial Driver";
+static char    *stli_drvversion = "1.0.0";
+static char    *stli_serialname = "ttyE";
+static char    *stli_calloutname = "cue";
+
+static struct tty_driver       stli_serial;
+static struct tty_driver       stli_callout;
+static struct tty_struct       *stli_ttys[STL_MAXDEVS];
+static struct termios          *stli_termios[STL_MAXDEVS];
+static struct termios          *stli_termioslocked[STL_MAXDEVS];
+static int                     stli_refcount;
+
+/*
+ *     We will need to allocate a temporary write buffer for chars that
+ *     come direct from user space. The problem is that a copy from user
+ *     space might cause a page fault (typically on a system that is
+ *     swapping!). All ports will share one buffer - since if the system
+ *     is already swapping a shared buffer won't make things any worse.
+ */
+static char                    *stli_tmpwritebuf = (char *) NULL;
+static struct semaphore                stli_tmpwritesem = MUTEX;
+
+#define        STLI_TXBUFSIZE          4096
+
+/*
+ *     Use a fast local buffer for cooked characters. Typically a whole
+ *     bunch of cooked characters come in for a port, 1 at a time. So we
+ *     save those up into a local buffer, then write out the whole lot
+ *     with a large memcpy. Just use 1 buffer for all ports, since its
+ *     use it is only need for short periods of time by each port.
+ */
+static char                    *stli_txcookbuf = (char *) NULL;
+static int                     stli_txcooksize = 0;
+static int                     stli_txcookrealsize = 0;
+static struct tty_struct       *stli_txcooktty = (struct tty_struct *) NULL;
+
+/*
+ *     Define a local default termios struct. All ports will be created
+ *     with this termios initially. Basically all it defines is a raw port
+ *     at 9600 baud, 8 data bits, no parity, 1 stop bit.
+ */
+static struct termios          stli_deftermios = {
+       0,
+       0,
+       (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+       0,
+       0,
+       INIT_C_CC
+};
+
+/*
+ *     Memory allocation vars. These keep track of what memory allocation
+ *     we can currently use. They help deal with memory in a consistent
+ *     way, whether during init or run-time.
+ */
+static int     stli_meminited = 0;
+static long    stli_memend;
+
+/*****************************************************************************/
+
+/*
+ *     Define a set of structures to hold all the board/panel/port info
+ *     for our ports. These will be dynamically allocated as required at
+ *     driver initialization time.
+ */
+
+/*
+ *     Port and board structures to hold status info about each object.
+ *     The board structure contains pointers to structures for each port
+ *     connected to it. Panels are not distinguished here, since
+ *     communication with the slave board will always be on a per port
+ *     basis.
+ */
+typedef struct {
+       int                     portnr;
+       int                     panelnr;
+       int                     brdnr;
+       unsigned long           state;
+       int                     devnr;
+       int                     flags;
+       int                     baud_base;
+       int                     custom_divisor;
+       int                     close_delay;
+       int                     closing_wait;
+       int                     refcount;
+       int                     openwaitcnt;
+       int                     rc;
+       int                     argsize;
+       void                    *argp;
+       long                    session;
+       long                    pgrp;
+       unsigned int            rxmarkmsk;
+       struct tty_struct       *tty;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+       struct wait_queue       *raw_wait;
+       struct tq_struct        tqhangup;
+       struct termios          normaltermios;
+       struct termios          callouttermios;
+       asysigs_t               asig;
+       unsigned long           addr;
+       unsigned long           rxoffset;
+       unsigned long           txoffset;
+       unsigned int            rxsize;
+       unsigned int            txsize;
+       unsigned long           sigs;
+       unsigned char           reqbit;
+       unsigned char           portidx;
+       unsigned char           portbit;
+} stliport_t;
+
+/*
+ *     Use a structure of function pointers to do board level operations.
+ *     These include, enable/disable, paging shared memory, interrupting, etc.
+ */
+typedef struct stlbrd {
+       int             brdnr;
+       int             brdtype;
+       int             state;
+       int             nrpanels;
+       int             nrports;
+       int             nrdevs;
+       unsigned int    iobase;
+       void            *membase;
+       int             memsize;
+       int             pagesize;
+       int             hostoffset;
+       int             slaveoffset;
+       int             bitsize;
+       int             panels[STL_MAXPANELS];
+       void            (*init)(struct stlbrd *brdp);
+       void            (*enable)(struct stlbrd *brdp);
+       void            (*reenable)(struct stlbrd *brdp);
+       void            (*disable)(struct stlbrd *brdp);
+       char            *(*getmemptr)(struct stlbrd *brdp, unsigned long offset, int line);
+       void            (*intr)(struct stlbrd *brdp);
+       void            (*reset)(struct stlbrd *brdp);
+       stliport_t      *ports[STL_MAXPORTS];
+} stlibrd_t;
+
+static stlibrd_t       *stli_brds;
+
+static int             stli_shared = 0;
+
+/*
+ *     Per board state flags. Used with the state field of the board struct.
+ *     Not really much here... All we need to do is keep track of whether
+ *     the board has been detected, and whether it is actully running a slave
+ *     or not.
+ */
+#define        BST_FOUND       0x1
+#define        BST_STARTED     0x2
+
+/*
+ *     Define the set of port state flags. These are marked for internal
+ *     state purposes only, usually to do with the state of communications
+ *     with the slave. Most of them need to be updated atomically, so always
+ *     use the bit setting operations (unless protected by cli/sti).
+ */
+#define        ST_INITIALIZING 1
+#define        ST_OPENING      2
+#define        ST_CLOSING      3
+#define        ST_CMDING       4
+#define        ST_TXBUSY       5
+#define        ST_RXING        6
+#define        ST_DOFLUSHRX    7
+#define        ST_DOFLUSHTX    8
+#define        ST_DOSIGS       9
+#define        ST_RXSTOP       10
+#define        ST_GETSIGS      11
+
+/*
+ *     Define an array of board names as printable strings. Handy for
+ *     referencing boards when printing trace and stuff.
+ */
+static char    *stli_brdnames[] = {
+       "Unknown",
+       "Stallion",
+       "Brumby",
+       "ONboard-MC",
+       "ONboard",
+       "Brumby",
+       "Brumby",
+       "ONboard-EI",
+       (char *) NULL,
+       "ONboard",
+       "ONboard-MC",
+       "ONboard-MC",
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       "EasyIO",
+       "EC8/32-AT",
+       "EC8/32-MC",
+       "EC8/64-AT",
+       "EC8/64-EI",
+       "EC8/64-MC",
+       "EC8/32-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ *     Hardware configuration info for ECP boards. These defines apply
+ *     to the directly accessable io ports of the ECP. There is a set of
+ *     defines for each ECP board type, ISA, EISA and MCA.
+ */
+#define        ECP_IOSIZE      4
+#define        ECP_MEMSIZE     (128 * 1024)
+#define        ECP_ATPAGESIZE  (4 * 1024)
+#define        ECP_EIPAGESIZE  (64 * 1024)
+#define        ECP_MCPAGESIZE  (4 * 1024)
+
+/*
+ *     Important defines for the ISA class of ECP board.
+ */
+#define        ECP_ATIREG      0
+#define        ECP_ATCONFR     1
+#define        ECP_ATMEMAR     2
+#define        ECP_ATMEMPR     3
+#define        ECP_ATSTOP      0x1
+#define        ECP_ATINTENAB   0x10
+#define        ECP_ATENABLE    0x20
+#define        ECP_ATDISABLE   0x00
+#define        ECP_ATADDRMASK  0x3f000
+#define        ECP_ATADDRSHFT  12
+
+/*
+ *     Important defines for the EISA class of ECP board.
+ */
+#define        ECP_EIIREG      0
+#define        ECP_EIMEMARL    1
+#define        ECP_EICONFR     2
+#define        ECP_EIMEMARH    3
+#define        ECP_EIENABLE    0x1
+#define        ECP_EIDISABLE   0x0
+#define        ECP_EISTOP      0x4
+#define        ECP_EIEDGE      0x00
+#define        ECP_EILEVEL     0x80
+#define        ECP_EIADDRMASKL 0x00ff0000
+#define        ECP_EIADDRSHFTL 16
+#define        ECP_EIADDRMASKH 0xff000000
+#define        ECP_EIADDRSHFTH 24
+#define        ECP_EIBRDENAB   0xc84
+
+/*
+ *     Important defines for the Micro-channel class of ECP board.
+ *     (It has a lot in common with the ISA boards.)
+ */
+#define        ECP_MCIREG      0
+#define        ECP_MCCONFR     1
+#define        ECP_MCSTOP      0x20
+#define        ECP_MCENABLE    0x80
+#define        ECP_MCDISABLE   0x00
+
+/*
+ *     Hardware configuration info for ONboard and Brumby boards. These
+ *     defines apply to the directly accessable io ports of these boards.
+ */
+#define        ONB_IOSIZE      16
+#define        ONB_MEMSIZE     (64 * 1024)
+#define        ONB_ATPAGESIZE  (64 * 1024)
+#define        ONB_MCPAGESIZE  (64 * 1024)
+#define        ONB_EIMEMSIZE   (128 * 1024)
+#define        ONB_EIPAGESIZE  (64 * 1024)
+
+/*
+ *     Important defines for the ISA class of ONboard board.
+ */
+#define        ONB_ATIREG      0
+#define        ONB_ATMEMAR     1
+#define        ONB_ATCONFR     2
+#define        ONB_ATSTOP      0x4
+#define        ONB_ATENABLE    0x01
+#define        ONB_ATDISABLE   0x00
+#define        ONB_ATADDRMASK  0xff0000
+#define        ONB_ATADDRSHFT  16
+
+#if STLI_HIMEMORY
+#define        ONB_HIMEMENAB   0x02
+#else
+#define        ONB_HIMEMENAB   0
+#endif
+
+/*
+ *     Important defines for the EISA class of ONboard board.
+ */
+#define        ONB_EIIREG      0
+#define        ONB_EIMEMARL    1
+#define        ONB_EICONFR     2
+#define        ONB_EIMEMARH    3
+#define        ONB_EIENABLE    0x1
+#define        ONB_EIDISABLE   0x0
+#define        ONB_EISTOP      0x4
+#define        ONB_EIEDGE      0x00
+#define        ONB_EILEVEL     0x80
+#define        ONB_EIADDRMASKL 0x00ff0000
+#define        ONB_EIADDRSHFTL 16
+#define        ONB_EIADDRMASKH 0xff000000
+#define        ONB_EIADDRSHFTH 24
+#define        ONB_EIBRDENAB   0xc84
+
+/*
+ *     Important defines for the Brumby boards. They are pretty simple,
+ *     there is not much that is programmably configurable.
+ */
+#define        BBY_IOSIZE      16
+#define        BBY_MEMSIZE     (64 * 1024)
+#define        BBY_PAGESIZE    (16 * 1024)
+
+#define        BBY_ATIREG      0
+#define        BBY_ATCONFR     1
+#define        BBY_ATSTOP      0x4
+
+/*
+ *     Important defines for the Stallion boards. They are pretty simple,
+ *     there is not much that is programmably configurable.
+ */
+#define        STAL_IOSIZE     16
+#define        STAL_MEMSIZE    (64 * 1024)
+#define        STAL_PAGESIZE   (64 * 1024)
+
+/*
+ *     Define the set of status register values for EasyConnection panels.
+ *     The signature will return with the status value for each panel. From
+ *     this we can determine what is attached to the board - before we have
+ *     actually down loaded any code to it.
+ */
+#define        ECH_PNLSTATUS   2
+#define        ECH_PNL16PORT   0x20
+#define        ECH_PNLIDMASK   0x07
+#define        ECH_PNLINTRPEND 0x80
+
+/*
+ *     Define some macros to do things to the board. Even those these boards
+ *     are somewhat related there is often significantly different ways of
+ *     doing some operation on it (like enable, paging, reset, etc). So each
+ *     board class has a set of functions which do the commonly required
+ *     operations. The macros below basically just call these functions,
+ *     generally checking for a NULL function - which means that the board
+ *     needs nothing done to it to achieve this operation!
+ */
+#define        EBRDINIT(brdp)                                          \
+       if (brdp->init != NULL)                                 \
+               (* brdp->init)(brdp)
+
+#define        EBRDENABLE(brdp)                                        \
+       if (brdp->enable != NULL)                               \
+               (* brdp->enable)(brdp);
+
+#define        EBRDDISABLE(brdp)                                       \
+       if (brdp->disable != NULL)                              \
+               (* brdp->disable)(brdp);
+
+#define        EBRDINTR(brdp)                                          \
+       if (brdp->intr != NULL)                                 \
+               (* brdp->intr)(brdp);
+
+#define        EBRDRESET(brdp)                                         \
+       if (brdp->reset != NULL)                                \
+               (* brdp->reset)(brdp);
+
+#define        EBRDGETMEMPTR(brdp,offset)                              \
+       (* brdp->getmemptr)(brdp, offset, __LINE__)
+
+/*
+ *     Define the maximal baud rate, and he default baud base for ports.
+ */
+#define        STL_MAXBAUD     230400
+#define        STL_BAUDBASE    115200
+#define        STL_CLOSEDELAY  50
+
+/*****************************************************************************/
+
+/*
+ *     Define macros to extract a brd or port number from a minor number.
+ */
+#define        MKDEV2BRD(min)          (((min) & 0xc0) >> 6)
+#define        MKDEV2PORT(min)         ((min) & 0x3f)
+
+/*
+ *     Define a baud rate table that converts termios baud rate selector
+ *     into the actual baud rate value. All baud rate calculations are based
+ *     on the actual baud rate required.
+ */
+static unsigned int    stli_baudrates[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400
+};
+
+/*****************************************************************************/
+
+/*
+ *     Define some handy local macros...
+ */
+#define        MIN(a,b)                (((a) <= (b)) ? (a) : (b))
+
+/*****************************************************************************/
+
+/*
+ *     Prototype all functions in this driver!
+ */
+
+#ifdef MODULE
+int            init_module(void);
+void           cleanup_module(void);
+#else
+static void    stli_meminit(long base);
+static long    stli_memhalt(void);
+#endif
+static void    *stli_memalloc(int len);
+
+long           stli_init(long kmem_start);
+static int     stli_open(struct tty_struct *tty, struct file *filp);
+static void    stli_close(struct tty_struct *tty, struct file *filp);
+static int     stli_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static void    stli_putchar(struct tty_struct *tty, unsigned char ch);
+static void    stli_flushchars(struct tty_struct *tty);
+static int     stli_writeroom(struct tty_struct *tty);
+static int     stli_charsinbuffer(struct tty_struct *tty);
+static int     stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void    stli_settermios(struct tty_struct *tty, struct termios *old);
+static void    stli_throttle(struct tty_struct *tty);
+static void    stli_unthrottle(struct tty_struct *tty);
+static void    stli_stop(struct tty_struct *tty);
+static void    stli_start(struct tty_struct *tty);
+static void    stli_flushbuffer(struct tty_struct *tty);
+static void    stli_hangup(struct tty_struct *tty);
+
+static int     stli_brdinit(void);
+static int     stli_initecp(stlibrd_t *brdp, stlconf_t *confp);
+static int     stli_initonb(stlibrd_t *brdp, stlconf_t *confp);
+static int     stli_initports(stlibrd_t *brdp);
+static int     stli_startbrd(stlibrd_t *brdp);
+static int     stli_memread(struct inode *ip, struct file *fp, char *buf, int count);
+static int     stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count);
+static int     stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void    stli_poll(unsigned long arg);
+static int     stli_hostcmd(stlibrd_t *brdp, int channr);
+static int     stli_initopen(stlibrd_t *brdp, stliport_t *portp);
+static int     stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int     stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int     stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp);
+static void    stli_dohangup(void *arg);
+static void    stli_delay(int len);
+static int     stli_setport(stliport_t *portp);
+static int     stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void    stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void    stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp);
+static void    stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp);
+static void    stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
+static long    stli_mktiocm(unsigned long sigvalue);
+static void    stli_read(stlibrd_t *brdp, stliport_t *portp);
+static void    stli_getserial(stliport_t *portp, struct serial_struct *sp);
+static int     stli_setserial(stliport_t *portp, struct serial_struct *sp);
+
+static void    stli_ecpinit(stlibrd_t *brdp);
+static void    stli_ecpenable(stlibrd_t *brdp);
+static void    stli_ecpdisable(stlibrd_t *brdp);
+static char    *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_ecpreset(stlibrd_t *brdp);
+static void    stli_ecpintr(stlibrd_t *brdp);
+static void    stli_ecpeiinit(stlibrd_t *brdp);
+static void    stli_ecpeienable(stlibrd_t *brdp);
+static void    stli_ecpeidisable(stlibrd_t *brdp);
+static char    *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_ecpeireset(stlibrd_t *brdp);
+static void    stli_ecpmcenable(stlibrd_t *brdp);
+static void    stli_ecpmcdisable(stlibrd_t *brdp);
+static char    *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_ecpmcreset(stlibrd_t *brdp);
+
+static void    stli_onbinit(stlibrd_t *brdp);
+static void    stli_onbenable(stlibrd_t *brdp);
+static void    stli_onbdisable(stlibrd_t *brdp);
+static char    *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_onbreset(stlibrd_t *brdp);
+static void    stli_onbeinit(stlibrd_t *brdp);
+static void    stli_onbeenable(stlibrd_t *brdp);
+static void    stli_onbedisable(stlibrd_t *brdp);
+static char    *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_onbereset(stlibrd_t *brdp);
+static void    stli_bbyinit(stlibrd_t *brdp);
+static char    *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_bbyreset(stlibrd_t *brdp);
+static void    stli_stalinit(stlibrd_t *brdp);
+static char    *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void    stli_stalreset(stlibrd_t *brdp);
+
+#if STLI_HIMEMORY
+static void *stli_mapbrdmem(unsigned long physaddr, unsigned int size);
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Define the driver info for a user level shared memory device. This
+ *     device will work sort of like the /dev/kmem device - except that it
+ *     will give access to the shared memory on the Stallion intelligent
+ *     board. This is also a very useful debugging tool.
+ */
+static struct file_operations  stli_fsiomem = {
+       NULL,
+       stli_memread,
+       stli_memwrite,
+       NULL,
+       NULL,
+       stli_memioctl,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
+/*****************************************************************************/
+
+/*
+ *     Define a timer_list entry for our poll routine. The slave board
+ *     is polled every so often to see if anything needs doing. This is
+ *     much cheaper on host cpu than using interrupts. It turns out to
+ *     not increase character latency by much either...
+ */
+static struct timer_list       stli_timerlist = {
+       NULL, NULL, 0, 0, stli_poll
+};
+
+static int     stli_timeron = 0;
+
+/*
+ *     This is hack to allow for the kernel changes made to add_timer
+ *     in the newer 1.3.X kernels (changed around 1.3.1X).
+ */
+#ifdef LINUX_1_2_X_COMPAT
+#define        STLI_TIMEOUT    0
+#else
+#define        STLI_TIMEOUT    (jiffies + 1)
+#endif
+
+/*****************************************************************************/
+
+#ifdef MODULE
+
+/*
+ *     Include kernel version number for modules.
+ */
+char   kernel_version[] = UTS_RELEASE;
+
+int init_module()
+{
+       unsigned long   flags;
+
+#if DEBUG
+       printk("init_module()\n");
+#endif
+
+       save_flags(flags);
+       cli();
+       stli_init(0);
+       restore_flags(flags);
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+void cleanup_module()
+{
+       stlibrd_t       *brdp;
+       stliport_t      *portp;
+       unsigned long   flags;
+       int             i, j;
+
+#if DEBUG
+       printk("cleanup_module()\n");
+#endif
+
+       printk("Unloading %s: version %s\n", stli_drvname, stli_drvversion);
+
+       save_flags(flags);
+       cli();
+
+/*
+ *     Free up all allocated resources used by the ports. This includes
+ *     memory and interrupts.
+ */
+       if (stli_timeron) {
+               stli_timeron = 0;
+               del_timer(&stli_timerlist);
+       }
+
+       i = tty_unregister_driver(&stli_serial);
+       j = tty_unregister_driver(&stli_callout);
+       if (i || j) {
+               printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+               restore_flags(flags);
+               return;
+       }
+       if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+               printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+
+       if (stli_tmpwritebuf != (char *) NULL)
+               kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
+       if (stli_txcookbuf != (char *) NULL)
+               kfree_s(stli_txcookbuf, STLI_TXBUFSIZE);
+
+       for (i = 0; (i < stli_nrbrds); i++) {
+               brdp = &stli_brds[i];
+               for (j = 0; (j < STL_MAXPORTS); j++) {
+                       portp = brdp->ports[j];
+                       if (portp != (stliport_t *) NULL) {
+                               if (portp->tty != (struct tty_struct *) NULL)
+                                       tty_hangup(portp->tty);
+                               kfree_s(portp, sizeof(stliport_t));
+                       }
+               }
+
+#if STLI_HIMEMORY
+               if (((unsigned long) brdp->membase) >= 0x100000)
+                       vfree(brdp->membase);
+#endif
+               if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC))
+                       release_region(brdp->iobase, ECP_IOSIZE);
+               else
+                       release_region(brdp->iobase, ONB_IOSIZE);
+       }
+       kfree_s(stli_brds, (sizeof(stlibrd_t) * stli_nrbrds));
+
+       restore_flags(flags);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Local memory allocation routines. These are used so we can deal with
+ *     memory allocation at init time and during run-time in a consistent
+ *     way. Everbody just calls the stli_memalloc routine to allocate
+ *     memory and it will do the right thing. There is no common memory
+ *     deallocation code - since this is only done is special cases, all of
+ *     which are tightly controlled.
+ */
+
+#ifndef MODULE
+
+static void stli_meminit(long base)
+{
+       stli_memend = base;
+       stli_meminited = 1;
+}
+
+static long stli_memhalt()
+{
+       stli_meminited = 0;
+       return(stli_memend);
+}
+
+#endif
+
+static void *stli_memalloc(int len)
+{
+       void    *mem;
+
+       if (stli_meminited) {
+               mem = (void *) stli_memend;
+               stli_memend += len;
+       } else {
+               mem = (void *) kmalloc(len, GFP_KERNEL);
+       }
+       return(mem);
+}
+
+/*****************************************************************************/
+
+static int stli_open(struct tty_struct *tty, struct file *filp)
+{
+       stlibrd_t       *brdp;
+       stliport_t      *portp;
+       unsigned int    minordev;
+       int             brdnr, portnr, rc;
+
+#if DEBUG
+       printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+#endif
+
+       minordev = MINOR(tty->device);
+       brdnr = MKDEV2BRD(minordev);
+       if (brdnr >= stli_nrbrds)
+               return(-ENODEV);
+       if (stli_brds == (stlibrd_t *) NULL)
+               return(-ENODEV);
+       brdp = &stli_brds[brdnr];
+       if ((brdp->state & BST_STARTED) == 0)
+               return(-ENODEV);
+       portnr = MKDEV2PORT(minordev);
+       if ((portnr < 0) || (portnr > brdp->nrports))
+               return(-ENODEV);
+
+       portp = brdp->ports[portnr];
+       if (portp == (stliport_t *) NULL)
+               return(-ENODEV);
+       if (portp->devnr < 1)
+               return(-ENODEV);
+
+/*
+ *     Check if this port is in the middle of closing. If so then wait
+ *     until it is closed then return error status based on flag settings.
+ *     The sleep here does not need interrupt protection since the wakeup
+ *     for it is done with the same context.
+ */
+       if (portp->flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&portp->close_wait);
+               if (portp->flags & ASYNC_HUP_NOTIFY)
+                       return(-EAGAIN);
+               return(-ERESTARTSYS);
+       }
+
+/*
+ *     On the first open of the device setup the port hardware, and
+ *     initialize the per port data structure. Since initializing the port
+ *     requires serval commands to the board we will need to wait for any
+ *     other open that is already initializing the port.
+ */
+       portp->tty = tty;
+       tty->driver_data = portp;
+       portp->refcount++;
+
+       while (test_bit(ST_INITIALIZING, &portp->state)) {
+               if (current->signal & ~current->blocked)
+                       return(-ERESTARTSYS);
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+
+       if ((portp->flags & ASYNC_INITIALIZED) == 0) {
+               set_bit(ST_INITIALIZING, &portp->state);
+               if ((rc = stli_initopen(brdp, portp)) >= 0) {
+                       portp->flags |= ASYNC_INITIALIZED;
+                       clear_bit(TTY_IO_ERROR, &tty->flags);
+               }
+               clear_bit(ST_INITIALIZING, &portp->state);
+               wake_up_interruptible(&portp->open_wait);
+               if (rc < 0)
+                       return(rc);
+       }
+
+/*
+ *     Check if this port is in the middle of closing. If so then wait
+ *     until it is closed then return error status, based on flag settings.
+ *     The sleep here does not need interrupt protection since the wakeup
+ *     for it is done with the same context.
+ */
+       if (portp->flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&portp->close_wait);
+               if (portp->flags & ASYNC_HUP_NOTIFY)
+                       return(-EAGAIN);
+               return(-ERESTARTSYS);
+       }
+
+/*
+ *     Based on type of open being done check if it can overlap with any
+ *     previous opens still in effect. If we are a normal serial device
+ *     then also we might have to wait for carrier.
+ */
+       if (tty->driver.subtype == STL_DRVTYPCALLOUT) {
+               if (portp->flags & ASYNC_NORMAL_ACTIVE)
+                       return(-EBUSY);
+               if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
+                       if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
+                                       (portp->session != current->session))
+                               return(-EBUSY);
+                       if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
+                                       (portp->pgrp != current->pgrp))
+                               return(-EBUSY);
+               }
+               portp->flags |= ASYNC_CALLOUT_ACTIVE;
+       } else {
+               if (filp->f_flags & O_NONBLOCK) {
+                       if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+                               return(-EBUSY);
+               } else {
+                       if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0)
+                               return(rc);
+               }
+               portp->flags |= ASYNC_NORMAL_ACTIVE;
+       }
+
+       if ((portp->refcount == 1) && (portp->flags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == STL_DRVTYPSERIAL)
+                       *tty->termios = portp->normaltermios;
+               else
+                       *tty->termios = portp->callouttermios;
+               stli_setport(portp);
+       }
+
+       portp->session = current->session;
+       portp->pgrp = current->pgrp;
+       return(0);
+}
+
+/*****************************************************************************/
+
+static void stli_close(struct tty_struct *tty, struct file *filp)
+{
+       stlibrd_t       *brdp;
+       stliport_t      *portp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stli_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
+#endif
+
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+
+       save_flags(flags);
+       cli();
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               return;
+       }
+       if (portp->refcount-- > 1) {
+               restore_flags(flags);
+               return;
+       }
+
+       portp->flags |= ASYNC_CLOSING;
+
+       if (portp->flags & ASYNC_NORMAL_ACTIVE)
+               portp->normaltermios = *tty->termios;
+       if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+               portp->callouttermios = *tty->termios;
+
+/*
+ *     May want to wait for data to drain before closing. The BUSY flag
+ *     keeps track of whether we are still transmitting or not. It is
+ *     updated by messages from the slave - indicating when all chars
+ *     really have drained.
+ */
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       tty->closing = 1;
+       if (test_bit(ST_TXBUSY, &portp->state)) {
+               if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+                       tty_wait_until_sent(tty, portp->closing_wait);
+       }
+
+       portp->flags &= ~ASYNC_INITIALIZED;
+       brdp = &stli_brds[portp->brdnr];
+       stli_rawclose(brdp, portp, 0, 1);
+       if (tty->termios->c_cflag & HUPCL) {
+               stli_mkasysigs(&portp->asig, 0, 0);
+               stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+       }
+       clear_bit(ST_TXBUSY, &portp->state);
+       clear_bit(ST_RXSTOP, &portp->state);
+       set_bit(TTY_IO_ERROR, &tty->flags);
+       if (tty->ldisc.flush_buffer)
+               (tty->ldisc.flush_buffer)(tty);
+       set_bit(ST_DOFLUSHRX, &portp->state);
+       stli_flushbuffer(tty);
+
+       tty->closing = 0;
+       tty->driver_data = (void *) NULL;
+       portp->tty = (struct tty_struct *) NULL;
+
+       if (portp->openwaitcnt) {
+               if (portp->close_delay)
+                       stli_delay(portp->close_delay);
+               wake_up_interruptible(&portp->open_wait);
+       }
+
+       portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+       wake_up_interruptible(&portp->close_wait);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Carry out first open operations on a port. This involves a number of
+ *     commands to be sent to the slave. We need to open the port, set the
+ *     notification events, set the initial port settings, get and set the
+ *     initial signal values. We sleep and wait in between each one. But
+ *     this still all happens pretty quickly.
+ */
+
+static int stli_initopen(stlibrd_t *brdp, stliport_t *portp)
+{
+       struct tty_struct       *tty;
+       asynotify_t             nt;
+       asyport_t               aport;
+       int                     rc;
+
+#if DEBUG
+       printk("stli_initopen(brdp=%x,portp=%x)\n", (int) brdp, (int) portp);
+#endif
+
+       if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
+               return(rc);
+
+       memset(&nt, 0, sizeof(asynotify_t));
+       nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
+       nt.signal = SG_DCD;
+       if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0)
+               return(rc);
+
+       tty = portp->tty;
+       if (tty == (struct tty_struct *) NULL)
+               return(-ENODEV);
+       stli_mkasyport(portp, &aport, tty->termios);
+       if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0)
+               return(rc);
+
+       set_bit(ST_GETSIGS, &portp->state);
+       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+               return(rc);
+       if (clear_bit(ST_GETSIGS, &portp->state))
+               portp->sigs = stli_mktiocm(portp->asig.sigvalue);
+       stli_mkasysigs(&portp->asig, 1, 1);
+       if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+               return(rc);
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send an open message to the slave. This will sleep waiting for the
+ *     acknowledgement, so must have user context. We need to co-ordinate
+ *     with close events here, since we don't want open and close events
+ *     to overlap.
+ */
+
+static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
+{
+       volatile cdkhdr_t       *hdrp;
+       volatile cdkctrl_t      *cp;
+       volatile unsigned char  *bits;
+       unsigned long           flags;
+       int                     rc;
+
+#if DEBUG
+       printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+#endif
+
+/*
+ *     Send a message to the slave to open this port.
+ */
+       save_flags(flags);
+       cli();
+
+/*
+ *     Slave is already closing this port. This can happen if a hangup
+ *     occurs on this port. So we must wait until it is complete. The
+ *     order of opens and closes may not be preserved across shared
+ *     memory, so we must wait until it is complete.
+ */
+       while (test_bit(ST_CLOSING, &portp->state)) {
+               if (current->signal & ~current->blocked) {
+                       restore_flags(flags);
+                       return(-ERESTARTSYS);
+               }
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+
+/*
+ *     Everything is ready now, so write the open message into shared
+ *     memory. Once the message is in set the service bits to say that
+ *     this port wants service.
+ */
+       EBRDENABLE(brdp);
+       cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       cp->openarg = arg;
+       cp->open = 1;
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       hdrp->slavereq |= portp->reqbit;
+       bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+       *bits |= portp->portbit;
+       EBRDDISABLE(brdp);
+
+       if (wait == 0) {
+               restore_flags(flags);
+               return(0);
+       }
+
+/*
+ *     Slave is in action, so now we must wait for the open acknowledgment
+ *     to come back.
+ */
+       rc = 0;
+       set_bit(ST_OPENING, &portp->state);
+       while (test_bit(ST_OPENING, &portp->state)) {
+               if (current->signal & ~current->blocked) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+       restore_flags(flags);
+
+       if ((rc == 0) && (portp->rc != 0))
+               rc = -EIO;
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a close message to the slave. Normally this will sleep waiting
+ *     for the acknowledgement, but if wait parameter is 0 it will not. If
+ *     wait is true then must have user context (to sleep).
+ */
+
+static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
+{
+       volatile cdkhdr_t       *hdrp;
+       volatile cdkctrl_t      *cp;
+       volatile unsigned char  *bits;
+       unsigned long           flags;
+       int                     rc;
+
+#if DEBUG
+       printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+#endif
+
+       save_flags(flags);
+       cli();
+
+/*
+ *     Slave is already closing this port. This can happen if a hangup
+ *     occurs on this port.
+ */
+       if (wait) {
+               while (test_bit(ST_CLOSING, &portp->state)) {
+                       if (current->signal & ~current->blocked) {
+                               restore_flags(flags);
+                               return(-ERESTARTSYS);
+                       }
+                       interruptible_sleep_on(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Write the close command into shared memory.
+ */
+       EBRDENABLE(brdp);
+       cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       cp->closearg = arg;
+       cp->close = 1;
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       hdrp->slavereq |= portp->reqbit;
+       bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+       *bits |= portp->portbit;
+       EBRDDISABLE(brdp);
+
+       set_bit(ST_CLOSING, &portp->state);
+       if (wait == 0) {
+               restore_flags(flags);
+               return(0);
+       }
+
+/*
+ *     Slave is in action, so now we must wait for the open acknowledgment
+ *     to come back.
+ */
+       rc = 0;
+       while (test_bit(ST_CLOSING, &portp->state)) {
+               if (current->signal & ~current->blocked) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+       restore_flags(flags);
+
+       if ((rc == 0) && (portp->rc != 0))
+               rc = -EIO;
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a command to the slave and wait for the response. This must
+ *     have user context (it sleeps). This routine is generic in that it
+ *     can send any type of command. Its purpose is to wait for that command
+ *     to complete (as opposed to initiating the command then returning).
+ */
+
+static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+#endif
+
+       save_flags(flags);
+       cli();
+       while (test_bit(ST_CMDING, &portp->state)) {
+               if (current->signal & ~current->blocked) {
+                       restore_flags(flags);
+                       return(-ERESTARTSYS);
+               }
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+
+       stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
+
+       while (test_bit(ST_CMDING, &portp->state)) {
+               if (current->signal & ~current->blocked) {
+                       restore_flags(flags);
+                       return(-ERESTARTSYS);
+               }
+               interruptible_sleep_on(&portp->raw_wait);
+       }
+       restore_flags(flags);
+
+       if (portp->rc != 0)
+               return(-EIO);
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send the termios settings for this port to the slave. This sleeps
+ *     waiting for the command to complete - so must have user context.
+ */
+
+static int stli_setport(stliport_t *portp)
+{
+       stlibrd_t       *brdp;
+       asyport_t       aport;
+
+#if DEBUG
+       printk("stli_setport(portp=%x)\n", (int) portp);
+#endif
+
+       if (portp == (stliport_t *) NULL)
+               return(-ENODEV);
+       if (portp->tty == (struct tty_struct *) NULL)
+               return(-ENODEV);
+       if ((portp->brdnr < 0) && (portp->brdnr >= stli_nrbrds))
+               return(-ENODEV);
+       brdp = &stli_brds[portp->brdnr];
+
+       stli_mkasyport(portp, &aport, portp->tty->termios);
+       return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Wait for a specified delay period, this is not a busy-loop. It will
+ *     give up the processor while waiting. Unfortunately this has some
+ *     rather intimate knowledge of the process management stuff.
+ */
+
+static void stli_delay(int len)
+{
+#if DEBUG
+       printk("stl_delay(len=%d)\n", len);
+#endif
+       if (len > 0) {
+               current->state = TASK_INTERRUPTIBLE;
+               current->timeout = jiffies + len;
+               schedule();
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Possibly need to wait for carrier (DCD signal) to come high. Say
+ *     maybe because if we are clocal then we don't need to wait...
+ */
+
+static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp)
+{
+       unsigned long   flags;
+       int             rc;
+
+#if DEBUG
+       printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp);
+#endif
+
+       rc = 0;
+
+       save_flags(flags);
+       cli();
+       portp->openwaitcnt++;
+       if (portp->refcount > 0)
+               portp->refcount--;
+
+       for (;;) {
+               if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) {
+                       stli_mkasysigs(&portp->asig, 1, 1);
+                       if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+                               break;
+               }
+               if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+                       if (portp->flags & ASYNC_HUP_NOTIFY)
+                               rc = -EBUSY;
+                       else
+                               rc = -ERESTARTSYS;
+                       break;
+               }
+               if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
+                               ((portp->flags & ASYNC_CLOSING) == 0) &&
+                               ((portp->tty->termios->c_cflag & CLOCAL) ||
+                               (portp->sigs & TIOCM_CD))) {
+                       break;
+               }
+               if (current->signal & ~current->blocked) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+               interruptible_sleep_on(&portp->open_wait);
+       }
+
+       if (! tty_hung_up_p(filp))
+               portp->refcount++;
+       portp->openwaitcnt--;
+       restore_flags(flags);
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Write routine. Take the data and put it in the shared memory ring
+ *     queue. If port is not already sending chars then need to mark the
+ *     service bits for this port.
+ */
+
+static int stli_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+       volatile cdkasy_t       *ap;
+       volatile cdkhdr_t       *hdrp;
+       volatile unsigned char  *bits;
+       unsigned char           *shbuf, *chbuf;
+       stliport_t              *portp;
+       stlibrd_t               *brdp;
+       unsigned int            len, stlen, head, tail, size;
+       unsigned long           flags;
+
+#if DEBUG
+       printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+#endif
+
+       if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL))
+               return(0);
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return(0);
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return(0);
+       brdp = &stli_brds[portp->brdnr];
+       chbuf = (unsigned char *) buf;
+
+/*
+ *     If copying direct from user space we need to be able to handle page
+ *     faults while we are copying. To do this copy as much as we can now
+ *     into a kernel buffer. From there we copy it into shared memory. The
+ *     big problem is that we do not want shared memory enabled when we are
+ *     sleeping (other boards may be serviced while asleep). Something else
+ *     to note here is the reading of the tail twice. Since the boards
+ *     shared memory can be on an 8-bit bus then we need to be very carefull
+ *     reading 16 bit quantities - since both the board (slave) and host
+ *     cound be writing and reading at the same time.
+ */
+       if (from_user) {
+               save_flags(flags);
+               cli();
+               EBRDENABLE(brdp);
+               ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+               head = (unsigned int) ap->txq.head;
+               tail = (unsigned int) ap->txq.tail;
+               if (tail != ((unsigned int) ap->txq.tail))
+                       tail = (unsigned int) ap->txq.tail;
+               len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1);
+               count = MIN(len, count);
+               EBRDDISABLE(brdp);
+
+               down(&stli_tmpwritesem);
+               memcpy_fromfs(stli_tmpwritebuf, chbuf, count);
+               up(&stli_tmpwritesem);
+               chbuf = &stli_tmpwritebuf[0];
+               restore_flags(flags);
+       }
+
+/*
+ *     All data is now local, shove as much as possible into shared memory.
+ */
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+       head = (unsigned int) ap->txq.head;
+       tail = (unsigned int) ap->txq.tail;
+       if (tail != ((unsigned int) ap->txq.tail))
+               tail = (unsigned int) ap->txq.tail;
+       size = portp->txsize;
+       if (head >= tail) {
+               len = size - (head - tail) - 1;
+               stlen = size - head;
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = MIN(len, count);
+       count = 0;
+       shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
+
+       while (len > 0) {
+               stlen = MIN(len, stlen);
+               memcpy((shbuf + head), chbuf, stlen);
+               chbuf += stlen;
+               len -= stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= size) {
+                       head = 0;
+                       stlen = tail;
+               }
+       }
+
+       ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+       ap->txq.head = head;
+       if (test_bit(ST_TXBUSY, &portp->state)) {
+               if (ap->changed.data & DT_TXEMPTY)
+                       ap->changed.data &= ~DT_TXEMPTY;
+       }
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       hdrp->slavereq |= portp->reqbit;
+       bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+       *bits |= portp->portbit;
+       set_bit(ST_TXBUSY, &portp->state);
+
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Output a single character. We put it into a temporary local buffer
+ *     (for speed) then write out that buffer when the flushchars routine
+ *     is called. There is a safety catch here so that if some other port
+ *     writes chars before the current buffer has been, then we write them
+ *     first them do the new ports.
+ */
+
+static void stli_putchar(struct tty_struct *tty, unsigned char ch)
+{
+#if DEBUG
+       printk("stli_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       if (tty != stli_txcooktty) {
+               if (stli_txcooktty != (struct tty_struct *) NULL)
+                       stli_flushchars(stli_txcooktty);
+               stli_txcooktty = tty;
+       }
+
+       stli_txcookbuf[stli_txcooksize++] = ch;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Transfer characters from the local TX cooking buffer to the board.
+ *     We sort of ignore the tty that gets passed in here. We rely on the
+ *     info stored with the TX cook buffer to tell us which port to flush
+ *     the data on. In any case we clean out the TX cook buffer, for re-use
+ *     by someone else.
+ */
+
+static void stli_flushchars(struct tty_struct *tty)
+{
+       volatile cdkhdr_t       *hdrp;
+       volatile unsigned char  *bits;
+       volatile cdkasy_t       *ap;
+       struct tty_struct       *cooktty;
+       stliport_t              *portp;
+       stlibrd_t               *brdp;
+       unsigned int            len, stlen, head, tail, size, count, cooksize;
+       unsigned char           *buf, *shbuf;
+       unsigned long           flags;
+
+#if DEBUG
+       printk("stli_flushchars(tty=%x)\n", (int) tty);
+#endif
+
+       cooksize = stli_txcooksize;
+       cooktty = stli_txcooktty;
+       stli_txcooksize = 0;
+       stli_txcookrealsize = 0;
+       stli_txcooktty = (struct tty_struct *) NULL;
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       if (cooktty == (struct tty_struct *) NULL)
+               return;
+       if (tty != cooktty)
+               tty = cooktty;
+       if (cooksize == 0)
+               return;
+
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+
+       ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+       head = (unsigned int) ap->txq.head;
+       tail = (unsigned int) ap->txq.tail;
+       if (tail != ((unsigned int) ap->txq.tail))
+               tail = (unsigned int) ap->txq.tail;
+       size = portp->txsize;
+       if (head >= tail) {
+               len = size - (head - tail) - 1;
+               stlen = size - head;
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = MIN(len, cooksize);
+       count = 0;
+       shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
+       buf = stli_txcookbuf;
+
+       while (len > 0) {
+               stlen = MIN(len, stlen);
+               memcpy((shbuf + head), buf, stlen);
+               buf += stlen;
+               len -= stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= size) {
+                       head = 0;
+                       stlen = tail;
+               }
+       }
+
+       ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+       ap->txq.head = head;
+
+       if (test_bit(ST_TXBUSY, &portp->state)) {
+               if (ap->changed.data & DT_TXEMPTY)
+                       ap->changed.data &= ~DT_TXEMPTY;
+       }
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       hdrp->slavereq |= portp->reqbit;
+       bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+       *bits |= portp->portbit;
+       set_bit(ST_TXBUSY, &portp->state);
+
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static int stli_writeroom(struct tty_struct *tty)
+{
+       volatile cdkasyrq_t     *rp;
+       stliport_t              *portp;
+       stlibrd_t               *brdp;
+       unsigned int            head, tail, len;
+       unsigned long           flags;
+
+#if DEBUG
+       printk("stli_writeroom(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(0);
+       if (tty == stli_txcooktty) {
+               if (stli_txcookrealsize != 0) {
+                       len = stli_txcookrealsize - stli_txcooksize;
+                       return(len);
+               }
+       }
+
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return(0);
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return(0);
+       brdp = &stli_brds[portp->brdnr];
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+       head = (unsigned int) rp->head;
+       tail = (unsigned int) rp->tail;
+       if (tail != ((unsigned int) rp->tail))
+               tail = (unsigned int) rp->tail;
+       len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head);
+       len--;
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       if (tty == stli_txcooktty) {
+               stli_txcookrealsize = len;
+               len -= stli_txcooksize;
+       }
+       return(len);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the number of characters in the transmit buffer. Normally we
+ *     will return the number of chars in the shared memory ring queue.
+ *     We need to kludge around the case where the shared memory buffer is
+ *     empty but not all characters have drained yet, for this case just
+ *     return that there is 1 character in the buffer!
+ */
+
+static int stli_charsinbuffer(struct tty_struct *tty)
+{
+       volatile cdkasyrq_t     *rp;
+       stliport_t              *portp;
+       stlibrd_t               *brdp;
+       unsigned int            head, tail, len;
+       unsigned long           flags;
+
+#if DEBUG
+       printk("stli_charsinbuffer(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(0);
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return(0);
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return(0);
+       brdp = &stli_brds[portp->brdnr];
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+       head = (unsigned int) rp->head;
+       tail = (unsigned int) rp->tail;
+       if (tail != ((unsigned int) rp->tail))
+               tail = (unsigned int) rp->tail;
+       len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head));
+       if ((len == 0) && test_bit(ST_TXBUSY, &portp->state))
+               len = 1;
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       return(len);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Generate the serial struct info.
+ */
+
+static void stli_getserial(stliport_t *portp, struct serial_struct *sp)
+{
+       struct serial_struct    sio;
+
+#if DEBUG
+       printk("stli_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+       memset(&sio, 0, sizeof(struct serial_struct));
+       sio.type = PORT_UNKNOWN;
+       sio.line = portp->portnr;
+       sio.port = stli_brdconf[portp->brdnr].ioaddr1;
+       sio.irq = stli_brdconf[portp->brdnr].irq;
+       sio.flags = portp->flags;
+       sio.baud_base = portp->baud_base;
+       sio.close_delay = portp->close_delay;
+       sio.closing_wait = portp->closing_wait;
+       sio.custom_divisor = portp->custom_divisor;
+       sio.xmit_fifo_size = 0;
+       sio.hub6 = 0;
+       memcpy_tofs(sp, &sio, sizeof(struct serial_struct));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set port according to the serial struct info.
+ *     At this point we do not do any auto-configure stuff, so we will
+ *     just quietly ignore any requests to change irq, etc.
+ */
+
+static int stli_setserial(stliport_t *portp, struct serial_struct *sp)
+{
+       struct serial_struct    sio;
+       int                     rc;
+
+#if DEBUG
+       printk("stli_setserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+       memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
+       if (!suser()) {
+               if ((sio.baud_base != portp->baud_base) ||
+                               (sio.close_delay != portp->close_delay) ||
+                               ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+                       return(-EPERM);
+       } 
+
+       portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+       portp->baud_base = sio.baud_base;
+       portp->close_delay = sio.close_delay;
+       portp->closing_wait = sio.closing_wait;
+       portp->custom_divisor = sio.custom_divisor;
+
+       if ((rc = stli_setport(portp)) < 0)
+               return(rc);
+       return(0);
+}
+
+/*****************************************************************************/
+
+static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       stliport_t      *portp;
+       stlibrd_t       *brdp;
+       unsigned long   val;
+       int             rc;
+
+#if DEBUG
+       printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(-ENODEV);
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return(-ENODEV);
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return(0);
+       brdp = &stli_brds[portp->brdnr];
+
+       rc = 0;
+
+       switch (cmd) {
+       case TCSBRK:
+               if ((rc = tty_check_change(tty)) == 0) {
+                       tty_wait_until_sent(tty, 0);
+                       if (! arg) {
+                               val = 250;
+                               rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+                       }
+               }
+               break;
+       case TCSBRKP:
+               if ((rc = tty_check_change(tty)) == 0) {
+                       tty_wait_until_sent(tty, 0);
+                       val = (arg ? (arg * 100) : 250);
+                       rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+               }
+               break;
+       case TIOCGSOFTCAR:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
+                       put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+               break;
+       case TIOCSSOFTCAR:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+               }
+               break;
+       case TIOCMGET:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+                       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+                               return(rc);
+                       val = stli_mktiocm(portp->asig.sigvalue);
+                       put_fs_long(val, (unsigned long *) arg);
+               }
+               break;
+       case TIOCMBIS:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
+                       rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+               }
+               break;
+       case TIOCMBIC:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
+                       rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+               }
+               break;
+       case TIOCMSET:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
+                       rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+               }
+               break;
+       case TIOCGSERIAL:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+                       stli_getserial(portp, (struct serial_struct *) arg);
+               break;
+       case TIOCSSERIAL:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+                       rc = stli_setserial(portp, (struct serial_struct *) arg);
+               break;
+       case TIOCSERCONFIG:
+       case TIOCSERGWILD:
+       case TIOCSERSWILD:
+       case TIOCSERGETLSR:
+       case TIOCSERGSTRUCT:
+       case TIOCSERGETMULTI:
+       case TIOCSERSETMULTI:
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     This routine assumes that we have user context and can sleep.
+ *     Looks like it is true for the current ttys implementation..!!
+ */
+
+static void stli_settermios(struct tty_struct *tty, struct termios *old)
+{
+       stliport_t      *portp;
+       stlibrd_t       *brdp;
+       struct termios  *tiosp;
+       asyport_t       aport;
+
+#if DEBUG
+       printk("stli_settermios(tty=%x,old=%x)\n", (int) tty, (int) old);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       tiosp = tty->termios;
+       if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+               return;
+
+       stli_mkasyport(portp, &aport, tiosp);
+       stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
+       stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
+       stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
+               tty->hw_stopped = 0;
+       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+               wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Attempt to flow control who ever is sending us data. We won't really
+ *     do any flow control action here. We can't directly, and even if we
+ *     wanted to we would have to send a command to the slave. The slave
+ *     knows how to flow control, and will do so when its buffers reach its
+ *     internal high water marks. So what we will do is set a local state
+ *     bit that will stop us sending any RX data up from the poll routine
+ *     (which is the place where RX data from the slave is handled).
+ */
+
+static void stli_throttle(struct tty_struct *tty)
+{
+       stliport_t      *portp;
+
+#if DEBUG
+       printk("stli_throttle(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+
+       set_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Unflow control the device sending us data... That means that all
+ *     we have to do is clear the RXSTOP state bit. The next poll call
+ *     will then be able to pass the RX data back up.
+ */
+
+static void stli_unthrottle(struct tty_struct *tty)
+{
+       stliport_t      *portp;
+
+#if DEBUG
+       printk("stli_unthrottle(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+
+       clear_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Stop the transmitter. Basically to do this we will just turn TX
+ *     interrupts off.
+ */
+
+static void stli_stop(struct tty_struct *tty)
+{
+       stlibrd_t       *brdp;
+       stliport_t      *portp;
+       asyctrl_t       actrl;
+
+#if DEBUG
+       printk("stli_stop(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       memset(&actrl, 0, sizeof(asyctrl_t));
+       actrl.txctrl = CT_STOPFLOW;
+#if 0
+       stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+#endif
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start the transmitter again. Just turn TX interrupts back on.
+ */
+
+static void stli_start(struct tty_struct *tty)
+{
+       stliport_t      *portp;
+       stlibrd_t       *brdp;
+       asyctrl_t       actrl;
+
+#if DEBUG
+       printk("stli_start(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       memset(&actrl, 0, sizeof(asyctrl_t));
+       actrl.txctrl = CT_STARTFLOW;
+#if 0
+       stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+#endif
+}
+
+/*****************************************************************************/
+
+/*
+ *     Scheduler called hang up routine. This is called from the scheduler,
+ *     not direct from the driver "poll" routine. We can't call it there
+ *     since the real local hangup code will enable/disable the board and
+ *     other things that we can't do while handling the poll. Much easier
+ *     to deal with it some time later (don't really care when, hangups
+ *     aren't that time critical).
+ */
+
+static void stli_dohangup(void *arg)
+{
+       stliport_t      *portp;
+
+#if DEBUG
+       printk("stli_dohangup(portp=%x)\n", (int) arg);
+#endif
+
+       portp = (stliport_t *) arg;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if (portp->tty == (struct tty_struct *) NULL)
+               return;
+       tty_hangup(portp->tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Hangup this port. This is pretty much like closing the port, only
+ *     a little more brutal. No waiting for data to drain. Shutdown the
+ *     port and maybe drop signals. This is rather tricky really. We want
+ *     to close the port as well.
+ */
+
+static void stli_hangup(struct tty_struct *tty)
+{
+       stliport_t      *portp;
+       stlibrd_t       *brdp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stli_hangup(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       portp->flags &= ~ASYNC_INITIALIZED;
+
+       save_flags(flags);
+       cli();
+       if (! test_bit(ST_CLOSING, &portp->state))
+               stli_rawclose(brdp, portp, 0, 0);
+       if (tty->termios->c_cflag & HUPCL) {
+               stli_mkasysigs(&portp->asig, 0, 0);
+               if (test_bit(ST_CMDING, &portp->state)) {
+                       set_bit(ST_DOSIGS, &portp->state);
+                       set_bit(ST_DOFLUSHTX, &portp->state);
+                       set_bit(ST_DOFLUSHRX, &portp->state);
+               } else {
+                       stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0);
+               }
+       }
+       restore_flags(flags);
+
+       clear_bit(ST_TXBUSY, &portp->state);
+       clear_bit(ST_RXSTOP, &portp->state);
+       set_bit(TTY_IO_ERROR, &tty->flags);
+       tty->driver_data = (void *) NULL;
+       portp->tty = (struct tty_struct *) NULL;
+       portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+       portp->refcount = 0;
+       wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Flush characters from the lower buffer. We may not have user context
+ *     so we cannot sleep waiting for it to complete. Also we need to check
+ *     if there is chars for this port in the TX cook buffer, and flush them
+ *     as well.
+ */
+
+static void stli_flushbuffer(struct tty_struct *tty)
+{
+       stliport_t      *portp;
+       stlibrd_t       *brdp;
+       unsigned long   ftype, flags;
+
+#if DEBUG
+       printk("stli_flushbuffer(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stliport_t *) NULL)
+               return;
+       if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+               return;
+       brdp = &stli_brds[portp->brdnr];
+
+       save_flags(flags);
+       cli();
+       if (tty == stli_txcooktty) {
+               stli_txcooktty = (struct tty_struct *) NULL;
+               stli_txcooksize = 0;
+               stli_txcookrealsize = 0;
+       }
+       if (test_bit(ST_CMDING, &portp->state)) {
+               set_bit(ST_DOFLUSHTX, &portp->state);
+       } else {
+               ftype = FLUSHTX;
+               if (test_bit(ST_DOFLUSHRX, &portp->state)) {
+                       ftype |= FLUSHRX;
+                       clear_bit(ST_DOFLUSHRX, &portp->state);
+               }
+               stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 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);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Generic send command routine. This will send a message to the slave,
+ *     of the specified type with the specified argument. Must be very
+ *     carefull of data that will be copied out from shared memory -
+ *     containing command results. The command completion is all done from
+ *     a poll routine that does not have user coontext. Therefore you cannot
+ *     copy back directly into user space, or to the kernel stack. This
+ *     routine does not sleep, so can be called from anywhere.
+ */
+
+static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+       volatile cdkhdr_t       *hdrp;
+       volatile cdkctrl_t      *cp;
+       volatile unsigned char  *bits;
+       unsigned long           flags;
+
+#if DEBUG
+       printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+#endif
+
+       if (test_bit(ST_CMDING, &portp->state)) {
+               printk("STALLION: command already busy, cmd=%x!\n", (int) cmd);
+               return;
+       }
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       if (size > 0) {
+               memcpy((void *) &(cp->args[0]), arg, size);
+               if (copyback) {
+                       portp->argp = arg;
+                       portp->argsize = size;
+               }
+       }
+       cp->status = 0;
+       cp->cmd = cmd;
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       hdrp->slavereq |= portp->reqbit;
+       bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+       *bits |= portp->portbit;
+       set_bit(ST_CMDING, &portp->state);
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Read data from shared memory. This assumes that the shared memory
+ *     is enabled and that interrupts are off. Basically we just empty out
+ *     the shared memory buffer into the tty buffer. Must be carefull to
+ *     handle the case where we fill up the tty buffer, but still have
+ *     more chars to unload.
+ */
+
+static inline void stli_read(stlibrd_t *brdp, stliport_t *portp)
+{
+       volatile cdkasyrq_t     *rp;
+       volatile char           *shbuf;
+       struct tty_struct       *tty;
+       unsigned int            head, tail, size;
+       unsigned int            len, stlen;
+
+#if DEBUG
+       printk("stli_read(brdp=%x,portp=%d)\n", (int) brdp, (int) portp);
+#endif
+
+       if (test_bit(ST_RXSTOP, &portp->state))
+               return;
+       tty = portp->tty;
+       if (tty == (struct tty_struct *) NULL)
+               return;
+
+       rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+       head = (unsigned int) rp->head;
+       if (head != ((unsigned int) rp->head))
+               head = (unsigned int) rp->head;
+       tail = (unsigned int) rp->tail;
+       size = portp->rxsize;
+       if (head >= tail) {
+               len = head - tail;
+               stlen = len;
+       } else {
+               len = size - (tail - head);
+               stlen = size - tail;
+       }
+
+       len = MIN(len, (TTY_FLIPBUF_SIZE - tty->flip.count));
+       shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset);
+
+       while (len > 0) {
+               stlen = MIN(len, stlen);
+               memcpy(tty->flip.char_buf_ptr, (char *) (shbuf + tail), stlen);
+               memset(tty->flip.flag_buf_ptr, 0, stlen);
+               tty->flip.char_buf_ptr += stlen;
+               tty->flip.flag_buf_ptr += stlen;
+               tty->flip.count += stlen;
+
+               len -= stlen;
+               tail += stlen;
+               if (tail >= size) {
+                       tail = 0;
+                       stlen = head;
+               }
+       }
+       rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+       rp->tail = tail;
+
+       if (head != tail)
+               set_bit(ST_RXING, &portp->state);
+
+       tty_schedule_flip(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set up and carry out any delayed commands. There is only a small set
+ *     of slave commands that can be done "off-level". So it is not too
+ *     difficult to deal with them here.
+ */
+
+static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
+{
+       int     cmd;
+
+       if (test_bit(ST_DOSIGS, &portp->state)) {
+               if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state))
+                       cmd = A_SETSIGNALSF;
+               else if (test_bit(ST_DOFLUSHTX, &portp->state))
+                       cmd = A_SETSIGNALSFTX;
+               else if (test_bit(ST_DOFLUSHRX, &portp->state))
+                       cmd = A_SETSIGNALSFRX;
+               else
+                       cmd = A_SETSIGNALS;
+               clear_bit(ST_DOFLUSHTX, &portp->state);
+               clear_bit(ST_DOFLUSHRX, &portp->state);
+               clear_bit(ST_DOSIGS, &portp->state);
+               memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t));
+               cp->status = 0;
+               cp->cmd = cmd;
+               set_bit(ST_CMDING, &portp->state);
+       } else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) {
+               cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
+               cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
+               clear_bit(ST_DOFLUSHTX, &portp->state);
+               clear_bit(ST_DOFLUSHRX, &portp->state);
+               memcpy((void *) &(cp->args[0]), (void *) &cmd, sizeof(int));
+               cp->status = 0;
+               cp->cmd = A_FLUSH;
+               set_bit(ST_CMDING, &portp->state);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Host command service checking. This handles commands or messages
+ *     coming from the slave to the host. Must have board shared memory
+ *     enabled and interrupts off when called. Notice that by servicing the
+ *     read data last we don't need to change the shared memory pointer
+ *     during processing (which is a slow IO operation).
+ */
+
+static inline int stli_hostcmd(stlibrd_t *brdp, int channr)
+{
+       volatile cdkasy_t       *ap;
+       volatile cdkctrl_t      *cp;
+       struct tty_struct       *tty;
+       asynotify_t             nt;
+       stliport_t              *portp;
+       unsigned long           oldsigs;
+       int                     rc, donerx;
+
+#if DEBUG
+       printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr);
+#endif
+
+       portp = brdp->ports[(channr - 1)];
+       ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+       cp = &ap->ctrl;
+
+/*
+ *     Check if we are waiting for an open completion message.
+ */
+       if (test_bit(ST_OPENING, &portp->state)) {
+               rc = (int) cp->openarg;
+               if ((cp->open == 0) && (rc != 0)) {
+                       if (rc > 0)
+                               rc--;
+                       cp->openarg = 0;
+                       portp->rc = rc;
+                       clear_bit(ST_OPENING, &portp->state);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check if we are waiting for a close completion message.
+ */
+       if (test_bit(ST_CLOSING, &portp->state)) {
+               rc = (int) cp->closearg;
+               if ((cp->close == 0) && (rc != 0)) {
+                       if (rc > 0)
+                               rc--;
+                       cp->closearg = 0;
+                       portp->rc = rc;
+                       clear_bit(ST_CLOSING, &portp->state);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check if we are waiting for a command completion message. We may
+ *     need to copy out the command results associated with this command.
+ */
+       if (test_bit(ST_CMDING, &portp->state)) {
+               rc = cp->status;
+               if ((cp->cmd == 0) && (rc != 0)) {
+                       if (rc > 0)
+                               rc--;
+                       if (portp->argp != (void *) NULL) {
+                               memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize);
+                               portp->argp = (void *) NULL;
+                       }
+                       cp->status = 0;
+                       portp->rc = rc;
+                       clear_bit(ST_CMDING, &portp->state);
+                       stli_dodelaycmd(portp, cp);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check for any notification messages ready. This includes lots of
+ *     different types of events - RX chars ready, RX break received,
+ *     TX data low or empty in the slave, modem signals changed state.
+ */
+       donerx = 0;
+
+       if (ap->notify) {
+               nt = ap->changed;
+               ap->notify = 0;
+               tty = portp->tty;
+
+               if (nt.signal & SG_DCD) {
+                       oldsigs = portp->sigs;
+                       portp->sigs = stli_mktiocm(nt.sigvalue);
+                       clear_bit(ST_GETSIGS, &portp->state);
+                       if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+                               wake_up_interruptible(&portp->open_wait);
+                       if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+                               if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+                                               (portp->flags & ASYNC_CALLOUT_NOHUP))) {
+                                       if (tty != (struct tty_struct *) NULL)
+                                               queue_task_irq_off(&portp->tqhangup, &tq_scheduler);
+                               }
+                       }
+               }
+
+               if (nt.data & DT_TXEMPTY)
+                       clear_bit(ST_TXBUSY, &portp->state);
+               if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
+                       if (tty != (struct tty_struct *) NULL) {
+                               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+                                       (tty->ldisc.write_wakeup)(tty);
+                               wake_up_interruptible(&tty->write_wait);
+                       }
+               }
+
+               if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) {
+                       if (tty != (struct tty_struct *) NULL) {
+                               if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+                                       tty->flip.count++;
+                                       *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+                                       *tty->flip.char_buf_ptr++ = 0;
+#ifndef MODULE
+                                       if (portp->flags & ASYNC_SAK)
+                                               do_SAK(tty);
+#endif
+                                       tty_schedule_flip(tty);
+                               }
+                       }
+               }
+
+               if (nt.data & DT_RXBUSY) {
+                       donerx++;
+                       stli_read(brdp, portp);
+               }
+       }
+
+/*
+ *     It might seem odd that we are checking for more RX chars here.
+ *     But, we need to handle the case where the tty buffer was previously
+ *     filled, but we had more characters to pass up. The slave will not
+ *     send any more RX notify messages until the RX buffer has been emptied.
+ *     But it will leave the service bits on (since the buffer is not empty).
+ *     So from here we can try to process more RX chars.
+ */
+       if ((!donerx) && test_bit(ST_RXING, &portp->state)) {
+               clear_bit(ST_RXING, &portp->state);
+               stli_read(brdp, portp);
+       }
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Driver poll routine. This routine polls the boards in use and passes
+ *     messages back up to host when neccesary. This is actually very
+ *     CPU efficient, since we will always have the kernel poll clock, it
+ *     adds only a few cycles when idle (since board service can be
+ *     determined very easily), but when loaded generates no interrupts
+ *     (with their expensive associated context change).
+ */
+
+static void stli_poll(unsigned long arg)
+{
+       volatile cdkhdr_t       *hdrp;
+       unsigned char           bits[(STL_MAXCHANS / 8) + 1];
+       unsigned char           hostreq, slavereq;
+       stliport_t              *portp;
+       stlibrd_t               *brdp;
+       int                     bitpos, bitat, bitsize;
+       int                     brdnr, channr, nrdevs;
+
+       stli_timerlist.expires = STLI_TIMEOUT;
+       add_timer(&stli_timerlist);
+
+/*
+ *     Check each board and do any servicing required.
+ */
+       for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
+               brdp = &stli_brds[brdnr];
+               if ((brdp->state & BST_STARTED) == 0)
+                       continue;
+
+               EBRDENABLE(brdp);
+               hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+               hostreq = hdrp->hostreq;
+               slavereq = hdrp->slavereq;
+               bitsize = brdp->bitsize;
+               nrdevs = brdp->nrdevs;
+
+/*
+ *             Check if slave wants any service. Basically we try to do as
+ *             little work as possible here. There are 2 levels of service
+ *             bits. So if there is nothing to do we bail early. We check
+ *             8 service bits at a time in the inner loop, so we can bypass
+ *             the lot if none of them want service.
+ */
+               if (hostreq) {
+                       memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
+
+                       for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+                               if (bits[bitpos] == 0)
+                                       continue;
+                               channr = bitpos * 8;
+                               for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+                                       if (bits[bitpos] & bitat) {
+                                               stli_hostcmd(brdp, channr);
+                                       }
+                               }
+                       }
+               }
+
+/*
+ *             Check if any of the out-standing host commands have completed.
+ *             It is a bit unfortunate that we need to check stuff that we
+ *             initiated!  This ain't pretty, but it needs to be fast.
+ */
+               if (slavereq) {
+                       slavereq = 0;
+                       hostreq = 0;
+                       memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize);
+
+                       for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+                               if (bits[bitpos] == 0)
+                                       continue;
+                               channr = bitpos * 8;
+                               for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+                                       if (bits[bitpos] & bitat) {
+                                               portp = brdp->ports[(channr - 1)];
+                                               if (test_bit(ST_OPENING, &portp->state) ||
+                                                               test_bit(ST_CLOSING, &portp->state) ||
+                                                               test_bit(ST_CMDING, &portp->state) ||
+                                                               test_bit(ST_TXBUSY, &portp->state)) {
+                                                       slavereq |= portp->reqbit;
+                                               } else {
+                                                       bits[bitpos] &= ~bitat;
+                                                       hostreq++;
+                                               }
+                                       }
+                               }
+                       }
+                       hdrp->slavereq = slavereq;
+                       if (hostreq)
+                               memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize);
+               }
+
+               EBRDDISABLE(brdp);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Translate the termios settings into the port setting structure of
+ *     the slave.
+ */
+
+static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
+{
+#if DEBUG
+       printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp);
+#endif
+
+       memset(pp, 0, sizeof(asyport_t));
+
+/*
+ *     Start of by setting the baud, char size, parity and stop bit info.
+ */
+       pp->baudout = tiosp->c_cflag & CBAUD;
+       if (pp->baudout & CBAUDEX) {
+               pp->baudout &= ~CBAUDEX;
+               if ((pp->baudout < 1) || (pp->baudout > 2))
+                       tiosp->c_cflag &= ~CBAUDEX;
+               else
+                       pp->baudout += 15;
+       }
+       pp->baudout = stli_baudrates[pp->baudout];
+       if ((tiosp->c_cflag & CBAUD) == B38400) {
+               if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       pp->baudout = 57600;
+               else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       pp->baudout = 115200;
+               else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+                       pp->baudout = (portp->baud_base / portp->custom_divisor);
+       }
+       if (pp->baudout > STL_MAXBAUD)
+               pp->baudout = STL_MAXBAUD;
+       pp->baudin = pp->baudout;
+
+       switch (tiosp->c_cflag & CSIZE) {
+       case CS5:
+               pp->csize = 5;
+               break;
+       case CS6:
+               pp->csize = 6;
+               break;
+       case CS7:
+               pp->csize = 7;
+               break;
+       default:
+               pp->csize = 8;
+               break;
+       }
+
+       if (tiosp->c_cflag & CSTOPB)
+               pp->stopbs = PT_STOP2;
+       else
+               pp->stopbs = PT_STOP1;
+
+       if (tiosp->c_cflag & PARENB) {
+               if (tiosp->c_cflag & PARODD)
+                       pp->parity = PT_ODDPARITY;
+               else
+                       pp->parity = PT_EVENPARITY;
+       } else {
+               pp->parity = PT_NOPARITY;
+       }
+
+/*
+ *     Set up any flow control options enabled.
+ */
+       if (tiosp->c_iflag & IXON) {
+               pp->flow |= F_IXON;
+               if (tiosp->c_iflag & IXANY)
+                       pp->flow |= F_IXANY;
+       }
+       if (tiosp->c_cflag & CRTSCTS)
+               pp->flow |= (F_RTSFLOW | F_CTSFLOW);
+
+       pp->startin = tiosp->c_cc[VSTART];
+       pp->stopin = tiosp->c_cc[VSTOP];
+       pp->startout = tiosp->c_cc[VSTART];
+       pp->stopout = tiosp->c_cc[VSTOP];
+
+/*
+ *     Set up the RX char marking mask with those RX error types we must
+ *     catch. We can get the slave to help us out a little here, it will
+ *     ignore parity errors and breaks for us, and mark parity errors in
+ *     the data stream.
+ */
+       if (tiosp->c_iflag & IGNPAR)
+               pp->iflag |= FI_IGNRXERRS;
+       if (tiosp->c_iflag & IGNBRK)
+               pp->iflag |= FI_IGNBREAK;
+
+       portp->rxmarkmsk = 0;
+       if (tiosp->c_iflag & (INPCK | PARMRK))
+               pp->iflag |= FI_1MARKRXERRS;
+       if (tiosp->c_iflag & BRKINT)
+               portp->rxmarkmsk |= BRKINT;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Construct a slave signals structure for setting the DTR and RTS
+ *     signals as specified.
+ */
+
+static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
+{
+#if DEBUG
+       printk("stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n", (int) sp, dtr, rts);
+#endif
+
+       memset(sp, 0, sizeof(asysigs_t));
+       if (dtr >= 0) {
+               sp->signal |= SG_DTR;
+               sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0);
+       }
+       if (rts >= 0) {
+               sp->signal |= SG_RTS;
+               sp->sigvalue |= ((rts > 0) ? SG_RTS : 0);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Convert the signals returned from the slave into a local TIOCM type
+ *     signals value. We keep them localy in TIOCM format.
+ */
+
+static long stli_mktiocm(unsigned long sigvalue)
+{
+       long    tiocm;
+
+#if DEBUG
+       printk("stli_mktiocm(sigvalue=%x)\n", (int) sigvalue);
+#endif
+
+       tiocm = 0;
+       tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
+       tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
+       tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
+       tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0);
+       tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0);
+       tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0);
+       return(tiocm);
+}
+
+/*****************************************************************************/
+
+/*
+ *     All panels and ports actually attached have been worked out. All
+ *     we need to do here is set up the appropriate per port data structures.
+ */
+
+static int stli_initports(stlibrd_t *brdp)
+{
+       stliport_t      *portp;
+       int             i, panelnr, panelport;
+
+#if DEBUG
+       printk("stli_initports(brdp=%x)\n", (int) brdp);
+#endif
+
+       for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
+               portp = (stliport_t *) stli_memalloc(sizeof(stliport_t));
+               if (portp == (stliport_t *) NULL) {
+                       printk("STALLION: failed to allocate port structure\n");
+                       continue;
+               }
+
+               memset(portp, 0, sizeof(stliport_t));
+               portp->portnr = i;
+               portp->brdnr = brdp->brdnr;
+               portp->panelnr = panelnr;
+               portp->baud_base = STL_BAUDBASE;
+               portp->close_delay = STL_CLOSEDELAY;
+               portp->closing_wait = 30 * HZ;
+               portp->tqhangup.routine = stli_dohangup;
+               portp->tqhangup.data = portp;
+               portp->normaltermios = stli_deftermios;
+               portp->callouttermios = stli_deftermios;
+               panelport++;
+               if (panelport >= brdp->panels[panelnr]) {
+                       panelport = 0;
+                       panelnr++;
+               }
+               brdp->ports[i] = portp;
+       }
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     All the following routines are board specific hardware operations.
+ */
+
+static void stli_ecpinit(stlibrd_t *brdp)
+{
+       unsigned long   memconf;
+
+#if DEBUG
+       printk("stli_ecpinit(brdp=%d)\n", (int) brdp);
+#endif
+
+       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+       udelay(10);
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+       udelay(100);
+
+       memconf = (((unsigned long) brdp->membase) & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
+       outb(memconf, (brdp->iobase + ECP_ATMEMAR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpenable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_ecpenable(brdp=%x)\n", (int) brdp);
+#endif
+       outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpdisable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_ecpdisable(brdp=%x)\n", (int) brdp);
+#endif
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void            *ptr;
+       unsigned char   val;
+
+#if DEBUG
+       printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_ATPAGESIZE);
+               val = (unsigned char) (offset / ECP_ATPAGESIZE);
+       }
+       outb(val, (brdp->iobase + ECP_ATMEMPR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpreset(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_ecpreset(brdp=%x)\n", (int) brdp);
+#endif
+
+       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+       udelay(10);
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpintr(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_ecpintr(brdp=%x)\n", (int) brdp);
+#endif
+       outb(0x1, brdp->iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following set of functions act on ECP EISA boards.
+ */
+
+static void stli_ecpeiinit(stlibrd_t *brdp)
+{
+       unsigned long   memconf;
+
+#if DEBUG
+       printk("stli_ecpeiinit(brdp=%x)\n", (int) brdp);
+#endif
+
+       outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
+       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+       udelay(10);
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+       udelay(500);
+
+       memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
+       outb(memconf, (brdp->iobase + ECP_EIMEMARL));
+       memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
+       outb(memconf, (brdp->iobase + ECP_EIMEMARH));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeienable(stlibrd_t *brdp)
+{      
+       outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeidisable(stlibrd_t *brdp)
+{      
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void            *ptr;
+       unsigned char   val;
+
+#if DEBUG
+       printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_EIPAGESIZE);
+               if (offset < ECP_EIPAGESIZE)
+                       val = ECP_EIENABLE;
+               else
+                       val = ECP_EIENABLE | 0x40;
+       }
+       outb(val, (brdp->iobase + ECP_EICONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeireset(stlibrd_t *brdp)
+{      
+       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+       udelay(10);
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following set of functions act on ECP MCA boards.
+ */
+
+static void stli_ecpmcenable(stlibrd_t *brdp)
+{      
+       outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcdisable(stlibrd_t *brdp)
+{      
+       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void            *ptr;
+       unsigned char   val;
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_MCPAGESIZE);
+               val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE;
+       }
+       outb(val, (brdp->iobase + ECP_MCCONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcreset(stlibrd_t *brdp)
+{      
+       outb(ECP_MCSTOP, (brdp->iobase + ECP_MCCONFR));
+       udelay(10);
+       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on ONboards.
+ */
+
+static void stli_onbinit(stlibrd_t *brdp)
+{
+       unsigned long   memconf;
+       int             i;
+
+#if DEBUG
+       printk("stli_onbinit(brdp=%d)\n", (int) brdp);
+#endif
+
+       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+       udelay(10);
+       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+
+       memconf = (((unsigned long) brdp->membase) & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
+       outb(memconf, (brdp->iobase + ONB_ATMEMAR));
+       outb(0x1, brdp->iobase);
+       udelay(1000);
+}
+
+/*****************************************************************************/
+
+static void stli_onbenable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_onbenable(brdp=%x)\n", (int) brdp);
+#endif
+       outb((ONB_ATENABLE | ONB_HIMEMENAB), (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbdisable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_onbdisable(brdp=%x)\n", (int) brdp);
+#endif
+       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void    *ptr;
+
+#if DEBUG
+       printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+       } else {
+               ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
+       }
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbreset(stlibrd_t *brdp)
+{      
+       int     i;
+
+#if DEBUG
+       printk("stli_onbreset(brdp=%x)\n", (int) brdp);
+#endif
+
+       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+       udelay(10);
+       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on ONboard EISA.
+ */
+
+static void stli_onbeinit(stlibrd_t *brdp)
+{
+       unsigned long   memconf;
+       int             i;
+
+#if DEBUG
+       printk("stli_onbeinit(brdp=%d)\n", (int) brdp);
+#endif
+
+       outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
+       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+       udelay(10);
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+
+       memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
+       outb(memconf, (brdp->iobase + ONB_EIMEMARL));
+       memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
+       outb(memconf, (brdp->iobase + ONB_EIMEMARH));
+       outb(0x1, brdp->iobase);
+       udelay(1000);
+}
+
+/*****************************************************************************/
+
+static void stli_onbeenable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_onbeenable(brdp=%x)\n", (int) brdp);
+#endif
+       outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbedisable(stlibrd_t *brdp)
+{      
+#if DEBUG
+       printk("stli_onbedisable(brdp=%x)\n", (int) brdp);
+#endif
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void            *ptr;
+       unsigned char   val;
+
+#if DEBUG
+       printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ONB_EIPAGESIZE);
+               if (offset < ONB_EIPAGESIZE)
+                       val = ONB_EIENABLE;
+               else
+                       val = ONB_EIENABLE | 0x40;
+       }
+       outb(val, (brdp->iobase + ONB_EICONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbereset(stlibrd_t *brdp)
+{      
+       int     i;
+
+#if DEBUG
+       printk("stli_onbereset(brdp=%x)\n", (int) brdp);
+#endif
+
+       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+       udelay(10);
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on Brumby boards.
+ */
+
+static void stli_bbyinit(stlibrd_t *brdp)
+{
+       int     i;
+
+#if DEBUG
+       printk("stli_bbyinit(brdp=%d)\n", (int) brdp);
+#endif
+
+       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + BBY_ATCONFR));
+       for (i = 0; (i < 500); i++)
+               udelay(1000);
+       outb(0x1, brdp->iobase);
+       udelay(1000);
+}
+
+/*****************************************************************************/
+
+static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void            *ptr;
+       unsigned char   val;
+
+#if DEBUG
+       printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % BBY_PAGESIZE);
+               val = (unsigned char) (offset / BBY_PAGESIZE);
+       }
+       outb(val, (brdp->iobase + BBY_ATCONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_bbyreset(stlibrd_t *brdp)
+{      
+       int     i;
+
+#if DEBUG
+       printk("stli_bbyreset(brdp=%x)\n", (int) brdp);
+#endif
+
+       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + BBY_ATCONFR));
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on original old Stallion boards.
+ */
+
+static void stli_stalinit(stlibrd_t *brdp)
+{
+       int     i;
+
+#if DEBUG
+       printk("stli_stalinit(brdp=%d)\n", (int) brdp);
+#endif
+
+       outb(0x1, brdp->iobase);
+       for (i = 0; (i < 100); i++)
+               udelay(1000);
+}
+
+/*****************************************************************************/
+
+static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{      
+       void    *ptr;
+
+#if DEBUG
+       printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+       if (offset > brdp->memsize) {
+               printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = 0;
+       } else {
+               ptr = brdp->membase + (offset % STAL_PAGESIZE);
+       }
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_stalreset(stlibrd_t *brdp)
+{      
+       volatile unsigned long  *vecp;
+       int                     i;
+
+#if DEBUG
+       printk("stli_stalreset(brdp=%x)\n", (int) brdp);
+#endif
+
+       vecp = (volatile unsigned long *) (brdp->membase + 0x30);
+       *vecp = 0xffff0000;
+       outb(0, brdp->iobase);
+       for (i = 0; (i < 500); i++)
+               udelay(1000);
+}
+
+/*****************************************************************************/
+
+#if STLI_HIMEMORY
+#define        PAGE_IOMEM      __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_PCD)
+
+/*
+ *     To support shared memory addresses outside of the lower 1 Mb region
+ *     we will need to pull some tricks with memory management to map the
+ *     higher range into kernel virtual address space... Radical stuff...
+ */
+
+static void *stli_mapbrdmem(unsigned long physaddr, unsigned int size)
+{
+       void    *virtaddr;
+       int     rc;
+
+#if DEBUG
+       printk("stli_mapbrdmem(physaddr=%x,size=%x)\n", (int) physaddr, size);
+#endif
+
+       if ((virtaddr = vmalloc(size)) == (char *) NULL) {
+               printk("STALLION: failed to allocate virtual address space, size=%x\n", size);
+               return((void *) NULL);
+       }
+       if ((rc = remap_page_range((TASK_SIZE + ((unsigned long) virtaddr)), physaddr, size, PAGE_IOMEM))) {
+               printk("STALLION: failed to map phyiscal address=%x, errno=%d\n", (int) physaddr, rc);
+               return((void *) NULL);
+       }
+       return(virtaddr);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ECP board and initialize it. This handles only ECP
+ *     board types.
+ */
+
+static int stli_initecp(stlibrd_t *brdp, stlconf_t *confp)
+{
+       cdkecpsig_t     sig;
+       cdkecpsig_t     *sigsp;
+       unsigned int    status, nxtid;
+       int             panelnr;
+
+#if DEBUG
+       printk("stli_initecp(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+#endif
+
+/*
+ *     Based on the specific board type setup the common vars to access
+ *     and enable shared memory. Set all board specific information now
+ *     as well.
+ */
+       switch (brdp->brdtype) {
+       case BRD_ECP:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->pagesize = ECP_ATPAGESIZE;
+               brdp->init = stli_ecpinit;
+               brdp->enable = stli_ecpenable;
+               brdp->reenable = stli_ecpenable;
+               brdp->disable = stli_ecpdisable;
+               brdp->getmemptr = stli_ecpgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpreset;
+               break;
+
+       case BRD_ECPE:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->pagesize = ECP_EIPAGESIZE;
+               brdp->init = stli_ecpeiinit;
+               brdp->enable = stli_ecpeienable;
+               brdp->reenable = stli_ecpeienable;
+               brdp->disable = stli_ecpeidisable;
+               brdp->getmemptr = stli_ecpeigetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpeireset;
+               break;
+
+       case BRD_ECPMC:
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->pagesize = ECP_MCPAGESIZE;
+               brdp->iobase = confp->ioaddr1;
+               brdp->init = NULL;
+               brdp->enable = stli_ecpmcenable;
+               brdp->reenable = stli_ecpmcenable;
+               brdp->disable = stli_ecpmcdisable;
+               brdp->getmemptr = stli_ecpmcgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpmcreset;
+               break;
+
+       default:
+               return(-EINVAL);
+       }
+
+/*
+ *     The per-board operations structure is all setup, so now lets go
+ *     and get the board operational. Firstly initialize board configuration
+ *     registers. Then if we are using the higher 1Mb support then set up
+ *     the memory mapping info so we can get at the boards shared memory.
+ */
+       EBRDINIT(brdp);
+
+#if STLI_HIMEMORY
+       if (confp->memaddr > 0x100000) {
+               brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+               if (brdp->membase == (void *) NULL)
+                       return(-ENOMEM);
+       }
+#endif
+
+/*
+ *     Now that all specific code is set up, enable the shared memory and
+ *     look for the a signature area that will tell us exactly what board
+ *     this is, and what is connected to it.
+ */
+       EBRDENABLE(brdp);
+       sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+       memcpy(&sig, sigsp, sizeof(cdkecpsig_t));
+       EBRDDISABLE(brdp);
+
+#if 0
+       printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
+               __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0],
+               (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3],
+               (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6],
+               (int) sig.panelid[7]);
+#endif
+
+       if (sig.magic != ECP_MAGIC)
+               return(-ENODEV);
+
+/*
+ *     Scan through the signature looking at the panels connected to the
+ *     board. Calculate the total number of ports as we go.
+ */
+       for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) {
+               status = sig.panelid[nxtid];
+               if ((status & ECH_PNLIDMASK) != nxtid)
+                       break;
+               if (status & ECH_PNL16PORT) {
+                       brdp->panels[panelnr] = 16;
+                       brdp->nrports += 16;
+                       nxtid += 2;
+               } else {
+                       brdp->panels[panelnr] = 8;
+                       brdp->nrports += 8;
+                       nxtid++;
+               }
+               brdp->nrpanels++;
+       }
+
+       request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)");
+       brdp->state |= BST_FOUND;
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ONboard, Brumby or Stallion board and initialize it.
+ *     This handles only these board types.
+ */
+
+static int stli_initonb(stlibrd_t *brdp, stlconf_t *confp)
+{
+       cdkonbsig_t     sig;
+       cdkonbsig_t     *sigsp;
+       int             i;
+
+#if DEBUG
+       printk("stli_initonb(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+#endif
+
+/*
+ *     Based on the specific board type setup the common vars to access
+ *     and enable shared memory. Set all board specific information now
+ *     as well.
+ */
+       switch (brdp->brdtype) {
+       case BRD_ONBOARD:
+       case BRD_ONBOARD32:
+       case BRD_ONBOARD2:
+       case BRD_ONBOARD2_32:
+       case BRD_ONBOARDRS:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = ONB_MEMSIZE;
+               brdp->pagesize = ONB_ATPAGESIZE;
+               brdp->init = stli_onbinit;
+               brdp->enable = stli_onbenable;
+               brdp->reenable = stli_onbenable;
+               brdp->disable = stli_onbdisable;
+               brdp->getmemptr = stli_onbgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_onbreset;
+               break;
+
+       case BRD_ONBOARDE:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = ONB_EIMEMSIZE;
+               brdp->pagesize = ONB_EIPAGESIZE;
+               brdp->init = stli_onbeinit;
+               brdp->enable = stli_onbeenable;
+               brdp->reenable = stli_onbeenable;
+               brdp->disable = stli_onbedisable;
+               brdp->getmemptr = stli_onbegetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_onbereset;
+               break;
+
+       case BRD_BRUMBY4:
+       case BRD_BRUMBY8:
+       case BRD_BRUMBY16:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = BBY_MEMSIZE;
+               brdp->pagesize = BBY_PAGESIZE;
+               brdp->init = stli_bbyinit;
+               brdp->enable = NULL;
+               brdp->reenable = NULL;
+               brdp->disable = NULL;
+               brdp->getmemptr = stli_bbygetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_bbyreset;
+               break;
+
+       case BRD_STALLION:
+               brdp->iobase = confp->ioaddr1;
+               brdp->membase = (void *) confp->memaddr;
+               brdp->memsize = STAL_MEMSIZE;
+               brdp->pagesize = STAL_PAGESIZE;
+               brdp->init = stli_stalinit;
+               brdp->enable = NULL;
+               brdp->reenable = NULL;
+               brdp->disable = NULL;
+               brdp->getmemptr = stli_stalgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_stalreset;
+               break;
+
+       default:
+               return(-EINVAL);
+       }
+
+/*
+ *     The per-board operations structure is all setup, so now lets go
+ *     and get the board operational. Firstly initialize board configuration
+ *     registers. Then if we are using the higher 1Mb support then set up
+ *     the memory mapping info so we can get at the boards shared memory.
+ */
+       EBRDINIT(brdp);
+
+#if STLI_HIMEMORY
+       if (confp->memaddr > 0x100000) {
+               brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+               if (brdp->membase == (void *) NULL)
+                       return(-ENOMEM);
+       }
+#endif
+
+/*
+ *     Now that all specific code is set up, enable the shared memory and
+ *     look for the a signature area that will tell us exactly what board
+ *     this is, and how many ports.
+ */
+       EBRDENABLE(brdp);
+       sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+       memcpy(&sig, sigsp, sizeof(cdkonbsig_t));
+       EBRDDISABLE(brdp);
+
+#if 0
+       printk("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n",
+               __FILE__, __LINE__, sig.magic0, sig.magic1, sig.magic2,
+               sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2);
+#endif
+
+       if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
+                       (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
+               return(-ENODEV);
+
+/*
+ *     Scan through the signature alive mask and calculate how many ports
+ *     there are on this board.
+ */
+       brdp->nrpanels = 1;
+       if (sig.amask1) {
+               brdp->nrports = 32;
+       } else {
+               for (i = 0; (i < 16); i++) {
+                       if (((sig.amask0 << i) & 0x8000) == 0)
+                               break;
+               }
+               brdp->nrports = i;
+       }
+
+       request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)");
+       brdp->state |= BST_FOUND;
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start up a running board. This routine is only called after the
+ *     code has been down loaded to the board and is operational. It will
+ *     read in the memory map, and get the show on the road...
+ */
+
+static int stli_startbrd(stlibrd_t *brdp)
+{
+       volatile cdkhdr_t       *hdrp;
+       volatile cdkmem_t       *memp;
+       volatile cdkasy_t       *ap;
+       unsigned long           flags;
+       stliport_t              *portp;
+       int                     portnr, nrdevs, i, rc;
+
+#if DEBUG
+       printk("stli_startbrd(brdp=%x)\n", (int) brdp);
+#endif
+
+       rc = 0;
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       nrdevs = hdrp->nrdevs;
+
+#if 0
+       printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n",
+                __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification,
+                hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp,
+                (int) hdrp->slavep);
+#endif
+
+       if (nrdevs < (brdp->nrports + 1)) {
+               printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs);
+               brdp->nrports = nrdevs - 1;
+       }
+       brdp->nrdevs = nrdevs;
+       brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
+       brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
+       brdp->bitsize = (nrdevs + 7) / 8;
+       memp = (volatile cdkmem_t *) hdrp->memp;
+       if (((unsigned long) memp) > brdp->memsize) {
+               printk("STALLION: corrupted shared memory region?\n");
+               rc = -EIO;
+               goto stli_donestartup;
+       }
+       memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp, (unsigned long) memp);
+       if (memp->dtype != TYP_ASYNCTRL) {
+               printk("STALLION: no slave control device found\n");
+               goto stli_donestartup;
+       }
+       memp++;
+
+/*
+ *     Cycle through memory allocation of each port. We are guaranteed to
+ *     have all ports inside the first page of slave window, so no need to
+ *     change pages while reading memory map.
+ */
+       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
+               if (memp->dtype != TYP_ASYNC)
+                       break;
+               portp = brdp->ports[portnr];
+               if (portp == (stliport_t *) NULL)
+                       break;
+               portp->devnr = i;
+               portp->addr = memp->offset;
+               portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs));
+               portp->portidx = (unsigned char) (i / 8);
+               portp->portbit = (unsigned char) (0x1 << (i % 8));
+       }
+
+/*
+ *     For each port setup a local copy of the RX and TX buffer offsets
+ *     and sizes. We do this separate from the above, because we need to
+ *     move the shared memory page...
+ */
+       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
+               portp = brdp->ports[portnr];
+               if (portp == (stliport_t *) NULL)
+                       break;
+               if (portp->addr == 0)
+                       break;
+               ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+               if (ap != (volatile cdkasy_t *) NULL) {
+                       portp->rxsize = ap->rxq.size;
+                       portp->txsize = ap->txq.size;
+                       portp->rxoffset = ap->rxq.offset;
+                       portp->txoffset = ap->txq.offset;
+               }
+       }
+
+stli_donestartup:
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       if (rc == 0)
+               brdp->state |= BST_STARTED;
+
+       if (! stli_timeron) {
+               stli_timeron++;
+               stli_timerlist.expires = STLI_TIMEOUT;
+               add_timer(&stli_timerlist);
+       }
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Scan through all the boards in the configuration and see what we
+ *     can find.
+ */
+
+static int stli_brdinit()
+{
+       stlibrd_t       *brdp;
+       stlconf_t       *confp;
+       int             i, j;
+
+#if DEBUG
+       printk("stli_brdinit()\n");
+#endif
+
+       if (stli_nrbrds > STL_MAXBRDS)
+               return(-EINVAL);
+
+       stli_brds = (stlibrd_t *) stli_memalloc((sizeof(stlibrd_t) * stli_nrbrds));
+       if (stli_brds == (stlibrd_t *) NULL) {
+               printk("STALLION: failed to allocate board structures\n");
+               return(-ENOMEM);
+       }
+       memset(stli_brds, 0, (sizeof(stlibrd_t) * stli_nrbrds));
+
+       for (i = 0; (i < stli_nrbrds); i++) {
+               brdp = &stli_brds[i];
+               confp = &stli_brdconf[i];
+               brdp->brdnr = i;
+               brdp->brdtype = confp->brdtype;
+
+               switch (confp->brdtype) {
+               case BRD_ECP:
+               case BRD_ECPE:
+               case BRD_ECPMC:
+                       stli_initecp(brdp, confp);
+                       break;
+               case BRD_ONBOARD:
+               case BRD_ONBOARDE:
+               case BRD_ONBOARD2:
+               case BRD_ONBOARD32:
+               case BRD_ONBOARD2_32:
+               case BRD_ONBOARDRS:
+               case BRD_BRUMBY4:
+               case BRD_BRUMBY8:
+               case BRD_BRUMBY16:
+               case BRD_STALLION:
+                       stli_initonb(brdp, confp);
+                       break;
+               case BRD_EASYIO:
+               case BRD_ECH:
+               case BRD_ECHMC:
+               case BRD_ECHPCI:
+                       printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
+                       break;
+               default:
+                       printk("STALLION: unit=%d is unknown board type=%d\n", i, confp->brdtype);
+                       break;
+               }
+
+               if ((brdp->state & BST_FOUND) == 0) {
+                       printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr);
+                       continue;
+               }
+
+               stli_initports(brdp);
+               printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr, brdp->nrpanels, brdp->nrports);
+       }
+
+/*
+ *     All found boards are initialized. Now for a little optimization, if
+ *     no boards are sharing the "shared memory" regions then we can just
+ *     leave them all enabled. This is in fact the usual case.
+ */
+       stli_shared = 0;
+       if (stli_nrbrds > 1) {
+               for (i = 0; (i < stli_nrbrds); i++) {
+                       for (j = i + 1; (j < stli_nrbrds); j++) {
+                               brdp = &stli_brds[i];
+                               if ((brdp->membase >= stli_brds[j].membase) &&
+                                               (brdp->membase <= (stli_brds[j].membase + stli_brds[j].memsize - 1))) {
+                                       stli_shared++;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (stli_shared == 0) {
+               for (i = 0; (i < stli_nrbrds); i++) {
+                       brdp = &stli_brds[i];
+                       if (brdp->state & BST_FOUND) {
+                               EBRDENABLE(brdp);
+                               brdp->enable = NULL;
+                               brdp->disable = NULL;
+                       }
+               }
+       }
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Code to handle an "staliomem" read operation. This device is the 
+ *     contents of the board shared memory. It is used for down loading
+ *     the slave image (and debugging :-)
+ */
+
+static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count)
+{
+       unsigned long   flags;
+       void            *memptr;
+       stlibrd_t       *brdp;
+       int             brdnr, size, n;
+
+#if DEBUG
+       printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, (int) fp, (int) buf, count);
+#endif
+
+       brdnr = MINOR(ip->i_rdev);
+       if (brdnr >= stli_nrbrds)
+               return(-ENODEV);
+       brdp = &stli_brds[brdnr];
+       if (brdp->state == 0)
+               return(-ENODEV);
+       if (fp->f_pos >= brdp->memsize)
+               return(0);
+
+       size = MIN(count, (brdp->memsize - fp->f_pos));
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       while (size > 0) {
+               memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
+               n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
+               memcpy_tofs(buf, memptr, n);
+               fp->f_pos += n;
+               buf += n;
+               size -= n;
+       }
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Code to handle an "staliomem" write operation. This device is the 
+ *     contents of the board shared memory. It is used for down loading
+ *     the slave image (and debugging :-)
+ */
+
+static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count)
+{
+       unsigned long   flags;
+       void            *memptr;
+       stlibrd_t       *brdp;
+       char            *chbuf;
+       int             brdnr, size, n;
+
+#if DEBUG
+       printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, (int) fp, (int) buf, count);
+#endif
+
+       brdnr = MINOR(ip->i_rdev);
+       if (brdnr >= stli_nrbrds)
+               return(-ENODEV);
+       brdp = &stli_brds[brdnr];
+       if (brdp->state == 0)
+               return(-ENODEV);
+       if (fp->f_pos >= brdp->memsize)
+               return(0);
+
+       chbuf = (char *) buf;
+       size = MIN(count, (brdp->memsize - fp->f_pos));
+
+       save_flags(flags);
+       cli();
+       EBRDENABLE(brdp);
+       while (size > 0) {
+               memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
+               n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
+               memcpy_fromfs(memptr, chbuf, n);
+               fp->f_pos += n;
+               chbuf += n;
+               size -= n;
+       }
+       EBRDDISABLE(brdp);
+       restore_flags(flags);
+
+       return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The "staliomem" device is also required to do some special operations on
+ *     the board. We need to be able to send an interrupt to the board,
+ *     reset it, and start/stop it.
+ */
+
+static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+       stlibrd_t       *brdp;
+       int             brdnr, rc;
+
+#if DEBUG
+       printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+#endif
+
+       brdnr = MINOR(ip->i_rdev);
+       if (brdnr >= stli_nrbrds)
+               return(-ENODEV);
+       brdp = &stli_brds[brdnr];
+       if (brdp->state == 0)
+               return(-ENODEV);
+
+       rc = 0;
+
+       switch (cmd) {
+       case STL_BINTR:
+               EBRDINTR(brdp);
+               break;
+       case STL_BSTART:
+               rc = stli_startbrd(brdp);
+               break;
+       case STL_BSTOP:
+               brdp->state &= ~BST_STARTED;
+               break;
+       case STL_BRESET:
+               brdp->state &= ~BST_STARTED;
+               EBRDRESET(brdp);
+               if (stli_shared == 0) {
+                       if (brdp->reenable != NULL)
+                               (* brdp->reenable)(brdp);
+               }
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+long stli_init(long kmem_start)
+{
+       printk("%s: version %s\n", stli_drvname, stli_drvversion);
+
+#ifndef MODULE
+       stli_meminit(kmem_start);
+#endif
+
+       stli_brdinit();
+
+/*
+ *     Allocate a temporary write buffer.
+ */
+       stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
+       if (stli_tmpwritebuf == (char *) NULL)
+               printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+       stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
+       if (stli_txcookbuf == (char *) NULL)
+               printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+
+/*
+ *     Set up a character driver for the shared memory region. We need this
+ *     to down load the slave code image. Also it is a useful debugging tool.
+ */
+       if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
+               printk("STALLION: failed to register serial memory device\n");
+
+/*
+ *     Set up the tty driver structure and register us as a driver.
+ *     Also setup the callout tty device.
+ */
+       memset(&stli_serial, 0, sizeof(struct tty_driver));
+       stli_serial.magic = TTY_DRIVER_MAGIC;
+       stli_serial.name = stli_serialname;
+       stli_serial.major = STL_SERIALMAJOR;
+       stli_serial.minor_start = 0;
+       stli_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+       stli_serial.type = TTY_DRIVER_TYPE_SERIAL;
+       stli_serial.subtype = STL_DRVTYPSERIAL;
+       stli_serial.init_termios = stli_deftermios;
+       stli_serial.flags = TTY_DRIVER_REAL_RAW;
+       stli_serial.refcount = &stli_refcount;
+       stli_serial.table = stli_ttys;
+       stli_serial.termios = stli_termios;
+       stli_serial.termios_locked = stli_termioslocked;
+       
+       stli_serial.open = stli_open;
+       stli_serial.close = stli_close;
+       stli_serial.write = stli_write;
+       stli_serial.put_char = stli_putchar;
+       stli_serial.flush_chars = stli_flushchars;
+       stli_serial.write_room = stli_writeroom;
+       stli_serial.chars_in_buffer = stli_charsinbuffer;
+       stli_serial.ioctl = stli_ioctl;
+       stli_serial.set_termios = stli_settermios;
+       stli_serial.throttle = stli_throttle;
+       stli_serial.unthrottle = stli_unthrottle;
+       stli_serial.stop = stli_stop;
+       stli_serial.start = stli_start;
+       stli_serial.hangup = stli_hangup;
+       stli_serial.flush_buffer = stli_flushbuffer;
+
+       stli_callout = stli_serial;
+       stli_callout.name = stli_calloutname;
+       stli_callout.major = STL_CALLOUTMAJOR;
+       stli_callout.subtype = STL_DRVTYPCALLOUT;
+
+       if (tty_register_driver(&stli_serial))
+               printk("STALLION: failed to register serial driver\n");
+       if (tty_register_driver(&stli_callout))
+               printk("STALLION: failed to register callout driver\n");
+
+#ifndef MODULE
+       kmem_start = stli_memhalt();
+#endif
+       return(kmem_start);
+}
+
+/*****************************************************************************/
index 3a365442359523af4d1e087356017d189bbc2e5a..eb8200e138f31df3c688af687480dcb3bef49597 100644 (file)
@@ -32,8 +32,6 @@
 #include <linux/major.h>
 #include <linux/malloc.h>
 
-#include "mouse.h"
-
 /*
  * Head entry for the doubly linked mouse list
  */
diff --git a/drivers/char/mouse.h b/drivers/char/mouse.h
deleted file mode 100644 (file)
index 5f61aa7..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __MOUSE_H
-#define __MOUSE_H
-
-struct mouse {
-       int minor;
-       const char *name;
-       struct file_operations *fops;
-       struct mouse * next, * prev;
-};
-
-extern int mouse_register(struct mouse * mouse);
-extern int mouse_deregister(struct mouse * mouse);
-
-#endif
index 4bb5e772927574f5fd0f2c116a755925011da4a4..c19c103f1c6646a1ba0a710c06881d8a4154d7b5 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/busmouse.h>
 #include <linux/signal.h>
 #include <linux/errno.h>
+#include <linux/mouse.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
index a72ee10464d05e3e2d1d0fc62732de77fcb04e60..05d5f01d763f97e39c47c006f0a30bdf7c93358a 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <linux/malloc.h>
+#include <linux/mouse.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -52,8 +53,6 @@
 
 #include <linux/config.h>
 
-#include "mouse.h"
-
 #define PSMOUSE_MINOR      1            /* minor device # for this mouse */
 
 /* aux controller ports */
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
new file mode 100644 (file)
index 0000000..67f1f8f
--- /dev/null
@@ -0,0 +1,2973 @@
+/*****************************************************************************/
+
+/*
+ *     stallion.c  -- stallion multiport serial driver.
+ *
+ *     Copyright (C) 1994,1995  Greg Ungerer (gerg@stallion.oz.au).
+ *
+ *     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.
+ */
+
+/*****************************************************************************/
+
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/cd1400.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Define different board types. At the moment I have only declared
+ *     those boards that this driver supports. But I will use the standard
+ *     "assigned" board numbers. In the future this driver will support
+ *     some of the other Stallion boards. Currently supported boards are
+ *     abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32.
+ */
+#define        BRD_EASYIO      20
+#define        BRD_ECH         21
+#define        BRD_ECHMC       22
+#define        BRD_ECHPCI      26
+
+/*
+ *     Define a configuration structure to hold the board configuration.
+ *     Need to set this up in the code (for now) with the boards that are
+ *     to be configured into the system. This is what needs to be modified
+ *     when adding/removing/modifying boards. Each line entry in the
+ *     stl_brdconf[] array is a board. Each line contains io/irq/memory
+ *     ranges for that board (as well as what type of board it is).
+ *     Some examples:
+ *             { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }
+ *     This line would configure an EasyIO board (4 or 8, no difference),
+ *     at io addres 2a0 and irq 10.
+ *     Another example:
+ *             { BRD_ECH, 0x2a8, 0x280, 0, 12, 0 },
+ *     This line will configure an EasyConnection 8/32 board at primary io
+ *     addres 2a8, secondary io address 280 and irq 12.
+ *     Enter as many lines into this array as you want (only the first 4
+ *     will actually be used!). Any combination of EasyIO and EasyConnection
+ *     boards can be specified. EasyConnection 8/32 boards can share their
+ *     secondary io addresses between each other.
+ *
+ *     NOTE: there is no need to put any entries in this table for PCI
+ *     boards. They will be found automatically by the driver - provided
+ *     PCI BIOS32 support is compiled into the kernel.
+ */
+
+typedef struct {
+       int             brdtype;
+       int             ioaddr1;
+       int             ioaddr2;
+       unsigned long   memaddr;
+       int             irq;
+       int             irqtype;
+} stlconf_t;
+
+static stlconf_t       stl_brdconf[] = {
+       { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+};
+
+static int     stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
+
+/*****************************************************************************/
+
+/*
+ *     Define some important driver characteristics. Device major numbers
+ *     allocated as per Linux Device Registery.
+ */
+#ifndef        STL_SERIALMAJOR
+#define        STL_SERIALMAJOR         24
+#endif
+#ifndef        STL_CALLOUTMAJOR
+#define        STL_CALLOUTMAJOR        25
+#endif
+
+#define        STL_DRVTYPSERIAL        1
+#define        STL_DRVTYPCALLOUT       2
+
+#define        STL_MAXBRDS             4
+#define        STL_MAXPANELS           4
+#define        STL_PORTSPERPANEL       16
+#define        STL_MAXPORTS            64
+#define        STL_MAXDEVS             (STL_MAXBRDS * STL_MAXPORTS)
+
+/*
+ *     I haven't really decided (or measured) what TX buffer size gives
+ *     a good balance between performance and memory usage. These seem
+ *     to work pretty well...
+ */
+#define        STL_TXBUFLOW            256
+#define        STL_TXBUFSIZE           2048
+
+/*****************************************************************************/
+
+/*
+ *     Define our local driver identity first. Set up stuff to deal with
+ *     all the local structures required by a serial tty driver.
+ */
+static char    *stl_drvname = "Stallion Multiport Serial Driver";
+static char    *stl_drvversion = "1.0.0";
+static char    *stl_serialname = "ttyE";
+static char    *stl_calloutname = "cue";
+
+static struct tty_driver       stl_serial;
+static struct tty_driver       stl_callout;
+static struct tty_struct       *stl_ttys[STL_MAXDEVS];
+static struct termios          *stl_termios[STL_MAXDEVS];
+static struct termios          *stl_termioslocked[STL_MAXDEVS];
+static int                     stl_refcount = 0;
+
+/*
+ *     We will need to allocate a temporary write buffer for chars that
+ *     come direct from user space. The problem is that a copy from user
+ *     space might cause a page fault (typically on a system that is
+ *     swapping!). All ports will share one buffer - since if the system
+ *     is already swapping a shared buffer won't make things any worse.
+ */
+static char                    *stl_tmpwritebuf;
+static struct semaphore                stl_tmpwritesem = MUTEX;
+
+/*
+ *     Define a local default termios struct. All ports will be created
+ *     with this termios initially. Basically all it defines is a raw port
+ *     at 9600, 8 data bits, 1 stop bit.
+ */
+static struct termios          stl_deftermios = {
+       0,
+       0,
+       (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+       0,
+       0,
+       INIT_C_CC
+};
+
+/*
+ *     Keep track of what interrupts we have requested for us.
+ *     We don't need to request an interrupt twice if it is being
+ *     shared with another Stallion board.
+ */
+static int     stl_gotintrs[STL_MAXBRDS];
+static int     stl_numintrs = 0;
+
+/*****************************************************************************/
+
+/*
+ *     Define a set of structures to hold all the board/panel/port info
+ *     for our ports. These will be dynamically allocated as required.
+ */
+
+/*
+ *     Define a ring queue structure for each port. This will hold the
+ *     TX data waiting to be output. Characters are fed into this buffer
+ *     from the line discipline (or even direct from user space!) and
+ *     then fed into the UARTs during interrupts. Will use a clasic ring
+ *     queue here for this. The good thing about this type of ring queue
+ *     is that the head and tail pointers can be updated without interrupt
+ *     protection - since "write" code only needs to change the head, and
+ *     interrupt code only needs to change the tail.
+ */
+typedef struct {
+       char    *buf;
+       char    *head;
+       char    *tail;
+} stlrq_t;
+
+/*
+ *     Port, panel and board structures to hold status info about each.
+ *     The board structure contains pointers to structures for each panel
+ *     connected to it, and in turn each panel structure contains pointers
+ *     for each port structure for each port on that panel. Note that
+ *     the port structure also contains the board and panel number that it
+ *     is associated with, this makes it (fairly) easy to get back to the
+ *     board/panel info for a port.
+ */
+typedef struct {
+       int                     portnr;
+       int                     panelnr;
+       int                     brdnr;
+       int                     ioaddr;
+       int                     uartaddr;
+       int                     pagenr;
+       int                     istate;
+       int                     flags;
+       int                     baud_base;
+       int                     custom_divisor;
+       int                     close_delay;
+       int                     closing_wait;
+       int                     refcount;
+       int                     openwaitcnt;
+       int                     brklen;
+       long                    session;
+       long                    pgrp;
+       unsigned int            sigs;
+       unsigned int            rxignoremsk;
+       unsigned int            rxmarkmsk;
+       struct tty_struct       *tty;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+       struct termios          normaltermios;
+       struct termios          callouttermios;
+       struct tq_struct        tqueue;
+       stlrq_t                 tx;
+} stlport_t;
+
+typedef struct {
+       int             panelnr;
+       int             brdnr;
+       int             pagenr;
+       int             nrports;
+       int             iobase;
+       unsigned int    ackmask;
+       stlport_t       *ports[STL_PORTSPERPANEL];
+} stlpanel_t;
+
+typedef struct {
+       int             brdnr;
+       int             brdtype;
+       int             state;
+       int             nrpanels;
+       int             nrports;
+       int             irq;
+       int             irqtype;
+       unsigned int    ioaddr1;
+       unsigned int    ioaddr2;
+       unsigned int    iostatus;
+       unsigned int    ioctrl;
+       unsigned int    ioctrlval;
+       stlpanel_t      *panels[STL_MAXPANELS];
+} stlbrd_t;
+
+static stlbrd_t                *stl_brds[STL_MAXBRDS];
+
+/*
+ *     Per board state flags. Used with the state field of the board struct.
+ *     Not really much here yet!
+ */
+#define        BRD_FOUND       0x1
+
+/*
+ *     Define the port structure istate flags. These set of flags are
+ *     modified at interrupt time - so setting and reseting them needs
+ *     to be atomic. Use the bit clear/setting routines for this.
+ */
+#define        ASYI_TXBUSY     1
+#define        ASYI_TXLOW      2
+#define        ASYI_DCDCHANGE  3
+
+/*
+ *     Define an array of board names as printable strings. Handy for
+ *     referencing boards when printing trace and stuff.
+ */
+static char    *stl_brdnames[] = {
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       "EasyIO",
+       "EC8/32-AT",
+       "EC8/32-MC",
+       (char *) NULL,
+       (char *) NULL,
+       (char *) NULL,
+       "EC8/32-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ *     Hardware ID bits for the EasyIO and ECH boards. These defines apply
+ *     to the directly accessable io ports of these boards (not the cd1400
+ *     uarts - they are in cd1400.h).
+ */
+#define        EIO_8PORTRS     0x04
+#define        EIO_4PORTRS     0x05
+#define        EIO_8PORTDI     0x00
+#define        EIO_8PORTM      0x06
+#define        EIO_IDBITMASK   0x07
+#define        EIO_INTRPEND    0x08
+#define        EIO_INTEDGE     0x00
+#define        EIO_INTLEVEL    0x08
+
+#define        ECH_ID          0xa0
+#define        ECH_IDBITMASK   0xe0
+#define        ECH_BRDENABLE   0x08
+#define        ECH_BRDDISABLE  0x00
+#define        ECH_INTENABLE   0x01
+#define        ECH_INTDISABLE  0x00
+#define        ECH_INTLEVEL    0x02
+#define        ECH_INTEDGE     0x00
+#define        ECH_INTRPEND    0x01
+#define        ECH_BRDRESET    0x01
+
+#define        ECHMC_INTENABLE 0x01
+#define        ECHMC_BRDRESET  0x02
+
+#define        ECH_PNLSTATUS   2
+#define        ECH_PNL16PORT   0x20
+#define        ECH_PNLIDMASK   0x07
+#define        ECH_PNLINTRPEND 0x80
+#define        ECH_ADDR2MASK   0x1e0
+
+/*
+ *     Define the offsets within the register bank for all io registers.
+ *     These io address offsets are common to both the EIO and ECH.
+ */
+#define        EREG_ADDR       0
+#define        EREG_DATA       4
+#define        EREG_RXACK      5
+#define        EREG_TXACK      6
+#define        EREG_MDACK      7
+
+#define        EREG_BANKSIZE   8
+
+/*
+ *     Define the vector mapping bits for the programmable interrupt board
+ *     hardware. These bits encode the interrupt for the board to use - it
+ *     is software selectable (except the EIO-8M).
+ */
+static unsigned char   stl_vecmap[] = {
+       0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07,
+       0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03
+};
+
+/*
+ *     Set up enable and disable macros for the ECH boards. They require
+ *     the secondary io address space to be activated and deactivated.
+ *     This way all ECH boards can share their secondary io region.
+ *     If this is an ECH-PCI board then also need to set the page pointer
+ *     to point to the correct page.
+ */
+#define        BRDENABLE(brdnr,pagenr)                                         \
+       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
+               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE),    \
+                       stl_brds[(brdnr)]->ioctrl);                     \
+       else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI)              \
+               outb((pagenr), stl_brds[(brdnr)]->ioctrl);
+
+#define        BRDDISABLE(brdnr)                                               \
+       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
+               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),   \
+                       stl_brds[(brdnr)]->ioctrl);
+
+/*
+ *     Define the cd1400 baud rate clocks. These are used when calculating
+ *     what clock and divisor to use for the required baud rate. Also
+ *     define the maximum baud rate allowed, and the default base baud.
+ */
+static int     stl_cd1400clkdivs[] = {
+       CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+#define        STL_MAXBAUD     230400
+#define        STL_BAUDBASE    115200
+#define        STL_CLOSEDELAY  50
+
+/*****************************************************************************/
+
+/*
+ *     Define macros to extract a brd/port number from a minor number.
+ */
+#define        MKDEV2BRD(min)          (((min) & 0xc0) >> 6)
+#define        MKDEV2PORT(min)         ((min) & 0x3f)
+
+/*
+ *     Define a baud rate table that converts termios baud rate selector
+ *     into the actual baud rate value. All baud rate calculates are based
+ *     on the actual baud rate required.
+ */
+static unsigned int    stl_baudrates[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400
+};
+
+/*****************************************************************************/
+
+/*
+ *     Memory allocation vars. These keep track of what memory allocation
+ *     we can currently use. They help deal with memory in a consistent
+ *     way, whether during init or run-time.
+ */
+static int     stl_meminited = 0;
+static long    stl_memend;
+
+/*****************************************************************************/
+
+/*
+ *     Define some handy local macros...
+ */
+#ifndef        MIN
+#define        MIN(a,b)                (((a) <= (b)) ? (a) : (b))
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Declare all those functions in this driver!
+ */
+
+#ifdef MODULE
+int            init_module(void);
+void           cleanup_module(void);
+#else
+static void    stl_meminit(long base);
+static long    stl_memhalt(void);
+#endif
+static void    *stl_memalloc(int len);
+
+long           stl_init(long kmem_start);
+static int     stl_open(struct tty_struct *tty, struct file *filp);
+static void    stl_close(struct tty_struct *tty, struct file *filp);
+static int     stl_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static void    stl_putchar(struct tty_struct *tty, unsigned char ch);
+static void    stl_flushchars(struct tty_struct *tty);
+static int     stl_writeroom(struct tty_struct *tty);
+static int     stl_charsinbuffer(struct tty_struct *tty);
+static int     stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void    stl_settermios(struct tty_struct *tty, struct termios *old);
+static void    stl_throttle(struct tty_struct *tty);
+static void    stl_unthrottle(struct tty_struct *tty);
+static void    stl_stop(struct tty_struct *tty);
+static void    stl_start(struct tty_struct *tty);
+static void    stl_flushbuffer(struct tty_struct *tty);
+static void    stl_hangup(struct tty_struct *tty);
+
+static int     stl_initbrds(void);
+static int     stl_brdinit(stlbrd_t *brdp);
+static int     stl_initeio(stlbrd_t *brdp);
+static int     stl_initech(stlbrd_t *brdp);
+static int     stl_initports(stlbrd_t *brdp, stlpanel_t *panelp);
+static int     stl_mapirq(int irq);
+static void    stl_getserial(stlport_t *portp, struct serial_struct *sp);
+static int     stl_setserial(stlport_t *portp, struct serial_struct *sp);
+static void    stl_setreg(stlport_t *portp, int regnr, int value);
+static int     stl_getreg(stlport_t *portp, int regnr);
+static int     stl_updatereg(stlport_t *portp, int regnr, int value);
+static void    stl_setport(stlport_t *portp, struct termios *tiosp);
+static void    stl_getsignals(stlport_t *portp);
+static void    stl_setsignals(stlport_t *portp, int dtr, int rts);
+static void    stl_ccrwait(stlport_t *portp);
+static void    stl_enablerxtx(stlport_t *portp, int rx, int tx);
+static void    stl_startrxtx(stlport_t *portp, int rx, int tx);
+static void    stl_disableintrs(stlport_t *portp);
+static void    stl_sendbreak(stlport_t *portp, long len);
+static int     stl_waitcarrier(stlport_t *portp, struct file *filp);
+static void    stl_delay(int len);
+static void    stl_intr(int irq, struct pt_regs *regs);
+static void    stl_offintr(void *private);
+
+#ifdef CONFIG_PCI
+static int     stl_findpcibrds(void);
+#endif
+
+/*****************************************************************************/
+
+#ifdef MODULE
+
+/*
+ *     Use the kernel version number for modules.
+ */
+char   kernel_version[] = UTS_RELEASE;
+
+int init_module()
+{
+       unsigned long   flags;
+
+#if DEBUG
+       printk("init_module()\n");
+#endif
+
+       save_flags(flags);
+       cli();
+       stl_init(0);
+       restore_flags(flags);
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+void cleanup_module()
+{
+       stlbrd_t        *brdp;
+       stlpanel_t      *panelp;
+       stlport_t       *portp;
+       unsigned long   flags;
+       int             i, j, k;
+
+#if DEBUG
+       printk("cleanup_module()\n");
+#endif
+
+       printk("Unloading %s: version %s\n", stl_drvname, stl_drvversion);
+
+       save_flags(flags);
+       cli();
+
+/*
+ *     Free up all allocated resources used by the ports. This includes
+ *     memory and interrupts. As part of this process we will also do
+ *     a hangup on every open port - to try and flush out any processes
+ *     hanging onto ports.
+ */
+       i = tty_unregister_driver(&stl_serial);
+       j = tty_unregister_driver(&stl_callout);
+       if (i || j) {
+               printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+               restore_flags(flags);
+               return;
+       }
+
+       if (stl_tmpwritebuf != (char *) NULL)
+               kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
+
+       for (i = 0; (i < stl_nrbrds); i++) {
+               brdp = stl_brds[i];
+               for (j = 0; (j < STL_MAXPANELS); j++) {
+                       panelp = brdp->panels[j];
+                       if (panelp != (stlpanel_t *) NULL) {
+                               for (k = 0; (k < STL_PORTSPERPANEL); k++) {
+                                       portp = panelp->ports[k];
+                                       if (portp != (stlport_t *) NULL) {
+                                               if (portp->tty != (struct tty_struct *) NULL)
+                                                       stl_hangup(portp->tty);
+                                               if (portp->tx.buf != (char *) NULL)
+                                                       kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+                                               kfree_s(portp, sizeof(stlport_t));
+                                       }
+                               }
+                               kfree_s(panelp, sizeof(stlpanel_t));
+                       }
+                       
+               }
+
+               if (brdp->brdtype == BRD_ECH) {
+                       release_region(brdp->ioaddr1, 2);
+                       release_region(brdp->ioaddr2, 32);
+               } else if (brdp->brdtype == BRD_ECHPCI) {
+                       release_region(brdp->ioaddr1, 4);
+                       release_region(brdp->ioaddr2, 8);
+               } else if (brdp->brdtype == BRD_ECHMC) {
+                       release_region(brdp->ioaddr1, 64);
+               } else if (brdp->brdtype == BRD_EASYIO) {
+                       release_region(brdp->ioaddr1, 8);
+               }
+
+               kfree_s(brdp, sizeof(stlbrd_t));
+               stl_brds[i] = (stlbrd_t *) NULL;
+       }
+
+       for (i = 0; (i < stl_numintrs); i++)
+               free_irq(stl_gotintrs[i]);
+
+       restore_flags(flags);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Local memory allocation routines. These are used so we can deal with
+ *     memory allocation at init time and during run-time in a consistent
+ *     way. Everbody just calls the stl_memalloc routine to allocate
+ *     memory and it will do the right thing.
+ */
+
+#ifndef MODULE
+
+static void stl_meminit(long base)
+{
+       stl_memend = base;
+       stl_meminited = 1;
+}
+
+static long stl_memhalt()
+{
+       stl_meminited = 0;
+       return(stl_memend);
+}
+
+#endif
+
+static void *stl_memalloc(int len)
+{
+       void    *mem;
+
+       if (stl_meminited) {
+               mem = (void *) stl_memend;
+               stl_memend += len;
+       } else {
+               mem = (void *) kmalloc(len, GFP_KERNEL);
+       }
+       return(mem);
+}
+
+/*****************************************************************************/
+
+static int stl_open(struct tty_struct *tty, struct file *filp)
+{
+       stlport_t       *portp;
+       stlbrd_t        *brdp;
+       unsigned int    minordev;
+       int             brdnr, panelnr, portnr, rc;
+
+#if DEBUG
+       printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+#endif
+
+       minordev = MINOR(tty->device);
+       brdnr = MKDEV2BRD(minordev);
+       if (brdnr >= stl_nrbrds)
+               return(-ENODEV);
+       brdp = stl_brds[brdnr];
+       if (brdp == (stlbrd_t *) NULL)
+               return(-ENODEV);
+       minordev = MKDEV2PORT(minordev);
+       for (portnr = -1, panelnr = 0; (panelnr < STL_MAXPANELS); panelnr++) {
+               if (brdp->panels[panelnr] == (stlpanel_t *) NULL)
+                       break;
+               if (minordev < brdp->panels[panelnr]->nrports) {
+                       portnr = minordev;
+                       break;
+               }
+               minordev -= brdp->panels[panelnr]->nrports;
+       }
+       if (portnr < 0)
+               return(-ENODEV);
+
+       portp = brdp->panels[panelnr]->ports[portnr];
+       if (portp == (stlport_t *) NULL)
+               return(-ENODEV);
+
+/*
+ *     On the first open of the device setup the port hardware, and
+ *     initialize the per port data structure.
+ */
+       portp->tty = tty;
+       tty->driver_data = portp;
+       portp->refcount++;
+
+       if ((portp->flags & ASYNC_INITIALIZED) == 0) {
+               if (portp->tx.buf == (char *) NULL) {
+                       portp->tx.buf = (char *) stl_memalloc(STL_TXBUFSIZE);
+                       if (portp->tx.buf == (char *) NULL)
+                               return(-ENOMEM);
+                       portp->tx.head = portp->tx.buf;
+                       portp->tx.tail = portp->tx.buf;
+               }
+               stl_setport(portp, tty->termios);
+               stl_getsignals(portp);
+               stl_setsignals(portp, 1, 1);
+               stl_enablerxtx(portp, 1, 1);
+               stl_startrxtx(portp, 1, 0);
+               clear_bit(TTY_IO_ERROR, &tty->flags);
+               portp->flags |= ASYNC_INITIALIZED;
+       }
+
+/*
+ *     Check if this port is in the middle of closing. If so then wait
+ *     until it is closed then return error status, based on flag settings.
+ *     The sleep here does not need interrupt protection since the wakeup
+ *     for it is done with the same context.
+ */
+       if (portp->flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&portp->close_wait);
+               if (portp->flags & ASYNC_HUP_NOTIFY)
+                       return(-EAGAIN);
+               return(-ERESTARTSYS);
+       }
+
+/*
+ *     Based on type of open being done check if it can overlap with any
+ *     previous opens still in effect. If we are a normal serial device
+ *     then also we might have to wait for carrier.
+ */
+       if (tty->driver.subtype == STL_DRVTYPCALLOUT) {
+               if (portp->flags & ASYNC_NORMAL_ACTIVE)
+                       return(-EBUSY);
+               if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
+                       if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
+                                       (portp->session != current->session))
+                               return(-EBUSY);
+                       if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
+                                       (portp->pgrp != current->pgrp))
+                               return(-EBUSY);
+               }
+               portp->flags |= ASYNC_CALLOUT_ACTIVE;
+       } else {
+               if (filp->f_flags & O_NONBLOCK) {
+                       if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+                               return(-EBUSY);
+               } else {
+                       if ((rc = stl_waitcarrier(portp, filp)) != 0)
+                               return(rc);
+               }
+               portp->flags |= ASYNC_NORMAL_ACTIVE;
+       }
+
+       if ((portp->refcount == 1) && (portp->flags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == STL_DRVTYPSERIAL)
+                       *tty->termios = portp->normaltermios;
+               else
+                       *tty->termios = portp->callouttermios;
+               stl_setport(portp, tty->termios);
+       }
+
+       portp->session = current->session;
+       portp->pgrp = current->pgrp;
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Possibly need to wait for carrier (DCD signal) to come high. Say
+ *     maybe because if we are clocal then we don't need to wait...
+ */
+
+static int stl_waitcarrier(stlport_t *portp, struct file *filp)
+{
+       unsigned long   flags;
+       int             rc;
+
+#if DEBUG
+       printk("stl_waitcarrier(portp=%x,filp=%x)\n", (int) portp, (int) filp);
+#endif
+
+       rc = 0;
+
+       save_flags(flags);
+       cli();
+       portp->openwaitcnt++;
+       if (portp->refcount > 0)
+               portp->refcount--;
+
+       for (;;) {
+               if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0)
+                       stl_setsignals(portp, 1, 1);
+               if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+                       if (portp->flags & ASYNC_HUP_NOTIFY)
+                               rc = -EBUSY;
+                       else
+                               rc = -ERESTARTSYS;
+                       break;
+               }
+               if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
+                               ((portp->flags & ASYNC_CLOSING) == 0) &&
+                               ((portp->tty->termios->c_cflag & CLOCAL) ||
+                               (portp->sigs & TIOCM_CD))) {
+                       break;
+               }
+               if (current->signal & ~current->blocked) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+               interruptible_sleep_on(&portp->open_wait);
+       }
+
+       if (! tty_hung_up_p(filp))
+               portp->refcount++;
+       portp->openwaitcnt--;
+       restore_flags(flags);
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+static void stl_close(struct tty_struct *tty, struct file *filp)
+{
+       stlport_t       *portp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
+#endif
+
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       save_flags(flags);
+       cli();
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               return;
+       }
+       if (portp->refcount-- > 1) {
+               restore_flags(flags);
+               return;
+       }
+
+       portp->refcount = 0;
+       portp->flags |= ASYNC_CLOSING;
+
+       if (portp->flags & ASYNC_NORMAL_ACTIVE)
+               portp->normaltermios = *tty->termios;
+       if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+               portp->callouttermios = *tty->termios;
+
+/*
+ *     May want to wait for any data to drain before closing. The BUSY
+ *     flag keeps track of whether we are still sending or not - it allows
+ *     for the FIFO in the cd1400.
+ */
+       tty->closing = 1;
+       if (test_bit(ASYI_TXBUSY, &portp->istate)) {
+               if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+                       tty_wait_until_sent(tty, portp->closing_wait);
+       }
+
+       portp->flags &= ~ASYNC_INITIALIZED;
+       stl_disableintrs(portp);
+       if (tty->termios->c_cflag & HUPCL)
+               stl_setsignals(portp, 0, 0);
+       stl_enablerxtx(portp, 0, 0);
+       stl_flushbuffer(tty);
+       portp->istate = 0;
+       if (portp->tx.buf != (char *) NULL) {
+               kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+               portp->tx.buf = (char *) NULL;
+               portp->tx.head = (char *) NULL;
+               portp->tx.tail = (char *) NULL;
+       }
+       set_bit(TTY_IO_ERROR, &tty->flags);
+       if (tty->ldisc.flush_buffer)
+               (tty->ldisc.flush_buffer)(tty);
+
+       tty->closing = 0;
+       tty->driver_data = (void *) NULL;
+       portp->tty = (struct tty_struct *) NULL;
+
+       if (portp->openwaitcnt) {
+               if (portp->close_delay)
+                       stl_delay(portp->close_delay);
+               wake_up_interruptible(&portp->open_wait);
+       }
+
+       portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+       wake_up_interruptible(&portp->close_wait);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Wait for a specified delay period, this is not a busy-loop. It will
+ *     give up the processor while waiting. Unfortunately this has some
+ *     rather intimate knowledge of the process management stuff.
+ */
+
+static void stl_delay(int len)
+{
+#if DEBUG
+       printk("stl_delay(len=%d)\n", len);
+#endif
+       if (len > 0) {
+               current->state = TASK_INTERRUPTIBLE;
+               current->timeout = jiffies + len;
+               schedule();
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Write routine. Take data and stuff it in to the TX ring queue.
+ *     If transmit interrupts are not running then start them.
+ */
+
+static int stl_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+       stlport_t       *portp;
+       unsigned int    len, stlen;
+       unsigned long   flags;
+       unsigned char   *chbuf;
+       char            *head, *tail;
+
+#if DEBUG
+       printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+#endif
+
+       if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL))
+               return(0);
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return(0);
+       if (portp->tx.buf == (char *) NULL)
+               return(0);
+
+/*
+ *     If copying direct from user space we must cater for page faults,
+ *     causing us to "sleep" here for a while. To handle this copy in all
+ *     the data we need now, into a local buffer. Then when we got it all
+ *     copy it into the TX buffer.
+ */
+       chbuf = (unsigned char *) buf;
+       if (from_user) {
+               head = portp->tx.head;
+               tail = portp->tx.tail;
+               len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) :
+                       (tail - head - 1);
+               count = MIN(len, count);
+               
+               save_flags(flags);
+               cli();
+               down(&stl_tmpwritesem);
+               memcpy_fromfs(stl_tmpwritebuf, chbuf, count);
+               up(&stl_tmpwritesem);
+               restore_flags(flags);
+               chbuf = &stl_tmpwritebuf[0];
+       }
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       if (head >= tail) {
+               len = STL_TXBUFSIZE - (head - tail) - 1;
+               stlen = STL_TXBUFSIZE - (head - portp->tx.buf);
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = MIN(len, count);
+       count = 0;
+       while (len > 0) {
+               stlen = MIN(len, stlen);
+               memcpy(head, chbuf, stlen);
+               len -= stlen;
+               chbuf += stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= (portp->tx.buf + STL_TXBUFSIZE)) {
+                       head = portp->tx.buf;
+                       stlen = tail - head;
+               }
+       }
+       portp->tx.head = head;
+
+       clear_bit(ASYI_TXLOW, &portp->istate);
+       stl_startrxtx(portp, -1, 1);
+
+       return(count);
+}
+
+/*****************************************************************************/
+
+static void stl_putchar(struct tty_struct *tty, unsigned char ch)
+{
+       stlport_t       *portp;
+       unsigned int    len;
+       char            *head, *tail;
+
+#if DEBUG
+       printk("stl_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+       if (portp->tx.buf == (char *) NULL)
+               return;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+
+       len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head);
+       len--;
+
+       if (len > 0) {
+               *head++ = ch;
+               if (head >= (portp->tx.buf + STL_TXBUFSIZE))
+                       head = portp->tx.buf;
+       }       
+       portp->tx.head = head;
+}
+
+/*****************************************************************************/
+
+/*
+ *     If there are any characters in the buffer then make sure that TX
+ *     interrupts are on and get'em out. Normally used after the putchar
+ *     routine has been called.
+ */
+
+static void stl_flushchars(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+
+#if DEBUG
+       printk("stl_flushchars(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+       if (portp->tx.buf == (char *) NULL)
+               return;
+
+#if 0
+       if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail))
+               return;
+#endif
+       stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+static int stl_writeroom(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+       char            *head, *tail;
+
+#if DEBUG
+       printk("stl_writeroom(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(0);
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return(0);
+       if (portp->tx.buf == (char *) NULL)
+               return(0);
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       return((head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return number of chars in the TX buffer. Normally we would just
+ *     calculate the number of chars in the buffer and return that, but if
+ *     the buffer is empty and TX interrupts are still on then we return
+ *     that the buffer still has 1 char in it. This way whoever called us
+ *     will not think that ALL chars have drained - since the UART still
+ *     must have some chars in it (we are busy after all).
+ */
+
+static int stl_charsinbuffer(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+       unsigned int    size;
+       char            *head, *tail;
+
+#if DEBUG
+       printk("stl_charsinbuffer(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(0);
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return(0);
+       if (portp->tx.buf == (char *) NULL)
+               return(0);
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+       if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate))
+               size = 1;
+       return(size);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Generate the serial struct info.
+ */
+
+static void stl_getserial(stlport_t *portp, struct serial_struct *sp)
+{
+       struct serial_struct    sio;
+       stlbrd_t                *brdp;
+
+#if DEBUG
+       printk("stl_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+       memset(&sio, 0, sizeof(struct serial_struct));
+       sio.type = PORT_CIRRUS;
+       sio.line = portp->portnr;
+       sio.port = portp->ioaddr;
+       sio.flags = portp->flags;
+       sio.baud_base = portp->baud_base;
+       sio.close_delay = portp->close_delay;
+       sio.closing_wait = portp->closing_wait;
+       sio.custom_divisor = portp->custom_divisor;
+       sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+       sio.hub6 = 0;
+
+       brdp = stl_brds[portp->brdnr];
+       if (brdp != (stlbrd_t *) NULL)
+               sio.irq = brdp->irq;
+
+       memcpy_tofs(sp, &sio, sizeof(struct serial_struct));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set port according to the serial struct info.
+ *     At this point we do not do any auto-configure stuff, so we will
+ *     just quietly ignore any requests to change irq, etc.
+ */
+
+static int stl_setserial(stlport_t *portp, struct serial_struct *sp)
+{
+       struct serial_struct    sio;
+
+#if DEBUG
+       printk("stl_setserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+       memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
+       if (!suser()) {
+               if ((sio.baud_base != portp->baud_base) ||
+                               (sio.close_delay != portp->close_delay) ||
+                               ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+                       return(-EPERM);
+       } 
+
+       portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+       portp->baud_base = sio.baud_base;
+       portp->close_delay = sio.close_delay;
+       portp->closing_wait = sio.closing_wait;
+       portp->custom_divisor = sio.custom_divisor;
+       stl_setport(portp, portp->tty->termios);
+       return(0);
+}
+
+/*****************************************************************************/
+
+static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       stlport_t       *portp;
+       int             rc;
+
+#if DEBUG
+       printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return(-ENODEV);
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return(-ENODEV);
+
+       rc = 0;
+
+       switch (cmd) {
+       case TCSBRK:
+               if ((rc = tty_check_change(tty)) == 0) {
+                       tty_wait_until_sent(tty, 0);
+                       if (! arg)
+                               stl_sendbreak(portp, 250);
+               }
+               break;
+       case TCSBRKP:
+               if ((rc = tty_check_change(tty)) == 0) {
+                       tty_wait_until_sent(tty, 0);
+                       stl_sendbreak(portp, (arg ? (arg * 100) : 250));
+               }
+               break;
+       case TIOCGSOFTCAR:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
+                       put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+               break;
+       case TIOCSSOFTCAR:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+               }
+               break;
+       case TIOCMGET:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+                       stl_getsignals(portp);
+                       put_fs_long(portp->sigs, (unsigned long *) arg);
+               }
+               break;
+       case TIOCMBIS:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
+               }
+               break;
+       case TIOCMBIC:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
+               }
+               break;
+       case TIOCMSET:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+                       arg = get_fs_long((unsigned long *) arg);
+                       stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
+               }
+               break;
+       case TIOCGSERIAL:
+               if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+                       stl_getserial(portp, (struct serial_struct *) arg);
+               break;
+       case TIOCSSERIAL:
+               if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+                       rc = stl_setserial(portp, (struct serial_struct *) arg);
+               break;
+       case TIOCSERCONFIG:
+       case TIOCSERGWILD:
+       case TIOCSERSWILD:
+       case TIOCSERGETLSR:
+       case TIOCSERGSTRUCT:
+       case TIOCSERGETMULTI:
+       case TIOCSERSETMULTI:
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+
+       return(rc);
+}
+
+/*****************************************************************************/
+
+static void stl_settermios(struct tty_struct *tty, struct termios *old)
+{
+       stlport_t       *portp;
+       struct termios  *tiosp;
+
+#if DEBUG
+       printk("stl_settermios(tty=%x,old=%x)\n", (int) tty, (int) old);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       tiosp = tty->termios;
+       if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+               return;
+
+       stl_setport(portp, tiosp);
+       stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1);
+       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
+               tty->hw_stopped = 0;
+               stl_start(tty);
+       }
+       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+               wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Attempt to flow control who ever is sending us data. Based on termios
+ *     settings use software or/and hardware flow control.
+ */
+
+static void stl_throttle(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_throttle(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       if (tty->termios->c_iflag & IXOFF) {
+               stl_ccrwait(portp);
+               stl_setreg(portp, CCR, CCR_SENDSCHR2);
+               stl_ccrwait(portp);
+       }
+       if (tty->termios->c_cflag & CRTSCTS) {
+               stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0));
+               stl_setreg(portp, MSVR2, 0);
+       }
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Unflow control the device sending us data...
+ */
+
+static void stl_unthrottle(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_unthrottle(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       if (tty->termios->c_iflag & IXOFF) {
+               stl_ccrwait(portp);
+               stl_setreg(portp, CCR, CCR_SENDSCHR1);
+               stl_ccrwait(portp);
+       }
+/*
+ *     Question: should we return RTS to what it was before? It may have
+ *     been set by an ioctl... Suppose not, since if you have hardware
+ *     flow control set then it is pretty silly to go and set the RTS line
+ *     by hand.
+ */
+       if (tty->termios->c_cflag & CRTSCTS) {
+               stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
+               stl_setreg(portp, MSVR2, MSVR2_RTS);
+       }
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Stop the transmitter. Basically to do this we will just turn TX
+ *     interrupts off.
+ */
+
+static void stl_stop(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+
+#if DEBUG
+       printk("stl_stop(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       stl_startrxtx(portp, -1, 0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start the transmitter again. Just turn TX interrupts back on.
+ */
+
+static void stl_start(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+
+#if DEBUG
+       printk("stl_start(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Hangup this port. This is pretty much like closing the port, only
+ *     a little more brutal. No waiting for data to drain. Shutdown the
+ *     port and maybe drop signals.
+ */
+
+static void stl_hangup(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+
+#if DEBUG
+       printk("stl_hangup(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       portp->flags &= ~ASYNC_INITIALIZED;
+       stl_disableintrs(portp);
+       if (tty->termios->c_cflag & HUPCL)
+               stl_setsignals(portp, 0, 0);
+       stl_enablerxtx(portp, 0, 0);
+       stl_flushbuffer(tty);
+       portp->istate = 0;
+       set_bit(TTY_IO_ERROR, &tty->flags);
+       if (portp->tx.buf != (char *) NULL) {
+               kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+               portp->tx.buf = (char *) NULL;
+               portp->tx.head = (char *) NULL;
+               portp->tx.tail = (char *) NULL;
+       }
+       tty->driver_data = (void *) NULL;
+       portp->tty = (struct tty_struct *) NULL;
+       portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+       portp->refcount = 0;
+       wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+static void stl_flushbuffer(struct tty_struct *tty)
+{
+       stlport_t       *portp;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_flushbuffer(tty=%x)\n", (int) tty);
+#endif
+
+       if (tty == (struct tty_struct *) NULL)
+               return;
+       portp = tty->driver_data;
+       if (portp == (stlport_t *) NULL)
+               return;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_ccrwait(portp);
+       stl_setreg(portp, CCR, CCR_TXFLUSHFIFO);
+       stl_ccrwait(portp);
+       portp->tx.tail = portp->tx.head;
+       BRDDISABLE(portp->brdnr);
+       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);
+}
+
+/*****************************************************************************/
+
+/*
+ *     These functions get/set/update the registers of the cd1400 UARTs.
+ *     Access to the cd1400 registers is via an address/data io port pair.
+ *     (Maybe should make this inline...)
+ */
+
+static int stl_getreg(stlport_t *portp, int regnr)
+{
+       outb((regnr + portp->uartaddr), portp->ioaddr);
+       return(inb(portp->ioaddr + EREG_DATA));
+}
+
+static void stl_setreg(stlport_t *portp, int regnr, int value)
+{
+       outb((regnr + portp->uartaddr), portp->ioaddr);
+       outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_updatereg(stlport_t *portp, int regnr, int value)
+{
+       outb((regnr + portp->uartaddr), portp->ioaddr);
+       if (inb(portp->ioaddr + EREG_DATA) != value) {
+               outb(value, portp->ioaddr + EREG_DATA);
+               return(1);
+       }
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *     chars is pretty simple, stuff as many as possible from the TX buffer
+ *     into the cd1400 FIFO. Must also handle TX breaks here, since they
+ *     are embedded as commands in the data stream. Oh no, had to use a goto!
+ *     This could be optimized more, will do when I get time...
+ *     In practice it is possible that interrupts are enabled but that the
+ *     port has been hung up. Need to handle not having any TX buffer here,
+ *     this is done by using the side effect that head and tail will also
+ *     be NULL if the buffer has been freed.
+ */
+
+static inline void stl_txisr(stlpanel_t *panelp, int ioaddr)
+{
+       stlport_t       *portp;
+       int             len, stlen;
+       char            *head, *tail;
+       unsigned char   ioack, srer;
+
+#if DEBUG
+       printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+       ioack = inb(ioaddr + EREG_TXACK);
+       if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+               printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+
+/*
+ *     Unfortunately we need to handle breaks in the data stream, since
+ *     this is the only way to generate them on the cd1400. Do it now if
+ *     a break is to be sent.
+ */
+       if (portp->brklen != 0) {
+               if (portp->brklen > 0) {
+                       outb((TDR + portp->uartaddr), ioaddr);
+                       outb(ETC_CMD, (ioaddr + EREG_DATA));
+                       outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+                       outb(ETC_CMD, (ioaddr + EREG_DATA));
+                       outb(ETC_DELAY, (ioaddr + EREG_DATA));
+                       outb(portp->brklen, (ioaddr + EREG_DATA));
+                       outb(ETC_CMD, (ioaddr + EREG_DATA));
+                       outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+                       portp->brklen = -1;
+                       goto stl_txalldone;
+               } else {
+                       outb((COR2 + portp->uartaddr), ioaddr);
+                       outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
+                       portp->brklen = 0;
+               }
+       }
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+       if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+               set_bit(ASYI_TXLOW, &portp->istate);
+               queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+       }
+
+       if (len == 0) {
+               outb((SRER + portp->uartaddr), ioaddr);
+               srer = inb(ioaddr + EREG_DATA);
+               if (srer & SRER_TXDATA) {
+                       srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+               } else {
+                       srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+                       clear_bit(ASYI_TXBUSY, &portp->istate);
+               }
+               outb(srer, (ioaddr + EREG_DATA));
+       } else {
+               len = MIN(len, CD1400_TXFIFOSIZE);
+               stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+               outb((TDR + portp->uartaddr), ioaddr);
+               outsb((ioaddr + EREG_DATA), tail, stlen);
+               len -= stlen;
+               tail += stlen;
+               if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+                       tail = portp->tx.buf;
+               if (len > 0) {
+                       outsb((ioaddr + EREG_DATA), tail, len);
+                       tail += len;
+               }
+               portp->tx.tail = tail;
+       }
+
+stl_txalldone:
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Receive character interrupt handler. Determine if we have good chars
+ *     or bad chars and then process appropriately. Good chars are easy
+ *     just shove the lot into the RX buffer and set all status byte to 0.
+ *     If a bad RX char then process as required. This routine needs to be
+ *     fast!  In practice it is possible that we get an interrupt on a port
+ *     that is closed. This can happen on hangups - since they completely
+ *     shutdown a port not in user context. Need to handle this case.
+ */
+
+static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr)
+{
+       stlport_t               *portp;
+       struct tty_struct       *tty;
+       unsigned int            ioack, len, buflen;
+       unsigned char           status;
+       char                    ch;
+       static char             unwanted[CD1400_RXFIFOSIZE];
+
+#if DEBUG
+       printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+       ioack = inb(ioaddr + EREG_RXACK);
+       if ((ioack & panelp->ackmask) != 0) {
+               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+       tty = portp->tty;
+
+       if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+               outb((RDCR + portp->uartaddr), ioaddr);
+               len = inb(ioaddr + EREG_DATA);
+               if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
+                               ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+                       outb((RDSR + portp->uartaddr), ioaddr);
+                       insb((ioaddr + EREG_DATA), &unwanted[0], len);
+               } else {
+                       len = MIN(len, buflen);
+                       if (len > 0) {
+                               outb((RDSR + portp->uartaddr), ioaddr);
+                               insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
+                               memset(tty->flip.flag_buf_ptr, 0, len);
+                               tty->flip.flag_buf_ptr += len;
+                               tty->flip.char_buf_ptr += len;
+                               tty->flip.count += len;
+                               tty_schedule_flip(tty);
+                       }
+               }
+       } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+               outb((RDSR + portp->uartaddr), ioaddr);
+               status = inb(ioaddr + EREG_DATA);
+               ch = inb(ioaddr + EREG_DATA);
+               if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
+                       if (portp->rxmarkmsk & status) {
+                               if (status & ST_BREAK) {
+                                       status = TTY_BREAK;
+#ifndef MODULE
+                                       if (portp->flags & ASYNC_SAK)
+                                               do_SAK(tty);
+#endif
+                               } else if (status & ST_PARITY) {
+                                       status = TTY_PARITY;
+                               } else if (status & ST_FRAMING) {
+                                       status = TTY_FRAME;
+                               } else if(status & ST_OVERRUN) {
+                                       status = TTY_OVERRUN;
+                               } else {
+                                       status = 0;
+                               }
+                       } else {
+                               status = 0;
+                       }
+                       if (tty->flip.char_buf_ptr != (char *) NULL) {
+                               if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+                                       *tty->flip.flag_buf_ptr++ = status;
+                                       *tty->flip.char_buf_ptr++ = ch;
+                                       tty->flip.count++;
+                               }
+                               tty_schedule_flip(tty);
+                       }
+               }
+       } else {
+               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+               return;
+       }
+
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Modem interrupt handler. The is called when the modem signal line
+ *     (DCD) has changed state. Leave most of the work to the off-level
+ *     processing routine.
+ */
+
+static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr)
+{
+       stlport_t       *portp;
+       unsigned int    ioack;
+       unsigned char   misr;
+
+#if DEBUG
+       printk("stl_mdmisr(panelp=%x)\n", (int) panelp);
+#endif
+
+       ioack = inb(ioaddr + EREG_MDACK);
+       if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+               printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+
+       outb((MISR + portp->uartaddr), ioaddr);
+       misr = inb(ioaddr + EREG_DATA);
+       if (misr & MISR_DCD) {
+               set_bit(ASYI_DCDCHANGE, &portp->istate);
+               queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+       }
+
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt handler for EIO and ECH boards. This code ain't all that
+ *     pretty, but the idea is to make it as fast as possible. This code is
+ *     well suited to be assemblerized :-)  We don't use the general purpose
+ *     register access functions here, for speed we will go strait to the
+ *     io region.
+ */
+
+static void stl_intr(int irq, struct pt_regs *regs)
+{
+       stlbrd_t        *brdp;
+       stlpanel_t      *panelp;
+       unsigned char   svrtype;
+       int             i, panelnr, iobase;
+
+#if DEBUG
+       printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+#endif
+
+       panelp = (stlpanel_t *) NULL;
+       for (i = 0; (i < stl_nrbrds); ) {
+               if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) {
+                       i++;
+                       continue;
+               }
+               if (brdp->state == 0) {
+                       i++;
+                       continue;
+               }
+/*
+ *             The following section of code handles the subtle differences
+ *             between board types. It is sort of similar, but different
+ *             enough to handle each separately.
+ */
+               if (brdp->brdtype == BRD_EASYIO) {
+                       if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) {
+                               i++;
+                               continue;
+                       }
+                       panelp = brdp->panels[0];
+                       iobase = panelp->iobase;
+                       outb(SVRR, iobase);
+                       svrtype = inb(iobase + EREG_DATA);
+                       if (brdp->nrports > 4) {
+                               outb((SVRR + 0x80), iobase);
+                               svrtype |= inb(iobase + EREG_DATA);
+                       }
+               } else if (brdp->brdtype == BRD_ECH) {
+                       if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
+                               i++;
+                               continue;
+                       }
+                       outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+                       for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+                               panelp = brdp->panels[panelnr];
+                               iobase = panelp->iobase;
+                               if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                       break;
+                               if (panelp->nrports > 8) {
+                                       iobase += 0x8;
+                                       if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                               break;
+                               }
+                       }       
+                       if (panelnr >= brdp->nrpanels) {
+                               i++;
+                               continue;
+                       }
+                       outb(SVRR, iobase);
+                       svrtype = inb(iobase + EREG_DATA);
+                       outb((SVRR + 0x80), iobase);
+                       svrtype |= inb(iobase + EREG_DATA);
+               } else if (brdp->brdtype == BRD_ECHPCI) {
+                       iobase = brdp->ioaddr2;
+                       for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+                               panelp = brdp->panels[panelnr];
+                               outb(panelp->pagenr, brdp->ioctrl);
+                               if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                       break;
+                               if (panelp->nrports > 8) {
+                                       outb((panelp->pagenr + 1), brdp->ioctrl);
+                                       if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                               break;
+                               }
+                       }       
+                       if (panelnr >= brdp->nrpanels) {
+                               i++;
+                               continue;
+                       }
+                       outb(SVRR, iobase);
+                       svrtype = inb(iobase + EREG_DATA);
+                       outb((SVRR + 0x80), iobase);
+                       svrtype |= inb(iobase + EREG_DATA);
+               } else if (brdp->brdtype == BRD_ECHMC) {
+                       if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
+                               i++;
+                               continue;
+                       }
+                       for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+                               panelp = brdp->panels[panelnr];
+                               iobase = panelp->iobase;
+                               if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                       break;
+                               if (panelp->nrports > 8) {
+                                       iobase += 0x8;
+                                       if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+                                               break;
+                               }
+                       }       
+                       if (panelnr >= brdp->nrpanels) {
+                               i++;
+                               continue;
+                       }
+                       outb(SVRR, iobase);
+                       svrtype = inb(iobase + EREG_DATA);
+                       outb((SVRR + 0x80), iobase);
+                       svrtype |= inb(iobase + EREG_DATA);
+               } else {
+                       printk("STALLION: unknown board type=%x\n", brdp->brdtype);
+                       i++;
+                       continue;
+               }
+
+/*
+ *             We have determined what type of service is required for a
+ *             port. From here on in the service of a port is the same no
+ *             matter what the board type...
+ */
+               if (svrtype & SVRR_RX)
+                       stl_rxisr(panelp, iobase);
+               if (svrtype & SVRR_TX)
+                       stl_txisr(panelp, iobase);
+               if (svrtype & SVRR_MDM)
+                       stl_mdmisr(panelp, iobase);
+
+               if (brdp->brdtype == BRD_ECH)
+                       outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Service an off-level request for some channel.
+ */
+
+static void stl_offintr(void *private)
+{
+       stlport_t               *portp;
+       struct tty_struct       *tty;
+       unsigned int            oldsigs;
+
+       portp = private;
+#if DEBUG
+       printk("stl_offintr(portp=%x)\n", (int) portp);
+#endif
+
+       if (portp == (stlport_t *) NULL)
+               return;
+       tty = portp->tty;
+       if (tty == (struct tty_struct *) NULL)
+               return;
+
+       if (test_bit(ASYI_TXLOW, &portp->istate)) {
+               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_bit(ASYI_DCDCHANGE, &portp->istate)) {
+               clear_bit(ASYI_DCDCHANGE, &portp->istate);
+               oldsigs = portp->sigs;
+               stl_getsignals(portp);
+               if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+                       wake_up_interruptible(&portp->open_wait);
+               if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+                       if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+                                       (portp->flags & ASYNC_CALLOUT_NOHUP))) {
+                               tty_hangup(tty);
+                       }
+               }
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Wait for the command register to be ready. We will poll this,
+ *     since it won't usually take too long to be ready.
+ */
+
+static void stl_ccrwait(stlport_t *portp)
+{
+       int     i;
+
+       for (i = 0; (i < CCR_MAXWAIT); i++) {
+               if (stl_getreg(portp, CCR) == 0) {
+                       return;
+               }
+       }
+
+       printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set up the cd1400 registers for a port based on the termios port
+ *     settings.
+ */
+
+static void stl_setport(stlport_t *portp, struct termios *tiosp)
+{
+       stlbrd_t        *brdp;
+       unsigned long   flags;
+       unsigned int    clkdiv, baudrate;
+       unsigned char   cor1, cor2, cor3;
+       unsigned char   cor4, cor5, ccr;
+       unsigned char   srer, sreron, sreroff;
+       unsigned char   mcor1, mcor2, rtpr;
+       unsigned char   clk, div;
+
+       cor1 = 0;
+       cor2 = 0;
+       cor3 = 0;
+       cor4 = 0;
+       cor5 = 0;
+       ccr = 0;
+       rtpr = 0;
+       clk = 0;
+       div = 0;
+       mcor1 = 0;
+       mcor2 = 0;
+       sreron = 0;
+       sreroff = 0;
+
+       brdp = stl_brds[portp->brdnr];
+       if (brdp == (stlbrd_t *) NULL)
+               return;
+
+/*
+ *     Set up the RX char ignore mask with those RX error types we
+ *     can ignore. We can get the cd1400 to help us out a little here,
+ *     it will ignore parity errors and breaks for us.
+ */
+       portp->rxignoremsk = 0;
+       if (tiosp->c_iflag & IGNPAR) {
+               portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+               cor1 |= COR1_PARIGNORE;
+       }
+       if (tiosp->c_iflag & IGNBRK) {
+               portp->rxignoremsk |= ST_BREAK;
+               cor4 |= COR4_IGNBRK;
+       }
+
+       portp->rxmarkmsk = ST_OVERRUN;
+       if (tiosp->c_iflag & (INPCK | PARMRK))
+               portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+       if (tiosp->c_iflag & BRKINT)
+               portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ *     Go through the char size, parity and stop bits and set all the
+ *     option register appropriately.
+ */
+       switch (tiosp->c_cflag & CSIZE) {
+       case CS5:
+               cor1 |= COR1_CHL5;
+               break;
+       case CS6:
+               cor1 |= COR1_CHL6;
+               break;
+       case CS7:
+               cor1 |= COR1_CHL7;
+               break;
+       default:
+               cor1 |= COR1_CHL8;
+               break;
+       }
+
+       if (tiosp->c_cflag & CSTOPB)
+               cor1 |= COR1_STOP2;
+       else
+               cor1 |= COR1_STOP1;
+
+       if (tiosp->c_cflag & PARENB) {
+               if (tiosp->c_cflag & PARODD)
+                       cor1 |= (COR1_PARENB | COR1_PARODD);
+               else
+                       cor1 |= (COR1_PARENB | COR1_PAREVEN);
+       } else {
+               cor1 |= COR1_PARNONE;
+       }
+
+/*
+ *     Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
+ *     space for hardware flow control and the like. This should be set to
+ *     VMIN. Also here we will set the RX data timeout to 10ms - this should
+ *     really be based on VTIME.
+ */
+       cor3 |= FIFO_RXTHRESHOLD;
+       rtpr = 2;
+
+/*
+ *     Calculate the baud rate timers. For now we will just assume that
+ *     the input and output baud are the same. Could have used a baud
+ *     table here, but this way we can generate virtually any baud rate
+ *     we like!
+ */
+       baudrate = tiosp->c_cflag & CBAUD;
+       if (baudrate & CBAUDEX) {
+               baudrate &= ~CBAUDEX;
+               if ((baudrate < 1) || (baudrate > 2))
+                       tiosp->c_cflag &= ~CBAUDEX;
+               else
+                       baudrate += 15;
+       }
+       baudrate = stl_baudrates[baudrate];
+       if ((tiosp->c_cflag & CBAUD) == B38400) {
+               if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       baudrate = 57600;
+               else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       baudrate = 115200;
+               else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+                       baudrate = (portp->baud_base / portp->custom_divisor);
+       }
+       if (baudrate > STL_MAXBAUD)
+               baudrate = STL_MAXBAUD;
+
+       if (baudrate > 0) {
+               for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
+                       clkdiv = ((CD1400_CLKHZ / stl_cd1400clkdivs[clk]) / baudrate);
+                       if (clkdiv < 0x100)
+                               break;
+               }
+               div = (unsigned char) clkdiv;
+       }
+
+/*
+ *     Check what form of modem signaling is required and set it up.
+ */
+       if ((tiosp->c_cflag & CLOCAL) == 0) {
+               mcor1 |= MCOR1_DCD;
+               mcor2 |= MCOR2_DCD;
+               sreron |= SRER_MODEM;
+       }
+
+/*
+ *     Setup cd1400 enhanced modes if we can. In particular we want to
+ *     handle as much of the flow control as possbile automatically. As
+ *     well as saving a few CPU cycles it will also greatly improve flow
+ *     control reliablilty.
+ */
+       if (tiosp->c_iflag & IXON) {
+               cor2 |= COR2_TXIBE;
+               cor3 |= (COR3_FCT | COR3_SCD12);
+               if (tiosp->c_iflag & IXANY)
+                       cor2 |= COR2_IXM;
+       }
+
+       if (tiosp->c_cflag & CRTSCTS) {
+               cor2 |= COR2_CTSAE;
+               mcor1 |= FIFO_RTSTHRESHOLD;
+       }
+
+/*
+ *     All register cd1400 register values calculated so go through and set
+ *     them all up.
+ */
+
+#if DEBUG
+       printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+       printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
+       printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
+       printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+       printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x3));
+       srer = stl_getreg(portp, SRER);
+       stl_setreg(portp, SRER, 0);
+       if (stl_updatereg(portp, COR1, cor1))
+               ccr = 1;
+       if (stl_updatereg(portp, COR2, cor2))
+               ccr = 1;
+       if (stl_updatereg(portp, COR3, cor3))
+               ccr = 1;
+       if (ccr) {
+               stl_ccrwait(portp);
+               stl_setreg(portp, CCR, CCR_CORCHANGE);
+       }
+       stl_setreg(portp, COR4, cor4);
+       stl_setreg(portp, COR5, cor5);
+       stl_setreg(portp, MCOR1, mcor1);
+       stl_setreg(portp, MCOR2, mcor2);
+       if (baudrate > 0) {
+               stl_setreg(portp, TCOR, clk);
+               stl_setreg(portp, TBPR, div);
+               stl_setreg(portp, RCOR, clk);
+               stl_setreg(portp, RBPR, div);
+       }
+       stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+       stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+       stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+       stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+       stl_setreg(portp, RTPR, rtpr);
+       mcor1 = stl_getreg(portp, MSVR1);
+       if (mcor1 & MSVR1_DCD)
+               portp->sigs |= TIOCM_CD;
+       else
+               portp->sigs &= ~TIOCM_CD;
+       stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set the state of the DTR and RTS signals.
+ */
+
+static void stl_setsignals(stlport_t *portp, int dtr, int rts)
+{
+       unsigned char   msvr1, msvr2;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
+
+       msvr1 = 0;
+       msvr2 = 0;
+       if (dtr > 0)
+               msvr1 = MSVR1_DTR;
+       if (rts > 0)
+               msvr2 = MSVR2_RTS;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       if (rts >= 0)
+               stl_setreg(portp, MSVR2, msvr2);
+       if (dtr >= 0)
+               stl_setreg(portp, MSVR1, msvr1);
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Get the state of the signals.
+ */
+
+static void stl_getsignals(stlport_t *portp)
+{
+       unsigned char   msvr1, msvr2;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_getsignals(portp=%x)\n", (int) portp);
+#endif
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       msvr1 = stl_getreg(portp, MSVR1);
+       msvr2 = stl_getreg(portp, MSVR2);
+       BRDDISABLE(portp->brdnr);
+       portp->sigs = 0;
+       portp->sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+       portp->sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+       portp->sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+       portp->sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+       portp->sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+       portp->sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Enable/Disable the Transmitter and/or Reciever.
+ */
+
+static void stl_enablerxtx(stlport_t *portp, int rx, int tx)
+{
+       unsigned char   ccr;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+       ccr = 0;
+
+       if (tx == 0)
+               ccr |= CCR_TXDISABLE;
+       else if (tx > 0)
+               ccr |= CCR_TXENABLE;
+       if (rx == 0)
+               ccr |= CCR_RXDISABLE;
+       else if (rx > 0)
+               ccr |= CCR_RXENABLE;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_ccrwait(portp);
+       stl_setreg(portp, CCR, ccr);
+       stl_ccrwait(portp);
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start/stop the Transmitter and/or Reciever.
+ */
+
+static void stl_startrxtx(stlport_t *portp, int rx, int tx)
+{
+       unsigned char   sreron, sreroff;
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+
+       sreron = 0;
+       sreroff = 0;
+       if (tx == 0)
+               sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+       else if (tx == 1)
+               sreron |= SRER_TXDATA;
+       else if (tx >= 2)
+               sreron |= SRER_TXEMPTY;
+       if (rx == 0)
+               sreroff |= SRER_RXDATA;
+       else if (rx > 0)
+               sreron |= SRER_RXDATA;
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron));
+       BRDDISABLE(portp->brdnr);
+       if (tx > 0)
+               set_bit(ASYI_TXBUSY, &portp->istate);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Disable all interrupts from this port.
+ */
+
+static void stl_disableintrs(stlport_t *portp)
+{
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_disableintrs(portp=%x)\n", (int) portp);
+#endif
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_setreg(portp, SRER, 0);
+       BRDDISABLE(portp->brdnr);
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static void stl_sendbreak(stlport_t *portp, long len)
+{
+       unsigned long   flags;
+
+#if DEBUG
+       printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+#endif
+
+       save_flags(flags);
+       cli();
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC));
+       stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
+       BRDDISABLE(portp->brdnr);
+       len = len / 5;
+       portp->brklen = (len > 255) ? 255 : len;
+       restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Map in interrupt vector to this driver. Check that we don't
+ *     already have this vector mapped, we might be sharing this
+ *     interrupt accross multiple boards.
+ */
+
+static int stl_mapirq(int irq)
+{
+       int     rc, i;
+
+#if DEBUG
+       printk("stl_mapirq(irq=%d)\n", irq);
+#endif
+
+       rc = 0;
+       for (i = 0; (i < stl_numintrs); i++) {
+               if (stl_gotintrs[i] == irq)
+                       break;
+       }
+       if (i >= stl_numintrs) {
+               if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname) != 0) {
+                       printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
+                       rc = -ENODEV;
+               } else {
+                       stl_gotintrs[stl_numintrs++] = irq;
+               }
+       }
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try and find and initialize all the ports on a panel. We don't care
+ *     what sort of board these ports are on - since the port io registers
+ *     are almost identical when dealing with ports.
+ */
+
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
+{
+       stlport_t       *portp;
+       unsigned int    chipmask;
+       unsigned int    gfrcr;
+       int             nrchips, uartaddr, ioaddr;
+       int             i, j;
+
+#if DEBUG
+       printk("stl_initports(panelp=%x)\n", (int) panelp);
+#endif
+
+       BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ *     Check that each chip is present and started up OK.
+ */
+       chipmask = 0;
+       nrchips = panelp->nrports / CD1400_PORTS;
+       for (i = 0; (i < nrchips); i++) {
+               if (brdp->brdtype == BRD_ECHPCI) {
+                       outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+                       ioaddr = panelp->iobase;
+               } else {
+                       ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+               }
+               uartaddr = (i & 0x01) ? 0x080 : 0;
+               outb((GFRCR + uartaddr), ioaddr);
+               outb(0, (ioaddr + EREG_DATA));
+               outb((CCR + uartaddr), ioaddr);
+               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+               outb((GFRCR + uartaddr), ioaddr);
+               for (j = 0; (j < CCR_MAXWAIT); j++) {
+                       if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+                               break;
+               }
+               if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+                       printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+                       continue;
+               }
+               chipmask |= (0x1 << i);
+               outb((PPR + uartaddr), ioaddr);
+               outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+       }
+
+/*
+ *     All cd1400's are initialized (if found!). Now go through and setup
+ *     each ports data structures. Also init the LIVR register of cd1400
+ *     for each port.
+ */
+       ioaddr = panelp->iobase;
+       for (i = 0; (i < panelp->nrports); i++) {
+               if (brdp->brdtype == BRD_ECHPCI) {
+                       outb((panelp->pagenr + (i >> 3)), brdp->ioctrl);
+                       ioaddr = panelp->iobase;
+               } else {
+                       ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3));
+               }
+               if ((chipmask & (0x1 << (i / 4))) == 0)
+                       continue;
+               portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
+               if (portp == (stlport_t *) NULL) {
+                       printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
+                       break;
+               }
+               memset(portp, 0, sizeof(stlport_t));
+               portp->portnr = i;
+               portp->brdnr = panelp->brdnr;
+               portp->panelnr = panelp->panelnr;
+               portp->ioaddr = ioaddr;
+               portp->uartaddr = (i & 0x04) << 5;
+               portp->pagenr = panelp->pagenr + (i >> 3);
+               portp->baud_base = STL_BAUDBASE;
+               portp->close_delay = STL_CLOSEDELAY;
+               portp->closing_wait = 30 * HZ;
+               portp->normaltermios = stl_deftermios;
+               portp->callouttermios = stl_deftermios;
+               portp->tqueue.routine = stl_offintr;
+               portp->tqueue.data = portp;
+               stl_setreg(portp, CAR, (i & 0x03));
+               stl_setreg(portp, LIVR, (i << 3));
+               panelp->ports[i] = portp;
+       }
+
+       BRDDISABLE(panelp->brdnr);
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find and initialize an EasyIO board.
+ */
+
+static int stl_initeio(stlbrd_t *brdp)
+{
+       stlpanel_t      *panelp;
+       unsigned int    status;
+       int             rc;
+
+#if DEBUG
+       printk("stl_initeio(brdp=%x)\n", (int) brdp);
+#endif
+
+       brdp->ioctrl = brdp->ioaddr1 + 1;
+       brdp->iostatus = brdp->ioaddr1 + 2;
+
+       status = inb(brdp->iostatus);
+       switch (status & EIO_IDBITMASK) {
+       case EIO_8PORTRS:
+       case EIO_8PORTM:
+       case EIO_8PORTDI:
+               brdp->nrports = 8;
+               break;
+       case EIO_4PORTRS:
+               brdp->nrports = 4;
+               break;
+       default:
+               return(-ENODEV);
+       }
+
+       request_region(brdp->ioaddr1, 8, "serial(EIO)");
+
+/*
+ *     Check that the supplied IRQ is good and then use it to setup the
+ *     programmable interrupt bits on EIO board. Also set the edge/level
+ *     triggered interrupt bit.
+ */
+       if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                       (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+               printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+               return(-EINVAL);
+       }
+       outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
+
+       panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+       if (panelp == (stlpanel_t *) NULL) {
+               printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+               return(-ENOMEM);
+       }
+       memset(panelp, 0, sizeof(stlpanel_t));
+
+       panelp->brdnr = brdp->brdnr;
+       panelp->panelnr = 0;
+       panelp->nrports = brdp->nrports;
+       panelp->iobase = brdp->ioaddr1;
+       brdp->panels[0] = panelp;
+       brdp->nrpanels = 1;
+       brdp->state |= BRD_FOUND;
+       rc = stl_mapirq(brdp->irq);
+       return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ECH board and initialize it. This code is capable of
+ *     dealing with all types of ECH board.
+ */
+
+static int stl_initech(stlbrd_t *brdp)
+{
+       stlpanel_t      *panelp;
+       unsigned int    status, nxtid;
+       int             panelnr, ioaddr, i;
+
+#if DEBUG
+       printk("stl_initech(brdp=%x)\n", (int) brdp);
+#endif
+
+/*
+ *     Set up the initial board register contents for boards. This varys a
+ *     bit between the different board types. So we need to handle each
+ *     separately. Also do a check that the supplied IRQ is good.
+ */
+       if (brdp->brdtype == BRD_ECH) {
+               brdp->ioctrl = brdp->ioaddr1 + 1;
+               brdp->iostatus = brdp->ioaddr1 + 1;
+               status = inb(brdp->iostatus);
+               if ((status & ECH_IDBITMASK) != ECH_ID)
+                       return(-ENODEV);
+
+               if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                               (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+                       printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+                       return(-EINVAL);
+               }
+               status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+               status |= (stl_vecmap[brdp->irq] << 1);
+               outb((status | ECH_BRDRESET), brdp->ioaddr1);
+               brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+               outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+               outb(status, brdp->ioaddr1);
+
+               request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
+               request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
+       } else if (brdp->brdtype == BRD_ECHMC) {
+               brdp->ioctrl = brdp->ioaddr1 + 0x20;
+               brdp->iostatus = brdp->ioctrl;
+               status = inb(brdp->iostatus);
+               if ((status & ECH_IDBITMASK) != ECH_ID)
+                       return(-ENODEV);
+
+               if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                               (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+                       printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+                       return(-EINVAL);
+               }
+               outb(ECHMC_BRDRESET, brdp->ioctrl);
+               outb(ECHMC_INTENABLE, brdp->ioctrl);
+
+               request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
+       } else if (brdp->brdtype == BRD_ECHPCI) {
+               brdp->ioctrl = brdp->ioaddr1 + 2;
+               request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
+               request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
+       }
+
+/*
+ *     Scan through the secondary io address space looking for panels.
+ *     As we find'em allocate and initialize panel structures for each.
+ */
+       ioaddr = brdp->ioaddr2;
+       panelnr = 0;
+       nxtid = 0;
+
+       for (i = 0; (i < STL_MAXPANELS); i++) {
+               if (brdp->brdtype == BRD_ECHPCI) {
+                       outb(nxtid, brdp->ioctrl);
+                       ioaddr = brdp->ioaddr2;
+               }
+               status = inb(ioaddr + ECH_PNLSTATUS);
+               if ((status & ECH_PNLIDMASK) != nxtid)
+                       break;
+               panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+               if (panelp == (stlpanel_t *) NULL) {
+                       printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+                       break;
+               }
+               memset(panelp, 0, sizeof(stlpanel_t));
+               panelp->brdnr = brdp->brdnr;
+               panelp->panelnr = panelnr;
+               panelp->iobase = ioaddr;
+               panelp->pagenr = nxtid;
+               if (status & ECH_PNL16PORT) {
+                       if ((brdp->nrports + 16) > 32)
+                               break;
+                       panelp->nrports = 16;
+                       panelp->ackmask = 0x80;
+                       brdp->nrports += 16;
+                       ioaddr += (EREG_BANKSIZE * 2);
+                       nxtid += 2;
+               } else {
+                       panelp->nrports = 8;
+                       panelp->ackmask = 0xc0;
+                       brdp->nrports += 8;
+                       ioaddr += EREG_BANKSIZE;
+                       nxtid++;
+               }
+               brdp->panels[panelnr++] = panelp;
+               brdp->nrpanels++;
+               if (ioaddr >= (brdp->ioaddr2 + 0x20))
+                       break;
+       }
+
+       if (brdp->brdtype == BRD_ECH)
+               outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+       brdp->state |= BRD_FOUND;
+       i = stl_mapirq(brdp->irq);
+       return(i);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Initialize and configure the specified board.
+ *     Scan through all the boards in the configuration and see what we
+ *     can find. Handle EIO and the ECH boards a little differently here
+ *     since the initial search and setup is too different.
+ */
+
+static int stl_brdinit(stlbrd_t *brdp)
+{
+       int     i;
+
+#if DEBUG
+       printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+       switch (brdp->brdtype) {
+       case BRD_EASYIO:
+               stl_initeio(brdp);
+               break;
+       case BRD_ECH:
+       case BRD_ECHMC:
+       case BRD_ECHPCI:
+               stl_initech(brdp);
+               break;
+       default:
+               printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+               return(ENODEV);
+       }
+
+       stl_brds[brdp->brdnr] = brdp;
+       if ((brdp->state & BRD_FOUND) == 0) {
+               printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
+               return(ENODEV);
+       }
+
+       for (i = 0; (i < STL_MAXPANELS); i++)
+               if (brdp->panels[i] != (stlpanel_t *) NULL)
+                       stl_initports(brdp, brdp->panels[i]);
+
+       printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
+       return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Find any ECH-PCI boards that might be installed. Initialize each
+ *     one as it is found.
+ */
+
+#ifdef CONFIG_PCI
+
+static int stl_findpcibrds()
+{
+       stlbrd_t        *brdp;
+       unsigned char   busnr, devnr, irq;
+       unsigned short  class;
+       unsigned int    ioaddr;
+       int             i, rc;
+
+#if DEBUG
+       printk("stl_findpcibrds()\n");
+#endif
+
+       if (pcibios_present()) {
+               for (i = 0; (i < STL_MAXBRDS); i++) {
+                       if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
+                               break;
+
+/*
+ *                     Found a device on the PCI bus that has our vendor and
+ *                     device ID. Need to check now that it is really us.
+ */
+                       if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
+                               printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+                       if (class == PCI_CLASS_STORAGE_IDE)
+                               continue;
+
+                       if (stl_nrbrds >= STL_MAXBRDS) {
+                               printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
+                               break;
+                       }
+
+/*
+ *                     We have a Stallion board. Allocate a board structure
+ *                     and initialize it. Read its IO and IRQ resources
+ *                     from conf space.
+ */
+                       brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+                       if (brdp == (stlbrd_t *) NULL) {
+                               printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+                               return(-ENOMEM);
+                       }
+                       memset(brdp, 0, sizeof(stlbrd_t));
+                       brdp->brdnr = stl_nrbrds++;
+                       brdp->brdtype = BRD_ECHPCI;
+
+                       if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
+                               printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+                       brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+
+                       if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
+                               printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+                       brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+#if DEBUG
+                       printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+#endif
+
+                       if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
+                               printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+                       brdp->irq = irq;
+
+#if 0
+                       ioaddr = 0x0c000001;
+                       if ((rc = pcibios_write_config_dword(busnr, devnr, 0x40, ioaddr))) {
+                               printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+                       if ((rc = pcibios_write_config_dword(busnr, devnr, 0x48, ioaddr))) {
+                               printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
+                               continue;
+                       }
+#endif
+
+                       stl_brdinit(brdp);
+               }
+       }
+
+       return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Scan through all the boards in the configuration and see what we
+ *     can find. Handle EIO and the ECH boards a little differently here
+ *     since the initial search and setup is too different.
+ */
+
+static int stl_initbrds()
+{
+       stlbrd_t        *brdp;
+       stlconf_t       *confp;
+       int             i;
+
+#if DEBUG
+       printk("stl_initbrds()\n");
+#endif
+
+       if (stl_nrbrds > STL_MAXBRDS) {
+               printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+               stl_nrbrds = STL_MAXBRDS;
+       }
+
+/*
+ *     Firstly scan the list of static boards configured. Allocate
+ *     resources and initialize the boards as found.
+ */
+       for (i = 0; (i < stl_nrbrds); i++) {
+               confp = &stl_brdconf[i];
+               brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+               if (brdp == (stlbrd_t *) NULL) {
+                       printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+                       return(-ENOMEM);
+               }
+               memset(brdp, 0, sizeof(stlbrd_t));
+
+               brdp->brdnr = i;
+               brdp->brdtype = confp->brdtype;
+               brdp->ioaddr1 = confp->ioaddr1;
+               brdp->ioaddr2 = confp->ioaddr2;
+               brdp->irq = confp->irq;
+               brdp->irqtype = confp->irqtype;
+               stl_brdinit(brdp);
+       }
+
+#ifdef CONFIG_PCI
+/*
+ *     If the PCI BIOS support is compiled in then lets go looking for
+ *     ECH-PCI boards.
+ */
+       stl_findpcibrds();
+#endif
+
+       return(0);
+}
+
+/*****************************************************************************/
+
+long stl_init(long kmem_start)
+{
+       printk("%s: version %s\n", stl_drvname, stl_drvversion);
+
+#ifndef        MODULE
+       stl_meminit(kmem_start);
+#endif
+
+       stl_initbrds();
+
+/*
+ *     Allocate a temporary write buffer.
+ */
+       stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
+       if (stl_tmpwritebuf == (char *) NULL)
+               printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+
+/*
+ *     Set up the tty driver structure and register us as a driver.
+ *     Also setup the callout tty device.
+ */
+       memset(&stl_serial, 0, sizeof(struct tty_driver));
+       stl_serial.magic = TTY_DRIVER_MAGIC;
+       stl_serial.name = stl_serialname;
+       stl_serial.major = STL_SERIALMAJOR;
+       stl_serial.minor_start = 0;
+       stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+       stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
+       stl_serial.subtype = STL_DRVTYPSERIAL;
+       stl_serial.init_termios = stl_deftermios;
+       stl_serial.flags = TTY_DRIVER_REAL_RAW;
+       stl_serial.refcount = &stl_refcount;
+       stl_serial.table = stl_ttys;
+       stl_serial.termios = stl_termios;
+       stl_serial.termios_locked = stl_termioslocked;
+       
+       stl_serial.open = stl_open;
+       stl_serial.close = stl_close;
+       stl_serial.write = stl_write;
+       stl_serial.put_char = stl_putchar;
+       stl_serial.flush_chars = stl_flushchars;
+       stl_serial.write_room = stl_writeroom;
+       stl_serial.chars_in_buffer = stl_charsinbuffer;
+       stl_serial.ioctl = stl_ioctl;
+       stl_serial.set_termios = stl_settermios;
+       stl_serial.throttle = stl_throttle;
+       stl_serial.unthrottle = stl_unthrottle;
+       stl_serial.stop = stl_stop;
+       stl_serial.start = stl_start;
+       stl_serial.hangup = stl_hangup;
+       stl_serial.flush_buffer = stl_flushbuffer;
+
+       stl_callout = stl_serial;
+       stl_callout.name = stl_calloutname;
+       stl_callout.major = STL_CALLOUTMAJOR;
+       stl_callout.subtype = STL_DRVTYPCALLOUT;
+
+       if (tty_register_driver(&stl_serial))
+               printk("STALLION: failed to register serial driver\n");
+       if (tty_register_driver(&stl_callout))
+               printk("STALLION: failed to register callout driver\n");
+
+#ifndef        MODULE
+       kmem_start = stl_memhalt();
+#endif
+       return(kmem_start);
+}
+
+/*****************************************************************************/
index d546c783f765e3a56bdc8bf9292953902b394bd6..7b916bb59bf575c87af7e5dcccb45d0a163c0301 100644 (file)
@@ -1788,6 +1788,12 @@ long tty_init(long kmem_start)
 #endif
 #ifdef CONFIG_CYCLADES
        kmem_start = cy_init(kmem_start);
+#endif
+#ifdef CONFIG_STALLION
+       kmem_start = stl_init(kmem_start);
+#endif
+#ifdef CONFIG_ISTALLION
+       kmem_start = stli_init(kmem_start);
 #endif
        kmem_start = pty_init(kmem_start);
        kmem_start = vcs_init(kmem_start);
index 90583ab7d6f7c98b32f0ee7154a430bc8e3b71fb..8c2a03ab4ea82398f312d3a2af09d073f8e32766 100644 (file)
@@ -1042,7 +1042,7 @@ static unsigned long check_aligned(struct buffer_head * first, unsigned long add
        while (nrbuf-- > 0)
                brelse(bh[nrbuf]);
        free_page(address);
-       ++current->mm->min_flt;
+       ++current->min_flt;
        return page;
 no_go:
        while (nrbuf-- > 0)
@@ -1100,7 +1100,7 @@ static unsigned long try_to_load_aligned(unsigned long address,
        read_buffers(arr,block);
        while (block-- > 0)
                brelse(arr[block]);
-       ++current->mm->maj_flt;
+       ++current->maj_flt;
        return address;
 not_aligned:
        while ((tmp = bh) != NULL) {
@@ -1154,7 +1154,7 @@ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, in
                if (where)
                        return where;
        }
-       ++current->mm->maj_flt;
+       ++current->maj_flt;
        for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
                bh[i] = NULL;
                if (b[i])
index 68483a2a4e78ca43fc1a63facd65519a9a398374..cc00e341d2a00fcfedd365fe6e22e49dd95b870a 100644 (file)
@@ -1,10 +1,11 @@
 /*
  *  linux/fs/isofs/inode.c
  * 
- *  (C) 1992, 1993, 1994  Eric Youngdale Modified for ISO9660 filesystem.
- *      1995  Mark Dobie - patch to allow VideoCD and PhotoCD mounting.
- *
  *  (C) 1991  Linus Torvalds - minix filesystem
+ *      1992, 1993, 1994  Eric Youngdale Modified for ISO9660 filesystem.
+ *      1994  Eberhard Moenkeberg - multi session handling.
+ *      1995  Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
+ *
  */
 
 #ifdef MODULE
@@ -152,7 +153,9 @@ static int parse_options(char *options, struct iso9660_options * popt)
        return 1;
 }
 
-
+/*
+ * look if the driver can tell the multi session redirection value
+ */
 static unsigned int isofs_get_last_session(int dev)
 {
   struct cdrom_multisession ms_info;
@@ -161,10 +164,6 @@ static unsigned int isofs_get_last_session(int dev)
   extern struct file_operations * get_blkfops(unsigned int);
   int i;
 
-  /*
-   * look if the driver can tell the multi session redirection value
-   * <emoenke@gwdg.de>
-   */
   vol_desc_start=0;
   if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
     {
@@ -565,7 +564,7 @@ void isofs_read_inode(struct inode * inode)
        
        inode->i_op = NULL;
 
-       /* get the volume sequence numner */
+       /* get the volume sequence number */
        volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
 
        /* 
index eee554b2e5165ddd95a85f09a3a137f005d23584..c9e90c4bda31cb892a282541e001bd391fe8880a 100644 (file)
@@ -449,10 +449,10 @@ static int get_stat(int pid, char * buffer)
                tsk->tty ? tsk->tty->device : 0,
                tty_pgrp,
                tsk->flags,
-               tsk->mm ? tsk->mm->min_flt : 0,
-               tsk->mm ? tsk->mm->cmin_flt : 0,
-               tsk->mm ? tsk->mm->maj_flt : 0,
-               tsk->mm ? tsk->mm->cmaj_flt : 0,
+               tsk->min_flt,
+               tsk->cmin_flt,
+               tsk->maj_flt,
+               tsk->cmaj_flt,
                tsk->utime,
                tsk->stime,
                tsk->cutime,
index 47b1508821430183e7c705b61cbcfd5146036f6f..b67d12d1cb9b4fa87b63b50df3bf8861f47addf3 100644 (file)
@@ -95,7 +95,6 @@ struct mouse_status {
 };
 
 /* Function Prototypes */
-extern long mouse_init(long);
 
 #endif
 
diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h
new file mode 100644 (file)
index 0000000..799fec0
--- /dev/null
@@ -0,0 +1,293 @@
+/*****************************************************************************/
+
+/*
+ *     cd1400.h  -- cd1400 UART hardware info.
+ *
+ *     Copyright (C) 1994,1995  Greg Ungerer (gerg@stallion.oz.au).
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+
+#ifndef        _CD1400_H
+#define        _CD1400_H
+/*****************************************************************************/
+
+/*
+ *     Define the number of async ports per cd1400 uart chip.
+ */
+#define        CD1400_PORTS            4
+
+#define        CD1400_CLKHZ            25000000
+
+/*
+ *     Define the cd1400 uarts internal FIFO sizes.
+ */
+#define        CD1400_TXFIFOSIZE       12
+#define        CD1400_RXFIFOSIZE       12
+
+/*
+ *     Local RX FIFO thresh hold level. Also define the RTS thresh hold
+ *     based on the RX thresh hold.
+ */
+#define        FIFO_RXTHRESHOLD        6
+#define        FIFO_RTSTHRESHOLD       7
+
+/*****************************************************************************/
+
+/*
+ *     Define the cd1400 register addresses. These are all the valid
+ *     registers with the cd1400. Some are global, some virtual, some
+ *     per port.
+ */
+#define        GFRCR           0x40
+#define        CAR             0x68
+#define        GCR             0x4b
+#define        SVRR            0x67
+#define        RICR            0x44
+#define        TICR            0x45
+#define        MICR            0x46
+#define        RIR             0x6b
+#define        TIR             0x6a
+#define        MIR             0x69
+#define        PPR             0x7e
+
+#define        RIVR            0x43
+#define        TIVR            0x42
+#define        MIVR            0x41
+#define        TDR             0x63
+#define        RDSR            0x62
+#define        MISR            0x4c
+#define        EOSRR           0x60
+
+#define        LIVR            0x18
+#define        CCR             0x05
+#define        SRER            0x06
+#define        COR1            0x08
+#define        COR2            0x09
+#define        COR3            0x0a
+#define        COR4            0x1e
+#define        COR5            0x1f
+#define        CCSR            0x0b
+#define        RDCR            0x0e
+#define        SCHR1           0x1a
+#define        SCHR2           0x1b
+#define        SCHR3           0x1c
+#define        SCHR4           0x1d
+#define        SCRL            0x22
+#define        SCRH            0x23
+#define        LNC             0x24
+#define        MCOR1           0x15
+#define        MCOR2           0x16
+#define        RTPR            0x21
+#define        MSVR1           0x6c
+#define        MSVR2           0x6d
+#define        PSVR            0x6f
+#define        RBPR            0x78
+#define        RCOR            0x7c
+#define        TBPR            0x72
+#define        TCOR            0x76
+
+/*****************************************************************************/
+
+/*
+ *     Define the set of baud rate clock divisors.
+ */
+#define        CD1400_CLK0     8
+#define        CD1400_CLK1     32
+#define        CD1400_CLK2     128
+#define        CD1400_CLK3     512
+#define        CD1400_CLK4     2048
+
+#define        CD1400_NUMCLKS  5
+
+/*****************************************************************************/
+
+/*
+ *     Define the clock pre-scalar value to be a 5 ms clock. This should be
+ *     OK for now. It would probably be better to make it 10 ms, but we
+ *     can't fit that divisor into 8 bits!
+ */
+#define        PPR_SCALAR      244
+
+/*****************************************************************************/
+
+/*
+ *     Define values used to set character size options.
+ */
+#define        COR1_CHL5       0x00
+#define        COR1_CHL6       0x01
+#define        COR1_CHL7       0x02
+#define        COR1_CHL8       0x03
+
+/*
+ *     Define values used to set the number of stop bits.
+ */
+#define        COR1_STOP1      0x00
+#define        COR1_STOP15     0x04
+#define        COR1_STOP2      0x08
+
+/*
+ *     Define values used to set the parity scheme in use.
+ */
+#define        COR1_PARNONE    0x00
+#define        COR1_PARFORCE   0x20
+#define        COR1_PARENB     0x40
+#define        COR1_PARIGNORE  0x10
+
+#define        COR1_PARODD     0x80
+#define        COR1_PAREVEN    0x00
+
+#define        COR2_IXM        0x80
+#define        COR2_TXIBE      0x40
+#define        COR2_ETC        0x20
+#define        COR2_LLM        0x10
+#define        COR2_RLM        0x08
+#define        COR2_RTSAO      0x04
+#define        COR2_CTSAE      0x02
+
+#define        COR3_SCDRNG     0x80
+#define        COR3_SCD34      0x40
+#define        COR3_FCT        0x20
+#define        COR3_SCD12      0x10
+
+/*
+ *     Define values used by COR4.
+ */
+#define        COR4_BRKINT     0x08
+#define        COR4_IGNBRK     0x18
+
+/*****************************************************************************/
+
+/*
+ *     Define the modem control register values.
+ *     Note that the actual hardware is a little different to the conventional
+ *     pin names on the cd1400.
+ */
+#define        MSVR1_DTR       0x01
+#define        MSVR1_DSR       0x10
+#define        MSVR1_RI        0x20
+#define        MSVR1_CTS       0x40
+#define        MSVR1_DCD       0x80
+
+#define        MSVR2_RTS       0x02
+#define        MSVR2_DSR       0x10
+#define        MSVR2_RI        0x20
+#define        MSVR2_CTS       0x40
+#define        MSVR2_DCD       0x80
+
+#define        MCOR1_DCD       0x80
+#define        MCOR1_CTS       0x40
+#define        MCOR1_RI        0x20
+#define        MCOR1_DSR       0x10
+
+#define        MCOR2_DCD       0x80
+#define        MCOR2_CTS       0x40
+#define        MCOR2_RI        0x20
+#define        MCOR2_DSR       0x10
+
+/*****************************************************************************/
+
+/*
+ *     Define the bits used with the service (interrupt) enable register.
+ */
+#define        SRER_NNDT       0x01
+#define        SRER_TXEMPTY    0x02
+#define        SRER_TXDATA     0x04
+#define        SRER_RXDATA     0x10
+#define        SRER_MODEM      0x80
+
+/*****************************************************************************/
+
+/*
+ *     Define operational commands for the command register.
+ */
+#define        CCR_RESET       0x80
+#define        CCR_CORCHANGE   0x4e
+#define        CCR_SENDCH      0x20
+#define        CCR_CHANCTRL    0x10
+
+#define        CCR_TXENABLE    (CCR_CHANCTRL | 0x08)
+#define        CCR_TXDISABLE   (CCR_CHANCTRL | 0x04)
+#define        CCR_RXENABLE    (CCR_CHANCTRL | 0x02)
+#define        CCR_RXDISABLE   (CCR_CHANCTRL | 0x01)
+
+#define        CCR_SENDSCHR1   (CCR_SENDCH | 0x01)
+#define        CCR_SENDSCHR2   (CCR_SENDCH | 0x02)
+#define        CCR_SENDSCHR3   (CCR_SENDCH | 0x03)
+#define        CCR_SENDSCHR4   (CCR_SENDCH | 0x04)
+
+#define        CCR_RESETCHAN   (CCR_RESET | 0x00)
+#define        CCR_RESETFULL   (CCR_RESET | 0x01)
+#define        CCR_TXFLUSHFIFO (CCR_RESET | 0x02)
+
+#define        CCR_MAXWAIT     10000
+
+/*****************************************************************************/
+
+/*
+ *     Define the valid acknowledgement types (for hw ack cycle).
+ */
+#define        ACK_TYPMASK     0x07
+#define        ACK_TYPTX       0x02
+#define        ACK_TYPMDM      0x01
+#define        ACK_TYPRXGOOD   0x03
+#define        ACK_TYPRXBAD    0x07
+
+#define        SVRR_RX         0x01
+#define        SVRR_TX         0x02
+#define        SVRR_MDM        0x04
+
+#define        ST_OVERRUN      0x01
+#define        ST_FRAMING      0x02
+#define        ST_PARITY       0x04
+#define        ST_BREAK        0x08
+#define        ST_SCHAR1       0x10
+#define        ST_SCHAR2       0x20
+#define        ST_SCHAR3       0x30
+#define        ST_SCHAR4       0x40
+#define        ST_RANGE        0x70
+#define        ST_TIMEOUT      0x80
+
+#define        MISR_DCD        0x80
+#define        MISR_CTS        0x40
+#define        MISR_RI         0x20
+#define        MISR_DSR        0x10
+
+/*****************************************************************************/
+
+/*
+ *     Defines for the CCSR status register.
+ */
+#define        CCSR_RXENABLED  0x80
+#define        CCSR_RXFLOWON   0x40
+#define        CCSR_RXFLOWOFF  0x20
+#define        CCSR_TXENABLED  0x08
+#define        CCSR_TXFLOWON   0x04
+#define        CCSR_TXFLOWOFF  0x02
+
+/*****************************************************************************/
+
+/*
+ *     Define the embedded commands.
+ */
+#define        ETC_CMD         0x00
+#define        ETC_STARTBREAK  0x81
+#define        ETC_DELAY       0x82
+#define        ETC_STOPBREAK   0x83
+
+/*****************************************************************************/
+#endif
diff --git a/include/linux/cdk.h b/include/linux/cdk.h
new file mode 100644 (file)
index 0000000..915dc0b
--- /dev/null
@@ -0,0 +1,472 @@
+/*****************************************************************************/
+
+/*
+ *     cdk.h  -- CDK interface definitions.
+ *
+ *     Copyright (C) 1994,1995  Greg Ungerer (gerg@stallion.oz.au).
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+
+#ifndef        _CDK_H
+#define        _CDK_H
+/*****************************************************************************/
+
+#pragma        pack(2)
+
+/*
+ *     The following set of definitions is used to communicate with the
+ *     shared memory interface of the Stallion intelligent multiport serial
+ *     boards. The definitions in this file are taken directly from the
+ *     document titled "Generic Stackable Interface, Downloader and
+ *     Communications Development Kit".
+ */
+
+/*
+ *     Define the set of importrant shared memory addresses. These are
+ *     required to intialize the board and get things started. All of these
+ *     addresses are relative to the start of the shared memory.
+ */
+#define        CDK_SIGADDR     0x200
+#define        CDK_FEATADDR    0x280
+#define        CDK_CDKADDR     0x300
+#define        CDK_RDYADDR     0x262
+
+#define        CDK_ALIVEMARKER 13
+
+/*
+ *     On hardware power up the ROMs located on the EasyConnection 8/64 will
+ *     fill out the following signature information into shared memory. This
+ *     way the host system can quickly determine that the board is present
+ *     and is operational.
+ */
+typedef struct cdkecpsig {
+       unsigned long   magic;
+       unsigned short  romver;
+       unsigned short  cputype;
+       unsigned char   panelid[8];
+} cdkecpsig_t;
+
+#define        ECP_MAGIC       0x21504345
+
+/*
+ *     On hardware power up the ROMs located on the ONboard, Stallion and
+ *     Brumbys will fill out the following signature information into shared
+ *     memory. This way the host system can quickly determine that the board
+ *     is present and is operational.
+ */
+typedef struct cdkonbsig {
+       unsigned short  magic0;
+       unsigned short  magic1;
+       unsigned short  magic2;
+       unsigned short  magic3;
+       unsigned short  romver;
+       unsigned short  memoff;
+       unsigned short  memseg;
+       unsigned short  amask0;
+       unsigned short  pic;
+       unsigned short  status;
+       unsigned short  btype;
+       unsigned short  clkticks;
+       unsigned short  clkspeed;
+       unsigned short  amask1;
+       unsigned short  amask2;
+} cdkonbsig_t;
+
+#define        ONB_MAGIC0      0xf2a7
+#define        ONB_MAGIC1      0xa149
+#define        ONB_MAGIC2      0x6352
+#define        ONB_MAGIC3      0xf121
+
+/*
+ *     Define the feature area structure. The feature area is the set of
+ *     startup parameters used by the slave image when it starts executing.
+ *     They allow for the specification of buffer sizes, debug trace, etc.
+ */
+typedef struct cdkfeature {
+       unsigned long   debug;
+       unsigned long   banner;
+       unsigned long   etype;
+       unsigned long   nrdevs;
+       unsigned long   brdspec;
+       unsigned long   txrqsize;
+       unsigned long   rxrqsize;
+       unsigned long   flags;
+} cdkfeature_t;
+
+#define        ETYP_DDK        0
+#define        ETYP_CDK        1
+
+/*
+ *     Define the CDK header structure. This is the info that the slave
+ *     environment sets up after it has been downloaded and started. It
+ *     essentially provides a memory map for the shared memory interface.
+ */
+typedef struct cdkhdr {
+       unsigned short  command;
+       unsigned short  status;
+       unsigned short  port;
+       unsigned short  mode;
+       unsigned long   cmd_buf[14];
+       unsigned short  alive_cnt;
+       unsigned short  intrpt_mode;
+       unsigned char   intrpt_id[8];
+       unsigned char   ver_release;
+       unsigned char   ver_modification;
+       unsigned char   ver_fix;
+       unsigned char   deadman_restart;
+       unsigned short  deadman;
+       unsigned short  nrdevs;
+       unsigned long   memp;
+       unsigned long   hostp;
+       unsigned long   slavep;
+       unsigned char   hostreq;
+       unsigned char   slavereq;
+       unsigned char   cmd_reserved[30];
+} cdkhdr_t;
+
+#define        MODE_DDK        0
+#define        MODE_CDK        1
+
+#define        IMD_INTR        0x0
+#define        IMD_PPINTR      0x1
+#define        IMD_POLL        0xff
+
+/*
+ *     Define the memory mapping structure. This structure is pointed to by
+ *     the memp field in the stlcdkhdr struct. As many as these structures
+ *     as required are layed out in shared memory to define how the rest of
+ *     shared memory is divided up. There will be one for each port.
+ */
+typedef struct cdkmem {
+       unsigned short  dtype;
+       unsigned long   offset;
+} cdkmem_t;
+
+#define        TYP_UNDEFINED   0x0
+#define        TYP_ASYNCTRL    0x1
+#define        TYP_ASYNC       0x20
+#define        TYP_PARALLEL    0x40
+#define        TYP_SYNCX21     0x60
+
+/*****************************************************************************/
+
+/*
+ *     Following is a set of defines and structures used to actually deal
+ *     with the serial ports on the board. Firstly is the set of commands
+ *     that can be applied to ports.
+ */
+#define        ASYCMD          (((unsigned long) 'a') << 8)
+
+#define        A_NULL          (ASYCMD | 0)
+#define        A_FLUSH         (ASYCMD | 1)
+#define        A_BREAK         (ASYCMD | 2)
+#define        A_GETPORT       (ASYCMD | 3)
+#define        A_SETPORT       (ASYCMD | 4)
+#define        A_SETPORTF      (ASYCMD | 5)
+#define        A_SETPORTFTX    (ASYCMD | 6)
+#define        A_SETPORTFRX    (ASYCMD | 7)
+#define        A_GETSIGNALS    (ASYCMD | 8)
+#define        A_SETSIGNALS    (ASYCMD | 9)
+#define        A_SETSIGNALSF   (ASYCMD | 10)
+#define        A_SETSIGNALSFTX (ASYCMD | 11)
+#define        A_SETSIGNALSFRX (ASYCMD | 12)
+#define        A_GETNOTIFY     (ASYCMD | 13)
+#define        A_SETNOTIFY     (ASYCMD | 14)
+#define        A_NOTIFY        (ASYCMD | 15)
+#define        A_PORTCTRL      (ASYCMD | 16)
+#define        A_GETSTATS      (ASYCMD | 17)
+#define        A_RQSTATE       (ASYCMD | 18)
+#define        A_FLOWSTATE     (ASYCMD | 19)
+
+/*
+ *     Define those arguments used for simple commands.
+ */
+#define        FLUSHRX         0x1
+#define        FLUSHTX         0x2
+
+#define        BREAKON         -1
+#define        BREAKOFF        -2
+
+/*
+ *     Define the port setting structure, and all those defines that go along
+ *     with it. Basically this structure defines the charcateristics of this
+ *     port: baud rate, chars, parity, input/output char cooking etc.
+ */
+typedef struct asyport {
+       unsigned long   baudout;
+       unsigned long   baudin;
+       unsigned long   iflag;
+       unsigned long   oflag;
+       unsigned long   lflag;
+       unsigned long   pflag;
+       unsigned long   flow;
+       unsigned long   spare1;
+       unsigned short  vtime;
+       unsigned short  vmin;
+       unsigned short  txlo;
+       unsigned short  txhi;
+       unsigned short  rxlo;
+       unsigned short  rxhi;
+       unsigned short  rxhog;
+       unsigned short  spare2;
+       unsigned char   csize;
+       unsigned char   stopbs;
+       unsigned char   parity;
+       unsigned char   stopin;
+       unsigned char   startin;
+       unsigned char   stopout;
+       unsigned char   startout;
+       unsigned char   parmark;
+       unsigned char   brkmark;
+       unsigned char   cc[11];
+} asyport_t;
+
+#define        PT_STOP1        0x0
+#define        PT_STOP15       0x1
+#define        PT_STOP2        0x2
+
+#define        PT_NOPARITY     0x0
+#define        PT_ODDPARITY    0x1
+#define        PT_EVENPARITY   0x2
+#define        PT_MARKPARITY   0x3
+#define        PT_SPACEPARITY  0x4
+
+#define        F_NONE          0x0
+#define        F_IXON          0x1
+#define        F_IXOFF         0x2
+#define        F_IXANY         0x4
+#define        F_IOXANY        0x8
+#define        F_RTSFLOW       0x10
+#define        F_CTSFLOW       0x20
+#define        F_DTRFLOW       0x40
+#define        F_DCDFLOW       0x80
+#define        F_DSROFLOW      0x100
+#define        F_DSRIFLOW      0x200
+
+#define        FI_NORX         0x1
+#define        FI_RAW          0x2
+#define        FI_ISTRIP       0x4
+#define        FI_UCLC         0x8
+#define        FI_INLCR        0x10
+#define        FI_ICRNL        0x20
+#define        FI_IGNCR        0x40
+#define        FI_IGNBREAK     0x80
+#define        FI_DSCRDBREAK   0x100
+#define        FI_1MARKBREAK   0x200
+#define        FI_2MARKBREAK   0x400
+#define        FI_XCHNGBREAK   0x800
+#define        FI_IGNRXERRS    0x1000
+#define        FI_DSCDRXERRS   0x2000
+#define        FI_1MARKRXERRS  0x4000
+#define        FI_2MARKRXERRS  0x8000
+#define        FI_XCHNGRXERRS  0x10000
+#define        FI_DSCRDNULL    0x20000
+
+#define        FO_OLCUC        0x1
+#define        FO_ONLCR        0x2
+#define        FO_OOCRNL       0x4
+#define        FO_ONOCR        0x8
+#define        FO_ONLRET       0x10
+#define        FO_ONL          0x20
+#define        FO_OBS          0x40
+#define        FO_OVT          0x80
+#define        FO_OFF          0x100
+#define        FO_OTAB1        0x200
+#define        FO_OTAB2        0x400
+#define        FO_OTAB3        0x800
+#define        FO_OCR1         0x1000
+#define        FO_OCR2         0x2000
+#define        FO_OCR3         0x4000
+#define        FO_OFILL        0x8000
+#define        FO_ODELL        0x10000
+
+#define        P_RTSLOCK       0x1
+#define        P_CTSLOCK       0x2
+#define        P_MAPRTS        0x4
+#define        P_MAPCTS        0x8
+#define        P_LOOPBACK      0x10
+#define        P_DTRFOLLOW     0x20
+#define        P_FAKEDCD       0x40
+
+/*
+ *     Define a structure to communicate serial port signal and data state
+ *     information.
+ */
+typedef struct asysigs {
+       unsigned long   data;
+       unsigned long   signal;
+       unsigned long   sigvalue;
+} asysigs_t;
+
+#define        DT_TXBUSY       0x1
+#define        DT_TXEMPTY      0x2
+#define        DT_TXLOW        0x4
+#define        DT_TXHIGH       0x8
+#define        DT_TXFULL       0x10
+#define        DT_TXHOG        0x20
+#define        DT_TXFLOWED     0x40
+#define        DT_TXBREAK      0x80
+
+#define        DT_RXBUSY       0x100
+#define        DT_RXEMPTY      0x200
+#define        DT_RXLOW        0x400
+#define        DT_RXHIGH       0x800
+#define        DT_RXFULL       0x1000
+#define        DT_RXHOG        0x2000
+#define        DT_RXFLOWED     0x4000
+#define        DT_RXBREAK      0x8000
+
+#define        SG_DTR          0x1
+#define        SG_DCD          0x2
+#define        SG_RTS          0x4
+#define        SG_CTS          0x8
+#define        SG_DSR          0x10
+#define        SG_RI           0x20
+
+/*
+ *     Define the notification setting structure. This is used to tell the
+ *     port what events we want to be informed about. Fields here use the
+ *     same defines as for the asysigs structure above.
+ */
+typedef struct asynotify {
+       unsigned long   ctrl;
+       unsigned long   data;
+       unsigned long   signal;
+       unsigned long   sigvalue;
+} asynotify_t;
+
+/*
+ *     Define the port control structure. It is used to do fine grain
+ *     control operations on the port.
+ */
+typedef struct {
+       unsigned long   rxctrl;
+       unsigned long   txctrl;
+       char            rximdch;
+       char            tximdch;
+       char            spare1;
+       char            spare2;
+} asyctrl_t;
+
+#define        CT_ENABLE       0x1
+#define        CT_DISABLE      0x2
+#define        CT_STOP         0x4
+#define        CT_START        0x8
+#define        CT_STARTFLOW    0x10
+#define        CT_STOPFLOW     0x20
+#define        CT_SENDCHR      0x40
+
+/*
+ *     Define the stats structure kept for each port. This is a useful set
+ *     of data collected for each port on the slave. The A_GETSTATS command
+ *     is used to retrive this data from the slave.
+ */
+typedef struct asystats {
+       unsigned long   opens;
+       unsigned long   txchars;
+       unsigned long   rxchars;
+       unsigned long   txringq;
+       unsigned long   rxringq;
+       unsigned long   txmsgs;
+       unsigned long   rxmsgs;
+       unsigned long   overruns;
+       unsigned long   framing;
+       unsigned long   parity;
+       unsigned long   ringover;
+       unsigned long   lost;
+       unsigned long   rxstart;
+       unsigned long   rxstop;
+       unsigned long   txstart;
+       unsigned long   txstop;
+       unsigned long   dcdcnt;
+       unsigned long   dtrcnt;
+       unsigned long   ctscnt;
+       unsigned long   rtscnt;
+       unsigned long   dsrcnt;
+       unsigned long   ricnt;
+       unsigned long   txbreaks;
+       unsigned long   rxbreaks;
+       unsigned long   signals;
+       unsigned long   state;
+} asystats_t;
+
+/*****************************************************************************/
+
+/*
+ *     All command and control communication with a device on the slave is
+ *     via a control block in shared memory. Each device has its own control
+ *     block, defined by the following structure. The control block allows
+ *     the host to open, close and control the device on the slave.
+ */
+typedef struct cdkctrl {
+       unsigned char   open;
+       unsigned char   close;
+       unsigned long   openarg;
+       unsigned long   closearg;
+       unsigned long   cmd;
+       unsigned long   status;
+       unsigned long   args[32];
+} cdkctrl_t;
+
+/*
+ *     Each device on the slave passes data to and from the host via a ring
+ *     queue in shared memory. Define a ring queue structure to hold the
+ *     vital information about each ring queue. Two ring queues will be
+ *     allocated for each port, one for reveice data and one for transmit
+ *     data.
+ */
+typedef struct cdkasyrq {
+       unsigned long   offset;
+       unsigned short  size;
+       unsigned short  head;
+       unsigned short  tail;
+} cdkasyrq_t;
+
+/*
+ *     Each asynchronous port is defined in shared memory by the following
+ *     structure. It contains a control block to command a device, and also
+ *     the neccessary data channel information as well.
+ */
+typedef struct cdkasy {
+       cdkctrl_t       ctrl;
+       unsigned short  notify;
+       asynotify_t     changed;
+       unsigned short  receive;
+       cdkasyrq_t      rxq;
+       unsigned short  transmit;
+       cdkasyrq_t      txq;
+} cdkasy_t;
+
+#pragma        pack()
+
+/*****************************************************************************/
+
+/*
+ *     Define the set of ioctls used by the driver to do special things
+ *     to the board. These include interrupting it, and initializeing
+ *     the driver after board startup and shutdown.
+ */
+#define        STLCMD          (((unsigned long) 's') << 8)
+
+#define        STL_BINTR       (STLCMD | 20)
+#define        STL_BSTART      (STLCMD | 21)
+#define        STL_BSTOP       (STLCMD | 22)
+#define        STL_BRESET      (STLCMD | 23)
+
+/*****************************************************************************/
+#endif
index 474bc056093e505d0a07f9bf51e53d2f5ddb83d4..1541e0fdcb202399c83da230200ad6422f7194fe 100644 (file)
-/****************************************************************************************
- *                                                                                     *
- * general (not only SCSI) header library for linux CDROM drivers                       *
- * (C) 1992 David Giller rafetmad@oxy.edu                                              *
- *     1994 Eberhard Moenkeberg emoenke@gwdg.de ("read audio" and some other stuff)    *
- *                                                                                     *
- * <linux/cdrom.h> -- CD-ROM IOCTLs and structs                                                *
- *                                                                                     *
- ****************************************************************************************/
-
-#ifndef        _LINUX_CDROM_H
-#define        _LINUX_CDROM_H
-
-/*
- * some fix numbers
- */
-#define CD_MINS                    74  /* max. minutes per CD             */
-#define CD_SECS                    60  /* seconds per minute              */
-#define CD_FRAMES                  75  /* frames per second               */
-#define CD_CHUNK_SIZE              24  /* lowest-level "data bytes piece" */
-#define CD_NUM_OF_CHUNKS           98  /* chunks per frame                */
-#define CD_FRAMESIZE             2048  /* bytes per frame, cooked mode    */
-#define CD_FRAMESIZE_RAW0        2336  /* bytes per frame, "raw" mode     */
-#define CD_FRAMESIZE_XA          2340  /* bytes per frame, "xa" mode      */
-#define CD_FRAMESIZE_RAW         2352  /* bytes per frame, "raw" mode     */
-#define CD_FRAMESIZE_SUB           96  /* subchannel data size            */
-#define CD_BLOCK_OFFSET           150  /* offset of first logical frame   */
-#define CD_XA_HEAD                 12  /* header size of XA frame         */
-#define CD_XA_TAIL                280  /* tail size of XA frame           */
-#define CD_XA_SYNC_HEAD CD_XA_HEAD+12  /* sync bytes + header of XA frame */
 /*
+ * -- <linux/cdrom.h>
+ * general (not only SCSI) header library for linux CDROM drivers
+ * (C) 1992         David Giller rafetmad@oxy.edu
+ *     1994, 1995   Eberhard Moenkeberg emoenke@gwdg.de
  *
- * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
- *
- */
-
-/*
- * CD-ROM-specific SCSI command opcodes
  */
 
-/*
- * Group 2 (10-byte).  All of these are called 'optional' by SCSI-II.
- */
-
-#define        SCMD_READ_TOC           0x43            /* read table of contents       */
-#define        SCMD_PLAYAUDIO_MSF      0x47            /* play data at time offset     */
-#define        SCMD_PLAYAUDIO_TI       0x48            /* play data at track/index     */
-#define        SCMD_PAUSE_RESUME       0x4B            /* pause/resume audio           */
-#define        SCMD_READ_SUBCHANNEL    0x42            /* read SC info on playing disc */
-#define        SCMD_PLAYAUDIO10        0x45            /* play data at logical block   */
-#define        SCMD_READ_HEADER        0x44            /* read TOC header              */
-
-/*
- * Group 5
- */
-
-#define        SCMD_PLAYAUDIO12        0xA5            /* play data at logical block   */
-#define        SCMD_PLAYTRACK_REL12    0xA9            /* play track at relative offset*/
+#ifndef        _LINUX_CDROM_H
+#define        _LINUX_CDROM_H
 
 /*
- * Group 6 Commands
+ * some fix numbers
  */
+#define CD_MINS             74 /* max. minutes per CD, not really a limit */
+#define CD_SECS             60 /* seconds per minute */
+#define CD_FRAMES           75 /* frames per second */
 
-#define        SCMD_CD_PLAYBACK_CONTROL 0xC9           /* Sony vendor-specific audio   */
-#define        SCMD_CD_PLAYBACK_STATUS 0xC4            /* control opcodes. info please!*/
+#define CD_SYNC_SIZE        12 /* 12 sync bytes per raw data frame, not transfered by the drive */
+#define CD_HEAD_SIZE         4 /* header (address) bytes per raw data frame */
+#define CD_SUBHEAD_SIZE      8 /* subheader bytes per raw XA data frame */
+#define CD_XA_HEAD        (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
+#define CD_XA_SYNC_HEAD   (CD_XA_HEAD+12)/* sync bytes + header of XA frame */
 
-/*
- * CD-ROM capacity structure.
- */
+#define CD_FRAMESIZE      2048 /* bytes per frame, "cooked" mode */
+#define CD_FRAMESIZE_RAW  2352 /* bytes per frame, "raw" mode */
+/* most drives don't deliver everything: */
+#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
+#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
 
-struct scsi_capacity 
-       {
-       u_long  capacity;
-       u_long  lbasize;
-       };
+#define CD_EDC_SIZE         4 /* bytes EDC per most raw data frame types */
+#define CD_ZERO_SIZE        8 /* bytes zero per yellow book mode 1 frame */
+#define CD_ECC_SIZE       276 /* bytes ECC per most raw data frame types */
+#define CD_XA_TAIL        (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
 
-/*
- * CD-ROM MODE_SENSE/MODE_SELECT parameters
- */
+#define CD_FRAMESIZE_SUB    96 /* subchannel data "frame" size */
+#define CD_MSF_OFFSET      150 /* MSF numbering offset of first frame */
 
-#define        ERR_RECOVERY_PARMS      0x01
-#define        DISCO_RECO_PARMS        0x02
-#define        FORMAT_PARMS            0x03
-#define        GEOMETRY_PARMS          0x04
-#define        CERTIFICATION_PARMS     0x06
-#define        CACHE_PARMS             0x38
-
-/*
- * standard mode-select header prepended to all mode-select commands
- */
+#define CD_CHUNK_SIZE       24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS    98 /* chunks per frame */
 
-struct ccs_modesel_head 
-       {
-       u_char  _r1;                    /* reserved                             */
-       u_char  medium;                 /* device-specific medium type          */
-       u_char  _r2;                    /* reserved                             */
-       u_char  block_desc_length;      /* block descriptor length              */
-       u_char  density;                /* device-specific density code         */
-       u_char  number_blocks_hi;       /* number of blocks in this block desc  */
-       u_char  number_blocks_med;
-       u_char  number_blocks_lo;
-       u_char  _r3;
-       u_char  block_length_hi;        /* block length for blocks in this desc */
-       u_short block_length;
-       };
+#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */
+#define CD_BLOCK_OFFSET    CD_MSF_OFFSET /* obsolete name */
 
 /*
- * error recovery parameters
- */
-
-struct ccs_err_recovery 
-       {
-       u_char  _r1             : 2;    /* reserved                             */
-       u_char  page_code       : 6;    /* page code                            */
-       u_char  page_length;            /* page length                          */
-       u_char  awre            : 1;    /* auto write realloc enabled           */
-       u_char  arre            : 1;    /* auto read realloc enabled            */
-       u_char  tb              : 1;    /* transfer block                       */
-       u_char  rc              : 1;    /* read continuous                      */
-       u_char  eec             : 1;    /* enable early correction              */
-       u_char  per             : 1;    /* post error                           */
-       u_char  dte             : 1;    /* disable transfer on error            */
-       u_char  dcr             : 1;    /* disable correction                   */
-       u_char  retry_count;            /* error retry count                    */
-       u_char  correction_span;        /* largest recov. to be attempted, bits */
-       u_char  head_offset_count;      /* head offset (2's C) for each retry   */
-       u_char  strobe_offset_count;    /* data strobe "                        */
-       u_char  recovery_time_limit;    /* time limit on recovery attempts      */
-};
-
-/*
- * disco/reco parameters
- */
-
-struct ccs_disco_reco 
-       {
-       u_char  _r1             : 2;    /* reserved                             */
-       u_char  page_code       : 6;    /* page code                            */
-       u_char  page_length;            /* page length                          */
-       u_char  buffer_full_ratio;      /* write buffer reconnect threshold     */
-       u_char  buffer_empty_ratio;     /* read "                               */
-       u_short bus_inactivity_limit;   /* limit on bus inactivity time         */
-       u_short disconnect_time_limit;  /* minimum disconnect time              */
-       u_short connect_time_limit;     /* minimum connect time                 */
-       u_short _r2;                    /* reserved                             */
-};
-
-/*
- * drive geometry parameters
- */
-
-struct ccs_geometry 
-       {
-       u_char  _r1             : 2;    /* reserved                             */
-       u_char  page_code       : 6;    /* page code                            */
-       u_char  page_length;            /* page length                          */
-       u_char  cyl_ub;                 /* #cyls                                */
-       u_char  cyl_mb;
-       u_char  cyl_lb;
-       u_char  heads;                  /* #heads                               */
-       u_char  precomp_cyl_ub;         /* precomp start                        */
-       u_char  precomp_cyl_mb;
-       u_char  precomp_cyl_lb;
-       u_char  current_cyl_ub;         /* reduced current start                */
-       u_char  current_cyl_mb;
-       u_char  current_cyl_lb;
-       u_short step_rate;              /* stepping motor rate                  */
-       u_char  landing_cyl_ub;         /* landing zone                         */
-       u_char  landing_cyl_mb;
-       u_char  landing_cyl_lb;
-       u_char  _r2;
-       u_char  _r3;
-       u_char  _r4;
-       };
-
-/*
- * cache parameters
+ * the raw frame layout:
+ *
+ * - audio (red):                  | audio_sample_bytes |
+ *                                 |        2352        |
+ *
+ * - data (yellow, mode1):         | sync - head - data - EDC - zero - ECC |
+ *                                 |  12  -   4  - 2048 -  4  -   8  - 276 |
+ *
+ * - data (yellow, mode2):         | sync - head - data |
+ *                                 |  12  -   4  - 2336 |
+ *
+ * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
+ *                                 |  12  -   4  -  8  - 2048 -  4  - 276 |
+ *
+ * - XA data (green, mode2 form2): | sync - head - sub - data - EDC |
+ *                                 |  12  -   4  -  8  - 2324 -  4  |
  */
 
-struct ccs_cache 
-       {
-       u_char  _r1             : 2;    /* reserved                             */
-       u_char  page_code       : 6;    /* page code                            */
-       u_char  page_length;            /* page length                          */
-       u_char  mode;                   /* cache control byte                   */
-       u_char  threshold;              /* prefetch threshold                   */
-       u_char  max_prefetch;           /* maximum prefetch size                */
-       u_char  max_multiplier;         /* maximum prefetch multiplier          */
-       u_char  min_prefetch;           /* minimum prefetch size                */
-       u_char  min_multiplier;         /* minimum prefetch multiplier          */
-       u_char  _r2[8];
-       };
-
 /*
  * CDROM IOCTL structures
  */
 
 struct cdrom_msf 
-       {
-       u_char  cdmsf_min0;             /* start minute                         */
-       u_char  cdmsf_sec0;             /* start second                         */
-       u_char  cdmsf_frame0;           /* start frame                          */
-       u_char  cdmsf_min1;             /* end minute                           */
-       u_char  cdmsf_sec1;             /* end second                           */
-       u_char  cdmsf_frame1;           /* end frame                            */
-       };
+{
+       u_char  cdmsf_min0;     /* start minute */
+       u_char  cdmsf_sec0;     /* start second */
+       u_char  cdmsf_frame0;   /* start frame */
+       u_char  cdmsf_min1;     /* end minute */
+       u_char  cdmsf_sec1;     /* end second */
+       u_char  cdmsf_frame1;   /* end frame */
+};
 
 struct cdrom_ti 
-       {
-       u_char  cdti_trk0;              /* start track                          */
-       u_char  cdti_ind0;              /* start index                          */
-       u_char  cdti_trk1;              /* end track                            */
-       u_char  cdti_ind1;              /* end index                            */
-       };
+{
+       u_char  cdti_trk0;      /* start track */
+       u_char  cdti_ind0;      /* start index */
+       u_char  cdti_trk1;      /* end track */
+       u_char  cdti_ind1;      /* end index */
+};
 
 struct cdrom_tochdr    
-       {
-       u_char  cdth_trk0;              /* start track                          */
-       u_char  cdth_trk1;              /* end track                            */
-       };
+{
+       u_char  cdth_trk0;      /* start track */
+       u_char  cdth_trk1;      /* end track */
+};
 
 struct cdrom_tocentry 
-       {
+{
        u_char  cdte_track;
        u_char  cdte_adr        :4;
        u_char  cdte_ctrl       :4;
        u_char  cdte_format;
        union
-               {
+       {
                struct
-                       {
+               {
                        u_char  minute;
                        u_char  second;
                        u_char  frame;
-                       } msf;
+               } msf;
                int     lba;
-               } cdte_addr;
+       } cdte_addr;
        u_char  cdte_datamode;
-       };
+};
 
 /*
  * CD-ROM address types (cdrom_tocentry.cdte_format)
@@ -245,19 +115,17 @@ struct cdrom_tocentry
 #define        CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
 
 /*
- * bit to tell whether track is data or audio
+ * bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl)
  */
-
 #define        CDROM_DATA_TRACK        0x04
 
 /*
  * The leadout track is always 0xAA, regardless of # of tracks on disc
  */
-
 #define        CDROM_LEADOUT   0xAA
 
 struct cdrom_subchnl 
-       {
+{
        u_char  cdsc_format;
        u_char  cdsc_audiostatus;
        u_char  cdsc_adr:       4;
@@ -265,74 +133,72 @@ struct cdrom_subchnl
        u_char  cdsc_trk;
        u_char  cdsc_ind;
        union
-               {
+       {
                struct                  
-                       {
+               {
                        u_char  minute;
                        u_char  second;
                        u_char  frame;
-                       } msf;
+               } msf;
                int     lba;
-               } cdsc_absaddr;
+       } cdsc_absaddr;
        union 
-               {
+       {
                struct 
-                       {
+               {
                        u_char  minute;
                        u_char  second;
                        u_char  frame;
-                       } msf;
+               } msf;
                int     lba;
-               } cdsc_reladdr;
-       };
+       } cdsc_reladdr;
+};
 
 /*
- * return value from READ SUBCHANNEL DATA
+ * audio states (from SCSI-2, but seen with other drives, too)
  */
-
-#define        CDROM_AUDIO_INVALID     0x00    /* audio status not supported           */
-#define        CDROM_AUDIO_PLAY        0x11    /* audio play operation in progress     */
-#define        CDROM_AUDIO_PAUSED      0x12    /* audio play operation paused          */
-#define        CDROM_AUDIO_COMPLETED   0x13    /* audio play successfully completed    */
-#define        CDROM_AUDIO_ERROR       0x14    /* audio play stopped due to error      */
-#define        CDROM_AUDIO_NO_STATUS   0x15    /* no current audio status to return    */
+#define        CDROM_AUDIO_INVALID     0x00    /* audio status not supported */
+#define        CDROM_AUDIO_PLAY        0x11    /* audio play operation in progress */
+#define        CDROM_AUDIO_PAUSED      0x12    /* audio play operation paused */
+#define        CDROM_AUDIO_COMPLETED   0x13    /* audio play successfully completed */
+#define        CDROM_AUDIO_ERROR       0x14    /* audio play stopped due to error */
+#define        CDROM_AUDIO_NO_STATUS   0x15    /* no current audio status to return */
 
 struct cdrom_volctrl
-       {
+{
        u_char  channel0;
        u_char  channel1;
        u_char  channel2;
        u_char  channel3;
-       };
+};
 
 struct cdrom_read      
-       {
+{
        int     cdread_lba;
        caddr_t cdread_bufaddr;
        int     cdread_buflen;
-       };
+};
 
 /*
- * preliminary extensions for transferring audio frames
- * currently used by sbpcd.c
- * (still may change if other drivers will use it, too):
+ * extensions for transfering audio frames
+ * currently used by sbpcd.c, cdu31a.c, ide-cd.c
  */
 struct cdrom_read_audio
+{
+       union
        {
-         union
-           {
-             struct                    
+               struct                  
                {
-                 u_char minute;
-                 u_char second;
-                 u_char frame;
+                       u_char minute;
+                       u_char second;
+                       u_char frame;
                } msf;
-             int       lba;
-           } addr; /* frame address */
-         u_char addr_format; /* CDROM_LBA or CDROM_MSF */
-         int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
-         u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
-       };
+               int     lba;
+       } addr; /* frame address */
+       u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+       int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
+       u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+};
 
 /*
  * this has to be the "arg" of the CDROMMULTISESSION ioctl
@@ -340,20 +206,20 @@ struct cdrom_read_audio
  * The returned "addr" is valid only if "xa_flag" is true.
  */
 struct cdrom_multisession
+{
+       union
        {
-         union
-           {
-             struct                    
+               struct                  
                {
-                 u_char minute;
-                 u_char second;
-                 u_char frame;
+                       u_char minute;
+                       u_char second;
+                       u_char frame;
                } msf;
-             int lba;
-           } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
-         u_char xa_flag; /* 1: "is XA disk" */
-         u_char addr_format; /* CDROM_LBA or CDROM_MSF */
-       };
+               int lba;
+       } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+       u_char xa_flag; /* 1: "is XA disk" */
+       u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+};
 
 #ifdef FIVETWELVE
 #define        CDROM_MODE1_SIZE        512
@@ -364,71 +230,225 @@ struct cdrom_multisession
 
 /*
  * CD-ROM IOCTL commands
+ * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
  */
 
-#define        CDROMPAUSE              0x5301          /* pause                        */
-#define        CDROMRESUME             0x5302          /* resume                       */
+#define CDROMPAUSE             0x5301
+#define CDROMRESUME            0x5302
+#define CDROMPLAYMSF           0x5303  /* (struct cdrom_msf) */
+#define CDROMPLAYTRKIND                0x5304  /* (struct cdrom_ti) */
 
-#define        CDROMPLAYMSF            0x5303          /* (struct cdrom_msf)           */
-                                                /* SCMD_PLAY_AUDIO_MSF         */
+#define CDROMREADTOCHDR                0x5305  /* (struct cdrom_tochdr) */
+#define CDROMREADTOCENTRY      0x5306  /* (struct cdrom_tocentry) */
 
-#define        CDROMPLAYTRKIND         0x5304          /* (struct cdrom_ti)            */
-                                                /* SCMD_PLAY_AUDIO_TI          */
+#define CDROMSTOP              0x5307  /* stop the drive motor */
+#define CDROMSTART             0x5308  /* turn the motor on */
 
-#define        CDROMREADTOCHDR         0x5305          /* (struct cdrom_tochdr)        */
-                                                /* read the TOC header         */
-#define        CDROMREADTOCENTRY       0x5306          /* (struct cdrom_tocentry)      */
-                                                /* read a TOC entry            */
+#define CDROMEJECT             0x5309  /* eject CD-ROM media */
 
-#define        CDROMSTOP               0x5307          /* stop the drive motor         */
-#define        CDROMSTART              0x5308          /* turn the motor on            */
+#define CDROMVOLCTRL           0x530a  /* (struct cdrom_volctrl) */
 
-#define        CDROMEJECT              0x5309          /* eject CD-ROM media           */
+#define CDROMSUBCHNL           0x530b  /* (struct cdrom_subchnl) */
 
-#define        CDROMVOLCTRL            0x530a          /* (struct cdrom_volctrl)       */
-                                                /* volume control              */
+#define CDROMREADMODE2         0x530c  /* (struct cdrom_read) */
+                                          /* read type-2 data (not suppt) */
 
-#define        CDROMSUBCHNL            0x530b          /* (struct cdrom_subchnl)       */
-                                                /* read Q sub-channel data     */
+#define CDROMREADMODE1         0x530d  /* (struct cdrom_read) */
+                                          /* read type-1 data */
 
-#define        CDROMREADMODE2          0x530c          /* (struct cdrom_read)          */
-                                                /* read type-2 data (not suppt)        */
+#define CDROMREADAUDIO         0x530e  /* (struct cdrom_read_audio) */
 
-#define        CDROMREADMODE1          0x530d          /* (struct cdrom_read)          */
-                                                /* read type-1 data            */
-/*
- * preliminary extension for transferring audio frames
- * currently used by cdu31a.c and sbpcd.c
- * (still may change if other drivers will use it, too):
- */
-#define        CDROMREADAUDIO          0x530e          /* (struct cdrom_read_audio)    */
 /*
- * preliminary extension for enable (1) / disable (0) auto-ejecting
- * currently used by sbpcd.c and sr.c
- * (still may change if other drivers will use it, too):
+ * enable (1) / disable (0) auto-ejecting
  */
-#define        CDROMEJECT_SW           0x530f          /* arg: 0 or 1 */
+#define CDROMEJECT_SW          0x530f  /* arg: 0 or 1 */
  
 /*
  * obtain the start-of-last-session address of multi session disks
  */
-#define        CDROMMULTISESSION       0x5310 /* (struct cdrom_multisession) */
+#define CDROMMULTISESSION      0x5310  /* (struct cdrom_multisession) */
 
 /*
  * obtain the "universal product code" number
  * (only some data disks have it coded)
  */
-#define        CDROM_GET_UPC           0x5311 /* 8 bytes returned */
+#define CDROM_GET_UPC          0x5311  /* 8 bytes returned */
 
-#define        CDROMRESET              0x5312 /* hard-reset the drive */
-#define        CDROMVOLREAD            0x5313 /* let the drive tell its volume setting */
+#define CDROMRESET             0x5312  /* hard-reset the drive */
+#define CDROMVOLREAD           0x5313  /* let the drive tell its volume setting */
                                        /* (struct cdrom_volctrl) */
 
 /* 
- *these ioctls are used in aztcd.c
+ * these ioctls are used in aztcd.c
  */
-#define CDROMREADRAW            0x5314  /*read data in raw mode*/
-#define CDROMREADCOOKED         0x5315  /*read data in cooked mode*/
-#define CDROMSEEK               0x5316  /*seek msf address*/
+#define CDROMREADRAW           0x5314  /* read data in raw mode */
+#define CDROMREADCOOKED                0x5315  /* read data in cooked mode */
+#define CDROMSEEK              0x5316  /*seek msf address*/
+  
+
+
+/*
+ * CD-ROM-specific SCSI command opcodes
+ */
+
+/*
+ * Group 2 (10-byte).  All of these are called 'optional' by SCSI-II.
+ */
+#define SCMD_READ_TOC          0x43    /* read table of contents */
+#define SCMD_PLAYAUDIO_MSF     0x47    /* play data at time offset */
+#define SCMD_PLAYAUDIO_TI      0x48    /* play data at track/index */
+#define SCMD_PAUSE_RESUME      0x4B    /* pause/resume audio */
+#define SCMD_READ_SUBCHANNEL   0x42    /* read SC info on playing disc */
+#define SCMD_PLAYAUDIO10       0x45    /* play data at logical block */
+#define SCMD_READ_HEADER       0x44    /* read TOC header */
+
+/*
+ * Group 5
+ */
+#define SCMD_PLAYAUDIO12       0xA5    /* play data at logical block */
+#define SCMD_PLAYTRACK_REL12   0xA9    /* play track at relative offset */
+
+/*
+ * Group 6 Commands
+ */
+#define SCMD_CD_PLAYBACK_CONTROL 0xC9  /* Sony vendor-specific audio */
+#define SCMD_CD_PLAYBACK_STATUS 0xC4   /* control opcodes */
+
+/*
+ * CD-ROM capacity structure.
+ */
+struct scsi_capacity 
+{
+       u_long  capacity;
+       u_long  lbasize;
+};
+
+/*
+ * CD-ROM MODE_SENSE/MODE_SELECT parameters
+ */
+#define ERR_RECOVERY_PARMS     0x01
+#define DISCO_RECO_PARMS       0x02
+#define FORMAT_PARMS           0x03
+#define GEOMETRY_PARMS         0x04
+#define CERTIFICATION_PARMS    0x06
+#define CACHE_PARMS            0x38
+
+/*
+ * standard mode-select header prepended to all mode-select commands
+ */
+struct ccs_modesel_head 
+{
+       u_char  _r1;    /* reserved */
+       u_char  medium; /* device-specific medium type */
+       u_char  _r2;    /* reserved */
+       u_char  block_desc_length; /* block descriptor length */
+       u_char  density; /* device-specific density code */
+       u_char  number_blocks_hi; /* number of blocks in this block desc */
+       u_char  number_blocks_med;
+       u_char  number_blocks_lo;
+       u_char  _r3;
+       u_char  block_length_hi; /* block length for blocks in this desc */
+       u_short block_length;
+};
+
+/*
+ * error recovery parameters
+ */
+struct ccs_err_recovery 
+{
+       u_char  _r1 : 2;        /* reserved */
+       u_char  page_code : 6;  /* page code */
+       u_char  page_length;    /* page length */
+       u_char  awre    : 1;    /* auto write realloc enabled */
+       u_char  arre    : 1;    /* auto read realloc enabled */
+       u_char  tb      : 1;    /* transfer block */
+       u_char  rc      : 1;    /* read continuous */
+       u_char  eec     : 1;    /* enable early correction */
+       u_char  per     : 1;    /* post error */
+       u_char  dte     : 1;    /* disable transfer on error */
+       u_char  dcr     : 1;    /* disable correction */
+       u_char  retry_count;    /* error retry count */
+       u_char  correction_span; /* largest recov. to be attempted, bits */
+       u_char  head_offset_count; /* head offset (2's C) for each retry */
+       u_char  strobe_offset_count; /* data strobe */
+       u_char  recovery_time_limit; /* time limit on recovery attempts */
+};
+
+/*
+ * disco/reco parameters
+ */
+struct ccs_disco_reco 
+{
+       u_char  _r1     : 2;    /* reserved */
+       u_char  page_code : 6;  /* page code */
+       u_char  page_length;    /* page length */
+       u_char  buffer_full_ratio; /* write buffer reconnect threshold */
+       u_char  buffer_empty_ratio; /* read */
+       u_short bus_inactivity_limit; /* limit on bus inactivity time */
+       u_short disconnect_time_limit; /* minimum disconnect time */
+       u_short connect_time_limit; /* minimum connect time */
+       u_short _r2;            /* reserved */
+};
+
+/*
+ * drive geometry parameters
+ */
+struct ccs_geometry 
+{
+       u_char  _r1     : 2;    /* reserved */
+       u_char  page_code : 6;  /* page code */
+       u_char  page_length;    /* page length */
+       u_char  cyl_ub;         /* #cyls */
+       u_char  cyl_mb;
+       u_char  cyl_lb;
+       u_char  heads;          /* #heads */
+       u_char  precomp_cyl_ub; /* precomp start */
+       u_char  precomp_cyl_mb;
+       u_char  precomp_cyl_lb;
+       u_char  current_cyl_ub; /* reduced current start */
+       u_char  current_cyl_mb;
+       u_char  current_cyl_lb;
+       u_short step_rate;      /* stepping motor rate */
+       u_char  landing_cyl_ub; /* landing zone */
+       u_char  landing_cyl_mb;
+       u_char  landing_cyl_lb;
+       u_char  _r2;
+       u_char  _r3;
+       u_char  _r4;
+};
+
+/*
+ * cache parameters
+ */
+struct ccs_cache 
+{
+       u_char  _r1     : 2;    /* reserved */
+       u_char  page_code : 6;  /* page code */
+       u_char  page_length;    /* page length */
+       u_char  mode;           /* cache control byte */
+       u_char  threshold;      /* prefetch threshold */
+       u_char  max_prefetch;   /* maximum prefetch size */
+       u_char  max_multiplier; /* maximum prefetch multiplier */
+       u_char  min_prefetch;   /* minimum prefetch size */
+       u_char  min_multiplier; /* minimum prefetch multiplier */
+       u_char  _r2[8];
+};
 
 #endif  _LINUX_CDROM_H
+/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file. 
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
index 1a44953b2ade188f05d0031a370ec4314458641e..80043f478eb4b079f38ecf27c110536133cc9f0b 100644 (file)
@@ -82,24 +82,24 @@ struct hd_geometry {
       unsigned long start;
 };
 
-/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x30n/0x31n */
-#define HDIO_GETGEO            0x301   /* get device geometry */
-#define HDIO_GET_UNMASKINTR    0x302   /* get current unmask setting */
-#define HDIO_GET_MULTCOUNT     0x304   /* get current IDE blockmode setting */
-#define HDIO_GET_IDENTITY      0x307   /* get IDE identification info */
-#define HDIO_GET_KEEPSETTINGS  0x308   /* get keep-settings-on-reset flag */
-#define HDIO_GET_CHIPSET       0x309   /* get current interface type setting */
-#define HDIO_GET_NOWERR                0x30a   /* get ignore-write-error flag */
-#define HDIO_GET_DMA           0x30b   /* get use-dma flag */
-#define HDIO_DRIVE_CMD         0x31f   /* execute a special drive command */
-
-/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x32n/0x33n */
-#define HDIO_SET_MULTCOUNT     0x321   /* set IDE blockmode */
-#define HDIO_SET_UNMASKINTR    0x322   /* permit other irqs during I/O */
-#define HDIO_SET_KEEPSETTINGS  0x323   /* keep ioctl settings on reset */
-#define HDIO_SET_CHIPSET       0x324   /* optimise driver for interface type */
-#define HDIO_SET_NOWERR                0x325   /* set ignore-write-error flag */
-#define HDIO_SET_DMA           0x326   /* set use-dma flag */
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define HDIO_GETGEO            0x0301  /* get device geometry */
+#define HDIO_GET_UNMASKINTR    0x0302  /* get current unmask setting */
+#define HDIO_GET_MULTCOUNT     0x0304  /* get current IDE blockmode setting */
+#define HDIO_GET_IDENTITY      0x0307  /* get IDE identification info */
+#define HDIO_GET_KEEPSETTINGS  0x0308  /* get keep-settings-on-reset flag */
+#define HDIO_GET_CHIPSET       0x0309  /* get current interface type setting */
+#define HDIO_GET_NOWERR                0x030a  /* get ignore-write-error flag */
+#define HDIO_GET_DMA           0x030b  /* get use-dma flag */
+#define HDIO_DRIVE_CMD         0x031f  /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define HDIO_SET_MULTCOUNT     0x0321  /* set IDE blockmode */
+#define HDIO_SET_UNMASKINTR    0x0322  /* permit other irqs during I/O */
+#define HDIO_SET_KEEPSETTINGS  0x0323  /* keep ioctl settings on reset */
+#define HDIO_SET_CHIPSET       0x0324  /* optimise driver for interface type */
+#define HDIO_SET_NOWERR                0x0325  /* set ignore-write-error flag */
+#define HDIO_SET_DMA           0x0326  /* set use-dma flag */
 
 /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
 struct hd_driveid {
index 35494cda78413558e589f5cba537a05ef26e24d8..c3c700e929a0ca6c4fb403a6b32272fda332817f 100644 (file)
 #define IDE1_MAJOR     22
 #define MITSUMI_CDROM_MAJOR 23
 #define CDU535_CDROM_MAJOR 24
+#define STL_SERIALMAJOR 24
 #define MATSUSHITA_CDROM_MAJOR 25
+#define STL_CALLOUTMAJOR 25
 #define MATSUSHITA_CDROM2_MAJOR 26
 #define QIC117_TAPE_MAJOR 27
 #define MATSUSHITA_CDROM3_MAJOR 27
 #define MATSUSHITA_CDROM4_MAJOR 28
+#define STL_SIOMEMMAJOR 28
 #define AZTECH_CDROM_MAJOR 29
 #define CM206_CDROM_MAJOR 32
 #define IDE2_MAJOR     33
index df4e4271813825dfbc9a6737fe61f3cc1ab090ab..f9c93249c38a49585000e3d53fed3ec8c6ef4adf 100644 (file)
@@ -8,4 +8,14 @@
 
 unsigned long mouse_init(unsigned long);
 
+struct mouse {
+       int minor;
+       const char *name;
+       struct file_operations *fops;
+       struct mouse * next, * prev;
+};
+
+extern int mouse_register(struct mouse * mouse);
+extern int mouse_deregister(struct mouse * mouse);
+
 #endif
index 37d267b321f7ed8e26d77d68a9a63c709cbc5354..dd9fb38d328edcf72df4c8da45d3df341cb3babe 100644 (file)
  *                             or
  *                                 sbpcd=0x300,LaserMate
  *                             or
- *                                 sbpcd=0x330,SPEA
+ *                                 sbpcd=0x338,SoundScape
  *
  * If sbpcd gets used as a module, you can load it with
  *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
  * or
  *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x300,0
  * or
- *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x330,2
+ *     insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x338,2
  * respective to override the configured address and type.
  */
 
  * ========
  * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
  * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ...
- * SPEA addresses are from the LASERMATE type and range.
+ * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to
+ * specify the REAL address here, not the configuration port address. Look
+ * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let
+ * sbpcd auto-probe, if you are not firm with the address.
  * There are some soundcards on the market with 0x0630, 0x0650, ...; their
  * type is not obvious (both types are possible).
  *
  * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1.
  *          if your soundcard has its CDROM port above 0x300, specify
  *          that address and try SBPRO 0 first.
+ *          if your SoundScape configuration port is at 0x330, specify
+ *          0x338 and SBPRO 2.
  *
  * interface type:
  * ===============
  * set SBPRO to 1 for "true" SoundBlaster card
  * set SBPRO to 0 for "compatible" soundcards and
  *                for "poor" (no sound) interface cards.
- * set SBPRO to 2 for the SPEA Media FX card
+ * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
  *
  * Almost all "compatible" sound boards need to set SBPRO to 0.
  * If SBPRO is set wrong, the drives will get found - but any
index 8843b0aa7be8adcd22c571b473bef1843d31ed12..426c2884bf29b3ab5f09701daa3b9dda06ad947c 100644 (file)
@@ -127,12 +127,6 @@ struct mm_struct {
        unsigned long start_brk, brk, start_stack, start_mmap;
        unsigned long arg_start, arg_end, env_start, env_end;
        unsigned long rss;
-       unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
-       int swappable:1;
-       unsigned long swap_address;
-       unsigned long old_maj_flt;      /* old value of maj_flt */
-       unsigned long dec_flt;          /* page fault count of the last time */
-       unsigned long swap_cnt;         /* number of pages to swap on next pass */
        struct vm_area_struct * mmap;
        struct vm_area_struct * mmap_avl;
 };
@@ -144,9 +138,6 @@ struct mm_struct {
                0, 0, 0, 0, \
                0, 0, 0, 0, \
                0, \
-/* ?_flt */    0, 0, 0, 0, \
-               0, \
-/* swap */     0, 0, 0, 0, \
                &init_mmap, &init_mmap }
 
 struct signal_struct {
@@ -195,7 +186,15 @@ struct task_struct {
        unsigned long it_real_incr, it_prof_incr, it_virt_incr;
        struct timer_list real_timer;
        long utime, stime, cutime, cstime, start_time;
-       struct rlimit rlim[RLIM_NLIMITS]; 
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+       unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
+       int swappable:1;
+       unsigned long swap_address;
+       unsigned long old_maj_flt;      /* old value of maj_flt */
+       unsigned long dec_flt;          /* page fault count of the last time */
+       unsigned long swap_cnt;         /* number of pages to swap on next pass */
+/* limits */
+       struct rlimit rlim[RLIM_NLIMITS];
        unsigned short used_math;
        char comm[16];
 /* file system info */
@@ -254,6 +253,8 @@ struct task_struct {
 /* timeout */  0,0,0,0,0,0,0, \
 /* timer */    { NULL, NULL, 0, 0, it_real_fn }, \
 /* utime */    0,0,0,0,0, \
+/* flt */      0,0,0,0, \
+/* swp */      0,0,0,0,0, \
 /* rlimits */   { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
                  {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},  \
                  {       0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
index 3fee0d665dd3d0be976eac80a2fa5f55561d7999..d4bb47a3b534680399e26afcd84222ccbbd1ce77 100644 (file)
@@ -281,6 +281,12 @@ extern long vcs_init(long);
 #ifdef CONFIG_CYCLADES
 extern long cy_init(long);
 #endif
+#ifdef CONFIG_STALLION
+extern long stl_init(long);
+#endif
+#ifdef CONFIG_ISTALLION
+extern long stli_init(long);
+#endif
 
 extern int tty_paranoia_check(struct tty_struct *tty, dev_t device,
                              const char *routine);
index f7c7f35802719df08f2694dc6383a36188e71216..e563966b2eb9a35e018d2a460eaf67c7990e7eb2 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -660,10 +660,10 @@ static pte_t shm_swap_in(struct vm_area_struct * shmd, unsigned long offset, uns
                pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
                shp->shm_pages[idx] = pte_val(pte);
        } else
-               --current->mm->maj_flt;  /* was incremented in do_no_page */
+               --current->maj_flt;  /* was incremented in do_no_page */
 
 done:  /* pte_val(pte) == shp->shm_pages[idx] */
-       current->mm->min_flt++;
+       current->min_flt++;
        mem_map[MAP_NR(pte_page(pte))]++;
        return pte_modify(pte, shmd->vm_page_prot);
 }
index ce25eec1c145290027b6f3ca4d1ef8087b0983c7..08fb1ac92466e780aacd9f128e10577082b844dd 100644 (file)
@@ -97,6 +97,8 @@ void release(struct task_struct * p)
                        if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
                                printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
                        free_page(p->kernel_stack_page);
+                       current->cmin_flt += p->min_flt + p->cmin_flt;
+                       current->cmaj_flt += p->maj_flt + p->cmaj_flt;
                        kfree(p);
                        return;
                }
@@ -405,10 +407,9 @@ static void exit_mm(void)
 {
        struct mm_struct * mm = current->mm;
 
+       current->swappable = 0;
        if (mm) {
                if (!--mm->count) {
-                       current->p_pptr->mm->cmin_flt += mm->min_flt + mm->cmin_flt;
-                       current->p_pptr->mm->cmaj_flt += mm->maj_flt + mm->cmaj_flt;
                        exit_mmap(mm);
                        free_page_tables(current);
                        kfree(mm);
index 35cdba3f946a9ec8f2c53d150518ac1885ec4dfa..1d5cb66b97ecea6d0643930037a6536bfc86bab8 100644 (file)
@@ -108,8 +108,8 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
                return -1;
        *tsk->mm = *current->mm;
        tsk->mm->count = 1;
-       tsk->mm->min_flt = tsk->mm->maj_flt = 0;
-       tsk->mm->cmin_flt = tsk->mm->cmaj_flt = 0;
+       tsk->min_flt = tsk->maj_flt = 0;
+       tsk->cmin_flt = tsk->cmaj_flt = 0;
        if (new_page_tables(tsk))
                return -1;
        if (dup_mmap(tsk->mm)) {
@@ -205,6 +205,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
                (*p->binfmt->use_count)++;
 
        p->did_exec = 0;
+       p->swappable = 0;
        p->kernel_stack_page = new_stack;
        *(unsigned long *) p->kernel_stack_page = STACK_MAGIC;
        p->state = TASK_UNINTERRUPTIBLE;
@@ -242,7 +243,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
        p->semundo = NULL;
 
        /* ok, now we should be set up.. */
-       p->mm->swappable = 1;
+       p->swappable = 1;
        p->exit_signal = clone_flags & CSIGNAL;
        p->counter = current->counter >> 1;
        wake_up_process(p);                     /* do this last, just in case */
index b90fc3dc47923986db8c087eaa509310beb1f323..1dccd2eb1789e6280eec2c02798ee0642e95aefc 100644 (file)
@@ -764,24 +764,24 @@ int getrusage(struct task_struct *p, int who, struct rusage *ru)
                        r.ru_utime.tv_usec = CT_TO_USECS(p->utime);
                        r.ru_stime.tv_sec = CT_TO_SECS(p->stime);
                        r.ru_stime.tv_usec = CT_TO_USECS(p->stime);
-                       r.ru_minflt = p->mm->min_flt;
-                       r.ru_majflt = p->mm->maj_flt;
+                       r.ru_minflt = p->min_flt;
+                       r.ru_majflt = p->maj_flt;
                        break;
                case RUSAGE_CHILDREN:
                        r.ru_utime.tv_sec = CT_TO_SECS(p->cutime);
                        r.ru_utime.tv_usec = CT_TO_USECS(p->cutime);
                        r.ru_stime.tv_sec = CT_TO_SECS(p->cstime);
                        r.ru_stime.tv_usec = CT_TO_USECS(p->cstime);
-                       r.ru_minflt = p->mm->cmin_flt;
-                       r.ru_majflt = p->mm->cmaj_flt;
+                       r.ru_minflt = p->cmin_flt;
+                       r.ru_majflt = p->cmaj_flt;
                        break;
                default:
                        r.ru_utime.tv_sec = CT_TO_SECS(p->utime + p->cutime);
                        r.ru_utime.tv_usec = CT_TO_USECS(p->utime + p->cutime);
                        r.ru_stime.tv_sec = CT_TO_SECS(p->stime + p->cstime);
                        r.ru_stime.tv_usec = CT_TO_USECS(p->stime + p->cstime);
-                       r.ru_minflt = p->mm->min_flt + p->mm->cmin_flt;
-                       r.ru_majflt = p->mm->maj_flt + p->mm->cmaj_flt;
+                       r.ru_minflt = p->min_flt + p->cmin_flt;
+                       r.ru_majflt = p->maj_flt + p->cmaj_flt;
                        break;
        }
        memcpy_tofs(ru, &r, sizeof(r));
index 1972443c94fecc7135ad2205c1cf43615568966b..e150d1b21d139f219017c7c10d3d83c488158071 100644 (file)
@@ -628,7 +628,7 @@ void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
        old_page = pte_page(pte);
        if (old_page >= high_memory)
                goto bad_wp_page;
-       vma->vm_mm->min_flt++;
+       tsk->min_flt++;
        /*
         * Do we need to copy?
         */
@@ -969,7 +969,7 @@ static inline void do_swap_page(struct task_struct * tsk,
        if (mem_map[MAP_NR(pte_page(page))] > 1 && !(vma->vm_flags & VM_SHARED))
                page = pte_wrprotect(page);
        ++vma->vm_mm->rss;
-       ++vma->vm_mm->maj_flt;
+       ++tsk->maj_flt;
        set_pte(page_table, page);
        return;
 }
@@ -1000,14 +1000,14 @@ void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma,
        address &= PAGE_MASK;
        if (!vma->vm_ops || !vma->vm_ops->nopage) {
                ++vma->vm_mm->rss;
-               ++vma->vm_mm->min_flt;
+               ++tsk->min_flt;
                get_empty_page(tsk, vma, page_table);
                return;
        }
        page = __get_free_page(GFP_KERNEL);
        if (share_page(vma, address, write_access, page)) {
-               ++vma->vm_mm->min_flt;
                ++vma->vm_mm->rss;
+               ++tsk->min_flt;
                return;
        }
        if (!page) {
@@ -1015,7 +1015,7 @@ void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma,
                put_page(page_table, BAD_PAGE);
                return;
        }
-       ++vma->vm_mm->maj_flt;
+       ++tsk->maj_flt;
        ++vma->vm_mm->rss;
        /*
         * The fourth argument is "no_share", which tells the low-level code
index d6f01252c5b0e6f5321ad6e6186c42443c17d103..fe74f80735629cf80eaf089a0ab525ec00cfd69d 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -331,7 +331,7 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
                return;
        }
        vma->vm_mm->rss++;
-       vma->vm_mm->maj_flt++;
+       tsk->maj_flt++;
        if (!write_access && add_to_swap_cache(page, entry)) {
                set_pte(page_table, mk_pte(page, vma->vm_page_prot));
                return;
@@ -461,7 +461,7 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
 
        do {
                int result;
-               vma->vm_mm->swap_address = address + PAGE_SIZE;
+               tsk->swap_address = address + PAGE_SIZE;
                result = try_to_swap_out(tsk, vma, address, pte, limit);
                if (result)
                        return result;
@@ -530,8 +530,8 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
        /*
         * Go through process' page directory.
         */
-       address = p->mm->swap_address;
-       p->mm->swap_address = 0;
+       address = p->swap_address;
+       p->swap_address = 0;
 
        /*
         * Find the proper vm-area
@@ -551,7 +551,7 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
                        break;
                address = vma->vm_start;
        }
-       p->mm->swap_address = 0;
+       p->swap_address = 0;
        return 0;
 }
 
@@ -578,7 +578,7 @@ static int swap_out(unsigned int priority, unsigned long limit)
                        }
 
                        p = task[swap_task];
-                       if (p && p->mm && p->mm->swappable && p->mm->rss)
+                       if (p && p->swappable && p->mm->rss)
                                break;
 
                        swap_task++;
@@ -587,23 +587,23 @@ static int swap_out(unsigned int priority, unsigned long limit)
                /*
                 * Determine the number of pages to swap from this process.
                 */
-               if (!p->mm->swap_cnt) {
-                       p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt;
-                       p->mm->old_maj_flt = p->mm->maj_flt;
-
-                       if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) {
-                               p->mm->dec_flt = SWAP_RATIO / SWAP_MIN;
-                               p->mm->swap_cnt = SWAP_MIN;
-                       } else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX)
-                               p->mm->swap_cnt = SWAP_MAX;
+               if (!p->swap_cnt) {
+                       p->dec_flt = (p->dec_flt * 3) / 4 + p->maj_flt - p->old_maj_flt;
+                       p->old_maj_flt = p->maj_flt;
+
+                       if (p->dec_flt >= SWAP_RATIO / SWAP_MIN) {
+                               p->dec_flt = SWAP_RATIO / SWAP_MIN;
+                               p->swap_cnt = SWAP_MIN;
+                       } else if (p->dec_flt <= SWAP_RATIO / SWAP_MAX)
+                               p->swap_cnt = SWAP_MAX;
                        else
-                               p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt;
+                               p->swap_cnt = SWAP_RATIO / p->dec_flt;
                }
-               if (!--p->mm->swap_cnt)
+               if (!--p->swap_cnt)
                        swap_task++;
                switch (swap_out_process(p, limit)) {
                        case 0:
-                               if (p->mm->swap_cnt)
+                               if (p->swap_cnt)
                                        swap_task++;
                                break;
                        case 1: