diff -u --recursive --new-file v1.1.22/linux/Makefile linux/Makefile
--- v1.1.22/linux/Makefile	Mon Jun 27 16:46:57 1994
+++ linux/Makefile	Mon Jun 27 00:03:30 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 22
+SUBLEVEL = 23
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.22/linux/boot/head.S linux/boot/head.S
--- v1.1.22/linux/boot/head.S	Thu Feb  3 12:37:44 1994
+++ linux/boot/head.S	Sun Jun 26 23:52:53 1994
@@ -289,7 +289,7 @@
  * data (18*2*512 bytes).
  */
 _floppy_track_buffer:
-	.fill 512*2*18,1,0
+	.fill 512*2*36,1,0
 
 /* This is the default interrupt "handler" :-) */
 int_msg:
diff -u --recursive --new-file v1.1.22/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd
--- v1.1.22/linux/drivers/block/README.sbpcd	Thu Jun  9 18:56:09 1994
+++ linux/drivers/block/README.sbpcd	Mon Jun 27 16:35:34 1994
@@ -1,11 +1,11 @@
-This README belongs to release 1.6 of the SoundBlaster Pro (Matsushita,
+This README belongs to release 2.0 of the SoundBlaster Pro (Matsushita,
 Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux.
 
 The driver is able to drive the whole family of IDE-style
 Matsushita/Kotobuki/Panasonic drives (the "double speed" versions like CR-562
 and CR-563, too), and it will work with the soundcard interfaces (SB Pro,
 SB 16, Galaxy, SoundFX, ...) and/or with the "no-sound" cards (Panasonic
-CI-101P, LaserMate, Aztech, ...).
+CI-101P, LaserMate, WDH-7001C, Aztech, ...).
 It should work too now with the "configurable" interface "Sequoia S-1000",
 which is found on the Spea Media FX sound card.
 The interface type has to get configured in /usr/include/linux/sbpcd.h, 
@@ -16,7 +16,10 @@
 (which count the releases around 0.75 or 1.00).
 
 Up to 4 drives are supported. CR-52x ("old") and CR-56x ("new") drives can be
-mixed, but the CR-521 ones are hard-wired to drive ID 0. 
+mixed, but the CR-521 ones are hard-wired to drive ID 0. The drives have to
+use different drive IDs, and each drive has to get a unique minor number
+(0...3), corresponding to it's drive ID. The drive IDs may be selected freely
+from 0 to 3 - they must not be in consecutive order.
 
 As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible
 to change old drives to any ID, too. He writes in this sense:
@@ -35,17 +38,20 @@
    did not work with other values. If the values are not good,
    ID 3 behaves like ID 0."
 
-The drives have to use different drive IDs, but the same controller (it will
-be a little bit harder to support up to four interface cards -  but I plan to
-do it the day somebody wishes to connect a fifth drive).
-Each drive has to get a unique minor number (0...3), corresponding to it's
-drive ID. The drive IDs may be selected freely from 0 to 3 - they must not be
-in consecutive order.
+To use more than 4 drives (now that the single-speed CR-521's are as cheap as
+50$), you have to "duplicate" the driver. Just copy sbpcd.c into sbpcd2.c and
+change SBPCD_ISSUE accordingly.
 
 The driver supports reading of data from the CD and playing of audio tracks.
 The audio part should run with WorkMan, xcdplayer, with the "non-X11" products
 CDplayer and WorkBone - tell me if it is not compatible with other software.
 
+With the "new" drive family CR-562 and CR-563, the reading of audio frames is
+possible. This is currently implemented by an IOCTL function which reads only
+one frame of 2352 bytes at a time. The transfer rate is as slow as 32 kB/sec.
+This will get better, and the software interface may change. We have to 
+standardize it the day the SCSI driver supports it too.
+
 MultiSession is supported (even my "old" CR-521 can handle it), "ManySession"
 (not recommended, see below) alternatively.
 Photo CDs work, too. At ftp.gwdg.de:/pub/linux/hpcdtoppm/ is Hadmut Danisch's
@@ -106,6 +112,10 @@
    So, if the DOS driver tells you have drive id #3, you have to
         mknod /dev/<any_name> b 25 3
    
+   For a second interface board, you have to make nodes like
+        mknod /dev/sbpcd4 b 26 0
+   and so on. Use the MAJORs 26, 27, 28.
+
    If you further make a link like
         ln -s sbpcd /dev/cdrom
    you can use the name /dev/cdrom, too.
@@ -142,7 +152,12 @@
 If you enable this feature, it is impossible to read true multisession CDs.
 
 The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
-specify "block=2048" as a mount option.
+specify "block=2048" as a mount option. Doing this will disable the direct
+execution of a binary from the CD; you have to copy it to a device with the
+standard BLOCK_SIZE (1024) before. So, do not use this if your system is
+directly "running from the CDROM" (like some of YGGDRASIL's installation
+variants). There are CDs on the market (like the german "unifix" Linux
+distribution) which MUST get handled with a block_size of 1024.
 
 
 Auto-probing at boot time:
@@ -199,6 +214,133 @@
 them without the need of supplying parameters.
 
 
+Copying audio tracks:
+---------------------
+
+The following little program will copy track 2 of an audio CD into the file
+"track02":
+
+/*=================== begin program ========================================*/
+/*
+ * read an audio track from a CD
+ *
+ * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
+ *          may be used & enhanced freely
+ *
+ * Due to non-existent sync bytes at the beginning of each audio frame,
+ * it is currently a kind of fortune if two consecutive frames fit together.
+ * Usually, they overlap, or a little piece is missing. This has to get
+ * fixed by higher-level software (reading until an overlap occurs, and then
+ * eliminate the overlapping bytes). Possibly the first read bytes of each
+ * frame must get discarded because they are read before we got synchronized.
+ * 
+ * This example program further is missing to obtain the SubChannel data
+ * which belong to each frame.
+ */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+static struct cdrom_tochdr hdr;
+static struct cdrom_tocentry entry[100];
+static struct cdrom_read_audio arg;
+static u_char buffer[CD_FRAMESIZE_RAW];
+static int datafile, drive;
+static int i, j, limit, track, err;
+static char filename[32];
+
+main(int argc, char *argv[])
+{
+/*
+ * open /dev/cdrom
+ */
+  drive=open("/dev/cdrom", 0);
+  if (drive<0)
+    {
+      fprintf(stderr, "can't open drive.\n");
+      exit (-1);
+    }
+/*
+ * get TocHeader
+ */
+  fprintf(stdout, "getting TocHeader...\n");
+  err=ioctl(drive, CDROMREADTOCHDR, &hdr);
+  if (err!=0)
+    {
+      fprintf(stderr, "can't get TocHeader (error %d).\n", err);
+      exit (-1);
+    }
+  else
+    fprintf(stdout, "TocHeader: %d %d\n", hdr.cdth_trk0, hdr.cdth_trk1);
+/*
+ * get and display all TocEntries
+ */
+  fprintf(stdout, "getting TocEntries...\n");
+  for (i=1;i<=hdr.cdth_trk1;i++)
+    {
+      entry[i].cdte_track = i;
+      entry[i].cdte_format = CDROM_LBA;
+      err=ioctl(drive, CDROMREADTOCENTRY, &entry[i]);
+      if (err!=0)
+	{
+	  fprintf(stderr, "can't get TocEntry #%d (error %d).\n", i, err);
+	  exit (-1);
+	}
+      else
+	{
+	  fprintf(stdout, "TocEntry #%d: %1X %1X %06X %02X\n",
+		 entry[i].cdte_track,
+		 entry[i].cdte_adr,
+		 entry[i].cdte_ctrl,
+		 entry[i].cdte_addr.lba,
+		 entry[i].cdte_datamode);
+	}
+    }
+  fprintf(stdout, "got all TocEntries.\n");
+/*
+ * ask for track number (not implemented here)
+ */
+track=2;
+#if 0 /* just read a little piece */
+entry[track].cdte_addr.lba=170;
+entry[track+1].cdte_addr.lba=190;
+#endif
+/*
+ * read track into file
+ */
+  sprintf(filename, "track%02d\0", track);
+  datafile=creat(filename, 0755);
+  if (datafile<0)
+    {
+      fprintf(stderr, "can't open datafile %s.\n", filename);
+      exit (-1);
+    }
+  arg.addr.lba=entry[track].cdte_addr.lba;
+  arg.addr_format=CDROM_LBA; /* CDROM_MSF is still buggy, I know that */
+  arg.nframes=1;
+  arg.buf=&buffer[0];
+  limit=entry[track+1].cdte_addr.lba;
+  for (i=arg.addr.lba;i<limit;i++)
+    {
+      err=ioctl(drive, CDROMREADAUDIO, &arg);
+      if (err!=0)
+	{
+	  fprintf(stderr, "can't read frame #%d (error %d).\n", 
+		 i-entry[track].cdte_addr.lba+1, err);
+	  exit (-1);
+	}
+      j=write(datafile, &buffer[0], CD_FRAMESIZE_RAW);
+      if (j!=CD_FRAMESIZE_RAW)
+	{
+	  fprintf(stderr,"I/O error (datafile) at frame %d\n",
+		 i-entry[track].cdte_addr.lba+1);
+	}
+      arg.addr.lba++;
+    }
+}
+/*===================== end program ========================================*/
+
+
 Known problems:
 ---------------
 
@@ -211,9 +353,6 @@
 All attempts to read the UPC/EAN code result in a stream of zeroes. All my
 drives are telling there is no UPC/EAN code on disk or there is, but it is an
 all-zero number.
-
-My attempts to read audio tracks like data files are of no success. Contact me,
-if you have an idea, please.
 
 Bug reports, comments, wishes, donations (technical information is a donation,
 too :-) etc. to
diff -u --recursive --new-file v1.1.22/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.1.22/linux/drivers/block/floppy.c	Tue Apr 19 10:52:12 1994
+++ linux/drivers/block/floppy.c	Mon Jun 27 00:13:27 1994
@@ -64,9 +64,15 @@
  * work.
  */
 
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run. 
+ */
+
+
 #define REALLY_SLOW_IO
 #define FLOPPY_IRQ 6
 #define FLOPPY_DMA 2
+#define FDC_FIFO_UNTESTED           /* -bb */
 
 #include <linux/sched.h>
 #include <linux/fs.h>
@@ -77,6 +83,7 @@
 #include <linux/errno.h>
 
 #include <asm/dma.h>
+#include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -110,13 +117,14 @@
  * Maximum disk size (in kilobytes). This default is used whenever the
  * current disk size is unknown.
  */
-#define MAX_DISK_SIZE 1440
+#define MAX_DISK_SIZE 2880 /* was 1440 -bb */
 
 /*
  * Maximum number of sectors in a track buffer. Track buffering is disabled
  * if tracks are bigger.
  */
-#define MAX_BUFFER_SECTORS 18
+#define MAX_BUFFER_SECTORS 36 /* was 18 -bb */
+
 
 /*
  * The DMA channel used by the floppy controller cannot access data at
@@ -154,6 +162,8 @@
 	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,NULL },	/* 360kB in 1.2MB drive */
 	{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,NULL },	/* 720kB in 1.2MB drive */
 	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL },	/* 1.44MB diskette */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,NULL },	/* 2.88MB diskette */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,NULL },	/* 2.88MB diskette */
 };
 
 /*
@@ -170,6 +180,10 @@
 	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" },	  /* 3.5" 720kB diskette */
 	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" },	  /* 1.44MB diskette */
 	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"2.88M-AMI" },   /* DUMMY */ 
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M-AMI" },   /* Dummy */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"2.88M" },   /* 2.88MB diskette */ 
+	{ 2880,18,2,80,0,0x1B,0x40,0xCF,0x6C,"1.44MX" },   /* 1.44MB diskette */ 
 };
 
 /* Auto-detection: Disk type used until the next media change occurs. */
@@ -186,13 +200,14 @@
 
 static int floppy_sizes[] ={
 	MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE,
-	 360, 360 ,360, 360,
+	 360, 360, 360, 360,
 	1200,1200,1200,1200,
 	 360, 360, 360, 360,
 	 720, 720, 720, 720,
 	 360, 360, 360, 360,
 	 720, 720, 720, 720,
-	1440,1440,1440,1440
+	1440,1440,1440,1440,
+	2880,2880,2880,2880           /* -bb */
 };
 
 /*
@@ -269,6 +284,11 @@
 extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
 
 static void redo_fd_request(void);
+static void floppy_ready(void);
+static void recalibrate_floppy(void);
+
+static int floppy_grab_irq_and_dma(void);
+static void floppy_release_irq_and_dma(void);
 
 /*
  * These are global variables, as that's the easiest way to give
@@ -290,10 +310,8 @@
 static unsigned char seek_track = 0;
 static unsigned char current_track = NO_TRACK;
 static unsigned char command = 0;
-static unsigned char fdc_version = FDC_TYPE_STD;	/* FDC version code */
+static unsigned char fdc_version = 0x90;	/* FDC version code */
 
-static void floppy_ready(void);
-
 static void select_callback(unsigned long unused)
 {
 	floppy_ready();
@@ -794,8 +812,6 @@
  * Special case - used after a unexpected interrupt (or reset)
  */
 
-static void recalibrate_floppy(void);
-
 static void recal_interrupt(void)
 {
 	output_byte(FD_SENSEI);
@@ -1237,7 +1253,7 @@
 {
 	struct floppy_struct *base;
 
-	if (code > 0 && code < 5) {
+	if (code > 0 && code < 7) {                            /* -bb*/
 		base = &floppy_types[(code-1)*2];
 		printk("fd%d is %s",drive,base->name);
 		return base;
@@ -1273,6 +1289,9 @@
 	int drive;
 	int old_dev;
 
+	if (floppy_grab_irq_and_dma()) {
+		return -EBUSY;
+	}
 	drive = inode->i_rdev & 3;
 	old_dev = fd_device[drive];
 	if (fd_ref[drive])
@@ -1295,6 +1314,7 @@
 		printk("floppy_release with fd_ref == 0");
 		fd_ref[inode->i_rdev & 3] = 0;
 	}
+        floppy_release_irq_and_dma();
 }
 
 static struct file_operations floppy_fops = {
@@ -1360,10 +1380,6 @@
 	timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
 	timer_active &= ~(1 << FLOPPY_TIMER);
 	config_types();
-	if (irqaction(FLOPPY_IRQ,&floppy_sigaction))
-		printk("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ);
-	if (request_dma(FLOPPY_DMA))
-		printk("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA);
 	/* Try to determine the floppy controller type */
 	DEVICE_INTR = ignore_interrupt;	/* don't ask ... */
 	output_byte(FD_VERSION);	/* get FDC version code */
@@ -1382,9 +1398,31 @@
 	 * properly, so force a reset for the standard FDC clones,
 	 * to avoid interrupt garbage.
 	 */
-
 	if (fdc_version == FDC_TYPE_STD) {
 		initial_reset_flag = 1;
 		reset_floppy();
 	}
+}
+
+static int floppy_grab_irq_and_dma(void)
+{
+	if (irqaction(FLOPPY_IRQ,&floppy_sigaction)) {
+		printk("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ);
+		return -1;
+	}
+	if (request_dma(FLOPPY_DMA)) {
+		printk("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA);
+		free_irq(FLOPPY_IRQ);
+		return -1;
+	}
+	enable_irq(FLOPPY_IRQ);
+	return 0;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+	disable_dma(FLOPPY_DMA);
+	free_dma(FLOPPY_DMA);
+	disable_irq(FLOPPY_IRQ);
+	free_irq(FLOPPY_IRQ);
 }
diff -u --recursive --new-file v1.1.22/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.1.22/linux/drivers/block/sbpcd.c	Thu Jun  9 18:56:11 1994
+++ linux/drivers/block/sbpcd.c	Mon Jun 27 16:35:36 1994
@@ -5,7 +5,7 @@
  *            and for "no-sound" interfaces like Lasermate and the
  *            Panasonic CI-101P.
  *
- *  NOTE:     This is release 1.6.
+ *  NOTE:     This is release 2.0.
  *            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. 
@@ -92,6 +92,20 @@
  *       called as it should I fear it must get synchronized for not to
  *       disturb the normal driver's activity.
  *
+ *  2.0  Version number bumped - two reasons:
+ *       - reading audio tracks as data works now with CR-562 and CR-563. We
+ *       currently do it by an IOCTL (yet has to get standardized), one frame
+ *       at a time; that is pretty slow. But it works.
+ *       - we are maintaining now up to 4 interfaces (each up to 4 drives):
+ *       did it the easy way - a different MAJOR (25, 26, ...) and a different
+ *       copy of the driver (sbpcd.c, sbpcd2.c, sbpcd3.c, sbpcd4.c - only
+ *       distinguished by the value of SBPCD_ISSUE and the driver's name),
+ *       and a common sbpcd.h file.
+ *       Bettered the "ReadCapacity error" problem with old CR-52x drives (the
+ *       drives sometimes need a manual "eject/insert" before work): just
+ *       reset the drive and do again. Needs lots of resets here and sometimes
+ *       that does not cure, so this can't be the solution.
+ *
  *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
  *     elaborated speed-up experiments (and the fabulous results!), for
  *     the "push" towards load-free wait loops, and for the extensive mail
@@ -123,6 +137,8 @@
  *
  */
 
+#define SBPCD_ISSUE 1 /* change to 2, 3, 4 for multiple interface boards */
+
 #include <linux/config.h>
 #include <linux/errno.h>
 
@@ -133,18 +149,30 @@
 #include <linux/kernel.h>
 #include <linux/cdrom.h>
 #include <linux/ioport.h>
+#include <linux/major.h> 
 #include <linux/sbpcd.h>
 #include <linux/string.h>
-#include <linux/major.h> 
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <stdarg.h>
 
+#if !(SBPCD_ISSUE-1)
 #define MAJOR_NR MATSUSHITA_CDROM_MAJOR
+#endif
+#if !(SBPCD_ISSUE-2)
+#define MAJOR_NR MATSUSHITA_CDROM2_MAJOR /* second driver issue */
+#endif
+#if !(SBPCD_ISSUE-3)
+#define MAJOR_NR MATSUSHITA_CDROM3_MAJOR /* third driver issue */
+#endif
+#if !(SBPCD_ISSUE-4)
+#define MAJOR_NR MATSUSHITA_CDROM4_MAJOR /* fourth driver issue */
+#endif
+
 #include "blk.h"
 
-#define VERSION "1.6 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "2.0 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 #define SBPCD_DEBUG
 
@@ -170,11 +198,64 @@
 #define XA_TEST2
 
 #define TEST_UPC 0
-#define READ_AUDIO 0 /* does not work today (the drives won't read audio) */
-
-/*==========================================================================*/
+#define READ_AUDIO 1
+#define SPEA_TEST 0
+#define PRINTK_BUG 0
+#define TEST_STI 0
+/*==========================================================================*/
+/*
+ * provisions for more than 1 driver issues
+ * currently up to 4 drivers, expandable
+ */
+#if !(SBPCD_ISSUE-1)
+#define SBPCD_IOCTL_F sbpcd_ioctl
+#define SBPCD_IOCTL(a,b,c,d) sbpcd_ioctl(a,b,c,d)
+#define DO_SBPCD_REQUEST(a) do_sbpcd_request(a)
+#define SBPCD_OPEN_F sbpcd_open
+#define SBPCD_OPEN(a,b) sbpcd_open(a,b)
+#define SBPCD_RELEASE_F sbpcd_release
+#define SBPCD_RELEASE(a,b) sbpcd_release(a,b)
+#define SBPCD_SETUP(a,b) sbpcd_setup(a,b)
+#define SBPCD_INIT(a,b) sbpcd_init(a,b)
+#define SBPCD_MEDIA_CHANGE(a,b) check_sbpcd_media_change(a,b)
+#endif
+#if !(SBPCD_ISSUE-2)
+#define SBPCD_IOCTL_F sbpcd2_ioctl
+#define SBPCD_IOCTL(a,b,c,d) sbpcd2_ioctl(a,b,c,d)
+#define DO_SBPCD_REQUEST(a) do_sbpcd2_request(a)
+#define SBPCD_OPEN_F sbpcd2_open
+#define SBPCD_OPEN(a,b) sbpcd2_open(a,b)
+#define SBPCD_RELEASE_F sbpcd2_release
+#define SBPCD_RELEASE(a,b) sbpcd2_release(a,b)
+#define SBPCD_SETUP(a,b) sbpcd2_setup(a,b)
+#define SBPCD_INIT(a,b) sbpcd2_init(a,b)
+#define SBPCD_MEDIA_CHANGE(a,b) check_sbpcd2_media_change(a,b)
+#endif
+#if !(SBPCD_ISSUE-3)
+#define SBPCD_IOCTL_F sbpcd3_ioctl
+#define SBPCD_IOCTL(a,b,c,d) sbpcd3_ioctl(a,b,c,d)
+#define DO_SBPCD_REQUEST(a) do_sbpcd3_request(a)
+#define SBPCD_OPEN_F sbpcd3_open
+#define SBPCD_OPEN(a,b) sbpcd3_open(a,b)
+#define SBPCD_RELEASE_F sbpcd3_release
+#define SBPCD_RELEASE(a,b) sbpcd3_release(a,b)
+#define SBPCD_SETUP(a,b) sbpcd3_setup(a,b)
+#define SBPCD_INIT(a,b) sbpcd3_init(a,b)
+#define SBPCD_MEDIA_CHANGE(a,b) check_sbpcd3_media_change(a,b)
+#endif
+#if !(SBPCD_ISSUE-4)
+#define SBPCD_IOCTL_F sbpcd4_ioctl
+#define SBPCD_IOCTL(a,b,c,d) sbpcd4_ioctl(a,b,c,d)
+#define DO_SBPCD_REQUEST(a) do_sbpcd4_request(a)
+#define SBPCD_OPEN_F sbpcd4_open
+#define SBPCD_OPEN(a,b) sbpcd4_open(a,b)
+#define SBPCD_RELEASE_F sbpcd4_release
+#define SBPCD_RELEASE(a,b) sbpcd4_release(a,b)
+#define SBPCD_SETUP(a,b) sbpcd4_setup(a,b)
+#define SBPCD_INIT(a,b) sbpcd4_init(a,b)
+#define SBPCD_MEDIA_CHANGE(a,b) check_sbpcd4_media_change(a,b)
+#endif
 /*==========================================================================*/
-
 #if MANY_SESSION
 #undef LONG_TIMING
 #define LONG_TIMING 1
@@ -215,8 +296,6 @@
   0x340, 0, /* Lasermate, CI-101P */
   0x360, 0, /* Lasermate, CI-101P */
   0x270, 1, /* Soundblaster 16 */
-  0x630, 0, /* "sound card #9" (default) */
-  0x650, 0, /* "sound card #9" */
   0x670, 0, /* "sound card #9" */
   0x690, 0, /* "sound card #9" */
   0x330, 2, /* SPEA Media FX (default) */
@@ -230,6 +309,9 @@
   0x370, 0, /* Lasermate, CI-101P */
   0x290, 1, /* Soundblaster 16 */
   0x310, 0, /* Lasermate, CI-101P */
+/* excluded due to incomplete address decoding of the SbPro card */
+  0x630, 0, /* "sound card #9" (default) */
+  0x650, 0, /* "sound card #9" */
 #endif
 };
 
@@ -242,6 +324,7 @@
  */
 static void sbp_read_cmd(void);
 static int  sbp_data(void);
+static int cmd_out(void);
 
 /*==========================================================================*/
 
@@ -273,22 +356,28 @@
  * (1<<DBG_LCK)  door (un)lock info
  * (1<<DBG_SQ)   dump SubQ frame
  * (1<<DBG_AUD)  "read audio" debugging
+ * (1<<DBG_SEQ)  Sequoia interface configuration 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);
+#else
 static int sbpcd_debug =  (1<<DBG_INF) |
                           (1<<DBG_TOC) |
                           (1<<DBG_UPC) |
-                          (1<<DBG_IOC) |
-                          (1<<DBG_XA)  |
+                          (1<<DBG_TIM) |
                           (1<<DBG_LCK) |
                           (1<<DBG_CHK) |
                           (1<<DBG_AUD) |
-                          (1<<DBG_BSZ) |
                           (1<<DBG_IOX);
 #endif
+#endif
 static int sbpcd_ioaddr = CDROM_PORT;	/* default I/O base address */
 static int sbpro_type = SBPRO;
 static int CDo_command, CDo_reset;
@@ -301,9 +390,10 @@
 static struct cdrom_tocentry tocentry;
 static struct cdrom_subchnl SC;
 static struct cdrom_volctrl volctrl;
-char *str_sb = "SoundBlaster";
-char *str_lm = "LaserMate";
-char *str_sp = "SPEA";
+static struct cdrom_read_audio read_audio;
+static char *str_sb = "SoundBlaster";
+static char *str_lm = "LaserMate";
+static char *str_sp = "SPEA";
 char *type;
 
 /*==========================================================================*/
@@ -371,7 +461,7 @@
 
   u_char mode;           /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
 #if READ_AUDIO
-  u_char *aud_buf;                  /* Pointer to internal data buffer,
+  u_char *aud_buf;                  /* Pointer to audio data buffer,
                                  space allocated during sbpcd_init() */
 #endif READ_AUDIO
 
@@ -452,7 +542,7 @@
 #ifdef SBPCD_DEBUG
 # define DPRINTF(x)	sbpcd_dprintf x
 
-void sbpcd_dprintf(int level, char *fmt, ...)
+static void sbpcd_dprintf(int level, char *fmt, ...)
 {
   char buff[256];
   va_list args;
@@ -464,6 +554,9 @@
   vsprintf(buff, fmt, args);
   va_end(args);
   printk(buff);
+#if PRINTK_BUG
+  sti(); /* to avoid possible "printk" bug */
+#endif
 }
 
 #else
@@ -666,6 +759,7 @@
   int i,j, st=0;
   u_long timeout;
 
+  DPRINTF((DBG_000,"SBPCD: ResponseInfo entered.\n"));
   if (current == task[0])
     for (i=0;i<response_count;i++)
       {
@@ -674,7 +768,11 @@
 	    st=inb(CDi_status);
 	    if (!(st&s_not_result_ready)) break;
 	  }
-	if (j==0) return (-1);
+	if (j==0)
+	  {
+	    DPRINTF((DBG_SEQ,"SBPCD: ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count));
+	    return (-1);
+	  }
 	infobuf[i]=inb(CDi_info);
       }
   else 
@@ -696,6 +794,7 @@
 	  infobuf[i]=inb(CDi_info);
 	}
     }
+  DPRINTF((DBG_000,"SBPCD: ResponseInfo: done.\n"));
   return (0);
 }
 /*==========================================================================*/
@@ -780,9 +879,8 @@
     }
 }
 /*==========================================================================*/
-int xx_ReadError(void)
+static int xx_ReadError(void)
 {
-  int cmd_out(void);
   int i;
 
   clr_cmdbuf();
@@ -811,7 +909,7 @@
   return (i);
 }
 /*==========================================================================*/
-int cmd_out(void)
+static int cmd_out(void)
 {
   int i=0;
 
@@ -1283,6 +1381,8 @@
   DriveStruct[d].diskstate_flags &= ~frame_size_bit;
   clr_cmdbuf();
   DriveStruct[d].frame_size=framesize;
+  if (framesize==CD_FRAMESIZE_RAW) DriveStruct[d].sense_byte=0x82;
+  else DriveStruct[d].sense_byte=0x00;
 
   DPRINTF((DBG_XA,"SBPCD: xx_ModeSelect: %02X %04X\n",
 	   DriveStruct[d].sense_byte, DriveStruct[d].frame_size));
@@ -1658,14 +1758,17 @@
 #ifdef CDMKE
   int i=0;
 
+  DPRINTF((DBG_IOX,"SBPCD: check_datarate entered.\n"));
   timed_out=0;
   datarate=0;
 
+#if TEST_STI
+  for (i=0;i<=1000;i++) printk(".");
+#endif
+
   /* set a timer to make (timed_out!=0) after 1.1 seconds */
 
   DPRINTF((DBG_TIM,"SBPCD: timer started (110).\n"));
-  sti(); /* to avoid possible "printf" bug */
-
   SET_TIMER(mark_timeout,110);
   do
     {
@@ -1700,12 +1803,14 @@
 {
   int i, j;
 
+  DPRINTF((DBG_INI,"SBPCD: check_version entered.\n"));
   /* clear any pending error state */
   clr_cmdbuf();
   drvcmd[0]=0x82;
   response_count=9;
   flags_cmd_out=f_putcmd;
-  cmd_out();
+  i=cmd_out();
+  if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_82 returns %d (ok anyway).\n",i));
 
   /* read drive version */
   clr_cmdbuf();
@@ -1732,7 +1837,11 @@
   else
     {
       for (i=0;i<8;i++) if (infobuf[i]!=drive_vendor[i]) break;
-      if (i!=8) return (-1);
+      if (i!=8)
+	{
+	  DPRINTF((DBG_INI,"SBPCD: check_version: error.\n"));
+	  return (-1);
+	}
       DriveStruct[d].drive_model[0]='2';
       DriveStruct[d].drive_model[1]='x';
       DriveStruct[d].drive_model[2]='-';
@@ -1754,6 +1863,7 @@
   else if (j<211) DriveStruct[d].drv_type=drv_210;
   else if (j<300) DriveStruct[d].drv_type=drv_211;
   else DriveStruct[d].drv_type=drv_300;
+  DPRINTF((DBG_INI,"SBPCD: check_version done.\n"));
   return (0);
 }
 /*==========================================================================*/
@@ -1792,7 +1902,8 @@
 	{
 	  ndrives++;
 	  DriveStruct[d].drv_options=drv_pattern[j];
-	  if (!new_drive) DriveStruct[d].drv_options&=~(speed_auto|speed_300|speed_150);
+	  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,
@@ -1975,47 +2086,36 @@
 /*==========================================================================*/
 static int DiskInfo(void)
 {
-  int i;
+  int i, j;
 
 #if READ_AUDIO
       DriveStruct[d].mode=READ_M1;
 #endif READ_AUDIO
 
-  i=SetSpeed();
-  if (i<0)
+#undef LOOP_COUNT
+#define LOOP_COUNT 20 /* needed for some "old" drives */
+
+  for (j=1;j<LOOP_COUNT;j++)
     {
-      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first SetSpeed returns %d\n", i));
       i=SetSpeed();
       if (i<0)
 	{
-	  DPRINTF((DBG_INF,"SBPCD: DiskInfo: second SetSpeed returns %d\n", i));
-	  return (i);
+	  DPRINTF((DBG_INF,"SBPCD: DiskInfo: SetSpeed returns %d\n", i));
+	  continue;
 	}
-    }
-  i=xx_ModeSense();
-  if (i<0)
-    {
-      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first xx_ModeSense returns %d\n", i));
       i=xx_ModeSense();
       if (i<0)
 	{
-	  DPRINTF((DBG_INF,"SBPCD: DiskInfo: second xx_ModeSense returns %d\n", i));
-	  return (i);
+	  DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ModeSense returns %d\n", i));
+	  continue;
 	}
-      return (i);
-    }
-  i=xx_ReadCapacity();
-  if (i<0)
-    {
-      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first ReadCapacity returns %d\n", i));
       i=xx_ReadCapacity();
-      if (i<0)
-	{
-	  DPRINTF((DBG_INF,"SBPCD: DiskInfo: second ReadCapacity returns %d\n", i));
-	  return (i);
-	}
-      return (i);
+      if (i>=0) break;
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadCapacity #%d returns %d\n", j, i));
+      i=DriveReset();
     }
+  if (j==LOOP_COUNT) return (-2); /* give up */
+
   i=xx_ReadTocDescr();
   if (i<0)
     {
@@ -2220,8 +2320,8 @@
 /*
  * ioctl support, adopted from scsi/sr_ioctl.c and mcd.c
  */
-static int sbpcd_ioctl(struct inode *inode,struct file *file,
-		       u_int cmd, u_long arg)
+static int SBPCD_IOCTL(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg)
 {
   int i, st;
 
@@ -2472,7 +2572,7 @@
       DriveStruct[d].mode=READ_M1;
       return (0);
 
-    case CDROMREADMODE2:
+    case CDROMREADMODE2: /* not useable at the moment */
       DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE2 requested.\n"));
       xx_ModeSelect(CD_FRAMESIZE_XA);
       xx_ModeSense();
@@ -2482,214 +2582,188 @@
 #if READ_AUDIO
     case CDROMREADAUDIO:
       { /* start of CDROMREADAUDIO */
-
-      int i=0, j=0, frame, block;
-      u_int try=0;
-      u_long timeout;
-      u_char *p;
-      u_int data_tries = 0;
-      u_int data_waits = 0;
-      u_int data_retrying = 0;
-      int status_tries;
-      int error_flag;
-      struct cdrom_aud aud_arg;
-
-      error_flag=0;
-
-#if 0
-#define AUD_FRM_SIZ CD_FRAMESIZE_RAW
-#else
-#define AUD_FRM_SIZ CD_FRAMESIZE_XA
-#endif
-
-      DPRINTF((DBG_IOC,"SBPCD: read_audio: ioctl: CDROMREADAUDIO requested.\n"));
-
-      i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_aud));
-      if (i) return (i);
-      memcpy_fromfs(&aud_arg, (void *) arg, sizeof(struct cdrom_aud));
-      i=verify_area(VERIFY_WRITE, aud_arg.buf, AUD_FRM_SIZ);
-      if (i) return (i);
-      DPRINTF((DBG_AUD,"SBPCD: read_audio: lba: %d, buffer: %08X\n", aud_arg.lba, aud_arg.buf));
-
-      DPRINTF((DBG_AUD,"SBPCD: read_audio: before xx_ReadStatus.\n"));
-      for (data_tries=5; data_tries>0; data_tries--)
-	{
-	  DPRINTF((DBG_AUD,"SBPCD: data_tries=%d ...\n", data_tries));
-	  DriveStruct[d].mode=READ_AU;
-	  xx_ModeSelect(AUD_FRM_SIZ);
-	  xx_ModeSense();
-
-
-	  for (status_tries=3; status_tries > 0; status_tries--)
-	    {
-	      flags_cmd_out |= f_respo3;
-	      xx_ReadStatus();
-	      if (sbp_status() != 0) break;
-	      sbp_sleep(1);    /* wait a bit, try again */
-	    }
-	  if (status_tries == 0)
-	    {
-	      DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: failed after 3 tries.\n"));
-	      continue;
-	    }
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: ok.\n"));
-
-
-	  block=aud_arg.lba;
-	  flags_cmd_out = f_putcmd |
-	                  f_respo2 |
-                          f_ResponseStatus |
-                          f_obey_p_check;
-
-
-
-
-	  if (!new_drive)
-	    {
-	      flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
-	      cmd_type=READ_M2;
-	      drvcmd[0]=0x03;   /* "read XA frames" command for old drives */
-	      drvcmd[1]=(block>>16)&0x000000ff;
-	      drvcmd[2]=(block>>8)&0x000000ff;
-	      drvcmd[3]=block&0x000000ff;
-	      drvcmd[4]=0;
-	      drvcmd[5]=1;   /* # of frames */
-	      drvcmd[6]=0;
-	    }
-	  else /* if new_drive */
-	    {
-	      drvcmd[0]=0x10;              /* "read frames" command for new drives */
-	      lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-	      drvcmd[4]=0;
-	      drvcmd[5]=0;
-	      drvcmd[6]=1;   /* # of frames */
-	    }
-
-
-
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n"));
-	  for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-
-	  sbp_sleep(0);
-
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: after giving \"read\" command.\n"));
-	  for (frame=1;frame<2 && !error_flag; frame++)
-	    {
-	      try=maxtim_data;
-	      for (timeout=jiffies+900; ; )
-		{
-		  for ( ; try!=0;try--)
-		    {
-		      j=inb(CDi_status);
-		      if (!(j&s_not_data_ready)) break;
-		      if (!(j&s_not_result_ready)) break;
-		      if (!new_drive) if (j&s_attention) break;
-		    }
-		  if (try != 0 || timeout <= jiffies) break;
-		  if (data_retrying == 0) data_waits++;
-		  data_retrying = 1;
-		  sbp_sleep(1);
-		  try = 1;
-		}
-	      if (try==0)
-		{
-		  DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status timeout.\n"));
-		  error_flag++;
-		  break;
-		}
-	      DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status ok.\n"));
-	      
-	      if (j&s_not_data_ready)
-		{
-		  printk("SBPCD: read_audio: sbp_data: DATA_READY timeout.\n");
-		  error_flag++;
-		  break;
-		}
-	      
-	      DPRINTF((DBG_AUD,"SBPCD: read_audio: before reading data.\n"));
-	      CLEAR_TIMER;
-	      error_flag=0;
-	      p = DriveStruct[d].aud_buf;
-	      if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
-	      READ_DATA(CDi_data, p, AUD_FRM_SIZ);
-	      if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
-	      data_retrying = 0;
-	    }
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: after reading data.\n"));
-	  if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
-	    {
-	      DPRINTF((DBG_AUD,"SBPCD: read_audio: read aborted by drive\n"));
+	int i=0, j=0, frame, block;
+	u_int try=0;
+	u_long timeout;
+	u_char *p;
+	u_int data_tries = 0;
+	u_int data_waits = 0;
+	u_int data_retrying = 0;
+	int status_tries;
+	int error_flag;
+
+	error_flag=0;
+
+	DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADAUDIO requested.\n"));
+
+	i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
+	if (i) return (i);
+	memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
+	i=verify_area(VERIFY_WRITE, read_audio.buf, CD_FRAMESIZE_RAW);
+	if (i) return (i);
+
+	if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
+	  block=msf2blk(read_audio.addr.lba);
+	else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
+	  block=read_audio.addr.lba;
+	else return (-EINVAL);
+	if (read_audio.nframes!=1) return (-EINVAL);
+	DPRINTF((DBG_AUD,"SBPCD: read_audio: lba: %d, msf: %06X\n",
+		 block, blk2msf(block)));
+	DPRINTF((DBG_AUD,"SBPCD: read_audio: before xx_ReadStatus.\n"));
+	for (data_tries=5; data_tries>0; data_tries--)
+	  {
+	    DPRINTF((DBG_AUD,"SBPCD: data_tries=%d ...\n", data_tries));
+	    DriveStruct[d].mode=READ_AU;
+	    xx_ModeSelect(CD_FRAMESIZE_RAW);
+	    xx_ModeSense();
+	    for (status_tries=3; status_tries > 0; status_tries--)
+	      {
+		flags_cmd_out |= f_respo3;
+		xx_ReadStatus();
+		if (sbp_status() != 0) break;
+		sbp_sleep(1);    /* wait a bit, try again */
+	      }
+	    if (status_tries == 0)
+	      {
+		DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: failed after 3 tries.\n"));
+		continue;
+	      }
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: ok.\n"));
+	    
+	    flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+	    if (!new_drive)
+	      {
+		flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+		cmd_type=READ_M2;
+		drvcmd[0]=0x03;   /* "read XA frames" command for old drives */
+		drvcmd[1]=(block>>16)&0x000000ff;
+		drvcmd[2]=(block>>8)&0x000000ff;
+		drvcmd[3]=block&0x000000ff;
+		drvcmd[4]=0;
+		drvcmd[5]=1;   /* # of frames */
+		drvcmd[6]=0;
+	      }
+	    else /* if new_drive */
+	      {
+		drvcmd[0]=0x10;              /* "read frames" command for new drives */
+		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+		drvcmd[4]=0;
+		drvcmd[5]=0;
+		drvcmd[6]=1;   /* # of frames */
+	      }
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n"));
+	    for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+	    sbp_sleep(0);
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: after giving \"read\" command.\n"));
+	    for (frame=1;frame<2 && !error_flag; frame++)
+	      {
+		try=maxtim_data;
+		for (timeout=jiffies+900; ; )
+		  {
+		    for ( ; try!=0;try--)
+		      {
+			j=inb(CDi_status);
+			if (!(j&s_not_data_ready)) break;
+			if (!(j&s_not_result_ready)) break;
+			if (!new_drive) if (j&s_attention) break;
+		      }
+		    if (try != 0 || timeout <= jiffies) break;
+		    if (data_retrying == 0) data_waits++;
+		    data_retrying = 1;
+		    sbp_sleep(1);
+		    try = 1;
+		  }
+		if (try==0)
+		  {
+		    DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status timeout.\n"));
+		    error_flag++;
+		    break;
+		  }
+		DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: CDi_status ok.\n"));
+		if (j&s_not_data_ready)
+		  {
+		    printk("SBPCD: read_audio: sbp_data: DATA_READY timeout.\n");
+		    error_flag++;
+		    break;
+		  }
+		DPRINTF((DBG_AUD,"SBPCD: read_audio: before reading data.\n"));
+		CLEAR_TIMER;
+		error_flag=0;
+		p = DriveStruct[d].aud_buf;
+		if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
+		READ_DATA(CDi_data, p, CD_FRAMESIZE_RAW);
+		if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
+		data_retrying = 0;
+	      }
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: after reading data.\n"));
+	    if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+	      {
+		DPRINTF((DBG_AUD,"SBPCD: read_audio: read aborted by drive\n"));
 #if 0000
-	      i=DriveReset();                /* ugly fix to prevent a hang */
+		i=DriveReset();                /* ugly fix to prevent a hang */
 #endif 0000
-	      continue;
-	    }
-
-	  if (!new_drive)
-	    {
-	      i=maxtim_data;
-	      for (timeout=jiffies+900; timeout > jiffies; timeout--)
-		{
-		  for ( ;i!=0;i--)
-		    {
-		      j=inb(CDi_status);
-		      if (!(j&s_not_data_ready)) break;
-		      if (!(j&s_not_result_ready)) break;
-		      if (j&s_attention) break;
-		    }
-		  if (i != 0 || timeout <= jiffies) break;
-		  sbp_sleep(0);
-		  i = 1;
-		}
-	      if (i==0) { DPRINTF((DBG_AUD,"SBPCD: read_audio: STATUS TIMEOUT AFTER READ")); }
-	      if (!(j&s_attention))
-		{
-		  DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n"));
-		  i=DriveReset();  /* ugly fix to prevent a hang */
+		continue;
+	      }
+	    if (!new_drive)
+	      {
+		i=maxtim_data;
+		for (timeout=jiffies+900; timeout > jiffies; timeout--)
+		  {
+		    for ( ;i!=0;i--)
+		      {
+			j=inb(CDi_status);
+			if (!(j&s_not_data_ready)) break;
+			if (!(j&s_not_result_ready)) break;
+			if (j&s_attention) break;
+		      }
+		    if (i != 0 || timeout <= jiffies) break;
+		    sbp_sleep(0);
+		    i = 1;
+		  }
+		if (i==0) { DPRINTF((DBG_AUD,"SBPCD: read_audio: STATUS TIMEOUT AFTER READ")); }
+		if (!(j&s_attention))
+		  {
+		    DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n"));
+		    i=DriveReset();  /* ugly fix to prevent a hang */
 		  continue;
-		}
-	    }
-
-	  do
-	    {
-	      if (!new_drive) xx_ReadStatus();
-	      i=ResponseStatus();  /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */
-	      if (i<0) { DPRINTF((DBG_AUD,
-				  "SBPCD: read_audio: xx_ReadStatus error after read: %02X\n",
-				  DriveStruct[d].status_byte));
-			 continue; /* FIXME */
-		       }
-	    }
-	  while ((!new_drive)&&(!st_check)&&(!(i&p_success_old)));
-	  if (st_check)
-	    {
-	      i=xx_ReadError();
-	      DPRINTF((DBG_AUD,"SBPCD: read_audio: xx_ReadError was necessary after read: %02X\n",i));
-	      continue;
-	    }
-	  memcpy_tofs((u_char *) aud_arg.buf,
-		      (u_char *) DriveStruct[d].aud_buf, AUD_FRM_SIZ);
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: memcpy_tofs done.\n"));
-	  break;
-	}
-      xx_ModeSelect(CD_FRAMESIZE);
-      xx_ModeSense();
-      DriveStruct[d].mode=READ_M1;
-
-
-      if (data_tries == 0)
-	{
-	  DPRINTF((DBG_AUD,"SBPCD: read_audio: failed after 5 tries.\n"));
-	  return (-8);
-	}
-      DPRINTF((DBG_AUD,"SBPCD: read_audio: successful return.\n"));
-      return (0);
-    } /* end of CDROMREADAUDIO */
+		  }
+	      }
+	    do
+	      {
+		if (!new_drive) xx_ReadStatus();
+		i=ResponseStatus();  /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */
+		if (i<0) { DPRINTF((DBG_AUD,
+				    "SBPCD: read_audio: xx_ReadStatus error after read: %02X\n",
+				    DriveStruct[d].status_byte));
+			   continue; /* FIXME */
+			 }
+	      }
+	    while ((!new_drive)&&(!st_check)&&(!(i&p_success_old)));
+	    if (st_check)
+	      {
+		i=xx_ReadError();
+		DPRINTF((DBG_AUD,"SBPCD: read_audio: xx_ReadError was necessary after read: %02X\n",i));
+		continue;
+	      }
+	    memcpy_tofs((u_char *) read_audio.buf,
+			(u_char *) DriveStruct[d].aud_buf, CD_FRAMESIZE_RAW);
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: memcpy_tofs done.\n"));
+	    break;
+	  }
+	xx_ModeSelect(CD_FRAMESIZE);
+	xx_ModeSense();
+	DriveStruct[d].mode=READ_M1;
+	if (data_tries == 0)
+	  {
+	    DPRINTF((DBG_AUD,"SBPCD: read_audio: failed after 5 tries.\n"));
+	    return (-8);
+	  }
+	DPRINTF((DBG_AUD,"SBPCD: read_audio: successful return.\n"));
+	return (0);
+      } /* end of CDROMREADAUDIO */
 #endif READ_AUDIO
 
-
-
-
     case BLKRASET:
       if(!suser())  return -EACCES;
       if(!inode->i_rdev) return -EINVAL;
@@ -2726,7 +2800,7 @@
 /*
  *  I/O request routine, called from Linux kernel.
  */
-static void do_sbpcd_request(void)
+static void DO_SBPCD_REQUEST(void)
 {
   u_int block;
   int dev;
@@ -2836,7 +2910,7 @@
 	  block=DriveStruct[d].lba_multi+block;
 	}
 #else
-      if ( (block==166) && (DriveStruct[d].f_multisession) && (multisession_valid) )
+      if ( (block==CD_BLOCK_OFFSET+16) && (DriveStruct[d].f_multisession) && (multisession_valid) )
 	{
 	  DPRINTF((DBG_MUL,"SBPCD: MultiSession: use %08X for %08X (msf)\n",
 		         blk2msf(DriveStruct[d].lba_multi+16),
@@ -3064,7 +3138,7 @@
 /*
  *  Open the device special file.  Check that a disk is in. Read TOC.
  */
-int sbpcd_open(struct inode *ip, struct file *fp)
+int SBPCD_OPEN(struct inode *ip, struct file *fp)
 {
   int i;
 
@@ -3100,8 +3174,12 @@
  */
   DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n",
 	   DriveStruct[d].open_count,DriveStruct[d].open_count+1));
-  if (++DriveStruct[d].open_count==1) yy_LockDoor(1);
-  
+  if (++DriveStruct[d].open_count==1)
+    {
+      do
+	i=yy_LockDoor(1);
+      while (i!=0);
+    }
   if (!st_spinning) xx_SpinUp();
 
   i=DiskInfo();
@@ -3113,7 +3191,7 @@
 /*
  *  On close, we flush all sbp blocks from the buffer cache.
  */
-static void sbpcd_release(struct inode * ip, struct file * file)
+static void SBPCD_RELEASE(struct inode * ip, struct file * file)
 {
   int i;
 
@@ -3153,10 +3231,10 @@
   block_write,            /* write - general block-dev write */
   NULL,                   /* readdir - bad */
   NULL,                   /* select */
-  sbpcd_ioctl,            /* ioctl */
+  SBPCD_IOCTL_F,          /* ioctl */
   NULL,                   /* mmap */
-  sbpcd_open,             /* open */
-  sbpcd_release,          /* release */
+  SBPCD_OPEN_F,           /* open */
+  SBPCD_RELEASE_F,        /* release */
   NULL,                   /* fsync */
   NULL                    /* fasync */
 };
@@ -3178,7 +3256,7 @@
  * not the soundcard base address.
  *
  */
-void sbpcd_setup(char *s, int *p)
+void SBPCD_SETUP(char *s, int *p)
 {
   DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s));
   sbpro_type=0;
@@ -3208,7 +3286,7 @@
  *     -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi)
  *     -> I/O base address (0x320, 0x330, 0x340, 0x350)
  */
-int config_spea(void)
+static int config_spea(void)
 {
   int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
   int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
@@ -3225,7 +3303,7 @@
   i=inb(SPEA_REG_1);
   if (i!=0x0F)
     {
-      DPRINTF((DBG_INF,"SBPCD: no SPEA interface at %04X present.\n",
+      DPRINTF((DBG_SEQ,"SBPCD: no SPEA interface at %04X present.\n",
 	       sbpcd_ioaddr));
       return (-1); /* no interface found */
     }
@@ -3249,18 +3327,22 @@
   OUT(SPEA_REG_2,i);
 
   sbpro_type = 0; /* acts like a LaserMate interface now */
+  DPRINTF((DBG_SEQ,"SBPCD: found SPEA interface at %04X.\n",
+	   sbpcd_ioaddr));
   return (0);
 }
 /*==========================================================================*/
 /*
  *  Test for presence of drive and initialize it.  Called at boot time.
  */
-u_long sbpcd_init(u_long mem_start, u_long mem_end)
+unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
 {
   int i=0, j=0;
   int addr[2]={1, CDROM_PORT};
   int port_index;
    
+  sti(); /* necessary, has consequences for other drivers' init routines */
+
   DPRINTF((DBG_INF,"SBPCD version %s\n", VERSION));
 
   DPRINTF((DBG_INF,"SBPCD: Looking for a SoundBlaster/Matsushita CD-ROM drive\n"));
@@ -3279,7 +3361,6 @@
   DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
   DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
   DPRINTF((DBG_WRN,"SBPCD: \n"));
-  sti(); /* to avoid possible "printk" bug */
 
   autoprobe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
   autoprobe[1]=sbpro_type; /* possibly changed by kernel command line */
@@ -3288,48 +3369,54 @@
     {
       addr[1]=autoprobe[port_index];
       if (check_region(addr[1],4)) continue;
-      DPRINTF((DBG_INI,"SBPCD: check_region done.\n"));
+      DPRINTF((DBG_INI,"SBPCD: check_region: free.\n"));
       if (autoprobe[port_index+1]==0) type=str_lm;
       else if (autoprobe[port_index+1]==1) type=str_sb;
       else type=str_sp;
-      sbpcd_setup(type, addr);
+      SBPCD_SETUP(type, addr);
       DPRINTF((DBG_INF,"SBPCD: Trying to detect a %s CD-ROM drive at 0x%X.\n",
 	             type, CDo_command));
 
       DPRINTF((DBG_INF,"SBPCD: - "));
-      sti(); /* to avoid possible "printk" bug */
       if (autoprobe[port_index+1]==2)
 	{
 	  i=config_spea();
-	  if (i<0) continue;
+	  if (i<0)
+	    {
+	      DPRINTF((DBG_INF,"\n"));
+	      continue;
+	    }
 	}
       i=check_drives();
       DPRINTF((DBG_INI,"SBPCD: check_drives done.\n"));
-      sti(); /* to avoid possible "printk" bug */
       if (i>=0) break; /* drive found */
       DPRINTF((DBG_INF,"\n"));
-      sti(); /* to avoid possible "printk" bug */
     } /* end of cycling through the set of possible I/O port addresses */
 
   if (ndrives==0)
     {
       printk("SBPCD: No drive found.\n");
-      sti();
+#if PRINTK_BUG
+      sti(); /* to avoid possible "printk" bug */
+#endif
       return (mem_start);
     }
 
   if (port_index>0)
     {
       printk("SBPCD: You should configure sbpcd.h for your hardware.\n");
-      sti();
+#if PRINTK_BUG
+      sti(); /* to avoid possible "printk" bug */
+#endif
     }
 
   printk("SBPCD: %d %s CD-ROM drive(s) at 0x%04X.\n",
 	   ndrives, type, CDo_command);
+#if PRINTK_BUG
   sti(); /* to avoid possible "printk" bug */
+#endif
   check_datarate();
   DPRINTF((DBG_INI,"SBPCD: check_datarate done.\n"));
-  sti(); /* to avoid possible "printk" bug */
 
   for (j=0;j<NR_SBPCD;j++)
     {
@@ -3354,16 +3441,13 @@
 	    {
 	      i=xx_ReadError();
 	      DPRINTF((DBG_INI,"SBPCD: init: xx_ReadError returns %d\n",i));
-	      sti(); /* to avoid possible "printk" bug */
 	    }
 	}
       DPRINTF((DBG_INI,"SBPCD: init: first GetStatus: %d\n",i));
-      sti(); /* to avoid possible "printk" bug */
       if (DriveStruct[d].error_byte==aud_12)
 	{
 	  do { i=GetStatus();
 	       DPRINTF((DBG_INI,"SBPCD: init: second GetStatus: %02X\n",i));
-	       sti(); /* to avoid possible "printk" bug */
 	       if (i<0) break;
 	       if (!st_caddy_in) break;
 	     }
@@ -3382,7 +3466,9 @@
   if (register_blkdev(MAJOR_NR, "sbpcd", &sbpcd_fops) != 0)
     {
       printk("SBPCD: Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
+#if PRINTK_BUG
       sti(); /* to avoid possible "printk" bug */
+#endif
       return (mem_start);
     }
   blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
@@ -3410,7 +3496,6 @@
   blksize_size[MAJOR_NR]=sbpcd_blocksizes;
 
   DPRINTF((DBG_INF,"SBPCD: init done.\n"));
-  sti(); /* to avoid possible "printk" bug */
   return (mem_start);
 }
 /*==========================================================================*/
@@ -3419,7 +3504,7 @@
  * used externally (isofs/inode.c, fs/buffer.c)
  * Currently disabled (has to get "synchronized").
  */
-int check_sbpcd_media_change(int full_dev, int unused_minor)
+int SBPCD_MEDIA_CHANGE(int full_dev, int unused_minor)
 {
   int st;
 
diff -u --recursive --new-file v1.1.22/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v1.1.22/linux/drivers/char/n_tty.c	Wed Jun 22 14:57:07 1994
+++ linux/drivers/char/n_tty.c	Sun Jun 26 23:47:30 1994
@@ -967,9 +967,12 @@
 				return 1;
 			if (tty_hung_up_p(file))
 				return 1;
-			if (!tty->read_wait)
-				tty->minimum_to_wake = MIN_CHAR(tty) ?
-					MIN_CHAR(tty) : 1;
+			if (!tty->read_wait) {
+				if (MIN_CHAR(tty) && !TIME_CHAR(tty))
+					tty->minimum_to_wake = MIN_CHAR(tty);
+				else
+					tty->minimum_to_wake = 1;
+			}
 			select_wait(&tty->read_wait, wait);
 			return 0;
 		case SEL_OUT:
diff -u --recursive --new-file v1.1.22/linux/fs/exec.c linux/fs/exec.c
--- v1.1.22/linux/fs/exec.c	Tue Jun 21 14:16:23 1994
+++ linux/fs/exec.c	Sun Jun 26 22:36:51 1994
@@ -255,13 +255,13 @@
 		dump_start = dump.u_tsize << 12;
 		dump_size = dump.u_dsize << 12;
 		DUMP_WRITE(dump_start,dump_size);
-	};
+	}
 /* Now prepare to dump the stack area */
 	if (dump.u_ssize != 0) {
 		dump_start = dump.start_stack;
 		dump_size = dump.u_ssize << 12;
 		DUMP_WRITE(dump_start,dump_size);
-	};
+	}
 /* Finally dump the task struct.  Not be used by gdb, but could be useful */
 	set_fs(KERNEL_DS);
 	DUMP_WRITE(current,sizeof(*current));
@@ -854,8 +854,8 @@
 			if (error != N_TXTADDR(ex)) {
 				sys_close(fd);
 				send_sig(SIGSEGV, current, 0);
-				return 0;
-			};
+				return -EINVAL;
+			}
 		}
 		
  		error = do_mmap(file, N_TXTADDR(ex) + ex.a_text, ex.a_data,
@@ -864,8 +864,8 @@
 		sys_close(fd);
 		if (error != N_TXTADDR(ex) + ex.a_text) {
 			send_sig(SIGSEGV, current, 0);
-			return 0;
-		};
+			return -EINVAL;
+		}
 		current->executable = bprm->inode;
 		bprm->inode->i_count++;
 	}
diff -u --recursive --new-file v1.1.22/linux/fs/msdos/misc.c linux/fs/msdos/misc.c
--- v1.1.22/linux/fs/msdos/misc.c	Tue Apr 19 10:53:24 1994
+++ linux/fs/msdos/misc.c	Sun Jun 26 23:50:58 1994
@@ -18,6 +18,7 @@
   "EXECOMBINAPPSYSDRVOVLOVROBJLIBDLLPIF"	/* program code */
   "ARCZIPLHALZHZOOTARZ  ARJ"	/* common archivers */
   "TZ TAZTZPTPZ"		/* abbreviations of tar.Z and tar.zip */
+  "GZ TGZDEB"			/* .gz, .tar.gz and Debian packages   */
   "GIFBMPTIFGL JPGPCX"		/* graphics */
   "TFMVF GF PK PXLDVI";		/* TeX */
 
diff -u --recursive --new-file v1.1.22/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.1.22/linux/fs/proc/array.c	Tue May 24 00:34:54 1994
+++ linux/fs/proc/array.c	Mon Jun 27 14:57:46 1994
@@ -466,6 +466,7 @@
 extern int get_module_list(char *);
 extern int get_device_list(char *);
 extern int get_filesystem_list(char *);
+extern int get_ksyms_list(char *);
 
 static int array_read(struct inode * inode, struct file * file,char * buf, int count)
 {
@@ -528,6 +529,9 @@
 			break;
 		case 19:
 			length = get_filesystem_list(page);
+			break;
+		case 20:
+			length = get_ksyms_list(page);
 			break;
 		default:
 			free_page((unsigned long) page);
diff -u --recursive --new-file v1.1.22/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.1.22/linux/fs/proc/root.c	Sat May  7 14:54:10 1994
+++ linux/fs/proc/root.c	Mon Jun 27 14:57:45 1994
@@ -69,6 +69,7 @@
    	{17,4,"stat" },
    	{18,7,"devices" },
    	{19,11,"filesystems" },
+   	{20,5,"ksyms" },
 };
 
 #define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
diff -u --recursive --new-file v1.1.22/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.1.22/linux/include/linux/cdrom.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/cdrom.h	Mon Jun 27 16:35:36 1994
@@ -1,7 +1,8 @@
 /****************************************************************************************
  *											*
- * SCSI header library for linux							*
+ * general (not only SCSI) header library for linux CDROM drivers                       *
  * (C) 1992 David Giller rafetmad@oxy.edu						*
+ *     1994 Eberhard Moenkeberg emoenke@gwdg.de ("read audio" and some other stuff)	*
  *											*
  * <linux/cdrom.h> -- CD-ROM IOCTLs and structs		 				*
  *											*
@@ -11,6 +12,20 @@
 #define	_LINUX_CDROM_H
 
 /*
+ * some fix numbers
+ */
+#define CD_MINS                   75  /* minutes per CD                  */
+#define CD_SECS                   60  /* seconds per minute              */
+#define CD_FRAMES                 75  /* frames per second               */
+#define CD_FRAMESIZE            2048  /* bytes per frame, cooked mode    */
+#define CD_FRAMESIZE_XA         2340  /* bytes per frame, "xa" mode      */
+#define CD_FRAMESIZE_RAW        2352  /* bytes per frame, "raw" mode     */
+#define CD_FRAMESIZE_SUB          96  /* subchannel data size            */
+#define CD_BLOCK_OFFSET          150  /* offset of first logical frame   */
+#define CD_XA_HEAD                12  /* header size of XA frame         */
+#define CD_XA_TAIL               280  /* tail size of XA frame           */
+
+/*
  *
  * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
  *
@@ -295,6 +310,28 @@
 	int	cdread_buflen;
 	};
 
+/*
+ * preliminary extensions for transfering audio frames
+ * currently used by sbpcd.c
+ * (still may change if other drivers will use it, too):
+ */
+struct cdrom_read_audio
+	{
+	  union
+	    {
+	      struct 			
+		{
+		  u_char minute;
+		  u_char second;
+		  u_char frame;
+		} msf;
+	      int	lba;
+	    } addr; /* frame address */
+	  u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+	  int nframes; /* number of 2352-byte-frames to read at once, currently only 1 allowed */
+	  u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+	};
+
 #ifdef FIVETWELVE
 #define	CDROM_MODE1_SIZE	512
 #else
@@ -309,7 +346,7 @@
 #define	CDROMPAUSE		0x5301		/* pause			*/
 #define	CDROMRESUME		0x5302		/* resume			*/
 
-#define	CDROMPLAYMSF		0x5303		/* (stuct cdrom_msf)		*/
+#define	CDROMPLAYMSF		0x5303		/* (struct cdrom_msf)		*/
                                                 /* SCMD_PLAY_AUDIO_MSF		*/
 
 #define	CDROMPLAYTRKIND		0x5304		/* (struct cdrom_ti)		*/
@@ -336,5 +373,12 @@
 
 #define	CDROMREADMODE1		0x530d		/* (struct cdrom_read)		*/
                                                 /* read type-1 data 		*/
+/*
+ * preliminary extension for transfering audio frames
+ * currently used by sbpcd.c
+ * (still may change if other drivers will use it, too):
+ */
+#define	CDROMREADAUDIO		0x530e		/* (struct cdrom_read_audio)	*/
 
 #endif  _LINUX_CDROM_H
+
diff -u --recursive --new-file v1.1.22/linux/include/linux/module.h linux/include/linux/module.h
--- v1.1.22/linux/include/linux/module.h	Sun Dec 12 13:48:27 1993
+++ linux/include/linux/module.h	Mon Jun 27 14:56:58 1994
@@ -1,5 +1,7 @@
 /*
  * Dynamic loading of modules into the kernel.
+ *
+ * Modified by Bjorn Ekwall <bj0rn@blox.se>
  */
 
 #ifndef _LINUX_MODULE_H
@@ -16,9 +18,36 @@
 /* maximum length of symbol name */
 #define SYM_MAX_NAME 60
 
+struct kernel_sym { /* sent to "insmod" */
+	unsigned long value;		/* value of symbol */
+	char name[SYM_MAX_NAME];	/* name of symbol */
+};
+
+struct module_ref {
+	struct module *module;
+	struct module_ref *next;
+};
+
+struct internal_symbol {
+	void *addr;
+	char *name;
+	};
+
+struct symbol_table { /* received from "insmod" */
+	int size; /* total, including string table!!! */
+	int n_symbols;
+	int n_refs;
+	struct internal_symbol symbol[0]; /* actual size defined by n_symbols */
+	struct module_ref ref[0]; /* actual size defined by n_refs */
+};
+/*
+ * Note: The string table follows immediately after the symbol table in memory!
+ */
 
 struct module {
 	struct module *next;
+	struct module_ref *ref;	/* the list of modules that refer to me */
+	struct symbol_table *symtab;
 	char *name;
 	int size;			/* size of module in pages */
 	void* addr;			/* address of module */
@@ -26,19 +55,13 @@
 	void (*cleanup)(void);		/* cleanup routine */
 };
 
-
 struct mod_routines {
 	int (*init)(void);		/* initialization routine */
 	void (*cleanup)(void);		/* cleanup routine */
 };
 
-
-struct kernel_sym {
-	unsigned long value;		/* value of symbol */
-	char name[SYM_MAX_NAME];	/* name of symbol */
-};
-
-extern struct module *module_list;
+/* rename_module_symbol(old_name, new_name)  WOW! */
+extern int rename_module_symbol(char *, char *);
 
 
 /*
diff -u --recursive --new-file v1.1.22/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.1.22/linux/include/linux/sbpcd.h	Thu Jun  9 18:56:12 1994
+++ linux/include/linux/sbpcd.h	Mon Jun 27 16:35:36 1994
@@ -10,11 +10,25 @@
  *                                 sbpcd=0x300,LaserMate
  *                             or
  *                                 sbpcd=0x330,SPEA
- * these strings are case sensitive !!!
+ *
+ *  and, if you have a second CDROM controller board,
+ *                                 sbpcd2=0x310,LaserMate
+ *  and so on.
+ *
+ * These strings are case sensitive !!!
  */
 
-/* 
- * change this to select the type of your interface board:
+/*
+ * put your CDROM port base address into CDROM_PORT
+ * and specify the type of your interface in SBPRO.
+ *
+ * SBPRO addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
+ * LASERMATE (CI-101P) adresses typically are 0x0300, 0x0310, ...
+ * SPEA addresses are 0x320, 0x330, 0x340, 0x350
+ * there are some soundcards on the market with 0x0630, 0x0650, ...
+ *
+ * example: if your SBPRO audio address is 0x220, specify 0x230.
+ *
  *
  * set SBPRO to 1 for "true" SoundBlaster card
  * set SBPRO to 0 for "poor" (no sound) interface cards
@@ -30,26 +44,65 @@
  * (currently I do not know any "compatible" with SBPRO 1)
  * 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
+#endif
 
+/* ignore the rest if you have only one interface board & driver */
+
+#if !(SBPCD_ISSUE-2) /* second interface board: */
+#define CDROM_PORT 0x0370
+#define SBPRO     0
+#endif
+#if !(SBPCD_ISSUE-3) /* third interface board: */
+#define CDROM_PORT 0x0330
+#define SBPRO     0
+#endif
+#if !(SBPCD_ISSUE-4) /* fourth interface board: */
+#define CDROM_PORT 0x0340
+#define SBPRO     0
+#endif
+
+/*==========================================================================*/
+/*==========================================================================*/
 /*
- * put your CDROM port base address here:
- * SBPRO addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
- * LASERMATE (CI-101P) adresses typically are 0x0300, 0x0310, ...
- * SPEA addresses are 0x320, 0x330, 0x340, 0x350
- * there are some soundcards on the market with 0x0630, 0x0650, ...
- *
- * example: if your SBPRO audio address is 0x220, specify 0x230.
- *
+ * nothing to change below here if you are not experimenting
  */
-#define CDROM_PORT 0x0230
+#ifndef _LINUX_SBPCD_H
 
+#define _LINUX_SBPCD_H
 
 /*==========================================================================*/
 /*==========================================================================*/
 /*
- * nothing to change below here if you are not experimenting
+ * to fork and execute a function after some elapsed time:
+ * one "jifs" unit is 10 msec.
  */
+#undef MY_TIMER
+#undef SET_TIMER
+#undef CLEAR_TIMER
+
+#if !(SBPCD_ISSUE-1)
+#define MY_TIMER SBPCD_TIMER
+#endif
+#if !(SBPCD_ISSUE-2)
+#define MY_TIMER SBPCD2_TIMER
+#endif
+#if !(SBPCD_ISSUE-3)
+#define MY_TIMER SBPCD3_TIMER
+#endif
+#if !(SBPCD_ISSUE-4)
+#define MY_TIMER SBPCD4_TIMER
+#endif
+
+#define SET_TIMER(func, jifs) \
+        ((timer_table[MY_TIMER].expires = jiffies + jifs), \
+        (timer_table[MY_TIMER].fn = func), \
+        (timer_active |= 1<<MY_TIMER))
+
+#define CLEAR_TIMER	timer_active &= ~(1<<MY_TIMER)
+
 /*==========================================================================*/
 /*==========================================================================*/
 /*
@@ -80,7 +133,8 @@
 #define DBG_LCK		23	/* door (un)lock info */
 #define DBG_SQ 		24	/* dump SubQ frame */
 #define DBG_AUD		25      /* "read audio" debugging */
-#define DBG_000		26      /* unnecessary information */
+#define DBG_SEQ		26      /* Sequoia interface configuration trace */
+#define DBG_000		27      /* unnecessary information */
 
 /*==========================================================================*/
 /*==========================================================================*/
@@ -207,35 +261,14 @@
 #define READ_AU  0x08 /* "audio frame": 2352 bytes per frame */
 
 /*
- * preliminary extensions to cdrom.h for transfering audio frames:
- */
-#define CDROMREADAUDIO 0xE0 /* IOCTL function (arg = &cdrom_aud) */
-
-struct cdrom_aud { u_int lba; /* frame address */
-                   u_char *buf; /* frame buffer (2352 bytes) */
-                 };
-
-/*
  * sense byte: used only if new_drive
  *                  only during cmd 09 00 xx ah al 00 00
  *
  *          values: 00
- *                  82
+ *                  82 "raw audio" mode
  *                  xx from infobuf[0] after 85 00 00 00 00 00 00
  */
 
-
-#define CD_MINS                   75  /* minutes per CD                  */
-#define CD_SECS                   60  /* seconds per minutes             */
-#define CD_FRAMES                 75  /* frames per second               */
-#define CD_FRAMESIZE            2048  /* bytes per frame, data mode      */
-#define CD_FRAMESIZE_XA         2340  /* bytes per frame, "xa" mode      */
-#define CD_FRAMESIZE_RAW        2352  /* bytes per frame, "raw" mode     */
-#define CD_FRAMESIZE_SUB          96  /* subchannel data size            */
-#define CD_BLOCK_OFFSET          150  /* offset of first logical frame   */
-#define CD_XA_HEAD                12  /* header size of XA frame         */
-#define CD_XA_TAIL               280  /* tail size of XA frame           */
-
 /* audio status (bin) */
 #define aud_00 0x00 /* Audio status byte not supported or not valid */
 #define audx11 0x0b /* Audio play operation in progress             */
@@ -406,7 +439,6 @@
 
 /*
  * highest allowed drive number (MINOR+1)
- * currently only one controller, maybe later up to 4
  */
 #define NR_SBPCD 4
 
@@ -431,23 +463,10 @@
 
 /*==========================================================================*/
 /*
- * to fork and execute a function after some elapsed time:
- * one "jifs" unit is 10 msec.
- */
-#define SET_TIMER(func, jifs) \
-        ((timer_table[SBPCD_TIMER].expires = jiffies + jifs), \
-        (timer_table[SBPCD_TIMER].fn = func), \
-        (timer_active |= 1<<SBPCD_TIMER))
-
-#define CLEAR_TIMER	timer_active &= ~(1<<SBPCD_TIMER)
-
-/*==========================================================================*/
-/*
  * Creative Labs Programmers did this:
  */
 #define MAX_TRACKS	120 /* why more than 99? */
 
-
 /*==========================================================================*/
 /*
  * To make conversions easier (machine dependent!)
@@ -467,3 +486,4 @@
 BLK;
 
 /*==========================================================================*/
+#endif _LINUX_SBPCD_H
diff -u --recursive --new-file v1.1.22/linux/init/main.c linux/init/main.c
--- v1.1.22/linux/init/main.c	Fri Jun 17 15:20:08 1994
+++ linux/init/main.c	Mon Jun 27 14:57:45 1994
@@ -71,6 +71,7 @@
 extern int vsprintf(char *,const char *,va_list);
 extern void init(void);
 extern void init_IRQ(void);
+extern void init_modules(void);
 extern long kmalloc_init (long,long);
 extern long blk_dev_init(long,long);
 extern long chr_dev_init(long,long);
@@ -399,6 +400,7 @@
 	init_IRQ();
 	sched_init();
 	parse_options(command_line);
+	init_modules();
 #ifdef CONFIG_PROFILE
 	prof_buffer = (unsigned long *) memory_start;
 	prof_len = (unsigned long) &end;
diff -u --recursive --new-file v1.1.22/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.1.22/linux/kernel/ksyms.c	Wed Jun 22 14:57:10 1994
+++ linux/kernel/ksyms.c	Mon Jun 27 14:56:45 1994
@@ -2,6 +2,9 @@
  * Herein lies all the functions/variables that are "exported" for linkage
  * with dynamically loaded kernel modules.
  *			Jon.
+ *
+ * Stacked module support and unified symbol table added by
+ * Bjorn Ekwall <bj0rn@blox.se>
  */
 
 #include <linux/autoconf.h>
@@ -16,12 +19,16 @@
 #include <linux/interrupt.h>
 #include <linux/binfmts.h>
 #include <linux/personality.h>
+#include <linux/module.h>
 #ifdef CONFIG_INET
 #include <linux/netdevice.h>
 #endif
+
+#include <asm/irq.h>
   
 extern void *sys_call_table;
 
+/* must match struct internal_symbol !!! */
 #define X(name)	{ (void *) &name, "_" #name }
 
 #ifdef CONFIG_FTAPE
@@ -53,12 +60,14 @@
 extern int dev_rint(unsigned char *, long, int, struct device *);
 extern void dev_tint(struct device *);
 extern struct device *irq2dev_map[];
+extern void dev_kfree_skb(struct sk_buff *, int);
 #endif
 
-struct {
-	void *addr;
-	const char *name;
-} symbol_table[] = {
+struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
+	{
+	/* stackable module support */
+	X(rename_module_symbol),
+
 	/* system info variables */
 	X(EISA_bus),
 	X(wp_works_ok),
@@ -109,6 +118,8 @@
 	X(irqaction),
 	X(request_irq),
 	X(free_irq),
+	X(enable_irq),
+	X(disable_irq),
 	X(bh_active),
 	X(bh_mask),
 
@@ -164,7 +175,18 @@
 	X(dev_rint),
 	X(dev_tint),
 	X(irq2dev_map),
+	X(dev_kfree_skb),
 #endif
+
+	/********************************************************
+	 * Do not add anything below this line,
+	 * as the stacked modules depend on this!
+	 */
+	{ NULL, NULL } /* mark end of table */
+	},
+	{ NULL, NULL } /* no module refs */
 };
 
+/*
 int symbol_table_size = sizeof (symbol_table) / sizeof (symbol_table[0]);
+*/
diff -u --recursive --new-file v1.1.22/linux/kernel/module.c linux/kernel/module.c
--- v1.1.22/linux/kernel/module.c	Wed Jun 22 14:57:10 1994
+++ linux/kernel/module.c	Mon Jun 27 14:56:45 1994
@@ -6,13 +6,109 @@
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
+/*
+ * Heavily modified by Bjorn Ekwall <bj0rn@blox.se> May 1994 (C)
+ * This source is covered by the GNU GPL, the same as all kernel sources.
+ *
+ * Features:
+ *	- Supports stacked modules (removeable only of there are no dependants).
+ *	- Supports table of symbols defined by the modules.
+ *	- Supports /proc/ksyms, showing value, name and owner of all
+ *	  the symbols defined by all modules (in stack order).
+ *	- Added module dependencies information into /proc/modules
+ *	- Supports redefines of all symbols, for streams-like behaviour.
+ *	- Compatible with older versions of insmod.
+ *
+ */
+
+#ifdef DEBUG_MODULE
+#define PRINTK(a) printk a
+#else
+#define PRINTK(a) /* */
+#endif
+
+static struct module kernel_module;
+static struct module *module_list = &kernel_module;
+
+static int freeing_modules; /* true if some modules are marked for deletion */
+
+static struct module *find_module( const char *name);
+static int get_mod_name( char *user_name, char *buf);
+static int free_modules( void);
+
+
+/*
+ * Called at boot time
+ */
+void init_modules(void) {
+	extern struct symbol_table symbol_table; /* in kernel/ksyms.c */
+	struct internal_symbol *sym;
+	int i;
+
+	for (i = 0, sym = symbol_table.symbol; sym->name; ++sym, ++i)
+		;
+	symbol_table.n_symbols = i;
+
+	kernel_module.symtab = &symbol_table;
+	kernel_module.state = MOD_RUNNING; /* Hah! */
+	kernel_module.name = "";
+}
 
-struct module *module_list = NULL;
-int freeing_modules;		/* true if some modules are marked for deletion */
+int
+rename_module_symbol(char *old_name, char *new_name)
+{
+	struct internal_symbol *sym;
+	int i = 0; /* keep gcc silent */
+
+	if (module_list->symtab) {
+		sym = module_list->symtab->symbol;
+		for (i = module_list->symtab->n_symbols; i > 0; ++sym, --i) {
+			if (strcmp(sym->name, old_name) == 0) { /* found it! */
+				sym->name = new_name; /* done! */
+				PRINTK(("renamed %s to %s\n", old_name, new_name));
+				return 1; /* it worked! */
+			}
+		}
+	}
+	printk("rename %s to %s failed!\n", old_name, new_name);
+	return 0; /* not there... */
 
-struct module *find_module( const char *name);
-int get_mod_name( char *user_name, char *buf);
-int free_modules( void);
+	/*
+	 * This one will change the name of the first matching symbol!
+	 *
+	 * With this function, you can replace the name of a symbol defined
+	 * in the current module with a new name, e.g. when you want to insert
+	 * your own function instead of a previously defined function
+	 * with the same name.
+	 *
+	 * "Normal" usage:
+	 *
+	 * bogus_function(int params)
+	 * {
+	 *	do something "smart";
+	 *	return real_function(params);
+	 * }
+	 *
+	 * ...
+	 *
+	 * init_module()
+	 * {
+	 *	if (rename_module_symbol("_bogus_function", "_real_function"))
+	 *		printk("yep!\n");
+	 *	else
+	 *		printk("no way!\n");
+	 * ...
+	 * }
+	 *
+	 * When loading this module, real_function will be resolved
+	 * to the real function address.
+	 * All later loaded modules that refer to "real_function()" will
+	 * then really call "bogus_function()" instead!!!
+	 *
+	 * This feature will give you ample opportunities to get to know
+	 * the taste of your foot when you stuff it into your mouth!!!
+	 */
+}
 
 /*
  * Allocate space for a module.
@@ -20,13 +116,12 @@
 asmlinkage int
 sys_create_module(char *module_name, unsigned long size)
 {
-	int npages;
-	void* addr;
-	int len;
-	char name[MOD_MAX_NAME];
-	char *savename;
 	struct module *mp;
+	void* addr;
 	int error;
+	int npages;
+	int sspace = sizeof(struct module) + MOD_MAX_NAME;
+	char name[MOD_MAX_NAME];
 
 	if (!suser())
 		return -EPERM;
@@ -37,30 +132,32 @@
 	if (find_module(name) != NULL) {
 		return -EEXIST;
 	}
-	len = strlen(name) + 1;
-	if ((savename = (char*) kmalloc(len, GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-	memcpy(savename, name, len);
-	if ((mp = (struct module*) kmalloc(sizeof *mp, GFP_KERNEL)) == NULL) {
-		kfree(savename);
+
+	if ((mp = (struct module*) kmalloc(sspace, GFP_KERNEL)) == NULL) {
 		return -ENOMEM;
 	}
+	strcpy((char *)(mp + 1), name); /* why not? */
+
 	npages = (size + sizeof (int) + 4095) / 4096;
 	if ((addr = vmalloc(npages * 4096)) == 0) {
-		kfree_s(mp, sizeof *mp);
-		kfree(savename);
+		kfree_s(mp, sspace);
 		return -ENOMEM;
 	}
-	mp->name = savename;
+
+	mp->next = module_list;
+	mp->ref = NULL;
+	mp->symtab = NULL;
+	mp->name = (char *)(mp + 1);
 	mp->size = npages;
 	mp->addr = addr;
 	mp->state = MOD_UNINITIALIZED;
-	* (int *) addr = 0;		/* set use count to zero */
 	mp->cleanup = NULL;
-	mp->next = module_list;
-	module_list = mp;
-	printk("module `%s' (%lu pages @ 0x%08lx) created\n",
-		mp->name, (unsigned long) mp->size, (unsigned long) mp->addr);
+
+	* (int *) addr = 0;	/* set use count to zero */
+	module_list = mp;	/* link it in */
+
+	PRINTK(("module `%s' (%lu pages @ 0x%08lx) created\n",
+		mp->name, (unsigned long) mp->size, (unsigned long) mp->addr));
 	return (int) addr;
 }
 
@@ -69,15 +166,24 @@
  */
 asmlinkage int
 sys_init_module(char *module_name, char *code, unsigned codesize,
-		struct mod_routines *routines)
+		struct mod_routines *routines,
+		struct symbol_table *symtab)
 {
 	struct module *mp;
+	struct symbol_table *newtab;
 	char name[MOD_MAX_NAME];
 	int error;
 	struct mod_routines rt;
 
 	if (!suser())
 		return -EPERM;
+
+	/* A little bit of protection... we "know" where the user stack is... */
+	if (symtab && ((unsigned long)symtab > 0xb0000000)) {
+		printk("warning: you are using an old insmod, no symbols will be inserted!\n");
+		symtab = NULL;
+	}
+
 	/*
 	 * First reclaim any memory from dead modules that where not
 	 * freed when deleted. Should I think be done by timers when
@@ -87,8 +193,8 @@
 
 	if ((error = get_mod_name(module_name, name)) != 0)
 		return error;
-	printk( "initializing module `%s', %d (0x%x) bytes\n",
-		name, codesize, codesize);
+	PRINTK(("initializing module `%s', %d (0x%x) bytes\n",
+		name, codesize, codesize));
 	memcpy_fromfs(&rt, routines, sizeof rt);
 	if ((mp = find_module(name)) == NULL)
 		return -ENOENT;
@@ -97,12 +203,53 @@
 	memcpy_fromfs((char *)mp->addr + sizeof (int), code, codesize);
 	memset((char *)mp->addr + sizeof (int) + codesize, 0,
 		mp->size * 4096 - (codesize + sizeof (int)));
-	printk( "  init entry @ 0x%08lx, cleanup entry @ 0x%08lx\n",
-		(unsigned long) rt.init, (unsigned long) rt.cleanup);
+	PRINTK(( "module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
+		(unsigned long) rt.init, (unsigned long) rt.cleanup));
 	mp->cleanup = rt.cleanup;
+
+	/* update kernel symbol table */
+	if (symtab) { /* symtab == NULL means no new entries to handle */
+		struct internal_symbol *sym;
+		struct module_ref *ref;
+		int size;
+		int i;
+
+		if ((error = verify_area(VERIFY_READ, symtab, sizeof(int))))
+			return error;
+		memcpy_fromfs((char *)(&(size)), symtab, sizeof(int));
+
+		if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) {
+			return -ENOMEM;
+		}
+
+		if ((error = verify_area(VERIFY_READ, symtab, size)))
+			return error;
+		memcpy_fromfs((char *)(newtab), symtab, size);
+
+		/* relocate name pointers, index referred from start of table */
+		for (sym = &(newtab->symbol[0]), i = 0;
+			i < newtab->n_symbols; ++sym, ++i) {
+			sym->name += (long)newtab;
+		}
+		mp->symtab = newtab;
+
+		/* Update module references.
+		 * On entry, from "insmod", ref->module points to
+		 * the referenced module!
+		 * Also, "sym" from above, points to the first ref entry!!!
+		 */
+		for (ref = (struct module_ref *)sym, i = 0;
+			i < newtab->n_refs; ++ref, ++i) {
+			ref->next = ref->module->ref;
+			ref->module->ref = ref;
+			ref->module = mp;
+		}
+	}
+
 	if ((*rt.init)() != 0)
 		return -EBUSY;
 	mp->state = MOD_RUNNING;
+
 	return 0;
 }
 
@@ -115,12 +262,13 @@
 
 	if (!suser())
 		return -EPERM;
+	/* else */
 	if (module_name != NULL) {
 		if ((error = get_mod_name(module_name, name)) != 0)
 			return error;
 		if ((mp = find_module(name)) == NULL)
 			return -ENOENT;
-		if (GET_USE_COUNT(mp) != 0)
+		if ((mp->ref != NULL) || (GET_USE_COUNT(mp) != 0))
 			return -EBUSY;
 		if (mp->state == MOD_RUNNING)
 			(*mp->cleanup)();
@@ -130,38 +278,74 @@
 	return 0;
 }
 
+
 /*
  * Copy the kernel symbol table to user space.  If the argument is null,
  * just return the size of the table.
+ *
+ * Note that the transient module symbols are copied _first_,
+ * in lifo order!!!
+ *
+ * The symbols to "insmod" are according to the "old" format: struct kernel_sym,
+ * which is actually quite handy for this purpose.
+ * Note that insmod inserts a struct symbol_table later on...
+ * (as that format is quite handy for the kernel...)
+ *
+ * For every module, the first (pseudo)symbol copied is the module name
+ * and the adress of the module struct.
+ * This lets "insmod" keep track of references, and build the array of
+ * struct module_refs in the symbol table.
+ * The format of the module name is "#module", so that "insmod" can easily
+ * notice when a module name comes along. Also, this will make it possible
+ * to use old versions of "insmod", albeit with reduced functionality...
+ * The "kernel" module has an empty name.
  */
 asmlinkage int
 sys_get_kernel_syms(struct kernel_sym *table)
 {
-	struct symbol {
-		unsigned long addr;
-		char *name;
-	};
-	extern int symbol_table_size;
-	extern struct symbol symbol_table[];
-	int i;
-	struct symbol *from;
+	struct internal_symbol *from;
+	struct kernel_sym isym;
 	struct kernel_sym *to;
-	struct kernel_sym sym;
+	struct module *mp = module_list;
+	int i;
+	int nmodsyms = 0;
+
+	for (mp = module_list; mp; mp = mp->next) {
+		if (mp->symtab && mp->symtab->n_symbols) {
+			/* include the count for the module name! */
+			nmodsyms += mp->symtab->n_symbols + 1;
+		}
+	}
 
 	if (table != NULL) {
-		from = symbol_table;
 		to = table;
-		i = verify_area(VERIFY_WRITE, to, symbol_table_size * sizeof *table);
-		if (i)
+
+		if ((i = verify_area(VERIFY_WRITE, to, nmodsyms * sizeof(*table))))
 			return i;
-		for (i = symbol_table_size ; --i >= 0 ; ) {
-			sym.value = from->addr;
-			strncpy(sym.name, from->name, sizeof sym.name);
-			memcpy_tofs(to, &sym, sizeof sym);
-			from++, to++;
+
+		/* copy all module symbols first (always LIFO order) */
+		for (mp = module_list; mp; mp = mp->next) {
+			if ((mp->state == MOD_RUNNING) &&
+				(mp->symtab != NULL) && (mp->symtab->n_symbols > 0)) {
+				/* magic: write module info as a pseudo symbol */
+				isym.value = (unsigned long)mp;
+				sprintf(isym.name, "#%s", mp->name);
+				memcpy_tofs(to, &isym, sizeof isym);
+				++to;
+
+				for (i = mp->symtab->n_symbols,
+					from = mp->symtab->symbol;
+					i > 0; --i, ++from, ++to) {
+
+					isym.value = (unsigned long)from->addr;
+					strncpy(isym.name, from->name, sizeof isym.name);
+					memcpy_tofs(to, &isym, sizeof isym);
+				}
+			}
 		}
 	}
-	return symbol_table_size;
+
+	return nmodsyms;
 }
 
 
@@ -199,6 +383,27 @@
 	return mp;
 }
 
+static void
+drop_refs(struct module *mp)
+{
+	struct module *step;
+	struct module_ref *prev;
+	struct module_ref *ref;
+
+	for (step = module_list; step; step = step->next) {
+		for (prev = ref = step->ref; ref; ref = prev->next) {
+			if (ref->module == mp) {
+				if (ref == step->ref)
+					step->ref = ref->next;
+				else
+					prev->next = ref->next;
+				break; /* every module only references once! */
+			}
+			else
+				prev = ref;
+		}
+	}
+}
 
 /*
  * Try to free modules which have been marked for deletion.  Returns nonzero
@@ -217,15 +422,22 @@
 	while ((mp = *mpp) != NULL) {
 		if (mp->state != MOD_DELETED) {
 			mpp = &mp->next;
-		} else if (GET_USE_COUNT(mp) != 0) {
-			freeing_modules = 1;
-			mpp = &mp->next;
-		} else {	/* delete it */
-			*mpp = mp->next;
-			vfree(mp->addr);
-			kfree(mp->name);
-			kfree_s(mp, sizeof *mp);
-			did_deletion = 1;
+		} else {
+			if (GET_USE_COUNT(mp) != 0) {
+				freeing_modules = 1;
+				mpp = &mp->next;
+			} else {	/* delete it */
+				*mpp = mp->next;
+				if (mp->symtab) {
+					if (mp->symtab->n_refs)
+						drop_refs(mp);
+					if (mp->symtab->size)
+						kfree_s(mp->symtab, mp->symtab->size);
+				}
+				vfree(mp->addr);
+				kfree_s(mp, sizeof(struct module) + MOD_MAX_NAME);
+				did_deletion = 1;
+			}
 		}
 	}
 	return did_deletion;
@@ -241,10 +453,12 @@
 	char *q;
 	int i;
 	struct module *mp;
+	struct module_ref *ref;
 	char size[32];
 
 	p = buf;
-	for (mp = module_list ; mp ; mp = mp->next) {
+	/* Do not show the kernel pseudo module */
+	for (mp = module_list ; mp && mp->next; mp = mp->next) {
 		if (p - buf > 4096 - 100)
 			break;			/* avoid overflowing buffer */
 		q = mp->name;
@@ -272,7 +486,60 @@
 			q = "  (bad state)";
 		while (*q)
 			*p++ = *q++;
+
+		if ((ref = mp->ref) != NULL) {
+			*p++ = '\t';
+			*p++ = '[';
+			for (; ref; ref = ref->next) {
+				q = ref->module->name;
+				while (*q)
+					*p++ = *q++;
+				if (ref->next)
+					*p++ = ' ';
+			}
+			*p++ = ']';
+		}
 		*p++ = '\n';
 	}
+	return p - buf;
+}
+
+
+/*
+ * Called by the /proc file system to return a current list of ksyms.
+ */
+int get_ksyms_list(char *buf)
+{
+	struct module *mp;
+	struct internal_symbol *sym;
+	int i;
+	char *p = buf;
+
+	for (mp = module_list; mp; mp = mp->next) {
+		if ((mp->state == MOD_RUNNING) &&
+			(mp->symtab != NULL) && (mp->symtab->n_symbols > 0)) {
+			for (i = mp->symtab->n_symbols,
+				sym = mp->symtab->symbol;
+				i > 0; --i, ++sym) {
+
+				if (p - buf > 4096 - 100) {
+					strcat(p, "...\n");
+					p += strlen(p);
+					return p - buf; /* avoid overflowing buffer */
+				}
+
+				if (mp->name[0]) {
+					sprintf(p, "%08lx %s\t[%s]\n",
+						(long)sym->addr, sym->name, mp->name);
+				}
+				else {
+					sprintf(p, "%08lx %s\n",
+						(long)sym->addr, sym->name);
+				}
+				p += strlen(p);
+			}
+		}
+	}
+
 	return p - buf;
 }
diff -u --recursive --new-file v1.1.22/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.22/linux/net/inet/tcp.c	Mon Jun 27 16:47:04 1994
+++ linux/net/inet/tcp.c	Mon Jun 27 17:32:02 1994
@@ -2030,9 +2030,9 @@
 	rt=ip_rt_route(saddr, NULL,NULL);
 	
 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
-		sk->window_clamp=rt->rt_window;
+		newsk->window_clamp = rt->rt_window;
 	else
-		sk->window_clamp=0;
+		newsk->window_clamp = 0;
 		
 	if (sk->user_mss)
 		newsk->mtu = sk->user_mss;
