diff -u --recursive --new-file v1.2.0/linux/Makefile linux/Makefile
--- v1.2.0/linux/Makefile	Thu Mar  9 20:37:33 1995
+++ linux/Makefile	Thu Mar  9 20:35:42 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 2
-SUBLEVEL = 0
+SUBLEVEL = 1
 
 ARCH = i386
 
diff -u --recursive --new-file v1.2.0/linux/README linux/README
--- v1.2.0/linux/README	Fri Jan 27 10:51:52 1995
+++ linux/README	Thu Mar  9 20:56:21 1995
@@ -1,14 +1,10 @@
 
-	Linux kernel release 1.1.xx
+	Linux kernel release 1.2.xx
 
-These are the release notes for linux version 1.1.  Read them carefully,
+These are the release notes for linux version 1.2.  Read them carefully,
 as they tell you what this is all about, explain how to install the
 kernel, and what to do if something goes wrong. 
 
-Warning: This is a work in progress. If you are not reasonably at ease with
-the C programming language, GCC, installing Linux, and recovering from
-system crashes, please use the 1.0 version, or wait for 1.2.
-
 WHAT IS LINUX?
 
   Linux is a Unix clone for 386/486-based PCs written from scratch by
@@ -23,12 +19,21 @@
   It is distributed under the GNU General Public License - see the
   accompanying COPYING file for more details. 
 
+DOCUMENTATION:
+
+ - there is a lot of documentation available both in electronic form on
+   the internet and in books, both Linux-specific and pertaining to
+   general UNIX questions.  I'd recommend looking into the documentation
+   subdirectories on any Linux ftp site for the LDP (Linux Documentation
+   Project) books.  This README is not meant to be documentation on the
+   system: there are much better sources available.
+
 INSTALLING the kernel:
 
  - If you install the full sources, do a
 
 		cd /usr/src
-		gzip -cd linux-1.1.XX.tar.gz | tar xfv -
+		gzip -cd linux-1.2.XX.tar.gz | tar xfv -
 
    to get it all put in place. Replace "XX" with the version number of the
    latest kernel.
diff -u --recursive --new-file v1.2.0/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.2.0/linux/drivers/block/Makefile	Sun Feb 12 15:00:45 1995
+++ linux/drivers/block/Makefile	Wed Mar 15 08:47:26 1995
@@ -23,6 +23,7 @@
 
 OBJS := ll_rw_blk.o ramdisk.o genhd.o 
 SRCS := ll_rw_blk.c ramdisk.c genhd.c 
+BLOCK_MODULE_OBJS =
 
 ifdef CONFIG_BLK_DEV_FD
 OBJS := $(OBJS) floppy.o
@@ -47,6 +48,8 @@
 ifdef CONFIG_SBPCD
 OBJS := $(OBJS) sbpcd.o
 SRCS := $(SRCS) sbpcd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sbpcd.o
 endif #CONFIG_SBPCD
 
 ifdef CONFIG_SBPCD2
@@ -82,6 +85,8 @@
 ifdef CONFIG_CDU535
 OBJS := $(OBJS) sonycd535.o
 SRCS := $(SRCS) sonycd535.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
 endif
 
 all: block.a
@@ -94,7 +99,10 @@
 dep:
 	$(CPP) -M $(SRCS) > .depend
 
-modules:
+modules: $(BLOCK_MODULE_OBJS)
+	echo $(BLOCK_MODULE_OBJS) > ../../modules/BLOCK_MODULES
+	(cd ../../modules;for i in $(BLOCK_MODULE_OBJS); do ln -sf ../drivers/block/$$i .; done)
+
 dummy:
 
 #
diff -u --recursive --new-file v1.2.0/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.2.0/linux/drivers/block/README.ide	Fri Feb 24 21:38:27 1995
+++ linux/drivers/block/README.ide	Thu Mar  9 20:33:55 1995
@@ -1,4 +1,4 @@
-README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.1.95)
+README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.2.x)
 ================================================================================
 Supported by:  mlord@bnr.ca           -- disks, interfaces, probing
                snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
@@ -9,8 +9,8 @@
 
 	- support for up to two IDE interfaces on one or two IRQs
 	- support for any mix of up to four disk and/or cdrom drives
-	- support for reading IDE ATAPI cdrom drives (NEC, MITSUMI, VERSA, SONY)
-	- support for audio functions on FX400,NEC-260 cdrom drives (others?)
+	- support for reading IDE ATAPI cdrom drives (NEC,MITSUMI,VERTOS,SONY)
+	- support for audio functions
 	- auto-detection of interfaces, drives, IRQs, and disk geometries
 	  -- "single" drives should be jumpered as "master", not "slave"
 	- support for BIOSs which report "more than 16 heads" on disk drives
diff -u --recursive --new-file v1.2.0/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd
--- v1.2.0/linux/drivers/block/README.sbpcd	Thu Mar  9 20:37:33 1995
+++ linux/drivers/block/README.sbpcd	Wed Mar 15 08:47:26 1995
@@ -1,4 +1,4 @@
-This README belongs to release 3.2 or newer of the SoundBlaster Pro
+This README belongs to release 3.4 or newer of the SoundBlaster Pro
 (Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and soon TEAC, too)
 CD-ROM driver for Linux.
 
@@ -18,12 +18,14 @@
 
 The quad-speed TEAC CD-55A drive uses the same interface types, but has a
 totally different command and flow control scheme. Support is under
-construction.
+construction, but splitted: Greg Nowicki (nowicki@tardis.larc.nasa.gov)
+cares about the separate driver "teaccd". Please, do not mail him merely for
+asking "user questions" at the moment - the project is still a "baby".
 
 CreativeLabs has a new drive "CD-200". Support is under construction.
 Detection should already work.
 
-Regarding CD200 and CD-55A support:
+Regarding CD200 support:
     Please, don't mail me about it if you are not a competent BETA tester 
             (if you are: mail!; I do not have such drives).
     Please, don't drop simple questions about the new drives in the
@@ -189,15 +191,27 @@
 "workman -c /dev/sbpcd &".
 
 
+Using sbpcd as a "loadable module":
+-----------------------------------
+
+If you do NOT select "Matsushita/Panasonic CDROM driver support" during the
+"make config" of your kernel, you can build the "loadable module" sbpcd.o.
+Read /usr/src/linux/README.modules on this.
+
+If sbpcd gets used as a module, the "audio copy" feature is disabled, and the
+internal read-ahead buffer has a reduced size (the latter may affect throughput
+a little bit under "slow" CPUs). Further, support of more than one interface
+card (i.e. drives 4...15) is disabled.
+
+
 Things of interest:
 -------------------
 
-The driver is configured to try the SoundBlaster Pro type of interface at
-I/O port 0x0230 first. If this is not appropriate, sbpcd.h should get changed
+The driver is configured to try the LaserMate type of interface at I/O port
+0x0340 first. If this is not appropriate, sbpcd.h should get changed
 (you will find the right place - just at the beginning).
 
-No DMA and no IRQ is used, so the IRQ line stays free for the SB Pro sound
-drivers.
+No DMA and no IRQ is used.
 
 To reduce or increase the amount of kernel messages, edit sbpcd.c and play
 with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
@@ -356,6 +370,20 @@
 For every-day use, address and type should get configured within sbpcd.h. That
 will stop the auto-probing due to success with the first try.
 
+The kernel command "sbpcd=0" suppresses each auto-probing and causes
+the driver not to find any drive; it is meant for people who love sbpcd
+so much that they do not want to miss it, even if they miss the drives. ;-)  
+
+If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is
+initially disabled and needs an explicit kernel command to get activated.
+Once activated, it does not stop before success or end-of-list. This may be
+useful within "universal" CDROM installation boot floppies (but using the 
+loadable module would be better because it allows an "extended" auto-probing
+without fearing NE2000 cards).
+
+To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within
+sbpcd.c.
+
 
 Setting up address and interface type:
 --------------------------------------
@@ -532,9 +560,7 @@
 Bug reports, comments, wishes, donations (technical information is a donation,
 too :-) etc. to
                          emoenke@gwdg.de
- or to                   eberhard_moenkeberg@rollo.central.de
  or to my FIDO address:  Eberhard Moenkeberg, 2:2437/210.27
-
 
 SnailMail address, preferable for CD editors if they want to submit a free
 "cooperation" copy:
diff -u --recursive --new-file v1.2.0/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.2.0/linux/drivers/block/ide.c	Fri Feb 24 21:38:28 1995
+++ linux/drivers/block/ide.c	Thu Mar  9 20:33:55 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 3.13  February 23, 1995
+ *  linux/drivers/block/ide.c	Version 3.14  March 7, 1995
  *
  *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
  */
@@ -104,6 +104,9 @@
  *  Version 3,12	simplify logic for selecting initial mult_count
  *			  (fixes problems with buggy WD drives)
  *  Version 3.13	remove excess "multiple mode disabled" messages
+ *  Version 3.14	fix ide_error() handling of BUSY_STAT
+ *			fix byte-swapped cdrom strings (again.. arghh!)
+ *			ignore INDEX bit when checking the ALTSTATUS reg
  *
  *  To do:
  *	- special 32-bit controller-type detection & support
@@ -612,17 +615,21 @@
 		return;
 	}
 #endif	/* IDE_DRIVE_CMD */
-	if (dev->type == disk && (stat & ERR_STAT)) {
-		/* err has different meaning on cdrom */
-		if (err & BBD_ERR)		/* retries won't help this! */
-			rq->errors = ERROR_MAX;
-		else if (err & TRK0_ERR)	/* help it find track zero */
-			rq->errors |= ERROR_RECAL;
-	}
-	if ((stat & DRQ_STAT) && rq->cmd == READ) {
-		int i = dev->mult_count ? dev->mult_count<<8 : 1<<8;
-		while (i-- > 0)			/* try to flush data */
-			(void) IN_BYTE(HD_DATA, dev->hwif);
+	if (stat & BUSY_STAT) {		/* other bits are useless when BUSY */
+		rq->errors |= ERROR_RESET;
+	} else {
+		if (dev->type == disk && (stat & ERR_STAT)) {
+			/* err has different meaning on cdrom */
+			if (err & BBD_ERR)		/* retries won't help this! */
+				rq->errors = ERROR_MAX;
+			else if (err & TRK0_ERR)	/* help it find track zero */
+				rq->errors |= ERROR_RECAL;
+		}
+		if ((stat & DRQ_STAT) && rq->cmd == READ) {
+			int i = dev->mult_count ? dev->mult_count<<8 : 1<<8;
+			while (i-- > 0)			/* try to flush data */
+				(void) IN_BYTE(HD_DATA, dev->hwif);
+		}
 	}
 	if (GET_STAT(dev->hwif) & (BUSY_STAT|DRQ_STAT))
 		rq->errors |= ERROR_RESET;	/* Mmmm.. timing problem */
@@ -1612,9 +1619,14 @@
 
 	/*
 	 *  WIN_IDENTIFY returns little-endian info,
-	 *  WIN_PIDENTIFY return big-endian info.
+	 *  WIN_PIDENTIFY *usually* returns little-endian info.
 	 */
-	bswap = (cmd == WIN_IDENTIFY);
+	bswap = 1;
+	if (cmd == WIN_PIDENTIFY) {
+		if ((id->model[0] == 'N' && id->model[1] == 'E')
+		 || (id->model[0] == 'F' && id->model[1] == 'X'))
+			bswap = 0;	/* NEC and *some* Mitsumi units */
+	}				/* Vertos drives may still be weird */
 	fixstring (id->model,     sizeof(id->model),     bswap);
 	fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
 	fixstring (id->serial_no, sizeof(id->serial_no), bswap);
@@ -1738,10 +1750,11 @@
 	}
 #endif	/* PROBE_FOR_IRQS */
 	delay_10ms();				/* take a deep breath */
-	if (IN_BYTE(HD_ALTSTATUS,DEV_HWIF) == IN_BYTE(HD_STATUS,DEV_HWIF))
-		hd_status = HD_ALTSTATUS;	/* use non-intrusive polling */
-	else
+	if ((IN_BYTE(HD_ALTSTATUS,DEV_HWIF) ^ IN_BYTE(HD_STATUS,DEV_HWIF)) & ~INDEX_STAT) {
 		hd_status = HD_STATUS;		/* an ancient Seagate drive */
+		printk("%s: probing with STATUS instead of ALTSTATUS\n", dev->name);
+	} else
+		hd_status = HD_ALTSTATUS;	/* use non-intrusive polling */
 	OUT_BYTE(cmd,HD_COMMAND);		/* ask drive for ID */
 	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
 	timeout += jiffies;
diff -u --recursive --new-file v1.2.0/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.2.0/linux/drivers/block/sbpcd.c	Sun Jan 29 20:28:43 1995
+++ linux/drivers/block/sbpcd.c	Wed Mar 15 08:47:26 1995
@@ -6,10 +6,11 @@
  *            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 CD200 drive (who knows?).
+ *            Still not for the CreativeLabs CD200 drive (but should work soon).
+ *            Not for the TEAC CD-55A drive (separate project now).
+ *            Not for Funai or Sanyo drives.
  *
- *  NOTE:     This is release 3.3.
+ *  NOTE:     This is release 3.4.
  *            It works with my SbPro & drive CR-521 V2.11 from 2/92
  *            and with the CR-562-B V0.75 on a "naked" Panasonic
  *            CI-101P interface. And vice versa. 
@@ -180,6 +181,12 @@
  *
  *  3.3  Working with CD200 support. Maybe a simple read is already possible.
  *
+ *  3.4  Auto-probing stops if an address of 0 is seen (to be entered with
+ *       the kernel command line).
+ *       Made the driver "loadable". If used as a module, "audio copy" is
+ *       disabled, and the internal read ahead data buffer has a reduced size
+ *       of 4 kB; so, throughput may be reduced a little bit with slow CPUs.
+ *
  *  TODO
  *
  *     disk change detection
@@ -196,7 +203,6 @@
  * 
  *
  *   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
- *                               or <eberhard_moenkeberg@rollo.central.de>
  *
  *                  If you change this software, you should mail a .diff
  *                  file with some description lines to emoenke@gwdg.de.
@@ -224,9 +230,17 @@
 #include <linux/config.h>
 #include <linux/errno.h>
 
+#ifdef MODULE
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#endif MODULE
+
 #include <linux/sched.h>
 #include <linux/mm.h>
-/* #undef DS */
 #include <linux/timer.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
@@ -253,18 +267,19 @@
 #define MAJOR_NR MATSUSHITA_CDROM4_MAJOR /* fourth driver issue */
 #endif
 
+#ifdef MODULE
+#include "/usr/src/linux/drivers/block/blk.h"
+#else
 #include "blk.h"
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif MODULE
 
-#define VERSION "3.3 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "3.4 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*
- * still testing around...
+ *
  */
-#define TEAC 0 /* set to 1 for TEAC CD-55A detection test (not more) */
-
-#define MULTISESSION_BY_DRIVER 0 /* if set to 0 here, we need the counterpart
-                                  * in linux/fs/isofs/inode.c
-                                  */
 #define READ_AUDIO 4 /* max. number of audio frames to read with one */
                      /* request (allocates n* 2352 bytes kernel memory!) */
 #define JUKEBOX 1 /* tray control: eject tray if no disk is in */
@@ -368,18 +383,18 @@
 /* due to incomplete address decoding of the SbPro card, these must be last */
   0x630, 0, /* "sound card #9" (default) */
   0x650, 0, /* "sound card #9" */
+#ifdef MODULE
 /*
- * some "hazardous" locations
+ * some "hazardous" locations (no harm with the loadable version)
  * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
  */
-#if 0
   0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
   0x350, 0, /* Lasermate, CI-101P */
   0x350, 2, /* SPEA Media FX */
   0x370, 0, /* Lasermate, CI-101P */
   0x290, 1, /* Soundblaster 16 */
   0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
-#endif
+#endif MODULE
 #endif DISTRIBUTION
 };
 #else
@@ -448,7 +463,6 @@
  * (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_CD2)  MKE CD200 debugging trace
  * (1<<DBG_000)  unnecessary information
  */
@@ -480,8 +494,10 @@
 static struct cdrom_multisession ms_info;
 
 static char *str_sb = "SoundBlaster";
+static char *str_sb_l = "soundblaster";
 static char *str_lm = "LaserMate";
 static char *str_sp = "SPEA";
+static char *str_sp_l = "spea";
 char *type;
 #if !(SBPCD_ISSUE-1)
 static char *major_name="sbpcd";
@@ -503,16 +519,20 @@
 #endif FUTURE
 
 /*==========================================================================*/
+#ifdef MODULE
+/* some restrictions for the "loadable" version */
+#define SBP_BUFFER_FRAMES 2 /* kmalloc() limit */
+#define SBP_BUFFER_AUDIO_FRAMES 0
+#else
 #define SBP_BUFFER_FRAMES 4 /* driver's own read_ahead, data mode */
 #define SBP_BUFFER_AUDIO_FRAMES READ_AUDIO /* buffer for read audio mode */
-
+#endif MODULE
 /*==========================================================================*/
 
 static u_char family0[]="MATSHITA"; /* MKE CR-52x */
 static u_char family1[]="CR-56"; /* MKE CR-56x */
 static u_char family2[]="CD200"; /* MKE CD200 */
 static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */
-static u_char familyT[]="CD-55A"; /* TEAC CD-55A (still unknown)*/
 
 static u_int response_count=0;
 static u_int flags_cmd_out;
@@ -580,9 +600,6 @@
   
   u_char f_multisession;
   u_int lba_multi;
-#if MULTISESSION_BY_DRIVER
-  u_int last_redirect;
-#endif MULTISESSION_BY_DRIVER
   int first_session;
   int last_session;
   
@@ -829,6 +846,10 @@
 #ifdef CDMKE
   int i;
 
+#ifdef MODULE
+  sbp_sleep(150);
+  for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+#else
   if (current == task[0])
     for (i=maxtim02;i!=0;i--) inb(CDi_status);
   else 
@@ -836,6 +857,7 @@
       sbp_sleep(150);
       for (i=maxtim_data;i!=0;i--) inb(CDi_status);
     }
+#endif MODULE
 #else
   timed_out=0;
 #if 0
@@ -857,6 +879,20 @@
   int i,j;
   u_long timeout;
   
+#ifdef MODULE
+    for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
+      {
+	for ( ;i!=0;i--)
+	  {
+	    j=inb(CDi_status);
+	    if (!(j&s_not_data_ready)) return (j);
+	    if (!(j&s_not_result_ready)) return (j);
+	    if (fam0L_drive) if (j&s_attention) return (j);
+	  }
+	sbp_sleep(1);
+	i = 1;
+      }
+#else
   if (current == task[0])
     for(i=maxtim16;i!=0;i--)
       {
@@ -878,47 +914,35 @@
 	sbp_sleep(1);
 	i = 1;
       }
+#endif MODULE
   DPRINTF((DBG_LCS,"SBPCD: CDi_stat_loop failed\n"));
   return (-1);
 }
 /*==========================================================================*/
-#if TEAC-X
-/*==========================================================================*/
-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-X
-/*==========================================================================*/
 static int ResponseInfo(void)
 {
   int i,j, st=0;
   u_long timeout;
 
   DPRINTF((DBG_000,"SBPCD: ResponseInfo entered.\n"));
+#ifdef MODULE
+  for (i=0, timeout = jiffies + 100; i < response_count; i++) 
+    {
+      for (j=maxtim_data; ; )
+	{
+	  for ( ;j!=0;j-- )
+	    {
+	      st=inb(CDi_status);
+	      if (!(st&s_not_result_ready)) break;
+	    }
+	  if (j != 0 || timeout <= jiffies) break;
+	  sbp_sleep(0);
+	  j = 1;
+	}
+      if (timeout <= jiffies) return (-1);
+      infobuf[i]=inb(CDi_info);
+    }
+#else
   if (current == task[0])
     for (i=0;i<response_count;i++)
       {
@@ -953,15 +977,16 @@
 	  infobuf[i]=inb(CDi_info);
 	}
     }
+#endif MODULE
   DPRINTF((DBG_000,"SBPCD: ResponseInfo: done.\n"));
   return (0);
 }
 /*==========================================================================*/
-static int EvaluateStatus(int st)
+static void EvaluateStatus(int st)
 {
+  DriveStruct[d].status_byte=0;
   if (fam0_drive)
     {
-      DriveStruct[d].status_byte=0;
       if (st&p_caddin_old) DriveStruct[d].status_byte |= p_door_closed|p_caddy_in;
       if (st&p_spinning) DriveStruct[d].status_byte |= p_spinning;
       if (st&p_check) DriveStruct[d].status_byte |= p_check;
@@ -970,23 +995,26 @@
     }
   else if (famL_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 if (fam1_drive)
-    {
-      DriveStruct[d].status_byte=st;
-      st=p_success_old; /* for new drives: fake "successful" bit of old drives */
     }
-  else /* CD200, CD-55A */
+  else if (fam2_drive)
     {
+      if (st&p2_check) DriveStruct[d].status_byte |= p1_check;
+      if (st&p2_door_closed) DriveStruct[d].status_byte |= p1_door_closed;
+      if (st&p2_disk_in) DriveStruct[d].status_byte |= p1_disk_in;
+      if (st&p2_busy1) DriveStruct[d].status_byte |= p1_busy;
+      if (st&p2_busy2) DriveStruct[d].status_byte |= p1_busy;
+      if (st&p2_spinning) DriveStruct[d].status_byte |= p1_spinning;
+      if (st&p2_door_locked) DriveStruct[d].status_byte |= p1_door_locked;
+      if (st&p2_disk_ok) DriveStruct[d].status_byte |= p1_disk_ok;
     }
-  return (st);
+  else if (fam1_drive) DriveStruct[d].status_byte=st;
+  return;
 }
 /*==========================================================================*/
 static int ResponseStatus(void)
@@ -995,7 +1023,24 @@
   u_long timeout;
 
   DPRINTF((DBG_STA,"SBPCD: doing ResponseStatus...\n"));
-
+#ifdef MODULE
+  if (flags_cmd_out & f_respo3) timeout = jiffies;
+  else if (flags_cmd_out & f_respo2) timeout = jiffies + 1600;
+  else timeout = jiffies + 400;
+  j=maxtim_8;
+  do
+    {
+      for ( ;j!=0;j--)
+	{ 
+	  i=inb(CDi_status);
+	  if (!(i&s_not_result_ready)) break;
+	}
+      if (j != 0 || timeout <= jiffies) break;
+      sbp_sleep(0);
+      j = 1;
+    }
+  while (1);
+#else
   if (current == task[0])
     {
       if (flags_cmd_out & f_respo3) j = maxtim_8;
@@ -1026,16 +1071,17 @@
 	}
       while (1);
     }
+#endif MODULE
   if (j==0) 
     { if ((flags_cmd_out & f_respo3) == 0)
 	DPRINTF((DBG_STA,"SBPCD: ResponseStatus: timeout.\n"));
-      EvaluateStatus(0);
+      DriveStruct[d].status_byte=0;
       return (-1);
     }
   i=inb(CDi_info);
   DPRINTF((DBG_STA,"SBPCD: ResponseStatus: response %2X.\n", i));
-  i=EvaluateStatus(i);
-  return (i);
+  EvaluateStatus(i);
+  return (0);
 }
 /*==========================================================================*/
 static void xx_ReadStatus(void)
@@ -1046,9 +1092,7 @@
   SBPCD_CLI;
   if (fam0L_drive) OUT(CDo_command,CMD0_STATUS);
   else if (fam1_drive) OUT(CDo_command,CMD1_STATUS);
-  else /* CD200, CD-55A */
-    {
-    }
+  else if (fam2_drive) OUT(CDo_command,CMD2_STATUS);
   if (!fam0L_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
   SBPCD_STI;
 }
@@ -1080,13 +1124,6 @@
       response_count=6;
       flags_cmd_out=f_putcmd;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-      drvcmd[0]=CMDT_READ_ERR;
-      response_count=5;
-      flags_cmd_out=f_putcmd;
-    }
   i=cmd_out(7);
   DriveStruct[d].error_byte=0;
   DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: cmd_out(82) returns %d (%02X)\n",i,i));
@@ -1201,10 +1238,6 @@
       drvcmd[5]=pos&0x00FF;
       flags_cmd_out=f_putcmd|f_ResponseStatus;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   response_count=0;
   i=cmd_out(7);
   return (i);
@@ -1234,10 +1267,6 @@
       drvcmd[4]=0x01;
       flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   response_count=0;
   i=cmd_out(7);
   DriveStruct[d].in_SpinUp = 0;
@@ -1268,10 +1297,6 @@
       drvcmd[1]=1;
       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   return (i);
 }
@@ -1307,10 +1332,6 @@
 	}
       flags_cmd_out=f_putcmd|f_ResponseStatus;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   return (i);
 }
@@ -1462,10 +1483,6 @@
       drvcmd[5]=value0;
       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   response_count=0;
   i=cmd_out(7);
   if (i>0) return (i);
@@ -1505,10 +1522,6 @@
       i=cmd_out(7);
       OUT(CDo_reset,0x00);
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   if (famL_drive) sbp_sleep(500); /* wait 5 seconds */
   else sbp_sleep(100); /* wait a second */
   flush_status();
@@ -1586,10 +1599,6 @@
 	flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
 	  f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   return (i);
 }
@@ -1621,10 +1630,6 @@
       if (lock==1) drvcmd[1]=0x01;
       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d after\n", st_door_locked));
   return (i);
@@ -1658,10 +1663,6 @@
       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
  	f_ResponseStatus|f_obey_p_check|f_bit1;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d after\n", st_door_closed));
   return (i);
@@ -1699,10 +1700,6 @@
  	    flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
 	  response_count=13;
 	}
-      else /* CD-55A */
-	{
-	  return (-1);
-	}
       i=cmd_out(7);
       if (i<0) return (i);
       DPRINTF((DBG_SQ,"SBPCD: xx_ReadSubQ:"));
@@ -1755,18 +1752,11 @@
 	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
       response_count=2;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   if (i<0) return (i);
   i=0;
   if (fam1_drive) DriveStruct[d].sense_byte=infobuf[i++];
   else if (fam0L_drive) DriveStruct[d].sense_byte=0;
-  else /* CD-55A */
-    {
-    }
   DriveStruct[d].frame_size=make16(infobuf[i],infobuf[i+1]);
 
   DPRINTF((DBG_XA,"SBPCD: xx_ModeSense: "));
@@ -1816,10 +1806,6 @@
       else
 	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   response_count=0;
   i=cmd_out(7);
   if (i<0) return (i);
@@ -1860,10 +1846,6 @@
       else
 	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   if (i<0) return (i);
   if (fam1_drive)
@@ -1931,10 +1913,6 @@
             }
         }
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   DriveStruct[d].vol_chan0=chan0;
   DriveStruct[d].vol_ctrl0=vol0;
   DriveStruct[d].vol_chan1=chan1;
@@ -1976,10 +1954,6 @@
       else
 	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   if (i<0) return (i);
   if (fam1_drive) DriveStruct[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_BLOCK_OFFSET;
@@ -2021,10 +1995,6 @@
       response_count=8;
       flags_cmd_out=f_putcmd;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   i=cmd_out(7);
   if (i<0) return (i);
   DriveStruct[d].xa_byte=infobuf[0];
@@ -2117,10 +2087,6 @@
       else
 	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   drvcmd[2]=num;
   i=cmd_out(7);
   if (i<0) return (i);
@@ -2159,10 +2125,7 @@
     flags_cmd_out=f_putcmd;
   else if (fam01_drive)
     flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-  else /* CD200, CD-55A */
-    {
-      return (-1);
-    }
+  else if (fam2_drive) return (-1); /* not implemented yet */
   i=cmd_out(7);
   return (i);
 }
@@ -2181,7 +2144,7 @@
 	  DriveStruct[d].UPC_buf[i]=((*p++)<<4)&0xFF;
 	  DriveStruct[d].UPC_buf[i] |= *p++;
 	}
-      else /* CD200, CD-55A */
+      else /* CD200 */
 	{
 	  return (-1);
 	}
@@ -2197,6 +2160,8 @@
   int block, checksum;
 #endif TEST_UPC
 
+  if (fam2_drive) return (0); /* not implemented yet */
+
   DriveStruct[d].diskstate_flags &= ~upc_bit;
 #if TEST_UPC
   for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
@@ -2225,7 +2190,7 @@
 	  response_count=0;
 	  flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
 	}
-      else /* CD200, CD-55A */
+      else /* CD200 */
 	{
 	  return (-1);
 	}
@@ -2296,9 +2261,6 @@
 	  DriveStruct[d].f_multisession=1;
 	  DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
 						  make16(infobuf[2],infobuf[3])));
-#if MULTISESSION_BY_DRIVER
-	  DriveStruct[d].last_redirect=19;
-#endif MULTISESSION_BY_DRIVER
 	  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],
@@ -2324,15 +2286,8 @@
 	{
 	  DPRINTF((DBG_MUL,"SBPCD: MultiSession base: %06X\n", DriveStruct[d].lba_multi));
 	  DriveStruct[d].f_multisession=1;
-#if MULTISESSION_BY_DRIVER
-	  DriveStruct[d].last_redirect=19;
-#endif MULTISESSION_BY_DRIVER
 	}
     }
-  else /* CD-55A */
-    {
-      return (-1);
-    }
   return (0);
 }
 /*==========================================================================*/
@@ -2408,53 +2363,6 @@
 #endif CDMKE
 }
 /*==========================================================================*/
-#if TEAC-X
-/*==========================================================================*/
-static void teac_reset(int drv_id)
-{
-  int i;
-
-  OUT(CDo_sel_i_d,0);
-  OUT(CDo_enable,drv_id);
-  OUT(CDo_command,CMDT_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_TEAC_drive(int drv_id)
-{
-  int i;
-  if (sbpro_type!=1) teac_reset(drv_id);
-
-  OUT(CDo_enable,drv_id);
-  OUT(CDo_sel_i_d,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_TEAC_drive(i);
-      if (j<1) continue;
-      found++;
-      DPRINTF((DBG_TEA,"SBPCD: TEAC drive (id=%d) found.\n",i));
-    }
-  return (found);
-}
-/*==========================================================================*/
-#endif TEAC-X
-/*==========================================================================*/
-/*==========================================================================*/
 #ifdef CD200
 
 #if 0
@@ -2510,6 +2418,7 @@
   int i;
 
   printk("SBPCD: please mail the following lines to emoenke@gwdg.de:\n");
+  printk("SBPCD: address %03X, type %s, drive ID %d\n", CDo_command, type, DriveStruct[d].drv_id);
   printk("SBPCD: infobuf = \"");
   for (i=0;i<12;i++) printk("%02X ",infobuf[i]);
   printk("\"\nSBPCD: infobuf = \"");
@@ -2525,25 +2434,6 @@
   DPRINTF((DBG_INI,"SBPCD: check_version entered.\n"));
   DriveStruct[d].drv_type=0;
 
-#if TEAC
-  /* check for CD-55A */
-  clr_cmdbuf();
-  drvcmd[0]=CMDT_READ_ERR;
-  response_count=5;
-  flags_cmd_out=f_putcmd;
-  i=cmd_out(7);
-  if (i<0) DPRINTF((DBG_INI,"SBPCD: CMDT_READERR returns %d (ok anyway).\n",i));
-  /* read drive version */
-  clr_cmdbuf();
-  for (i=0;i<12;i++) infobuf[i]=0;
-  if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-  response_count=12; /* may be too much */
-  drvcmd[0]=CMDT_READ_VER;
-  drvcmd[4]=response_count;
-  flags_cmd_out=f_putcmd;
-  i=cmd_out(10); /* possibly only 6 */
-  if (i<0) DPRINTF((DBG_INI,"SBPCD: CMDT_READ_VER returns %d\n",i));
-#else
   /* check for CD200 */
   clr_cmdbuf();
   drvcmd[0]=CMD2_READ_ERR;
@@ -2565,8 +2455,7 @@
   flags_cmd_out=f_putcmd;
   i=cmd_out(7);
   if (i<0) DPRINTF((DBG_INI,"SBPCD: CMD2_READ_VER returns %d\n",i));
-#endif TEAC
-  if (i>=0) /* either from CD200 or CD-55A */
+  if (i>=0) /* from CD200 */
     {
       for (i=0, j=0;i<12;i++) j+=infobuf[i];
       if (j)
@@ -2589,23 +2478,6 @@
 	  DriveStruct[d].drive_model[7]=0;
 	  DriveStruct[d].drv_type=drv_fam2;
 	}
-      else
-	{
-	  for (i=0;i<5;i++) if (infobuf[i]!=familyT[i]) break;
-	  if (i==5)
-	    {
-	      DriveStruct[d].drive_model[0]='C';
-	      DriveStruct[d].drive_model[1]='D';
-	      DriveStruct[d].drive_model[2]='-';
-	      DriveStruct[d].drive_model[3]='5';
-	      DriveStruct[d].drive_model[4]='5';
-	      DriveStruct[d].drive_model[5]='A';
-	      DriveStruct[d].drive_model[6]=infobuf[i++];
-	      DriveStruct[d].drive_model[7]=infobuf[i++];
-	      DriveStruct[d].drive_model[8]=0;
-	    }
-	  DriveStruct[d].drv_type=drv_famT; /* assumed, not sure here */
-	}
     }
 
   if (!DriveStruct[d].drv_type)
@@ -2692,14 +2564,6 @@
       if (j!=4) ask_mail();
       DriveStruct[d].drv_type=drv_260;
     }
-  else if (famT_drive)
-    {
-      printk("\n\nSBPCD: possibly TEAC CD-55A present.\n");
-      printk("SBPCD: support is not fulfilled yet - drive gets ignored.\n");
-      ask_mail();
-      DriveStruct[d].drv_type=0;
-      return (-1);
-    }
   else
     {
       j = (DriveStruct[d].firmware_version[0] & 0x0F) * 100 +
@@ -2717,15 +2581,18 @@
       else if (fam1_drive)
 	{
 	  if (j<100) DriveStruct[d].drv_type=drv_099;
-	  else DriveStruct[d].drv_type=drv_100;
+	  else
+	    {
+	      DriveStruct[d].drv_type=drv_100;
+	      ask_mail();
+	    }
 	}
       else if (fam2_drive)
 	{
 	  printk("\n\nSBPCD: new drive CD200 (%s)detected.\n",
 		 DriveStruct[d].firmware_version);
 	  printk("SBPCD: support is not fulfilled yet.\n");
-	  if (j!=101) /* only 1.01 known at time */
-	    ask_mail();
+	  if (j!=101) ask_mail(); /* only 1.01 known at time */
 	}
     }
   DPRINTF((DBG_LCS,"SBPCD: drive type %02X\n",DriveStruct[d].drv_type));
@@ -3035,9 +2902,7 @@
       if (i&s_attention) GetStatus();
     }
   else if (fam1_drive) GetStatus();
-  else /* CD200, CD-55A */
-    {
-    }
+  else if (fam2_drive) GetStatus();
   if (DriveStruct[d].CD_changed==0xFF)
     {
 #if MANY_SESSION
@@ -3134,15 +2999,17 @@
 	  flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
 	    f_obey_p_check | f_wait_if_busy;
 	}
+      else if (fam2_drive)
+	{
+	  drvcmd[0]=CMD2_PLAY_MSF;
+	  flags_cmd_out = f_putcmd | f_ResponseStatus;
+	}
       else if (fam0_drive)
 	{
 	  drvcmd[0]=CMD0_PLAY_MSF;
 	  flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
 	    f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
 	}
-      else /* CD200, CD-55A */
-	{
-	}
       drvcmd[1]=(pos_audio_start>>16)&0x00FF;
       drvcmd[2]=(pos_audio_start>>8)&0x00FF;
       drvcmd[3]=pos_audio_start&0x00FF;
@@ -3495,7 +3362,6 @@
 	if (fam0_drive) return (-EINVAL);
 	if (famL_drive) return (-EINVAL);
 	if (fam2_drive) return (-EINVAL);
-	if (famT_drive) return (-EINVAL);
 	if (DriveStruct[d].aud_buf==NULL) return (-EINVAL);
 	i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
 	if (i) return (i);
@@ -3559,7 +3425,7 @@
 		drvcmd[5]=0;
 		drvcmd[6]=read_audio.nframes; /* # of frames */
 	      }
-	    else /* CD200, CD-55A */
+	    else if (fam2_drive) /* CD200: not implemented yet */
 	      {
 	      }
 	    DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n"));
@@ -3743,29 +3609,26 @@
   int i, status_tries, data_tries;
   
 request_loop:
-
+  INIT_REQUEST;
   sti();
 
-  if ((CURRENT==NULL)||(CURRENT->dev<0)) goto done;
-  if (CURRENT -> sector == -1) goto done;
-
+  if ((CURRENT==NULL)||(CURRENT->dev<0)) goto err_done;
+  if (CURRENT -> sector == -1) goto err_done;
+  if (CURRENT->cmd != READ)
+    {
+      printk("SBPCD: bad cmd %d\n", CURRENT->cmd);
+      goto err_done;
+    }
   i = MINOR(CURRENT->dev);
   if ( (i<0) || (i>=NR_SBPCD) || (DriveStruct[i].drv_id==-1))
     {
       printk("SBPCD: do_request: bad device: %04X\n", CURRENT->dev);
-      goto done;
+      goto err_done;
     }
   switch_drive(i);
 
-  INIT_REQUEST;
   block = CURRENT->sector; /* always numbered as 512-byte-pieces */
   nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
-  if (CURRENT->cmd != READ)
-    {
-      printk("SBPCD: bad cmd %d\n", CURRENT->cmd);
-      end_request(0);
-      goto request_loop;
-    }
 
   DPRINTF((DBG_BSZ,"SBPCD: read sector %d (%d sectors)\n", block, nsect));
 #if 0
@@ -3822,13 +3685,11 @@
 	}
     }
   
+err_done:
+  busy_data=0;
   end_request(0);
   sbp_sleep(10);    /* wait a bit, try again */
   goto request_loop;
-
-done:
-  busy_data=0;
-  return;
 }
 /*==========================================================================*/
 /*
@@ -3842,32 +3703,6 @@
 
   DriveStruct[d].sbp_first_frame=DriveStruct[d].sbp_last_frame=-1;      /* purge buffer */
   block=CURRENT->sector/4;
-
-#if MULTISESSION_BY_DRIVER
-  if (!fam0_drive)
-    {
-#if MANY_SESSION
-      DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block)));
-      if (DriveStruct[d].f_multisession)
-	{
-	  DPRINTF((DBG_MUL,"SBPCD: ManySession: use %08X for %08X (msf)\n",
-		         blk2msf(DriveStruct[d].lba_multi+block),
-                         blk2msf(block)));
-	  block=DriveStruct[d].lba_multi+block;
-	}
-#else
-      if ((block<=DriveStruct[d].last_redirect)
-	  && (DriveStruct[d].f_multisession))
-	  {
-	    DPRINTF((DBG_MUL,"SBPCD: MultiSession: use %08X for %08X (msf)\n",
-		     blk2msf(DriveStruct[d].lba_multi+block),
-		     blk2msf(block)));
-	    block=DriveStruct[d].lba_multi+block;
-	  }
-#endif MANY_SESSION
-    }
-#endif MULTISESSION_BY_DRIVER
-
   if (block+SBP_BUFFER_FRAMES <= DriveStruct[d].CDsize_frm)
     DriveStruct[d].sbp_read_frames = SBP_BUFFER_FRAMES;
   else
@@ -3940,9 +3775,6 @@
       drvcmd[5]=DriveStruct[d].sbp_read_frames;
       drvcmd[6]=0x02;
     }
-  else /* CD-55A */
-    {
-    }
   SBPCD_CLI;
   for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
   SBPCD_STI;
@@ -4152,6 +3984,7 @@
 /*
  * try to keep an "open" counter here and lock the door if 0->1.
  */
+  MOD_INC_USE_COUNT;
   DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n",
 	   DriveStruct[d].open_count,DriveStruct[d].open_count+1));
   if (++DriveStruct[d].open_count==1)
@@ -4190,6 +4023,7 @@
 /*
  * try to keep an "open" counter here and unlock the door if 1->0.
  */
+  MOD_DEC_USE_COUNT;
   DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n",
 	   DriveStruct[d].open_count,DriveStruct[d].open_count-1));
   if (DriveStruct[d].open_count!=0) /* CDROMEJECT may have been done */
@@ -4254,7 +4088,9 @@
   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;
+  else if (!strcmp(s,str_sb_l)) sbpro_type=1;
   else if (!strcmp(s,str_sp)) sbpro_type=2;
+  else if (!strcmp(s,str_sp_l)) sbpro_type=2;
   if (p[0]>0) sbpcd_ioaddr=p[1];
 
   CDo_command=sbpcd_ioaddr;
@@ -4329,7 +4165,11 @@
 /*
  *  Test for presence of drive and initialize it.  Called at boot time.
  */
+#ifdef MODULE
+int init_module(void)
+#else
 unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
+#endif MODULE
 {
   int i=0, j=0;
   int addr[2]={1, CDROM_PORT};
@@ -4339,9 +4179,10 @@
 
   DPRINTF((DBG_INF,"SBPCD version %s\n", VERSION));
 
+#ifndef MODULE
   if (!setup_done)
     {
-      DPRINTF((DBG_INF,"SBPCD: Looking for Matsushita, Panasonic, CreativeLabs, IBM, Longshine, TEAC CD-ROM drives\n"));
+      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"));
@@ -4358,12 +4199,14 @@
       DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
       DPRINTF((DBG_WRN,"SBPCD: \n"));
     }
+#endif MODULE
   sbpcd_probe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
   sbpcd_probe[1]=sbpro_type; /* possibly changed by kernel command line */
 
   for (port_index=0;port_index<NUM_PROBE;port_index+=2)
     {
       addr[1]=sbpcd_probe[port_index];
+      if (addr[1]==0) break;
       if (check_region(addr[1],4))
 	{
 	  DPRINTF((DBG_INI,"SBPCD: check_region: %03X is not free.\n",addr[1]));
@@ -4384,14 +4227,6 @@
 	      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 */
@@ -4404,7 +4239,11 @@
 #if PRINTK_BUG
       sti(); /* to avoid possible "printk" bug */
 #endif
+#ifdef MODULE
+      return -EIO;
+#else
       goto init_done;
+#endif MODULE
     }
 
   if (port_index>0)
@@ -4501,7 +4340,11 @@
 #if PRINTK_BUG
       sti(); /* to avoid possible "printk" bug */
 #endif
+#ifdef MODULE
+      return -EIO;
+#else
       goto init_done;
+#endif MODULE
     }
   blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
   read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
@@ -4514,7 +4357,11 @@
       switch_drive(j);
 /*
  * allocate memory for the frame buffers
- */ 
+ */
+#ifdef MODULE
+      DriveStruct[j].sbp_buf=(u_char *) kmalloc(SBP_BUFFER_FRAMES*CD_FRAMESIZE, GFP_KERNEL);
+      DriveStruct[j].aud_buf=NULL;
+#else
       DriveStruct[j].sbp_buf=(u_char *)mem_start;
       mem_start += SBP_BUFFER_FRAMES*CD_FRAMESIZE;
       if ((fam1_drive) && (SBP_BUFFER_AUDIO_FRAMES>0))
@@ -4523,6 +4370,7 @@
 	  mem_start += SBP_BUFFER_AUDIO_FRAMES*CD_FRAMESIZE_RAW;
 	}
       else DriveStruct[j].aud_buf=NULL;
+#endif MODULE
 /*
  * set the block size
  */
@@ -4530,6 +4378,9 @@
     }
   blksize_size[MAJOR_NR]=sbpcd_blocksizes;
 
+#ifdef MODULE
+return (0);
+#else
 init_done:
 #if !(SBPCD_ISSUE-1)
 #ifdef CONFIG_SBPCD2
@@ -4546,7 +4397,34 @@
   DPRINTF((DBG_INF,"SBPCD: init done.\n"));
 #endif
   return (mem_start);
+#endif MODULE
+}
+/*==========================================================================*/
+#ifdef MODULE
+void cleanup_module(void)
+{
+  int j;
+
+  if (MOD_IN_USE)
+    {
+      printk("%s module in use - can't remove it.\n", major_name);
+      return;
+    }
+  if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+    {
+      printk("What's that: can't unregister %s\n", major_name);
+      return;
+    }
+  release_region(CDo_command,4);
+
+  for (j=0;j<NR_SBPCD;j++)
+    {
+      if (DriveStruct[j].drv_id==-1) continue;
+      kfree_s(DriveStruct[j].sbp_buf, SBP_BUFFER_FRAMES*CD_FRAMESIZE);
+    }
+  printk("%s module released.\n", major_name);
 }
+#endif MODULE
 /*==========================================================================*/
 /*
  * Check if the media has changed in the CD-ROM drive.
diff -u --recursive --new-file v1.2.0/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.2.0/linux/drivers/char/console.c	Thu Mar  9 20:37:34 1995
+++ linux/drivers/char/console.c	Fri Mar 17 14:31:40 1995
@@ -1686,11 +1686,8 @@
 				  case '@':  /* defined in ISO 2022 */
 					utf = 0;
 					continue;
-				  case '8':
-					/* ISO/ECMA hasn't yet registered an
-					   official ESC sequence for UTF-8,
-					   so this one (ESC %8) will likely
-					   change in the future. */
+				  case 'G':  /* prelim official escape code */
+				  case '8':  /* retained for compatibility */
 					utf = 1;
 					continue;
 				}
diff -u --recursive --new-file v1.2.0/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v1.2.0/linux/drivers/char/cyclades.c	Thu Feb 23 13:31:40 1995
+++ linux/drivers/char/cyclades.c	Fri Mar 17 14:31:40 1995
@@ -1,8 +1,11 @@
 static char rcsid[] =
-"$Revision: 1.35 $$Date: 1994/12/16 13:54:18 $";
+"$Revision: 1.36.1.1 $$Date: 1995/03/13 15:44:43 $";
 /*
  *  linux/kernel/cyclades.c
  *
+ * Maintained by Marcio Saito (cyclades@netcom.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org)
+ *
  * 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,
@@ -15,6 +18,27 @@
  *   int  cy_open(struct tty_struct *tty, struct file *filp);
  *
  * $Log: cyclades.c,v $
+ * Revision 1.36.1.1  1995/03/13  15:44:43  bentson
+ * initialize defaults for receive threshold and stale data timeout;
+ * cosmetic changes;
+ *
+ * Revision 1.36  1995/03/10  23:33:53  bentson
+ * added support of chips 4-7 in 32 port Cyclom-Ye;
+ * fix cy_interrupt pointer dereference problem
+ * (Joe Portman <baron@aa.net>);
+ * give better error response if open is attempted on non-existant port
+ * (Zachariah Vaum <jchryslr@netcom.com>);
+ * correct command timeout (Kenneth Lerman <lerman@@seltd.newnet.com>);
+ * conditional compilation for -16Y on systems with fast, noisy bus;
+ * comment out diagnostic print function;
+ * cleaned up table of base addresses;
+ * set receiver time-out period register to correct value,
+ * set receive threshold to better default values,
+ * set chip timer to more accurate 200 Hz ticking,
+ * add code to monitor and modify receive parameters
+ * (Rik Faith <faith@cs.unc.edu> Nick Simicich
+ * <njs@scifi.emi.net>);
+ *
  * Revision 1.35  1994/12/16  13:54:18  steffen
  * additional patch by Marcio Saito for board detection
  * Accidently left out in 1.34
@@ -199,6 +223,8 @@
 #undef  SERIAL_DEBUG_IO
 #undef  SERIAL_DEBUG_COUNT
 #undef  SERIAL_DEBUG_DTR
+#undef  CYCLOM_16Y_HACK
+#undef  CYCLOM_ENABLE_MONITORING
 
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
@@ -221,25 +247,37 @@
 static int cy_wild_int_mask;
 static unsigned char *intr_base_addr;
 
-/* This is the per-card data structure */
+/* This is the per-card data structure containing a card's base
+   address.  Here are declarations for some common addresses.
+   Add entries to match your configuration (there are sixty-
+   four possible from 0x80000 to 0xFE000) */
 struct cyclades_card cy_card[] = {
  /* BASE_ADDR */
-    {0xD0000,0},
-    {0xD2000,0},
-    {0xD4000,10},
-    {0xD6000,11},
-    {0xD8000,12},
-    {0xDA000,15}
+    {0xD0000},
+    {0xD2000},
+    {0xD4000},
+    {0xD6000},
+    {0xD8000},
+    {0xDA000},
+    {0xDC000},
+    {0xDE000}
 };
 
 #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))
+/*  The Cyclom-Ye has placed the sequential chips in non-sequential
+ *  address order.  This look-up table overcomes that problem.
  */
+int cy_chip_offset [] =
+    { 0x0000,
+      0x0400,
+      0x0800,
+      0x0C00,
+      0x0200,
+      0x0600,
+      0x0A00,
+      0x0E00
+    };
 
 /* This is the per-port data structure */
 struct cyclades_port cy_port[] = {
@@ -328,7 +366,7 @@
 
 static char baud_cor3[] = {  /* receive threshold */
         0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
-        0x0a,  0x0a,  0x0a,  0x08,  0x04,  0x02,  0x01,  0x01,  0x01,  0x01};
+        0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07};
 
 
 
@@ -338,7 +376,9 @@
 static void cy_unthrottle(struct tty_struct *);
 static void config_setup(struct cyclades_port *);
 extern void console_print(const char *);
+#ifdef CYCLOM_SHOW_STATUS
 static void show_status(int);
+#endif
 
 
 
@@ -399,7 +439,7 @@
 void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
 
 
-/* This routine waits up to 100 micro-seconds for the previous
+/* This routine waits up to 1000 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.
@@ -412,7 +452,7 @@
 
     save_flags(flags); cli();
 	/* Check to see that the previous command has completed */
-	for(i = 0 ; i < 10000 ; i++){
+	for(i = 0 ; i < 100 ; i++){
 	    if (base_addr[CyCCR] == 0){
 		break;
 	    }
@@ -456,7 +496,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-                   (cy_card[info->card].base_addr + chip * CyRegSize);
+                   (cy_card[info->card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
         base_addr[CyCAR] = (u_char)(channel & 0x0003); /* index channel */
@@ -487,7 +527,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-                   (cy_card[info->card].base_addr + chip * CyRegSize);
+                   (cy_card[info->card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
         base_addr[CyCAR] = (u_char)(channel & 0x0003);
@@ -562,8 +602,8 @@
     do{
         had_work = 0;
         for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
-	    base_addr = (unsigned char *)(cinfo->base_addr
-                                             + CyRegSize * chip);
+	    base_addr = (unsigned char *)
+	                   (cinfo->base_addr + cy_chip_offset[chip]);
             too_many = 0;
             while ( (status = base_addr[CySVRR]) != 0x00) {
                 had_work++;
@@ -661,6 +701,13 @@
                             /* load # characters available from the chip */
                             char_count = base_addr[CyRDCR];
 
+#ifdef CYCLOM_ENABLE_MONITORING
+			    ++info->mon.int_count;
+			    info->mon.char_count += char_count;
+			    if (char_count > info->mon.char_max)
+			       info->mon.char_max = char_count;
+			    info->mon.char_last = char_count;
+#endif
                             while(char_count--){
 				if (tty->flip.count >= TTY_FLIPBUF_SIZE){
                                         break;
@@ -669,6 +716,9 @@
                                 data = base_addr[CyRDSR];
 				*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
 				*tty->flip.char_buf_ptr++ = data;
+#ifdef CYCLOM_16Y_HACK
+				udelay(10L);
+#endif
                             }
                         }
                         queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
@@ -738,6 +788,10 @@
 			    base_addr[CySRER] &= ~CyTxMpty;
 			    goto txdone;
                         }
+			if (info->xmit_buf == 0){
+			    base_addr[CySRER] &= ~CyTxMpty;
+			    goto txdone;
+			}
 			if (info->tty->stopped || info->tty->hw_stopped){
 			    base_addr[CySRER] &= ~CyTxMpty;
 			    goto txdone;
@@ -1103,7 +1157,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
 #ifdef SERIAL_DEBUG_OPEN
     printk("startup card %d, chip %d, channel %d, base_addr %lx",
@@ -1113,7 +1167,9 @@
     save_flags(flags); cli();
 	base_addr[CyCAR] = (u_char)channel;
 
-	base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+	base_addr[CyRTPR] = (info->default_timeout
+			     ? info->default_timeout
+			     : 0x02); /* 10ms rx timeout */
 
 	write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR);
 
@@ -1155,7 +1211,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
     save_flags(flags); cli();
 	base_addr[CyCAR] = channel;
 	base_addr[CySRER] |= CyTxMpty;
@@ -1183,7 +1239,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
 #ifdef SERIAL_DEBUG_OPEN
     printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
@@ -1298,7 +1354,9 @@
     /* byte size and parity */
     info->cor5 = 0;
     info->cor4 = 0;
-    info->cor3 = baud_cor3[i]; /* receive threshold */
+    info->cor3 = (info->default_threshold
+		  ? info->default_threshold
+		  : baud_cor3[i]); /* receive threshold */
     info->cor2 = CyETC;
     switch(cflag & CSIZE){
     case CS5:
@@ -1343,7 +1401,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
 	base_addr[CyCAR] = (u_char)channel;
@@ -1369,7 +1427,9 @@
 
 	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
 
-	base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+	base_addr[CyRTPR] = (info->default_timeout
+			     ? info->default_timeout
+			     : 0x02); /* 10ms rx timeout */
 
 	if (C_CLOCAL(info->tty)) {
 	    base_addr[CySRER] |= 0; /* without modem intr */
@@ -1474,7 +1534,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
 	base_addr[CyCAR] = channel;
@@ -1636,7 +1696,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
 	base_addr[CyCAR] = (u_char)channel;
@@ -1677,7 +1737,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + chip * CyRegSize);
+		   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
 	base_addr[CyCAR] = (u_char)channel;
@@ -1768,7 +1828,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-                   (cy_card[card].base_addr + chip * CyRegSize);
+                   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     save_flags(flags); cli();
         base_addr[CyCAR] = (u_char)channel;
@@ -1799,7 +1859,7 @@
     chip = channel>>2;
     channel &= 0x03;
     base_addr = (unsigned char*)
-                   (cy_card[card].base_addr + chip * CyRegSize);
+                   (cy_card[card].base_addr + cy_chip_offset[chip]);
 
     switch (cmd) {
     case TIOCMBIS:
@@ -1891,6 +1951,120 @@
     }
 } /* send_break */
 
+static int
+get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
+{
+
+   memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor));
+   info->mon.int_count  = 0;
+   info->mon.char_count = 0;
+   info->mon.char_max   = 0;
+   info->mon.char_last  = 0;
+   return 0;
+}
+
+static int
+set_threshold(struct cyclades_port * info, unsigned long value)
+{
+   unsigned char *base_addr;
+   int card,channel,chip;
+   
+   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 + cy_chip_offset[chip]);
+
+   info->cor3 &= ~CyREC_FIFO;
+   info->cor3 |= value & CyREC_FIFO;
+   base_addr[CyCOR3] = info->cor3;
+   write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch);
+   return 0;
+}
+
+static int
+get_threshold(struct cyclades_port * info, unsigned long *value)
+{
+   unsigned char *base_addr;
+   int card,channel,chip;
+   unsigned long tmp;
+   
+   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 + cy_chip_offset[chip]);
+
+   tmp = base_addr[CyCOR3] & CyREC_FIFO;
+   put_fs_long(tmp,value);
+   return 0;
+}
+
+static int
+set_default_threshold(struct cyclades_port * info, unsigned long value)
+{
+   info->default_threshold = value & 0x0f;
+   return 0;
+}
+
+static int
+get_default_threshold(struct cyclades_port * info, unsigned long *value)
+{
+   put_fs_long(info->default_threshold,value);
+   return 0;
+}
+
+static int
+set_timeout(struct cyclades_port * info, unsigned long value)
+{
+   unsigned char *base_addr;
+   int card,channel,chip;
+   
+   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 + cy_chip_offset[chip]);
+
+   base_addr[CyRTPR] = value & 0xff;
+   return 0;
+}
+
+static int
+get_timeout(struct cyclades_port * info, unsigned long *value)
+{
+   unsigned char *base_addr;
+   int card,channel,chip;
+   unsigned long tmp;
+   
+   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 + cy_chip_offset[chip]);
+
+   tmp = base_addr[CyRTPR];
+   put_fs_long(tmp,value);
+   return 0;
+}
+
+static int
+set_default_timeout(struct cyclades_port * info, unsigned long value)
+{
+   info->default_timeout = value & 0xff;
+   return 0;
+}
+
+static int
+get_default_timeout(struct cyclades_port * info, unsigned long *value)
+{
+   put_fs_long(info->default_timeout,value);
+   return 0;
+}
 
 static int
 cy_ioctl(struct tty_struct *tty, struct file * file,
@@ -1905,6 +2079,63 @@
 #endif
 
     switch (cmd) {
+        case CYGETMON:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct cyclades_monitor));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_mon_info(info, (struct cyclades_monitor *)arg);
+	    break;
+        case CYGETTHRESH:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_threshold(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETTHRESH:
+            ret_val = set_threshold(info, (unsigned long)arg);
+	    break;
+        case CYGETDEFTHRESH:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_default_threshold(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETDEFTHRESH:
+            ret_val = set_default_threshold(info, (unsigned long)arg);
+	    break;
+        case CYGETTIMEOUT:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_timeout(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETTIMEOUT:
+            ret_val = set_timeout(info, (unsigned long)arg);
+	    break;
+        case CYGETDEFTIMEOUT:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_default_timeout(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETDEFTIMEOUT:
+            ret_val = set_default_timeout(info, (unsigned long)arg);
+	    break;
         case TCSBRK:    /* SVID version: non-zero arg --> no break */
 	    ret_val = tty_check_change(tty);
 	    if (ret_val)
@@ -2212,7 +2443,7 @@
     channel = info->line - cinfo->first_line;
     chip = channel>>2;
     channel &= 0x03;
-    base_addr = (char *) (cinfo->base_addr + chip * CyRegSize);
+    base_addr = (char *) (cinfo->base_addr + cy_chip_offset[chip]);
 
     while (1) {
 	save_flags(flags); cli();
@@ -2293,6 +2524,9 @@
         return -ENODEV;
     }
     info = &cy_port[line];
+    if (info->line < 0){
+        return -ENODEV;
+    }
 #ifdef SERIAL_DEBUG_OTHER
     printk("cy_open ttyC%d\n", info->line); /* */
 #endif
@@ -2372,16 +2606,18 @@
 /* initialize chips on card -- return number of valid
    chips (which is number of ports/4) */
 int
-cy_init_card(unsigned char *base_addr)
+cy_init_card(unsigned char *true_base_addr)
 {
   volatile unsigned short discard;
   unsigned int chip_number;
+  unsigned char* base_addr;
 
-    discard = base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
-    discard = base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
+    discard = true_base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
+    discard = true_base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
     udelay(500L);
 
     for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+        base_addr = true_base_addr + cy_chip_offset[chip_number];
         udelay(1000L);
         if(base_addr[CyCCR] != 0x00){
             /*************
@@ -2408,13 +2644,11 @@
             return chip_number;
         }
         base_addr[CyGCR] = CyCH0_SERIAL;
-        base_addr[CyPPR] = CyCLOCK_25_1MS * 5; /* run clock at 200 Hz */
+        base_addr[CyPPR] = 244; /* better value than CyCLOCK_25_1MS * 5
+                                                  to 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;
@@ -2567,6 +2801,8 @@
     printk("cyc: %d: setting count to 0\n", __LINE__);
 #endif
 		info->blocked_open = 0;
+		info->default_threshold = 0;
+		info->default_timeout = 0;
 		info->tqueue.routine = do_softint;
 		info->tqueue.data = info;
 		info->callout_termios =cy_callout_driver.init_termios;
@@ -2575,7 +2811,9 @@
 		info->close_wait = 0;
 		/* info->session */
 		/* info->pgrp */
-		/* info->read_status_mask */
+/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
+		info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK
+                                       | CyPARITY| CyFRAME| CyOVERRUN;
 		/* info->timeout */
 
 		printk("ttyC%1d ", info->line);
@@ -2597,6 +2835,7 @@
     
 } /* cy_init */
 
+#ifdef CYCLOM_SHOW_STATUS
 static void
 show_status(int line_num)
 {
@@ -2638,7 +2877,7 @@
     save_flags(flags); cli();
 
 	base_addr = (unsigned char*)
-		       (cy_card[card].base_addr + chip * CyRegSize);
+		       (cy_card[card].base_addr + cy_chip_offset[chip]);
 
 /* Global Registers */
 
@@ -2693,5 +2932,5 @@
 
     restore_flags(flags);
 } /* show_status */
-
+#endif
 
diff -u --recursive --new-file v1.2.0/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.2.0/linux/drivers/char/tty_io.c	Mon Feb 20 21:29:52 1995
+++ linux/drivers/char/tty_io.c	Fri Mar 17 14:31:51 1995
@@ -1051,7 +1051,9 @@
 		if (o_tty->ldisc.close)
 			(o_tty->ldisc.close)(o_tty);
 		o_tty->ldisc = ldiscs[N_TTY];
+#if 0 /* No way! We just released the termios struct! */
 		o_tty->termios->c_line = N_TTY;
+#endif
 	}
 	
 	tty->driver.table[idx] = NULL;
@@ -1659,7 +1661,7 @@
 	int	retval;
 	struct tty_driver *p;
 	int	found = 0;
-	int	major_inuse = 0;
+	char *othername = NULL;
 	
 	if (*driver->refcount)
 		return -EBUSY;
@@ -1668,14 +1670,15 @@
 		if (p == driver)
 			found++;
 		else if (p->major == driver->major)
-			major_inuse++;
+			othername = p->name;
 	}
 
-	if (!major_inuse) {
+	if (othername == NULL) {
 		retval = unregister_chrdev(driver->major, driver->name);
 		if (retval)
 			return retval;
-	}
+	} else
+		register_chrdev(driver->major, othername, &tty_fops);
 
 	if (driver->prev)
 		driver->prev->next = driver->next;
diff -u --recursive --new-file v1.2.0/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v1.2.0/linux/drivers/net/ne.c	Mon Jan  9 07:56:28 1995
+++ linux/drivers/net/ne.c	Fri Mar 17 14:31:40 1995
@@ -45,6 +45,7 @@
     {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
     {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
     {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh?  */
+    {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
     {0,}
 };
 
diff -u --recursive --new-file v1.2.0/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v1.2.0/linux/include/linux/cyclades.h	Mon Dec 19 16:31:57 1994
+++ linux/include/linux/cyclades.h	Fri Mar 17 14:31:40 1995
@@ -9,6 +9,13 @@
   int filler;
 };
 
+struct cyclades_monitor {
+        unsigned long           int_count;
+        unsigned long           char_count;
+        unsigned long           char_max;
+        unsigned long           char_last;
+};
+
 /*
  * This is our internal structure for each serial port's state.
  * 
@@ -45,14 +52,28 @@
 	int			xmit_head;
 	int			xmit_tail;
 	int			xmit_cnt;
+        int                     default_threshold;
+        int                     default_timeout;
 	struct tq_struct	tqueue;
 	struct termios		normal_termios;
 	struct termios		callout_termios;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
+        struct cyclades_monitor mon;
 };
 
 #define CYCLADES_MAGIC  0x4359
+
+#define CYGETMON                0x435901
+#define CYGETTHRESH             0x435902
+#define CYSETTHRESH             0x435903
+#define CYGETDEFTHRESH          0x435904
+#define CYSETDEFTHRESH          0x435905
+#define CYGETTIMEOUT            0x435906
+#define CYSETTIMEOUT            0x435907
+#define CYGETDEFTIMEOUT         0x435908
+#define CYSETDEFTIMEOUT         0x435909
+
 /*
  * Events are used to schedule things to happen at timer-interrupt
  * time, instead of at cy interrupt time.
diff -u --recursive --new-file v1.2.0/linux/include/linux/elf.h linux/include/linux/elf.h
--- v1.2.0/linux/include/linux/elf.h	Sun Feb  5 14:44:32 1995
+++ linux/include/linux/elf.h	Sat Mar 11 14:10:14 1995
@@ -227,6 +227,14 @@
 #define	ELFCLASS64	2
 #define	ELFCLASSNUM	3
 
+#define ELFDATANONE	0		/* e_ident[EI_DATA] */
+#define ELFDATA2LSB	1
+#define ELFDATA2MSB	2
+
+#define EV_NONE		0		/* e_version, EI_VERSION */
+#define EV_CURRENT	1
+#define EV_NUM		2
+
 #define ELF_START_MMAP 0x80000000
 
 #endif /* _LINUX_ELF_H */
diff -u --recursive --new-file v1.2.0/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.2.0/linux/include/linux/ip_fw.h	Thu Mar  9 20:37:36 1995
+++ linux/include/linux/ip_fw.h	Thu Mar  9 20:33:55 1995
@@ -80,7 +80,7 @@
 				 * (ports[0] <= port <= ports[1])     *
 				 *                                    */
 #define IP_FW_F_PRN	0x020	/* In verbose mode print this firewall*/
-#define IP_FW_F_BIDIR	0x040	/* For accounting-count two way       */
+#define IP_FW_F_BIDIR	0x040	/* For bidirectional firewalls        */
 #define IP_FW_F_TCPSYN	0x080	/* For tcp packets-check SYN only     */
 #define IP_FW_F_ICMPRPL 0x100	/* Send back icmp unreachable packet  */
 #define IP_FW_F_MASK	0x1FF	/* All possible flag bits mask        */
@@ -111,6 +111,15 @@
 #define IP_ACCT_FLUSH    (IP_FW_BASE_CTL+18)
 #define IP_ACCT_ZERO     (IP_FW_BASE_CTL+19)
 
+struct ip_fwpkt
+{
+	struct iphdr fwp_iph;			/* IP header */
+	union {
+		struct tcphdr fwp_tcph;		/* TCP header or */
+		struct udphdr fwp_udph;		/* UDP header */
+	} fwp_protoh;
+	struct in_addr fwp_via;			/* interface address */
+};
 
 /*
  *	Main firewall chains definitions and global var's definitions.
@@ -129,10 +138,10 @@
 #endif
 #ifdef CONFIG_IP_ACCT
 extern struct ip_fw *ip_acct_chain;
-extern int ip_acct_cnt(struct iphdr *, struct device *, struct ip_fw *);
+extern void ip_acct_cnt(struct iphdr *, struct device *, struct ip_fw *);
 extern int ip_acct_ctl(int, void *, int);
 #endif
-extern int ip_fw_chk(struct iphdr *, struct device *rif,struct ip_fw *, int);
+extern int ip_fw_chk(struct iphdr *, struct device *rif,struct ip_fw *, int, int);
 #endif /* KERNEL */
 
 #endif /* _IP_FW_H */
diff -u --recursive --new-file v1.2.0/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.2.0/linux/include/linux/sbpcd.h	Sun Jan 29 20:28:43 1995
+++ linux/include/linux/sbpcd.h	Wed Mar 15 08:47:38 1995
@@ -120,9 +120,8 @@
 #define DBG_AUD		25      /* "read audio" debugging */
 #define DBG_SEQ		26      /* Sequoia interface configuration trace */
 #define DBG_LCS		27      /* Longshine LCS-7260 debugging trace */
-#define DBG_TEA		28      /* TEAC CD-55A debugging trace */
-#define DBG_CD2		29      /* MKE CD200 debugging trace */
-#define DBG_000		30      /* unnecessary information */
+#define DBG_CD2		28      /* MKE CD200 debugging trace */
+#define DBG_000		29      /* unnecessary information */
 
 /*==========================================================================*/
 /*==========================================================================*/
@@ -254,13 +253,11 @@
 #define drv_099 (drv_fam1+0x01)    /* <100 */
 #define drv_100 (drv_fam1+0x02)    /* >=100 */
 
-#define drv_famT 0x40    /* TEAC CD-55A */
 #define drv_fam2 0x80    /* CD200 family */
 
 #define fam0_drive (DriveStruct[d].drv_type&drv_fam0)
 #define famL_drive (DriveStruct[d].drv_type&drv_famL)
 #define fam1_drive (DriveStruct[d].drv_type&drv_fam1)
-#define famT_drive (DriveStruct[d].drv_type&drv_famT)
 #define fam2_drive (DriveStruct[d].drv_type&drv_fam2)
 #define fam0L_drive (DriveStruct[d].drv_type&(drv_fam0|drv_famL))
 #define fam1L_drive (DriveStruct[d].drv_type&(drv_fam1|drv_famL))
@@ -522,19 +519,15 @@
  * CR-56x:      CMD1_
  * CD200:       CMD2_
  * LCS-7260:    CMDL_
- * TEAC CD-55A: CMDT_
  */
 #define CMD1_RESET	0x0a
 #define CMD2_RESET	0x01
-#define CMDT_RESET	0xc0
 #define CMD1_LOCK_CTL	0x0c
 #define CMD2_LOCK_CTL	0x1e
 #define CMDL_LOCK_CTL	0x0e
-#define CMDT_LOCK_CTL	0x1e
 #define CMD1_TRAY_CTL	0x07
 #define CMD2_TRAY_CTL	0x1b
 #define CMDL_TRAY_CTL	0x0d
-#define CMDT_TRAY_CTL	0x1b
 #define CMD1_MULTISESS	0x8d
 #define CMDL_MULTISESS	0x8c
 #define CMD1_SUBCHANINF	0x11
@@ -542,7 +535,6 @@
 #define CMD2_x02	0x02
 #define CMD1_x08	0x08
 #define CMD2_x08	0x08
-#define CMDT_x08	0x08
 #define CMD2_SETSPEED	0xda
 
 #define CMD0_PATH_CHECK	0x00
@@ -553,12 +545,10 @@
 #define CMD1_SEEK	0x01
 #define CMD2_SEEK	0x2b
 #define CMDL_SEEK	0x01
-#define CMDT_SEEK	0x2b
 #define CMD0_READ	0x02
 #define CMD1_READ	0x10
 #define CMD2_READ	0x28
 #define CMDL_READ	0x02
-#define CMDT_READ	0x28
 #define CMD0_READ_XA	0x03
 #define CMD2_READ_XA	0xd4
 #define CMDL_READ_XA	0x03 /* really ?? */
@@ -585,33 +575,27 @@
 #define CMD1_PLAY_MSF	0x0e
 #define CMD2_PLAY_MSF	0x47
 #define CMDL_PLAY_MSF	0x
-#define CMDT_PLAY_MSF	0x47
 #define CMD0_PLAY_TI	0x0c
 #define CMD0_STATUS	0x81
 #define CMD1_STATUS	0x05
 #define CMD2_STATUS	0x00
 #define CMDL_STATUS	0x81
-#define CMDT_STATUS	0x00
 #define CMD0_READ_ERR	0x82
 #define CMD1_READ_ERR	0x82
 #define CMD2_READ_ERR	0x03
 #define CMDL_READ_ERR	0x82
-#define CMDT_READ_ERR	0x03 /* get audio status */
 #define CMD0_READ_VER	0x83
 #define CMD1_READ_VER	0x83
 #define CMD2_READ_VER	0x12
-#define CMDT_READ_VER	0x12 /* ??? (unused) */
 #define CMDL_READ_VER	0x83
 #define CMD0_SETMODE	0x84
 #define CMD1_SETMODE	0x09
 #define CMD2_SETMODE	0x55
 #define CMDL_SETMODE	0x84
-#define CMDT_SETMODE	0x55
 #define CMD0_GETMODE	0x85
 #define CMD1_GETMODE	0x84
 #define CMD2_GETMODE	0x5a
 #define CMDL_GETMODE	0x85
-#define CMDT_GETMODE	0x5a
 #define CMD0_SET_XA	0x86
 #define CMD0_GET_XA	0x87
 #define CMD0_CAPACITY	0x88
@@ -622,13 +606,11 @@
 #define CMD1_READSUBQ	0x87
 #define CMD2_READSUBQ	0x42
 #define CMDL_READSUBQ	0x89
-#define CMDT_READSUBQ	0x42
 #define CMD0_DISKCODE	0x8a
 #define CMD0_DISKINFO	0x8b
 #define CMD1_DISKINFO	0x8b
 #define CMD2_DISKINFO	0x43
 #define CMDL_DISKINFO	0x8b
-#define CMDT_DISKINFO	0x43
 #define CMD0_READTOC	0x8c
 #define CMD1_READTOC	0x8c
 #define CMD2_READTOC	0x
@@ -637,7 +619,6 @@
 #define CMD1_PAU_RES	0x0d
 #define CMD2_PAU_RES	0x4b
 #define CMDL_PAU_RES	0x8d
-#define CMDT_PAU_RES	0x4b
 #define CMD0_PACKET	0x8e
 #define CMD1_PACKET	0x8e
 #define CMD2_PACKET	0x
diff -u --recursive --new-file v1.2.0/linux/net/inet/README linux/net/inet/README
--- v1.2.0/linux/net/inet/README	Tue Oct 25 18:10:14 1994
+++ linux/net/inet/README	Fri Mar 10 12:49:24 1995
@@ -1,21 +1,42 @@
-Changes for NET3.017
+Upgrade Notes from 1.0
+[Alan Cox -  Alan.Cox@linux.org]
 
-This is mostly small stuff as follows:
+Upgrading to 1.2.0 from a 1.0 kernel networking set. If you are using
+a complete 1.2 distribution you can ignore this.
 
-o	accept()ed socket don't end up with an invalid sk->socket and give bogus
-	netstat output.
-o	FASYNC/SIGIO now works with sockets.
-o	Fixed the permissions on F_SETOWN for all. Its now as broken/working
-	as other systems. Really we need something like a 32bit generation
-	number on processes.
-o	ARP allows proxy for whole networks (a la cisco routers)
-o	TCP sendto() reports ENOTCONN in the right cases
-o	Removed some surplus uncommented code from tcp.c
-o	Fixed protocol violation during closedown in tcp.c
-	[Still not got the window < MSS bug fix included]
+This doesn't attempt to list the changes. That would be too large. Instead
+just what you need and can change
 
-Fixes for 1.1.58
+arp,ifconfig, etc. Get net-tools-1.1.95 (or 1.2.0 if its out) from
+ftp.linux.org.uk:/pub/Linux/Networking/PROGRAMS/NetTools, and install
+these. You will also acquire a couple of new tools "plipconfig" for tuning
+plip links and "ipfw" for ip firewall management.
 
-o	non blocking connect fail gets the error code right.
-o	select() not reporting read ok after an urgent read fixed.
+bootpd: The original bootpd has a bug that the 1.2 kernel spots. You will
+need to upgrade this to the version in
+ftp.linux.org.uk:/pub/Linux/Networking/PROGRAMS/Upgrades
 
+
+Standard programs that you ought to update are
+
+named 4.7.x	to 4.9.x	Stops named dying occasionally
+pop3d 1.001     to 1.004	Fixes a bug that can lose mail
+
+A complete current networking set for Linux can be obtained by getting
+the NetKit[A,B...] series archives from ftp.funet.fi. Funet also carries
+binaries for Linux mbone applications if you now wish to make use of
+these facilities.
+
+For commercial UK custom Linux networking projects, drivers and development
+(but not free support!) I can be contacted via
+
+	I^2IT Ltd, The Innovation Centre, University Of Wales
+		Swansea SA2 8PP.  
+	Fax: +44 1792 295811
+	Tel: +44 1792 295213
+
+Please don't send commercial queries to my email address as I have that
+in an academic and _not_ commercial capacity. On the other hand feel
+free to send bug reports, queries and enhancements that way.
+
+Alan
diff -u --recursive --new-file v1.2.0/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.2.0/linux/net/inet/arp.c	Thu Feb 23 13:26:37 1995
+++ linux/net/inet/arp.c	Fri Mar 10 12:29:54 1995
@@ -130,9 +130,15 @@
 
 #define ARP_CHECK_INTERVAL	(60 * HZ)
 
+enum proxy {
+   PROXY_EXACT=0,
+   PROXY_ANY,
+   PROXY_NONE,
+};
+
 /* Forward declarations. */
 static void arp_check_expire (unsigned long);  
-static struct arp_table *arp_lookup(unsigned long paddr, int exact);
+static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy);
 
 
 static struct timer_list arp_timer =
@@ -497,6 +503,7 @@
 	struct arp_table **pentry;
 	unsigned long hash = HASH(ip_addr);
 
+ugly:
 	cli();
 	pentry = &arp_tables[hash];
 	if (! *pentry) /* also check proxy entries */
@@ -512,7 +519,12 @@
 			del_timer(&entry->timer);
 			sti();
 			arp_release_entry(entry);
-			return;
+			/* this would have to be cleaned up */
+			goto ugly;
+			/* perhaps like this ?
+			cli();
+			entry = *pentry;
+			*/
 		}
 		pentry = &entry->next;
 		if (!checked_proxies && ! *pentry)
@@ -842,7 +854,7 @@
 	/*
 	 *	Find an entry
 	 */
-	entry = arp_lookup(paddr, 1);
+	entry = arp_lookup(paddr, PROXY_NONE);
 
 	if (entry != NULL) 	/* It exists */
 	{
@@ -1006,11 +1018,11 @@
 
 /*
  *	This will find an entry in the ARP table by looking at the IP address.
- *      If exact is true then only exact IP matches will be allowed
+ *      If proxy is PROXY_EXACT then only exact IP matches will be allowed
  *      for proxy entries, otherwise the netmask will be used
  */
 
-static struct arp_table *arp_lookup(unsigned long paddr, int exact)
+static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy)
 {
 	struct arp_table *entry;
 	unsigned long hash = HASH(paddr);
@@ -1019,9 +1031,10 @@
 		if (entry->ip == paddr) break;
 
 	/* it's possibly a proxy entry (with a netmask) */
-	if (!entry)
+	if (!entry && proxy != PROXY_NONE)
 	for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next)
-	  if (exact? (entry->ip==paddr) : !((entry->ip^paddr)&entry->mask)) 
+	  if ((proxy==PROXY_EXACT) ? (entry->ip==paddr)
+	                           : !((entry->ip^paddr)&entry->mask)) 
 	    break;	  
 
 	return entry;
@@ -1099,7 +1112,7 @@
 	/*
 	 *	Find the entry
 	 */
-	entry = arp_lookup(ip, 1);
+	entry = arp_lookup(ip, PROXY_EXACT);
 	if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL))
 	{
 		sti();
@@ -1179,7 +1192,7 @@
 	
 	si = (struct sockaddr_in *) &r.arp_pa;
 	cli();
-	entry = arp_lookup(si->sin_addr.s_addr,0);
+	entry = arp_lookup(si->sin_addr.s_addr,PROXY_ANY);
 
 	if (entry == NULL)
 	{
diff -u --recursive --new-file v1.2.0/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.2.0/linux/net/inet/ip.c	Thu Mar  9 20:37:37 1995
+++ linux/net/inet/ip.c	Thu Mar  9 20:33:55 1995
@@ -109,6 +109,7 @@
 #include "protocol.h"
 #include "route.h"
 #include "tcp.h"
+#include "udp.h"
 #include <linux/skbuff.h>
 #include "sock.h"
 #include "arp.h"
@@ -1270,7 +1271,7 @@
 #ifdef CONFIG_IP_FIREWALL
 	int err;
 	
-	if((err=ip_fw_chk(skb->h.iph, dev, ip_fw_fwd_chain, ip_fw_fwd_policy))!=1)
+	if((err=ip_fw_chk(skb->h.iph, dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0))!=1)
 	{
 		if(err==-1)
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
@@ -1500,7 +1501,7 @@
 
 #ifdef	CONFIG_IP_FIREWALL
 	
-	if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy))!=1)
+	if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))!=1)
 	{
 		if(err==-1)
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
@@ -1816,6 +1817,12 @@
 	iph = (struct iphdr *)ptr;
 	skb->ip_hdr = iph;
 	iph->tot_len = ntohs(skb->len-dev->hard_header_len);
+
+#ifdef CONFIG_IP_FIREWALL
+	if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) != 1)
+		/* just don't send this packet */
+		return;
+#endif	
 
 	/*
 	 *	No reassigning numbers to fragments...
diff -u --recursive --new-file v1.2.0/linux/net/inet/ip_fw.c linux/net/inet/ip_fw.c
--- v1.2.0/linux/net/inet/ip_fw.c	Thu Mar  9 20:37:37 1995
+++ linux/net/inet/ip_fw.c	Fri Mar 10 10:07:14 1995
@@ -16,6 +16,9 @@
  *	appear it's not practical - Read: It works, it's not clean but please
  *	don't consider it to be his standard of finished work.
  *		Alan Cox 12/Feb/1995
+ *	Porting bidirectional entries from BSD, fixing accounting issues,
+ *	adding struct ip_fwpkt for checking packets with interface address
+ *		Jos Vos 5/Mar/1995.
  *
  *	All the real work was done by .....
  */
@@ -133,12 +136,17 @@
 
 
 /*
- *	Returns 0 if packet should be dropped, 1 or more if it should be accepted.
+ *	Returns 0 if packet should be dropped, 1 if it should be accepted,
+ *	and -1 if an ICMP host unreachable packet should be sent.
  *	Also does accounting so you can feed it the accounting chain.
+ *	If opt is set to 1, it means that we do this for accounting
+ *	purposes (searches all entries and handles fragments different).
+ *	If opt is set to 2, it doesn't count a matching packet, which
+ *	is used when calling this for checking purposes (IP_FW_CHK_*).
  */
 
 
-int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int policy)
+int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int policy, int opt)
 {
 	struct ip_fw *f;
 	struct tcphdr		*tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl);
@@ -146,7 +154,7 @@
 	__u32			src, dst;
 	__u16			src_port=0, dst_port=0;
 	unsigned short		f_prt=0, prt;
-	char			notcpsyn=1;
+	char			notcpsyn=1, frag1, match;
 	unsigned short		f_flag;
 
 	/*
@@ -172,7 +180,9 @@
 	 *	of system.
 	 */
 
-	if (ip->frag_off&IP_OFFSET)
+	frag1 = ((ntohs(ip->frag_off) & IP_OFFSET) == 0);
+	if (!frag1 && (opt != 1) && (ip->protocol == IPPROTO_TCP ||
+			ip->protocol == IPPROTO_UDP))
 		return(1);
 
 	src = ip->saddr;
@@ -191,16 +201,23 @@
 	{
 		case IPPROTO_TCP:
 			dprintf1("TCP ");
-			src_port=ntohs(tcp->source);
-			dst_port=ntohs(tcp->dest);
-			if(tcp->syn && !tcp->ack)
-				notcpsyn=0; /* We *DO* have SYN, value FALSE */
+			/* ports stay 0 if it is not the first fragment */
+			if (frag1) {
+				src_port=ntohs(tcp->source);
+				dst_port=ntohs(tcp->dest);
+				if(tcp->syn && !tcp->ack)
+					/* We *DO* have SYN, value FALSE */
+					notcpsyn=0;
+			}
 			prt=IP_FW_F_TCP;
 			break;
 		case IPPROTO_UDP:
 			dprintf1("UDP ");
-			src_port=ntohs(udp->source);
-			dst_port=ntohs(udp->dest);
+			/* ports stay 0 if it is not the first fragment */
+			if (frag1) {
+				src_port=ntohs(udp->source);
+				dst_port=ntohs(udp->dest);
+			}
 			prt=IP_FW_F_UDP;
 			break;
 		case IPPROTO_ICMP:
@@ -214,10 +231,12 @@
 	}
 	dprint_ip(ip->saddr);
 	
-	if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) 
+	if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+		/* This will print 0 when it is not the first fragment! */
 		dprintf2(":%d ", src_port);
 	dprint_ip(ip->daddr);
-	if ( ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) 
+	if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+		/* This will print 0 when it is not the first fragment! */
 		dprintf2(":%d ",dst_port);
 	dprintf1("\n");
 
@@ -228,8 +247,32 @@
 		 *	an interface chain as you do in BSD - same logic
 		 *	however.
 		 */
+
+		/*
+		 *	Match can become 0x01 (a "normal" match was found),
+		 *	0x02 (a reverse match was found), and 0x03 (the
+		 *	IP addresses match in both directions).
+		 *	Now we know in which direction(s) we should look
+		 *	for a match for the TCP/UDP ports.  Both directions
+		 *	might match (e.g., when both addresses are on the
+		 *	same network for which an address/mask is given), but
+		 *	the ports might only match in one direction.
+		 *	This was obviously wrong in the original BSD code.
+		 */
+		match = 0x00;
+
 		if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr
-		&&  (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr) 
+		&&  (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+			/* normal direction */
+			match |= 0x01;
+
+		if ((f->fw_flg & IP_FW_F_BIDIR) &&
+		    (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr
+		&&  (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+			/* reverse direction */
+			match |= 0x02;
+
+		if (match)
 		{
 			/*
 			 *	Look for a VIA match 
@@ -270,11 +313,15 @@
 			if(prt!=f_prt)
 				continue;
 				
-			if(!(prt==IP_FW_F_ICMP ||(
+			if(!(prt==IP_FW_F_ICMP || ((match & 0x01) &&
 				port_match(&f->fw_pts[0], f->fw_nsp, src_port,
 					f->fw_flg&IP_FW_F_SRNG) &&
 				port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port,
-					f->fw_flg&IP_FW_F_SRNG))))
+					f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) &&
+				port_match(&f->fw_pts[0], f->fw_nsp, dst_port,
+					f->fw_flg&IP_FW_F_SRNG) &&
+				port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port,
+					f->fw_flg&IP_FW_F_DRNG))))
 			{
 				continue;
 			}
@@ -287,10 +334,14 @@
 
 		if (f->fw_flg & IP_FW_F_PRN)
 		{
-			if(f->fw_flg&IP_FW_F_ACCEPT)
-				printk("Accept ");
-			else
-				printk("Deny ");
+			if(opt != 1) {
+				if(f->fw_flg&IP_FW_F_ACCEPT)
+					printk("Accept ");
+				else if(f->fw_flg&IP_FW_F_ICMPRPL)
+					printk("Reject ");
+				else
+					printk("Deny ");
+			}
 			switch(ip->protocol)
 			{
 				case IPPROTO_TCP:
@@ -315,22 +366,24 @@
 			printk("\n");
 		}
 #endif		
-		if(f->fw_flg&IP_FW_F_ACCEPT)
-		{
+		if (opt != 2) {
 			f->fw_bcnt+=ntohs(ip->tot_len);
 			f->fw_pcnt++;
-			return 1;
 		}
-		break;
+		if (opt != 1)
+			break;
 	} /* Loop */
 	
+	if(opt == 1)
+		return 0;
+
 	/*
-	 * If we get here then none of the firewalls matched or one matched
-	 * but was a no. So now we rely on policy defined in the rejecting
-	 * entry or if none was found in the policy variable
+	 * We rely on policy defined in the rejecting entry or, if no match
+	 * was found, we rely on the general policy variable for this type
+	 * of firewall.
 	 */
 
-	if(f!=NULL)	/* A deny match */
+	if(f!=NULL)	/* A match was found */
 		f_flag=f->fw_flg;
 	else
 		f_flag=policy;
@@ -342,9 +395,6 @@
 }
 
 
-
-
-
 static void zero_fw_chain(struct ip_fw *chainptr)
 {
 	struct ip_fw *ctmp=chainptr;
@@ -526,6 +576,10 @@
 						addb4--;
 skip_check:
 				}
+				/* finally look at the interface address */
+				if ((addb4 == 0) && ftmp->fw_via.s_addr &&
+						!(chtmp->fw_via.s_addr))
+					addb4++;
 			}
 			if (addb4>0) 
 			{
@@ -686,9 +740,10 @@
 
 #ifdef CONFIG_IP_ACCT
 
-int ip_acct_cnt(struct iphdr *iph, struct device *dev, struct ip_fw *f)
+void ip_acct_cnt(struct iphdr *iph, struct device *dev, struct ip_fw *f)
 {
-	return ip_fw_chk(iph, dev, f, 0);
+	(void) ip_fw_chk(iph, dev, f, 0, 1);
+	return;
 }
 
 int ip_acct_ctl(int stage, void *m, int len)
@@ -738,6 +793,8 @@
 #ifdef CONFIG_IP_FIREWALL
 int ip_fw_ctl(int stage, void *m, int len)
 {
+	int ret;
+
 	if ( stage == IP_FW_FLUSH_BLK )
 	{
 		free_fw_chain(&ip_fw_blk_chain);
@@ -775,18 +832,21 @@
 
 	if ( stage == IP_FW_CHK_BLK || stage == IP_FW_CHK_FWD )
 	{
+		struct device viadev;
+		struct ip_fwpkt *ipfwp;
 		struct iphdr *ip;
 
-		if ( len < sizeof(struct iphdr) + 2 * sizeof(unsigned short) )
+		if ( len < sizeof(struct ip_fwpkt) )
 		{
 #ifdef DEBUG_CONFIG_IP_FIREWALL
-			printf("ip_fw_ctl: len=%d, want at least %d\n",
-				len,sizeof(struct ip) + 2 * sizeof(unsigned short));
+			printf("ip_fw_ctl: length=%d, expected %d\n",
+				len, sizeof(struct ip_fwpkt));
 #endif
 			return( EINVAL );
 		}
 
-	 	ip = (struct iphdr *)m;
+	 	ipfwp = (struct ip_fwpkt *)m;
+	 	ip = &(ipfwp->fwp_iph);
 
 		if ( ip->ihl != sizeof(struct iphdr) / sizeof(int))
 		{
@@ -797,15 +857,19 @@
 			return(EINVAL);
 		}
 
-		if ( ip_fw_chk(ip, NULL,
+		viadev.pa_addr = ipfwp->fwp_via.s_addr;
+
+		if ((ret = ip_fw_chk(ip, &viadev,
 			stage == IP_FW_CHK_BLK ?
 	                ip_fw_blk_chain : ip_fw_fwd_chain,
 			stage == IP_FW_CHK_BLK ?
-	                ip_fw_blk_policy : ip_fw_fwd_policy )
-		       ) 
+	                ip_fw_blk_policy : ip_fw_fwd_policy, 2 )) > 0
+		   )
 			return(0);
-	    	else	
-			return(EACCES);
+	    	else if (ret == -1)	
+			return(ECONNREFUSED);
+		else
+			return(ETIMEDOUT);
 	}
 
 /*
@@ -858,7 +922,7 @@
 	off_t pos=0, begin=0;
 	struct ip_fw *i;
 	unsigned long flags;
-	int len;
+	int len, p;
 	
 
 	switch(stage)
@@ -866,12 +930,12 @@
 #ifdef CONFIG_IP_FIREWALL
 		case IP_INFO_BLK:
 			i = ip_fw_blk_chain;
-			len=sprintf(buffer, "IP firewall block rules, policy = %d\n",
+			len=sprintf(buffer, "IP firewall block rules, default %d\n",
 				ip_fw_blk_policy);
 			break;
 		case IP_INFO_FWD:
 			i = ip_fw_fwd_chain;
-			len=sprintf(buffer, "IP firewall forward rules, policy = %d\n",
+			len=sprintf(buffer, "IP firewall forward rules, default %d\n",
 				ip_fw_fwd_policy);
 			break;
 #endif
@@ -897,12 +961,12 @@
 			ntohl(i->fw_src.s_addr),ntohl(i->fw_smsk.s_addr),
 			ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
 			ntohl(i->fw_via.s_addr),i->fw_flg);
-		len+=sprintf(buffer+len,"%u %u %lu %lu ",
+		len+=sprintf(buffer+len,"%u %u %lu %lu",
 			i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
-		len+=sprintf(buffer+len,"%u %u %u %u %u %u %u %u %u %u\n",
-			i->fw_pts[0],i->fw_pts[1],i->fw_pts[2],i->fw_pts[3],	
-			i->fw_pts[4],i->fw_pts[5],i->fw_pts[6],i->fw_pts[7],	
-			i->fw_pts[8],i->fw_pts[9]);	
+		for (p = 0; p < IP_FW_MAX_PORTS; p++)
+			len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
+		buffer[len++]='\n';
+		buffer[len]='\0';
 		pos=begin+len;
 		if(pos<offset)
 		{
diff -u --recursive --new-file v1.2.0/linux/net/inet/ipx.c linux/net/inet/ipx.c
--- v1.2.0/linux/net/inet/ipx.c	Thu Feb 23 13:31:40 1995
+++ linux/net/inet/ipx.c	Fri Mar 17 14:31:50 1995
@@ -465,6 +465,7 @@
 	struct datalink_proto	*dl = intrfc->if_dlink;
 	char		dest_node[IPX_NODE_LEN];
 	int		send_to_wire = 1;
+	int		addr_len;
 	
 	/* We need to know how many skbuffs it will take to send out this
 	 * packet to avoid unnecessary copies.
@@ -501,8 +502,13 @@
 		return 0;
 	}
 
-	/* In some case, ipxitf_adjust_skbuff can overwrite node */
-	memcpy(dest_node, node, IPX_NODE_LEN);
+	/* determine the appropriate hardware address */
+	addr_len = dev->addr_len;
+	if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+		memcpy(dest_node, dev->broadcast, addr_len);
+	} else {
+		memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
+	}
 
 	/* make any compensation for differing physical/data link size */
 	skb = ipxitf_adjust_skbuff(intrfc, skb);
@@ -699,9 +705,6 @@
 	if(dev->addr_len>IPX_NODE_LEN)
 		return -EINVAL;
 
-	if(dev->addr_len<2)
-		return -EINVAL;
-
 	if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) {
 
 		/* Ok now create */
@@ -781,8 +784,6 @@
 
 	/* Check addresses are suitable */
 	if(dev->addr_len>IPX_NODE_LEN) return NULL;
-
-	if(dev->addr_len<2) return NULL;
 
 	intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
 	if (intrfc!=NULL) {
