diff -u --recursive --new-file v1.3.26/linux/Makefile linux/Makefile
--- v1.3.26/linux/Makefile	Wed Sep 13 12:45:28 1995
+++ linux/Makefile	Wed Sep 13 12:46:09 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 26
+SUBLEVEL = 27
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.26/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.26/linux/arch/i386/config.in	Wed Sep 13 12:45:29 1995
+++ linux/arch/i386/config.in	Thu Sep 14 15:03:36 1995
@@ -226,13 +226,13 @@
   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 @@
 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
diff -u --recursive --new-file v1.3.26/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd
--- v1.3.26/linux/drivers/block/README.sbpcd	Fri Jun  2 08:16:16 1995
+++ linux/drivers/block/README.sbpcd	Thu Sep 14 09:46:22 1995
@@ -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 @@
 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, ...).
+SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P,
+LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
 
-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.
+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.
+
+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).
 
-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.
-
-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 @@
 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 @@
 
 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 @@
       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 @@
         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 @@
 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 @@
 
 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 @@
 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 @@
 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 @@
 			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 @@
 			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 @@
 	}
 }
 /*==========================================================================*/
-/*
- * 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:
- */
 
diff -u --recursive --new-file v1.3.26/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.3.26/linux/drivers/block/sbpcd.c	Sun Sep  3 12:26:51 1995
+++ linux/drivers/block/sbpcd.c	Thu Sep 14 09:46:22 1995
@@ -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
  *
@@ -217,12 +217,19 @@
  *
  *  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 @@
 
 #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 @@
 	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 @@
 	 */
 	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_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 @@
 	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 @@
 	u_char UPC_ctl_adr;
 	u_char UPC_buf[7];
 	
-	int CDsize_blk;
 	int frame_size;
 	int CDsize_frm;
 	
@@ -730,20 +739,26 @@
 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 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 @@
 	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 @@
 	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 @@
 {
 	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 @@
 			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 @@
 				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 @@
 					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 @@
 				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 @@
 				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 @@
 		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 @@
 	{
 		i=cc_LockDoor(0);
 		--j;
-		sbp_sleep(HZ/100);
+		sbp_sleep(1);
 	}
 	while ((i<0)&&(j));
 	if (i<0)
@@ -2095,7 +2110,7 @@
 	{
 		i=cc_LockDoor(1);
 		--j;
-		sbp_sleep(HZ/100);
+		sbp_sleep(1);
 	}
 	while ((i<0)&&(j));
 	if (j==0)
@@ -2106,7 +2121,7 @@
 		{
 			i=cc_LockDoor(1);
 			--j;
-			sbp_sleep(HZ/100);
+			sbp_sleep(1);
 		}
 		while ((i<0)&&(j));
 	}
@@ -2256,12 +2271,12 @@
 	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 @@
 {
 	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 @@
 			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 @@
 	{
 		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 @@
 	
 	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 @@
 		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 @@
 	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 @@
 	}
 	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 @@
 #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 @@
 			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 @@
 	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 @@
 		
 	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 @@
 				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 @@
 					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 @@
  *             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 @@
 {
 	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 @@
 /*==========================================================================*/
 /*
  * 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 @@
 	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 @@
 #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 */
diff -u --recursive --new-file v1.3.26/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.26/linux/drivers/char/Makefile	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/Makefile	Thu Sep 14 15:05:38 1995
@@ -24,6 +24,22 @@
 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
diff -u --recursive --new-file v1.3.26/linux/drivers/char/atixlmouse.c linux/drivers/char/atixlmouse.c
--- v1.3.26/linux/drivers/char/atixlmouse.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/atixlmouse.c	Thu Sep 14 08:07:08 1995
@@ -22,13 +22,12 @@
 #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) */
diff -u --recursive --new-file v1.3.26/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v1.3.26/linux/drivers/char/busmouse.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/busmouse.c	Thu Sep 14 08:07:08 1995
@@ -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 @@
 	fasync_mouse,
 };
 
-static struct mouse bus_mous = {
+static struct mouse bus_mouse = {
 	LOGITECH_BUSMOUSE, "busmouse", &bus_mouse_fops
 };
 
diff -u --recursive --new-file v1.3.26/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v1.3.26/linux/drivers/char/istallion.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/istallion.c	Thu Sep 14 15:03:36 1995
@@ -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);
+}
+
+/*****************************************************************************/
diff -u --recursive --new-file v1.3.26/linux/drivers/char/mouse.c linux/drivers/char/mouse.c
--- v1.3.26/linux/drivers/char/mouse.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/mouse.c	Thu Sep 14 08:07:08 1995
@@ -32,8 +32,6 @@
 #include <linux/major.h>
 #include <linux/malloc.h>
 
-#include "mouse.h"
-
 /*
  * Head entry for the doubly linked mouse list
  */
diff -u --recursive --new-file v1.3.26/linux/drivers/char/mouse.h linux/drivers/char/mouse.h
--- v1.3.26/linux/drivers/char/mouse.h	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/mouse.h	Thu Jan  1 02:00:00 1970
@@ -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
diff -u --recursive --new-file v1.3.26/linux/drivers/char/msbusmouse.c linux/drivers/char/msbusmouse.c
--- v1.3.26/linux/drivers/char/msbusmouse.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/msbusmouse.c	Thu Sep 14 08:07:51 1995
@@ -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>
diff -u --recursive --new-file v1.3.26/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v1.3.26/linux/drivers/char/psaux.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/psaux.c	Thu Sep 14 08:07:08 1995
@@ -45,14 +45,13 @@
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <linux/malloc.h>
+#include <linux/mouse.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/system.h>
 
 #include <linux/config.h>
-
-#include "mouse.h"
 
 #define PSMOUSE_MINOR      1            /* minor device # for this mouse */
 
diff -u --recursive --new-file v1.3.26/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v1.3.26/linux/drivers/char/stallion.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/stallion.c	Thu Sep 14 15:03:36 1995
@@ -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);
+}
+
+/*****************************************************************************/
diff -u --recursive --new-file v1.3.26/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.3.26/linux/drivers/char/tty_io.c	Wed Sep 13 12:45:30 1995
+++ linux/drivers/char/tty_io.c	Thu Sep 14 15:03:36 1995
@@ -1789,6 +1789,12 @@
 #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);
 	return kmem_start;
diff -u --recursive --new-file v1.3.26/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.26/linux/fs/buffer.c	Sun Sep  3 12:27:00 1995
+++ linux/fs/buffer.c	Thu Sep 14 14:48:34 1995
@@ -1042,7 +1042,7 @@
 	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 @@
 	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 @@
 		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])
diff -u --recursive --new-file v1.3.26/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.3.26/linux/fs/isofs/inode.c	Tue Jul 11 10:02:58 1995
+++ linux/fs/isofs/inode.c	Thu Sep 14 09:46:23 1995
@@ -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 @@
 	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 @@
   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 @@
 	
 	inode->i_op = NULL;
 
-	/* get the volume sequence numner */
+	/* get the volume sequence number */
 	volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
 
 	/* 
diff -u --recursive --new-file v1.3.26/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.26/linux/fs/proc/array.c	Wed Sep 13 12:45:32 1995
+++ linux/fs/proc/array.c	Thu Sep 14 14:56:25 1995
@@ -449,10 +449,10 @@
 	        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,
diff -u --recursive --new-file v1.3.26/linux/include/linux/busmouse.h linux/include/linux/busmouse.h
--- v1.3.26/linux/include/linux/busmouse.h	Wed Sep 13 12:45:32 1995
+++ linux/include/linux/busmouse.h	Thu Sep 14 08:11:34 1995
@@ -95,7 +95,6 @@
 };
 
 /* Function Prototypes */
-extern long mouse_init(long);
 
 #endif
 
diff -u --recursive --new-file v1.3.26/linux/include/linux/cd1400.h linux/include/linux/cd1400.h
--- v1.3.26/linux/include/linux/cd1400.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/cd1400.h	Thu Sep 14 15:03:37 1995
@@ -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 -u --recursive --new-file v1.3.26/linux/include/linux/cdk.h linux/include/linux/cdk.h
--- v1.3.26/linux/include/linux/cdk.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/cdk.h	Thu Sep 14 15:03:37 1995
@@ -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
diff -u --recursive --new-file v1.3.26/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.3.26/linux/include/linux/cdrom.h	Sun Sep  3 12:27:02 1995
+++ linux/include/linux/cdrom.h	Thu Sep 14 09:46:23 1995
@@ -1,242 +1,112 @@
-/****************************************************************************************
- *											*
- * 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*/
-
-/*
- * Group 6 Commands
  */
 
-#define	SCMD_CD_PLAYBACK_CONTROL 0xC9		/* Sony vendor-specific audio	*/
-#define	SCMD_CD_PLAYBACK_STATUS 0xC4		/* control opcodes. info please!*/
-
-/*
- * 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	*/
-};
+#ifndef	_LINUX_CDROM_H
+#define	_LINUX_CDROM_H
 
 /*
- * disco/reco parameters
+ * 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 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 */
+
+#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 */
+
+#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 */
+
+#define CD_FRAMESIZE_SUB    96 /* subchannel data "frame" size */
+#define CD_MSF_OFFSET      150 /* MSF numbering offset of first frame */
 
-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
- */
+#define CD_CHUNK_SIZE       24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS    98 /* chunks per frame */
 
-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;
-	};
+#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */
+#define CD_BLOCK_OFFSET    CD_MSF_OFFSET /* obsolete name */
 
 /*
- * 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 @@
 #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 @@
 	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 @@
  * 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 @@
 
 /*
  * 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):
+ * enable (1) / disable (0) auto-ejecting
  */
-#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):
- */
-#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*/
+  
+
+
+/*
+ * 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 CDROMREADRAW            0x5314  /*read data in raw mode*/
-#define CDROMREADCOOKED         0x5315  /*read data in cooked mode*/
-#define CDROMSEEK               0x5316  /*seek msf address*/
+#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:
+ */
diff -u --recursive --new-file v1.3.26/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v1.3.26/linux/include/linux/hdreg.h	Sun Sep  3 16:12:49 1995
+++ linux/include/linux/hdreg.h	Thu Sep 14 15:10:19 1995
@@ -82,24 +82,24 @@
       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) 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 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) 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 {
diff -u --recursive --new-file v1.3.26/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.26/linux/include/linux/major.h	Sun Sep  3 12:27:02 1995
+++ linux/include/linux/major.h	Thu Sep 14 15:03:37 1995
@@ -83,11 +83,14 @@
 #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
diff -u --recursive --new-file v1.3.26/linux/include/linux/mouse.h linux/include/linux/mouse.h
--- v1.3.26/linux/include/linux/mouse.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/mouse.h	Thu Sep 14 08:05:29 1995
@@ -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
diff -u --recursive --new-file v1.3.26/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.3.26/linux/include/linux/sbpcd.h	Fri Jul  7 08:54:54 1995
+++ linux/include/linux/sbpcd.h	Thu Sep 14 09:46:23 1995
@@ -13,14 +13,14 @@
  *                             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.
  */
 
@@ -35,20 +35,25 @@
  * ========
  * 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
diff -u --recursive --new-file v1.3.26/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.3.26/linux/include/linux/sched.h	Wed Sep 13 12:45:33 1995
+++ linux/include/linux/sched.h	Thu Sep 14 14:51:02 1995
@@ -127,12 +127,6 @@
 	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 @@
 		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 @@
 	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 @@
 /* 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}, \
diff -u --recursive --new-file v1.3.26/linux/include/linux/tty.h linux/include/linux/tty.h
--- v1.3.26/linux/include/linux/tty.h	Tue Jun 20 09:10:32 1995
+++ linux/include/linux/tty.h	Thu Sep 14 15:03:37 1995
@@ -281,6 +281,12 @@
 #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);
diff -u --recursive --new-file v1.3.26/linux/ipc/shm.c linux/ipc/shm.c
--- v1.3.26/linux/ipc/shm.c	Thu Sep  7 17:17:19 1995
+++ linux/ipc/shm.c	Thu Sep 14 14:48:34 1995
@@ -660,10 +660,10 @@
 		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);
 }
diff -u --recursive --new-file v1.3.26/linux/kernel/exit.c linux/kernel/exit.c
--- v1.3.26/linux/kernel/exit.c	Wed Sep 13 12:45:33 1995
+++ linux/kernel/exit.c	Thu Sep 14 14:54:28 1995
@@ -97,6 +97,8 @@
 			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 @@
 {
 	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);
diff -u --recursive --new-file v1.3.26/linux/kernel/fork.c linux/kernel/fork.c
--- v1.3.26/linux/kernel/fork.c	Wed Sep 13 12:45:33 1995
+++ linux/kernel/fork.c	Thu Sep 14 14:53:48 1995
@@ -108,8 +108,8 @@
 		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 @@
 		(*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 @@
 	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 */
diff -u --recursive --new-file v1.3.26/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.26/linux/kernel/sys.c	Sun Sep  3 12:27:04 1995
+++ linux/kernel/sys.c	Thu Sep 14 14:57:33 1995
@@ -764,24 +764,24 @@
 			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));
diff -u --recursive --new-file v1.3.26/linux/mm/memory.c linux/mm/memory.c
--- v1.3.26/linux/mm/memory.c	Wed Sep 13 12:45:34 1995
+++ linux/mm/memory.c	Thu Sep 14 14:48:34 1995
@@ -628,7 +628,7 @@
 	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 @@
 	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 @@
 	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 @@
 		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
diff -u --recursive --new-file v1.3.26/linux/mm/swap.c linux/mm/swap.c
--- v1.3.26/linux/mm/swap.c	Wed Sep 13 12:45:34 1995
+++ linux/mm/swap.c	Thu Sep 14 14:53:08 1995
@@ -331,7 +331,7 @@
 		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 @@
 
 	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 @@
 	/*
 	 * 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 @@
 			break;
 		address = vma->vm_start;
 	}
-	p->mm->swap_address = 0;
+	p->swap_address = 0;
 	return 0;
 }
 
@@ -578,7 +578,7 @@
 			}
 
 			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 @@
 		/*
 		 * 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:
