diff -u --recursive --new-file v2.0.16/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.0.16/linux/Documentation/devices.tex	Wed Jul 10 13:11:40 1996
+++ linux/Documentation/devices.tex	Mon Sep  2 08:41:25 1996
@@ -1104,7 +1104,7 @@
 commercial interface by P\&E.
 
 \begin{devicelist}
-\major{54}{Electrocardiognosis Holter serial card}
+\major{54}{}{char }{Electrocardiognosis Holter serial card}
 	\minor{0}{/dev/holter0}{First Holter port}
 	\minor{1}{/dev/holter1}{Second Holter port}
 	\minor{2}{/dev/holter2}{Third Holter port}
diff -u --recursive --new-file v2.0.16/linux/Makefile linux/Makefile
--- v2.0.16/linux/Makefile	Sun Sep  1 09:15:32 1996
+++ linux/Makefile	Sun Sep  1 09:15:14 1996
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 16
+SUBLEVEL = 17
 
 ARCH = i386
 
diff -u --recursive --new-file v2.0.16/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v2.0.16/linux/drivers/block/cmd640.c	Sat Aug 17 21:19:26 1996
+++ linux/drivers/block/cmd640.c	Mon Sep  2 08:41:25 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/cmd640.c	Version 1.01  Aug 12, 1996
+ *  linux/drivers/block/cmd640.c	Version 1.02  Sep 01, 1996
  *
  *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
  */
@@ -94,9 +94,12 @@
  *  Version 1.00	Mmm.. cannot depend on PCMD_ENA in all systems
  *  Version 1.01	slow/fast devsel can be selected with "hdparm -p6/-p7"
  *			 ("fast" is necessary for 32bit I/O in some systems)
+ *  Version 1.02	fix bug that resulted in slow "setup times"
+ *			 (patch courtesy of Zoltan Hidvegi)
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
+#define CMD640_PREFETCH_MASKS 1
 
 #include <linux/config.h>
 #include <linux/types.h>
@@ -408,9 +411,11 @@
 		drive->no_io_32bit = 1;
 		drive->io_32bit = 0;
 	} else {
+#if CMD640_PREFETCH_MASKS
 		drive->no_unmask = 1;
-		drive->no_io_32bit = 0;
 		drive->unmask = 0;
+#endif
+		drive->no_io_32bit = 0;
 	}
 }
 
@@ -454,8 +459,10 @@
 	cli();
 	b = get_cmd640_reg(reg);
 	if (mode) {	/* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
 		drive->no_unmask = 1;
 		drive->unmask = 0;
+#endif
 		drive->no_io_32bit = 0;
 		b &= ~prefetch_masks[index];	/* enable prefetch */
 	} else {
@@ -556,9 +563,10 @@
 	 * Convert setup_count to internal chipset representation
 	 */
 	switch (setup_count) {
-		case 4:	 setup_count = 0x00;
-		case 3:	 setup_count = 0x80;
-		case 2:	 setup_count = 0x40;
+		case 4:	 setup_count = 0x00; break;
+		case 3:	 setup_count = 0x80; break;
+		case 1:
+		case 2:	 setup_count = 0x40; break;
 		default: setup_count = 0xc0; /* case 5 */
 	}
 
diff -u --recursive --new-file v2.0.16/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.0.16/linux/drivers/block/floppy.c	Sun Sep  1 09:15:32 1996
+++ linux/drivers/block/floppy.c	Mon Sep  2 10:55:26 1996
@@ -4067,12 +4067,16 @@
 	if (fd_request_irq()) {
 		DPRINT("Unable to grab IRQ%d for the floppy driver\n",
 			FLOPPY_IRQ);
+		MOD_DEC_USE_COUNT;
+		usage_count--;
 		return -1;
 	}
 	if (fd_request_dma()) {
 		DPRINT("Unable to grab DMA%d for the floppy driver\n",
 			FLOPPY_DMA);
 		fd_free_irq();
+		MOD_DEC_USE_COUNT;
+		usage_count--;
 		return -1;
 	}
 	for (fdc = 0; fdc < N_FDC; fdc++)
@@ -4098,7 +4102,6 @@
 		return;
 	}
 	INT_ON;
-	MOD_DEC_USE_COUNT;
 	fd_disable_dma();
 	fd_free_dma();
 	fd_disable_irq();
@@ -4131,6 +4134,7 @@
 	if (floppy_tq.sync)
 		printk("task queue still active\n");
 #endif
+	MOD_DEC_USE_COUNT;
 }
 
 
diff -u --recursive --new-file v2.0.16/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v2.0.16/linux/drivers/cdrom/sbpcd.c	Sat Aug 17 21:19:26 1996
+++ linux/drivers/cdrom/sbpcd.c	Sun Sep  1 09:14:25 1996
@@ -13,7 +13,7 @@
  *             labelled E2550UA or MK4015 or 2800F).
  */
 
-#define VERSION "v4.5 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v4.6 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
  *
@@ -293,11 +293,15 @@
  *       ll_rw_blk.c).
  *       "Check media change" without touching any drive.
  *
+ *  4.6  Use a semaphore to synchronize multi-activity; elaborated by Rob
+ *       Riggs <rriggs@tesser.com>. At the moment, we simply block "read"
+ *       against "ioctl" and vice versa. This could be refined further, but
+ *       I guess with almost no performance increase.
+ *       Experiments to speed up the CD-55A; again with help of Rob Riggs
+ *       (to be true, he gave both, idea & code. ;-)
  *
- *  TODO
  *
- *     synchronize multi-activity
- *        (data + audio + ioctl + disk change, multiple drives)
+ *  TODO
  *     implement "read all subchannel data" (96 bytes per frame)
  *
  *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
@@ -571,9 +575,8 @@
 static struct wait_queue *sbp_waitq = NULL;
 #endif FUTURE
 
-/*==========================================================================*/
-#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
-/*==========================================================================*/
+static int teac=SBP_TEAC_SPEED;
+static int buffers=SBP_BUFFER_FRAMES;
 
 static u_char family0[]="MATSHITA"; /* MKE CR-521, CR-522, CR-523 */
 static u_char family1[]="CR-56";    /* MKE CR-562, CR-563 */
@@ -592,8 +595,11 @@
 static u_char xa_head_buf[CD_XA_HEAD];
 static u_char xa_tail_buf[CD_XA_TAIL];
 
+#if OLD_BUSY
 static volatile u_char busy_data=0;
 static volatile u_char busy_audio=0; /* true semaphores would be safer */
+#endif OLD_BUSY
+static struct semaphore ioctl_read_sem = MUTEX;
 static u_long timeout;
 static volatile u_char timed_out_delay=0;
 static volatile u_char timed_out_data=0;
@@ -828,6 +834,8 @@
 	sti();
 }
 /*==========================================================================*/
+#define RETURN_UP(rc) {up(&ioctl_read_sem); return(rc);}
+/*==========================================================================*/
 /*
  *  convert logical_block_address to m-s-f_number (3 bytes only)
  */
@@ -960,6 +968,57 @@
 	sbp_sleep(15*HZ/10);
 	for (i=maxtim_data;i!=0;i--) inb(CDi_status);
 }
+/*====================================================================*/
+/*
+ * CDi status loop for Teac CD-55A (Rob Riggs)
+ *
+ * This is needed because for some strange reason
+ * the CD-55A can take a real long time to give a
+ * status response. This seems to happen after we
+ * issue a READ command where a long seek is involved.
+ *
+ * I tried to ensure that we get max throughput with
+ * minimal busy waiting. We busy wait at first, then
+ * "switch gears" and start sleeping. We sleep for
+ * longer periods of time the longer we wait.
+ *
+ */
+static int CDi_stat_loop_T(void)
+{
+	int	i, gear=1;
+	u_long  timeout_1, timeout_2, timeout_3, timeout_4;
+
+	timeout_1 = jiffies + HZ / 50;  /* sbp_sleep(0) for a short period */
+	timeout_2 = jiffies + HZ / 5;	/* nap for no more than 200ms */
+	timeout_3 = jiffies + 5 * HZ;	/* sleep for up to 5s */
+	timeout_4 = jiffies + 45 * HZ;	/* long sleep for up to 45s. */
+	do
+          {
+            i = inb(CDi_status);
+            if (!(i&s_not_data_ready)) return (i);
+            if (!(i&s_not_result_ready)) return (i);
+            switch(gear)
+              {
+              case 4:
+                sbp_sleep(HZ);
+                if (jiffies > timeout_4) gear++;
+                msg(DBG_TEA, "CDi_stat_loop_T: long sleep active.\n");
+                break;
+              case 3:
+                sbp_sleep(HZ/10);
+                if (jiffies > timeout_3) gear++;
+                break;
+              case 2:
+                sbp_sleep(HZ/100);
+                if (jiffies > timeout_2) gear++;
+                break;
+              case 1:
+                sbp_sleep(0);
+                if (jiffies > timeout_1) gear++;
+              }
+          } while (gear < 5);
+	return -1;
+}
 /*==========================================================================*/
 static int CDi_stat_loop(void)
 {
@@ -1290,8 +1349,20 @@
 	for (ntries=CMDT_TRIES;ntries>0;ntries--)
 	{
 		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ); /* fixme */
+#if 01
 		OUT(CDo_sel_i_d,1);
-		i=inb(CDi_status);
+#endif 01
+		if (teac==2)
+                  {
+                    if ((i=CDi_stat_loop_T()) == -1) break;
+                  }
+		else
+                  {
+#if 0
+                    OUT(CDo_sel_i_d,1);
+#endif 0
+                    i=inb(CDi_status);
+                  }
 		if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
 		{
 			OUT(CDo_sel_i_d,1);
@@ -1384,7 +1455,8 @@
 			return (-D_S[d].error_state-400);
 		}
 		if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
-		sbp_sleep(HZ/10);
+		if ((teac==0)||(ntries<(CMDT_TRIES-5))) sbp_sleep(HZ/10);
+		else sbp_sleep(HZ/100);
 		if (ntries>(CMDT_TRIES-50)) continue;
 		msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
 	}
@@ -3343,7 +3415,7 @@
 		}
 		else if (famV_drive)
 		  {
-		    if (j==100) D_S[d].drv_type=drv_at;
+		    if ((j==100)||(j==150)) D_S[d].drv_type=drv_at;
 		    ask_mail(); /* hopefully we get some feedback by this */
 		  }
 	}
@@ -3716,7 +3788,7 @@
 		return (i);
 	}
 	if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1;  /* possibly a weird PhotoCD */
-	else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
+	else D_S[d].sbp_bufsiz=buffers;
 	i=cc_ReadTocEntry(D_S[d].n_first_track);
 	if (i<0)
 	{
@@ -3878,16 +3950,17 @@
 		msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
 		return (-ENXIO);             /* no such drive */
 	}
+	down(&ioctl_read_sem);
 	if (d!=i) switch_drive(i);
 	
 #if 0
 	st=GetStatus();
-	if (st<0) return (-EIO);
+	if (st<0) RETURN_UP(-EIO);
 	
 	if (!toc_valid)
 	{
 		i=DiskInfo();
-		if (i<0) return (-EIO);	/* error reading TOC */
+		if (i<0) RETURN_UP(-EIO);	/* error reading TOC */
 	}
 #endif
 	
@@ -3895,9 +3968,9 @@
 	switch (cmd) 		/* Sun-compatible */
 	{
 	case DDIOCSDBG:		/* DDI Debug */
-		if (!suser()) return (-EPERM);
+		if (!suser()) RETURN_UP(-EPERM);
 		i=sbpcd_dbg_ioctl(arg,1);
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMPAUSE:     /* Pause the drive */
 		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
@@ -3910,19 +3983,19 @@
 		case audio_playing:
 			if (famL_drive) i=cc_ReadSubQ();
 			else i=cc_Pause_Resume(1);
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			if (famL_drive) i=cc_Pause_Resume(1);
 			else i=cc_ReadSubQ();
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
 			D_S[d].audio_state=audio_pausing;
-			return (0);
+			RETURN_UP(0);
 		case audio_pausing:
 			i=cc_Seek(D_S[d].pos_audio_start,1);
-			if (i<0) return (-EIO);
-			return (0);
+			if (i<0) RETURN_UP(-EIO);
+			RETURN_UP(0);
 		default:
-			return (-EINVAL);
+			RETURN_UP(-EINVAL);
 		}
 		
 	case CDROMRESUME: /* resume paused audio play */
@@ -3935,26 +4008,26 @@
 			i=cc_PlayAudio(D_S[d].pos_audio_start,
 				       D_S[d].pos_audio_end);
 		else i=cc_Pause_Resume(3);
-		if (i<0) return (-EIO);
+		if (i<0) RETURN_UP(-EIO);
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMPLAYMSF:
 		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			i=cc_Pause_Resume(1);
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			i=cc_ReadSubQ();
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
 			i=cc_Seek(D_S[d].pos_audio_start,1);
 		}
 		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
 		/* values come as msf-bin */
 		D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
@@ -3971,33 +4044,36 @@
 			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
 			DriveReset();
 			D_S[d].audio_state=0;
-			return (-EIO);
+			RETURN_UP(-EIO);
 		}
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
 		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
-			return (0);
-			return (-EINVAL);
+#if 1
+			RETURN_UP(0); /* just let us play on */
+#else
+			RETURN_UP(-EINVAL); /* play on, but say "error" */
+#endif
 		}
 		st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
 		if (st<0)
 		{
 			msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
-			return (st);
+			RETURN_UP(st);
 		}
 		memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
 		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
 		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
-		if (ti.cdti_trk0<D_S[d].n_first_track) return (-EINVAL);
-		if (ti.cdti_trk0>D_S[d].n_last_track) return (-EINVAL);
+		if (ti.cdti_trk0<D_S[d].n_first_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk0>D_S[d].n_last_track) RETURN_UP(-EINVAL);
 		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
 		if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
 		D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
@@ -4008,28 +4084,29 @@
 			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
 			DriveReset();
 			D_S[d].audio_state=0;
-			return (-EIO);
+			RETURN_UP(-EIO);
 		}
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADTOCHDR:        /* Read the table of contents header */
 		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
 		tochdr.cdth_trk0=D_S[d].n_first_track;
 		tochdr.cdth_trk1=D_S[d].n_last_track;
 		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
 		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
 		i=tocentry.cdte_track;
 		if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
-		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track) return (-EINVAL);
+		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track)
+                  RETURN_UP(-EINVAL);
 		tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
 		tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
 		tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
@@ -4041,68 +4118,68 @@
 		}
 		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
 			tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 		memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMRESET:      /* hard reset the drive */
 		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
 		i=DriveReset();
 		D_S[d].audio_state=0;
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMSTOP:      /* Spin down the drive */
 		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		i=cc_Pause_Resume(1);
 		D_S[d].audio_state=0;
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMSTART:  /* Spin up the drive */
 		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
 		cc_SpinUp();
 		D_S[d].audio_state=0;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMEJECT:
 		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
 		if (fam0_drive) return (0);
-		if (D_S[d].open_count>1) return (-EBUSY);
+		if (D_S[d].open_count>1) RETURN_UP(-EBUSY);
 		i=UnLockDoor();
 		D_S[d].open_count=-9; /* to get it locked next time again */
 		i=cc_SpinDown();
 		msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
 		msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
-		if (i<0) return (-EIO);
+		if (i<0) RETURN_UP(-EIO);
 		D_S[d].CD_changed=0xFF;
 		D_S[d].diskstate_flags=0;
 		D_S[d].audio_state=0;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMEJECT_SW:
 		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
-		if (fam0_drive) return (0);
+		if (fam0_drive) RETURN_UP(0);
 		D_S[d].f_eject=arg;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMVOLCTRL:   /* Volume control */
 		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
 		st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
 		D_S[d].vol_chan0=0;
 		D_S[d].vol_ctrl0=volctrl.channel0;
 		D_S[d].vol_chan1=1;
 		D_S[d].vol_ctrl1=volctrl.channel1;
 		i=cc_SetVolume();
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMVOLREAD:   /* read Volume settings from drive */
 		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		st=cc_GetVolume();
 		if (st<0) return (st);
 		volctrl.channel0=D_S[d].vol_ctrl0;
@@ -4110,15 +4187,16 @@
 		volctrl.channel2=0;
 		volctrl.channel2=0;
 		memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
-		return (0);
+		RETURN_UP(0);
 
 	case CDROMSUBCHNL:   /* Get subchannel info */
 		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
-		if ((st_spinning)||(!subq_valid)) { i=cc_ReadSubQ();
-						    if (i<0) return (-EIO);
-					    }
+		if ((st_spinning)||(!subq_valid)) {
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+		}
 		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st)	return (st);
+		if (st)	RETURN_UP(st);
 		memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
 		switch (D_S[d].audio_state)
 		{
@@ -4156,27 +4234,27 @@
 		    SC.cdsc_adr,SC.cdsc_ctrl,
 		    SC.cdsc_trk,SC.cdsc_ind,
 		    SC.cdsc_absaddr,SC.cdsc_reladdr);
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADMODE1:
 		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE);
 		cc_ModeSense();
 		D_S[d].mode=READ_M1;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADMODE2: /* not usable at the moment */
 		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE_RAW1);
 		cc_ModeSense();
 		D_S[d].mode=READ_M2;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
 		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
@@ -4193,11 +4271,11 @@
 			}
 			else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
 		}
-		return (D_S[d].sbp_audsiz);
+		RETURN_UP(D_S[d].sbp_audsiz);
 
 	case CDROMREADAUDIO:
 	{ /* start of CDROMREADAUDIO */
-		int i=0, j=0, frame, block;
+		int i=0, j=0, frame, block=0;
 		u_int try=0;
 		u_long timeout;
 		u_char *p;
@@ -4208,27 +4286,27 @@
 		int error_flag;
 		
 		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
-		if (fam0_drive) return (-EINVAL);
-		if (famL_drive) return (-EINVAL);
-		if (famV_drive) return (-EINVAL);
-		if (famT_drive) return (-EINVAL);
+		if (fam0_drive) RETURN_UP(-EINVAL);
+		if (famL_drive) RETURN_UP(-EINVAL);
+		if (famV_drive) RETURN_UP(-EINVAL);
+		if (famT_drive) RETURN_UP(-EINVAL);
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
-		if (D_S[d].aud_buf==NULL) return (-EINVAL);
+		if (D_S[d].aud_buf==NULL) RETURN_UP(-EINVAL);
 		i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (i) return (i);
+		if (i) RETURN_UP(i);
 		memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
+		if (read_audio.nframes>D_S[d].sbp_audsiz) RETURN_UP(-EINVAL);
 		i=verify_area(VERIFY_WRITE, read_audio.buf,
 			      read_audio.nframes*CD_FRAMESIZE_RAW);
-		if (i) return (i);
+		if (i) RETURN_UP(i);
 		
 		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
 			block=msf2lba(&read_audio.addr.msf.minute);
 		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
 			block=read_audio.addr.lba;
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 #if 000
 		i=cc_SetSpeed(speed_150,0,0);
 		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
@@ -4236,8 +4314,10 @@
 		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
 		    block, blk2msf(block));
 		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
+#if OLD_BUSY
 		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
 		busy_audio=1;
+#endif OLD_BUSY
 		error_flag=0;
 		for (data_tries=5; data_tries>0; data_tries--)
 		{
@@ -4400,43 +4480,45 @@
 		cc_ModeSelect(CD_FRAMESIZE);
 		cc_ModeSense();
 		D_S[d].mode=READ_M1;
+#if OLD_BUSY
 		busy_audio=0;
+#endif OLD_BUSY
 		if (data_tries == 0)
 		{
 			msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
-			return (-8);
+			RETURN_UP(-EIO);
 		}
 		msg(DBG_AUD,"read_audio: successful return.\n");
-		return (0);
+		RETURN_UP(0);
 	} /* end of CDROMREADAUDIO */
 		
 	case CDROMMULTISESSION: /* tell start-of-last-session */
 		msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
 		if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
 			lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute);
 		else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
 			ms_info.addr.lba=D_S[d].lba_multi;
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 		if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
 		else ms_info.xa_flag=0; /* invalid redirection address */
 		memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
 		msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n",
 		    ms_info.xa_flag, ms_info.addr.lba);
-		return (0);
+		RETURN_UP(0);
 		
 	case BLKRASET:
-		if(!suser())  return -EACCES;
-		if(!(inode->i_rdev)) return -EINVAL;
-		if(arg > 0xff) return -EINVAL;
+		if(!suser()) RETURN_UP(-EACCES);
+		if(!(inode->i_rdev)) RETURN_UP(-EINVAL);
+		if(arg > 0xff) RETURN_UP(-EINVAL);
 		read_ahead[MAJOR(inode->i_rdev)] = arg;
-		return (0);
+		RETURN_UP(0);
 		
 	default:
 		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
-		return (-EINVAL);
+		RETURN_UP(-EINVAL);
 	} /* end switch(cmd) */
 }
 /*==========================================================================*/
@@ -4489,6 +4571,7 @@
 static inline void sbpcd_end_request(struct request *req, int uptodate) {
 	req->next=CURRENT;
 	CURRENT=req;
+	up(&ioctl_read_sem);
 	end_request(uptodate);
 }
 /*==========================================================================*/
@@ -4528,6 +4611,7 @@
 	CURRENT=req->next;	/* task can fuck it up         GTL  */
 	sti();
 	
+	down(&ioctl_read_sem);
 	if (req->rq_status == RQ_INACTIVE)
 		sbpcd_end_request(req, 0);
 	if (req -> sector == -1)
@@ -4545,8 +4629,10 @@
 			kdevname(req->rq_dev));
 		goto err_done;
 	}
+#if OLD_BUSY
 	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
 	busy_data=1;
+#endif OLD_BUSY
 	
 	if (D_S[i].audio_state==audio_playing) goto err_done;
 	if (d!=i) switch_drive(i);
@@ -4612,7 +4698,9 @@
 	}
 	
  err_done:
+#if OLD_BUSY
 	busy_data=0;
+#endif OLD_BUSY
 #ifdef DEBUG_GTL
 	printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n",
 		xnr, req, req->sector, req->nr_sectors, jiffies);
@@ -4753,8 +4841,8 @@
 #if LONG_TIMING
 	max_latency=9*HZ;
 #else
-	if (D_S[d].f_multisession) max_latency=9*HZ;
-	else max_latency=3*HZ;
+	if (D_S[d].f_multisession) max_latency=15*HZ;
+	else max_latency=5*HZ;
 #endif
 	duration=jiffies;
 	for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
@@ -4855,9 +4943,16 @@
 		wait=8;
 		do
 		{
-			sbp_sleep(1);
-			OUT(CDo_sel_i_d,0);
-			i=inb(CDi_status);
+			if (teac==2)
+                          {
+                            if ((i=CDi_stat_loop_T()) == -1) break;
+                          }
+                        else
+                          {
+                            sbp_sleep(1);
+                            OUT(CDo_sel_i_d,0); 
+                            i=inb(CDi_status);
+                          } 
 			if (!(i&s_not_data_ready))
 			{
 				OUT(CDo_sel_i_d,1);
@@ -5018,22 +5113,26 @@
 	if (fp->f_mode & 2)
 		return -EROFS;
 	
+	down(&ioctl_read_sem);
 	switch_drive(i);
 	
 	i=cc_ReadError();
 	flags_cmd_out |= f_respo2;
-	cc_ReadStatus();                         /* command: give 1-byte status */
+	cc_ReadStatus(); /* command: give 1-byte status */
 	i=ResponseStatus();
 	if (famT_drive&&(i<0))
 	{
 		cc_DriveReset();
 		i=ResponseStatus();
+#if 0
+                sbp_sleep(HZ);
+#endif 0
 		i=ResponseStatus();
 	}
 	if (i<0)
 	{
 		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
-		return (-EIO);                  /* drive doesn't respond */
+		RETURN_UP(-EIO);                  /* drive doesn't respond */
 	}
 	if (famT_drive)	msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i);
 	if (!st_door_closed)
@@ -5065,7 +5164,7 @@
 			cc_SpinDown(); /* eject tray */
 		}
 #endif
-		return (-ENXIO);
+		RETURN_UP(-ENXIO);
 	}
 	/*
 	 * try to keep an "open" counter here and lock the door if 0->1.
@@ -5092,7 +5191,7 @@
 #endif SAFE_MIXED
 	}
 	if (!st_spinning) cc_SpinUp();
-	return (0);
+	RETURN_UP(0);
 }
 /*==========================================================================*/
 /*
@@ -5108,6 +5207,7 @@
 		msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
 		return;
 	}
+	down(&ioctl_read_sem);
 	switch_drive(i);
 	/*
 	 * try to keep an "open" counter here and unlock the door if 1->0.
@@ -5132,6 +5232,7 @@
 #endif SAFE_MIXED
 		}
 	}
+	up(&ioctl_read_sem);
 }
 /*==========================================================================*/
 /*
@@ -5365,7 +5466,10 @@
 	}
 	
 	if (port_index>0)
-		msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
+          {
+            msg(DBG_INF, "You should read linux/Documentation/cdrom/sbpcd\n");
+            msg(DBG_INF, "and then configure sbpcd.h for your hardware.\n");
+          }
 	check_datarate();
 	msg(DBG_INI,"check_datarate done.\n");
 	
@@ -5450,7 +5554,7 @@
 #endif MODULE
 	}
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
+	read_ahead[MAJOR_NR] = buffers * (CD_FRAMESIZE / 512);
 	
 	request_region(CDo_command,4,major_name);
 	
@@ -5466,7 +5570,7 @@
 		 */
 		D_S[j].aud_buf=NULL;
 		D_S[j].sbp_audsiz=0;
-		D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
+		D_S[j].sbp_bufsiz=buffers;
 		if (D_S[j].drv_type&drv_fam1)
 			if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
 		D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
@@ -5476,7 +5580,7 @@
 			return -EIO;
 		}
 #ifdef MODULE
-		msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
+		msg(DBG_INF,"data buffer size: %d frames.\n",buffers);
 #endif MODULE
 		if (D_S[j].sbp_audsiz>0)
 		{
diff -u --recursive --new-file v2.0.16/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.0.16/linux/drivers/char/console.c	Thu Aug 29 19:15:14 1996
+++ linux/drivers/char/console.c	Mon Sep  2 08:41:25 1996
@@ -1433,34 +1433,34 @@
 		  tc = translate[toggle_meta ? (c|0x80) : c];
 		}
 
-		/* If the original code was < 32 we only allow a
-		 * glyph to be displayed if the code is not normally
-		 * used (such as for cursor movement) or if the
-		 * disp_ctrl mode has been explicitly enabled.
-		 * Note: ESC is *never* allowed to be displayed as
-		 * that would disable all escape sequences!
-		 * To display font position 0x1B, go into UTF mode
-		 * and display character U+F01B, or change the mapping.
-		 */
-		ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
-					    : CTRL_ACTION) >> c) & 1))));
+                /* If the original code was a control character we
+                 * only allow a glyph to be displayed if the code is
+                 * not normally used (such as for cursor movement) or
+                 * if the disp_ctrl mode has been explicitly enabled.
+                 * Certain characters (as given by the CTRL_ALWAYS
+                 * bitmap) are always displayed as control characters,
+                 * as the console would be pretty useless without
+                 * them; to display an arbitrary font position use the
+                 * direct-to-font zone in UTF-8 mode.
+                 */
+                ok = tc && (c >= 32 ||
+                            (!utf && !(((disp_ctrl ? CTRL_ALWAYS
+                                         : CTRL_ACTION) >> c) & 1)))
+                        && (c != 127 || disp_ctrl);
 
 		if (vc_state == ESnormal && ok) {
 			/* Now try to find out how to display it */
 			tc = conv_uni_to_pc(tc);
-			if ( tc == -4 )
-			  {
-			    /* If we got -4 (not found) then see if we have
-			       defined a replacement character (U+FFFD) */
-			    tc = conv_uni_to_pc(0xfffd);
-			  }
-			else if ( tc == -3 )
-			  {
-			    /* Bad hash table -- hope for the best */
-			    tc = c;
-			  }
+			if ( tc == -4 ) {
+                                /* If we got -4 (not found) then see if we have
+                                   defined a replacement character (U+FFFD) */
+                                tc = conv_uni_to_pc(0xfffd);
+                        } else if ( tc == -3 ) {
+                                /* Bad hash table -- hope for the best */
+                                tc = c;
+                        }
 			if (tc & ~console_charmask)
-			  continue; /* Conversion failed */
+                                continue; /* Conversion failed */
 
 			if (need_wrap) {
 				cr(currcons);
diff -u --recursive --new-file v2.0.16/linux/drivers/char/ftape/fdc-io.c linux/drivers/char/ftape/fdc-io.c
--- v2.0.16/linux/drivers/char/ftape/fdc-io.c	Fri Apr 12 09:49:34 1996
+++ linux/drivers/char/ftape/fdc-io.c	Sun Sep  1 09:14:45 1996
@@ -311,7 +311,7 @@
 	int current_blocked = current->blocked;
 	static int resetting = 0;
 
-	if (wait_intr) {
+	if (waitqueue_active(&wait_intr)) {
 		TRACE(1, "error: nested call");
 		return -EIO;	/* return error... */
 	}
diff -u --recursive --new-file v2.0.16/linux/drivers/char/ftape/fdc-isr.c linux/drivers/char/ftape/fdc-isr.c
--- v2.0.16/linux/drivers/char/ftape/fdc-isr.c	Fri Apr 12 09:49:34 1996
+++ linux/drivers/char/ftape/fdc-isr.c	Sun Sep  1 09:14:45 1996
@@ -771,7 +771,7 @@
 			}
 			seek_completed = 1;
 			fdc_mode = fdc_idle;
-		} else if (!wait_intr) {
+		} else if (!waitqueue_active(&wait_intr)) {
 			if (expected_stray_interrupts == 0) {
 				TRACE(2, "unexpected stray interrupt");
 			} else {
diff -u --recursive --new-file v2.0.16/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.0.16/linux/drivers/char/n_tty.c	Wed Jul 24 10:49:53 1996
+++ linux/drivers/char/n_tty.c	Sun Sep  1 09:14:45 1996
@@ -795,7 +795,7 @@
 		  	current->timeout = (unsigned long) -1;
 			if (time)
 				tty->minimum_to_wake = 1;
-			else if (!tty->read_wait ||
+			else if (!waitqueue_active(&tty->read_wait) ||
 				 (tty->minimum_to_wake > minimum))
 				tty->minimum_to_wake = minimum;
 		} else {
@@ -907,7 +907,7 @@
 	}
 	remove_wait_queue(&tty->read_wait, &wait);
 
-	if (!tty->read_wait)
+	if (!waitqueue_active(&tty->read_wait))
 		tty->minimum_to_wake = minimum;
 
 	current->state = TASK_RUNNING;
@@ -991,7 +991,7 @@
 				return 1;
 			if (tty_hung_up_p(file))
 				return 1;
-			if (!tty->read_wait) {
+			if (!waitqueue_active(&tty->read_wait)) {
 				if (MIN_CHAR(tty) && !TIME_CHAR(tty))
 					tty->minimum_to_wake = MIN_CHAR(tty);
 				else
diff -u --recursive --new-file v2.0.16/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.0.16/linux/drivers/char/tty_io.c	Wed Jul 24 10:53:39 1996
+++ linux/drivers/char/tty_io.c	Sun Sep  1 09:14:45 1996
@@ -1349,7 +1349,7 @@
 		return retval;
 
 	if (on) {
-		if (!tty->read_wait)
+		if (!waitqueue_active(&tty->read_wait))
 			tty->minimum_to_wake = 1;
 		if (filp->f_owner == 0) {
 			if (tty->pgrp)
@@ -1358,7 +1358,7 @@
 				filp->f_owner = current->pid;
 		}
 	} else {
-		if (!tty->fasync && !tty->read_wait)
+		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
 			tty->minimum_to_wake = N_TTY_BUF_SIZE;
 	}
 	return 0;
diff -u --recursive --new-file v2.0.16/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.0.16/linux/include/asm-i386/irq.h	Thu Aug 29 19:15:14 1996
+++ linux/include/asm-i386/irq.h	Sun Sep  1 10:53:04 1996
@@ -90,7 +90,7 @@
 	"outb %al,$0x21\n\t" \
 	"jmp 1f\n" \
 	"1:\tjmp 1f\n" \
-	"1:\tmovb $0x60+"#nr",%al\n\t" \
+	"1:\tmovb $0xE0+"#nr",%al\n\t" \
 	"outb %al,$0x20\n\t"
 
 #define ACK_SECOND(mask,nr) \
@@ -102,11 +102,11 @@
 	"outb %al,$0xA1\n\t" \
 	"jmp 1f\n" \
 	"1:\tjmp 1f\n" \
-	"1:\tmovb $0x60+"#nr",%al\n\t" \
+	"1:\tmovb $0xE0+"#nr",%al\n\t" \
 	"outb %al,$0xA0\n\t" \
 	"jmp 1f\n" \
 	"1:\tjmp 1f\n" \
-	"1:\tmovb $0x62,%al\n\t" \
+	"1:\tmovb $0xE2,%al\n\t" \
 	"outb %al,$0x20\n\t"
 
 #define UNBLK_FIRST(mask) \
diff -u --recursive --new-file v2.0.16/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v2.0.16/linux/include/linux/sbpcd.h	Sat Aug 17 21:19:29 1996
+++ linux/include/linux/sbpcd.h	Sun Sep  1 09:14:25 1996
@@ -134,16 +134,38 @@
 /* runtime by use of the CDROMAUDIOBUFSIZ ioctl.    */
 #define READ_AUDIO 0
 
+/* Optimizations for the Teac CD-55A drive read performance.
+ * SBP_TEAC_SPEED can be changed here, or one can set the 
+ * variable "teac" when loading as a module.
+ * Valid settings are:
+ *   0 - very slow - the recommended "DISTRIBUTION 1" setup.
+ *   1 - 2x performance with little overhead. No busy waiting.
+ *   2 - 4x performance with 5ms overhead per read. Busy wait.
+ *
+ * Setting SBP_TEAC_SPEED or the variable 'teac' to anything
+ * other than 0 may cause problems. If you run into them, first
+ * change SBP_TEAC_SPEED back to 0 and see if your drive responds
+ * normally. If yes, you are "allowed" to report your case - to help
+ * me with the driver, not to solve your hassle. Donīt mail if you
+ * simply are stuck into your own "tuning" experiments, you know?
+ */
+#define SBP_TEAC_SPEED 1
+
 /*==========================================================================*/
 /*==========================================================================*/
 /*
- * nothing to change below here if you are not experimenting
+ * nothing to change below here if you are not fully aware what you're doing
  */
 #ifndef _LINUX_SBPCD_H
 
 #define _LINUX_SBPCD_H
 /*==========================================================================*/
 /*==========================================================================*/
+/*
+ * driver's own read_ahead, data mode
+ */
+#define SBP_BUFFER_FRAMES 8 
+
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #undef  FUTURE
 #undef SAFE_MIXED
@@ -151,9 +173,14 @@
 #define TEST_UPC 0
 #define SPEA_TEST 0
 #define TEST_STI 0
+#define OLD_BUSY 0
 #undef PATH_CHECK
 #ifndef SOUND_BASE
 #define SOUND_BASE 0
+#endif
+#if DISTRIBUTION
+#undef SBP_TEAC_SPEED
+#define SBP_TEAC_SPEED 0
 #endif
 /*==========================================================================*/
 /*
diff -u --recursive --new-file v2.0.16/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.0.16/linux/include/linux/sched.h	Sun Sep  1 09:15:33 1996
+++ linux/include/linux/sched.h	Mon Sep  2 08:55:09 1996
@@ -260,8 +260,8 @@
 #define PF_DUMPCORE	0x00000200	/* dumped core */
 #define PF_SIGNALED	0x00000400	/* killed by a signal */
 
-#define PF_STARTING	0x00000100	/* being created */
-#define PF_EXITING	0x00000200	/* getting shut down */
+#define PF_STARTING	0x00000002	/* being created */
+#define PF_EXITING	0x00000004	/* getting shut down */
 
 #define PF_USEDFPU	0x00100000	/* Process used the FPU this quantum (SMP only) */
 #define PF_DTRACE	0x00200000	/* delayed trace (used on m68k) */
diff -u --recursive --new-file v2.0.16/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.0.16/linux/include/net/tcp.h	Thu Jul 25 20:31:57 1996
+++ linux/include/net/tcp.h	Mon Sep  2 09:00:29 1996
@@ -161,6 +161,7 @@
 
 extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
 extern struct sk_buff * tcp_dequeue_partial(struct sock *);
+extern void tcp_shrink_skb(struct sock *,struct sk_buff *,u32);
 
 /* tcp_input.c */
 extern void tcp_cache_zap(void);
diff -u --recursive --new-file v2.0.16/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.0.16/linux/net/ipv4/tcp_input.c	Mon Aug  5 10:13:56 1996
+++ linux/net/ipv4/tcp_input.c	Mon Sep  2 08:41:24 1996
@@ -978,10 +978,11 @@
 	/*
 	 * Maybe we can take some stuff off of the write queue,
 	 * and put it onto the xmit queue.
-	 * FIXME: (?) There is bizarre case being tested here, to check if
+	 * There is bizarre case being tested here, to check if
 	 * the data at the head of the queue ends before the start of
-	 * the sequence we already ACKed. This does not appear to be
-	 * a case that can actually occur. Why are we testing it?
+	 * the sequence we already ACKed. This is not an error,
+	 * it can occur when we send a packet directly off of the write_queue
+	 * in a zero window probe.
 	 */
 
 	if (!skb_queue_empty(&sk->write_queue) &&
diff -u --recursive --new-file v2.0.16/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.0.16/linux/net/ipv4/tcp_output.c	Sun Sep  1 09:15:34 1996
+++ linux/net/ipv4/tcp_output.c	Mon Sep  2 08:41:24 1996
@@ -325,35 +325,38 @@
 		skb_unlink(skb);
 		
 		/*
-		 *	See if we really need to send the packet. 
+		 *	See if we really need to send the whole packet. 
 		 */
 		 
-		if (before(skb->end_seq, sk->rcv_ack_seq +1)) 
-		{
+		if (before(skb->end_seq, sk->rcv_ack_seq +1)) {
 			/*
-			 *	This is acked data. We can discard it. This 
-			 *	cannot currently occur.
+			 *	This is acked data. We can discard it.
+			 *	This implies the packet was sent out
+			 *	of the write queue by a zero window probe.
 			 */
 			 
 			sk->retransmits = 0;
 			kfree_skb(skb, FREE_WRITE);
 			if (!sk->dead) 
 				sk->write_space(sk);
-		} 
-		else
-		{
+		} else {
 			struct tcphdr *th;
 			struct iphdr *iph;
 			int size;
-/*
- * put in the ack seq and window at this point rather than earlier,
- * in order to keep them monotonic.  We really want to avoid taking
- * back window allocations.  That's legal, but RFC1122 says it's frowned on.
- * Ack and window will in general have changed since this packet was put
- * on the write queue.
- */
+
 			iph = skb->ip_hdr;
 			th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
+
+                        /* See if we need to shrink the leading packet on
+                         * the retransmit queue. Strictly speaking, we
+                         * should never need to do this, but some buggy TCP
+                         * implementations get confused if you send them
+                         * a packet that contains both old and new data. (Feh!)
+                         * Soooo, we have this uglyness here.
+                         */
+			if (after(sk->rcv_ack_seq,skb->seq+th->syn+th->fin))
+				tcp_shrink_skb(sk,skb,sk->rcv_ack_seq);
+
 			size = skb->len - (((unsigned char *) th) - skb->data);
 #ifndef CONFIG_NO_PATH_MTU_DISCOVERY
 			if (size > sk->mtu - sizeof(struct iphdr))
@@ -363,6 +366,13 @@
 			}
 #endif
 			
+/*
+ * put in the ack seq and window at this point rather than earlier,
+ * in order to keep them monotonic.  We really want to avoid taking
+ * back window allocations.  That's legal, but RFC1122 says it's frowned on.
+ * Ack and window will in general have changed since this packet was put
+ * on the write queue.
+ */
 			th->ack_seq = htonl(sk->acked_seq);
 			th->window = htons(tcp_select_window(sk));
 
@@ -1189,4 +1199,57 @@
 	sk->retransmits++;
 	sk->prot->retransmits ++;
 	tcp_reset_xmit_timer (sk, TIME_PROBE0, sk->rto);
+}
+
+/*
+ * Remove the portion of a packet that has already been sent.
+ * Needed to deal with buggy TCP implementations that can't deal
+ * with seeing a packet that contains some data that has already
+ * been received.
+ */
+void tcp_shrink_skb(struct sock *sk, struct sk_buff *skb, u32 ack)
+{
+	struct iphdr *iph;
+	struct tcphdr *th;
+	unsigned char *old, *new;
+	unsigned long len;
+	int diff;
+
+	/*
+	 *	Recover the buffer pointers
+	 */
+	 
+	iph = (struct iphdr *)skb->ip_hdr;
+	th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
+
+	/* how much data are we droping from the tcp frame */
+	diff = ack - skb->seq;
+	/* how much data are we keeping in the tcp frame */
+	len = (skb->end_seq - (th->fin + th->syn)) - ack;
+
+	/* pointers to new start of remaining data, and old start */
+	new = (unsigned char *)th + th->doff*4;
+	old = new+diff;
+
+	/* Update our starting seq number */
+	skb->seq = ack;
+	th->seq = htonl(ack);
+	iph->tot_len = htons(ntohs(iph->tot_len)-diff);
+
+	/* Get the partial checksum for the IP options */
+	if (th->doff*4 - sizeof(*th) > 0)
+		skb->csum = csum_partial((void *)(th+1),
+				th->doff*4-sizeof(*th),0);
+	else
+		skb->csum = 0;
+
+	/* Copy the good data down and get it's checksum */
+	skb->csum = csum_partial_copy((void *)old,(void *)new,len,skb->csum);
+
+	/* shorten the skb */
+	skb_trim(skb,skb->len-diff);
+	 
+	/* Checksum the shrunk buffer */
+	tcp_send_check(th, sk->saddr, sk->daddr, 
+		   th->doff * 4 + len , skb);
 }
