diff -u --recursive --new-file v1.1.73/linux/CREDITS linux/CREDITS
--- v1.1.73/linux/CREDITS	Tue Dec  6 17:37:05 1994
+++ linux/CREDITS	Mon Dec 19 17:49:56 1994
@@ -721,6 +721,16 @@
 S: 00180 Helsinki
 S: Finland
 
+N: Jeff Tranter
+E: Jeff_Tranter@Mitel.COM
+D: Enhancements to Joystick driver
+D: Author of Sound HOWTO and CD-ROM HOWTO
+D: Author of several small utilities
+D: (bogomips, scope, eject, statserial)
+S: 1 Laurie Court
+S: Kanata, Ontario
+S: CANADA K2L 1S2
+
 N: Theodore Ts'o
 E: tytso@mit.edu
 D: Random Linux hacker
diff -u --recursive --new-file v1.1.73/linux/Makefile linux/Makefile
--- v1.1.73/linux/Makefile	Thu Dec 15 11:01:48 1994
+++ linux/Makefile	Fri Dec 23 13:36:25 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 73
+SUBLEVEL = 74
 
 ARCH = i386
 
@@ -148,7 +148,7 @@
 	@echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> include/linux/version.h
 
 tools/build: tools/build.c $(CONFIGURE)
-	$(HOSTCC) $(CFLAGS) -o $@ $<
+	$(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
 
 boot/head.o: $(CONFIGURE) boot/head.s
 
diff -u --recursive --new-file v1.1.73/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.1.73/linux/arch/i386/config.in	Thu Dec 15 11:01:49 1994
+++ linux/arch/i386/config.in	Thu Dec 22 13:38:24 1994
@@ -74,7 +74,7 @@
 bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
 bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n
 bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
-bool 'EISA EATA support' CONFIG_SCSI_EATA n
+bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n
 #bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
 fi
 
@@ -196,6 +196,7 @@
 
 comment 'character devices'
 
+bool 'Cyclades async mux support' CONFIG_CYCLADES n
 bool 'Parallel printer support' CONFIG_PRINTER n
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
 bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
diff -u --recursive --new-file v1.1.73/linux/arch/sparc/vac-flush.c linux/arch/sparc/vac-flush.c
--- v1.1.73/linux/arch/sparc/vac-flush.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/sparc/vac-flush.c	Mon Dec 19 17:39:00 1994
@@ -0,0 +1,93 @@
+/* vac.c:   Routines for flushing various amount of the Sparc VAC
+            (virtual address cache).
+
+   Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+*/
+
+#include <asm/vac-ops.h>
+
+/* Flush all VAC entries for the current context */
+
+extern int do_hw_vac_flushes, vac_size, vac_linesize;
+extern int vac_entries_per_context, vac_entries_per_segment;
+extern int vac_entries_per_page;
+
+void
+flush_vac_context()
+{
+  register int entries_left, offset;
+  register char* address;
+
+  entries_left = vac_entries_per_context;
+  address = (char *) 0;
+
+  if(do_hw_vac_flushes)
+    {
+      while(entries_left-- >=0)
+	{
+	  hw_flush_vac_context_entry(address);
+	  address += 4096;
+	}
+    }
+  else
+    {
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+	{
+	  sw_flush_vac_context_entry(address);
+	  address += offset;
+	}
+    }
+}
+
+void
+flush_vac_segment(register unsigned int segment)
+{
+  register int entries_left, offset;
+  register char* address;
+  
+  entries_left = vac_entries_per_segment;
+  __asm__ __volatile__("sll %0, 18, %1\n\t"
+		       "sra %1, 0x2, %1\n\t"
+		       : "=r" (segment) : "0" (address));
+
+  if(do_hw_vac_flushes)
+    {
+      while(entries_left-- >=0)
+	{
+	  hw_flush_vac_segment_entry(address);
+	  address += 4096;
+	}
+    }
+  else
+    {
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+	{
+	  sw_flush_vac_segment_entry(address);
+	  address += offset;
+	}
+    }
+}
+
+void
+flush_vac_page(register unsigned int addr)
+{
+  register int entries_left, offset;
+
+  if(do_hw_vac_flushes)
+    {
+      hw_flush_vac_page_entry(addr);
+    }
+  else
+    {
+      entries_left = vac_entries_per_page;
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+	{
+	  sw_flush_vac_page_entry(addr);
+	  addr += offset;
+	}
+    }
+}
+  
diff -u --recursive --new-file v1.1.73/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd
--- v1.1.73/linux/drivers/block/README.sbpcd	Tue Nov 15 15:35:39 1994
+++ linux/drivers/block/README.sbpcd	Mon Dec 19 17:26:24 1994
@@ -1,5 +1,6 @@
-This README belongs to release 2.8 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux.
+This README belongs to release 2.9 or newer of the SoundBlaster Pro
+(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and soon TEAC, too)
+CD-ROM driver for Linux.
 
 The driver is able to drive the whole family of "traditional" IDE-style (that
 has nothing to do with the new "Enhanced IDE" drive standard) Matsushita,
@@ -7,26 +8,36 @@
 well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
 
 The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It should be fully supported soon, too.
+Matsushita command set. It is supported now - with help by Serge Robyns.
 
-There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-562
+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
 of the "LaserMate" type), and it is possibly the best buy today (cheaper than
 an internal drive, and you can use it as an internal, too - f.e. plug it into
-a soundcard). If you own such a drive, please mail me the DOS driver (gzipped
-and uuencoded) and the specifications (exact name, address range etc.) I still
-have to confirm my opinion. ;-)
+a soundcard).
+
+The quad-speed TEAC CD-55A drive uses the same interface types, but has a
+totally different command and flow control scheme. It is not supported yet,
+but I plan to add it.
+
+CreativeLabs has a new drive "CD-200". This drive is not supported yet, but
+I plan to add it.
 
 This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives.
-The matter that some other brand's drives work with newer sound card
-interfaces does NOT make the drives compatible. :-)
+For Aztech CDA-268 drives (and for some Wearnes, Okano and Orchid drives),
+Werner Zimmermann has done a driver which currently resides at ftp.gwdg.de
+under /pub/linux/cdrom/drivers/aztech/.
 
-It will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, SoundFX,
-...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
+This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
+SoundFX, ...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
 WDH-7001C, Longshine LCS-6853, older Aztech cards, ...).
+
 It should work too now with the "configurable" interface "Sequoia S-1000",
 which is found on the Spea Media FX sound card. I still need feedback about
-this.
+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.
+
 The interface type has to get configured in /usr/include/linux/sbpcd.h, 
 because the behavior of some sound card interfaces is different.
 
@@ -252,6 +263,70 @@
 }
 /*===================== end program ========================================*/
 
+There is a new 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
+following example illustrates how to call it:
+
+/*=================== begin program ========================================*/
+/*
+ * ask for multisession redirection info
+ *
+ * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
+ *          may be used & enhanced freely
+ *
+ */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+static struct cdrom_multisession ms_info;
+static int drive;
+static int err;
+
+main(int argc, char *argv[])
+{
+/*
+ * open /dev/cdrom
+ */
+  drive=open("/dev/cdrom", 0);
+  if (drive<0)
+    {
+      fprintf(stderr, "can't open drive /dev/cdrom.\n");
+      exit (-1);
+    }
+/*
+ * 
+ */
+  ms_info.addr_format=CDROM_LBA;
+  err=ioctl(drive, CDROMMULTISESSION, &ms_info);
+  if (err!=0)
+    {
+      fprintf(stderr, "CDROMMULTISESSION(lba) returns error %d.\n", err);
+      exit (-1);
+    }
+  else
+	if (ms_info.xa_flag)
+	    fprintf(stdout, "lba: %d\n", ms_info.addr.lba);
+	else
+	    fprintf(stdout, "not an XA disk.\n");
+
+  ms_info.addr_format=CDROM_MSF;
+  err=ioctl(drive, CDROMMULTISESSION, &ms_info);
+  if (err!=0)
+    {
+      fprintf(stderr, "CDROMMULTISESSION(msf) returns error %d.\n", err);
+      exit (-1);
+    }
+  else
+	if (ms_info.xa_flag)
+	    fprintf(stdout, "msf: %02d:%02d:%02d\n",
+				 ms_info.addr.msf.minute,
+				 ms_info.addr.msf.second,
+				 ms_info.addr.msf.frame);
+	else
+	    fprintf(stdout, "not an XA disk.\n");
+}
+/*===================== end program ========================================*/
 
 
 Auto-probing at boot time:
diff -u --recursive --new-file v1.1.73/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.1.73/linux/drivers/block/sbpcd.c	Tue Nov 15 15:35:39 1994
+++ linux/drivers/block/sbpcd.c	Mon Dec 19 17:26:24 1994
@@ -6,8 +6,10 @@
  *            Panasonic CI-101P.
  *            Also for the Longshine LCS-7260 drive.
  *            Also for the IBM "External ISA CD-Rom" drive.
+ *            Not for the TEAC CD-55A drive (yet).
+ *            Not for the CreativeLabs CDD-200 drive (yet).
  *
- *  NOTE:     This is release 2.8.
+ *  NOTE:     This is release 2.9.
  *            It works with my SbPro & drive CR-521 V2.11 from 2/92
  *            and with the new CR-562-B V0.75 on a "naked" Panasonic
  *            CI-101P interface. And vice versa. 
@@ -146,6 +148,16 @@
  *       Changed default of the "JUKEBOX" define. If you use this default,
  *       your tray will eject if you try to mount without a disk in. Next
  *       mount command will insert the tray - so, just insert a disk. ;-)
+ *       
+ *  2.9  Fulfilled the Longshine LCS-7260 support; with great help and
+ *       experiments by Serge Robyns.
+ *       First attempts to support the TEAC CD-55A drives; but still not
+ *       useable yet.
+ *       Implemented the CDROMMULTISESSION and CDROMMULTISESSION_SYS ioctls;
+ *       this is an attempt to handle multi session CDs more "transparent"
+ *       (redirection handling has to be done within the isofs routines, and
+ *       only for the special purpose of obtaining the "right" volume
+ *       descriptor; accesses to the raw device should not get redirected).
  *
  *  TODO
  *
@@ -222,22 +234,16 @@
 
 #include "blk.h"
 
-#define VERSION "2.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
-
-#define SBPCD_DEBUG
-
-#ifndef CONFIG_ISO9660_FS
-#error "SBPCD: \"make config\" again. File system iso9660 is necessary."
-#endif
-
-/*
- * This may come back some day..
- */
-#define DDIOCSDBG	0x9000
+#define VERSION "2.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*
  * still testing around...
  */
+#define MULTISESSION_BY_DRIVER 0 /* needs "#define MULTISESSION" in
+                                  * linux/fs/isofs/inode.c
+                                  * if set to 0 here
+                                  */
+#define TEAC 0 /* if 1: enable TEAC CD-55A support (not useable yet) */
 #define JUKEBOX 1 /* tray control: eject tray if no disk is in */
 #define EJECT 1 /* tray control: eject tray after last use */
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
@@ -253,6 +259,13 @@
 #define SPEA_TEST 0
 #define PRINTK_BUG 0
 #define TEST_STI 0
+
+#if 0
+#define INLINE
+#else
+#define INLINE inline
+#endif
+
 /*==========================================================================*/
 /*
  * provisions for more than 1 driver issues
@@ -312,8 +325,10 @@
 	       Galaxy (default), Reveal (one default) */
   0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
   0x260, 1, /* OmniCD */
-  0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default) */
-  0x340, 0, /* Lasermate, CI-101P */
+  0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
+               Longshine LCS-6853 (default) */
+  0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
+  0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
   0x360, 0, /* Lasermate, CI-101P */
   0x270, 1, /* Soundblaster 16 */
   0x670, 0, /* "sound card #9" */
@@ -326,7 +341,9 @@
   0x630, 0, /* "sound card #9" (default) */
   0x650, 0, /* "sound card #9" */
 #if 0
-/* some "hazardous" locations (ethernet cards) */
+/* some "hazardous" locations
+ * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
+ */
   0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
   0x350, 0, /* Lasermate, CI-101P */
   0x370, 0, /* Lasermate, CI-101P */
@@ -396,23 +413,23 @@
  * (1<<DBG_SQ)   dump SubQ frame
  * (1<<DBG_AUD)  "read audio" debugging
  * (1<<DBG_SEQ)  Sequoia interface configuration trace
+ * (1<<DBG_LCS)  Longshine LCS-7260 debugging trace
+ * (1<<DBG_TEA)  TEAC CD-55A debugging trace
  * (1<<DBG_000)  unnecessary information
  */
 #if 1
-static int sbpcd_debug =  (1<<DBG_INF) | (1<<DBG_WRN);
-#else
-#if SPEA_TEST
 static int sbpcd_debug =  (1<<DBG_INF) |
-                          (1<<DBG_INI) |
-                          (1<<DBG_ID)  |
-                          (1<<DBG_SEQ);
+                          (1<<DBG_WRN) |
+                          (1<<DBG_MUL);
 #else
 static int sbpcd_debug =  (1<<DBG_INF) |
                           (1<<DBG_TOC) |
                           (1<<DBG_MUL) |
+                          (1<<DBG_LCS) |
+                          (1<<DBG_TEA) |
                           (1<<DBG_UPC);
 #endif
-#endif
+static unsigned char setup_done = 0;
 static int sbpcd_ioaddr = CDROM_PORT;	/* default I/O base address */
 static int sbpro_type = SBPRO;
 static int CDo_command, CDo_reset;
@@ -426,6 +443,8 @@
 static struct cdrom_subchnl SC;
 static struct cdrom_volctrl volctrl;
 static struct cdrom_read_audio read_audio;
+static struct cdrom_multisession ms_info;
+
 static char *str_sb = "SoundBlaster";
 static char *str_lm = "LaserMate";
 static char *str_sp = "SPEA";
@@ -457,7 +476,7 @@
 /*==========================================================================*/
 
 static u_char drive_family[]="CR-5"; /* Panasonic CR-56x */
-static u_char lcs_family[]="LCS-"; /* Longshine LCS-7260 */
+static u_char lcs_family[]="LCS-7260"; /* Longshine LCS-7260 */
 static u_char drive_vendor[]="MATSHITA"; /* Matsushita CR-52x */
 
 static u_int response_count=0;
@@ -500,7 +519,7 @@
 static struct {
   char drv_minor; /* minor number or -1 */
 
-  char drive_model[4];
+  char drive_model[9];
   char firmware_version[4];
   char f_eject; /* auto-eject flag: 0 or 1 */
   u_char *sbp_buf; /* Pointer to internal data buffer,
@@ -534,7 +553,7 @@
   u_char vol_ctrl0;
   char vol_chan1;
   u_char vol_ctrl1;
-#if 000
+#if 000 /* no supported drive has it */
   char vol_chan2;
   u_char vol_ctrl2;
   char vol_chan3;
@@ -586,11 +605,8 @@
 /*==========================================================================*/
 /*==========================================================================*/
 /*
- * DDI interface definitions
+ * DDI interface
  */
-#ifdef SBPCD_DEBUG
-# define DPRINTF(x)	sbpcd_dprintf x
-
 static void sbpcd_dprintf(int level, char *fmt, ...)
 {
   char buff[256];
@@ -608,13 +624,8 @@
 #endif
 }
 
-#else
-# define DPRINTF(x)	/* nothing */
-
-#endif SBPCD_DEBUG
-
 /*
- * maintain trace bit pattern
+ * DDI interface: runtime trace bit pattern maintainance
  */
 static int sbpcd_dbg_ioctl(unsigned long arg, int level)
 {
@@ -634,14 +645,13 @@
   return(0);
 }
 
-
 /*==========================================================================*/
 /*==========================================================================*/
 /*
  * Wait a little while (used for polling the drive).  If in initialization,
  * setting a timeout doesn't work, so just loop for a while.
  */
-static inline void sbp_sleep(u_int jifs)
+static INLINE void sbp_sleep(u_int jifs)
 {
    current->state = TASK_INTERRUPTIBLE;
    current->timeout = jiffies + jifs;
@@ -653,7 +663,7 @@
 /*
  *  convert logical_block_address to m-s-f_number (3 bytes only)
  */
-static void lba2msf(int lba, u_char *msf)
+static INLINE void lba2msf(int lba, u_char *msf)
 {
   lba += CD_BLOCK_OFFSET;
   msf[0] = lba / (CD_SECS*CD_FRAMES);
@@ -666,12 +676,12 @@
 /*
  *  convert msf-bin to msf-bcd
  */
-static void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
+static INLINE void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
 {
   *p=((*p/10)<<4)|(*p%10);
 }
 /*==========================================================================*/
-static u_int blk2msf(u_int blk)
+static INLINE u_int blk2msf(u_int blk)
 {
   MSF msf;
   u_int mm;
@@ -684,32 +694,32 @@
   return (msf.n);
 }
 /*==========================================================================*/
-static u_int make16(u_char rh, u_char rl)
+static INLINE u_int make16(u_char rh, u_char rl)
 {
   return ((rh<<8)|rl);
 }
 /*==========================================================================*/
-static u_int make32(u_int rh, u_int rl)
+static INLINE u_int make32(u_int rh, u_int rl)
 {
   return ((rh<<16)|rl);
 }
 /*==========================================================================*/
-static u_char swap_nibbles(u_char i)
+static INLINE u_char swap_nibbles(u_char i)
 {
   return ((i<<4)|(i>>4));
 }
 /*==========================================================================*/
-static u_char byt2bcd(u_char i)
+static INLINE u_char byt2bcd(u_char i)
 {
   return (((i/10)<<4)+i%10);
 }
 /*==========================================================================*/
-static u_char bcd2bin(u_char bcd)
+static INLINE u_char bcd2bin(u_char bcd)
 {
   return ((bcd>>4)*10+(bcd&0x0F));
 }
 /*==========================================================================*/
-static int msf2blk(int msfx)
+static INLINE int msf2blk(int msfx)
 {
   MSF msf;
   int i;
@@ -723,7 +733,7 @@
 /*
  *  convert m-s-f_number (3 bytes only) to logical_block_address 
  */
-static int msf2lba(u_char *msf)
+static INLINE int msf2lba(u_char *msf)
 {
   int i;
 
@@ -746,11 +756,13 @@
   if (sta==0x10) return (-11); /* read fault */
   if (sta>=0x16) return (-12); /* general failure */
   DriveStruct[d].CD_changed=0xFF;
-  if (sta==0x11) return (-15); /* invalid disk change */
+  if (sta==0x11) return (-15); /* invalid disk change (LCS: removed) */
+  if (lcs_drive)
+    if (sta==0x12) return (-15); /* invalid disk change (inserted) */
   return (-2); /* drive not ready */
 }
 /*==========================================================================*/
-static void clr_cmdbuf(void)
+static INLINE void clr_cmdbuf(void)
 {
   int i;
 
@@ -824,9 +836,41 @@
 	sbp_sleep(1);
 	i = 1;
       }
+  DPRINTF((DBG_LCS,"SBPCD: CDi_stat_loop failed\n"));
   return (-1);
 }
 /*==========================================================================*/
+#if TEAC
+/*==========================================================================*/
+static int tst_DataReady(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_not_data_ready) return (0);
+  return (1);
+}
+/*==========================================================================*/
+static int tst_ResultReady(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_not_result_ready) return (0);
+  return (1);
+}
+/*==========================================================================*/
+static int tst_Attention(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_attention) return (1);
+  return (0);
+}
+/*==========================================================================*/
+#endif TEAC
+/*==========================================================================*/
 static int ResponseInfo(void)
 {
   int i,j, st=0;
@@ -873,7 +917,7 @@
 /*==========================================================================*/
 static int EvaluateStatus(int st)
 {
-  if (!new_drive)
+  if (old_drive)
     {
       DriveStruct[d].status_byte=0;
       if (st&p_caddin_old) DriveStruct[d].status_byte |= p_door_closed|p_caddy_in;
@@ -882,9 +926,21 @@
       if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new;
       if (st&p_disk_ok) DriveStruct[d].status_byte |= p_disk_ok;
     }
-  else { DriveStruct[d].status_byte=st;
-	 st=p_success_old; /* for new drives: fake "successful" bit of old drives */
-       }
+  else if (lcs_drive)
+    {
+      DriveStruct[d].status_byte=0;
+      if (st&p_caddin_old) DriveStruct[d].status_byte |= p_disk_ok|p_caddy_in;
+      if (st&p_spinning) DriveStruct[d].status_byte |= p_spinning;
+      if (st&p_check) DriveStruct[d].status_byte |= p_check;
+      if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new;
+      if (st&p_lcs_door_closed) DriveStruct[d].status_byte |= p_door_closed;
+      if (st&p_lcs_door_locked) DriveStruct[d].status_byte |= p_door_locked;
+      }
+  else /* new drive */ 
+    {
+      DriveStruct[d].status_byte=st;
+      st=p_success_old; /* for new drives: fake "successful" bit of old drives */
+    }
   return (st);
 }
 /*==========================================================================*/
@@ -932,6 +988,7 @@
       return (-1);
     }
   i=inb(CDi_info);
+  DPRINTF((DBG_STA,"SBPCD: ResponseStatus: response %2X.\n", i));
   i=EvaluateStatus(i);
   return (i);
 }
@@ -941,15 +998,11 @@
   int i;
 
   DPRINTF((DBG_STA,"SBPCD: giving xx_ReadStatus command\n"));
-
-  if (!new_drive) OUT(CDo_command,0x81);
-  else
-    {
-      SBPCD_CLI;
-      OUT(CDo_command,0x05);
-      for (i=0;i<6;i++) OUT(CDo_command,0);
-      SBPCD_STI;
-    }
+  SBPCD_CLI;
+  if (new_drive) OUT(CDo_command,0x05);
+  else OUT(CDo_command,0x81);
+  if (!old_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
+  SBPCD_STI;
 }
 /*==========================================================================*/
 static int xx_ReadError(void)
@@ -968,14 +1021,17 @@
     {
       drvcmd[0]=0x82;
       response_count=6;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
+      if (lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
     }
   i=cmd_out();
   DriveStruct[d].error_byte=0;
   DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: cmd_out(82) returns %d (%02X)\n",i,i));
   if (i<0) return (i);
-  if (new_drive) i=2;
-  else i=1;
+  if (old_drive) i=1;
+  else i=2;
   DriveStruct[d].error_byte=infobuf[i];
   DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: infobuf[%d] is %d (%02X)\n",i,DriveStruct[d].error_byte,DriveStruct[d].error_byte));
   i=sta2err(infobuf[i]);
@@ -1052,14 +1108,14 @@
 
   clr_cmdbuf();
   if (f_blk_msf>1) return (-3);
-  if (!new_drive)
+  if (old_drive)
     {
       if (f_blk_msf==1) pos=msf2blk(pos);
       drvcmd[2]=(pos>>16)&0x00FF;
       drvcmd[3]=(pos>>8)&0x00FF;
       drvcmd[4]=pos&0x00FF;
       flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
-	               f_ResponseStatus | f_obey_p_check | f_bit1;
+                      f_ResponseStatus | f_obey_p_check | f_bit1;
     }
   else
     {
@@ -1067,7 +1123,10 @@
       drvcmd[1]=(pos>>16)&0x00FF;
       drvcmd[2]=(pos>>8)&0x00FF;
       drvcmd[3]=pos&0x00FF;
-      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+	flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+      else
+	flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
     }
   drvcmd[0]=0x01;
   response_count=0;
@@ -1082,21 +1141,17 @@
   DPRINTF((DBG_SPI,"SBPCD: SpinUp.\n"));
   DriveStruct[d].in_SpinUp = 1;
   clr_cmdbuf();
-  if (old_drive)
+  if (!new_drive)
     {
       drvcmd[0]=0x05;
-      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+	f_ResponseStatus|f_obey_p_check|f_bit1;
     }
-  else if (new_drive)
+  else /* new_drive */
     {
       drvcmd[0]=0x02;
       flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
     }
-  else /* lcs_drive */
-    {
-      drvcmd[0]=0x0D;
-      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-    }
   response_count=0;
   i=cmd_out();
   DriveStruct[d].in_SpinUp = 0;
@@ -1119,7 +1174,7 @@
     {
       drvcmd[0]=0x0D;
       drvcmd[1]=1;
-      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
       response_count=0;
     }
   i=cmd_out();
@@ -1206,7 +1261,7 @@
       drvcmd[1]=0x03;
       drvcmd[4]=control0;
       drvcmd[5]=value0;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
     }
   else /* old_drive, different firmware levels */
     {
@@ -1307,7 +1362,8 @@
       response_count=0;
       i=cmd_out();
     }
-  sbp_sleep(100); /* wait a second */
+  if (lcs_drive) sbp_sleep(500); /* wait 5 seconds */
+  else sbp_sleep(100); /* wait a second */
   flush_status();
   i=GetStatus();
   if (i>=0) return -1;
@@ -1367,7 +1423,12 @@
   else
     {
       drvcmd[0]=0x8D;
-      flags_cmd_out=f_putcmd|f_respo2|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+	flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+	  f_obey_p_check|f_bit1;
+      else
+	flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+	  f_obey_p_check;
     }
   if (pau_res!=1) drvcmd[1]=0x80;
   response_count=0;
@@ -1381,6 +1442,7 @@
 
   if (old_drive) return (0);
   DPRINTF((DBG_LCK,"SBPCD: yy_LockDoor: %d (drive %d)\n", lock, d));
+  DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d before\n", st_door_locked));
   clr_cmdbuf();
   if (new_drive)
     {
@@ -1393,10 +1455,11 @@
     {
       drvcmd[0]=0x0E;
       if (lock==1) drvcmd[1]=0x01;
-      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
       response_count=0;
     }
   i=cmd_out();
+  DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d after\n", st_door_locked));
   return (i);
 }
 /*==========================================================================*/
@@ -1404,14 +1467,25 @@
 {
   int i;
 
-  if (!new_drive) return (0);
+  if (old_drive) return (0);
   DPRINTF((DBG_LCK,"SBPCD: yy_CloseTray (drive %d)\n", d));
+  DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d before\n", st_door_closed));
 
   clr_cmdbuf();
-  drvcmd[0]=0x07;
-  flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+  if (new_drive)
+    {
+      drvcmd[0]=0x07;
+      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+    }
+  else /* lcs_drive */
+    {
+      drvcmd[0]=0x0D;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+ 	f_ResponseStatus|f_obey_p_check|f_bit1;
+    }
   response_count=0;
   i=cmd_out();
+  DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d after\n", st_door_closed));
   return (i);
 }
 /*==========================================================================*/
@@ -1433,7 +1507,10 @@
 	{
 	  drvcmd[0]=0x89;
 	  drvcmd[1]=0x02;
-	  flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ 	  if (lcs_drive)
+ 	    flags_cmd_out=f_putcmd;
+ 	  else
+ 	    flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
 	  response_count=13;
 	}
       i=cmd_out();
@@ -1483,7 +1560,10 @@
     {
       drvcmd[0]=0x85;
       drvcmd[1]=0x00;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
       response_count=2;
     }
   i=cmd_out();
@@ -1533,7 +1613,10 @@
       drvcmd[2]=(DriveStruct[d].frame_size>>8)&0xFF;
       drvcmd[3]=DriveStruct[d].frame_size&0xFF;
       drvcmd[4]=0x00;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+	flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=0;
   i=cmd_out();
@@ -1563,7 +1646,10 @@
       drvcmd[0]=0x85;
       drvcmd[1]=0x03;
       response_count=2;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   i=cmd_out();
   if (i<0) return (i);
@@ -1629,10 +1715,13 @@
   DriveStruct[d].vol_ctrl0=vol0;
   DriveStruct[d].vol_chan1=chan1;
   DriveStruct[d].vol_ctrl1=vol1;
-  DriveStruct[d].vol_chan2=2;
-  DriveStruct[d].vol_ctrl2=0xFF;
-  DriveStruct[d].vol_chan3=3;
-  DriveStruct[d].vol_ctrl3=0xFF;
+  if (!lcs_drive)
+    {
+      DriveStruct[d].vol_chan2=2;
+      DriveStruct[d].vol_ctrl2=0xFF;
+      DriveStruct[d].vol_chan3=3;
+      DriveStruct[d].vol_ctrl3=0xFF;
+    }
   DriveStruct[d].diskstate_flags |= volume_bit;
   return (0);
 }
@@ -1652,7 +1741,10 @@
   else
     {
       drvcmd[0]=0x88;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=5;
   i=cmd_out();
@@ -1679,7 +1771,10 @@
   else
     {
       drvcmd[0]=0x8B;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=6;
   i=cmd_out();
@@ -1709,7 +1804,10 @@
     {
       drvcmd[0]=0x8C;
       drvcmd[1]=0x02;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+	flags_cmd_out=f_putcmd;
+      else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   drvcmd[2]=num;
   response_count=8;
@@ -1737,7 +1835,10 @@
   clr_cmdbuf();
   drvcmd[0]=0x8E;
   drvcmd[1]=response_count;
-  flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+  if(lcs_drive)
+    flags_cmd_out=f_putcmd;
+  else
+    flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
   i=cmd_out();
   return (i);
 }
@@ -1801,6 +1902,7 @@
       if (!new_drive)
 	{
 	  response_count=16;
+ 	  if (lcs_drive) flags_cmd_out=f_putcmd;
 	  i=xx_ReadPacket();
 	  if (i<0) return (i);
 	}
@@ -1847,6 +1949,7 @@
 
   DriveStruct[d].f_multisession=0;
   clr_cmdbuf();
+  DriveStruct[d].lba_multi=0;
   if (new_drive)
     {
       drvcmd[0]=0x8D;
@@ -1856,17 +1959,18 @@
       if (i<0) return (i);
       if ((infobuf[0]&0x80)!=0)
 	{
-	  DPRINTF((DBG_MUL,"SBPCD: MultiSession CD detected: %02X %02X %02X %02X %02X %02X\n",
-                         infobuf[0], infobuf[1], infobuf[2],
-		         infobuf[3], infobuf[4], infobuf[5]));
 	  DriveStruct[d].f_multisession=1;
 	  DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
-                                   make16(infobuf[2],infobuf[3])));
-	  DriveStruct[d].last_redirect=20;
+						  make16(infobuf[2],infobuf[3])));
+	  DriveStruct[d].last_redirect=19;
 	  /* preliminary - has to get adjusted the following way:
            *   look at the first byte of frames 17 ff. until 0xFF is seen
            *   the frame before this one is the last continuation frame
            */
+	  DPRINTF((DBG_MUL,"SBPCD: MultiSession CD detected: %02X %02X %02X %02X %02X %02X (%d)\n",
+		   infobuf[0], infobuf[1], infobuf[2],
+		   infobuf[3], infobuf[4], infobuf[5],
+		   DriveStruct[d].lba_multi));
 	}
     }
   else if (lcs_drive)
@@ -1875,23 +1979,24 @@
       drvcmd[1]=3;
       drvcmd[2]=1;
       response_count=8;
-      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd;
       i=cmd_out();
       if (i<0) return (i);
-      DPRINTF((DBG_MUL,"SBPCD: MultiSession Info: %02X %02X %02X %02X %02X %02X %02X %02X\n",
-	       infobuf[0], infobuf[1], infobuf[2], infobuf[3],
-	       infobuf[4], infobuf[5], infobuf[6], infobuf[7]));
       DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
 					      make16(infobuf[6],infobuf[7])));
+      DPRINTF((DBG_MUL,"SBPCD: MultiSession Info: %02X %02X %02X %02X %02X %02X %02X %02X (%d)\n",
+	       infobuf[0], infobuf[1], infobuf[2], infobuf[3],
+	       infobuf[4], infobuf[5], infobuf[6], infobuf[7],
+	       DriveStruct[d].lba_multi));
       if (DriveStruct[d].lba_multi>200)
 	{
 	  DPRINTF((DBG_MUL,"SBPCD: MultiSession base: %06X\n", DriveStruct[d].lba_multi));
 	  DriveStruct[d].f_multisession=1;
-	  DriveStruct[d].last_redirect=20;
+	  DriveStruct[d].last_redirect=19;
 	  /* preliminary - has to get adjusted the following way:
-           *   look at the first byte of frames 17 ff. until 0xFF is seen;
-           *   the frame before this one is the last repetition frame.
-           */
+	   *   look at the first byte of frames 17 ff. until 0xFF is seen;
+	   *   the frame before this one is the last repetition frame.
+	   */
 	}
     }
   return (0);
@@ -1969,6 +2074,53 @@
 #endif CDMKE
 }
 /*==========================================================================*/
+#if TEAC
+/*==========================================================================*/
+static void teac_reset(int drv_id)
+{
+  int i;
+#define CMD_TEAC_RESET 0xc0
+
+  OUT(CDo_sel_d_i,0);
+  OUT(CDo_enable,drv_id);
+  OUT(CDo_command,CMD_TEAC_RESET);
+  for (i=0;i<9;i++) OUT(CDo_command,0);
+  DPRINTF((DBG_TEA,"SBPCD: TEAC soft reset.\n"));
+  sbp_sleep(100); /* wait a second */
+}
+/*==========================================================================*/
+static int look_for_drive(int drv_id)
+{
+  int i;
+  if (sbpro_type!=1) teac_reset(drv_id);
+
+  OUT(CDo_enable,drv_id);
+  OUT(CDo_sel_d_i,0);
+  i=inb(CDi_status);
+  if (i&s_not_result_ready) return (-1); /* drive not present */
+  i=inb(CDi_info);
+  DPRINTF((DBG_TEA,"SBPCD: TEAC look_for_drive: %02X.\n",i));
+  if (i!=0x55) return (-2); /* something else present */
+  return (1); /* drive found */
+}
+/*==========================================================================*/
+static int find_teac_drives(void)
+{
+  int i, j, found;
+
+  found=0;
+  for (i=0;i<4;i++)
+    {
+      j=look_for_drive(i);
+      if (j<1) continue;
+      found++;
+      DPRINTF((DBG_TEA,"SBPCD: TEAC drive (id=%d) found.\n",i));
+    }
+  return (found);
+}
+/*==========================================================================*/
+#endif TEAC
+/*==========================================================================*/
 static int check_version(void)
 {
   int i, j;
@@ -2003,6 +2155,7 @@
       DriveStruct[d].drive_model[1]=infobuf[i++];
       DriveStruct[d].drive_model[2]='-';
       DriveStruct[d].drive_model[3]='x';
+      DriveStruct[d].drive_model[4]=0;
       DriveStruct[d].drv_type=drv_new;
     }
   if (!DriveStruct[d].drv_type)
@@ -2014,18 +2167,18 @@
 	  DriveStruct[d].drive_model[1]='x';
 	  DriveStruct[d].drive_model[2]='-';
 	  DriveStruct[d].drive_model[3]='x';
+	  DriveStruct[d].drive_model[4]=0;
 	  DriveStruct[d].drv_type=drv_old;
 	}
     }
   if (!DriveStruct[d].drv_type)
     {
-      for (i=0;i<4;i++) if (infobuf[i]!=lcs_family[i]) break;
-      if (i==4)
+      for (i=0;i<8;i++) if (infobuf[i]!=lcs_family[i]) break;
+      if (i==8)
 	{
-	  DriveStruct[d].drive_model[0]=infobuf[i++];
-	  DriveStruct[d].drive_model[1]=infobuf[i++];
-	  DriveStruct[d].drive_model[2]=infobuf[i++];
-	  DriveStruct[d].drive_model[3]=infobuf[i++];
+	  for (j=0;j<8;j++)
+	    DriveStruct[d].drive_model[j]=infobuf[j];
+	  DriveStruct[d].drive_model[8]=0;
 	  DriveStruct[d].drv_type=drv_lcs;
 	}
     }
@@ -2034,10 +2187,18 @@
       DPRINTF((DBG_INI,"SBPCD: check_version: error.\n"));
       return (-1);
     }
-  if (lcs_drive) DriveStruct[d].drv_type=drv_260; /* still needs a closer look */
+  for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j];
+  if (lcs_drive)
+    {
+      DriveStruct[d].drv_type=drv_260;
+      if ((DriveStruct[d].firmware_version[0]!='A') ||
+	  (DriveStruct[d].firmware_version[1]!='4') ||
+	  (DriveStruct[d].firmware_version[2]!='F') ||
+	  (DriveStruct[d].firmware_version[3]!='4'))
+	printk("SBPCD: please mail me your LCS-7260 DOS driver.\n");
+    }
   else
     {
-      for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j];
       j = (DriveStruct[d].firmware_version[0] & 0x0F) * 100 +
 	(DriveStruct[d].firmware_version[2] & 0x0F) *10 +
 	  (DriveStruct[d].firmware_version[3] & 0x0F);
@@ -2053,6 +2214,7 @@
       else if (j<300) DriveStruct[d].drv_type=drv_211;
       else DriveStruct[d].drv_type=drv_300;
     }
+  DPRINTF((DBG_LCS,"SBPCD: drive type %02X\n",DriveStruct[d].drv_type));
   DPRINTF((DBG_INI,"SBPCD: check_version done.\n"));
   return (0);
 }
@@ -2094,11 +2256,21 @@
 	  DriveStruct[d].drv_options=drv_pattern[j];
 	  if (!new_drive)
 	    DriveStruct[d].drv_options&=~(speed_auto|speed_300|speed_150);
-	  printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
-		 DriveStruct[d].drv_minor,
-                 drive_family,
-                 DriveStruct[d].drive_model,
-                 DriveStruct[d].firmware_version);
+	  if (lcs_drive)
+	    {
+	      printk("%sDrive %d: %s (%.4s)\n", printk_header,
+		     DriveStruct[d].drv_minor,
+		     DriveStruct[d].drive_model,
+		     DriveStruct[d].firmware_version);
+	    }
+	  else
+	    {
+	      printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
+		     DriveStruct[d].drv_minor,
+		     drive_family,
+		     DriveStruct[d].drive_model,
+		     DriveStruct[d].firmware_version);
+	    }
 	  printk_header="       - ";
 	}
       else DriveStruct[d].drv_minor=-1;
@@ -2529,6 +2701,7 @@
 		       u_long arg)
 {
   int i, st;
+  unsigned int *p_lba;
 
   DPRINTF((DBG_IO2,"SBPCD: ioctl(%d, 0x%08lX, 0x%08lX)\n",
 				MINOR(inode->i_rdev), cmd, arg));
@@ -2568,12 +2741,14 @@
       switch (DriveStruct[d].audio_state)
 	{
 	case audio_playing:
-	  i=xx_Pause_Resume(1);
+	  if (lcs_drive) i=xx_ReadSubQ();
+	  else i=xx_Pause_Resume(1);
 	  if (i<0) return (-EIO);
-	  DriveStruct[d].audio_state=audio_pausing;
-	  i=xx_ReadSubQ();
+	  if (lcs_drive) i=xx_Pause_Resume(1);
+	  else i=xx_ReadSubQ();
 	  if (i<0) return (-EIO);
 	  DriveStruct[d].pos_audio_start=DriveStruct[d].SubQ_run_tot;
+	  DriveStruct[d].audio_state=audio_pausing;
 	  return (0);
 	case audio_pausing:
 	  i=xx_Seek(DriveStruct[d].pos_audio_start,1);
@@ -2589,7 +2764,10 @@
       /* been paused with a PAUSE command.                                */
       /* It will resume playing from the location saved in SubQ_run_tot.  */
       if (DriveStruct[d].audio_state!=audio_pausing) return -EINVAL;
-      i=xx_Pause_Resume(3);
+      if (lcs_drive)
+	i=xx_PlayAudio(DriveStruct[d].pos_audio_start,
+		       DriveStruct[d].pos_audio_end);
+      else i=xx_Pause_Resume(3);
       if (i<0) return (-EIO);
       DriveStruct[d].audio_state=audio_playing;
       return (0);
@@ -2716,7 +2894,7 @@
       
     case CDROMEJECT_SW:
       DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT_SW entered.\n"));
-      if (!new_drive) return (0);
+      if (old_drive) return (0);
       DriveStruct[d].f_eject=arg;
       return (0);
       
@@ -2987,6 +3165,37 @@
 	return (0);
       } /* end of CDROMREADAUDIO */
 
+    case CDROMMULTISESSION: /* tell start-of-last-session to user */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION entered.\n"));
+      st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
+      if (st) return (st);
+      memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+      if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
+	lba2msf(DriveStruct[d].lba_multi,&ms_info.addr.msf.minute);
+      else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+	ms_info.addr.lba=DriveStruct[d].lba_multi;
+      else return (-EINVAL);
+      if (DriveStruct[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
+      else ms_info.xa_flag=0; /* invalid redirection address */
+      st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
+      if (st) return (st);
+      memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+      DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION done (%d, %08X).\n",
+	       ms_info.xa_flag, ms_info.addr.lba));
+      return (0);
+
+    case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION_SYS entered.\n"));
+      if(!suser()) return -EACCES;
+      p_lba=arg;
+      if (DriveStruct[d].f_multisession)
+	*p_lba=DriveStruct[d].lba_multi;
+      else
+	*p_lba=0;
+      DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION_SYS done (%d).\n",
+	       p_lba[0]));
+      return (0);
+
     case BLKRASET:
       if(!suser())  return -EACCES;
       if(!inode->i_rdev) return -EINVAL;
@@ -3129,7 +3338,8 @@
   DriveStruct[d].sbp_first_frame=DriveStruct[d].sbp_last_frame=-1;      /* purge buffer */
   block=CURRENT->sector/4;
 
-  if (new_drive)
+#if MULTISESSION_BY_DRIVER
+  if (!old_drive)
     {
 #if MANY_SESSION
       DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block)));
@@ -3151,6 +3361,7 @@
 	  }
 #endif MANY_SESSION
     }
+#endif MULTISESSION_BY_DRIVER
 
   if (block+SBP_BUFFER_FRAMES <= DriveStruct[d].CDsize_frm)
     DriveStruct[d].sbp_read_frames = SBP_BUFFER_FRAMES;
@@ -3247,7 +3458,7 @@
 #if LONG_TIMING
       for (timeout=jiffies+900; ; )
 #else
-      for (timeout=jiffies+100; ; )
+      for (timeout=jiffies+ ( (lcs_drive)? 300:100 ); ; )
 #endif
 	{
 	  for ( ; try!=0;try--)
@@ -3389,12 +3600,20 @@
   flags_cmd_out |= f_respo2;
   xx_ReadStatus();                         /* command: give 1-byte status */
   i=ResponseStatus();
-  if (!st_door_closed) yy_CloseTray();
-  if (!st_spinning) xx_SpinUp();
-
-  flags_cmd_out |= f_respo2;
-  xx_ReadStatus();                         /* command: give 1-byte status */
-  i=ResponseStatus();
+  if (!st_door_closed)
+    {
+      yy_CloseTray();
+      flags_cmd_out |= f_respo2;
+      xx_ReadStatus();
+      i=ResponseStatus();
+    }
+  if (!st_spinning)
+    {
+      xx_SpinUp();
+      flags_cmd_out |= f_respo2;
+      xx_ReadStatus();
+      i=ResponseStatus();
+    }
   if (i<0)
     {
       DPRINTF((DBG_INF,"SBPCD: sbpcd_open: xx_ReadStatus timed out\n"));
@@ -3408,7 +3627,7 @@
       do
 	i=yy_LockDoor(0);
       while (i!=0);
-      if (new_drive) yy_SpinDown(); /* eject tray */
+      if (!old_drive) yy_SpinDown(); /* eject tray */
 #endif
       return (-ENXIO);
     }
@@ -3513,6 +3732,7 @@
 #endif
 void sbpcd_setup(char *s, int *p)
 {
+  setup_done++;
   DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s));
   sbpro_type=0;
   if (!strcmp(s,str_sb)) sbpro_type=1;
@@ -3522,13 +3742,13 @@
   CDo_command=sbpcd_ioaddr;
   CDi_info=sbpcd_ioaddr;
   CDi_status=sbpcd_ioaddr+1;
+  CDo_sel_d_i=sbpcd_ioaddr+1;
   CDo_reset=sbpcd_ioaddr+2;
   CDo_enable=sbpcd_ioaddr+3; 
   if (sbpro_type==1)
     {
       MIXER_addr=sbpcd_ioaddr-0x10+0x04;
       MIXER_data=sbpcd_ioaddr-0x10+0x05;
-      CDo_sel_d_i=sbpcd_ioaddr+1;
       CDi_data=sbpcd_ioaddr;
     }
   else CDi_data=sbpcd_ioaddr+2;
@@ -3600,23 +3820,25 @@
 
   DPRINTF((DBG_INF,"SBPCD version %s\n", VERSION));
 
-  DPRINTF((DBG_INF,"SBPCD: Looking for a SoundBlaster/Matsushita CD-ROM drive\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = W A R N I N G = = = = = = = = = =\n"));
-  DPRINTF((DBG_WRN,"SBPCD: Auto-Probing can cause a hang (f.e. touching an ethernet card).\n"));
-  DPRINTF((DBG_WRN,"SBPCD: If that happens, you have to reboot and use the\n"));
-  DPRINTF((DBG_WRN,"SBPCD: LILO (kernel) command line feature like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x230,SoundBlaster\n"));
-  DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x300,LaserMate\n"));
-  DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x330,SPEA\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
-  DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-
+  if (!setup_done)
+    {
+      DPRINTF((DBG_INF,"SBPCD: Looking for Matsushita, Panasonic, CreativeLabs, IBM, Longshine CD-ROM drives\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = W A R N I N G = = = = = = = = = =\n"));
+      DPRINTF((DBG_WRN,"SBPCD: Auto-Probing can cause a hang (f.e. touching an ethernet card).\n"));
+      DPRINTF((DBG_WRN,"SBPCD: If that happens, you have to reboot and use the\n"));
+      DPRINTF((DBG_WRN,"SBPCD: LILO (kernel) command line feature like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x230,SoundBlaster\n"));
+      DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x300,LaserMate\n"));
+      DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x330,SPEA\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
+      DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+    }
   autoprobe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
   autoprobe[1]=sbpro_type; /* possibly changed by kernel command line */
 
@@ -3642,6 +3864,14 @@
 	      continue;
 	    }
 	}
+#if TEAC
+      i=find_teac_drives();
+      if (i>0)
+	{
+	  DPRINTF((DBG_INF,"SBPCD: found %d TEAC drives. A wonder.\n",i));
+	  DPRINTF((DBG_INF,"SBPCD: - "));
+	}
+#endif TEAC
       i=check_drives();
       DPRINTF((DBG_INI,"SBPCD: check_drives done.\n"));
       if (i>=0) break; /* drive found */
@@ -3673,14 +3903,17 @@
   check_datarate();
   DPRINTF((DBG_INI,"SBPCD: check_datarate done.\n"));
 
-  OUT(CDo_reset,0);
-  sbp_sleep(100);
+   if (!lcs_drive)
+   {
+     OUT(CDo_reset,0);
+     sbp_sleep(100);
+   }
 
   for (j=0;j<NR_SBPCD;j++)
     {
       if (DriveStruct[j].drv_minor==-1) continue;
       switch_drive(j);
-      xy_DriveReset();
+      if (!lcs_drive) xy_DriveReset();
       if (!st_spinning) xx_SpinUp();
       DriveStruct[d].sbp_first_frame = -1;  /* First frame in buffer */
       DriveStruct[d].sbp_last_frame = -1;   /* Last frame in buffer  */
@@ -3689,7 +3922,7 @@
       DriveStruct[d].CD_changed=1;
       DriveStruct[d].frame_size=CD_FRAMESIZE;
 #if EJECT
-      if (new_drive) DriveStruct[d].f_eject=1;
+      if (!old_drive) DriveStruct[d].f_eject=1;
       else DriveStruct[d].f_eject=0;
 #else
       DriveStruct[d].f_eject=0;
@@ -3708,10 +3941,15 @@
 	    }
 	}
       DPRINTF((DBG_INI,"SBPCD: init: first GetStatus: %d\n",i));
+      DPRINTF((DBG_LCS,"SBPCD: init: first GetStatus: error_byte=%d\n",
+	       DriveStruct[d].error_byte));
       if (DriveStruct[d].error_byte==aud_12)
 	{
 	  do { i=GetStatus();
 	       DPRINTF((DBG_INI,"SBPCD: init: second GetStatus: %02X\n",i));
+	       DPRINTF((DBG_LCS,
+			"SBPCD: init: second GetStatus: error_byte=%d\n",
+			DriveStruct[d].error_byte));
 	       if (i<0) break;
 	       if (!st_caddy_in) break;
 	     }
diff -u --recursive --new-file v1.1.73/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.1.73/linux/drivers/char/Makefile	Thu Oct 13 14:07:03 1994
+++ linux/drivers/char/Makefile	Mon Dec 19 16:35:51 1994
@@ -25,6 +25,11 @@
 	defkeymap.c uni_to_437.c
 
 
+ifdef CONFIG_CYCLADES
+OBJS := $(OBJS) cyclades.o
+SRCS := $(SRCS) cyclades.c
+endif
+
 ifdef CONFIG_ATIXL_BUSMOUSE
 M = y
 OBJS := $(OBJS) atixlmouse.o
diff -u --recursive --new-file v1.1.73/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v1.1.73/linux/drivers/char/cyclades.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/cyclades.c	Mon Dec 19 16:31:14 1994
@@ -0,0 +1,2696 @@
+static char rcsid[] =
+"$Revision: 1.35 $$Date: 1994/12/16 13:54:18 $";
+/*
+ *  linux/kernel/cyclades.c
+ *
+ * Much of the design and some of the code came from serial.c
+ * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
+ * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
+ * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ *
+ * This version does not support shared irq's.
+ *
+ * This module exports the following rs232 io functions:
+ *   long cy_init(long);
+ *   int  cy_open(struct tty_struct *tty, struct file *filp);
+ *
+ * $Log: cyclades.c,v $
+ * Revision 1.35  1994/12/16  13:54:18  steffen
+ * addditional patch by Marcio Saito for board detection
+ * Accidently left out in 1.34
+ *
+ * Revision 1.34  1994/12/10  12:37:12  steffen
+ * This is the corrected version as suggested by Marcio Saito
+ *
+ * Revision 1.33  1994/12/01  22:41:18  bentson
+ * add hooks to support more high speeds directly; add tytso
+ * patch regarding CLOCAL wakeups
+ *
+ * Revision 1.32  1994/11/23  19:50:04  bentson
+ * allow direct kernel control of higher signalling rates;
+ * look for cards at additional locations
+ *
+ * Revision 1.31  1994/11/16  04:33:28  bentson
+ * ANOTHER fix from Corey Minyard, minyard@wf-rch.cirr.com--
+ * a problem in chars_in_buffer has been resolved by some
+ * small changes;  this should yield smoother output
+ *
+ * Revision 1.30  1994/11/16  04:28:05  bentson
+ * Fix from Corey Minyard, Internet: minyard@metronet.com,
+ * UUCP: minyard@wf-rch.cirr.com, WORK: minyardbnr.ca, to
+ * cy_hangup that appears to clear up much (all?) of the
+ * DTR glitches; also he's added/cleaned-up diagnostic messages
+ *
+ * Revision 1.29  1994/11/16  04:16:07  bentson
+ * add change proposed by Ralph Sims, ralphs@halcyon.com, to
+ * operate higher speeds in same way as other serial ports;
+ * add more serial ports (for up to two 16-port muxes).
+ *
+ * Revision 1.28  1994/11/04  00:13:16  root
+ * turn off diagnostic messages
+ *
+ * Revision 1.27  1994/11/03  23:46:37  root
+ * bunch of changes to bring driver into greater conformance
+ * with the serial.c driver (looking for missed fixes)
+ *
+ * Revision 1.26  1994/11/03  22:40:36  root
+ * automatic interrupt probing fixed.
+ *
+ * Revision 1.25  1994/11/03  20:17:02  root
+ * start to implement auto-irq
+ *
+ * Revision 1.24  1994/11/03  18:01:55  root
+ * still working on modem signals--trying not to drop DTR
+ * during the getty/login processes
+ *
+ * Revision 1.23  1994/11/03  17:51:36  root
+ * extend baud rate support; set receive threshold as function
+ * of baud rate; fix some problems with RTS/CTS;
+ *
+ * Revision 1.22  1994/11/02  18:05:35  root
+ * changed arguments to udelay to type long to get
+ * delays to be of correct duration
+ *
+ * Revision 1.21  1994/11/02  17:37:30  root
+ * employ udelay (after calibrating loops_per_second earlier
+ * in init/main.c) instead of using home-grown delay routines
+ *
+ * Revision 1.20  1994/11/02  03:11:38  root
+ * cy_chars_in_buffer forces a return value of 0 to let
+ * login work (don't know why it does); some functions
+ * that were returning EFAULT, now executes the code;
+ * more work on deciding when to disable xmit interrupts;
+ *
+ * Revision 1.19  1994/11/01  20:10:14  root
+ * define routine to start transmission interrupts (by enabling
+ * transmit interrupts); directly enable/disable modem interrupts;
+ *
+ * Revision 1.18  1994/11/01  18:40:45  bentson
+ * Don't always enable transmit interrupts in startup; interrupt on
+ * TxMpty instead of TxRdy to help characters get out before shutdown;
+ * restructure xmit interrupt to check for chars first and quit if
+ * none are ready to go; modem status (MXVRx) is upright, _not_ inverted
+ * (to my view);
+ *
+ * Revision 1.17  1994/10/30  04:39:45  bentson
+ * rename serial_driver and callout_driver to cy_serial_driver and
+ * cy_callout_driver to avoid linkage interference; initialize
+ * info->type to PORT_CIRRUS; ruggedize paranoia test; elide ->port
+ * from cyclades_port structure; add paranoia check to cy_close;
+ *
+ * Revision 1.16  1994/10/30  01:14:33  bentson
+ * change major numbers; add some _early_ return statements;
+ *
+ * Revision 1.15  1994/10/29  06:43:15  bentson
+ * final tidying up for clean compile;  enable some error reporting
+ *
+ * Revision 1.14  1994/10/28  20:30:22  Bentson
+ * lots of changes to drag the driver towards the new tty_io
+ * structures and operation.  not expected to work, but may
+ * compile cleanly.
+ *
+ * Revision 1.13  1994/07/21  23:08:57  Bentson
+ * add some diagnostic cruft; support 24 lines (for testing
+ * both -8Y and -16Y cards; be more thorough in servicing all
+ * chips during interrupt; add "volatile" a few places to
+ * circumvent compiler optimizations; fix base & offset
+ * computations in block_til_ready (was causing chip 0 to
+ * stop operation)
+ *
+ * Revision 1.12  1994/07/19  16:42:11  Bentson
+ * add some hackery for kernel version 1.1.8; expand
+ * error messages; refine timing for delay loops and
+ * declare loop params volatile
+ *
+ * Revision 1.11  1994/06/11  21:53:10  bentson
+ * get use of save_car right in transmit interrupt service
+ *
+ * Revision 1.10.1.1  1994/06/11  21:31:18  bentson
+ * add some diagnostic printing; try to fix save_car stuff
+ *
+ * Revision 1.10  1994/06/11  20:36:08  bentson
+ * clean up compiler warnings
+ *
+ * Revision 1.9  1994/06/11  19:42:46  bentson
+ * added a bunch of code to support modem signalling
+ *
+ * Revision 1.8  1994/06/11  17:57:07  bentson
+ * recognize break & parity error
+ *
+ * Revision 1.7  1994/06/05  05:51:34  bentson
+ * Reorder baud table to be monotonic; add cli to CP; discard
+ * incoming characters and status if the line isn't open; start to
+ * fold code into cy_throttle; start to port get_serial_info,
+ * set_serial_info, get_modem_info, set_modem_info, and send_break
+ * from serial.c; expand cy_ioctl; relocate and expand config_setup;
+ * get flow control characters from tty struct; invalidate ports w/o
+ * hardware;
+ *
+ * Revision 1.6  1994/05/31  18:42:21  bentson
+ * add a loop-breaker in the interrupt service routine;
+ * note when port is initialized so that it can be shut
+ * down under the right conditions; receive works without
+ * any obvious errors
+ *
+ * Revision 1.5  1994/05/30  00:55:02  bentson
+ * transmit works without obvious errors
+ *
+ * Revision 1.4  1994/05/27  18:46:27  bentson
+ * incorporated more code from lib_y.c; can now print short
+ * strings under interrupt control to port zero; seems to
+ * select ports/channels/lines correctly
+ *
+ * Revision 1.3  1994/05/25  22:12:44  bentson
+ * shifting from multi-port on a card to proper multiplexor
+ * data structures;  added skeletons of most routines
+ *
+ * Revision 1.2  1994/05/19  13:21:43  bentson
+ * start to crib from other sources
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/cyclades.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#define small_delay(x) for(j=0;j<x;j++)k++;
+
+
+#define SERIAL_PARANOIA_CHECK
+#undef  SERIAL_DEBUG_OPEN
+#undef  SERIAL_DEBUG_THROTTLE
+#undef  SERIAL_DEBUG_OTHER
+#undef  SERIAL_DEBUG_IO
+#undef  SERIAL_DEBUG_COUNT
+#undef  SERIAL_DEBUG_DTR
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#define WAKEUP_CHARS 256
+
+#define STD_COM_FLAGS (0)
+
+#define SERIAL_TYPE_NORMAL  1
+#define SERIAL_TYPE_CALLOUT 2
+
+
+DECLARE_TASK_QUEUE(tq_cyclades);
+
+struct tty_driver cy_serial_driver, cy_callout_driver;
+
+static volatile int cy_irq_triggered;
+static volatile int cy_triggered;
+static int cy_wild_int_mask;
+static unsigned char *intr_base_addr;
+
+/* This is the per-card data structure */
+struct cyclades_card cy_card[] = {
+ /* BASE_ADDR */
+    {0xD0000,0},
+    {0xD2000,0},
+    {0xD4000,10},
+    {0xD6000,11},
+    {0xD8000,12},
+    {0xDA000,15}
+};
+
+#define NR_CARDS        (sizeof(cy_card)/sizeof(struct cyclades_card))
+
+/*  No need has yet been found for this per-chip data structure
+ *  struct cyclades_chip cy_chip[] = {
+ *	    {0}
+ *  };
+ *  #define NR_CHIPS  (sizeof(cy_chip)/sizeof(struct cyclades_chip))
+ */
+
+/* This is the per-port data structure */
+struct cyclades_port cy_port[] = {
+      /* CARD#  */
+        {-1 },      /* ttyC0 */
+        {-1 },      /* ttyC1 */
+        {-1 },      /* ttyC2 */
+        {-1 },      /* ttyC3 */
+        {-1 },      /* ttyC4 */
+        {-1 },      /* ttyC5 */
+        {-1 },      /* ttyC6 */
+        {-1 },      /* ttyC7 */
+        {-1 },      /* ttyC8 */
+        {-1 },      /* ttyC9 */
+        {-1 },      /* ttyC10 */
+        {-1 },      /* ttyC11 */
+        {-1 },      /* ttyC12 */
+        {-1 },      /* ttyC13 */
+        {-1 },      /* ttyC14 */
+        {-1 },      /* ttyC15 */
+        {-1 },      /* ttyC16 */
+        {-1 },      /* ttyC17 */
+        {-1 },      /* ttyC18 */
+        {-1 },      /* ttyC19 */
+        {-1 },      /* ttyC20 */
+        {-1 },      /* ttyC21 */
+        {-1 },      /* ttyC22 */
+        {-1 },      /* ttyC23 */
+        {-1 },      /* ttyC24 */
+        {-1 },      /* ttyC25 */
+        {-1 },      /* ttyC26 */
+        {-1 },      /* ttyC27 */
+        {-1 },      /* ttyC28 */
+        {-1 },      /* ttyC29 */
+        {-1 },      /* ttyC30 */
+        {-1 }       /* ttyC31 */
+};
+#define NR_PORTS        (sizeof(cy_port)/sizeof(struct cyclades_port))
+
+static int serial_refcount;
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+
+/* This is the per-irq data structure,
+               it maps an irq to the corresponding card */
+struct cyclades_card * IRQ_cards[16];
+
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+/*
+ * This is used to look up the divsor speeds and the timeouts
+ * We're normally limited to 15 distinct baud rates.  The extra
+ * are accessed via settings in info->flags.
+ *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                                  HI            VHI
+ */
+static int baud_table[] = {
+           0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
+        1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
+        0};
+
+static char baud_co[] = {  /* 25 MHz clock option table */
+        /* value =>    00    01   02    03    04 */
+        /* divide by    8    32   128   512  2048 */
+        0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
+        0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
+
+static char baud_bpr[] = {  /* 25 MHz baud rate period table */
+        0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
+        0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
+
+static char baud_cor3[] = {  /* receive threashold */
+        0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+        0x0a,  0x0a,  0x0a,  0x08,  0x04,  0x02,  0x01,  0x01,  0x01,  0x01};
+
+
+
+static void shutdown(struct cyclades_port *);
+static int startup (struct cyclades_port *);
+static void cy_throttle(struct tty_struct *);
+static void cy_unthrottle(struct tty_struct *);
+static void config_setup(struct cyclades_port *);
+extern void console_print(const char *);
+static void show_status(int);
+
+
+
+static inline int
+serial_paranoia_check(struct cyclades_port *info,
+			dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+    static const char *badmagic =
+	"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+    static const char *badinfo =
+	"Warning: null cyclades_port for (%d, %d) in %s\n";
+    static const char *badrange =
+	"Warning: cyclades_port out of range for (%d, %d) in %s\n";
+
+    if (!info) {
+	printk(badinfo, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+
+    if( (long)info < (long)(&cy_port[0])
+    || (long)(&cy_port[NR_PORTS]) < (long)info ){
+	printk(badrange, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+
+    if (info->magic != CYCLADES_MAGIC) {
+	printk(badmagic, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+#endif
+	return 0;
+} /* serial_paranoia_check */
+
+/* The following diagnostic routines allow the driver to spew
+   information on the screen, even (especially!) during interrupts.
+ */
+void
+SP(char *data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        console_print(data);
+    restore_flags(flags);
+}
+char scrn[2];
+void
+CP(char data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        scrn[0] = data;
+        console_print(scrn);
+    restore_flags(flags);
+}/* CP */
+
+void CP1(int data) { (data<10)?  CP(data+'0'): CP(data+'A'-10); }/* CP1 */
+void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */
+void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */
+void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
+
+
+/* This routine waits up to 100 micro-seconds for the previous
+   command to the Cirrus chip to complete and then issues the
+   new command.  An error is returned if the previous command
+   didn't finish within the time limit.
+ */
+u_short
+write_cy_cmd(u_char *base_addr, u_char cmd)
+{
+  unsigned long flags;
+  volatile int  i;
+
+    save_flags(flags); cli();
+	/* Check to see that the previous command has completed */
+	for(i = 0 ; i < 10000 ; i++){
+	    if (base_addr[CyCCR] == 0){
+		break;
+	    }
+	    udelay(10L);
+	}
+	/* if the CCR never cleared, the previous command
+	    didn't finish within the "reasonable time" */
+	if ( i == 10 ) {
+	    restore_flags(flags);
+	    return (-1);
+	}
+
+	/* Issue the new command */
+	base_addr[CyCCR] = cmd;
+    restore_flags(flags);
+    return(0);
+} /* write_cy_cmd */
+
+
+/* cy_start and cy_stop provide software output flow control as a
+   function of XON/XOFF, software CTS, and other such stuff. */
+
+static void
+cy_stop(struct tty_struct *tty)
+{
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_stop ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_stop"))
+	return;
+	
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[info->card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel & 0x0003); /* index channel */
+        base_addr[CySRER] &= ~CyTxMpty;
+    restore_flags(flags);
+
+    return;
+} /* cy_stop */
+
+static void
+cy_start(struct tty_struct *tty)
+{
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_start ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_start"))
+	return;
+	
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[info->card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel & 0x0003);
+        base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+
+    return;
+} /* cy_start */
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver
+ * (also known as the "bottom half").  This can be called any
+ * number of times for any channel without harm.
+ */
+static inline void
+cy_sched_event(struct cyclades_port *info, int event)
+{
+    info->event |= 1 << event; /* remember what kind of event and who */
+    queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */
+    mark_bh(CYCLADES_BH);                       /* then trigger event */
+} /* cy_sched_event */
+
+
+
+/*
+ * This interrupt routine is used
+ * while we are probing for submarines.
+ */
+static void
+cy_probe(int irq)
+{
+    cy_irq_triggered = irq;
+    cy_triggered |= 1 << irq;
+    return;
+} /* cy_probe */
+
+/* The real interrupt service routine is called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static void
+cy_interrupt(int irq)
+{
+  struct tty_struct *tty;
+  int status;
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  volatile unsigned char *base_addr, *card_base_addr;
+  int chip;
+  int save_xir, channel, save_car;
+  char data;
+  volatile char vdata;
+  int char_count;
+  int outch;
+  int i,j;
+  int too_many;
+  int had_work;
+  int mdm_change;
+  int mdm_status;
+
+    if((cinfo = IRQ_cards[irq]) == 0){
+        return; /* spurious interrupt */
+    }
+
+    /* This loop checks all chips in the card.  Make a note whenever
+       _any_ chip had some work to do, as this is considered an
+       indication that there will be more to do.  Only when no chip
+       has any work does this outermost loop exit.
+     */
+    do{
+        had_work = 0;
+        for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
+	    base_addr = (unsigned char *)(cinfo->base_addr
+                                             + CyRegSize * chip);
+            too_many = 0;
+            while ( (status = base_addr[CySVRR]) != 0x00) {
+                had_work++;
+                /* The purpose of the following test is to ensure that
+                   no chip can monopolize the driver.  This forces the
+                   chips to be checked in a round-robin fashion (after
+                   draining each of a bunch (1000) of characters).
+		 */
+                if(1000<too_many++){
+                    break;
+                }
+                if (status & CySRReceive) {      /* reception interrupt */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyRIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    i = channel + chip * 4 + cinfo->first_line;
+                    info = &cy_port[i];
+                    info->last_active = jiffies;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    /* if there is nowhere to put the data, discard it */
+                    if(info->tty == 0){
+                        j = (base_addr[CyRIVR] & CyIVRMask);
+                        if ( j == CyIVRRxEx ) { /* exception */
+                            data = base_addr[CyRDSR];
+                        } else { /* normal character reception */
+                            char_count = base_addr[CyRDCR];
+                            while(char_count--){
+                                data = base_addr[CyRDSR];
+                            }
+                        }
+                    }else{ /* there is an open port for this data */
+                        tty = info->tty;
+                        j = (base_addr[CyRIVR] & CyIVRMask);
+                        if ( j == CyIVRRxEx ) { /* exception */
+                            data = base_addr[CyRDSR];
+                            if(data & info->ignore_status_mask){
+                                continue;
+                            }
+                            if (tty->flip.count < TTY_FLIPBUF_SIZE){
+				tty->flip.count++;
+				if (data & info->read_status_mask){
+				    if(data & CyBREAK){
+					*tty->flip.flag_buf_ptr++ =
+								TTY_BREAK;
+					*tty->flip.char_buf_ptr++ =
+							base_addr[CyRDSR];
+					if (info->flags & ASYNC_SAK){
+					    do_SAK(tty);
+					}
+				    }else if(data & CyFRAME){
+					*tty->flip.flag_buf_ptr++ =
+								TTY_FRAME;
+					*tty->flip.char_buf_ptr++ =
+							base_addr[CyRDSR];
+				    }else if(data & CyPARITY){
+					*tty->flip.flag_buf_ptr++ =
+								TTY_PARITY;
+					*tty->flip.char_buf_ptr++ =
+							base_addr[CyRDSR];
+				    }else if(data & CyOVERRUN){
+					*tty->flip.flag_buf_ptr++ =
+								TTY_OVERRUN;
+					*tty->flip.char_buf_ptr++ = 0;
+					/* If the flip buffer itself is
+					   overflowing, we still loose
+					   the next incoming character.
+					 */
+					if(tty->flip.count < TTY_FLIPBUF_SIZE){
+					    tty->flip.count++;
+					    *tty->flip.flag_buf_ptr++ =
+					                         TTY_NORMAL;
+					    *tty->flip.char_buf_ptr++ =
+							base_addr[CyRDSR];
+					}
+				    /* These two conditions may imply */
+				    /* a normal read should be done. */
+				    /* }else if(data & CyTIMEOUT){ */
+				    /* }else if(data & CySPECHAR){ */
+				    }else{
+					*tty->flip.flag_buf_ptr++ = 0;
+					*tty->flip.char_buf_ptr++ = 0;
+				    }
+				}else{
+				    *tty->flip.flag_buf_ptr++ = 0;
+				    *tty->flip.char_buf_ptr++ = 0;
+				}
+                            }else{
+                                /* there was a software buffer overrun
+				    and nothing could be done about it!!! */
+                            }
+                        } else { /* normal character reception */
+                            /* load # characters available from the chip */
+                            char_count = base_addr[CyRDCR];
+
+                            while(char_count--){
+				if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+                                        break;
+                                }
+				tty->flip.count++;
+                                data = base_addr[CyRDSR];
+				*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+				*tty->flip.char_buf_ptr++ = data;
+                            }
+                        }
+                        queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                    }
+                    /* end of service */
+                    base_addr[CyRIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = (save_car);
+                }
+
+
+                if (status & CySRTransmit) {     /* transmission interrupt */
+                    /* Since we only get here when the transmit buffer is empty,
+                        we know we can always stuff a dozen characters. */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyTIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    i = channel + chip * 4 + cinfo->first_line;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    /* validate the port number (as configured and open) */
+                    if( (i < 0) || (NR_PORTS <= i) ){
+                        base_addr[CySRER] &= ~CyTxMpty;
+                        goto txend;
+                    }
+                    info = &cy_port[i];
+                    info->last_active = jiffies;
+                    if(info->tty == 0){
+                        base_addr[CySRER] &= ~CyTxMpty;
+                        goto txdone;
+                    }
+
+                    /* load the on-chip space available for outbound data */
+                    char_count = info->xmit_fifo_size;
+
+
+                    if(info->x_char) { /* send special char */
+                        outch = info->x_char;
+                        base_addr[CyTDR] = outch;
+                        char_count--;
+                        info->x_char = 0;
+                    }
+
+		    if (info->x_break){
+			/*  The Cirrus chip requires the "Embedded Transmit
+			    Commands" of start break, delay, and end break
+			    sequences to be sent.  The duration of the
+			    break is given in TICs, which runs at HZ
+			    (typically 100) and the PPR runs at 200 Hz,
+			    so the delay is duration * 200/HZ, and thus a
+			    break can run from 1/100 sec to about 5/4 sec.
+			 */
+			base_addr[CyTDR] = 0; /* start break */
+			base_addr[CyTDR] = 0x81;
+			base_addr[CyTDR] = 0; /* delay a bit */
+			base_addr[CyTDR] = 0x82;
+			base_addr[CyTDR] = info->x_break*200/HZ;
+			base_addr[CyTDR] = 0; /* terminate break */
+			base_addr[CyTDR] = 0x83;
+			char_count -= 7;
+			info->x_break = 0;
+		    }
+
+                    while (char_count-- > 0){
+                        if (!info->xmit_cnt){
+			    base_addr[CySRER] &= ~CyTxMpty;
+			    goto txdone;
+                        }
+			if (info->tty->stopped || info->tty->hw_stopped){
+			    base_addr[CySRER] &= ~CyTxMpty;
+			    goto txdone;
+			}
+                        /* Because the Embedded Transmit Commands have been
+                           enabled, we must check to see if the escape
+                           character, NULL, is being sent.  If it is, we
+                           must ensure that there is room for it to be
+                           doubled in the output stream.  Therefore we
+                           no longer advance the pointer when the character
+                           is fetched, but rather wait until after the check
+                           for a NULL output character. (This is necessary
+                           because there may not be room for the two chars
+                           needed to send a NULL.
+		         */
+                        outch = info->xmit_buf[info->xmit_tail];
+                        if( outch ){
+			    info->xmit_cnt--;
+			    info->xmit_tail = (info->xmit_tail + 1)
+						      & (PAGE_SIZE - 1);
+			    base_addr[CyTDR] = outch;
+                        }else{
+                            if(char_count > 1){
+				info->xmit_cnt--;
+				info->xmit_tail = (info->xmit_tail + 1)
+							  & (PAGE_SIZE - 1);
+				base_addr[CyTDR] = outch;
+				base_addr[CyTDR] = 0;
+				char_count--;
+                            }else{
+                            }
+                        }
+                    }
+
+        txdone:
+                    if (info->xmit_cnt < WAKEUP_CHARS) {
+                        cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+                    }
+
+        txend:
+                    /* end of service */
+                    base_addr[CyTIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = (save_car);
+                }
+
+                if (status & CySRModem) {        /* modem interrupt */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyMIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    info = &cy_port[channel + chip * 4 + cinfo->first_line];
+                    info->last_active = jiffies;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    mdm_change = base_addr[CyMISR];
+                    mdm_status = base_addr[CyMSVR1];
+
+                    if(info->tty == 0){ /* nowhere to put the data, ignore it */
+                        ;
+                    }else{
+                        if((mdm_change & CyDCD)
+                        && (info->flags & ASYNC_CHECK_CD)){
+                            if(mdm_status & CyDCD){
+/* CP('!'); */
+                                cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+                            }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+                                     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+/* CP('@'); */
+                                cy_sched_event(info, Cy_EVENT_HANGUP);
+                            }
+                        }
+                        if((mdm_change & CyCTS)
+                        && (info->flags & ASYNC_CTS_FLOW)){
+                            if(info->tty->stopped){
+                                if(mdm_status & CyCTS){
+                                    /* !!! cy_start isn't used because... */
+                                    info->tty->stopped = 0;
+				    base_addr[CySRER] |= CyTxMpty;
+				    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+                                }
+                            }else{
+                                if(!(mdm_status & CyCTS)){
+                                    /* !!! cy_stop isn't used because... */
+                                    info->tty->stopped = 1;
+				    base_addr[CySRER] &= ~CyTxMpty;
+                                }
+                            }
+                        }
+                        if(mdm_status & CyDSR){
+                        }
+                        if(mdm_status & CyRI){
+                        }
+                    }
+                    /* end of service */
+                    base_addr[CyMIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = save_car;
+                }
+            }          /* end while status != 0 */
+        }            /* end loop for chips... */
+    } while(had_work);
+
+   /* clear interrupts */
+    card_base_addr = (unsigned char *)cinfo->base_addr;
+   vdata = *(card_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+
+} /* cy_interrupt */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using cy_sched_event(), and they get done here.
+ *
+ * This is done through one level of indirection--the task queue.
+ * When a hardware interrupt service routine wants service by the
+ * driver's bottom half, it enqueues the appropriate tq_struct (one
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue.  When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * that the work queue be processed.
+ *
+ * Although this may seem unwieldy, it gives the system a way to
+ * pass an argument (in this case the pointer to the cyclades_port
+ * structure) to the bottom half of the driver.  Previous kernels
+ * had to poll every port to see if that port needed servicing.
+ */
+static void
+do_cyclades_bh(void *unused)
+{
+    run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
+
+static void
+do_softint(void *private_)
+{
+  struct cyclades_port *info = (struct cyclades_port *) private_;
+  struct tty_struct    *tty;
+
+    tty = info->tty;
+    if (!tty)
+	return;
+
+    if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+	tty_hangup(info->tty);
+	wake_up_interruptible(&info->open_wait);
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+			     ASYNC_CALLOUT_ACTIVE);
+    }
+    if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+	wake_up_interruptible(&info->open_wait);
+    }
+    if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+	if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+	&& tty->ldisc.write_wakeup){
+	    (tty->ldisc.write_wakeup)(tty);
+	}
+	wake_up_interruptible(&tty->write_wait);
+    }
+} /* do_softint */
+
+
+/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int
+grab_all_interrupts(int dontgrab)
+{
+  int irq_lines = 0;
+  int i, mask;
+    
+    for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+	if (!(mask & dontgrab)
+	&& !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe")) {
+	    irq_lines |= mask;
+	}
+    }
+    return irq_lines;
+} /* grab_all_interrupts */
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void
+free_all_interrupts(int irq_lines)
+{
+  int i;
+    
+    for (i = 0; i < 16; i++) {
+	if (irq_lines & (1 << i))
+	    free_irq(i);
+    }
+} /* free_all_interrupts */
+
+/*
+ * This routine returns a bitfield of "wild interrupts".  Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int
+check_wild_interrupts(void)
+{
+  int	i, mask;
+  int	wild_interrupts = 0;
+  int	irq_lines;
+  unsigned long timeout;
+  unsigned long flags;
+	
+    /*Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+	irq_lines = grab_all_interrupts(0);
+       
+	/*
+	 * Delay for 0.1 seconds -- we use a busy loop since this may 
+	 * occur during the bootup sequence
+	 */
+	timeout = jiffies+10;
+	while (timeout >= jiffies)
+	    ;
+	
+	cy_triggered = 0;	/* Reset after letting things settle */
+
+	timeout = jiffies+10;
+	while (timeout >= jiffies)
+		;
+	
+	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+	    if ((cy_triggered & (1 << i)) &&
+		(irq_lines & (1 << i))) {
+		    wild_interrupts |= mask;
+	    }
+	}
+	free_all_interrupts(irq_lines);
+    restore_flags(flags);
+    return wild_interrupts;
+} /* check_wild_interrupts */
+
+/*
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use.  It is not
+ * fool-proof, but it works a large part of the time.
+ */
+static int
+get_auto_irq(int card)
+{
+  unsigned long timeout;
+  unsigned char *base_addr;
+  int save_xir, save_car;
+  volatile char vdata;
+
+    base_addr = (unsigned char*) (cy_card[card].base_addr);
+    intr_base_addr = base_addr;
+	
+    /*
+     * Enable interrupts and see who answers
+     */
+    cy_irq_triggered = 0;
+    cli();
+	base_addr[CyCAR] = 0;
+	write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR);
+	base_addr[CySRER] |= CyTxMpty;
+    sti();
+    
+    timeout = jiffies+2;
+    while (timeout >= jiffies) {
+	if (cy_irq_triggered)
+	    break;
+    }
+    /*
+     * Now check to see if we got any business, and clean up.
+     */
+    cli();
+	if(intr_base_addr[CySVRR] != 0){
+	    save_xir = (u_char) intr_base_addr[CyTIR];
+	    save_car = intr_base_addr[CyCAR];
+	    if ((save_xir & 0x3) != 0){
+		printk("channel %x requesting unexpected interrupt\n",save_xir);
+	    }
+	    intr_base_addr[CyCAR] = (save_xir & 0x3);
+	    intr_base_addr[CySRER] &= ~CyTxMpty;
+	    intr_base_addr[CyTIR] = (save_xir & 0x3f);
+	    intr_base_addr[CyCAR] = (save_car);
+	    vdata = *(intr_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+	}
+    sti();
+
+    return(cy_irq_triggered);
+} /* get_auto_irq */
+
+/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int
+do_auto_irq(int card)
+{
+  int 			irq_lines = 0;
+  int			irq_try_1 = 0, irq_try_2 = 0;
+  int			retries;
+  unsigned long flags;
+
+    /* Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+        cy_wild_int_mask = check_wild_interrupts();
+
+	irq_lines = grab_all_interrupts(cy_wild_int_mask);
+	
+	for (retries = 0; retries < 5; retries++) {
+	    if (!irq_try_1)
+		irq_try_1 = get_auto_irq(card);
+	    if (!irq_try_2)
+		irq_try_2 = get_auto_irq(card);
+	    if (irq_try_1 && irq_try_2) {
+		if (irq_try_1 == irq_try_2)
+		    break;
+		irq_try_1 = irq_try_2 = 0;
+	    }
+	}
+    restore_flags(flags);
+    free_all_interrupts(irq_lines);
+    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+} /* do_auto_irq */
+
+
+/* This is called whenever a port becomes active;
+   interrupts are enabled and DTR & RTS are turned on.
+ */
+static int
+startup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    if (info->flags & ASYNC_INITIALIZED){
+	return 0;
+    }
+
+    if (!info->type){
+	if (info->tty){
+	    set_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	return 0;
+    }
+    if (!info->xmit_buf){
+	info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
+	if (!info->xmit_buf){
+	    return -ENOMEM;
+	}
+    }
+
+    config_setup(info);
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("startup card %d, chip %d, channel %d, base_addr %lx",
+	 card, chip, channel, (long)base_addr);/**/
+#endif
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+
+	base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+
+	write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR);
+
+	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+	base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('1'); */
+	base_addr[CyMSVR2] = CyDTR;
+
+#ifdef SERIAL_DEBUG_DTR
+        printk("cyc: %d: raising DTR\n", __LINE__);
+        printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+
+	base_addr[CySRER] |= CyRxData;
+	info->flags |= ASYNC_INITIALIZED;
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return 0;
+} /* startup */
+
+void
+start_xmit( struct cyclades_port *info )
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+    save_flags(flags); cli();
+	base_addr[CyCAR] = channel;
+	base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+} /* start_xmit */
+
+/*
+ * This routine shuts down a serial port; interrupts are disabled,
+ * and DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    if (!(info->flags & ASYNC_INITIALIZED)){
+/* CP('$'); */
+	return;
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
+	    card, chip, channel, (long)base_addr);
+#endif
+
+    /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
+       SENT BEFORE DROPPING THE LINE !!!  (Perhaps
+       set some flag that is read when XMTY happens.)
+       Other choices are to delay some fixed interval
+       or schedule some later processing.
+     */
+    save_flags(flags); cli();
+	if (info->xmit_buf){
+	    free_page((unsigned long) info->xmit_buf);
+	    info->xmit_buf = 0;
+	}
+
+	base_addr[CyCAR] = (u_char)channel;
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+	    base_addr[CyMSVR1] = ~CyRTS;
+/* CP('C');CP('1'); */
+	    base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+        }
+	write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR);
+         /* it may be appropriate to clear _XMIT at
+           some later date (after testing)!!! */
+
+	if (info->tty){
+	    set_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	info->flags &= ~ASYNC_INITIALIZED;
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return;
+} /* shutdown */
+
+/*
+ * This routine finds or computes the various line characteristics.
+ */
+static void
+config_setup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+  unsigned cflag;
+  int   i;
+
+    if (!info->tty || !info->tty->termios){
+        return;
+    }
+    if (info->line == -1){
+        return;
+    }
+    cflag = info->tty->termios->c_cflag;
+
+    /* baud rate */
+    i = cflag & CBAUD;
+#ifdef CBAUDEX
+/* Starting with kernel 1.1.65, there is direct support for
+   higher baud rates.  The following code supports those
+   changes.  The conditional aspect allows this driver to be
+   used for earlier as well as later kernel versions.  (The
+   mapping is slightly different from serial.c because there
+   is still the possibility of supporting 75 kbit/sec with
+   the Cyclades board.)
+ */
+    if (i & CBAUDEX) {
+	if (i == B57600)
+	    i = 16;
+	else if(i == B115200) 
+	    i = 18;
+#ifdef B78600
+	else if(i == B78600) 
+	    i = 17;
+#endif
+	else
+	    info->tty->termios->c_cflag &= ~CBAUDEX;
+    }
+#endif
+    if (i == 15) {
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		    i += 1;
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		    i += 3;
+    }
+    info->tbpr = baud_bpr[i]; /* Tx BPR */
+    info->tco = baud_co[i]; /* Tx CO */
+    info->rbpr = baud_bpr[i]; /* Rx BPR */
+    info->rco = baud_co[i]; /* Rx CO */
+    if (baud_table[i] == 134) {
+        info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+        /* get it right for 134.5 baud */
+    } else if (baud_table[i]) {
+        info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+        /* this needs to be propagated into the card info */
+    } else {
+        info->timeout = 0;
+    }
+    /* By tradition (is it a standard?) a baud rate of zero
+       implies the line should be/has been closed.  A bit
+       later in this routine such a test is performed. */
+
+    /* byte size and parity */
+    info->cor5 = 0;
+    info->cor4 = 0;
+    info->cor3 = baud_cor3[i]; /* receive threshold */
+    info->cor2 = CyETC;
+    switch(cflag & CSIZE){
+    case CS5:
+        info->cor1 = Cy_5_BITS;
+        break;
+    case CS6:
+        info->cor1 = Cy_6_BITS;
+        break;
+    case CS7:
+        info->cor1 = Cy_7_BITS;
+        break;
+    case CS8:
+        info->cor1 = Cy_8_BITS;
+        break;
+    }
+    if(cflag & CSTOPB){
+        info->cor1 |= Cy_2_STOP;
+    }
+    if (cflag & PARENB){
+        if (cflag & PARODD){
+            info->cor1 |= CyPARITY_O;
+        }else{
+            info->cor1 |= CyPARITY_E;
+        }
+    }else{
+        info->cor1 |= CyPARITY_NONE;
+    }
+	
+    /* CTS flow control flag */
+    if (cflag & CRTSCTS)
+	info->flags |= ASYNC_CTS_FLOW;
+    else
+	info->flags &= ~ASYNC_CTS_FLOW;
+    if (cflag & CLOCAL)
+	info->flags &= ~ASYNC_CHECK_CD;
+    else
+	info->flags |= ASYNC_CHECK_CD;
+
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+
+       /* tx and rx baud rate */
+
+	base_addr[CyTCOR] = info->tco;
+	base_addr[CyTBPR] = info->tbpr;
+	base_addr[CyRCOR] = info->rco;
+	base_addr[CyRBPR] = info->rbpr;
+
+	/* set line characteristics  according configuration */
+
+	base_addr[CySCHR1] = START_CHAR(info->tty);
+	base_addr[CySCHR2] = STOP_CHAR(info->tty);
+	base_addr[CyCOR1] = info->cor1;
+	base_addr[CyCOR2] = info->cor2;
+	base_addr[CyCOR3] = info->cor3;
+	base_addr[CyCOR4] = info->cor4;
+	base_addr[CyCOR5] = info->cor5;
+
+	write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch);
+
+	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+
+	base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+
+	if (C_CLOCAL(info->tty)) {
+	    base_addr[CySRER] |= 0; /* without modem intr */
+				    /* ignore 1->0 modem transitions */
+	    base_addr[CyMCOR1] = 0x0;
+				    /* ignore 0->1 modem transitions */
+	    base_addr[CyMCOR2] = 0x0;
+	} else {
+	    base_addr[CySRER] |= CyMdmCh; /* with modem intr */
+				    /* act on 1->0 modem transitions */
+	    base_addr[CyMCOR1] = CyDSR|CyCTS|CyRI|CyDCD;
+				    /* act on 0->1 modem transitions */
+	    base_addr[CyMCOR2] = CyDSR|CyCTS|CyRI|CyDCD;
+	}
+
+	if(i == 0){ /* baud rate is zero, turn off line */
+	    base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	}else{
+	    base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	}
+
+	/* The hardware option, CyRtsAO, presents RTS when
+	   the chip has characters to send.  Since most modems
+	   use RTS as reverse (inbound) flow control, this
+	   option is not used.  If inbound flow control is
+	   necessary, DTR can be programmed to provide the
+	   appropriate signals.  Contact Marcio Saito for
+	   details. */
+	if (C_CRTSCTS(info->tty)) {
+	    info->cor2 |= CyCtsAE;
+	}else{
+	    info->cor2 &= ~CyCtsAE;
+	}
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+
+    restore_flags(flags);
+
+} /* config_setup */
+
+
+static void
+cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_put_char ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_put_char"))
+	return;
+
+    if (!tty || !info->xmit_buf)
+	return;
+
+    save_flags(flags); cli();
+	if (info->xmit_cnt >= PAGE_SIZE - 1) {
+	    restore_flags(flags);
+	    return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= PAGE_SIZE - 1;
+	info->xmit_cnt++;
+    restore_flags(flags);
+} /* cy_put_char */
+
+
+static void
+cy_flush_chars(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_chars ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
+	return;
+
+    if (info->xmit_cnt <= 0 || tty->stopped
+    || tty->hw_stopped || !info->xmit_buf)
+	return;
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = channel;
+	base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+} /* cy_flush_chars */
+
+
+/* This routine gets called when tty_write has put something into
+    the write_queue.  If the port is not already transmitting stuff,
+    start it off by enabling interrupts.  The interrupt service
+    routine will then ensure that the characters are sent.  If the
+    port is already active, there is no need to kick it.
+ */
+static int
+cy_write(struct tty_struct * tty, int from_user,
+           unsigned char *buf, int count)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  int c, total = 0;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write")){
+	return 0;
+    }
+	
+    if (!tty || !info->xmit_buf || !tmp_buf){
+        return 0;
+    }
+
+    while (1) {
+        save_flags(flags); cli();		
+	c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+			   SERIAL_XMIT_SIZE - info->xmit_head));
+	if (c <= 0){
+	    restore_flags(flags);
+	    break;
+	}
+
+	if (from_user) {
+	    down(&tmp_buf_sem);
+	    memcpy_fromfs(tmp_buf, buf, c);
+	    c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+		       SERIAL_XMIT_SIZE - info->xmit_head));
+	    memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+	    up(&tmp_buf_sem);
+	} else
+	    memcpy(info->xmit_buf + info->xmit_head, buf, c);
+	info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt += c;
+	restore_flags(flags);
+	buf += c;
+	count -= c;
+	total += c;
+    }
+
+
+    if (info->xmit_cnt
+    && !tty->stopped
+    && !tty->hw_stopped ) {
+        start_xmit(info);
+    }
+    return total;
+} /* cy_write */
+
+
+static int
+cy_write_room(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  int	ret;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write_room ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write_room"))
+	return 0;
+    ret = PAGE_SIZE - info->xmit_cnt - 1;
+    if (ret < 0)
+	ret = 0;
+    return ret;
+} /* cy_write_room */
+
+
+static int
+cy_chars_in_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
+	return 0;
+
+    return info->xmit_cnt;
+} /* cy_chars_in_buffer */
+
+
+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_buffer ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
+	return;
+    save_flags(flags); cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+    restore_flags(flags);
+    wake_up_interruptible(&tty->write_wait);
+    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+    && tty->ldisc.write_wakeup)
+	(tty->ldisc.write_wakeup)(tty);
+} /* cy_flush_buffer */
+
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled or that the
+   throttle should be released.
+ */
+static void
+cy_throttle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+	
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	   tty->ldisc.chars_in_buffer(tty));
+    printk("cy_throttle ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+	    return;
+    }
+
+    if (I_IXOFF(tty)) {
+	info->x_char = STOP_CHAR(tty);
+	    /* Should use the "Send Special Character" feature!!! */
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+	base_addr[CyMSVR1] = ~CyRTS;
+    restore_flags(flags);
+
+    return;
+} /* cy_throttle */
+
+
+static void
+cy_unthrottle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+	
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	   tty->ldisc.chars_in_buffer(tty));
+    printk("cy_unthrottle ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+	    return;
+    }
+
+    if (I_IXOFF(tty)) {
+	info->x_char = START_CHAR(tty);
+	/* Should use the "Send Special Character" feature!!! */
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+		   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+	base_addr[CyMSVR1] = CyRTS;
+    restore_flags(flags);
+
+    return;
+} /* cy_unthrottle */
+
+static int
+get_serial_info(struct cyclades_port * info,
+                           struct serial_struct * retinfo)
+{
+  struct serial_struct tmp;
+  struct cyclades_card *cinfo = &cy_card[info->card];
+
+/* CP('g'); */
+    if (!retinfo)
+            return -EFAULT;
+    memset(&tmp, 0, sizeof(tmp));
+    tmp.type = info->type;
+    tmp.line = info->line;
+    tmp.port = info->card * 0x100 + info->line - cinfo->first_line;
+    tmp.irq = cinfo->irq;
+    tmp.flags = info->flags;
+    tmp.baud_base = 0;          /*!!!*/
+    tmp.close_delay = info->close_delay;
+    tmp.custom_divisor = 0;     /*!!!*/
+    tmp.hub6 = 0;               /*!!!*/
+    memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+    return 0;
+} /* get_serial_info */
+
+static int
+set_serial_info(struct cyclades_port * info,
+                           struct serial_struct * new_info)
+{
+  struct serial_struct new_serial;
+  struct cyclades_port old_info;
+
+/* CP('s'); */
+    if (!new_info)
+	    return -EFAULT;
+    memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+    old_info = *info;
+
+    if (!suser()) {
+	    if ((new_serial.close_delay != info->close_delay) ||
+		((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+		 (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+		    return -EPERM;
+	    info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			   (new_serial.flags & ASYNC_USR_MASK));
+	    goto check_and_exit;
+    }
+
+
+    /*
+     * OK, past this point, all the error checking has been done.
+     * At this point, we start making changes.....
+     */
+
+    info->flags = ((info->flags & ~ASYNC_FLAGS) |
+		    (new_serial.flags & ASYNC_FLAGS));
+    info->close_delay = new_serial.close_delay;
+
+
+check_and_exit:
+    if (info->flags & ASYNC_INITIALIZED){
+	config_setup(info);
+	return 0;
+    }else{
+        return startup(info);
+    }
+} /* set_serial_info */
+
+static int
+get_modem_info(struct cyclades_port * info, unsigned int *value)
+{
+  int card,chip,channel;
+  unsigned char *base_addr;
+  unsigned long flags;
+  unsigned char status;
+  unsigned int result;
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)channel;
+        status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+    restore_flags(flags);
+
+    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
+            | ((status  & CyDTR) ? TIOCM_DTR : 0)
+            | ((status  & CyDCD) ? TIOCM_CAR : 0)
+            | ((status  & CyRI) ? TIOCM_RNG : 0)
+            | ((status  & CyDSR) ? TIOCM_DSR : 0)
+            | ((status  & CyCTS) ? TIOCM_CTS : 0);
+    put_fs_long(result,(unsigned long *) value);
+    return 0;
+} /* get_modem_info */
+
+static int
+set_modem_info(struct cyclades_port * info, unsigned int cmd,
+                          unsigned int *value)
+{
+  int card,chip,channel;
+  unsigned char *base_addr;
+  unsigned long flags;
+  unsigned int arg = get_fs_long((unsigned long *) value);
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[card].base_addr + chip * CyRegSize);
+
+    switch (cmd) {
+    case TIOCMBIS:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('2'); */
+	    base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    case TIOCMBIC:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = ~CyRTS;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('2'); */
+	    base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    case TIOCMSET:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+	    restore_flags(flags);
+	}else{
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = ~CyRTS;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('3'); */
+	    base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}else{
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('3'); */
+	    base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    default:
+		return -EINVAL;
+        }
+    return 0;
+} /* set_modem_info */
+
+static void
+send_break( struct cyclades_port * info, int duration)
+{ /* Let the transmit ISR take care of this (since it
+     requires stuffing characters into the output stream).
+   */
+    info->x_break = duration;
+    if (!info->xmit_cnt ) {
+	start_xmit(info);
+    }
+} /* send_break */
+
+
+static int
+cy_ioctl(struct tty_struct *tty, struct file * file,
+            unsigned int cmd, unsigned long arg)
+{
+  int error;
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  int ret_val = 0;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */
+#endif
+
+    switch (cmd) {
+        case TCSBRK:    /* SVID version: non-zero arg --> no break */
+	    ret_val = tty_check_change(tty);
+	    if (ret_val)
+		return ret_val;
+            wait_until_sent(tty,0);
+            if (!arg)
+                send_break(info, HZ/4); /* 1/4 second */
+            break;
+        case TCSBRKP:   /* support for POSIX tcsendbreak() */
+	    ret_val = tty_check_change(tty);
+	    if (ret_val)
+		return ret_val;
+            wait_until_sent(tty,0);
+            send_break(info, arg ? arg*(HZ/10) : HZ/4);
+            break;
+        case TIOCMBIS:
+        case TIOCMBIC:
+        case TIOCMSET:
+            ret_val = set_modem_info(info, cmd, (unsigned int *) arg);
+
+/* The following commands are imcompletely implemented!!! */
+        case TIOCGSOFTCAR:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+                        (unsigned long *) arg);
+            break;
+        case TIOCSSOFTCAR:
+            arg = get_fs_long((unsigned long *) arg);
+            tty->termios->c_cflag =
+                    ((tty->termios->c_cflag & ~CLOCAL) |
+                     (arg ? CLOCAL : 0));
+            break;
+        case TIOCMGET:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_modem_info(info, (unsigned int *) arg);
+            break;
+        case TIOCGSERIAL:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct serial_struct));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        case TIOCSSERIAL:
+            ret_val = set_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        default:
+	    ret_val = -ENOIOCTLCMD;
+    }
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl done\n");
+#endif
+
+    return ret_val;
+} /* cy_ioctl */
+
+
+
+
+static void
+cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_set_termios ttyC%d\n", info->line);
+#endif
+
+    if (tty->termios->c_cflag == old_termios->c_cflag)
+        return;
+    config_setup(info);
+
+    if ((old_termios->c_cflag & CRTSCTS) &&
+        !(tty->termios->c_cflag & CRTSCTS)) {
+            tty->stopped = 0;
+            cy_start(tty);
+    }
+#ifdef tytso_patch_94Nov25_1726
+    if (!(old_termios->c_cflag & CLOCAL) &&
+        (tty->termios->c_cflag & CLOCAL))
+            wake_up_interruptible(&info->open_wait);
+#endif
+
+    return;
+} /* cy_set_termios */
+
+
+static void
+cy_close(struct tty_struct * tty, struct file * filp)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+
+/* CP('C'); */
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close ttyC%d\n", info->line);
+#endif
+
+    if (!info
+    || serial_paranoia_check(info, tty->device, "cy_close")){
+        return;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_close ttyC%d, count = %d\n", info->line, info->count);
+#endif
+
+    if ((tty->count == 1) && (info->count != 1)) {
+	/*
+	 * Uh, oh.  tty->count is 1, which means that the tty
+	 * structure will be freed.  Info->count should always
+	 * be one in these conditions.  If it's greater than
+	 * one, we've got real problems, since it means the
+	 * serial port won't be shutdown.
+	 */
+	printk("cy_close: bad serial port count; tty->count is 1, "
+	   "info->count is %d\n", info->count);
+	info->count = 1;
+    }
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrmenting count to %d\n", __LINE__, info->count - 1);
+#endif
+    if (--info->count < 0) {
+	printk("cy_close: bad serial port count for ttys%d: %d\n",
+	       info->line, info->count);
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+	info->count = 0;
+    }
+    if (info->count)
+	return;
+    info->flags |= ASYNC_CLOSING;
+    /*
+     * Save the termios structure, since this port may have
+     * separate termios for callout and dialin.
+     */
+    if (info->flags & ASYNC_NORMAL_ACTIVE)
+	info->normal_termios = *tty->termios;
+    if (info->flags & ASYNC_CALLOUT_ACTIVE)
+	info->callout_termios = *tty->termios;
+    if (info->flags & ASYNC_INITIALIZED)
+	wait_until_sent(tty, 3000); /* 30 seconds timeout */
+    shutdown(info);
+    if (tty->driver.flush_buffer)
+	tty->driver.flush_buffer(tty);
+    if (tty->ldisc.flush_buffer)
+	tty->ldisc.flush_buffer(tty);
+    info->event = 0;
+    info->tty = 0;
+    if (tty->ldisc.num != ldiscs[N_TTY].num) {
+	if (tty->ldisc.close)
+	    (tty->ldisc.close)(tty);
+	tty->ldisc = ldiscs[N_TTY];
+	tty->termios->c_line = N_TTY;
+	if (tty->ldisc.open)
+	    (tty->ldisc.open)(tty);
+    }
+    if (info->blocked_open) {
+	if (info->close_delay) {
+	    current->state = TASK_INTERRUPTIBLE;
+	    current->timeout = jiffies + info->close_delay;
+	    schedule();
+	}
+	wake_up_interruptible(&info->open_wait);
+    }
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+		     ASYNC_CLOSING);
+    wake_up_interruptible(&info->close_wait);
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close done\n");
+#endif
+
+    return;
+} /* cy_close */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+cy_hangup(struct tty_struct *tty)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+	
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_hangup ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_hangup"))
+	return;
+    
+    shutdown(info);
+#if 0
+    info->event = 0;
+    info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+    info->tty = 0;
+#endif
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+    wake_up_interruptible(&info->open_wait);
+} /* cy_hangup */
+
+
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct cyclades_port *info)
+{
+  struct wait_queue wait = { current, NULL };
+  struct cyclades_card *cinfo;
+  unsigned long flags;
+  int chip, channel;
+  int retval;
+  char *base_addr;
+
+    /*
+     * If the device is in the middle of being closed, then block
+     * until it's done, and then try again.
+     */
+    if (info->flags & ASYNC_CLOSING) {
+	interruptible_sleep_on(&info->close_wait);
+	if (info->flags & ASYNC_HUP_NOTIFY){
+	    return -EAGAIN;
+	}else{
+	    return -ERESTARTSYS;
+	}
+    }
+
+    /*
+     * If this is a callout device, then just make sure the normal
+     * device isn't being used.
+     */
+    if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+	if (info->flags & ASYNC_NORMAL_ACTIVE){
+	    return -EBUSY;
+	}
+	if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+	    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+	    (info->session != current->session)){
+	    return -EBUSY;
+	}
+	if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+	    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+	    (info->pgrp != current->pgrp)){
+	    return -EBUSY;
+	}
+	info->flags |= ASYNC_CALLOUT_ACTIVE;
+	return 0;
+    }
+
+    /*
+     * If non-blocking mode is set, then make the check up front
+     * and then exit.
+     */
+    if (filp->f_flags & O_NONBLOCK) {
+	if (info->flags & ASYNC_CALLOUT_ACTIVE){
+	    return -EBUSY;
+	}
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+    }
+
+    /*
+     * Block waiting for the carrier detect and the line to become
+     * free (i.e., not in use by the callout).  While we are in
+     * this loop, info->count is dropped by one, so that
+     * cy_close() knows when to free things.  We restore it upon
+     * exit, either normal or abnormal.
+     */
+    retval = 0;
+    add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+    printk("block_til_ready before block: ttyC%d, count = %d\n",
+	   info->line, info->count);/**/
+#endif
+    info->count--;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrmenting count to %d\n", __LINE__, info->count);
+#endif
+    info->blocked_open++;
+
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (char *) (cinfo->base_addr + chip * CyRegSize);
+
+    while (1) {
+	save_flags(flags); cli();
+	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('4'); */
+		base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+                printk("cyc: %d: raising DTR\n", __LINE__);
+                printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    }
+	restore_flags(flags);
+	current->state = TASK_INTERRUPTIBLE;
+	if (tty_hung_up_p(filp)
+	|| !(info->flags & ASYNC_INITIALIZED) ){
+	    if (info->flags & ASYNC_HUP_NOTIFY) {
+		retval = -EAGAIN;
+	    }else{
+		retval = -ERESTARTSYS;
+	    }
+	    break;
+	}
+	save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+	    && !(info->flags & ASYNC_CLOSING)
+	    && (C_CLOCAL(tty)
+	        || (base_addr[CyMSVR1] & CyDCD))) {
+		    restore_flags(flags);
+		    break;
+	    }
+	restore_flags(flags);
+	if (current->signal & ~current->blocked) {
+	    retval = -ERESTARTSYS;
+	    break;
+	}
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready blocking: ttyC%d, count = %d\n",
+	       info->line, info->count);/**/
+#endif
+	schedule();
+    }
+    current->state = TASK_RUNNING;
+    remove_wait_queue(&info->open_wait, &wait);
+    if (!tty_hung_up_p(filp)){
+	info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrmenting count to %d\n", __LINE__, info->count);
+#endif
+    }
+    info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+    printk("block_til_ready after blocking: ttyC%d, count = %d\n",
+	   info->line, info->count);/**/
+#endif
+    if (retval)
+	    return retval;
+    info->flags |= ASYNC_NORMAL_ACTIVE;
+    return 0;
+} /* block_til_ready */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initalization for the tty structure.
+ */
+int
+cy_open(struct tty_struct *tty, struct file * filp)
+{
+  struct cyclades_port  *info;
+  int retval, line;
+
+/* CP('O'); */
+    line = MINOR(tty->device) - tty->driver.minor_start;
+    if ((line < 0) || (NR_PORTS <= line)){
+        return -ENODEV;
+    }
+    info = &cy_port[line];
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_open ttyC%d\n", info->line); /* */
+#endif
+    if (serial_paranoia_check(info, tty->device, "cy_open")){
+        return -ENODEV;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/
+#endif
+    info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrmenting count to %d\n", __LINE__, info->count);
+#endif
+    tty->driver_data = info;
+    info->tty = tty;
+
+    if (!tmp_buf) {
+	tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+	if (!tmp_buf){
+	    return -ENOMEM;
+        }
+    }
+
+    if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+	if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+	    *tty->termios = info->normal_termios;
+	else 
+	    *tty->termios = info->callout_termios;
+    }
+    /*
+     * Start up serial port
+     */
+    retval = startup(info);
+    if (retval){
+	return retval;
+    }
+
+    retval = block_til_ready(tty, filp, info);
+    if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+	printk("cy_open returning after block_til_ready with %d\n",
+	       retval);
+#endif
+	return retval;
+    }
+
+    info->session = current->session;
+    info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open done\n");/**/
+#endif
+    return 0;
+} /* cy_open */
+
+
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_init() and friends
+ *
+ * cy_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void
+show_version(void)
+{
+    printk("Cyclom driver %s\n",rcsid);
+} /* show_version */
+
+/* initialize chips on card -- return number of valid
+   chips (which is number of ports/4) */
+int
+cy_init_card(unsigned char *base_addr)
+{
+  volatile unsigned short discard;
+  unsigned int chip_number;
+
+    discard = base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
+    discard = base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
+    udelay(500L);
+
+    for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+        udelay(1000L);
+        if(base_addr[CyCCR] != 0x00){
+            /*************
+            printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
+               chip_number, (unsigned long)base_addr);
+            *************/
+            return chip_number;
+        }
+
+        base_addr[CyGFRCR] = 0;
+        udelay(10L);
+
+        base_addr[CyCCR] = CyCHIP_RESET;
+        udelay(1000L);
+
+        if(base_addr[CyGFRCR] == 0x00){
+            printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n",
+               chip_number, (unsigned long)base_addr);
+            return chip_number;
+        }
+        if((0xf0 & base_addr[CyGFRCR]) != 0x40){
+            printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
+               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+            return chip_number;
+        }
+        base_addr[CyGCR] = CyCH0_SERIAL;
+        base_addr[CyPPR] = CyCLOCK_25_1MS * 5; /* run clock at 200 Hz */
+
+        printk(" chip #%d at %#6lx is rev 0x%2x\n",
+               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+
+        /* advance to next chip on card */
+        base_addr += CyRegSize;
+    }
+
+    return chip_number;
+} /* cy_init_card */
+
+/* The serial driver boot-time initialization code!
+    Hardware I/O ports are mapped to character special devices on a
+    first found, first allocated manner.  That is, this code searches
+    for Cyclom cards in the system.  As each is found, it is probed
+    to discover how many chips (and thus how many ports) are present.
+    These ports are mapped to the tty ports 32 and upward in monotonic
+    fashion.  If an 8-port card is replaced with a 16-port card, the
+    port mapping on a following card will shift.
+
+    This approach is different from what is used in the other serial
+    device driver because the Cyclom is more properly a multiplexer,
+    not just an aggregation of serial ports on one card.
+
+    If there are more cards with more ports than have been statically
+    allocated above, a warning is printed and the extra ports are ignored.
+ */
+long
+cy_init(long kmem_start)
+{
+  struct cyclades_port *info;
+  struct cyclades_card *cinfo;
+  int good_ports = 0;
+  int port_num = 0;
+  int index;
+#ifdef notyet
+  struct sigaction sa;
+#endif
+  int retval;
+
+scrn[1] = '\0';
+    show_version();
+
+    /* Initialize the tty_driver structure */
+    
+    memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
+    cy_serial_driver.magic = TTY_DRIVER_MAGIC;
+    cy_serial_driver.name = "ttyC";
+    cy_serial_driver.major = 19 /* TTY_MAJOR */;
+    cy_serial_driver.minor_start = 32;
+    cy_serial_driver.num = NR_PORTS;
+    cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+    cy_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+    cy_serial_driver.init_termios = tty_std_termios;
+    cy_serial_driver.init_termios.c_cflag =
+	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+    cy_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+    cy_serial_driver.refcount = &serial_refcount;
+    cy_serial_driver.table = serial_table;
+    cy_serial_driver.termios = serial_termios;
+    cy_serial_driver.termios_locked = serial_termios_locked;
+    cy_serial_driver.open = cy_open;
+    cy_serial_driver.close = cy_close;
+    cy_serial_driver.write = cy_write;
+    cy_serial_driver.put_char = cy_put_char;
+    cy_serial_driver.flush_chars = cy_flush_chars;
+    cy_serial_driver.write_room = cy_write_room;
+    cy_serial_driver.chars_in_buffer = cy_chars_in_buffer;
+    cy_serial_driver.flush_buffer = cy_flush_buffer;
+    cy_serial_driver.ioctl = cy_ioctl;
+    cy_serial_driver.throttle = cy_throttle;
+    cy_serial_driver.unthrottle = cy_unthrottle;
+    cy_serial_driver.set_termios = cy_set_termios;
+    cy_serial_driver.stop = cy_stop;
+    cy_serial_driver.start = cy_start;
+    cy_serial_driver.hangup = cy_hangup;
+
+    /*
+     * The callout device is just like normal device except for
+     * major number and the subtype code.
+     */
+    cy_callout_driver = cy_serial_driver;
+    cy_callout_driver.name = "cub";
+    cy_callout_driver.major = 20 /* TTYAUX_MAJOR */;
+    cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+    if (tty_register_driver(&cy_serial_driver))
+	    panic("Couldn't register Cyclom serial driver\n");
+    if (tty_register_driver(&cy_callout_driver))
+	    panic("Couldn't register Cyclom callout driver\n");
+
+    bh_base[CYCLADES_BH].routine = do_cyclades_bh;
+
+    for (index = 0; index < 16; index++) {
+	    IRQ_cards[index] = 0;
+    }
+
+    port_num = 0;
+    info = cy_port;
+    for (index = 0, cinfo = cy_card; index < NR_CARDS; index++,cinfo++) {
+	/*** initialize card ***/
+	if(0 == (cinfo->num_chips =
+		    cy_init_card((unsigned char *)cinfo->base_addr))){
+	    /* this card is not present */
+	    continue;
+	}
+
+#ifndef CY_DONT_PROBE
+	/* find out the board's irq by probing */
+	cinfo->irq = do_auto_irq(index);
+#endif
+
+	/** bind IRQ to card **/
+	if (cinfo->irq) {
+	    retval = request_irq(cinfo->irq, cy_interrupt,
+				SA_INTERRUPT, "cyclades");
+	    if (retval){
+		printk("request_irq returned %d\n",retval);
+		/* return retval; */
+	    }
+	    IRQ_cards[cinfo->irq] = cinfo;
+        }else{
+	    printk("couldn't get board's irq\n");
+	    continue;
+	}
+
+	printk("  share IRQ %d: ", cinfo->irq);
+	good_ports = 4 * cinfo->num_chips;
+
+	if(port_num < NR_PORTS){
+	    cinfo->first_line = port_num;
+	    while( good_ports-- && port_num < NR_PORTS){
+		/*** initialize port ***/
+		info->magic = CYCLADES_MAGIC;
+		info->type = PORT_CIRRUS;
+		info->card = index;
+		info->line = port_num;
+		info->flags = STD_COM_FLAGS;
+		info->tty = 0;
+		info->xmit_fifo_size = 12;
+		info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
+		info->cor2 = CyETC;
+		info->cor3 = 0x08; /* _very_ small receive threshold */
+		info->cor4 = 0;
+		info->cor5 = 0;
+		info->tbpr = baud_bpr[13]; /* Tx BPR */
+		info->tco = baud_co[13]; /* Tx CO */
+		info->rbpr = baud_bpr[13]; /* Rx BPR */
+		info->rco = baud_co[13]; /* Rx CO */
+		info->close_delay = 0;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->callout_termios =cy_callout_driver.init_termios;
+		info->normal_termios = cy_serial_driver.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		/* info->session */
+		/* info->pgrp */
+		/* info->read_status_mask */
+		/* info->timeout */
+
+		printk("ttyC%1d ", info->line);
+		port_num++;info++;
+		if(!(port_num & 7)){
+		    printk("\n               ");
+		}
+	    }
+	}else{
+	    cinfo->first_line = -1;
+	}
+	printk("\n");
+    }
+    while( port_num < NR_PORTS){
+	info->line = -1;
+	port_num++;info++;
+    }
+    return kmem_start;
+    
+} /* cy_init */
+
+static void
+show_status(int line_num)
+{
+  unsigned char *base_addr;
+  int card,chip,channel;
+  struct cyclades_port * info;
+  unsigned long flags;
+
+    info = &cy_port[line_num];
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    printk("  card %d, chip %d, channel %d\n", card, chip, channel);/**/
+
+    printk(" cy_card\n");
+    printk("  irq base_addr num_chips first_line = %d %lx %d %d\n",
+           cy_card[card].irq, (long)cy_card[card].base_addr,
+           cy_card[card].num_chips, cy_card[card].first_line);
+
+    printk(" cy_port\n");
+    printk("  card line flags = %d %d %x\n",
+                 info->card, info->line, info->flags);
+    printk("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+                 (long)info->tty, info->read_status_mask,
+                 info->timeout, info->xmit_fifo_size);
+    printk("  cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n",
+             info->cor1, info->cor2, info->cor3, info->cor4, info->cor5);
+    printk("  tbpr,tco,rbpr,rco = %d %d %d %d\n",
+             info->tbpr, info->tco, info->rbpr, info->rco);
+    printk("  close_delay event count = %d %d %d\n",
+             info->close_delay, info->event, info->count);
+    printk("  x_char blocked_open = %x %x\n",
+             info->x_char, info->blocked_open);
+    printk("  session pgrp open_wait = %lx %lx %lx\n",
+             info->session, info->pgrp, (long)info->open_wait);
+
+
+    save_flags(flags); cli();
+
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr + chip * CyRegSize);
+
+/* Global Registers */
+
+	printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
+	printk(" CyCAR %x\n", base_addr[CyCAR]);
+	printk(" CyGCR %x\n", base_addr[CyGCR]);
+	printk(" CySVRR %x\n", base_addr[CySVRR]);
+	printk(" CyRICR %x\n", base_addr[CyRICR]);
+	printk(" CyTICR %x\n", base_addr[CyTICR]);
+	printk(" CyMICR %x\n", base_addr[CyMICR]);
+	printk(" CyRIR %x\n", base_addr[CyRIR]);
+	printk(" CyTIR %x\n", base_addr[CyTIR]);
+	printk(" CyMIR %x\n", base_addr[CyMIR]);
+	printk(" CyPPR %x\n", base_addr[CyPPR]);
+
+	base_addr[CyCAR] = (u_char)channel;
+
+/* Virtual Registers */
+
+	printk(" CyRIVR %x\n", base_addr[CyRIVR]);
+	printk(" CyTIVR %x\n", base_addr[CyTIVR]);
+	printk(" CyMIVR %x\n", base_addr[CyMIVR]);
+	printk(" CyMISR %x\n", base_addr[CyMISR]);
+
+/* Channel Registers */
+
+	printk(" CyCCR %x\n", base_addr[CyCCR]);
+	printk(" CySRER %x\n", base_addr[CySRER]);
+	printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
+	printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
+	printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
+	printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
+	printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
+	printk(" CyCCSR %x\n", base_addr[CyCCSR]);
+	printk(" CyRDCR %x\n", base_addr[CyRDCR]);
+	printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
+	printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
+	printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
+	printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
+	printk(" CySCRL %x\n", base_addr[CySCRL]);
+	printk(" CySCRH %x\n", base_addr[CySCRH]);
+	printk(" CyLNC %x\n", base_addr[CyLNC]);
+	printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
+	printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
+	printk(" CyRTPR %x\n", base_addr[CyRTPR]);
+	printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
+	printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
+	printk(" CyRBPR %x\n", base_addr[CyRBPR]);
+	printk(" CyRCOR %x\n", base_addr[CyRCOR]);
+	printk(" CyTBPR %x\n", base_addr[CyTBPR]);
+	printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+
+    restore_flags(flags);
+} /* show_status */
+
+
diff -u --recursive --new-file v1.1.73/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v1.1.73/linux/drivers/char/keyboard.c	Tue Nov 22 15:41:10 1994
+++ linux/drivers/char/keyboard.c	Mon Dec 19 18:00:38 1994
@@ -82,8 +82,8 @@
 
 /*
  * global state includes the following, and various static variables
- * in this module: prev_scancode, shift_state, diacr, npadch,
- *   dead_key_next, last_console
+ * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
+ * (last_console is now a global variable)
  */
 
 /* shift state counters.. */
@@ -91,8 +91,8 @@
 /* keyboard key bitmap */
 static unsigned long key_down[8] = { 0, };
 
+extern int last_console;
 static int want_console = -1;
-static int last_console = 0;		/* last used VC */
 static int dead_key_next = 0;
 /* 
  * In order to retrieve the shift_state (for the mouse server), either
@@ -190,7 +190,7 @@
 	put_queue(0x80 | ((c >> 6) & 0x3f));
 	put_queue(0x80 | (c & 0x3f));
     }
-    /* utf-8 is defined for words of up to 36 bits,
+    /* UTF-8 is defined for words of up to 31 bits,
        but we need only 16 bits here */
 }
 
@@ -1131,7 +1131,6 @@
 	}
 	if (want_console >= 0) {
 		if (want_console != fg_console) {
-			last_console = fg_console;
 			change_console(want_console);
 			/* we only changed when the console had already
 			   been allocated - a new console is not created
diff -u --recursive --new-file v1.1.73/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.1.73/linux/drivers/char/serial.c	Thu Dec 15 11:01:50 1994
+++ linux/drivers/char/serial.c	Thu Dec 22 06:53:15 1994
@@ -1533,8 +1533,13 @@
 static int set_modem_info(struct async_struct * info, unsigned int cmd,
 			  unsigned int *value)
 {
-	unsigned int arg = get_fs_long((unsigned long *) value);
+	int error;
+	unsigned int arg;
 
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error)
+		return error;
+	arg = get_fs_long((unsigned long *) value);
 	switch (cmd) {
 	case TIOCMBIS: 
 		if (arg & TIOCM_RTS) {
diff -u --recursive --new-file v1.1.73/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.73/linux/drivers/char/tty_io.c	Thu Dec 15 11:01:50 1994
+++ linux/drivers/char/tty_io.c	Thu Dec 22 06:55:40 1994
@@ -82,10 +82,12 @@
 
 /*
  * fg_console is the current virtual console,
+ * last_console is the last used one
  * redirect is the pseudo-tty that console output
  * is redirected to if asked by TIOCCONS.
  */
 int fg_console = 0;
+int last_console = 0;
 struct tty_struct * redirect = NULL;
 struct wait_queue * keypress_wait = NULL;
 
@@ -364,6 +366,7 @@
 	tty->flags = 0;
 	tty->session = 0;
 	tty->pgrp = -1;
+	tty->ctrl_status = 0;
  	for_each_task(p) {
 		if (p->tty == tty)
 			p->tty = NULL;
@@ -412,11 +415,11 @@
 
 	if (!tty)
 		return;
-
 	if (tty->pgrp > 0) {
 		kill_pg(tty->pgrp, SIGHUP, priv);
 		kill_pg(tty->pgrp, SIGCONT, priv);
 	}
+
 	tty->session = 0;
 	tty->pgrp = -1;
 
@@ -470,6 +473,7 @@
                 return;
         if (!vc_cons_allocated(new_console))
                 return;
+	last_console = fg_console;
 
 	/*
 	 * If we're switching, we could be going from KD_GRAPHICS to
@@ -1685,6 +1689,9 @@
 
 	kmem_start = kbd_init(kmem_start);
 	kmem_start = rs_init(kmem_start);
+#ifdef CONFIG_CYCLADES
+	kmem_start = cy_init(kmem_start);
+#endif
 	kmem_start = pty_init(kmem_start);
 	return kmem_start;
 }
diff -u --recursive --new-file v1.1.73/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v1.1.73/linux/drivers/char/vt.c	Thu Dec 15 11:01:50 1994
+++ linux/drivers/char/vt.c	Fri Dec 23 11:48:05 1994
@@ -190,6 +190,8 @@
 		 * doesn't do a whole lot. i'm not sure if it should do any
 		 * restoration of modes or what...
 		 */
+		if (!suser())
+			return -EPERM;
 		switch (arg) {
 		case KD_GRAPHICS:
 			break;
@@ -230,6 +232,8 @@
 		return -EINVAL;
 
 	case KDSKBMODE:
+		if (!suser())
+			return -EPERM;
 		switch(arg) {
 		  case K_RAW:
 			kbd->kbdmode = VC_RAW;
@@ -309,6 +313,8 @@
 		struct kbkeycode * const a = (struct kbkeycode *)arg;
 		unsigned int sc, kc;
 
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
 		if (i)
 			return i;
@@ -348,6 +354,8 @@
 		u_char s;
 		u_short v;
 
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
 		if (i)
 			return i;
@@ -437,6 +445,8 @@
 		u_char *p;
 		char *q;
 
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
 		if (i)
 			return i;
@@ -520,6 +530,8 @@
 		struct kbdiacrs *a = (struct kbdiacrs *)arg;
 		unsigned int ct;
 
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
 		if (i)
 			return i;
@@ -542,6 +554,8 @@
 		return 0;
 
 	case KDSKBLED:
+		if (!suser())
+			return -EPERM;
 		if (arg & ~0x77)
 			return -EINVAL;
 		kbd->ledflagstate = (arg & 7);
@@ -559,6 +573,7 @@
 		return 0;
 
 	case KDSETLED:
+		/* OK, I'll let ordinary users run blinkenlights - zblaxell */
 		setledstate(kbd, arg);
 		return 0;
 
@@ -567,6 +582,8 @@
 		struct vt_mode *vtmode = (struct vt_mode *)arg;
 		char mode;
 
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
 		if (i)
 			return i;
@@ -642,6 +659,8 @@
 	 * to preserve sanity).
 	 */
 	case VT_ACTIVATE:
+		if (!suser())
+			return -EPERM;
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		arg--;
@@ -655,6 +674,8 @@
 	 * wait until the specified VT has been activated
 	 */
 	case VT_WAITACTIVE:
+		if (!suser())
+			return -EPERM;
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		arg--;
@@ -676,6 +697,8 @@
 	 *	2:	completed switch-to OK
 	 */
 	case VT_RELDISP:
+		if (!suser())
+			return -EPERM;
 		if (vt_cons[console]->vt_mode.mode != VT_PROCESS)
 			return -EINVAL;
 
@@ -724,6 +747,8 @@
 	  * Disallocate memory associated to VT (but leave VT1)
 	  */
 	 case VT_DISALLOCATE:
+		if (!suser())
+			return -EPERM;
 		if (arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		if (arg == 0) {
@@ -751,6 +776,8 @@
 	{
 		struct vt_sizes *vtsizes = (struct vt_sizes *) arg;
 		ushort ll,cc;
+		if (!suser())
+			return -EPERM;
 		i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
 		if (i)
 			return i;
@@ -760,6 +787,8 @@
 	}
 
 	case PIO_FONT:
+		if (!suser())
+			return -EPERM;
 		return con_set_font((char *)arg);
 		/* con_set_font() defined in console.c */
 
@@ -768,6 +797,8 @@
 		/* con_get_font() defined in console.c */
 
 	case PIO_SCRNMAP:
+		if (!suser())
+			return -EPERM;
 		return con_set_trans((char *)arg);
 		/* con_set_trans() defined in console.c */
 
diff -u --recursive --new-file v1.1.73/linux/drivers/net/dummy.c linux/drivers/net/dummy.c
--- v1.1.73/linux/drivers/net/dummy.c	Mon Nov 28 14:21:44 1994
+++ linux/drivers/net/dummy.c	Tue Dec 20 09:58:46 1994
@@ -75,6 +75,7 @@
 
 	/* Fill in the fields of the device structure with ethernet-generic values. */
 	ether_setup(dev);
+	dev->flags |= IFF_NOARP;
 
 	return 0;
 }
diff -u --recursive --new-file v1.1.73/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.1.73/linux/drivers/net/eexpress.c	Tue Dec  6 17:37:06 1994
+++ linux/drivers/net/eexpress.c	Thu Dec 22 09:04:09 1994
@@ -686,6 +686,8 @@
 static void
 set_multicast_list(struct device *dev, int num_addrs, void *addrs)
 {
+/* This doesn't work yet */
+#if 0
 	short ioaddr = dev->base_addr;
 	if (num_addrs < 0) {
 		/* Not written yet, this requires expanding the init_words config
@@ -702,6 +704,7 @@
 		   cmd. */
 		outw(99, ioaddr);		/* Disable promiscuous mode, use normal mode */
 	}
+#endif
 }
 
 /* The horrible routine to read a word from the serial EEPROM. */
diff -u --recursive --new-file v1.1.73/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.1.73/linux/drivers/net/plip.c	Tue Dec  6 17:37:06 1994
+++ linux/drivers/net/plip.c	Fri Dec 16 13:56:43 1994
@@ -1,3 +1,4 @@
+/* $Id: plip.c,v 1.7 1994/12/16 06:20:02 gniibe Exp $ */
 /* plip.c: A parallel port "network" driver for linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -31,7 +32,7 @@
 #ifdef MODULE
     "MODULAR "    
 #endif    
-    "PLIP.014 gniibe@mri.co.jp\n";
+    "PLIP $Revision: 1.7 $ gniibe@mri.co.jp\n";
 
 #include <linux/config.h>
 
@@ -110,9 +111,9 @@
 /* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
 #define PLIP_NIBBLE_WAIT        5000
 
-#define PAR_DATA(dev)		(dev->base_addr+0)
-#define PAR_STATUS(dev)		(dev->base_addr+1)
-#define PAR_CONTROL(dev)	(dev->base_addr+2)
+#define PAR_DATA(dev)		((dev)->base_addr+0)
+#define PAR_STATUS(dev)		((dev)->base_addr+1)
+#define PAR_CONTROL(dev)	((dev)->base_addr+2)
 
 /* Index to functions, as function prototypes. */
 static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
@@ -226,6 +227,8 @@
 	}
     }
 
+    snarf_region(PAR_DATA(dev), 3);
+
     /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
 
@@ -297,6 +300,8 @@
 	case PLIP_CN_RECEIVE:
 	    sti();
 	    disable_irq(dev->irq);
+	    outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
+	    outb(0x01, PAR_DATA(dev));   /* send ACK */
 	    dev->interrupt = 0;
 	    result = plip_receive_packet(dev);
 	    if (result == 0) { /* success */
@@ -307,9 +312,11 @@
 		netif_rx(skb);
 		if (snd->state != PLIP_PK_DONE) {
 		    nl->connection = PLIP_CN_SEND;
+		    outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
 		    enable_irq(dev->irq);
 		} else {
 		    nl->connection = PLIP_CN_NONE;
+		    outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
 		    enable_irq(dev->irq);
 		    return;
 		}
@@ -340,6 +347,7 @@
 		snd->skb = NULL;
 		nl->connection = PLIP_CN_CLOSING;
 		queue_task(&nl->deferred, &tq_timer);
+		outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
 		enable_irq(dev->irq);
 		return;
 	    } else
@@ -359,6 +367,7 @@
 	    if (result == 0) {
 		nl->connection = PLIP_CN_NONE;
 		dev->tbusy = 0;
+		outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
 		enable_irq(dev->irq);
 		return;
 	    } else {
@@ -392,6 +401,7 @@
 		dev_kfree_skb(rcv->skb, FREE_WRITE);
 	}
 	disable_irq(dev->irq);
+	outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
 	dev->tbusy = 1;
 	nl->connection = PLIP_CN_ERROR;
 	outb(0x00, PAR_DATA(dev));
@@ -542,6 +552,7 @@
     cli();
     dev->tbusy = 0;
     sti();
+    outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
     enable_irq(dev->irq);
 }
 
@@ -702,7 +713,6 @@
 	    printk("plip: spurious interrupt\n");
 	return;
     }
-    outb(0x01, PAR_DATA(dev));   /* send ACK */
     dev->interrupt = 1;
     if (net_debug > 3)
 	printk("%s: interrupt.\n", dev->name);
@@ -809,6 +819,7 @@
 	    c0 = inb(PAR_STATUS(dev));
 	    if (c0 & 0x08) {
 		disable_irq(dev->irq);
+		outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
 		if (net_debug > 3)
 		    printk("+");
 		/* OK, connection established! */
@@ -952,16 +963,19 @@
     else {
 	if (dev_plip0.priv) {
 	    unregister_netdev(&dev_plip0);
+	    release_region(PAR_DATA(&dev_plip0), 3);
 	    kfree_s(dev_plip0.priv, sizeof(struct net_local));
 	    dev_plip0.priv = NULL;
 	}
 	if (dev_plip1.priv) {
 	    unregister_netdev(&dev_plip1);
+	    release_region(PAR_DATA(&dev_plip1), 3);
 	    kfree_s(dev_plip1.priv, sizeof(struct net_local));
 	    dev_plip1.priv = NULL;
 	}
 	if (dev_plip2.priv) {
 	    unregister_netdev(&dev_plip2);
+	    release_region(PAR_DATA(&dev_plip2), 3);
 	    kfree_s(dev_plip2.priv, sizeof(struct net_local));
 	    dev_plip2.priv = NULL;
 	}
diff -u --recursive --new-file v1.1.73/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v1.1.73/linux/drivers/net/wd.c	Sun Oct  9 19:28:50 1994
+++ linux/drivers/net/wd.c	Thu Dec 22 13:37:13 1994
@@ -262,6 +262,13 @@
 	dev->stop = &wd_close_card;
 	NS8390_init(dev, 0);
 
+#if 1
+	/* Enable interrupt generation on softconfig cards -- M.U */
+	/* .. but possibly potentially unsafe - Donald */
+	if (inb(ioaddr+14) & 0x20)
+		outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
 	return 0;
 }
 
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.1.73/linux/drivers/scsi/README.st	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/README.st	Mon Dec 19 17:33:44 1994
@@ -1,5 +1,5 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Wed Nov 30 20:34:03 1994 by root@kai.home
+Last modified: Sat Dec 17 13:38:43 1994 by root@kai.home
 
 BASICS
 
@@ -11,8 +11,9 @@
 device number) are implemented.
 
 By default the driver writes one filemark when the device is closed after
-writing. Two filemarks can be optionally written. In both cases end
-of data is signified by returning zero bytes for two consecutive reads.
+writing and the last operation has been a write. Two filemarks can be
+optionally written. In both cases end of data is signified by
+returning zero bytes for two consecutive reads.
 
 BUFFERING
 
@@ -129,7 +130,8 @@
         mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
         MT_ST_DENSITY_SHIFT).
 	The GMT_xxx status bits reflect the drive status. GMT_DR_OPEN
-	is set if there is no tape in the drive.
+	is set if there is no tape in the drive. GMT_EOD means either
+	end of recorded data or end of tape. GMT_EOT means end of tape.
 
 
 MISCELLANEOUS COMPILE OPTIONS
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v1.1.73/linux/drivers/scsi/eata.c	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/eata.c	Tue Dec 20 10:23:14 1994
@@ -1,5 +1,16 @@
 /*
- *      eata.c - Low-level SCSI driver for EISA EATA SCSI controllers.
+ *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ *      17 Dec 1994 rev. 1.11 for linux 1.1.74
+ *          Use the scsicam_bios_param routine. This allows an easy
+ *          migration path from disk partition tables created using 
+ *          different SCSI drivers and non optimal disk geometry.
+ *
+ *      15 Dec 1994 rev. 1.10 for linux 1.1.74
+ *          Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ *          The host->block flag is set for all the detected ISA boards.
+ *          The detect routine no longer enforces LEVEL triggering
+ *          for EISA boards, it just prints a warning message.
  *
  *      30 Nov 1994 rev. 1.09 for linux 1.1.68
  *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
@@ -14,7 +25,7 @@
  *
  *
  *          This driver is based on the CAM (Common Access Method Committee)
- *          EATA (Enhanced AT Bus Attachment) rev. 2.0A.
+ *          EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
  *
  *      Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
  *
@@ -22,6 +33,21 @@
 
 /*
  *
+ *  Here is a brief description of the DPT SCSI host adapters.
+ *  All these boards provide an EATA/DMA compatible programming interface
+ *  and are fully supported by this driver:
+ *
+ *  PM2011B/9X -  Entry Level ISA
+ *  PM2021A/9X -  High Performance ISA
+ *  PM2012A       Old EISA
+ *  PM2012B       Old EISA
+ *  PM2022A/9X -  Entry Level EISA
+ *  PM2122A/9X -  High Performance EISA
+ *  PM2322A/9X -  Extra High Performance EISA
+ *
+ *  The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ *  supported by this driver.
+ *
  *  This code has been tested with up to 3 Distributed Processing Technology 
  *  PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) eisa controllers,
  *  no on board cache and no RAID option. 
@@ -31,9 +57,11 @@
  *  All boards should be configured at the same IRQ level.
  *  Multiple IRQ configurations are supported too.
  *  Boards can be located in any eisa slot (1-15) and are named EATA0, 
- *  EATA1,... in increasing eisa slot number.
- *  In order to detect the boards, the IRQ must be _level_ triggered 
- *  (not _edge_ triggered).
+ *  EATA1,... in increasing eisa slot number. ISA boards are detected
+ *  after the eisa slot probes.
+ *
+ *  The IRQ for EISA boards should be _level_ triggered (not _edge_ triggered).
+ *  This is a requirement in order to support multiple boards on the same IRQ.
  *
  *  Other eisa configuration parameters are:
  *
@@ -41,6 +69,8 @@
  *  COMMAND TIMEOUT   : ENABLED
  *  CACHE             : DISABLED
  *
+ *  In order to support multiple ISA boards in a reliable way,
+ *  the driver sets host->block = TRUE for all ISA boards.
  */
 
 #include <linux/string.h>
@@ -53,18 +83,22 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <asm/dma.h>
 #include <asm/irq.h>
 #include "linux/in.h"
 #include "eata.h"
 
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
-#define NO_SINGLE_HOST_OPERATIONS
+/* Subversion values */
+#define ISA  0
+#define ESA 1
+
+#undef  DEBUG_DETECT
+#undef  DEBUG_INTERRUPT
+#undef  DEBUG_STATISTICS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
-#define MAX_BOARDS 15
+#define MAX_BOARDS 18
 #define MAX_MAILBOXES 64
 #define MAX_SGLIST 64
 #define MAX_CMD_PER_LUN 2
@@ -88,6 +122,8 @@
 #define REG_LM          3
 #define REG_MID         4
 #define REG_MSB         5
+#define REG_REGION      9
+#define EISA_RANGE      0xf000
 #define BSY_ASSERTED      0x80
 #define DRQ_ASSERTED      0x08
 #define ABSY_ASSERTED     0x01
@@ -130,7 +166,7 @@
    unchar     irq:4,    /* Interrupt Request assigned to this controller */
            irq_tr:1,    /* 0 for edge triggered, 1 for level triggered */
            second:1,    /* 1 if this is a secondary (not primary) controller */
-             drqx:2;    /* DRQ Index (0=DRQ0, 1=DRQ7, 2=DRQ6, 3=DRQ5) */
+             drqx:2;    /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
    unchar  sync;        /* 1 if scsi target id 7...0 is running sync scsi */
    ushort ipad[250];
    };
@@ -157,7 +193,7 @@
    char mess[12];
    };
 
-/* Command packet structure */
+/* MailBox SCSI Command Packet */
 struct mscp {
    unchar  sreset:1,     /* SCSI Bus Reset Signal should be asserted */
            interp:1,     /* The controller interprets cp, not the target */ 
@@ -204,6 +240,7 @@
    int in_reset;                        /* True if board is doing a reset */
    int target_time_out[MAX_TARGET];     /* N. of timeout errors on target */
    int target_reset[MAX_TARGET];        /* If TRUE redo operation on target */
+   unsigned char subversion;            /* Bus type, either ISA or ESA */
    struct mssp sp[MAX_MAILBOXES];       /* Returned status for this board */
    };
 
@@ -257,14 +294,22 @@
    return FALSE;
 }
 
-static inline int port_detect (ushort *port_base, unsigned int j, 
-                               Scsi_Host_Template * tpnt) {
+static inline int port_detect(ushort *port_base, unsigned int j, 
+                              Scsi_Host_Template * tpnt) {
+   unsigned char irq, dma_channel, subversion;
    struct eata_info info;
    struct eata_config config;
+   char *board_status;
+
+   /* Allowed DMA channels for ISA (0 indicates reserved) */
+   unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
    char name[16];
 
    sprintf(name, "%s%d", driver_name, j);
 
+   if(check_region(*port_base, REG_REGION)) return FALSE;
+
    if (do_dma(*port_base, 0, READ_CONFIG_PIO)) return FALSE;
 
    /* Read the info structure */
@@ -274,20 +319,49 @@
    /* check the controller "EATA" signature */
    if (info.sign != EATA_SIGNATURE) return FALSE;
 
-   if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
-      printk("%s: unusable board found, detaching.\n", name);
-      return FALSE;
+   irq = info.irq;
+
+   if (*port_base & EISA_RANGE) {
+
+      if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
+         printk("%s: unusable EISA board found, detaching.\n", name);
+         return FALSE;
+         }
+
+      subversion = ESA;
+      dma_channel = 0;
       }
+   else {
+
+      if (!info.haaval || info.ata || !info.drqvld || !info.dmasup) {
+         printk("%s: unusable ISA board found, detaching.\n", name);
+         return FALSE;
+         }
+
+      subversion = ISA;
+      dma_channel = dma_channel_table[3 - info.drqx];
+      }
+
+   if (subversion == ESA && !info.irq_tr)
+      printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+             name, irq);
 
-   if (!info.irq_tr) {
-      printk("%s: IRQ must be level triggered, detaching.\n", name);
+   if (info.second)
+      board_status = "Sec.";
+   else
+      board_status = "Prim.";
+
+   /* Board detected, allocate its IRQ if not already done */
+   if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
+       (irq, eata_interrupt_handler, SA_INTERRUPT, driver_name))) {
+      printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
-   /* EATA board detected, allocate its IRQ if not already done */
-   if ((info.irq >= MAX_IRQ) || ((irqlist[info.irq] == NO_IRQ) && request_irq
-       (info.irq, eata_interrupt_handler, SA_INTERRUPT, driver_name))) {
-      printk("%s: unable to allocate IRQ %u, detaching.\n", name, info.irq);
+   if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+      printk("%s: unable to allocate DMA channel %u, detaching.\n",
+             name, dma_channel);
+      free_irq(irq);
       return FALSE;
       }
 
@@ -303,21 +377,38 @@
 
    sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
    sh[j]->io_port = *port_base;
-   sh[j]->dma_channel = 0;
-   sh[j]->irq = info.irq;
+   sh[j]->dma_channel = dma_channel;
+   sh[j]->irq = irq;
    sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
    sh[j]->this_id = (ushort) ntohl(info.host_addr);
    sh[j]->can_queue = (ushort) ntohs(info.queue_size);
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
-   sh[j]->unchecked_isa_dma = FALSE;
+
+   /* Register the I/O space that we use */
+   snarf_region(sh[j]->io_port, REG_REGION);
+
    memset(HD(j), 0, sizeof(struct hostdata));
+   HD(j)->subversion = subversion;
    HD(j)->board_number = j;
-   irqlist[info.irq] = j;
+   irqlist[irq] = j;
+
+   if (HD(j)->subversion == ESA)
+      sh[j]->unchecked_isa_dma = FALSE;
+   else {
+      sh[j]->block = sh[j];
+      sh[j]->unchecked_isa_dma = TRUE;
+      disable_dma(dma_channel);
+      clear_dma_ff(dma_channel);
+      set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+      enable_dma(dma_channel);
+      }
+
    strcpy(BN(j), name);
 
-   printk("%s: SCSI ID %d, PORT 0x%03x, IRQ %u, SG %d, "\
-          "Mbox %d, CmdLun %d.\n", BN(j), sh[j]->this_id, 
-           sh[j]->io_port, sh[j]->irq, sh[j]->sg_tablesize, 
+   printk("%s: %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
+          "Mbox %d, CmdLun %d.\n", BN(j), board_status, sh[j]->this_id, 
+           sh[j]->io_port, sh[j]->irq, 
+           sh[j]->dma_channel, sh[j]->sg_tablesize, 
            sh[j]->can_queue, sh[j]->cmd_per_lun);
 
    /* DPT PM2012 does not allow to detect sg_tablesize correctly */
@@ -344,12 +435,13 @@
 int eata_detect (Scsi_Host_Template * tpnt) {
    unsigned int j = 0, k, flags;
 
-   ushort eisa_io_port[] = { 
+   ushort io_port[] = { 
       0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
-      0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 0x0
+      0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 
+      0x330,  0x230,  0x1f0,  0x170,  0x0
       };
 
-   ushort *port_base = eisa_io_port;
+   ushort *port_base = io_port;
 
    save_flags(flags);
    cli();
@@ -363,21 +455,11 @@
 
    while (*port_base) {
 
-      if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+      if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
 
       port_base++;
       }
 
-#if defined (SINGLE_HOST_OPERATIONS)
-   /* Create a circular linked list among the detected boards. */
-   if (j > 1) {
-
-      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
-
-      sh[j - 1]->block = sh[0];
-      }
-#endif
-
    restore_flags(flags);
    return j;
 }
@@ -388,7 +470,7 @@
 
    sgpnt = (struct scatterlist *) SCpnt->request_buffer;
 
-   for(k = 0; k < SCpnt->use_sg; k++) {
+   for (k = 0; k < SCpnt->use_sg; k++) {
       cpp->sglist[k].address = htonl((unsigned int) sgpnt[k].address);
       cpp->sglist[k].num_bytes = htonl((unsigned int) sgpnt[k].length);
       }
@@ -413,7 +495,7 @@
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
 
-   for(k = 0; k < sh[j]->can_queue; k++, i++) {
+   for (k = 0; k < sh[j]->can_queue; k++, i++) {
 
       if (i >= sh[j]->can_queue) i = 0;
 
@@ -664,30 +746,6 @@
       }
 }
 
-int eata_bios_param (Disk * disk, int dev, int *ip) {
-   int size = disk->capacity;
-
-   if(size < 0x200000) {           /* < 1Gbyte */
-      ip[0]=64;
-      ip[1]=32;
-      }
-   else if (size < 0x400000) {     /* < 2Gbyte */
-      ip[0]=65;
-      ip[1]=63;
-      }
-   else if(size < 0x800000) {      /* < 4Gbyte */
-      ip[0]=128;
-      ip[1]=63;
-      }
-   else {                          /* ok up to 8Gbyte */
-      ip[0]=255;
-      ip[1]=63;
-      }    
-
-   ip[2] = size / (ip[0] * ip[1]);
-   return 0;
-}
-
 static void eata_interrupt_handler(int irq) {
    Scsi_Cmnd *SCpnt;
    unsigned int i, j, k, flags, status, loops, total_loops = 0;
@@ -725,7 +783,7 @@
          inb(sh[j]->io_port + REG_STATUS);
    
          /* Service all mailboxes of this board */
-         for(i = 0; i < sh[j]->can_queue; i++) {
+         for (i = 0; i < sh[j]->can_queue; i++) {
             spp = &HD(j)->sp[i];
    
             /* Check if this mailbox has completed the operation */
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v1.1.73/linux/drivers/scsi/eata.h	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/eata.h	Tue Dec 20 10:23:14 1994
@@ -1,21 +1,22 @@
 /*
- *	eata.h - used by low-level scsi driver for EISA EATA controllers.
+ *	eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
  *
  */
-#ifndef _EISA_EATA_H
-#define _EISA_EATA_H
+#ifndef _EATA_H
+#define _EATA_H
 
-#define EATA_VERSION "1.09.01"
+#include <linux/scsicam.h>
+
+#define EATA_VERSION "1.11.00"
 
 int eata_detect(Scsi_Host_Template *);
 int eata_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int eata_abort(Scsi_Cmnd *);
 int eata_reset(Scsi_Cmnd *);
-int eata_bios_param(Disk *, int, int*);
 
 #define EATA {  NULL, /* Ptr for modules */                    \
                 NULL, /* usage count for modules */	       \
-                "EISA EATA 2.0A rev. " EATA_VERSION " by "     \
+                "EATA/DMA 2.0A rev. " EATA_VERSION " by "      \
                 "Dario_Ballabio@milano.europe.dg.com.",        \
                 eata_detect,	        	               \
                 NULL, /* Release */     	               \
@@ -25,13 +26,13 @@
 		eata_abort,				       \
 		eata_reset,				       \
 	        NULL,		                               \
-		eata_bios_param,   			       \
+		scsicam_bios_param,   			       \
 		0,   /* can_queue, reset by detect */          \
                 7,   /* this_id, reset by detect */            \
                 0,   /* sg_tablesize, reset by detect */       \
                 0,   /* cmd_per_lun, reset by detect */        \
 		0,   /* number of boards present */            \
-                0,   /* unchecked isa dma */                   \
+                0,   /* unchecked isa dma, reset by detect */  \
                 ENABLE_CLUSTERING                              \
                 }
 #endif
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.1.73/linux/drivers/scsi/hosts.c	Thu Dec 15 11:01:53 1994
+++ linux/drivers/scsi/hosts.c	Tue Dec 20 10:23:14 1994
@@ -313,6 +313,30 @@
 	}
 	printk ("scsi : %d hosts.\n", count);
 
+      {
+      int block_count = 0, index;
+      struct Scsi_Host * sh[128], * shpnt;
+
+         for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+            if (shpnt->block) sh[block_count++] = shpnt;
+      
+         if (block_count == 1) sh[0]->block = NULL;
+
+         else if (block_count > 1) {
+
+            for(index = 0; index < block_count - 1; index++) {
+               sh[index]->block = sh[index + 1];
+               printk("scsi%d : added to blocked host list.\n", 
+                      sh[index]->host_no);
+               }
+           
+            sh[block_count - 1]->block = sh[0];
+            printk("scsi%d : added to blocked host list.\n", 
+                   sh[index]->host_no);
+            }
+
+      }
+
 	/* Now attach the high level drivers */
 #ifdef CONFIG_BLK_DEV_SD
 	scsi_register_device(&sd_template);
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v1.1.73/linux/drivers/scsi/hosts.h	Mon Dec  5 18:47:55 1994
+++ linux/drivers/scsi/hosts.h	Tue Dec 20 10:23:14 1994
@@ -228,8 +228,6 @@
 	be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
 */
 
-#define SCSI_HOST_BLOCK 0x80
-
 struct Scsi_Host
 	{
 		struct Scsi_Host * next;
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.1.73/linux/drivers/scsi/scsi.c	Thu Dec 15 11:01:54 1994
+++ linux/drivers/scsi/scsi.c	Thu Dec 22 06:54:41 1994
@@ -46,6 +46,9 @@
 static int time_start;
 static int time_elapsed;
 static int scsi_maybe_deadlocked = 0;
+static int max_active = 0;
+static struct Scsi_Host * host_active = NULL;
+static struct Scsi_Host * host_next = NULL;
 
 #define MAX_SCSI_DEVICE_CODE 10
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
@@ -160,6 +163,8 @@
    {"QUANTUM","LPS525S","3110"},/* Locks sometimes if polled for lun != 0 */
    {"QUANTUM","PD1225S","3110"},/* Locks sometimes if polled for lun != 0 */
    {"MEDIAVIS","CDR-H93MV","1.31"},  /* Locks up if polled for lun != 0 */
+   {"SANKYO", "CP525","6.64"},  /* causes failed REQ SENSE, extra reset */
+   {"HP", "C1750A", "3226"},    /* scanjet iic */
    {NULL, NULL, NULL}};	
 
 static int blacklisted(unsigned char * response_data){
@@ -190,6 +195,21 @@
 
 volatile int in_scan_scsis = 0;
 static int the_result;
+
+static void restart_all(void) {
+ /*
+  * If we might have deadlocked, get things started again. Only
+  * for block devices. Character devices should never deadlock.
+  */
+  struct Scsi_Device_Template * sdtpnt;
+
+  for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+
+     if (sdtpnt->blk && blk_dev[sdtpnt->major].request_fn)
+        (*blk_dev[sdtpnt->major].request_fn)();
+
+  }
+
 static void scan_scsis_done (Scsi_Cmnd * SCpnt)
 	{
 	
@@ -375,6 +395,8 @@
 		case TYPE_TAPE :
 		case TYPE_DISK :
 		case TYPE_MOD :
+		case TYPE_PROCESSOR :
+		case TYPE_SCANNER :
 		  SDpnt->writeable = 1;
 		  break;
 		case TYPE_WORM :
@@ -589,8 +611,17 @@
 
   if (!SCpnt) return NULL;
 
-  if (device->host->can_queue
-      && device->host->host_busy >= device->host->can_queue) return NULL;
+  if (device->host->can_queue &&
+       ((device->host->block && host_active && device->host != host_active) || 
+        (device->host->block && host_next && device->host != host_next) ||
+        device->host->host_busy >= device->host->can_queue)) {
+
+     if (device->host->block && !host_next && host_active 
+                             && device->host != host_active) 
+        host_next = device->host;
+
+     return NULL;
+     }
 
   if (req) {
     memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -665,11 +696,18 @@
   
   host = device->host;
   
-  if (intr_count && device->host->can_queue
-      && device->host->host_busy >= device->host->can_queue) {
-    scsi_maybe_deadlocked = 1;
-    return NULL;
-  }
+  if (intr_count && host->can_queue &&
+                      ((host->block && host_active && host != host_active) ||
+                       (host->block && host_next && host != host_next) ||
+                       host->host_busy >= host->can_queue)) {
+
+     scsi_maybe_deadlocked = 1;
+
+     if (host->block && !host_next && host_active && host != host_active) 
+        host_next = host;
+
+     return NULL;
+     }
 
   while (1==1){
     SCpnt = host->host_queue;
@@ -891,25 +929,49 @@
 
 	SCpnt->pid = scsi_pid++; 
 
-	while (1==1){
-	  cli();
-	  if (host->can_queue
-	      && host->host_busy >= host->can_queue)
-	    {
-	      sti();
-	      SCSI_SLEEP(&host->host_wait, 
-			 (host->host_busy >= host->can_queue));
-	    } else {
-	      host->host_busy++;
-	      if (host->block) {
-		struct Scsi_Host * block;
-		for(block = host->block; block != host; block = block->block)
-		  block->host_busy |= SCSI_HOST_BLOCK;
-	      }
-	      sti();
-	      break;
-	    };
-      };
+        for(;;) {
+
+          cli();
+
+	  if (host->can_queue &&
+                ((host->block && host_active && host != host_active) ||
+	         (host->block && host_next && host != host_next) ||
+	         host->host_busy >= host->can_queue)) {
+
+             if (intr_count) 
+                panic("scsi: queueing to inactive host while in interrupt.\n");
+
+             if (host->block && !host_next && host_active 
+                                           && host != host_active) 
+                host_next = host;
+
+	     sti();
+	     SCSI_SLEEP(&host->host_wait, 
+                       ((host->block && host_active && host != host_active) ||
+                        (host->block && host_next && host != host_next) ||
+	                host->host_busy >= host->can_queue));
+	     } 
+
+          else {
+	     host->host_busy++;
+
+	     if (host->block) {
+
+	        if (!host_active) {
+                   max_active = 0;
+                   host_active = host;
+                   host_next = NULL;
+                   }
+                else if (max_active > 7 && !host_next) 
+                   host_next = host->block;
+
+                max_active++;
+                }
+
+	     sti();
+	     break;
+	     }
+          }
 /*
 	Our own function scsi_done (which marks the host as not busy, disables 
 	the timeout counter, etc) will be called by us or by the 
@@ -1344,6 +1406,9 @@
 			/* fall through to REDO */
 
 		case REDO:
+
+                        if (host->block) scsi_maybe_deadlocked = 1;
+
 			if (SCpnt->flags & WAS_SENSE)			
 				scsi_request_sense(SCpnt); 	
 			else	
@@ -1362,44 +1427,39 @@
 			INTERNAL_ERROR;
 		}
 
-	if (status == FINISHED) 
-	  {
+
+	if (status == FINISHED) {
 #ifdef DEBUG
-	    	printk("Calling done function - at address %08x\n", SCpnt->done);
+	   printk("Calling done function - at address %08x\n", SCpnt->done);
 #endif
-		host->host_busy--; /* Indicate that we are free */
-		if (host->host_busy == 0 && host->block) {
-		  struct Scsi_Host * block;
-		  /*
-		   * Now remove the locks for all of the related hosts.
-		   */
-		  for(block = host->block; block != host; block = block->block)
-		      block->host_busy &= ~SCSI_HOST_BLOCK;
-		  /*
-		   * Now wake them up.  We do this in two separate stages to prevent
-		   * race conditions.
-		   */
-		  for(block = host->block; block != host; block = block->block)
-		      wake_up(&block->host_wait);
-		  /*
-		   * If we might have deadlocked, get things started again.  Only
-		   * for block devices - character devices should never deadlock.
-		   */
-		  if (scsi_maybe_deadlocked) {
-		    struct Scsi_Device_Template * sdtpnt;
-		    scsi_maybe_deadlocked = 0;
-		    for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
-		      if (sdtpnt->blk && blk_dev[sdtpnt->major].request_fn)
-			(*blk_dev[sdtpnt->major].request_fn)();
-		  }
-		}
-		wake_up(&host->host_wait);
-		SCpnt->result = result | ((exit & 0xff) << 24);
-		SCpnt->use_sg = SCpnt->old_use_sg;
-		SCpnt->cmd_len = SCpnt->old_cmd_len;
-		SCpnt->done (SCpnt);
-		}
+	   host->host_busy--; /* Indicate that we are free */
+
+	   if (host->block && host->host_busy == 0) {
+              struct Scsi_Host * block;
 
+              block = host_next;
+              host_active = NULL;
+
+              if (block) wake_up(&block->host_wait);
+
+              for (block = host->block; block != host; block = block->block)
+                 wake_up(&block->host_wait);
+
+              host_next = NULL;
+
+              if (scsi_maybe_deadlocked) {
+                 scsi_maybe_deadlocked = 0;
+                 restart_all();
+                 }
+
+              }
+
+           wake_up(&host->host_wait);
+	   SCpnt->result = result | ((exit & 0xff) << 24);
+	   SCpnt->use_sg = SCpnt->old_use_sg;
+	   SCpnt->cmd_len = SCpnt->old_cmd_len;
+   	   SCpnt->done (SCpnt);
+	   }
 
 #undef FINISHED
 #undef REDO
@@ -1561,13 +1621,26 @@
 			else
 				{
 				host->host_busy++;
+
+                                if (host->block) {
+                                   host_active = host;
+                                   host_next = NULL;
+                                   }
 	
 				sti();
 				temp = host->hostt->reset(SCpnt);
 				host->last_reset = jiffies;
 				host->host_busy--;
+
 				}
 
+                        if (host->block && host->host_busy == 0) {
+                           host_active = NULL;
+                           host_next = NULL;
+                           restart_all();
+                           }
+
+
 #ifdef DEBUG
 			printk("scsi reset function returned %d\n", temp);
 #endif
@@ -2283,7 +2356,7 @@
     for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
       {
 	/*  (0) 0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
-	printk("(%d) %d:%d:%d (%4.4x %d %d %d %d) (%d %d %x) (%d %d %d) %x %x %x\n",
+	printk("(%d) %d:%d:%d (%4.4x %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
 	       i++, SCpnt->host->host_no,
 	       SCpnt->target,
 	       SCpnt->lun,
@@ -2302,7 +2375,7 @@
 	       SCpnt->sense_buffer[2],
 	       SCpnt->result);
       };
-  printk("wait_for_request = %x\n", wait_for_request);
+  printk("wait_for_request = %p\n", wait_for_request);
   /* Now dump the request lists for each block device */
   printk("Dump of pending block device requests\n");
   for(i=0; i<MAX_BLKDEV; i++)
@@ -2312,7 +2385,7 @@
 	printk("%d: ", i);
 	req = blk_dev[i].current_request;
 	while(req) {
-	  printk("(%x %d %d %d %d) ",
+	  printk("(%x %d %ld %ld %ld) ",
 		 req->dev,
 		 req->cmd,
 		 req->sector,
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v1.1.73/linux/drivers/scsi/scsi.h	Mon Dec  5 18:47:55 1994
+++ linux/drivers/scsi/scsi.h	Thu Dec 22 06:54:41 1994
@@ -234,8 +234,10 @@
 
 #define TYPE_DISK	0x00
 #define TYPE_TAPE	0x01
+#define TYPE_PROCESSOR	0x03	/* HP scanners use this */
 #define TYPE_WORM	0x04	/* Treated as ROM by our system */
 #define TYPE_ROM	0x05
+#define TYPE_SCANNER	0x06
 #define TYPE_MOD	0x07  /* Magneto-optical disk - treated as TYPE_DISK */
 #define TYPE_NO_LUN	0x7f
 
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.1.73/linux/drivers/scsi/scsi_ioctl.c	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/scsi_ioctl.c	Tue Dec 20 10:23:14 1994
@@ -13,7 +13,7 @@
 #include "scsi_ioctl.h"
 
 #define MAX_RETRIES 5	
-#define MAX_TIMEOUT 200
+#define MAX_TIMEOUT 900
 #define MAX_BUF 4096
 
 #define max(a,b) (((a) > (b)) ? (a) : (b))
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.1.73/linux/drivers/scsi/st.c	Sat Dec  3 13:05:42 1994
+++ linux/drivers/scsi/st.c	Mon Dec 19 17:33:44 1994
@@ -5,13 +5,13 @@
   History:
   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
   Contribution and ideas from several people including (in alphabetical
-  order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
-  Eric Youngdale.
+  order) Klaus Ehrenfried, Wolfgang Denk, Andreas Koppenh"ofer, J"org Weule,
+  and Eric Youngdale.
 
   Copyright 1992, 1993, 1994 Kai Makisara
 		 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
 
-  Last modified: Wed Nov 30 21:09:53 1994 by root@kai.home
+  Last modified: Sun Dec 18 10:15:33 1994 by root@kai.home
 */
 
 #include <linux/fs.h>
@@ -74,7 +74,7 @@
 #define MAX_READY_RETRIES 5
 #define NO_TAPE  NOT_READY
 
-#define ST_TIMEOUT 27000
+#define ST_TIMEOUT 90000
 #define ST_LONG_TIMEOUT 200000
 
 static int st_nbr_buffers;
@@ -249,7 +249,8 @@
   SCpnt->request.dev = -1;
   if ((STp->buffer)->last_result != 0) {
     printk("st%d: Backing over filemark failed.\n", dev);
-    (STp->mt_status)->mt_fileno += 1;
+    if ((STp->mt_status)->mt_fileno >= 0)
+      (STp->mt_status)->mt_fileno += 1;
     (STp->mt_status)->mt_blkno = 0;
   }
 
@@ -363,6 +364,7 @@
 	STp->block_size;
   (STp->buffer)->buffer_bytes = 0;
   (STp->buffer)->read_pointer = 0;
+  STp->drv_block -= backspace;
   result = 0;
   if (!seek_next) {
     if ((STp->eof == ST_FM) && !STp->eof_hit) {
@@ -637,7 +639,8 @@
 	}
 	else {
 	  SCpnt->request.dev = -1;  /* Mark as not busy */
-          (STp->mt_status)->mt_fileno++ ;
+	  if ((STp->mt_status)->mt_fileno >= 0)
+	      (STp->mt_status)->mt_fileno++ ;
 	  STp->drv_block = 0;
 	  if (STp->two_fm)
 	    back_over_eof(dev);
@@ -677,6 +680,7 @@
     int dev;
     int total, do_count, blks, retval, transfer;
     int write_threshold;
+    int doing_write = 0;
     static unsigned char cmd[10];
     char *b_point;
     Scsi_Cmnd * SCpnt;
@@ -757,6 +761,7 @@
 	  (STp->block_size != 0 &&
 	   (STp->buffer)->buffer_bytes + count > write_threshold))
     {
+      doing_write = 1;
       if (STp->block_size == 0)
 	do_count = count;
       else {
@@ -864,7 +869,7 @@
       count = 0;
     }
 
-    if ((STp->buffer)->last_result_fatal != 0) {
+    if (doing_write && (STp->buffer)->last_result_fatal != 0) {
       SCpnt->request.dev = -1;
       return (STp->buffer)->last_result_fatal;
     }
@@ -1123,7 +1128,8 @@
 	  STp->drv_block = 0;
 	  if (STp->moves_after_eof > 1)
 	    STp->moves_after_eof = 0;
-	  (STp->mt_status)->mt_fileno++;
+	  if ((STp->mt_status)->mt_fileno >= 0)
+	    (STp->mt_status)->mt_fileno++;
 	}
 	if (total == 0 && STp->eof == ST_EOM_OK)
 	  return (-EIO);  /* ST_EOM_ERROR not used in read */
@@ -1223,7 +1229,8 @@
 	 printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
 		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
-       fileno += arg;
+       if (fileno >= 0)
+	 fileno += arg;
        blkno = 0;
        at_sm &= (arg != 0);
        break; 
@@ -1243,11 +1250,12 @@
 	 printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
        }
 #endif
-       fileno -= arg;
+       if (fileno >= 0)
+	 fileno -= arg;
        blkno = (-1);  /* We can't know the block number */
        at_sm &= (arg != 0);
        break; 
-      case MTFSR:
+     case MTFSR:
        cmd[0] = SPACE;
        cmd[1] = 0x00; /* Space Blocks */
        cmd[2] = (arg >> 16);
@@ -1281,7 +1289,7 @@
 	 blkno -= arg;
        at_sm &= (arg != 0);
        break; 
-      case MTFSS:
+     case MTFSS:
        cmd[0] = SPACE;
        cmd[1] = 0x04; /* Space Setmarks */
        cmd[2] = (arg >> 16);
@@ -1338,7 +1346,8 @@
 		  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
        }
 #endif
-       fileno += arg;
+       if (fileno >= 0)
+	 fileno += arg;
        blkno = 0;
        at_sm = (cmd_in == MTWSM);
        break; 
@@ -1390,6 +1399,8 @@
        /* space to the end of tape */
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
        fileno = (STp->mt_status)->mt_fileno ;
+       if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+	 return 0;
        /* The next lines would hide the number of spaced FileMarks
           That's why I inserted the previous lines. I had no luck
 	  with detecting EOM with FSF, so we go now to EOM.
@@ -1400,7 +1411,7 @@
        if (debugging)
 	 printk("st%d: Spacing to end of recorded medium.\n", dev);
 #endif
-       blkno = (-1);
+       blkno = 0;
        at_sm = 0;
        break; 
      case MTERASE:
@@ -1408,6 +1419,12 @@
 	 return (-EACCES);
        cmd[0] = ERASE;
        cmd[1] = 1;  /* To the end of tape */
+#ifdef ST_NOWAIT
+       cmd[1] |= 2;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#else
+       timeout = ST_LONG_TIMEOUT * 8;
+#endif
 #ifdef DEBUG
        if (debugging)
 	 printk("st%d: Erasing tape.\n", dev);
@@ -1509,6 +1526,10 @@
 
    SCpnt->request.dev = -1;  /* Mark as not busy */
 
+   if (cmd_in == MTFSF)
+     STp->moves_after_eof = 0;
+   else
+     STp->moves_after_eof = 1;
    if (!ioctl_result) {
      if (cmd_in != MTSEEK) {
        STp->drv_block = blkno;
@@ -1519,10 +1540,6 @@
        STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
        STp->at_sm = 0;
      }
-     if (cmd_in == MTFSF)
-       STp->moves_after_eof = 0;
-     else
-       STp->moves_after_eof = 1;
      if (cmd_in == MTBSFM)
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
      else if (cmd_in == MTFSFM)
@@ -1565,17 +1582,24 @@
           (SCpnt->sense_buffer[4] << 16) +
           (SCpnt->sense_buffer[5] << 8) +
           SCpnt->sense_buffer[6] );
-     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) )
-       (STp->mt_status)->mt_fileno = fileno - undone ;
-     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) )
+     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+       if (fileno >= 0)
+	 (STp->mt_status)->mt_fileno = fileno - undone ;
+       else
+	 (STp->mt_status)->mt_fileno = fileno;
+       STp->drv_block = 0;
+     }
+     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
        (STp->mt_status)->mt_fileno = fileno + undone ;
+       STp->drv_block = 0;
+     }
      else if (cmd_in == MTFSR) {
        if (blkno >= undone)
 	 STp->drv_block = blkno - undone;
        else
 	 STp->drv_block = (-1);
      }
-     else if (cmd_in == MTBSR && blkno >= 0) {
+     else if (cmd_in == MTBSR) {
        if (blkno >= 0)
 	 STp->drv_block = blkno + undone;
        else
@@ -1625,11 +1649,15 @@
 
      memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
 
-     i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
+     i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
 		      mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
 		      mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
      if (i < 0)
        return i;
+     if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+	 mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+	 mtc.mt_op != MTSETDRVBUFFER)
+       STp->rw = ST_IDLE;  /* Prevent automatic WEOF */
 
      if (mtc.mt_op == MTSETDRVBUFFER &&
 	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
@@ -1669,7 +1697,7 @@
      }
      if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR)
        (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
-     else if (STp->eof == ST_EOD)
+     else if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
        (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
      if (STp->density == 1)
        (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v1.1.73/linux/drivers/scsi/u14-34f.c	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/u14-34f.c	Tue Dec 20 10:23:14 1994
@@ -1,7 +1,10 @@
 /*
- *      u14-34f.c - Low-level SCSI driver for UltraStor 14F/34F
+ *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
- *      30 Nov 1994 rev. 1.09 for linux 1.1.68
+ *      15 Dec 1994 rev. 1.12 for linux 1.1.74
+ *          The host->block flag is set for all the detected ISA boards.
+ *
+ *      30 Nov 1994 rev. 1.11 for linux 1.1.68
  *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
  *          Added optional support for using a single board at a time.
  *
@@ -20,12 +23,11 @@
  *
  *      Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
  *
- *      WARNING: if your 14F board has old firmware revision (see below)
- *               keep the following statement, otherwise comment it.
+ *      WARNING: if your 14F board has an old firmware revision (see below)
+ *               you must change "#undef" into "#define" in the following
+ *               statement.
  */
-#if 0
-#define HAVE_OLD_U14F_FIRMWARE
-#endif
+#undef HAVE_OLD_U14F_FIRMWARE
 /*
  *  The UltraStor 14F, 24F, and 34F are a family of intelligent, high
  *  performance SCSI-2 host adapters.
@@ -33,7 +35,7 @@
  *
  *  14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
  *  24F - EISA Bus Master HA with floppy support and WD1003 emulation.
- *  34F - VL-Bus Bus Master HA (no WD1003 emulation).
+ *  34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
  *
  *  This code has been tested with up to two U14F boards, using both 
  *  firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
@@ -54,7 +56,7 @@
  *  The boot controller must have its BIOS enabled, while other boards can
  *  have their BIOS disabled, or enabled to an higher address.
  *  Boards are named Ux4F0, Ux4F1..., according to the port address order in
- *  the isa_io_port[] array.
+ *  the io_port[] array.
  *  
  *  The following facts are based on real testing results (not on
  *  documentation) on the above U14F board.
@@ -93,6 +95,8 @@
  *    The new firmware has fixed all the above problems and has been tested 
  *    with up to 33 scatter/gather lists.
  *
+ *  In order to support multiple ISA boards in a reliable way,
+ *  the driver sets host->block = TRUE for all ISA boards.
  */
 
 #include <linux/string.h>
@@ -109,13 +113,13 @@
 #include <asm/irq.h>
 #include "u14-34f.h"
 
-/* Values for the PRODUCT_ID ports for the 14F */
-#define U14F_PRODUCT_ID1  0x56
-#define U14F_PRODUCT_ID2  0x40        /* NOTE: Only upper nibble is used */
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1  0x56
+#define PRODUCT_ID2  0x40        /* NOTE: Only upper nibble is used */
 
 /* Subversion values */
-#define U14F 0
-#define U34F 1
+#define ISA  0
+#define ESA 1
 
 #define OP_HOST_ADAPTER   0x1
 #define OP_SCSI           0x2
@@ -140,11 +144,9 @@
 #define U34F_MAX_SGLIST 33
 #define U34F_CLUSTERING ENABLE_CLUSTERING
 
-#define DO_BUS_RESET          /* Reset SCSI bus on error */
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
-#define SINGLE_HOST_OPERATIONS
+#undef  DEBUG_DETECT
+#undef  DEBUG_INTERRUPT
+#undef  DEBUG_STATISTICS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
@@ -230,7 +232,7 @@
    unsigned char bios_drive_number: 1;
    unsigned char heads;
    unsigned char sectors;
-   unsigned char subversion: 4;
+   unsigned char subversion: 4;         /* Bus type, either ISA or ESA */
 
    /* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
    unsigned char slot;
@@ -257,7 +259,8 @@
 
 static inline int port_detect(ushort *port_base, unsigned int j, 
                               Scsi_Host_Template * tpnt) {
-   unsigned char irq, dma_channel, in_byte, subversion, sys_mask, lcl_mask;
+   unsigned char irq, dma_channel, subversion;
+   unsigned char in_byte;
 
    /* Allowed BIOS base addresses (NULL indicates reserved) */
    void *bios_segment_table[8] = { 
@@ -267,10 +270,10 @@
       };
    
    /* Allowed IRQs */
-   unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+   unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
    
-   /* Allowed DMA channels for 14f (0 indicates reserved) */
-   unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+   /* Allowed DMA channels for ISA (0 indicates reserved) */
+   unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
    
    /* Head/sector mappings */
    struct {
@@ -300,26 +303,27 @@
 
    if(check_region(*port_base, REG_REGION)) return FALSE;
 
-   if (inb(*port_base + REG_PRODUCT_ID1) != U14F_PRODUCT_ID1) return FALSE;
+   if (inb(*port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) return FALSE;
 
    in_byte = inb(*port_base + REG_PRODUCT_ID2);
 
-   if ((in_byte & 0xf0) != U14F_PRODUCT_ID2) return FALSE;
+   if ((in_byte & 0xf0) != PRODUCT_ID2) return FALSE;
 
    *(char *)&config_1 = inb(*port_base + REG_CONFIG1);
    *(char *)&config_2 = inb(*port_base + REG_CONFIG2);
 
-   irq = interrupt_table_14f[config_1.interrupt];
-   dma_channel = dma_channel_table_14f[config_1.dma_channel];
+   irq = interrupt_table[config_1.interrupt];
+   dma_channel = dma_channel_table[config_1.dma_channel];
    subversion = (in_byte & 0x0f);
 
+   /* Board detected, allocate its IRQ if not already done */
    if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
        (irq, u14_34f_interrupt_handler, SA_INTERRUPT, driver_name))) {
       printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
-   if (subversion == U14F && request_dma(dma_channel, driver_name)) {
+   if (subversion == ISA && request_dma(dma_channel, driver_name)) {
       printk("%s: unable to allocate DMA channel %u, detaching.\n",
              name, dma_channel);
       free_irq(irq);
@@ -333,24 +337,20 @@
    sh[j]->this_id = config_2.ha_scsi_id;
    sh[j]->can_queue = MAX_MAILBOXES;
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
-   sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
-   lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
 
 #if defined(DEBUG_DETECT)
+   {
+   unsigned char sys_mask, lcl_mask;
+
+   sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+   lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
    printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+   }
 #endif
 
    /* If BIOS is disabled, force enable interrupts */
    if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
 
-#if defined (DO_BUS_RESET)
-   lcl_mask = 0xc2;
-#else
-   lcl_mask = 0x82;
-#endif
-
-   outb(lcl_mask, sh[j]->io_port + REG_LCL_MASK);
-
    /* Register the I/O space that we use */
    snarf_region(sh[j]->io_port, REG_REGION);
 
@@ -362,7 +362,7 @@
    HD(j)->board_number = j;
    irqlist[irq] = j;
 
-   if (HD(j)->subversion == U34F) {
+   if (HD(j)->subversion == ESA) {
       sh[j]->dma_channel = 0;
       sh[j]->unchecked_isa_dma = FALSE;
       sh[j]->sg_tablesize = U34F_MAX_SGLIST;
@@ -371,9 +371,9 @@
       }
    else {
       sh[j]->dma_channel = dma_channel;
+      sh[j]->block = sh[j];
       sh[j]->unchecked_isa_dma = TRUE;
       sh[j]->sg_tablesize = U14F_MAX_SGLIST;
-      /*if (j > 0) sh[j]->sg_tablesize = 0;*/
       sh[j]->hostt->use_clustering = U14F_CLUSTERING;
       sprintf(BN(j), "U14F%d", j);
       disable_dma(dma_channel);
@@ -394,11 +394,11 @@
 int u14_34f_detect (Scsi_Host_Template * tpnt) {
    unsigned int j = 0, k, flags;
 
-   ushort isa_io_port[] = {
+   ushort io_port[] = {
       0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, 0x0
       };
 
-   ushort *port_base = isa_io_port;
+   ushort *port_base = io_port;
 
    save_flags(flags);
    cli();
@@ -412,21 +412,11 @@
 
    while (*port_base) {
 
-      if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+      if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
 
       port_base++;
       }
 
-#if defined (SINGLE_HOST_OPERATIONS)
-   /* Create a circular linked list among the detected boards. */
-   if (j > 1) {
-
-      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
-
-      sh[j - 1]->block = sh[0];
-      }
-#endif
-
    restore_flags(flags);
    return j;
 }
@@ -437,7 +427,7 @@
 
    sgpnt = (struct scatterlist *) SCpnt->request_buffer;
 
-   for(k = 0; k < SCpnt->use_sg; k++) {
+   for (k = 0; k < SCpnt->use_sg; k++) {
       cpp->sglist[k].address = (unsigned int) sgpnt[k].address;
       cpp->sglist[k].num_bytes = sgpnt[k].length;
       data_len += sgpnt[k].length;
@@ -463,7 +453,7 @@
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
 
-   for(k = 0; k < sh[j]->can_queue; k++, i++) {
+   for (k = 0; k < sh[j]->can_queue; k++, i++) {
 
       if (i >= sh[j]->can_queue) i = 0;
 
diff -u --recursive --new-file v1.1.73/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v1.1.73/linux/drivers/scsi/u14-34f.h	Thu Dec  1 19:15:04 1994
+++ linux/drivers/scsi/u14-34f.h	Tue Dec 20 10:23:14 1994
@@ -1,5 +1,5 @@
 /*
- *   u14-34f.h - used by low-level scsi driver for UltraStor 14F/34F
+ *   u14-34f.h - used by the low-level driver for UltraStor 14F/34F
  */
 #ifndef _U14_34F_H
 #define _U14_34F_H
@@ -10,7 +10,7 @@
 int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_biosparam(Disk *, int, int *);
 
-#define U14_34F_VERSION "1.11.01"
+#define U14_34F_VERSION "1.12.04"
 
 #define ULTRASTOR_14_34F {                                            \
                 NULL,                                                 \
diff -u --recursive --new-file v1.1.73/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.73/linux/fs/buffer.c	Thu Dec 15 11:01:54 1994
+++ linux/fs/buffer.c	Mon Dec 19 17:34:22 1994
@@ -777,12 +777,7 @@
 {
 	if (!buf)
 		return;
-/*
- * Hmm.. Leaving this in for now, as I'm a bit nervous about it..
- */
-#if 0
 	wait_on_buffer(buf);
-#endif
 
 	/* If dirty, mark the time this buffer should be written back */
 	set_writetime(buf, 0);
diff -u --recursive --new-file v1.1.73/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.1.73/linux/fs/isofs/inode.c	Tue Dec  6 17:37:06 1994
+++ linux/fs/isofs/inode.c	Mon Dec 19 17:26:24 1994
@@ -17,6 +17,11 @@
 #include <linux/malloc.h>
 #include <linux/errno.h>
 
+#define MULTISESSION /* emoenke@gwdg.de */
+#ifdef MULTISESSION
+#include <linux/cdrom.h>
+#endif MULTISESSION
+
 #include <asm/system.h>
 #include <asm/segment.h>
 
@@ -145,6 +150,14 @@
 	unsigned int blocksize_bits;
 	int high_sierra;
 	int dev=s->s_dev;
+#ifdef MULTISESSION
+	int i;
+	unsigned int vol_desc_start;
+	unsigned int *p_vol_desc_start=&vol_desc_start;
+	struct inode inode_fake;
+	struct file file_fake;
+	extern struct file_operations * get_blkfops(unsigned int);
+#endif MULTISESSION
 	struct iso_volume_descriptor *vdp;
 	struct hs_volume_descriptor *hdp;
 
@@ -184,7 +197,27 @@
 
 	s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
 
+#ifdef MULTISESSION
+	/*
+	 * look if the driver can tell the multi session redirection
+         * value; this allows to do the redirection if we are looking
+         * for the volume descriptor, and to avoid it during "raw" access.
+         */
+	vol_desc_start=0;
+	inode_fake.i_rdev=dev;
+	i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+					 &file_fake,
+					 CDROMMULTISESSION_SYS,
+					 (unsigned long) p_vol_desc_start);
+	if (i!=0) vol_desc_start=0;
+#if 0
+	printk("isofs.inode: CDROMMULTISESSION_SYS rc=%d\n",i);
+	printk("isofs.inode: vol_desc_start = %d\n", vol_desc_start);
+#endif
+	for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) {
+#else
 	for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
+#endif MULTISESSION
 		if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) {
 			s->s_dev=0;
 			printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n",
diff -u --recursive --new-file v1.1.73/linux/fs/open.c linux/fs/open.c
--- v1.1.73/linux/fs/open.c	Thu Dec 15 11:01:54 1994
+++ linux/fs/open.c	Tue Dec 20 15:09:11 1994
@@ -147,6 +147,11 @@
 	}
 	/* Don't worry, the checks are done in inode_change_ok() */
 	if (times) {
+		error = verify_area(VERIFY_READ, times, sizeof(*times));
+		if (error) {
+			iput(inode);
+			return error;
+		}
 		actime = get_fs_long((unsigned long *) &times->actime);
 		modtime = get_fs_long((unsigned long *) &times->modtime);
 		newattrs.ia_ctime = CURRENT_TIME;
diff -u --recursive --new-file v1.1.73/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.1.73/linux/fs/proc/array.c	Thu Dec 15 11:01:54 1994
+++ linux/fs/proc/array.c	Mon Dec 19 18:00:38 1994
@@ -247,9 +247,9 @@
 	   advance */
 	char *yes="yes";
 	char *no="no";
-	char *model[2][9]={{"DX","SX","DX/2","4","5","6",
+	char *model[2][9]={{"DX","SX","DX/2","4","SX/2","6",
 				"7","DX/4"},
-			{"Pentium 60/66","Pentium 90","3",
+			{"Pentium 60/66","Pentium 90/100","3",
 				"4","5","6","7","8"}};
 	return sprintf(buffer,"cpu\t\t: %c86\n"
 			      "model\t\t: %s\n"
diff -u --recursive --new-file v1.1.73/linux/fs/super.c linux/fs/super.c
--- v1.1.73/linux/fs/super.c	Mon Nov 28 14:21:46 1994
+++ linux/fs/super.c	Thu Dec 22 06:57:29 1994
@@ -286,6 +286,7 @@
 	s->s_covered = NULL;
 	s->s_rd_only = 0;
 	s->s_dirt = 0;
+	s->s_type = type;
 	return s;
 }
 
diff -u --recursive --new-file v1.1.73/linux/include/asm-i386/types.h linux/include/asm-i386/types.h
--- v1.1.73/linux/include/asm-i386/types.h	Thu Dec 15 11:01:55 1994
+++ linux/include/asm-i386/types.h	Tue Dec 20 10:23:14 1994
@@ -21,17 +21,17 @@
  * header files exported to user space
  */
 
-typedef signed char __s8;
+typedef __signed__ char __s8;
 typedef unsigned char __u8;
 
-typedef signed short __s16;
+typedef __signed__ short __s16;
 typedef unsigned short __u16;
 
-typedef signed long __s32;
+typedef __signed__ long __s32;
 typedef unsigned long __u32;
 
 #ifndef __STRICT_ANSI__
-typedef signed long long __s64;
+typedef __signed__ long long __s64;
 typedef unsigned long long __u64;
 #endif
 
diff -u --recursive --new-file v1.1.73/linux/include/asm-sparc/vac-ops.h linux/include/asm-sparc/vac-ops.h
--- v1.1.73/linux/include/asm-sparc/vac-ops.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-sparc/vac-ops.h	Mon Dec 19 17:38:40 1994
@@ -0,0 +1,68 @@
+/* vac-ops.h: Inline assembly routines to do operations on the Sparc
+              VAC (virtual address cache).
+
+   Copyright (C) 1994, David S. Miller (davem@caip.rutgers.edu)
+*/
+
+/* enable_vac() enables the virtual address cache. It returns 0 on
+   success, 1 on failure.
+*/
+
+extern __inline__ int enable_vac()
+{
+  int success;
+
+  __asm__ __volatile__("lduba [0x40000000] 0x2, %0\n\t"
+		       "or    %0, 0x10, %0\n\t"
+		       "stba  %0, [0x40000000] 0x2\n\t"
+		       "or    %g0, %g0, %0" : "=r" (success) : "0" (success));
+  return success;
+}
+
+/* disable_vac() disables the virtual address cache. It returns 0 on
+   success, 1 on failure.
+*/
+
+extern __inline__ int disable_vac()
+{
+  int success;
+
+  __asm__ __volatile__("lduba [0x40000000] 0x2, %0\n\t"
+			"xor   %0, 0x10, %0\n\t"
+			"stba  %0, [0x40000000] 0x2\n\t"
+			"or    %g0, %g0, %0" : "=r" (success) : "0" (success));
+  return success;
+}
+
+/* Various one-shot VAC entry flushes on the Sparc */
+
+extern __inline__ void hw_flush_vac_context_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x7" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_context_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0xe" : : "r" (addr));
+}
+
+extern __inline__ void hw_flush_vac_segment_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x5" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_segment_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0], 0xc" : : "r" (addr));
+}
+
+extern __inline__ void hw_flush_vac_page_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x6" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_page_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0xd" : : "r" (addr));
+}
+ 
diff -u --recursive --new-file v1.1.73/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.1.73/linux/include/linux/cdrom.h	Thu Nov  3 17:53:30 1994
+++ linux/include/linux/cdrom.h	Mon Dec 19 17:26:24 1994
@@ -334,6 +334,28 @@
 	  u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
 	};
 
+/*
+ * preliminary extension for obtaining multi session info
+ * (still may change if other drivers will use it, too):
+ * this has to be the "arg" of the CDROMMULTISESSION ioctl.
+ * The returned "addr" is valid only if "xa_flag" is true.
+ */
+struct cdrom_multisession
+	{
+	  union
+	    {
+	      struct 			
+		{
+		  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 */
+	};
+
 #ifdef FIVETWELVE
 #define	CDROM_MODE1_SIZE	512
 #else
@@ -387,6 +409,14 @@
  * (still may change if other drivers will use it, too):
  */
 #define	CDROMEJECT_SW		0x530f		/* arg: 0 or 1 */
+ 
+/*
+ * preliminary extension for obtaining the start-of-last-session
+ * address of multi session disks
+ * currently used by sbpcd.c
+ * (still may change if more drivers will use it).
+ */
+#define	CDROMMULTISESSION_SYS	0x5310 /* internal use only (linux/fs/isofs/inode.c) */
+#define	CDROMMULTISESSION	0x5311 /* "user" interface (arg has to be a "cdrom_multisession" struct) */
 
 #endif  _LINUX_CDROM_H
-
diff -u --recursive --new-file v1.1.73/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v1.1.73/linux/include/linux/cyclades.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/cyclades.h	Mon Dec 19 16:31:57 1994
@@ -0,0 +1,236 @@
+struct cyclades_card {
+    int base_addr;
+    int irq;
+    int num_chips; /* implies card type, 0 if card is absent */
+    int first_line; /* line number of first channel of first chip on card */
+};
+
+struct cyclades_chip {
+  int filler;
+};
+
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct cyclades_port {
+	int                     magic;
+	int                     type;
+	int			card;
+	int			line;
+	int			flags; 		/* defined in tty.h */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int                     cor1,cor2,cor3,cor4,cor5;
+	int                     tbpr,tco,rbpr,rco;
+	int			ignore_status_mask;
+	int			close_delay;
+	int			IER; 	/* Interrupt Enable Register */
+	int			event;
+	unsigned long		last_active;
+	int			count;	/* # of fd on device */
+	int                     x_char; /* to be pushed out ASAP */
+	int                     x_break;
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+};
+
+#define CYCLADES_MAGIC  0x4359
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at cy interrupt time.
+ */
+#define Cy_EVENT_READ_PROCESS	0
+#define Cy_EVENT_WRITE_WAKEUP	1
+#define Cy_EVENT_HANGUP		2
+#define Cy_EVENT_BREAK		3
+#define Cy_EVENT_OPEN_WAKEUP	4
+
+
+
+#define CyMaxChipsPerCard 4
+
+/**** CD1400 registers ****/
+
+#define CyRegSize  0x0400
+#define Cy_HwReset 0x1400
+#define Cy_ClrIntr 0x1800
+
+/* Global Registers */
+
+#define CyGFRCR		(0x40*2)
+#define      CyRevE		(44)
+#define CyCAR		(0x68*2)
+#define      CyCHAN_0		(0x00)
+#define      CyCHAN_1		(0x01)
+#define      CyCHAN_2		(0x02)
+#define      CyCHAN_3		(0x03)
+#define CyGCR		(0x4B*2)
+#define      CyCH0_SERIAL	(0x00)
+#define      CyCH0_PARALLEL	(0x80)
+#define CySVRR		(0x67*2)
+#define      CySRModem		(0x04)
+#define      CySRTransmit	(0x02)
+#define      CySRReceive	(0x01)
+#define CyRICR		(0x44*2)
+#define CyTICR		(0x45*2)
+#define CyMICR		(0x46*2)
+#define      CyICR0		(0x00)
+#define      CyICR1		(0x01)
+#define      CyICR2		(0x02)
+#define      CyICR3		(0x03)
+#define CyRIR		(0x6B*2)
+#define CyTIR		(0x6A*2)
+#define CyMIR		(0x69*2)
+#define      CyIRDirEq		(0x80)
+#define      CyIRBusy		(0x40)
+#define      CyIRUnfair		(0x20)
+#define      CyIRContext	(0x1C)
+#define      CyIRChannel	(0x03)
+#define CyPPR 		(0x7E*2)
+#define      CyCLOCK_20_1MS	(0x27)
+#define      CyCLOCK_25_1MS	(0x31)
+
+/* Virtual Registers */
+
+#define CyRIVR		(0x43*2)
+#define CyTIVR		(0x42*2)
+#define CyMIVR		(0x41*2)
+#define      CyIVRMask (0x07)
+#define      CyIVRRxEx (0x07)
+#define      CyIVRRxOK (0x03)
+#define      CyIVRTxOK (0x02)
+#define      CyIVRMdmOK (0x01)
+#define CyTDR		(0x63*2)
+#define CyRDSR		(0x62*2)
+#define      CyTIMEOUT		(0x80)
+#define      CySPECHAR		(0x70)
+#define      CyBREAK		(0x08)
+#define      CyPARITY		(0x04)
+#define      CyFRAME		(0x02)
+#define      CyOVERRUN		(0x01)
+#define CyMISR		(0x4C*2)
+/* see CyMCOR_ and CyMSVR_ for bits*/
+#define CyEOSRR		(0x60*2)
+
+/* Channel Registers */
+
+#define CyLIVR		(0x18*2)
+#define      CyMscsr		(0x01)
+#define      CyTdsr		(0x02)
+#define      CyRgdsr		(0x03)
+#define      CyRedsr		(0x07)
+#define CyCCR		(0x05*2)
+/* Format 1 */
+#define      CyCHAN_RESET	(0x80)
+#define      CyCHIP_RESET	(0x81)
+#define      CyFlushTransFIFO	(0x82)
+/* Format 2 */
+#define      CyCOR_CHANGE	(0x40)
+#define      CyCOR1ch		(0x02)
+#define      CyCOR2ch		(0x04)
+#define      CyCOR3ch		(0x08)
+/* Format 3 */
+#define      CySEND_SPEC_1	(0x21)
+#define      CySEND_SPEC_2	(0x22)
+#define      CySEND_SPEC_3	(0x23)
+#define      CySEND_SPEC_4	(0x24)
+/* Format 4 */
+#define      CyCHAN_CTL		(0x10)
+#define      CyDIS_RCVR		(0x01)
+#define      CyENB_RCVR		(0x02)
+#define      CyDIS_XMTR		(0x04)
+#define      CyENB_XMTR		(0x08)
+#define CySRER		(0x06*2)
+#define      CyMdmCh		(0x80)
+#define      CyRxData		(0x10)
+#define      CyTxRdy		(0x04)
+#define      CyTxMpty		(0x02)
+#define      CyNNDT		(0x01)
+#define CyCOR1		(0x08*2)
+#define      CyPARITY_NONE	(0x00)
+#define      CyPARITY_0		(0x20)
+#define      CyPARITY_1		(0xA0)
+#define      CyPARITY_E		(0x40)
+#define      CyPARITY_O		(0xC0)
+#define      Cy_1_STOP		(0x00)
+#define      Cy_1_5_STOP	(0x04)
+#define      Cy_2_STOP		(0x08)
+#define      Cy_5_BITS		(0x00)
+#define      Cy_6_BITS		(0x01)
+#define      Cy_7_BITS		(0x02)
+#define      Cy_8_BITS		(0x03)
+#define CyCOR2		(0x09*2)
+#define      CyIXM		(0x80)
+#define      CyTxIBE		(0x40)
+#define      CyETC		(0x20)
+#define      CyAUTO_TXFL	(0x60)
+#define      CyLLM		(0x10)
+#define      CyRLM		(0x08)
+#define      CyRtsAO		(0x04)
+#define      CyCtsAE		(0x02)
+#define      CyDsrAE		(0x01)
+#define CyCOR3		(0x0A*2)
+#define      CySPL_CH_DRANGE	(0x80)  /* special character detect range */
+#define      CySPL_CH_DET1	(0x40)  /* enable special character detection
+                                                               on SCHR4-SCHR3 */
+#define      CyFL_CTRL_TRNSP	(0x20)  /* Flow Control Transparency */
+#define      CySPL_CH_DET2	(0x10)  /* Enable special character detection
+                                                               on SCHR2-SCHR1 */
+#define      CyREC_FIFO		(0x0F)  /* Receive FIFO threshold */
+#define CyCOR4		(0x1E*2)
+#define CyCOR5		(0x1F*2)
+#define CyCCSR		(0x0B*2)
+#define      CyRxEN		(0x80)
+#define      CyRxFloff		(0x40)
+#define      CyRxFlon		(0x20)
+#define      CyTxEN		(0x08)
+#define      CyTxFloff		(0x04)
+#define      CyTxFlon		(0x02)
+#define CyRDCR		(0x0E*2)
+#define CySCHR1		(0x1A*2)
+#define CySCHR2 	(0x1B*2)
+#define CySCHR3		(0x1C*2)
+#define CySCHR4		(0x1D*2)
+#define CySCRL		(0x22*2)
+#define CySCRH		(0x23*2)
+#define CyLNC		(0x24*2)
+#define CyMCOR1 	(0x15*2)
+#define CyMCOR2		(0x16*2)
+#define CyRTPR		(0x21*2)
+#define CyMSVR1		(0x6C*2)
+#define CyMSVR2		(0x6D*2)
+#define      CyDSR		(0x80)
+#define      CyCTS		(0x40)
+#define      CyRI		(0x20)
+#define      CyDCD		(0x10)
+#define      CyDTR              (0x02)
+#define      CyRTS              (0x01)
+#define CyPVSR		(0x6F*2)
+#define CyRBPR		(0x78*2)
+#define CyRCOR		(0x7C*2)
+#define CyTBPR		(0x72*2)
+#define CyTCOR		(0x76*2)
+
+/* max number of chars in the FIFO */
+
+#define CyMAX_CHAR_FIFO	12
+
+/***************************************************************************/
diff -u --recursive --new-file v1.1.73/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.73/linux/include/linux/fs.h	Thu Dec  1 19:15:05 1994
+++ linux/include/linux/fs.h	Thu Dec 22 06:57:29 1994
@@ -301,6 +301,7 @@
 	unsigned char s_lock;
 	unsigned char s_rd_only;
 	unsigned char s_dirt;
+	struct file_system_type *s_type;
 	struct super_operations *s_op;
 	unsigned long s_flags;
 	unsigned long s_magic;
diff -u --recursive --new-file v1.1.73/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v1.1.73/linux/include/linux/interrupt.h	Wed Nov  9 14:56:36 1994
+++ linux/include/linux/interrupt.h	Mon Dec 19 16:35:51 1994
@@ -21,7 +21,8 @@
 	SERIAL_BH,
 	NET_BH,
 	IMMEDIATE_BH,
-	KEYBOARD_BH
+	KEYBOARD_BH,
+	CYCLADES_BH
 };
 
 extern inline void mark_bh(int nr)
diff -u --recursive --new-file v1.1.73/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.1.73/linux/include/linux/sbpcd.h	Tue Nov 15 15:35:41 1994
+++ linux/include/linux/sbpcd.h	Mon Dec 19 17:26:24 1994
@@ -42,8 +42,8 @@
  * then I can include better information with the next release.
  */
 #if !(SBPCD_ISSUE-1) /* first (or if you have only one) interface board: */
-#define CDROM_PORT 0x0230
-#define SBPRO     1
+#define CDROM_PORT 0x0340
+#define SBPRO     0
 #endif
 
 /*
@@ -54,7 +54,7 @@
  * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
  *          #define SOUND_BASE 0     leaves the soundcard untouched
  */
-#define SOUND_BASE 0
+#define SOUND_BASE 0x220
 
 /* ignore the rest if you have only one interface board & driver */
 
@@ -67,8 +67,8 @@
 #define SBPRO     0
 #endif
 #if !(SBPCD_ISSUE-4) /* fourth interface board: */
-#define CDROM_PORT 0x0340
-#define SBPRO     0
+#define CDROM_PORT 0x0230
+#define SBPRO     1
 #endif
 
 /*==========================================================================*/
@@ -83,6 +83,14 @@
 /*==========================================================================*/
 /*==========================================================================*/
 /*
+ * DDI interface definitions
+ * "invented" by Fred N. van Kempen..
+ */
+#define DDIOCSDBG	0x9000
+#define DPRINTF(x)	sbpcd_dprintf x
+
+/*==========================================================================*/
+/*
  * Debug output levels
  */
 #define DBG_INF		1	/* necessary information */
@@ -111,7 +119,9 @@
 #define DBG_SQ 		24	/* dump SubQ frame */
 #define DBG_AUD		25      /* "read audio" debugging */
 #define DBG_SEQ		26      /* Sequoia interface configuration trace */
-#define DBG_000		27      /* unnecessary information */
+#define DBG_LCS		27      /* Longshine LCS-7260 debugging trace */
+#define DBG_TEA		28      /* TEAC CD-55A debugging trace */
+#define DBG_000		29      /* unnecessary information */
 
 /*==========================================================================*/
 /*==========================================================================*/
@@ -159,8 +169,10 @@
 #define p_check 0x10
 #define p_busy_new 0x08
 #define p_door_locked 0x04
-#define p_bit_1 0x02
+#define p_bit_1 0x02 /* hopefully unused now */
+#define p_lcs_door_locked 0x02 /* new use of old bit */
 #define p_disk_ok 0x01
+#define p_lcs_door_closed 0x01 /* new use of old bit */
 /*
  * "old" drives status result bits:
  */
diff -u --recursive --new-file v1.1.73/linux/include/linux/serial.h linux/include/linux/serial.h
--- v1.1.73/linux/include/linux/serial.h	Wed Aug 10 19:26:43 1994
+++ linux/include/linux/serial.h	Mon Dec 19 16:35:51 1994
@@ -33,7 +33,8 @@
 #define PORT_16450	2
 #define PORT_16550	3
 #define PORT_16550A	4
-#define PORT_MAX	4
+#define PORT_CIRRUS     5
+#define PORT_MAX	5
 
 /*
  * Definitions for async_struct (and serial_struct) flags field
diff -u --recursive --new-file v1.1.73/linux/init/main.c linux/init/main.c
--- v1.1.73/linux/init/main.c	Thu Dec 15 11:01:56 1994
+++ linux/init/main.c	Mon Dec 19 16:35:51 1994
@@ -542,10 +542,12 @@
 	memory_start = console_init(memory_start,memory_end);
 	memory_start = bios32_init(memory_start,memory_end);
 	memory_start = kmalloc_init(memory_start,memory_end);
+	sti();
+	calibrate_delay();
+	cli();
 	memory_start = chr_dev_init(memory_start,memory_end);
 	memory_start = blk_dev_init(memory_start,memory_end);
 	sti();
-	calibrate_delay();
 #ifdef CONFIG_SCSI
 	memory_start = scsi_dev_init(memory_start,memory_end);
 #endif
diff -u --recursive --new-file v1.1.73/linux/kernel/irq.c linux/kernel/irq.c
--- v1.1.73/linux/kernel/irq.c	Thu Aug 11 11:56:27 1994
+++ linux/kernel/irq.c	Mon Dec 19 18:22:06 1994
@@ -108,7 +108,7 @@
 	}
 	return;
 bad_bh:
-	printk ("irq.c:bad bottom half entry\n");
+	printk ("irq.c:bad bottom half entry %08lx\n", mask);
 }
 
 /*
diff -u --recursive --new-file v1.1.73/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.73/linux/net/inet/tcp.c	Thu Dec 15 11:01:58 1994
+++ linux/net/inet/tcp.c	Fri Dec 23 10:44:58 1994
@@ -3291,10 +3291,15 @@
 			 * happens, we must ack the received FIN and
 			 * enter the CLOSING state.
 			 *
-			 * XXX timeout not set properly
+			 * This causes a WRITE timeout, which will either
+			 * move on to TIME_WAIT when we timeout, or resend
+			 * the FIN properly (maybe we get rid of that annoying
+			 * FIN lost hang). The TIME_WRITE code is already correct
+			 * for handling this timeout.
 			 */
 
-			reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			if(sk->timeout != TIME_WRITE)
+				reset_timer(sk, TIME_WRITE, sk->rto);
 			tcp_set_state(sk,TCP_CLOSING);
 			break;
 		case TCP_FIN_WAIT2:
@@ -4008,9 +4013,11 @@
 
 static int tcp_std_reset(struct sock *sk, struct sk_buff *skb)
 {
-	sk->zapped=1;
+	sk->zapped = 1;
 	sk->err = ECONNRESET;
-	if(sk->state==TCP_CLOSE_WAIT)
+	if (sk->state == TCP_SYN_SENT)
+		sk->err = ECONNREFUSED;
+	if (sk->state == TCP_CLOSE_WAIT)
 		sk->err = EPIPE;
 #ifdef TCP_DO_RFC1337		
 	/*
@@ -4320,14 +4327,18 @@
 		if(sk->debug)
 			printk("Doing a BSD time wait\n");
 		tcp_statistics.TcpEstabResets++;	   
+		sk->rmem_alloc -= skb->mem_len;
+		skb->sk = NULL;
 		sk->err=ECONNRESET;
 		tcp_set_state(sk, TCP_CLOSE);
 		sk->shutdown = SHUTDOWN_MASK;
 		release_sock(sk);
-		sk=get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
+		sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
 		if(sk && sk->state==TCP_LISTEN)
 		{
 			sk->inuse=1;
+			skb->sk = sk;
+			sk->rmem_alloc += skb->mem_len;
 			tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000);
 			release_sock(sk);
 			return 0;
