diff -u --recursive --new-file v1.1.40/linux/CREDITS linux/CREDITS
--- v1.1.40/linux/CREDITS	Mon Jul 25 17:56:37 1994
+++ linux/CREDITS	Sun Aug  7 00:31:14 1994
@@ -419,11 +419,11 @@
 S: Finland
 
 N: David C. Niemi
-E: David.Niemi@oasis.gtegsc.com
+E: niemidc@slma.com
 D: FSSTND, The XFree86 Project
-D: DMA memory support and future floppy driver
+D: DMA memory support, floppy driver
 S: 2364 Old Trail Drive
-S: Reston, VA 22091
+S: Reston, Virginia 22091
 S: USA
 
 N: Kai Petzke
diff -u --recursive --new-file v1.1.40/linux/Makefile linux/Makefile
--- v1.1.40/linux/Makefile	Sun Aug  7 03:30:37 1994
+++ linux/Makefile	Sun Aug  7 00:29:52 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 40
+SUBLEVEL = 41
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.40/linux/boot/head.S linux/boot/head.S
--- v1.1.40/linux/boot/head.S	Mon Jul 18 14:48:28 1994
+++ linux/boot/head.S	Sun Aug  7 00:46:56 1994
@@ -14,10 +14,12 @@
 .globl _empty_bad_page
 .globl _empty_bad_page_table
 .globl _empty_zero_page
-.globl _tmp_floppy_area,_floppy_track_buffer
+.globl _floppy_track_buffer
 
 #include <linux/tasks.h>
 #include <linux/segment.h>
+#define ASSEMBLER
+#include <linux/fd.h>
 
 #define CL_MAGIC_ADDR	0x90020
 #define CL_MAGIC	0xA33F
@@ -276,20 +278,13 @@
 
 .org 0x6000
 /*
- * tmp_floppy_area is used by the floppy-driver when DMA cannot
- * reach to a buffer-block. It needs to be aligned, so that it isn't
- * on a 64kB border.
- */
-_tmp_floppy_area:
-	.fill 1024,1,0
-/*
  * floppy_track_buffer is used to buffer one track of floppy data: it
  * has to be separate from the tmp_floppy area, as otherwise a single-
  * sector read/write can mess it up. It can contain one full cylinder (sic) of
  * data (36*2*512 bytes).
  */
 _floppy_track_buffer:
-	.fill 512*2*36,1,0
+	.fill 512*2*MAX_BUFFER_SECTORS,1,0
 
 /* This is the default interrupt "handler" :-) */
 int_msg:
diff -u --recursive --new-file v1.1.40/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.1.40/linux/drivers/block/blk.h	Sat Aug  6 21:32:05 1994
+++ linux/drivers/block/blk.h	Sun Aug  7 00:46:56 1994
@@ -124,7 +124,7 @@
 #define DEVICE_NAME "floppy"
 #define DEVICE_INTR do_floppy
 #define DEVICE_REQUEST do_fd_request
-#define DEVICE_NR(device) ((device) & 3)
+#define DEVICE_NR(device) ( ((device) & 3) | (((device) & 0x80 ) >> 5 ))
 #define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
 #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
 
diff -u --recursive --new-file v1.1.40/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.1.40/linux/drivers/block/floppy.c	Wed Aug  3 09:32:23 1994
+++ linux/drivers/block/floppy.c	Sun Aug  7 00:46:57 1994
@@ -2,8 +2,25 @@
  *  linux/kernel/floppy.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1993, 1994  Alain Knaff
  */
 
+/* Configuration */
+/* The following does some extra sanity checks */
+#define SANITY
+
+/* Undefine the following if you have to floppy disk controllers:
+ * This is untested. If you get two controllers working, with drives attached
+ * too both, please mail me: Alain.Knaff@imag.fr */
+/* #define HAVE_2_CONTROLLERS */
+
+/* Undefine the following if you have problems accessing ED disks, but don't
+ * have problems accessing them with the stock driver. If that is the case,
+ * please mail me: Alain.Knaff@imag.fr */
+/* #define FDC_FIFO_BUG */
+
+/* End of configuration */
+
 /*
  * 02.12.91 - Changed to static variables to indicate need for reset
  * and recalibrate. This makes some things easier (output_byte reset
@@ -16,7 +33,7 @@
  * but I don't like programming floppies, and I have only one anyway.
  * Urgel. I should check for more errors, and do more graceful error
  * recovery. Seems there are problems with several drives. I've tried to
- * correct them. No promises. 
+ * correct them. No promises.
  */
 
 /*
@@ -39,7 +56,7 @@
  */
 
 /*
- * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed 
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
  * FDC data overrun bug, added some preliminary stuff for vertical
  * recording support.
  *
@@ -50,7 +67,7 @@
 
 /* 1992/9/20
  * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
- * modelled after the freeware MS/DOS program fdformat/88 V1.8 by 
+ * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
  * Christoph H. Hochst\"atter.
  * I have fixed the shift values to the ones I always use. Maybe a new
  * ioctl() should be created to be able to modify them.
@@ -65,7 +82,7 @@
  */
 
 /* 1994/6/24 --bbroad-- added the floppy table entries and made
- * minor modifications to allow 2.88 floppies to be run. 
+ * minor modifications to allow 2.88 floppies to be run.
  */
 
 /* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
@@ -76,17 +93,21 @@
 #define REALLY_SLOW_IO
 #define FLOPPY_IRQ 6
 #define FLOPPY_DMA 2
-#define FDC_FIFO_UNTESTED           /* -bb */
 
-#define FDC_FIFO_BUG
+#define DEBUGT 2
 
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
+#define FDPATCHES
 #include <linux/fdreg.h>
 #include <linux/fd.h>
 #include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
 
 #include <asm/dma.h>
 #include <asm/irq.h>
@@ -97,39 +118,57 @@
 #define MAJOR_NR FLOPPY_MAJOR
 #include "blk.h"
 
-static unsigned int changed_floppies = 0, fake_change = 0, read_only = 0;
-
-static int initial_reset_flag = 0;
-static int need_configure = 1;		/* for 82077 */
-static int recalibrate = 0;
-static int reset = 0;
-static int recover = 0; /* recalibrate immediately after resetting */
-static int seek = 0;
+static unsigned int changed_floppies = 0, fake_change = 0;
+static int initialising=1;
 
-static unsigned char current_DOR = 0x0C;
-static unsigned char running = 0;
 
-#define TYPE(x) ((x)>>2)
-#define DRIVE(x) ((x)&0x03)
+#ifdef HAVE_2_CONTROLLERS
+#define N_FDC 2
+#define N_DRIVE 8
+#else
+#define N_FDC 1
+#define N_DRIVE 4
+#endif
 
-/*
- * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
- * max X times - some types of errors increase the errorcount by 2 or
- * even 3, so we might actually retry only X/2 times before giving up.
- */
-#define MAX_ERRORS 12
+#define TYPE(x) ( ((x)>>2) & 0x1f )
+#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5))
+#define UNIT(x) ( (x) & 0x03 )		/* drive on fdc */
+#define FDC(x) ( ((x) & 0x04) >> 2 )  /* fdc of drive */
+#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 ))
+				/* reverse mapping from unit and fdc to drive */
+#define DP (&drive_params[current_drive])
+#define DRS (&drive_state[current_drive])
+#define FDCS (&fdc_state[fdc])
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+
+/* read/write */
+#define COMMAND raw_cmd.cmd[0]
+#define DR_SELECT raw_cmd.cmd[1]
+#define TRACK raw_cmd.cmd[2]
+#define HEAD raw_cmd.cmd[3]
+#define SECTOR raw_cmd.cmd[4]
+#define SIZECODE raw_cmd.cmd[5]
+#define SECT_PER_TRACK raw_cmd.cmd[6]
+#define GAP raw_cmd.cmd[7]
+#define SIZECODE2 raw_cmd.cmd[8]
+#define NR_RW 9
+
+/* format */
+#define F_SIZECODE raw_cmd.cmd[2]
+#define F_SECT_PER_TRACK raw_cmd.cmd[3]
+#define F_GAP raw_cmd.cmd[4]
+#define F_FILL raw_cmd.cmd[5]
+#define NR_F 6
 
 /*
  * Maximum disk size (in kilobytes). This default is used whenever the
  * current disk size is unknown.
  */
-#define MAX_DISK_SIZE 2880 /* was 1440 -bb */
+#define MAX_DISK_SIZE 3984
 
-/*
- * Maximum number of sectors in a track buffer. Track buffering is disabled
- * if tracks are bigger.
- */
-#define MAX_BUFFER_SECTORS 36 /* was 18 -bb */
 
 
 /*
@@ -140,18 +179,76 @@
  * driver otherwise. It doesn't matter much for performance anyway, as most
  * floppy accesses go through the track buffer.
  */
-#define LAST_DMA_ADDR	(0x100000 - BLOCK_SIZE)
+#define LAST_DMA_ADDR	(0x1000000)
+#define K_64 (0x10000) /* 64 k */
 
 /*
  * globals used by 'result()'
  */
-#define MAX_REPLIES 7
+#define MAX_REPLIES 10
 static unsigned char reply_buffer[MAX_REPLIES];
+static int inr; /* size of reply buffer, when called from interrupt */
 #define ST0 (reply_buffer[0])
 #define ST1 (reply_buffer[1])
 #define ST2 (reply_buffer[2])
 #define ST3 (reply_buffer[0]) /* result of GETSTATUS */
+#define R_TRACK (reply_buffer[3])
+#define R_HEAD (reply_buffer[4])
+#define R_SECTOR (reply_buffer[5])
+#define R_SIZECODE (reply_buffer[6])
+
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+	struct floppy_drive_params params;
+	char *name; /* name printed while booting */
+} default_drive_params[]= {
+/* NOTE: the time values in jiffies should be in msec!
+ CMOS drive type
+  |     Maximum data rate supported by drive type
+  |     |   Head load time, msec
+  |     |   |   Head unload time, msec (not used)
+  |     |   |   |     Step rate interval, usec
+  |     |   |   |     |    Time needed for spinup time (jiffies)
+  |     |   |   |     |    |    Timeout for spinning down (jiffies)
+  |     |   |   |     |    |    |   Spindown offset (where disk stops)
+  |     |   |   |     |    |    |   |  Select delay
+  |     |   |   |     |    |    |   |  |  RPS
+  |     |   |   |     |    |    |   |  |  |    Max number of tracks
+  |     |   |   |     |    |    |   |  |  |    |     Interrupt timeout
+  |     |   |   |     |    |    |   |  |  |    |     |   Max nonintlv. sectors
+  |     |   |   |     |    |    |   |  |  |    |     |   | -Max Errors- flags */
+{{0,  500, 16, 16, 8000, 100, 300,  0, 2, 5,  80, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
+
+{{1,  300, 16, 16, 8000, 100, 300,  0, 2, 5,  40, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2,  500, 16, 16, 6000,  40, 300, 14, 2, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 2, 5,18,31,11,11, 0, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3,  250, 16, 16, 3000, 100, 300,  0, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 4,14,13,10, 0, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
+
+{{4,  500, 16, 16, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4,24,14,20,13,23,21}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
+
+{{5, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,24,27,14,20,13}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,24,27,14,20,13}, 150, 8 }, "2.88M" } /*3 1/2 ED*/
+/*    |  ---autodetected formats--   |   |      |
+      read_track                     |   |    Name printed when booting
+                                     |  Native format
+                                   Frequency of disk change checks */
+};
 
+static struct floppy_drive_params drive_params[N_DRIVE];
+static struct floppy_drive_struct volatile drive_state[N_DRIVE];
+static struct floppy_raw_cmd raw_cmd;
+
 /*
  * This struct defines the different floppy types.
  *
@@ -159,96 +256,61 @@
  * types (ie 360kB diskette in 1.2MB drive etc). Others should
  * be self-explanatory.
  */
-static struct floppy_struct floppy_type[] = {
-	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL },	/* no testing */
-	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,NULL },	/* 360kB PC diskettes */
-	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,NULL },	/* 1.2 MB AT-diskettes */
-	{  720, 9,2,40,1,0x2A,0x02,0xDF,0x50,NULL },	/* 360kB in 720kB drive */
-	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,NULL },	/* 3.5" 720kB diskette */
-	{  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 */
+static struct floppy_struct floppy_type[32] = {
+	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL    },	/*  0 no testing    */
+	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360"  }, /*  1 360kB PC/QD   */
+	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" },	/*  2 *1.2 MB AT    */
+	{  720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360"  },	/*  3 360kB SS 3.5" */
+	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720"  },	/*  4 720KB 3.5"    */
+	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360"  },	/*  5 *360kB AT     */
+	{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720"  },	/*  6 720kB AT      */
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" },	/*  7 *1.44MB 3.5"  */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" },	/*  8 *2.88MB 3.5"  */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"},	/*  9 2.88MB 3.5"   */
+
+	{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 *1.44MB 5.25" */
+	{ 3360,21,2,80,0,0x1c,0x00,0xcf,0x6c,"H1680" }, /* 11 *1.68MB 3.5"  */
+	{  820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410"  },	/* 12 410KB 5.25"   */
+	{ 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820"  },	/* 13 820KB 3.5"    */
+	{ 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" },	/* 14 1.48MB 5.25"  */
+	{ 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" },	/* 15 1.72MB 3.5"   */
+	{  840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420"  },	/* 16 420KB 5.25"   */
+	{ 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830"  },	/* 17 830KB 3.5"    */
+	{ 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" },	/* 18 1.49MB 5.25"  */
+	{ 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5"  */
+
+	{ 1760,11,2,80,0,0x1c,0x09,0xcf,0x6c,"d880"  }, /* 20 880k 5.25"    */
+	{ 2080,13,2,80,0,0x1c,0x0a,0xcf,0x6c,"D1040" }, /* 21 1.04MB 3.5"   */
+	{ 2240,14,2,80,0,0x1c,0x1a,0xcf,0x6c,"D1120" }, /* 22 *1.12MB 3.5"  */
+	{ 3200,20,2,80,0,0x1c,0x20,0xcf,0x6c,"h1600" }, /* 23 1.6MB 5.25"   */
+	{ 3520,22,2,80,0,0x1c,0x08,0xcf,0x6c,"H1760" }, /* 24 1.76MB 3.5"   */
+	{ 3840,24,2,80,0,0x1c,0x18,0xcf,0x6c,"H1920" }, /* 25 *1.92MB 3.5"  */
+	{ 6400,40,2,80,0,0x25,0x5b,0xcf,0x6c,"E3200" }, /* 26 3.20MB 3.5"   */
+	{ 7040,44,2,80,0,0x25,0x5b,0xcf,0x6c,"E3520" }, /* 27 3.52MB 3.5"   */
+	{ 7680,48,2,80,0,0x25,0x63,0xcf,0x6c,"E3840" }, /* 28 *3.84MB 3.5"  */
+
+	{ 3680,23,2,80,0,0x1c,0x10,0xcf,0x6c,"H1840" }, /* 29 1.84MB 3.5"   */
+	{ 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"H800"  },	/* 30 *800k 3.5"    */
+	{ 3200,20,2,80,0,0x1c,0x00,0xcf,0x6c,"H1600" }, /* 31 *1.6MB 3.5"   */
 };
 
-/*
- * Auto-detection. Each drive type has a number of formats which are
- * used in succession to try to read the disk. If the FDC cannot lock onto
- * the disk, the next format is tried. This uses the variable 'probing'.
- */
 #define	NUMBER(x)	(sizeof(x) / sizeof(*(x)))
-
-static struct floppy_struct floppy_type_1[] = {
-	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */
-};
-
-static struct floppy_struct floppy_type_2[] = {
-	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M" },	  /* 1.2 MB AT-diskettes */
-	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,"360k/AT" }, /* 360kB in 1.2MB drive */
-};
-
-static struct floppy_struct floppy_type_3[] = {
-	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" },	  /* 3.5" 720kB diskette */
-};
-
-static struct floppy_struct floppy_type_4[] = {
-	{ 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 */
-};
+#define SECTSIZE ( _FD_SECTSIZE(*floppy))
 
-static struct floppy_struct floppy_type_5[] = {
-	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x50,"2.88M" },	  /* 2.88MB 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 */
-};
-
-#define	floppy_type_6	floppy_type_5		/* 5 and 6 are the same */
-
-
-static struct floppy_struct *floppy_types[] = {
-	floppy_type_1,
-	floppy_type_2,
-	floppy_type_3,
-	floppy_type_4,
-	floppy_type_5,
-	floppy_type_6,
-};
-
-static int floppy_num_types[] = {
-	NUMBER(floppy_type_1),
-	NUMBER(floppy_type_2),
-	NUMBER(floppy_type_3),
-	NUMBER(floppy_type_4),
-	NUMBER(floppy_type_5),
-	NUMBER(floppy_type_6),
-};
-
 /* Auto-detection: Disk type used until the next media change occurs. */
-struct floppy_struct *current_type[4] = { NULL, NULL, NULL, NULL };
-
-/* This type is tried first. */
-struct floppy_struct *base_type[4] = { NULL, NULL, NULL, NULL };
-
-/* This gives the end of the list */
-struct floppy_struct *base_type_end[4];
+struct floppy_struct *current_type[N_DRIVE] = { NULL, NULL, NULL, NULL
+#ifdef HAVE_2_CONTROLLERS
+				          ,NULL, NULL, NULL, NULL
+#endif
+					};
 
 /*
  * User-provided type information. current_type points to
  * the respective entry of this array.
  */
-struct floppy_struct user_params[4];
+struct floppy_struct user_params[N_DRIVE];
 
-static int floppy_sizes[] ={
-	MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE,
-	 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,
-	2880,2880,2880,2880           /* -bb */
-};
+static int floppy_sizes[256];
 
 /*
  * The driver is trying to determine the correct media format
@@ -257,28 +319,14 @@
  */
 static int probing = 0;
 
-/*
- * (User-provided) media information is _not_ discarded after a media change
- * if the corresponding keep_data flag is non-zero. Positive values are
- * decremented after each probe.
- */
-static int keep_data[4] = { 0,0,0,0 };
-
-/*
- * Announce successful media type detection and media information loss after
- * disk changes.
- * Also used to enable/disable printing of overrun warnings.
- */
-static ftd_msg[4] = { 0,0,0,0 };
-
-/* Prevent "aliased" accesses. */
-
-static fd_ref[4] = { 0,0,0,0 };
-static fd_device[4] = { 0,0,0,0 };
-
 /* Synchronization of FDC access. */
-static volatile int format_status = FORMAT_NONE, fdc_busy = 0;
-static struct wait_queue *fdc_wait = NULL, *format_done = NULL;
+#define FD_COMMAND_DETECT -2
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
 
 /* Errors during formatting are counted here. */
 static int format_errors;
@@ -287,477 +335,649 @@
 static struct format_descr format_req;
 
 /*
- * Current device number. Taken either from the block header or from the
- * format request descriptor.
- */
-#define CURRENT_DEVICE (format_status == FORMAT_BUSY ? format_req.device : \
-   (CURRENT->dev))
-
-/* Current error count. */
-#define CURRENT_ERRORS (format_status == FORMAT_BUSY ? format_errors : \
-    (CURRENT->errors))
-
-/*
- * Threshold for reporting FDC errors to the console.
- * Setting this to zero may flood your screen when using
- * ultra cheap floppies ;-)
- */
-static unsigned short min_report_error_cnt[4] = {2, 2, 2, 2};
-
-/*
  * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
  * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
  * H is head unload time (1=16ms, 2=32ms, etc)
- *
- * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
- * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
  */
 
 /*
- * Track buffer and block buffer (in case track buffering doesn't work).
+ * Track buffer
  * Because these are written to by the DMA controller, they must
  * not contain a 64k byte boundary crossing, or data will be
  * corrupted/lost. Alignment of these is enforced in boot/head.S.
  * Note that you must not change the sizes below without updating head.S.
  */
-extern char tmp_floppy_area[BLOCK_SIZE];
 extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
 
-static void redo_fd_request(void);
+int *errors;
+typedef void (*done_f)(int);
+struct cont_t {
+void (*interrupt)(void); /* this is called after the interrupt of the
+			  * main command */
+void (*redo)(void); /* this is called to retry the operation */
+void (*error)(void); /* this is called to tally an error */
+done_f done; /* this is called to say if the operation has succeeded/failed */
+} *cont;
+
 static void floppy_ready(void);
 static void recalibrate_floppy(void);
+static void seek_floppy(void);
+static void floppy_shutdown(void);
 
 int floppy_grab_irq_and_dma(void);
 void floppy_release_irq_and_dma(void);
 
 /*
+ * The "reset" variable should be tested whenever an interrupt is scheduled,
+ * after the commands have been sent. This is to ensure that the driver doesn't
+ * get wedged when the interrupt doesn't come because of a failed command.
+ * reset doesn't need to be tested before sending commands, because
+ * output_byte is automatically disabled when reset is set.
+ */
+#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
+static void reset_fdc(void);
+
+/*
  * These are global variables, as that's the easiest way to give
  * information to interrupts. They are the data used for the current
  * request.
  */
-#define NO_TRACK 255
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
 
-static int read_track = 0;	/* flag to indicate if we want to read entire track */
+/* buffer related variables */
 static int buffer_track = -1;
 static int buffer_drive = -1;
-static int cur_spec1 = -1;
-static int cur_rate = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+#ifdef FDC_FIFO_BUG
+static int force=0;
+#endif
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+int fdc; /* current fdc */
+
 static struct floppy_struct * floppy = floppy_type;
 static unsigned char current_drive = 255;
-static unsigned char sector = 0;
-static unsigned char head = 0;
-static unsigned char track = 0;
-static unsigned char seek_track = 0;
-static unsigned char current_track = NO_TRACK;
-static unsigned char command = 0;
-static unsigned char fdc_version = 0x90;	/* FDC version code */
+static long current_count_sectors = 0;
+static char *current_addr = 0;
+static unsigned char sector_t; /* sector in track */
 
-static void select_callback(unsigned long unused)
+#ifdef DEBUGT
+long unsigned debugtimer;
+#endif
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
 {
-	floppy_ready();
+#ifdef DEBUGT
+	debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(char *message)
+{
+#ifdef DEBUGT
+  if ( DP->flags & DEBUGT )
+	printk("%s dtime=%lu\n", message, jiffies-debugtimer );
+#endif
 }
 
-static void floppy_select(unsigned int nr)
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+static int set_dor(int fdc, char mask, char data)
 {
-	static struct timer_list select = { NULL, NULL, 0, 0, select_callback };
+	register unsigned char drive, unit, newdor,olddor;
 
-	if (current_drive == (current_DOR & 3)) {
-		floppy_ready();
-		return;
+	cli();
+	olddor = FDCS->dor;
+	newdor =  (olddor & mask) | data;
+	if ( newdor != olddor ){
+		unit = olddor & 0x3;
+		drive = REVDRIVE(fdc,unit);
+		if ( olddor & ( 0x10 << unit )){
+			if ( inb_p( FD_DIR ) & 0x80 )
+				UDRS->flags |= FD_VERIFY;
+			else
+				UDRS->last_checked=jiffies;
+		}
+		FDCS->dor = newdor;
+		outb_p( newdor, FD_DOR);
 	}
-	seek = 1;
-	current_track = NO_TRACK;
-	current_DOR &= 0xFC;
-	current_DOR |= current_drive;
-	outb(current_DOR,FD_DOR);
-	del_timer(&select);
-	select.expires = 2;
-	add_timer(&select);
-}
-
-static void motor_on_callback(unsigned long nr)
-{
-	running |= 0x10 << nr;
-	floppy_select(nr);
-}
-
-static struct timer_list motor_on_timer[4] = {
-	{ NULL, NULL, 0, 0, motor_on_callback },
-	{ NULL, NULL, 0, 1, motor_on_callback },
-	{ NULL, NULL, 0, 2, motor_on_callback },
-	{ NULL, NULL, 0, 3, motor_on_callback }
-};
+	sti();
+	return olddor;
+}
 
-static void motor_off_callback(unsigned long nr)
+static void twaddle(void)
+{
+	cli();
+	outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+	outb_p(FDCS->dor, FD_DOR);
+	sti();
+}
+
+/* reset all driver information about the current fdc. This is needed after
+ * a reset, and after a raw command. */
+static void reset_fdc_info(int mode)
+{
+	int drive;
+
+	FDCS->spec1 = FDCS->spec2 = -1;
+	FDCS->need_configure = 1;
+	FDCS->perp_mode = 1;
+	FDCS->rawcmd = 0;
+	for ( drive = 0; drive < N_DRIVE; drive++)
+		if (FDC(drive) == fdc &&
+		    ( mode || UDRS->track != NEED_1_RECAL))
+			UDRS->track = NEED_2_RECAL;
+}
+
+/* selects the fdc and drive, and enables the fdc's its input/dma. */
+static void set_fdc(int drive)
+{
+	if ( drive >= 0 ){
+		fdc = FDC(drive);
+		current_drive = drive;
+	}
+	set_dor(fdc,~0,8);
+#ifdef HAVE_2_CONTROLLERS
+	set_dor(1-fdc, ~8, 0);
+#endif
+	if ( FDCS->rawcmd == 2 )
+		reset_fdc_info(1);
+	if( inb_p(FD_STATUS) != STATUS_READY )
+		FDCS->reset = 1;
+}
+
+/* locks the driver */
+static void lock_fdc(int drive)
 {
-	unsigned char mask = ~(0x10 << nr);
 	cli();
-	running &= mask;
-	current_DOR &= mask;
-	outb(current_DOR,FD_DOR);
+	while (fdc_busy) sleep_on(&fdc_wait);
+	fdc_busy = 1;
 	sti();
+	command_status = FD_COMMAND_NONE;
+	set_fdc(drive);
+	if ( drive >= 0 ){
+		timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+		timer_active |= 1 << FLOPPY_TIMER;
+	}
+}
+
+/* unlocks the driver */
+static inline int unlock_fdc(void)
+{
+	if (current_drive < N_DRIVE)
+		floppy_off(current_drive);
+	if (!fdc_busy)
+		printk(DEVICE_NAME ": FDC access conflict!\n");
+
+	if ( DEVICE_INTR )
+		printk(DEVICE_NAME
+		       ":device interrupt still active at FDC release: %p!\n",
+		       DEVICE_INTR);
+	command_status = FD_COMMAND_NONE;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+	fdc_busy = 0;
+	wake_up(&fdc_wait);
+	return 0;
+}
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long nr)
+{
+	unsigned char mask = ~(0x10 << UNIT(nr));
+
+	set_dor( FDC(nr), mask, 0 );
 }
 
-static struct timer_list motor_off_timer[4] = {
+static struct timer_list motor_off_timer[N_DRIVE] = {
 	{ NULL, NULL, 0, 0, motor_off_callback },
 	{ NULL, NULL, 0, 1, motor_off_callback },
 	{ NULL, NULL, 0, 2, motor_off_callback },
 	{ NULL, NULL, 0, 3, motor_off_callback }
+#ifdef HAVE_2_CONTROLLERS
+	,{ NULL, NULL, 0, 4, motor_off_callback },
+	{ NULL, NULL, 0, 5, motor_off_callback },
+	{ NULL, NULL, 0, 6, motor_off_callback },
+	{ NULL, NULL, 0, 7, motor_off_callback }
+#endif
 };
 
-static void floppy_on(unsigned int nr)
-{
-	unsigned char mask = 0x10 << nr;
-
-	del_timer(motor_off_timer + nr);
-	if (mask & running)
-		floppy_select(nr);
-	if (!(mask & current_DOR)) {
-		del_timer(motor_on_timer + nr);
-		motor_on_timer[nr].expires = HZ;
-		add_timer(motor_on_timer + nr);
-	}
-	current_DOR &= 0xFC;
-	current_DOR |= mask;
-	current_DOR |= nr;
-	outb(current_DOR,FD_DOR);
-}
-
+/* schedules motor off */
 static void floppy_off(unsigned int nr)
 {
+	unsigned long volatile delta;
+
+	cli();
 	del_timer(motor_off_timer+nr);
-	motor_off_timer[nr].expires = 3*HZ;
+
+	/* make spindle stop in a position which minimizes spinup time
+	 * next time */
+	if ( drive_params[nr].rps ){
+		delta = jiffies - drive_state[nr].first_read_date + HZ -
+			drive_params[nr].spindown_offset;
+		delta = (( delta * drive_params[nr].rps) % HZ ) /
+			drive_params[nr].rps;
+		motor_off_timer[nr].expires = drive_params[nr].spindown - delta;
+	}
 	add_timer(motor_off_timer+nr);
+	sti();
 }
 
-void request_done(int uptodate)
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+	int i, drive, saved_drive;
+
+	saved_drive = current_drive % N_DRIVE;
+	for(i=0; i< N_DRIVE; i++){
+		drive = (saved_drive + i + 1 ) % N_DRIVE;
+		if ( UDRS->fd_ref == 0 )
+			continue; /* skip closed drives */
+		set_fdc(drive);
+		UDRS->select_date = jiffies;
+		if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
+		      (0x10 << UNIT(drive))))
+			/* switch the motor off again, if it was off to
+			 * begin with */
+			set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
+	}
+	current_drive = saved_drive;
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
+
+/* this function makes sure that the disk stays in the drive during the
+ * transfer */
+static void fd_watchdog(void)
 {
-	timer_active &= ~(1 << FLOPPY_TIMER);
-	probing = 0;
-	if (format_status != FORMAT_BUSY)
-		end_request(uptodate);
-	else {
-		format_status = uptodate ? FORMAT_OKAY : FORMAT_ERROR;
-		wake_up(&format_done);
+	if ( inb_p( FD_DIR ) & 0x80 ){
+		floppy_shutdown();
+	} else {
+		cli();
+		del_timer(&fd_timer);
+		fd_timer.function = (timeout_fn) fd_watchdog;
+		fd_timer.expires = 10;
+		add_timer(&fd_timer);
+		sti();
 	}
 }
 
-/*
- * floppy-change is never called from an interrupt, so we can relax a bit
- * here, sleep etc. Note that floppy-on tries to set current_DOR to point
- * to the desired drive, but it will probably not survive the sleep if
- * several floppies are used at the same time: thus the loop.
- */
-static int floppy_change(struct buffer_head * bh)
+static void main_command_interrupt(void)
 {
-	unsigned int mask = 1 << (bh->b_dev & 0x03);
+	del_timer(&fd_timer);
+	cont->interrupt();
+}
 
-	if (MAJOR(bh->b_dev) != MAJOR_NR) {
-		printk("floppy_changed: not a floppy\n");
-		return 0;
-	}
-	if (fake_change & mask) {
-		buffer_track = -1;
-		fake_change &= ~mask;
-/* omitting the next line breaks formatting in a horrible way ... */
-		changed_floppies &= ~mask;
-		return 1;
-	}
-	if (changed_floppies & mask) {
-		buffer_track = -1;
-		changed_floppies &= ~mask;
-		recalibrate = 1;
+/* waits for a delay (spinup or select) to pass */
+static int wait_for_completion(int nr, int delay, timeout_fn function)
+{
+	if ( FDCS->reset ){
+		reset_fdc(); /* do the reset during sleep to win time
+			      * if we don't need to sleep, it's a good
+			      * occasion anyways */
 		return 1;
 	}
-	if (!bh)
-		return 0;
-	if (bh->b_dirt)
-		ll_rw_block(WRITE, 1, &bh);
-	else {
-		buffer_track = -1;
-		bh->b_uptodate = 0;
-		ll_rw_block(READ, 1, &bh);
-	}
-	wait_on_buffer(bh);
-	if (changed_floppies & mask) {
-		changed_floppies &= ~mask;
-		recalibrate = 1;
+
+	if ( jiffies < delay ){
+		del_timer(&fd_timer);
+		fd_timer.function = function;
+		fd_timer.expires = delay  - jiffies;
+		add_timer(&fd_timer);
 		return 1;
 	}
 	return 0;
 }
 
-#define copy_buffer(from,to) \
-__asm__("cld ; rep ; movsl" \
-	: \
-	:"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
-	:"cx","di","si")
-
 static void setup_DMA(void)
 {
-	unsigned long addr,count;
-	unsigned char dma_code;
-
-	dma_code = DMA_WRITE;
-	if (command == FD_READ)
-		dma_code = DMA_READ;
-	if (command == FD_FORMAT) {
-		addr = (long) tmp_floppy_area;
-		count = floppy->sect*4;
-	} else {
-		addr = (long) CURRENT->buffer;
-		count = 1024;
+#ifdef SANITY
+	if ((!CURRENT ||
+	     CURRENT->buffer != current_addr ||
+	     raw_cmd.length > 512 * CURRENT->nr_sectors) &&
+	    (current_addr < floppy_track_buffer ||
+	     current_addr + raw_cmd.length >
+	     floppy_track_buffer + 1024 * MAX_BUFFER_SECTORS)){
+		printk("bad adrress. start=%p lg=%lx tb=%p\n",
+		       current_addr, raw_cmd.length, floppy_track_buffer);
+		if ( CURRENT ){
+			printk("buffer=%p nr=%lx cnr=%lx\n",
+			       CURRENT->buffer, CURRENT->nr_sectors,
+			       CURRENT->current_nr_sectors);
+		}
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if ((long) current_addr % 512 ){
+		printk("non aligned address: %p\n", current_addr );
+		cont->done(0);
+		FDCS->reset=1;
+		return;
 	}
-	if (read_track) {
-/* mark buffer-track bad, in case all this fails.. */
-		buffer_drive = buffer_track = -1;
-		count = floppy->sect*floppy->head*512;
-		addr = (long) floppy_track_buffer;
-	} else if (addr >= LAST_DMA_ADDR) {
-		addr = (long) tmp_floppy_area;
-		if (command == FD_WRITE)
-			copy_buffer(CURRENT->buffer,tmp_floppy_area);
+	if ( ( (long)current_addr & ~(64*1024-1) ) !=
+	    ((long)(current_addr + raw_cmd.length-1)  & ~(64*1024-1))){
+		printk("DMA crossing 64-K boundary %p-%p\n",
+		       current_addr, current_addr + raw_cmd.length);
+		cont->done(0);
+		FDCS->reset=1;
+		return;
 	}
+
+#endif
 	cli();
 	disable_dma(FLOPPY_DMA);
 	clear_dma_ff(FLOPPY_DMA);
-	set_dma_mode(FLOPPY_DMA, (command == FD_READ)? DMA_MODE_READ : DMA_MODE_WRITE);
-	set_dma_addr(FLOPPY_DMA, addr);
-	set_dma_count(FLOPPY_DMA, count);
+	set_dma_mode(FLOPPY_DMA,
+		     (raw_cmd.flags & FD_RAW_READ)?
+		     DMA_MODE_READ : DMA_MODE_WRITE);
+	set_dma_addr(FLOPPY_DMA, (long) current_addr);
+	set_dma_count(FLOPPY_DMA, raw_cmd.length);
 	enable_dma(FLOPPY_DMA);
 	sti();
 }
 
-static void output_byte(char byte)
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
 {
 	int counter;
 	unsigned char status;
 
-	if (reset)
-		return;
+	if (FDCS->reset)
+		return -1;
 	for(counter = 0 ; counter < 10000 ; counter++) {
-		status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
-		if (status == STATUS_READY) {
-			outb(byte,FD_DATA);
-			return;
-		}
+		status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY
+#ifdef FDC_FIFO_BUG
+		    || ((status == STATUS_READY|STATUS_DIR|STATUS_BUSY) &&force)
+#endif
+		    )
+		{
+			outb_p(byte,FD_DATA);
+			return 0;
+		} else
+			break;
 	}
-	current_track = NO_TRACK;
-	reset = 1;
-	printk("Unable to send byte to FDC\n");
+	FDCS->reset = 1;
+	if ( !initialising )
+		printk(DEVICE_NAME ": Unable to send byte to FDC %d (%x)\n",
+		       fdc, status);
+	return -1;
 }
+#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;}
 
 #ifdef FDC_FIFO_BUG
-
-static void output_byte_force(char byte)
-{
-	int counter;
-	unsigned char status;
-
-	if (reset)
-		return;
-	for (counter = 0 ; counter < 10000 ; counter++) {
-		status = inb_p(FD_STATUS);
-		if ((status & (STATUS_READY | STATUS_DIR)) == STATUS_READY) {
-			outb(byte,FD_DATA);
-			return;
-		}
-		if ((status & (STATUS_READY | STATUS_BUSY)) == (STATUS_READY | STATUS_BUSY)) {
-			outb(byte,FD_DATA);
-			return;
-		}
-	}
-	current_track = NO_TRACK;
-	reset = 1;
-	printk("Unable to send byte to FDC\n");
-}
-#endif		/* FDC_FIFO_BUG */
+#define output_byte_force(x) force=1;output_byte(x);force=0;
+#else
+#define output_byte_force(x) output_byte(x);
+#endif
 
+/* gets the response from the fdc */
 static int result(void)
 {
 	int i = 0, counter, status;
 
-	if (reset)
+	if (FDCS->reset)
 		return -1;
 	for (counter = 0 ; counter < 10000 ; counter++) {
-		status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
-		if (status == STATUS_READY) {
+		status = inb_p(FD_STATUS)&
+			(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY)
 			return i;
-		}
+		if (status & STATUS_DMA )
+			break;
 		if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
 			if (i >= MAX_REPLIES) {
-				printk("floppy_stat reply overrun\n");
+				printk(DEVICE_NAME
+				       ": floppy_stat reply overrun\n");
 				break;
 			}
 			reply_buffer[i++] = inb_p(FD_DATA);
 		}
 	}
-	reset = 1;
-	current_track = NO_TRACK;
-	printk("Getstatus times out\n");
+	FDCS->reset = 1;
+	if ( !initialising )
+		printk(DEVICE_NAME ": Getstatus times out (%x) on fdc %d [%d]\n",
+		       status, fdc,i);
 	return -1;
 }
 
-static void bad_flp_intr(void)
-{
-	int errors;
-
-	if (probing) {
-		int device = MINOR(CURRENT_DEVICE) & 3;
-		++floppy;
-		if (floppy < base_type_end[device])
-			return;
-		floppy = base_type[device];
-	}
-	current_track = NO_TRACK;
-	if (format_status == FORMAT_BUSY)
-		errors = ++format_errors;
-	else if (!CURRENT) {
-		printk(DEVICE_NAME ": no current request\n");
-		reset = recalibrate = 1;
-		return;
-	} else
-		errors = ++CURRENT->errors;
-	if (errors > MAX_ERRORS) {
-		request_done(0);
-	}
-	if (errors > MAX_ERRORS/2)
-		reset = 1;
-	else
-		recalibrate = 1;
-}	
-
-
 /* Set perpendicular mode as required, based on data rate, if supported.
  * 82077 Untested! 1Mbps data rate only possible with 82077-1.
  * TODO: increase MAX_BUFFER_SECTORS, add floppy_type entries.
  */
-static inline void perpendicular_mode(unsigned char rate)
+static inline void perpendicular_mode(void)
 {
-	if (fdc_version == FDC_TYPE_82077) {
-		output_byte(FD_PERPENDICULAR);
-		if (rate & 0x40) {
-			unsigned char r = rate & 0x03;
-			if (r == 0)
-#ifndef FDC_FIFO_BUG
-				output_byte(2);	/* perpendicular, 500 kbps */
-#else
-				output_byte_force(2);	/* perpendicular, 500 kbps */
-#endif
-			else if (r == 3)
-#ifndef FDC_FIFO_BUG
-				output_byte(3);	/* perpendicular, 1Mbps */
-#else
-				output_byte_force(3);	/* perpendicular, 1Mbps */
-#endif
-			else {
-				printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n");
-				reset = 1;
-			}
-		} else
-#ifndef FDC_FIFO_BUG
-			output_byte(0);		/* conventional mode */
-#else
-			output_byte_force(0);		/* conventional mode */
-#endif
-	} else {
-		if (rate & 0x40) {
-			printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n");
-			reset = 1;
+	unsigned char perp_mode;
+
+	if (!floppy)
+		return;
+	if (floppy->rate & 0x40){
+		switch(raw_cmd.rate){
+		case 0:
+			perp_mode=/*2*/3;
+			break;
+		case 3:
+			perp_mode=3;
+			break;
+		default:
+			printk(DEVICE_NAME
+			       ": Invalid data rate for perpendicular mode!\n");
+			cont->done(0);
+			FDCS->reset = 1; /* convenient way to return to
+					  * redo without to much hassle (deep
+					  * stack et al. */
+			return;
 		}
+	} else
+		perp_mode = 0;
+			
+	if ( FDCS->perp_mode == perp_mode )
+		return;
+	if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+		output_byte(FD_PERPENDICULAR);
+		output_byte_force(perp_mode);
+		FDCS->perp_mode = perp_mode;
+	} else if (perp_mode) {
+		printk(DEVICE_NAME
+		       ": perpendicular mode not supported by this FDC.\n");
 	}
 } /* perpendicular_mode */
 
+#define NOMINAL_DTR 500
 
-/*
- * This has only been tested for the case fdc_version == FDC_TYPE_STD.
- * In case you have a 82077 and want to test it, you'll have to compile
- * with `FDC_FIFO_UNTESTED' defined. You may also want to add support for
- * recognizing drives with vertical recording support.
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps.  It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types.  This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
  */
-static void configure_fdc_mode(void)
+static void fdc_specify(void)
 {
-	if (need_configure && (fdc_version == FDC_TYPE_82077)) {
-		/* Enhanced version with FIFO & vertical recording. */
+	unsigned char spec1, spec2;
+	int srt, hlt, hut;
+	unsigned long dtr = NOMINAL_DTR;
+	unsigned long scale_dtr = NOMINAL_DTR;
+	int hlt_max_code = 0x7f;
+	int hut_max_code = 0xf;
+
+	if (FDCS->need_configure && FDCS->has_fifo) {
+		if ( FDCS->reset )
+			return;
+		/* Turn on FIFO for 82077-class FDC (improves performance) */
+		/* TODO: lock this in via LOCK during initialization */
 		output_byte(FD_CONFIGURE);
-#ifndef FDC_FIFO_BUG
-		output_byte(0);
-#else
 		output_byte_force(0);
-#endif
 		output_byte(0x1A);	/* FIFO on, polling off, 10 byte threshold */
-#ifndef FDC_FIFO_BUG
-		output_byte(0);		/* precompensation from track 0 upwards */
-#else
 		output_byte_force(0);		/* precompensation from track 0 upwards */
-#endif
-		need_configure = 0;
-		printk(DEVICE_NAME ": FIFO enabled\n");
+		if ( FDCS->reset ){
+			FDCS->has_fifo=0;
+			return;
+		}
+		FDCS->need_configure = 0;
+		/*printk(DEVICE_NAME ": FIFO enabled\n");*/
 	}
-	if (cur_spec1 != floppy->spec1) {
-		cur_spec1 = floppy->spec1;
+
+	switch (raw_cmd.rate & 0x03) {
+	case 3:
+		dtr = 1000;
+		break;
+	case 1:
+		dtr = 300;
+		break;
+	case 2:
+		dtr = 250;
+		break;
+	}
+
+	if (FDCS->version >= FDC_82072) {
+		scale_dtr = dtr;
+		hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
+		hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
+	}
+
+	/* Convert step rate from microseconds to milliseconds and 4 bits */
+	srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (srt > 0xf)
+		srt = 0xf;
+	else if (srt < 0)
+		srt = 0;
+
+	hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hlt < 0x01)
+		hlt = 0x01;
+	else if (hlt > 0x7f)
+		hlt = hlt_max_code;
+
+	hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hut < 0x1)
+		hut = 0x1;
+	else if (hut > 0xf)
+		hut = hut_max_code;
+
+	spec1 = (srt << 4) | hut;
+#define fd_disable_dma 0
+	spec2 = (hlt << 1) | fd_disable_dma;
+
+	/* If these parameters did not change, just return with success */
+	if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
+		/* Go ahead and set spec1 and spec2 */
 		output_byte(FD_SPECIFY);
-		output_byte(cur_spec1);		/* hut etc */
-		output_byte(6);			/* Head load time =6ms, DMA */
+		output_byte(FDCS->spec1 = spec1);
+		output_byte(FDCS->spec2 = spec2);
 	}
-	if (cur_rate != floppy->rate) {
-		/* use bit 6 of floppy->rate to indicate perpendicular mode */
-		perpendicular_mode(floppy->rate);
-		outb_p((cur_rate = (floppy->rate)) & ~0x40, FD_DCR);
-	}
-} /* configure_fdc_mode */
+} /* fdc_specify */
 
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static void fdc_dtr(void)
+{
+	/* If data rate not already set to desired value, set it. */
+	if ( raw_cmd.rate == FDCS->dtr)
+		return;
+	
+	/* Set dtr */
+	outb_p(raw_cmd.rate, FD_DCR);
+	
+	/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+	 * need a stabilization period of several milliseconds to be
+	 * enforced after data rate changes before R/W operations.
+	 * Pause 5 msec to avoid trouble.
+	 */
+	udelay(5000);
+	FDCS->dtr = raw_cmd.rate;
+} /* fdc_dtr */
 
-static void tell_sector(int nr)
+static void tell_sector(void)
 {
-	if (nr!=7) {
-		printk(" -- FDC reply errror");
-		reset = 1;
-	} else
-		printk(": track %d, head %d, sector %d", reply_buffer[3],
-			reply_buffer[4], reply_buffer[5]);
+	printk(": track %d, head %d, sector %d, size %d",
+	       R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
 } /* tell_sector */
 
 
 /*
- * Ok, this interrupt is called after a DMA read/write has succeeded
+ * Ok, this error interpreting routine is called after a
+ * DMA read/write has succeeded
  * or failed, so we check the results, and copy any buffers.
  * hhb: Added better error reporting.
+ * ak: Made this into a separate routine.
  */
-static void rw_interrupt(void)
+static int interpret_errors(void)
 {
-	char * buffer_area;
-	int nr;
 	char bad;
 
-	nr = result();
+	if (inr!=7) {
+		printk(DEVICE_NAME ": -- FDC reply errror");
+		FDCS->reset = 1;
+		return 1;
+	}
+
 	/* check IC to find cause of interrupt */
 	switch ((ST0 & ST0_INTR)>>6) {
 		case 1:	/* error occured during command execution */
 			bad = 1;
 			if (ST1 & ST1_WP) {
 				printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive);
-				request_done(0);
-				bad = 0;
+				DRS->flags &= ~FD_DISK_WRITABLE;
+				cont->done(0);
+				bad = 2;
+			} else if (ST1 & ST1_ND) {
+				DRS->flags |= FD_NEED_TWADDLE;
 			} else if (ST1 & ST1_OR) {
-				if (ftd_msg[ST0 & ST0_DS])
+				if (DP->flags & FTD_MSG )
 					printk(DEVICE_NAME ": Over/Underrun - retrying\n");
 				/* could continue from where we stopped, but ... */
 				bad = 0;
-			} else if (CURRENT_ERRORS > min_report_error_cnt[ST0 & ST0_DS]) {
+			}else if(*errors >= DP->max_errors.reporting){
 				printk(DEVICE_NAME " %d: ", ST0 & ST0_DS);
 				if (ST0 & ST0_ECE) {
 					printk("Recalibrate failed!");
 				} else if (ST2 & ST2_CRC) {
 					printk("data CRC error");
-					tell_sector(nr);
+					tell_sector();
 				} else if (ST1 & ST1_CRC) {
 					printk("CRC error");
-					tell_sector(nr);
+					tell_sector();
 				} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
 					if (!probing) {
 						printk("sector not found");
-						tell_sector(nr);
+						tell_sector();
 					} else
 						printk("probe failed...");
 				} else if (ST2 & ST2_WC) {	/* seek error */
@@ -770,175 +990,244 @@
 				printk("\n");
 
 			}
-			if (bad)
-				bad_flp_intr();
-			redo_fd_request();
-			return;
+			if ( ST2 & ST2_WC || ST2 & ST2_BC)
+				/* wrong cylinder => recal */
+				DRS->track = NEED_2_RECAL;
+			return bad;
 		case 2: /* invalid command given */
 			printk(DEVICE_NAME ": Invalid FDC command given!\n");
-			request_done(0);
-			return;
+			cont->done(0);
+			return 2;
 		case 3:
 			printk(DEVICE_NAME ": Abnormal termination caused by polling\n");
-			bad_flp_intr();
-			redo_fd_request();
-			return;
+			cont->error();
+			return 2;
 		default: /* (0) Normal command termination */
-			break;
-	}
-
-	if (probing) {
-		int drive = MINOR(CURRENT->dev);
-
-		if (ftd_msg[drive])
-			printk("Auto-detected floppy type %s in fd%d\n",
-			    floppy->name,drive);
-		current_type[drive] = floppy;
-		floppy_sizes[drive] = floppy->size >> 1;
-		probing = 0;
+			return 0;
 	}
-	if (read_track) {
-		buffer_track = seek_track;
-		buffer_drive = current_drive;
-		buffer_area = floppy_track_buffer +
-			((sector-1 + head*floppy->sect)<<9);
-		copy_buffer(buffer_area,CURRENT->buffer);
-	} else if (command == FD_READ &&
-		(unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR)
-		copy_buffer(tmp_floppy_area,CURRENT->buffer);
-	request_done(1);
-	redo_fd_request();
 }
 
 /*
- * We try to read tracks, but if we get too many errors, we
- * go back to reading just one sector at a time.
- *
- * This means we should be able to read a sector even if there
- * are other bad sectors on this track.
+ * This routine is called when everything should be correctly set up
+ * for the transfer (ie floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
  */
-inline void setup_rw_floppy(void)
+static void setup_rw_floppy(void)
 {
-	setup_DMA();
-	do_floppy = rw_interrupt;
-	output_byte(command);
-	if (command != FD_FORMAT) {
-		if (read_track) {
-			output_byte(current_drive);
-			output_byte(track);
-			output_byte(0);
-			output_byte(1);
-		} else {
-			output_byte(head<<2 | current_drive);
-			output_byte(track);
-			output_byte(head);
-			output_byte(sector);
-		}
-		output_byte(2);		/* sector size = 512 */
-		output_byte(floppy->sect);
-		output_byte(floppy->gap);
-		output_byte(0xFF);	/* sector size (0xff when n!=0 ?) */
-	} else {
-		output_byte(head<<2 | current_drive);
-		output_byte(2);
-		output_byte(floppy->sect);
-		output_byte(floppy->fmt_gap);
-		output_byte(FD_FILL_BYTE);
+	int i,ready_date,r, flags,dflags;
+	timeout_fn function;
+
+	flags = raw_cmd.flags;
+	if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
+		flags |= FD_RAW_INTR;
+
+	if (flags & FD_RAW_SPIN){
+		ready_date = DRS->spinup_date + DP->spinup;		
+		/* If spinup will take a long time, rerun scandrives
+		 * again just before spinup completion. Beware that
+		 * after scandrives, we must again wait for selection.
+		 */
+		if ( ready_date > jiffies + DP->select_delay){
+			ready_date -= DP->select_delay;
+			function = (timeout_fn) floppy_on;
+		} else
+			function = (timeout_fn) setup_rw_floppy;
+
+		/* wait until the floppy is spinning fast enough */
+		if (wait_for_completion(current_drive,ready_date,function))
+			return;
 	}
-	if (reset)
-		redo_fd_request();
+	dflags = DRS->flags;
+
+	if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE)){
+		if ( flags & FD_RAW_USER_SUPPLIED )
+			buffer_track = -1;
+		setup_DMA();
+	}
+	
+	if ( flags & FD_RAW_INTR )
+		SET_INTR(main_command_interrupt);
+
+	r=0;
+	for(i=0; i< raw_cmd.cmd_count; i++)
+		r|=output_byte( raw_cmd.cmd[i] );
+
+#ifdef DEBUGT
+	debugt("rw_command: ");
+#endif
+	if ( r ){
+		reset_fdc();
+		return;
+	}
+
+	if ( ! ( flags & FD_RAW_INTR )){
+		inr = result();
+		cont->interrupt();
+	} else if ( flags & FD_RAW_NEED_DISK )
+		fd_watchdog();
 }
 
 /*
  * This is the routine called after every seek (or recalibrate) interrupt
- * from the floppy controller. Note that the "unexpected interrupt" routine
- * also does a recalibrate, but doesn't come here.
+ * from the floppy controller.
  */
 static void seek_interrupt(void)
 {
-/* sense drive status */
-	output_byte(FD_SENSEI);
-	if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
+#ifdef DEBUGT
+	debugt("seek interrupt:");
+#endif
+	if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
 		printk(DEVICE_NAME ": seek failed\n");
-		recalibrate = 1;
-		bad_flp_intr();
-		redo_fd_request();
+		DRS->track = NEED_2_RECAL;
+		cont->error();
+		cont->redo();
 		return;
 	}
-	current_track = ST1;
-	setup_rw_floppy();
-}
-
-
-/*
- * This routine is called when everything should be correctly set up
- * for the transfer (ie floppy motor is on and the correct floppy is
- * selected).
- */
-static void transfer(void)
-{
-	read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) &&
-	    (floppy->sect <= MAX_BUFFER_SECTORS);
-
-	configure_fdc_mode();
-
-	if (reset) {
-		redo_fd_request();
+	if ( DRS->track >= 0 && DRS->track != ST1 )
+		DRS->flags &= ~FD_DISK_NEWCHANGE;
+	DRS->track = ST1;
+	seek_floppy();
+}
+
+static void seek_floppy(void)
+{
+	int track;
+
+	if ((raw_cmd.flags & FD_RAW_NEED_DISK) &&
+	    !(DRS->flags & FD_DISK_NEWCHANGE ) &&
+	    (inb_p(FD_DIR) & 0x80)){
+		/* the media changed flag should be cleared after the seek.
+		 * If it isn't, this means that there is really no disk in
+		 * the drive.
+		 */
+		cont->done(0);
+		cont->redo();
 		return;
 	}
-	if (!seek) {
+	if ( DRS->track <= NEED_1_RECAL ){
+		recalibrate_floppy();
+		return;
+	} else if ((DRS->flags & FD_DISK_NEWCHANGE) &&
+		   (DRS->track <= NO_TRACK || DRS->track == raw_cmd.track)) {
+		/* we seek to clear the media-changed condition. Does anybody
+		 * know a more elegant way, which works on all drives? */
+		if ( raw_cmd.track )
+			track = raw_cmd.track - 1;
+		else
+			track = 1;
+	} else if (raw_cmd.track != DRS->track)
+		track = raw_cmd.track;
+	else {
 		setup_rw_floppy();
 		return;
 	}
 
-	do_floppy = seek_interrupt;
-	output_byte(FD_SEEK);
-	if (read_track)
-		output_byte(current_drive);
-	else
-		output_byte((head<<2) | current_drive);
-	output_byte(seek_track);
-	if (reset)
-		redo_fd_request();
+	if ( !track && DRS->track >= 0 && DRS->track < 80 ){
+		DRS->flags &= ~FD_DISK_NEWCHANGE;
+		/* if we go to track 0 anyways, we can just as well use
+		 * recalibrate */
+		recalibrate_floppy();
+	} else {
+		SET_INTR(seek_interrupt);
+		output_byte(FD_SEEK);
+		output_byte(UNIT(current_drive));
+		LAST_OUT(track);
+#ifdef DEBUGT
+		debugt("seek command:");
+#endif
+	}
 }
 
-/*
- * Special case - used after a unexpected interrupt (or reset)
- */
-
 static void recal_interrupt(void)
 {
-	output_byte(FD_SENSEI);
-	current_track = NO_TRACK;
-	if (result()!=2 || (ST0 & 0xE0) == 0x60)
-		reset = 1;
-/* Recalibrate until track 0 is reached. Might help on some errors. */
-	if ((ST0 & 0x10) == 0x10)
-		recalibrate_floppy();	/* FIXME: should limit nr of recalibrates */
-	else
-		redo_fd_request();
+#ifdef DEBUGT
+	debugt("recal interrupt:");
+#endif
+	if (inr !=2 )
+		FDCS->reset = 1;
+	else if (ST0 & ST0_ECE) {
+	       	switch(DRS->track){
+	       	case NEED_1_RECAL:
+			/* after a second recalibrate, we still haven't
+			 * reached track 0. Probably no drive */
+			cont->error();
+			cont->redo();
+			return;
+		case NEED_2_RECAL:
+			/* If we already did a recalibrate, and we are not at
+			 * track 0, this means we have moved. (The only way
+			 * not to move at recalibration is to be already at
+			 * track 0.) Clear the new change flag
+			 */
+			DRS->flags &= ~FD_DISK_NEWCHANGE;
+			/* fall through */
+		default:
+			/* Recalibrate moves the head by at most 80 steps. If
+			 * after one recalibrate we don't have reached track
+			 * 0, this might mean that we started beyond track 80.
+			 * Try again.
+			 */
+			DRS->track = NEED_1_RECAL;
+			break;
+		}
+	} else
+		DRS->track = ST1;
+	seek_floppy();
 }
 
+/*
+ * Unexpected interrupt - Print as much debugging info as we can...
+ * All bets are off...
+ */
 static void unexpected_floppy_interrupt(void)
 {
-	current_track = NO_TRACK;
-	output_byte(FD_SENSEI);
+	int i;
+	if ( initialising )
+		return;
 	printk(DEVICE_NAME ": unexpected interrupt\n");
-	if (result()!=2 || (ST0 & 0xE0) == 0x60)
-		reset = 1;
-	else
-		recalibrate = 1;
+	if ( inr >= 0 )
+		for(i=0; i<inr; i++)
+			printk("%d %x\n", i, reply_buffer[i] );
+	while(1){
+		output_byte(FD_SENSEI);
+		inr=result();
+		if ( inr != 2 )
+			break;
+		printk("sensei\n");
+		for(i=0; i<inr; i++)
+			printk("%d %x\n", i, reply_buffer[i] );
+	}
+	FDCS->reset = 1;
+}
+
+/* interrupt handler */
+static void floppy_interrupt(int unused)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	CLEAR_INTR;
+	if ( fdc >= N_FDC ) /* we don't even know which FDC is the culprit */
+		return;
+	inr = result();
+	if (!handler){
+		unexpected_floppy_interrupt();
+		return;
+	}
+	if ( inr == 0 ){
+		do {
+			output_byte(FD_SENSEI);
+			inr = result();
+		} while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+	}
+	sti(); /* this should help improve interrupt latency. */
+	handler();
 }
 
 static void recalibrate_floppy(void)
 {
-	recalibrate = 0;
-	current_track = 0;
-	do_floppy = recal_interrupt;
+	SET_INTR(recal_interrupt);
 	output_byte(FD_RECALIBRATE);
-	output_byte(head<<2 | current_drive);
-	if (reset)
-		redo_fd_request();
+	LAST_OUT(UNIT(current_drive));
 }
 
 /*
@@ -946,407 +1235,1335 @@
  */
 static void reset_interrupt(void)
 {
-	short i;
+#ifdef DEBUGT
+	debugt("reset interrupt:");
+#endif
+	fdc_specify();		/* reprogram fdc */
+	result();		/* get the status ready for set_fdc */
+	if ( FDCS->reset )
+		cont->error(); /* a reset just after a reset. BAD! */
+	cont->redo();
+}
 
-	for (i=0; i<4; i++) {
-		output_byte(FD_SENSEI);
-		(void) result();
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
+ * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ */
+static void reset_fdc(void)
+{
+	SET_INTR(reset_interrupt);
+	FDCS->reset = 0;
+	reset_fdc_info(0);
+	if ( FDCS->version >= FDC_82077 )
+		outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+	else {
+		cli();
+		outb_p(FDCS->dor & ~0x04, FD_DOR);
+		udelay(FD_RESET_DELAY);
+		outb(FDCS->dor, FD_DOR);
+		sti();
 	}
-	output_byte(FD_SPECIFY);
-	output_byte(cur_spec1);		/* hut etc */
-	output_byte(6);			/* Head load time =6ms, DMA */
-	configure_fdc_mode();		/* reprogram fdc */
-	if (initial_reset_flag) {
-		initial_reset_flag = 0;
-		recalibrate = 1;
-		reset = 0;
+}
+
+static void floppy_shutdown(void)
+{
+	cli();
+	if ( !DEVICE_INTR ){ /* no interrupt pending. Probably has just been
+			      * served */
+		sti();
 		return;
 	}
-	if (!recover)
-		redo_fd_request();
+	CLEAR_INTR;
+	sti();
+	if ( !initialising )
+		printk(DEVICE_NAME ": timeout\n");
+	FDCS->reset = 1;
+	cont->done(0);
+	cont->redo(); /* this will recall reset when needed */
+}
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor(void)
+{
+	int cnt;
+	int dir;
+
+	if ( (FDCS->dor & 0x03) != UNIT(current_drive) )
+		/* notes select time if floppy is not yet selected */
+		DRS->select_date = jiffies;
+
+	if ( ! ( FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
+		set_debugt();
+		/* no read since this drive is running */
+		DRS->first_read_date = 0;
+		/* note motor start time if motor is not yet running */
+		DRS->spinup_date = jiffies;
+	}
+
+	/* starts motor and selects floppy */
+	del_timer(motor_off_timer + current_drive);
+	set_dor( fdc, 0xfc,
+		( 0x10 << UNIT(current_drive) ) | UNIT(current_drive) );
+
+	dir = inb_p( FD_DIR) & 0x80;
+	if ( ! (dir & 0x80) )
+		DRS->last_checked =jiffies;
+	if ( dir || ( DRS->flags & FD_VERIFY )) {
+		DRS->flags &= FD_DRIVE_PRESENT | FD_DISK_NEWCHANGE;
+		DRS->flags |= FD_DISK_WRITABLE;
+
+		/* check write protection */
+		output_byte( FD_GETSTATUS );
+		output_byte( UNIT(current_drive) );
+		if ( (cnt=result()) != 1 ){
+			changed_floppies |= 1 << current_drive;
+			FDCS->reset = 1;
+			DRS->flags |= FD_VERIFY;
+			return -1;
+		}
+		if ( ( ST3  & 0x60 ) == 0x60 )
+			DRS->flags &= ~FD_DISK_WRITABLE;
+
+		if ( ! ( DRS->flags & FD_DISK_NEWCHANGE) ){
+			/* the following code is only executed the first time
+			 * a particular disk change has been detected */
+			changed_floppies |= 1 << current_drive;
+			if (DRS->keep_data >= 0) {
+				if ((DP->flags & FTD_MSG) &&
+				    current_type[current_drive] != NULL)
+					printk(DEVICE_NAME
+					       ": Disk type is undefined after "
+					       "disk change in fd%d\n",
+					       current_drive);
+				current_type[current_drive] = NULL;
+				floppy_sizes[current_drive] = MAX_DISK_SIZE;
+			}
+			if ( ST3 & 0x10 )
+				DRS->track = 0;
+		}
+	}
+	if ( dir ) /* check if media changed is still on */
+		DRS->flags |= FD_DISK_NEWCHANGE;
 	else {
-		recalibrate_floppy();
-		recover = 0;
+		DRS->flags &= ~FD_DISK_NEWCHANGE;
+		DRS->last_checked =jiffies;
 	}
+
+	return DRS->flags;
+}
+
+static void floppy_ready(void)
+{
+	CHECK_RESET;
+	start_motor();
+
+	/* wait_for_completion also schedules reset if needed. */
+	if(wait_for_completion(current_drive,
+			       DRS->select_date+DP->select_delay,
+			       (timeout_fn) floppy_ready))
+		return;
+	fdc_dtr();
+	if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){
+		perpendicular_mode();
+		fdc_specify(); /* must be done here because of hut, hlt ... */
+		seek_floppy();
+	} else
+		setup_rw_floppy();
+}
+
+static void floppy_on(unsigned int drive)
+{
+	timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+	timer_active |= 1 << FLOPPY_TIMER;
+	scandrives();
+	floppy_ready();
 }
 
 /*
- * reset is done by pulling bit 2 of DOR low for a while.
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+  * floppy_on, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
+ * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * and set_dor.
+ * ========================================================================
+ */
+/*
+ * General purpose continuations.
+ * ==============================
  */
-static void reset_floppy(void)
+static void empty(void)
 {
-	int i;
+}
 
-	do_floppy = reset_interrupt;
-	reset = 0;
-	current_track = NO_TRACK;
-	cur_spec1 = -1;
-	cur_rate = -1;
-	recalibrate = 1;
-	need_configure = 1;
-	if (!initial_reset_flag)
-		printk("Reset-floppy called\n");
-	cli();
-	outb_p(current_DOR & ~0x04, FD_DOR);
-	for (i=0 ; i<1000 ; i++)
-		__asm__("nop");
-	outb(current_DOR, FD_DOR);
-	sti();
+static void do_wakeup(void)
+{
+	timer_active &= ~(1 << FLOPPY_TIMER);
+	command_status += 2;
+	wake_up(&command_done);
+	cont = 0;
 }
 
-static void floppy_shutdown(void)
+static struct cont_t wakeup_cont={
+	empty,
+	do_wakeup,
+	empty,
+	(done_f)empty
+};
+
+static int wait_til_done(void)
 {
-	cli();
-	do_floppy = NULL;
-	request_done(0);
-	recover = 1;
-	reset_floppy();
-	sti();
-	redo_fd_request();
+	int ret;
+	while(command_status < 2)
+		if (current->pid)
+			sleep_on( & command_done );
+	if ( FDCS->reset )
+		command_status = FD_COMMAND_ERROR;
+	if ( command_status == FD_COMMAND_OKAY )
+		ret=0;
+	else
+		ret=-EIO;
+	command_status = FD_COMMAND_NONE;
+	return ret;
 }
 
-static void shake_done(void)
+static void generic_done(int result)
 {
-	current_track = NO_TRACK;
-	if (inb(FD_DIR) & 0x80)
-		request_done(0);
-	redo_fd_request();
+	command_status = result;
+	cont = &wakeup_cont;
 }
 
-static int retry_recal(void (*proc)(void))
+static void generic_success(void)
 {
-	output_byte(FD_SENSEI);
-	if (result() == 2 && (ST0 & 0x10) != 0x10) return 0;
-	do_floppy = proc;
-	output_byte(FD_RECALIBRATE);
-	output_byte(head<<2 | current_drive);
-	return 1;
+	generic_done(1);
 }
 
-static void shake_zero(void)
+static void generic_failure(void)
 {
-	if (!retry_recal(shake_zero)) shake_done();
+	generic_done(0);
 }
 
-static void shake_one(void)
+static void success_and_wakeup(void)
 {
-	if (retry_recal(shake_one)) return;
-	do_floppy = shake_done;
-	output_byte(FD_SEEK);
-	output_byte(head << 2 | current_drive);
-	output_byte(1);
+	generic_success();
+	do_wakeup();
 }
 
-static void check_readonly(void)
+static void failure_and_wakeup(void)
 {
-	unsigned long mask = 1 << current_drive;
+	generic_failure();
+	do_wakeup();
+}
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
 
-	read_only &= ~mask;
-	output_byte(FD_GETSTATUS);
-	output_byte(current_drive);
-	if ((result() == 1) && (ST3 & ST3_RY) && !(ST3 & ST3_FT) && (ST3 & ST3_WP))
-		read_only |= mask;
+static int next_valid_format(void)
+{
+	int probed_format;
+	while(1){
+		probed_format = DRS->probed_format;
+		if ( probed_format > N_DRIVE ||
+		    ! DP->autodetect[probed_format] ){
+			DRS->probed_format = 0;
+			return 1;
+		}
+		if ( floppy_type[DP->autodetect[probed_format]].sect ){
+			DRS->probed_format = probed_format;
+			return 0;
+		}
+		probed_format++;
+	}
 }
 
-static void floppy_ready(void)
+static void bad_flp_intr(void)
 {
-	check_readonly();
-	if (inb(FD_DIR) & 0x80) {
-		changed_floppies |= 1<<current_drive;
-		buffer_track = -1;
-		if (keep_data[current_drive]) {
-			if (keep_data[current_drive] > 0)
-				keep_data[current_drive]--;
-		} else {
-			if (ftd_msg[current_drive] && current_type[current_drive] != NULL)
-				printk("Disk type is undefined after disk "
-				    "change in fd%d\n",current_drive);
-			current_type[current_drive] = NULL;
-			floppy_sizes[current_drive] = MAX_DISK_SIZE;
-		}
-/* Forcing the drive to seek makes the "media changed" condition go away.
- * There should be a cleaner solution for that ...
- */
-		if (!reset && !recalibrate) {
-			if (current_track && current_track != NO_TRACK)
-				do_floppy = shake_zero;
-			else
-				do_floppy = shake_one;
-			output_byte(FD_RECALIBRATE);
-			output_byte(head<<2 | current_drive);
+	if ( probing ){
+		DRS->probed_format++;
+		if ( !next_valid_format())
 			return;
+	}
+	(*errors)++;
+	if (*errors > DP->max_errors.abort)
+		cont->done(0);
+	if (*errors > DP->max_errors.reset)
+		FDCS->reset = 1;
+	else if (*errors > DP->max_errors.recal)
+		DRS->track = NEED_2_RECAL;
+}
+
+static void set_floppy(int device)
+{
+	if (TYPE(device))
+		floppy = TYPE(device) + floppy_type;
+	else
+		floppy = current_type[ DRIVE(device) ];
+}
+
+/*
+ * formatting and support.
+ * =======================
+ */
+static void format_interrupt(void)
+{
+	switch (interpret_errors()){
+	case 1:
+		cont->error();
+	case 2:
+		break;
+	case 0:
+		cont->done(1);
+	}
+	cont->redo();
+}
+
+static void setup_format_params(void)
+{
+	struct fparm {
+		unsigned char track,head,sect,size;
+	} *here = (void *)floppy_track_buffer;
+	int ssize,il,n;
+	int count,head_shift,track_shift;
+
+	raw_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+		FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
+	raw_cmd.rate = floppy->rate & 0x3;
+	raw_cmd.cmd_count = NR_F;
+	COMMAND = FD_FORMAT;
+	DR_SELECT = UNIT(current_drive) + ( format_req.head << 2 );
+	F_SIZECODE = FD_SIZECODE(floppy);
+	ssize = 1 << ( F_SIZECODE - 2 );
+	F_SECT_PER_TRACK = floppy->sect / ssize;
+	F_GAP = floppy->fmt_gap;
+	F_FILL = FD_FILL_BYTE;
+
+	current_addr = floppy_track_buffer;
+	raw_cmd.length = 4 * F_SECT_PER_TRACK;
+
+	/* allow for about 30ms for data transport per track */
+	head_shift  = (F_SECT_PER_TRACK + 5) / 6;
+
+	/* a ``cylinder'' is two tracks plus a little stepping time */
+	track_shift = 2 * head_shift + 1;
+
+	/* position of logical sector 1 on this track */
+	n = (track_shift * format_req.track + head_shift * format_req.head )
+		% F_SECT_PER_TRACK;
+
+	/* determine interleave */
+	il = 1;
+	if (floppy->sect > DP->interleave_sect && ssize==1)
+		il++;
+
+	/* initialize field */
+	for (count = 0; count < F_SECT_PER_TRACK; ++count) {
+		here[count].track = format_req.track;
+		here[count].head = format_req.head;
+		here[count].sect = 0;
+		here[count].size = F_SIZECODE;
+	}
+	/* place logical sectors */
+	for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
+		here[n].sect = count;
+		n = (n+il) % F_SECT_PER_TRACK;
+		if (here[n].sect) { /* sector busy, find next free sector */
+			++n;
+			if (n>= F_SECT_PER_TRACK) {
+				n-=F_SECT_PER_TRACK;
+				while (here[n].sect) ++n;
+			}
 		}
 	}
-	if (reset) {
-		reset_floppy();
+}
+
+static void redo_format(void)
+{
+	raw_cmd.track = format_req.track << floppy->stretch;
+	buffer_track = -1;
+	setup_format_params();
+	floppy_on(current_drive);
+#ifdef DEBUGT
+	debugt("queue format request");
+#endif
+}
+
+static struct cont_t format_cont={
+	format_interrupt,
+	redo_format,
+	bad_flp_intr,
+	generic_done };
+
+static int do_format(int device, struct format_descr *tmp_format_req)
+{
+	int okay;
+
+	lock_fdc(DRIVE(device));
+	set_floppy(device);
+	if (!floppy ||
+	    tmp_format_req->track >= floppy->track ||
+	    tmp_format_req->head >= floppy->head){
+		unlock_fdc();
+		return -EINVAL;
+	}
+	format_req = *tmp_format_req;
+	format_errors = 0;
+	cont = &format_cont;
+	errors = &format_errors;
+	redo_format();
+	okay=wait_til_done();
+	unlock_fdc();
+	return okay;
+}
+
+/*
+ * Buffer read/write and support
+ * =============================
+ */
+
+/* new request_done. Can handle physical sectors which are smaller than a
+ * logical buffer */
+static void request_done(int uptodate)
+{
+	int block;
+
+	probing = 0;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+
+	if (!CURRENT){
+		printk(DEVICE_NAME
+		       ": request list destroyed in floppy request done\n");
 		return;
 	}
-	if (recalibrate) {
-		recalibrate_floppy();
+	if (uptodate){
+		/* maintain values for invalidation on geometry
+		   change */
+		block = current_count_sectors + CURRENT->sector;
+		if (block > DRS->maxblock)
+			DRS->maxblock=block;
+		if ( block > floppy->sect)
+			DRS->maxtrack = 1;
+
+		/* unlock chained buffers */
+		while (current_count_sectors && CURRENT &&
+		       current_count_sectors >= CURRENT->current_nr_sectors ){
+			current_count_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->sector += CURRENT->current_nr_sectors;
+			end_request(1);
+		}
+		if ( current_count_sectors && CURRENT){
+			/* "unlock" last subsector */
+			CURRENT->buffer += current_count_sectors <<9;
+			CURRENT->current_nr_sectors -= current_count_sectors;
+			CURRENT->nr_sectors -= current_count_sectors;
+			CURRENT->sector += current_count_sectors;
+			return;
+		}
+
+		if ( current_count_sectors && ! CURRENT )
+			printk(DEVICE_NAME "request list destroyed in floppy request done\n");
+
+	} else {
+#ifdef NO_WEIRD_UNLOCKED
+		if ( CURRENT->bh )
+			/* avoid those pesky "Weird unlocked ... errors" */
+			CURRENT->bh->b_req = 0;
+#endif
+		end_request(0);
+	}
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+#if 0
+	int i;
+#endif
+	int nr_sectors, ssize;
+	char bad;
+
+	if ( ! DRS->first_read_date )
+		DRS->first_read_date = jiffies;
+
+	nr_sectors = 0;
+	ssize = 1 << (SIZECODE - 2);
+	nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
+		floppy->sect + (R_SECTOR-SECTOR) * ssize -
+			(sector_t % floppy->sect) % ssize;
+
+#ifdef SANITY
+	if ( nr_sectors > current_count_sectors + ssize -
+	    (current_count_sectors + sector_t) % ssize +
+	    sector_t % ssize){
+		printk(DEVICE_NAME ": long rw: %x instead of %lx\n",
+		       nr_sectors, current_count_sectors);
+		printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
+		printk("rh=%d h=%d\n", R_HEAD, HEAD);
+		printk("rt=%d t=%d\n", R_TRACK, TRACK);
+		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
+		       sector_t, ssize);
+	}
+#endif
+	if ( nr_sectors < 0 )
+		nr_sectors = 0;
+	if ( nr_sectors < current_count_sectors ){
+#if 0
+		printk(DEVICE_NAME ": short read got %d instead of %ld\n",
+		       nr_sectors, current_count_sectors);
+#endif
+#if 0
+		printk("command: ");
+		for(i=0; i<raw_cmd.cmd_count; i++)
+			printk("%x ", raw_cmd.cmd[i]);
+		printk("rate=%x\n", raw_cmd.rate);
+		printk("reply: ");
+		for(i=0; i< inr; i++)
+			printk("%x ", reply_buffer[i]);
+		printk("\n");
+#endif
+		current_count_sectors = nr_sectors;
+	}
+
+	switch (interpret_errors()){
+	case 2:
+		cont->redo();
 		return;
+	case 1:
+		if (  !current_count_sectors){
+			cont->error();
+			cont->redo();
+			return;
+		}
+		break;
+	case 0:
+		if (  !current_count_sectors){
+			int i;
+			printk(DEVICE_NAME ": dma problem?\n");
+			for(i=0; i< inr ; i++)
+				printk("%2x,", reply_buffer[i]);
+			printk("\n");
+			bad=1;
+			cont->error();
+			cont->redo();
+			return;
+		}
+		current_type[current_drive] = floppy;
+		break;
+	}
+
+	if (probing) {
+		if (DP->flags & FTD_MSG)
+			printk(DEVICE_NAME
+			       ": Auto-detected floppy type %s in fd%d\n",
+			       floppy->name,current_drive);
+		current_type[current_drive] = floppy;
+		floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
+			floppy->size >> 1;
+		probing = 0;
+	}
+
+	if ( COMMAND != FD_READ || current_addr == CURRENT->buffer ){
+		/* transfer directly from buffer */
+		cont->done(1);
+	} else if ( COMMAND == FD_READ){
+		buffer_track = raw_cmd.track;
+		buffer_drive = current_drive;
+		if ( nr_sectors + sector_t > buffer_max )
+			buffer_max = nr_sectors + sector_t;
 	}
-	transfer();
+	cont->redo();
 }
 
-static void setup_format_params(void)
+/* Compute maximal contiguous buffer size. */
+static int buffer_chain_size(void)
+{
+	struct buffer_head *bh;
+	int size;
+	char *base;
+
+	base = CURRENT->buffer;
+	size = CURRENT->current_nr_sectors << 9;
+	bh = CURRENT->bh;
+
+#ifdef SANITY
+	if ( !bh ){
+		printk(DEVICE_NAME ": null request in buffer_chain_size\n");
+		return size >> 9;
+	}
+#endif
+
+	bh = bh->b_reqnext;
+	while ( bh && bh->b_data == base + size ){
+		size += bh->b_size;
+		bh = bh->b_reqnext;
+	}
+	return size >> 9;
+}
+
+/* Compute the maximal transfer size */
+static int transfer_size(int ssize, int max_sector, int max_size)
+{
+	if ( max_sector > sector_t + max_size)
+		max_sector = sector_t + max_size;
+
+	/* alignment */
+	max_sector -= (max_sector % floppy->sect ) % ssize;
+
+	/* transfer size, beginning not aligned */
+	current_count_sectors = max_sector - sector_t ;
+
+	return max_sector;
+}
+
+/*
+ * Move data from/to the track buffer to/from the buffer cache.
+ */
+static void copy_buffer(int ssize, int max_sector, int max_sector_2)
 {
-    unsigned char *here = (unsigned char *) tmp_floppy_area;
-    int count,head_shift,track_shift,total_shift;
+	int remaining; /* number of transferred 512-byte sectors */
+	struct buffer_head *bh;
+	char *buffer, *dma_buffer;
+	int size;
+
+	if ( max_sector > max_sector_2 )
+		max_sector = max_sector_2;
+
+	max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors);
+
+	if (current_count_sectors <= 0 && COMMAND == FD_WRITE &&
+	    buffer_max > sector_t + CURRENT->nr_sectors){
+		current_count_sectors = buffer_max - sector_t;
+		if ( current_count_sectors > CURRENT->nr_sectors )
+			current_count_sectors = CURRENT->nr_sectors;
+	}
+	remaining = current_count_sectors << 9;
+#ifdef SANITY
+	if ((remaining >> 9) > CURRENT->nr_sectors  && COMMAND == 0xc5 ){
+		printk(DEVICE_NAME ": in copy buffer\n");
+		printk("current_count_sectors=%ld\n", current_count_sectors);
+		printk("remaining=%d\n", remaining >> 9);
+		printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
+		printk("CURRENT->current_nr_sectors=%ld\n",
+		       CURRENT->current_nr_sectors);
+		printk("max_sector=%d\n", max_sector);
+		printk("ssize=%d\n", ssize);
+	}
+#endif
+
+	if ( max_sector > buffer_max )
+		buffer_max = max_sector;
+
+	dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
+
+	bh = CURRENT->bh;
+	size = CURRENT->current_nr_sectors << 9;
+	buffer = CURRENT->buffer;
+
+	while ( remaining > 0){
+		if ( size > remaining )
+			size = remaining;
+#ifdef SANITY
+		if (!bh){
+			printk(DEVICE_NAME
+			       ": bh=null in copy buffer before copy\n");
+			break;
+		}
+		if (dma_buffer + size >
+		    floppy_track_buffer + ( 2 * MAX_BUFFER_SECTORS << 9 ) ||
+		    dma_buffer < floppy_track_buffer ){
+			printk(DEVICE_NAME
+			       ": buffer overrun in copy buffer %d\n",
+			       (floppy_track_buffer - dma_buffer) >>9);
+			printk("sector_t=%d buffer_min=%d\n",
+			       sector_t, buffer_min);
+			printk("current_count_sectors=%ld\n",
+			       current_count_sectors);
+			if ( COMMAND == FD_READ )
+				printk("read\n");
+			if ( COMMAND == FD_READ )
+				printk("write\n");
+			break;
+		}
+		if ( ((int)buffer) % 512 )
+			printk(DEVICE_NAME ": %p buffer not aligned\n", buffer);
+#endif
+		if ( COMMAND == FD_READ )
+			memcpy( buffer, dma_buffer, size);
+		else
+			memcpy( dma_buffer, buffer, size);
+		remaining -= size;
+		if ( !remaining)
+			break;
+
+		dma_buffer += size;
+		bh = bh->b_reqnext;
+#ifdef SANITY
+		if ( !bh){
+			printk(DEVICE_NAME
+			       ": bh=null in copy buffer after copy\n");
+			break;
+		}
+#endif
+		size = bh->b_size;
+		buffer = bh->b_data;
+	}
+#ifdef SANITY
+	if ( remaining ){
+		if ( remaining > 0 )
+			max_sector -= remaining >> 9;
+		printk(DEVICE_NAME
+		       ": weirdness: remaining %d\n", remaining>>9);
+	}
+#endif
+}
+
+/*
+ * Formulate a read/write request.
+ * this routine decides where to load the data (directly to buffer, or to
+ * tmp floppy area), how much data to load (the size of the buffer, the whole
+ * track, or a single sector)
+ * All floppy_track_buffer handling goes in here. If we ever add track buffer
+ * allocation on the fly, it should be done here. No other part should need
+ * modification.
+ */
+
+static int make_raw_rw_request(void)
+{
+	int aligned_sector_t;
+	int max_sector, max_size, tracksize, ssize;
+
+	current_drive = DRIVE(CURRENT->dev);
+
+	raw_cmd.flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+		FD_RAW_NEED_SEEK;
+	raw_cmd.cmd_count = NR_RW;
+	if (CURRENT->cmd == READ){
+		raw_cmd.flags |= FD_RAW_READ;
+		COMMAND = FD_READ;
+	} else if (CURRENT->cmd == WRITE){
+		raw_cmd.flags |= FD_RAW_WRITE;
+		COMMAND = FD_WRITE;
+	} else {
+		printk(DEVICE_NAME
+		       ": make_raw_rw_request: unknown command\n");
+		return 0;
+	}
+
+	max_sector = floppy->sect * floppy->head;
+	TRACK = CURRENT->sector / max_sector;
+	sector_t = CURRENT->sector % max_sector;
+	if ( floppy->track && TRACK >= floppy->track )
+		return 0;
+	HEAD = sector_t / floppy->sect;
+
+	if ( (DRS->flags & FD_NEED_TWADDLE) && sector_t < floppy->sect )
+		max_sector = floppy->sect;
+
+	/* 2M disks have phantom sectors on the first track */
+	if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
+		max_sector = 2 * floppy->sect / 3;
+		if (sector_t >= max_sector){
+			current_count_sectors =  (floppy->sect - sector_t);
+			if ( current_count_sectors > CURRENT->nr_sectors )
+				current_count_sectors = CURRENT->nr_sectors;
+			return 1;
+		}
+		SIZECODE = 2;
+	} else
+		SIZECODE = FD_SIZECODE(floppy);
+	raw_cmd.rate = floppy->rate & 3;
+	if ((floppy->rate & FD_2M) &&
+	    (TRACK || HEAD ) &&
+	    raw_cmd.rate == 2)
+		raw_cmd.rate = 1;
+
+	SIZECODE2 = 0xff;
+	raw_cmd.track = TRACK << floppy->stretch;
+	DR_SELECT = UNIT(current_drive) + ( HEAD << 2 );
+	GAP = floppy->gap;
+	ssize = 1 << (SIZECODE - 2 );
+	SECT_PER_TRACK = floppy->sect / ssize;
+	SECTOR = (sector_t % floppy->sect) / ssize + 1;
+	tracksize = floppy->sect - floppy->sect % ssize;
+	if ( tracksize < floppy->sect ){
+		SECT_PER_TRACK ++;
+		if (  tracksize <= sector_t % floppy->sect)
+			SECTOR--;
+		while ( tracksize <= sector_t % floppy->sect){
+			while( tracksize + ssize > floppy->sect ){
+				SIZECODE--;
+				ssize >>= 1;
+			}
+			SECTOR++; SECT_PER_TRACK ++;
+			tracksize += ssize;
+		}
+		max_sector = HEAD * floppy->sect + tracksize;
+	} else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing)
+		max_sector = floppy->sect;
+
+	aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize;
+	max_size = CURRENT->nr_sectors;
+	if ((raw_cmd.track == buffer_track) && (current_drive == buffer_drive) &&
+	    (sector_t >= buffer_min) && (sector_t < buffer_max)) {
+		/* data already in track buffer */
+		if (COMMAND == FD_READ) {
+			copy_buffer(1, max_sector, buffer_max);
+			return 1;
+		}
+	} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+		if (COMMAND == FD_WRITE){
+			if(sector_t + CURRENT->nr_sectors > ssize &&
+			   sector_t + CURRENT->nr_sectors < ssize + ssize)
+				max_size = ssize + ssize;
+			else
+				max_size = ssize;
+		}
+		raw_cmd.flags &= ~FD_RAW_WRITE;
+		raw_cmd.flags |= FD_RAW_READ;
+		COMMAND = FD_READ;
+	} else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+		int direct, indirect;
+
+		indirect= transfer_size(ssize,max_sector,MAX_BUFFER_SECTORS*2) -
+			sector_t;
+
+		max_size = buffer_chain_size();
+		if ( max_size > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
+			max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
+		/* 64 kb boundaries */
+		if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
+		     ((long) CURRENT->buffer ) / K_64 )
+			max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
+		direct = transfer_size(ssize,max_sector,max_size) - sector_t;
+		/*
+		 * We try to read tracks, but if we get too many errors, we
+		 * go back to reading just one sector at a time.
+		 *
+		 * This means we should be able to read a sector even if there
+		 * are other bad sectors on this track.
+		 */
+		if ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
+		    *errors < DP->max_errors.read_track &&
+		    /*!(DRS->flags & FD_NEED_TWADDLE) &&*/
+		    ( ( !probing || (DP->read_track &
+			   (1 <<DRS->probed_format))))){
+			max_size = CURRENT->nr_sectors;
+		} else {
+			current_addr = CURRENT->buffer;
+			raw_cmd.length = current_count_sectors << 9;
+			return 2;
+		}
+	}
+
+	if ( COMMAND == FD_READ )
+		max_size = max_sector; /* unbounded */
+
+	/* claim buffer track if needed */
+	if (buffer_track != raw_cmd.track ||  /* bad track */
+	    buffer_drive !=current_drive || /* bad drive */
+	    sector_t < buffer_min ||
+	    ((COMMAND == FD_READ ||
+	      (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
+	     max_sector > 2 * MAX_BUFFER_SECTORS + buffer_min &&
+	     max_size + sector_t > 2 * MAX_BUFFER_SECTORS + buffer_min)
+	    /* not enough space */ ){
+		buffer_track = -1;
+		buffer_drive = current_drive;
+		buffer_max = buffer_min = aligned_sector_t;
+	}
+	current_addr = floppy_track_buffer +((aligned_sector_t-buffer_min )<<9);
 
-    /* allow for about 30ms for data transport per track */
-    head_shift  = floppy->sect / 6;
-    /* a ``cylinder'' is two tracks plus a little stepping time */
-    track_shift = 2 * head_shift + 1; 
-    /* count backwards */
-    total_shift = floppy->sect - 
-	((track_shift * track + head_shift * head) % floppy->sect);
-
-    /* XXX: should do a check to see this fits in tmp_floppy_area!! */
-    for (count = 0; count < floppy->sect; count++) {
-	*here++ = track;
-	*here++ = head;
-	*here++ = 1 + (( count + total_shift ) % floppy->sect);
-	*here++ = 2; /* 512 bytes */
-    }
+	if ( COMMAND == FD_WRITE ){
+		/* copy write buffer to track buffer.
+		 * if we get here, we know that the write
+		 * is either aligned or the data already in the buffer
+		 * (buffer will be overwritten) */
+#ifdef SANITY
+		if (sector_t != aligned_sector_t && buffer_track == -1 )
+			printk(DEVICE_NAME
+			       ": internal error offset !=0 on write\n");
+#endif
+		buffer_track = raw_cmd.track;
+		buffer_drive = current_drive;
+		copy_buffer(ssize, max_sector, 2*MAX_BUFFER_SECTORS+buffer_min);
+	} else
+		transfer_size(ssize, max_sector,
+			      2*MAX_BUFFER_SECTORS+buffer_min-aligned_sector_t);
+
+	/* round up current_count_sectors to get dma xfer size */
+	raw_cmd.length = sector_t+current_count_sectors-aligned_sector_t;
+	raw_cmd.length = ((raw_cmd.length -1)|(ssize-1))+1;
+	raw_cmd.length <<= 9;
+#ifdef SANITY
+	if ((raw_cmd.length < current_count_sectors << 9) ||
+	    (current_addr != CURRENT->buffer && COMMAND == FD_WRITE &&
+	     (aligned_sector_t + (raw_cmd.length >> 9) > buffer_max ||
+	      aligned_sector_t < buffer_min )) ||
+	    raw_cmd.length % ( 512 << ( SIZECODE -2 )) ||
+	    raw_cmd.length <= 0 || current_count_sectors <= 0){
+		printk(DEVICE_NAME ": fractionary current count b=%lx s=%lx\n",
+		       raw_cmd.length, current_count_sectors);
+		if ( current_addr != CURRENT->buffer )
+			printk("addr=%d, length=%ld\n",
+			       (current_addr - floppy_track_buffer ) >> 9,
+			       current_count_sectors);
+		printk("st=%d ast=%d mse=%d msi=%d\n",
+		       sector_t, aligned_sector_t, max_sector, max_size);
+		printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
+		printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
+		       COMMAND, SECTOR, HEAD, TRACK);
+		printk("buffer drive=%d\n", buffer_drive);
+		printk("buffer track=%d\n", buffer_track);
+		printk("buffer_min=%d\n", buffer_min );
+		printk("buffer_max=%d\n", buffer_max );
+		return 0;
+	}
+
+	if (current_addr != CURRENT->buffer ){
+		if (current_addr < floppy_track_buffer ||
+		    current_count_sectors < 0 ||
+		    raw_cmd.length < 0 ||
+		    current_addr + raw_cmd.length >
+		    floppy_track_buffer + ( 2 * MAX_BUFFER_SECTORS  << 9 )){
+			printk(DEVICE_NAME
+			       ": buffer overrun in schedule dma\n");
+			printk("sector_t=%d buffer_min=%d current_count=%ld\n",
+			       sector_t, buffer_min,
+			       raw_cmd.length >> 9 );
+			printk("current_count_sectors=%ld\n",
+			       current_count_sectors);
+			if ( COMMAND == FD_READ )
+				printk("read\n");
+			if ( COMMAND == FD_READ )
+				printk("write\n");
+			return 0;
+		}
+	} else if (raw_cmd.length > CURRENT->nr_sectors << 9 ||
+		   current_count_sectors > CURRENT->nr_sectors){
+		printk(DEVICE_NAME ": buffer overrun in direct transfer\n");
+		return 0;
+	} else if ( raw_cmd.length < current_count_sectors << 9 ){
+		printk(DEVICE_NAME ": more sectors than bytes\n");
+		printk("bytes=%ld\n", raw_cmd.length >> 9 );
+		printk("sectors=%ld\n", current_count_sectors);
+	}
+#endif
+	return 2;
 }
 
 static void redo_fd_request(void)
 {
-	unsigned int block;
-	char * buffer_area;
+#define REPEAT {request_done(0); continue; }
 	int device;
+	int tmp;
 
 	if (CURRENT && CURRENT->dev < 0) return;
-
-repeat:
-	if (format_status == FORMAT_WAIT)
-		format_status = FORMAT_BUSY;
-	if (format_status != FORMAT_BUSY) {
+	
+	/* hooray, the goto is gone! */
+	while(1){
 		if (!CURRENT) {
-			if (!fdc_busy)
-				printk("FDC access conflict!");
-			fdc_busy = 0;
-			wake_up(&fdc_wait);
 			CLEAR_INTR;
+			unlock_fdc();
 			return;
 		}
 		if (MAJOR(CURRENT->dev) != MAJOR_NR)
-			panic(DEVICE_NAME ": request list destroyed"); \
-		if (CURRENT->bh) {
-			if (!CURRENT->bh->b_lock)
-				panic(DEVICE_NAME ": block not locked");
-		}
-	}
-	seek = 0;
-	device = MINOR(CURRENT_DEVICE);
-	if (device > 3)
-		floppy = (device >> 2) + floppy_type;
-	else { /* Auto-detection */
-		if (!probing) {
-			floppy = current_type[device & 3];
-			if (!floppy) {
-				probing = 1;
-				floppy = base_type[device & 3];
-				if (!floppy) {
-					request_done(0);
-					goto repeat;
+			panic(DEVICE_NAME ": request list destroyed");
+		if (CURRENT->bh && !CURRENT->bh->b_lock)
+			panic(DEVICE_NAME ": block not locked");
+		
+		device = MINOR(CURRENT->dev);
+		set_fdc( DRIVE(device));
+		CHECK_RESET;
+		start_motor();
+		if (( changed_floppies | fake_change) & ( 1 << DRIVE(device))){
+			printk(DEVICE_NAME
+			       ": disk absent or changed during operation\n");
+			REPEAT;
+		}
+		set_floppy(device);
+		if (!floppy) { /* Autodetection */
+			if (!probing){
+				DRS->probed_format = 0;
+				if ( next_valid_format() ){
+					printk(DEVICE_NAME
+					       ": no autodetectable formats\n");
+					floppy = NULL;
+					REPEAT;
 				}
 			}
+			probing = 1;
+			floppy = floppy_type+DP->autodetect[DRS->probed_format];
+		} else
+			probing = 0;
+		errors = & (CURRENT->errors);
+		tmp = make_raw_rw_request();
+		if ( tmp < 2 ){
+			request_done(tmp);
+			continue;
 		}
+		if ( DRS->flags & FD_NEED_TWADDLE )
+			twaddle();
+		floppy_on(current_drive);
+#ifdef DEBUGT
+		debugt("queue fd request");
+#endif
+		return;
 	}
-	if (format_status != FORMAT_BUSY) {
-		if (current_drive != CURRENT_DEV) {
-			current_track = NO_TRACK;
-			current_drive = CURRENT_DEV;
-		}
-		block = CURRENT->sector;
-		if (block+2 > floppy->size) {
-			request_done(0);
-			goto repeat;
-		}
-		sector = block % floppy->sect;
-		block /= floppy->sect;
-		head = block % floppy->head;
-		track = block / floppy->head;
-		seek_track = track << floppy->stretch;
-		if (CURRENT->cmd == READ)
-			command = FD_READ;
-		else if (CURRENT->cmd == WRITE)
-			command = FD_WRITE;
-		else {
-			printk("do_fd_request: unknown command\n");
-			request_done(0);
-			goto repeat;
-		}
-	} else {
-		if (current_drive != (format_req.device & 3))
-			current_track = NO_TRACK;
-		current_drive = format_req.device & 3;
-		if (((unsigned) format_req.track) >= floppy->track ||
-		    (format_req.head & 0xfffe) || probing) {
-			request_done(0);
-			goto repeat;
-		}
-		head = format_req.head;
-		track = format_req.track;
-		seek_track = track << floppy->stretch;
-		if (seek_track == buffer_track) buffer_track = -1;
-		command = FD_FORMAT;
-		setup_format_params();
+#undef REPEAT
+}
+
+static struct cont_t rw_cont={
+	rw_interrupt,
+	redo_fd_request,
+	bad_flp_intr,
+	request_done };
+
+void do_fd_request(void)
+{
+	lock_fdc(-1);
+	cont = &rw_cont;
+	redo_fd_request();
+}
+
+/*
+ * User triggered reset
+ * ====================
+ */
+
+static void reset_intr(void)
+{
+	printk("weird, reset interrupt called\n");
+}
+
+static struct cont_t reset_cont={
+	reset_intr,
+	success_and_wakeup,
+	generic_failure,
+	generic_done };
+
+static int user_reset_fdc(int drive, int arg)
+{
+	int result;
+
+	result=0;
+	lock_fdc(drive);
+	switch(arg){
+	case FD_RESET_ALWAYS:
+		FDCS->reset=1;
+		break;
+	case FD_RESET_IF_RAWCMD:
+		if(FDCS->rawcmd == 2 )
+			reset_fdc_info(1);
+		break;
 	}
-	timer_table[FLOPPY_TIMER].expires = jiffies+10*HZ;
-	timer_active |= 1 << FLOPPY_TIMER;
-	if ((seek_track == buffer_track) &&
-	 (current_drive == buffer_drive)) {
-		buffer_area = floppy_track_buffer +
-			((sector + head*floppy->sect)<<9);
-		if (command == FD_READ) {
-			copy_buffer(buffer_area,CURRENT->buffer);
-			request_done(1);
-			goto repeat;
-		} else if (command == FD_WRITE)
-			copy_buffer(CURRENT->buffer,buffer_area);
-	}
-	if (seek_track != current_track)
-		seek = 1;
-	sector++;
-	del_timer(motor_off_timer + current_drive);
+	if ( FDCS->reset ){
+		cont = &reset_cont;
+		timer_table[FLOPPY_TIMER].expires = jiffies + 5;
+		timer_active |= 1 << FLOPPY_TIMER;
+		reset_fdc();
+		result=wait_til_done();
+	}
+	unlock_fdc();
+	return result;
+}
+
+/*
+ * Misc Ioctl's and support
+ * ========================
+ */
+static int fd_copyout(void *param, volatile void *address, int size)
+{
+	int i;
+
+	i = verify_area(VERIFY_WRITE,param,size);
+	if (i)
+		return i;
+	memcpy_tofs(param,(void *) address, size);
+	return 0;
+}
+#define COPYOUT(x) (fd_copyout( (void *)param, &(x), sizeof(x)))
+#define COPYIN(x) (memcpy_fromfs( &(x), (void *) param, sizeof(x)),0)
+
+static void poll_drive(int drive)
+{
+	lock_fdc(drive);
+	start_motor();
+	unlock_fdc();
+}
+
+static char *drive_name(int type, int drive )
+{
+	struct floppy_struct *floppy;	
+
+	if ( type )
+		floppy = floppy_type + type;
+	else {
+		if ( UDP->native_format )
+			floppy = floppy_type + UDP->native_format;
+		else
+			return "(null)";
+	}
+	if ( floppy->name )
+		return floppy->name;
+	else
+		return "(null)";
+}
+
+/* raw commands */
+static struct cont_t raw_cmd_cont={
+	success_and_wakeup,
+	failure_and_wakeup,
+	generic_failure,
+	generic_done };
+
+static int raw_cmd_ioctl(int drive, void *param)
+{
+	int i, count, ret;
+
+	if ( FDCS->rawcmd <= 1 )
+		FDCS->rawcmd = 1;
+	for ( i= 0; i < N_DRIVE; i++){
+		if ( FDC(i) != fdc)
+			continue;
+		if ( i == drive ){
+			if ( drive_state[i].fd_ref > 1 )
+				return -EBUSY;
+		} else if ( drive_state[i].fd_ref )
+			return -EBUSY;
+	}
+
+	if(FDCS->reset)
+		return -EIO;
+
+	COPYIN(raw_cmd);
+	raw_cmd.rate &= 0x03;	
+	count = raw_cmd.length;
+	if ((raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)) &&
+	    count > MAX_BUFFER_SECTORS * 512 * 2 )
+		return -ENOMEM;
+
+	if ( raw_cmd.flags & FD_RAW_WRITE ){
+		i = verify_area(VERIFY_READ, raw_cmd.data, count );
+		if (i)
+			return i;
+		buffer_track = -1;
+		memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count);
+	}
+
+	current_addr = floppy_track_buffer;
+	raw_cmd.flags |= FD_RAW_USER_SUPPLIED;
+	cont =  &raw_cmd_cont;
 	floppy_on(current_drive);
+	ret=wait_til_done();
+
+	if ( !ret && !FDCS->reset ){
+		raw_cmd.reply_count = inr;
+		for( i=0; i< raw_cmd.reply_count; i++)
+			raw_cmd.reply[i] = reply_buffer[i];
+		if ( raw_cmd.flags & ( FD_RAW_READ | FD_RAW_WRITE ))
+			raw_cmd.length = get_dma_residue(FLOPPY_DMA);
+	} else
+		ret = -EIO;
+	DRS->track = NO_TRACK;
+	if ( ret )
+		return ret;
+
+	if ( raw_cmd.flags & FD_RAW_READ ){
+		i=fd_copyout( raw_cmd.data, floppy_track_buffer, count);
+		if (i)
+			return i;
+	}
+
+	return COPYOUT(raw_cmd);
 }
 
-void do_fd_request(void)
+static int invalidate_drive(int rdev)
 {
+	/* invalidate the buffer track to force a reread */
 	cli();
-	while (fdc_busy) sleep_on(&fdc_wait);
-	fdc_busy = 1;
+	fake_change |= 1 << DRIVE(rdev);
 	sti();
-	redo_fd_request();
+	unlock_fdc();
+	check_disk_change(rdev);
+	return 0;
 }
 
 static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
     unsigned long param)
 {
-	int i,drive,cnt,okay;
+#define IOCTL_ALLOWED (filp && (filp->f_mode & 8 ))
+
+	struct floppy_struct newparams;
+	struct format_descr tmp_format_req;
+	int i,device,drive,type,cnt;
 	struct floppy_struct *this_floppy;
+	char *name;
 
+	device = MINOR(inode->i_rdev);
 	switch (cmd) {
-		RO_IOCTLS(inode->i_rdev,param);
+		RO_IOCTLS(device,param);
 	}
-	drive = MINOR(inode->i_rdev);
+	type = TYPE(MINOR(device));
+	drive = DRIVE(MINOR(device));
 	switch (cmd) {
-		case FDFMTBEG:
-			if (!suser())
-				return -EPERM;
-			return 0;
-		case FDFMTEND:
-			if (!suser())
-				return -EPERM;
-			cli();
-			fake_change |= 1 << (drive & 3);
-			sti();
-			drive &= 3;
-			cmd = FDCLRPRM;
-			break;
-		case FDGETPRM:
-			if (drive > 3) this_floppy = &floppy_type[drive >> 2];
-			else if ((this_floppy = current_type[drive & 3]) == NULL)
-				    return -ENODEV;
-			i = verify_area(VERIFY_WRITE,(void *) param,sizeof(struct floppy_struct));
-			if (i)
-				return i;
-			for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++)
-				put_fs_byte(((char *) this_floppy)[cnt],
-				    (char *) param+cnt);
-			return 0;
-		case FDFMTTRK:
-			if (!suser())
+	case FDGETDRVTYP:
+		i=verify_area(VERIFY_WRITE,(void *) param,16);
+		if (i)
+			return i;
+		name = drive_name(type,drive);
+		for ( cnt=0; cnt<16; cnt++){
+			put_fs_byte(name[cnt],
+				    ((char*)param)+cnt);
+			if ( ! *name )
+				break;
+		}
+		return 0;
+	case FDGETMAXERRS:
+		return COPYOUT(UDP->max_errors);
+	case FDGETPRM:
+		if (type)
+			this_floppy = &floppy_type[type];
+		else if ((this_floppy = current_type[drive]) ==
+			 NULL)
+			return -ENODEV;
+		return COPYOUT(this_floppy[0]);
+	case FDPOLLDRVSTAT:
+		poll_drive(drive);
+		/* fall through */
+	case FDGETDRVSTAT:
+		return COPYOUT(*UDRS);
+	case FDGETFDCSTAT:
+		return COPYOUT(*UFDCS);
+	case FDGETDRVPRM:
+		return COPYOUT(*UDP);
+	}
+	if (!IOCTL_ALLOWED)
+		return -EPERM;
+	switch (cmd) {
+	case FDRAWCMD:
+		if (type)
+			return -EINVAL;
+		lock_fdc(drive);
+		set_floppy(device);
+		i = raw_cmd_ioctl(drive, (void *) param);
+		unlock_fdc();
+		return i;
+	case FDFMTTRK:
+		if (UDRS->fd_ref != 1)
+			return -EBUSY;
+		if (! (UDRS->flags & FD_DRIVE_PRESENT ))
+			return -ENXIO;
+		COPYIN(tmp_format_req);
+		return do_format(device, &tmp_format_req);
+	case FDSETMAXERRS:
+		return COPYIN(UDP->max_errors);
+	case FDFMTBEG:
+		return 0;
+	case FDCLRPRM:
+		lock_fdc(drive);
+		current_type[drive] = NULL;
+		floppy_sizes[drive] = 2;
+		UDRS->keep_data = 0;
+		return invalidate_drive(device);
+	case FDFMTEND:
+	case FDFLUSH:
+		lock_fdc(drive);
+		return invalidate_drive(device);
+	case FDSETPRM:
+	case FDDEFPRM:
+		COPYIN(newparams);
+		/* sanity checking for parameters.*/
+		if(newparams.sect <= 0 ||
+		   newparams.head <= 0 ||
+		   newparams.track <= 0 ||
+		   newparams.track >
+		   UDP->tracks>>newparams.stretch)
+			return -EINVAL;
+		if ( type){
+			if ( !suser() )
 				return -EPERM;
-			if (fd_ref[drive & 3] != 1)
-				return -EBUSY;
-			cli();
-			while (format_status != FORMAT_NONE)
-				sleep_on(&format_done);
-			for (cnt = 0; cnt < sizeof(struct format_descr); cnt++)
-				((char *) &format_req)[cnt] = get_fs_byte(
-				    (char *) param+cnt);
-			format_req.device = drive;
-			format_status = FORMAT_WAIT;
-			format_errors = 0;
-			while (format_status != FORMAT_OKAY && format_status !=
-			    FORMAT_ERROR) {
-				if (fdc_busy) sleep_on(&fdc_wait);
-				else {
-					fdc_busy = 1;
-					redo_fd_request();
+			lock_fdc(-1);
+			for ( cnt = 0; cnt < N_DRIVE; cnt++){
+				if (TYPE(drive_state[cnt].fd_device) == type &&
+				    drive_state[cnt].fd_ref){
+					cli(); fake_change |= 1 << cnt; sti();
 				}
 			}
-			while (format_status != FORMAT_OKAY && format_status !=
-			    FORMAT_ERROR)
-				sleep_on(&format_done);
-			sti();
-			okay = format_status == FORMAT_OKAY;
-			format_status = FORMAT_NONE;
-			floppy_off(drive & 3);
-			wake_up(&format_done);
-			return okay ? 0 : -EIO;
-		case FDFLUSH:
-			if (!permission(inode, 2))
-				return -EPERM;
-			cli();
-			fake_change |= 1 << (drive & 3);
-			sti();
-			check_disk_change(inode->i_rdev);
+			floppy_type[type] = newparams;
+			floppy_type[type].name="user format";
+			for (cnt = type << 2 ;
+			     cnt < (type << 2 ) + 4 ;
+			     cnt++)
+				floppy_sizes[cnt]=
+#ifdef HAVE_2_CONTROLLERS
+					floppy_sizes[cnt+0x80]=
+#endif
+						floppy_type[type].size>>1;
+			unlock_fdc();
+			for ( cnt = 0; cnt < N_DRIVE; cnt++){
+				if (TYPE(drive_state[cnt].fd_device) == type &&
+				    drive_state[cnt].fd_ref)
+					check_disk_change(drive_state[cnt].
+							  fd_device);
+			}
 			return 0;
- 	}
-	if (!suser())
+		}
+
+		lock_fdc(drive);
+		if ( cmd != FDDEFPRM )
+			/* notice a disk change immediately, else
+			 * we loose our settings immediately*/
+			start_motor();
+		user_params[drive] = newparams;
+		if (buffer_drive == drive &&
+		    buffer_max > user_params[drive].sect)
+			buffer_max=user_params[drive].sect;
+		current_type[drive] = &user_params[drive];
+		floppy_sizes[drive] = user_params[drive].size >> 1;
+		if (cmd == FDDEFPRM)
+			DRS->keep_data = -1;
+		else
+			DRS->keep_data = 1;
+		/* invalidation. Invalidate only when needed, i.e.
+		 * when there are already sectors in the buffer cache
+		 * whose number will change. This is useful, because
+		 * mtools often changes the geometry of the disk after
+		 * looking at the boot block */
+		if (DRS->maxblock >
+		    user_params[drive].sect ||
+		    DRS->maxtrack )
+			return invalidate_drive(device);
+		else
+			return unlock_fdc();
+	case FDRESET:
+		return user_reset_fdc( drive, (int)param);
+	case FDMSGON:
+		UDP->flags |= FTD_MSG;
+		return 0;
+	case FDMSGOFF:
+		UDP->flags &= ~FTD_MSG;
+		return 0;
+	case FDSETEMSGTRESH:
+		UDP->max_errors.reporting =
+			(unsigned short) (param & 0x0f);
+		return 0;
+	case FDTWADDLE:
+		lock_fdc(drive);
+		twaddle();
+		unlock_fdc();
+	}
+	if ( ! suser() )
 		return -EPERM;
-	if (drive < 0 || drive > 3)
+	switch(cmd){
+	case FDSETDRVPRM:
+		return COPYIN(*UDP);
+	default:
 		return -EINVAL;
-	switch (cmd) {
-		case FDCLRPRM:
-			current_type[drive] = NULL;
-			floppy_sizes[drive] = MAX_DISK_SIZE;
-			keep_data[drive] = 0;
-			break;
-		case FDSETPRM:
-		case FDDEFPRM:
-			memcpy_fromfs(user_params+drive,
-				(void *) param,
-				sizeof(struct floppy_struct));
-			current_type[drive] = &user_params[drive];
-			floppy_sizes[drive] = user_params[drive].size >> 1;
-			if (cmd == FDDEFPRM)
-				keep_data[drive] = -1;
-			else {
-				cli();
-				while (fdc_busy) sleep_on(&fdc_wait);
-				fdc_busy = 1;
-				sti();
-				outb_p((current_DOR & 0xfc) | drive |
-				    (0x10 << drive),FD_DOR);
-				for (cnt = 0; cnt < 1000; cnt++) __asm__("nop");
-				if (inb(FD_DIR) & 0x80)
-					keep_data[drive] = 1;
-				else
-					keep_data[drive] = 0;
-				outb_p(current_DOR,FD_DOR);
-				fdc_busy = 0;
-				wake_up(&fdc_wait);
-			}
-			break;
-		case FDMSGON:
-			ftd_msg[drive] = 1;
-			break;
-		case FDMSGOFF:
-			ftd_msg[drive] = 0;
-			break;
-		case FDSETEMSGTRESH:
-			min_report_error_cnt[drive] = (unsigned short) (param & 0x0f);
-			break;
-		default:
-			return -EINVAL;
 	}
 	return 0;
+#undef IOCTL_ALLOWED
 }
 
 #define CMOS_READ(addr) ({ \
@@ -1356,13 +2573,12 @@
 
 static void set_base_type(int drive,int code)
 {
-	struct floppy_struct *base;
-
-	if (code > 0 && code <= NUMBER(floppy_types)) {
-		base = floppy_types[code-1];
-		printk("fd%d is %s",drive,base->name);
-		base_type[drive] = base;
-		base_type_end[drive] = base + floppy_num_types[code-1];
+	if (code > 0 && code <= NUMBER(default_drive_params)) {
+		memcpy((char *) UDP,
+		       (char *) (&default_drive_params[code].params),
+		       sizeof( struct floppy_drive_params ));
+		printk("fd%d is %s", drive, default_drive_params[code].name);
+		return;
 	} else if (!code)
 		printk("fd%d is not installed", drive);
 	else
@@ -1371,6 +2587,13 @@
 
 static void config_types(void)
 {
+	int drive;
+
+	for (drive=0; drive<N_DRIVE ; drive++){
+		/* default type for unidentifiable drives */
+		memcpy((char *) UDP, (char *) (&default_drive_params->params),
+		       sizeof( struct floppy_drive_params ));
+	}
 	printk("Floppy drive(s): ");
 	set_base_type(0, (CMOS_READ(0x10) >> 4) & 15);
 	if (CMOS_READ(0x10) & 15) {
@@ -1380,14 +2603,52 @@
 	printk("\n");
 }
 
+
+static void maybe_check_change(int device)
+{
+	register int drive;
+
+	drive = DRIVE(device);
+	if (UDRS->last_checked + UDP->checkfreq < jiffies  ||
+	    UDRS->flags & FD_VERIFY ||
+	    (( changed_floppies | fake_change ) & ( 1 << drive)))
+		check_disk_change(device);
+}
+
+int floppy_is_wp( int minor)
+{
+	maybe_check_change(minor + (MAJOR_NR << 8));
+	return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE );
+}
+
+
+#define WRAPPER(op) \
+static int floppy_##op(struct inode * inode, struct file * filp, \
+		     char * buf, int count) \
+{ \
+	maybe_check_change(inode->i_rdev);	\
+	if ( changed_floppies & ( 1 << DRIVE(inode->i_rdev) )) \
+		return -ENXIO; \
+	return block_##op(inode, filp, buf, count); \
+}
+
+WRAPPER(read)
+WRAPPER(write)
+
+static int exclusive = 0;
 static void floppy_release(struct inode * inode, struct file * filp)
 {
+	int drive= DRIVE(inode->i_rdev);
+
 	fsync_dev(inode->i_rdev);
-	if (!fd_ref[inode->i_rdev & 3]--) {
-		printk("floppy_release with fd_ref == 0");
-		fd_ref[inode->i_rdev & 3] = 0;
+	if ( UDRS->fd_ref < 0)
+		UDRS->fd_ref=0;
+	else if (!UDRS->fd_ref--) {
+		printk(DEVICE_NAME ": floppy_release with fd_ref == 0");
+		UDRS->fd_ref = 0;
 	}
 	floppy_release_irq_and_dma();
+	exclusive=0;
 }
 
 /*
@@ -1395,51 +2656,171 @@
  * /dev/PS0 etc), and disallows simultaneous access to the same
  * drive with different device numbers.
  */
+#define RETERR(x) \
+	do{floppy_release(inode,filp); \
+	   return -(x);}while(0)
+static int usage_count = 0;
 static int floppy_open(struct inode * inode, struct file * filp)
 {
 	int drive;
 	int old_dev;
 
-	drive = inode->i_rdev & 3;
-	old_dev = fd_device[drive];
-	if (fd_ref[drive])
-		if (old_dev != inode->i_rdev)
-			return -EBUSY;
+	if (exclusive)
+		return -EBUSY;
+	if ( !filp ){
+		printk(DEVICE_NAME ": Weird, open called with filp=0\n");
+		return -EIO;
+	}
+
+	drive = DRIVE(inode->i_rdev);
+	if ( drive >= N_DRIVE )
+		return -ENXIO;
+
+	if (command_status == FD_COMMAND_DETECT && drive >= current_drive){
+		lock_fdc(-1);
+		unlock_fdc();
+	}
+
+	if ( TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+	  return -ENXIO;
+
+	if (filp->f_mode & 3){
+		if ( !(UDRS->flags & FD_DRIVE_PRESENT))
+			return -ENXIO;
+	}
+
+	old_dev = UDRS->fd_device;
+	if (UDRS->fd_ref && old_dev != inode->i_rdev)
+		return -EBUSY;
 	if (floppy_grab_irq_and_dma())
 		return -EBUSY;
-	fd_ref[drive]++;
-	fd_device[drive] = inode->i_rdev;
-	buffer_drive = buffer_track = -1;
-	if (old_dev && old_dev != inode->i_rdev)
+
+	if (filp->f_flags & O_EXCL ){
+		if (usage_count>1){
+			floppy_release_irq_and_dma();
+			return -EBUSY;
+		}else
+			exclusive=1;
+	}
+
+	/* filp->f_op is NULL if the disk is mounted, and non-NULL if opened */
+	if (filp->f_op){
+		if (UDRS->fd_ref == -1)
+			return -EBUSY;
+		UDRS->fd_ref++;
+		UDRS->fd_device = inode->i_rdev;
+	} else {
+		if (UDRS->fd_ref ){
+			floppy_release_irq_and_dma();
+			return -EBUSY;
+		}
+		UDRS->fd_ref=-1;
+	}
+
+	if (old_dev && old_dev != inode->i_rdev){
+		if ( buffer_drive == drive )
+			buffer_track = -1;
 		invalidate_buffers(old_dev);
-	if (filp && filp->f_mode) {
+	}
+
+	if (filp->f_mode & 2 || permission(inode,2))
+		filp->f_mode |= 8; /* keep mtools working */
+
+	if (UFDCS->rawcmd == 1)
+	       UFDCS->rawcmd = 2;
+
+	if (filp->f_flags & O_NDELAY)
+		return 0;
+	if (filp->f_mode && !(UDRS->flags & FD_DRIVE_PRESENT))
+		RETERR(ENXIO);
+	if(user_reset_fdc(drive, FD_RESET_IF_NEEDED))
+		RETERR(EIO);
+
+	if (filp->f_mode & 3){
 		check_disk_change(inode->i_rdev);
-		if (filp->f_mode & 2) {
-			if (1 & (read_only >> drive)) {
-				floppy_release(inode, filp);
-				return -EROFS;
-			}
+		if ( changed_floppies & ( 1 << drive )){
+			if ( suser() && filp->f_op)
+				/* prevent dosemu from crashing */
+				filp->f_mode &= ~3;
+			else
+				RETERR(ENXIO);
 		}
 	}
+	if ((filp->f_mode & 2) && UDRS->flags < FD_DISK_WRITABLE){
+		if ( suser() && filp->f_op)
+			/* prevent dosemu from crashing */
+			filp->f_mode &= ~2;
+		else
+			RETERR(EROFS);
+	}
 	return 0;
+#undef RETERR
+}
+
+/*
+ * Acknowledge disk change
+ */
+static int ack_change(int drive)
+{
+	unsigned int mask = 1 << drive;
+	UDRS->maxblock = 0;
+	UDRS->maxtrack = 0;
+	if ( buffer_drive == drive )
+		buffer_track = -1;
+	fake_change &= ~mask;
+	changed_floppies &= ~mask;
+	return 1;
 }
 
+/*
+ * Check if the disk has been changed or if a change has been faked.
+ */
 static int check_floppy_change(dev_t dev)
 {
-	int i;
+	int drive = DRIVE( dev );
+	unsigned int mask = 1 << drive;
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		printk(DEVICE_NAME ": floppy_changed: not a floppy\n");
+		return 0;
+	}
+
+	if (fake_change & mask)
+		return ack_change(drive);
+
+	if ((UDRS->flags & FD_VERIFY ) || (changed_floppies & mask) ||
+	    UDRS->last_checked + UDP->checkfreq <
+	    jiffies){
+		user_reset_fdc(drive, FD_RESET_IF_NEEDED);
+		poll_drive(drive);
+		if (changed_floppies & mask){
+			UDRS->generation++;
+			return ack_change(drive);
+		}
+	}
+	return 0;
+}
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0) */
+static int floppy_revalidate(dev_t dev)
+{
 	struct buffer_head * bh;
 
-	if (!(bh = getblk(dev,0,1024)))
+	if ( TYPE(dev) || current_type[DRIVE(dev)] )
 		return 0;
-	i = floppy_change(bh);
+	if (!(bh = getblk(dev,0,1024)))
+		return 1;
+	if ( bh && ! bh->b_uptodate)
+		ll_rw_block(READ, 1, &bh);
 	brelse(bh);
-	return i;
+	return 0;
 }
 
 static struct file_operations floppy_fops = {
 	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
+	floppy_read,		/* read - general block-dev read */
+	floppy_write,		/* write - general block-dev write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select */
 	fd_ioctl,		/* ioctl */
@@ -1449,20 +2830,88 @@
 	block_fsync,		/* fsync */
 	NULL,			/* fasync */
 	check_floppy_change,	/* media_change */
-	NULL			/* revalidate */
+	floppy_revalidate,	/* revalidate */
 };
 
-
-static void floppy_interrupt(int unused)
-{
-	void (*handler)(void) = DEVICE_INTR;
+/*
+ * Floppy Driver initialisation
+ * =============================
+ */
 
-	DEVICE_INTR = NULL;
-	if (!handler)
-		handler = unexpected_floppy_interrupt;
-	handler();
+/* Determine the floppy disk controller type */
+/* This routine was written by David C. Niemi */
+static char get_fdc_version(void)
+{
+	int r;
+
+	output_byte(FD_DUMPREGS);	/* 82072 and better know DUMPREGS */
+	if ( FDCS->reset )
+		return FDC_NONE;
+	if ( (r = result()) <= 0x00)
+		return FDC_NONE;	/* No FDC present ??? */
+	if ((r==1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a 8272A\n",fdc);
+		return FDC_8272A;		/* 8272a/765 don't know DUMPREGS */
+	}
+	if (r != 10) {
+		printk("FDC init: DUMPREGS: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	output_byte(FD_VERSION);
+	r = result();
+	if ((r == 1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a 82072\n",fdc);
+		return FDC_82072;		/* 82072 doesn't know VERSION */
+	}
+	if ((r != 1) || (reply_buffer[0] != 0x90)) {
+		printk("FDC init: VERSION: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	output_byte(FD_UNLOCK);
+	r = result();
+	if ((r == 1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a pre-1991 82077\n", fdc);
+		return FDC_82077_ORIG;	/* Pre-1991 82077 doesn't know LOCK/UNLOCK */
+	}
+	if ((r != 1) || (reply_buffer[0] != 0x00)) {
+		printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	printk("FDC %d is a post-1991 82077\n",fdc);
+	return FDC_82077;	/* Revised 82077AA passes all the tests */
+} /* fdc_init */
+
+/*
+ * Drive detection routine. This runs in the background while the kernel
+ * does other (non floppy related) initialisation work.
+ */
+static void detect_interrupt(void)
+{
+	if ( DRS->track == 0 )
+		DRS->flags |= FD_DRIVE_PRESENT | FD_VERIFY;
+	else if ( DRS->track != NEED_1_RECAL ){
+		floppy_ready();
+		return;
+	}
+	floppy_off(current_drive);
+	current_drive++;
+	if (current_drive == N_DRIVE ||
+	    fdc_state[FDC(current_drive)].version == FDC_NONE){
+		set_fdc(0);
+		unlock_fdc();
+		floppy_release_irq_and_dma();
+		return;
+	}
+	set_fdc(current_drive);
+	floppy_ready(); /* next */
 }
 
+static struct cont_t detect_cont={
+	detect_interrupt,
+	detect_interrupt,
+	empty,
+	(done_f) empty };
+
 /*
  * This is the floppy IRQ description. The SA_INTERRUPT in sa_flags
  * means we run the IRQ-handler with interrupts disabled.
@@ -1476,51 +2925,88 @@
 
 void floppy_init(void)
 {
-	outb(current_DOR,FD_DOR);
+	int i;
+
 	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
 		printk("Unable to get major %d for floppy\n",MAJOR_NR);
 		return;
 	}
+
+	for(i=0; i<256; i++)
+		if ( TYPE(i))
+			floppy_sizes[i] = floppy_type[TYPE(i)].size >> 1;
+		else
+			floppy_sizes[i] = MAX_DISK_SIZE;
+
 	blk_size[MAJOR_NR] = floppy_sizes;
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 	timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
 	timer_active &= ~(1 << FLOPPY_TIMER);
 	config_types();
-	/* Try to determine the floppy controller type */
-	output_byte(FD_VERSION);	/* get FDC version code */
-	if (result() != 1) {
-		printk(DEVICE_NAME ": FDC failed to return version byte\n");
-		fdc_version = FDC_TYPE_STD;
-	} else
-		fdc_version = reply_buffer[0];
-	if (fdc_version != FDC_TYPE_STD) 
-		printk(DEVICE_NAME ": FDC version 0x%x\n", fdc_version);
-#ifndef FDC_FIFO_UNTESTED
-	fdc_version = FDC_TYPE_STD;	/* force std fdc type; can't test other. */
-#endif
 
-	/* Not all FDCs seem to be able to handle the version command
-	 * 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();
-	}
+	fdc_state[0].address = 0x3f0;
+	fdc_state[1].address = 0x370;
+	for(fdc = 0 ; fdc < N_FDC; fdc++){
+		FDCS->dtr = -1;
+		FDCS->dor = 0;
+		FDCS->reset = 0;
+		FDCS->version = FDC_NONE;
+		set_dor(fdc, ~0, 0xc );
+	}
+
+	/* initialise drive state */
+	for ( current_drive=0; current_drive < N_DRIVE ; current_drive++){
+		DRS->flags = 0;
+		DRS->generation = 0;
+		DRS->keep_data = 0;
+		DRS->fd_ref = 0;
+		DRS->fd_device = 0;
+	}
+
+	floppy_grab_irq_and_dma();
+	for(fdc = 0 ; fdc < N_FDC; fdc++){
+		FDCS->rawcmd = 2;
+		if(user_reset_fdc(-1,FD_RESET_IF_NEEDED))
+			continue;
+		/* Try to determine the floppy controller type */
+		FDCS->version = get_fdc_version();
+		if (FDCS->version == FDC_NONE)
+			continue;
+
+		/* Not all FDCs seem to be able to handle the version command
+		 * properly, so force a reset for the standard FDC clones,
+		 * to avoid interrupt garbage.
+		 */
+		FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
+		user_reset_fdc(-1,FD_RESET_ALWAYS);
+	}
+	fdc=0;
+	current_drive = 0;
+	lock_fdc(-1);
+	initialising =0;
+	command_status = FD_COMMAND_DETECT;
+	cont = &detect_cont;
+	raw_cmd.cmd_count = 0;
+	raw_cmd.flags = FD_RAW_NEED_SEEK;
+	raw_cmd.track = 0;
+	floppy_ready();
+	cli();
 }
 
-static int usage_count = 0;
-
 int floppy_grab_irq_and_dma(void)
 {
 	if (usage_count++)
 		return 0;
 	if (irqaction(FLOPPY_IRQ,&floppy_sigaction)) {
-		printk("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ);
+		printk(DEVICE_NAME
+		       ": 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);
+		printk(DEVICE_NAME
+		       ": Unable to grab DMA%d for the floppy driver\n",
+		       FLOPPY_DMA);
 		free_irq(FLOPPY_IRQ);
 		return -1;
 	}
@@ -1536,4 +3022,10 @@
 	free_dma(FLOPPY_DMA);
 	disable_irq(FLOPPY_IRQ);
 	free_irq(FLOPPY_IRQ);
+#ifdef HAVE_2_CONTROLLERS
+	/* switch on first controller.
+	 * This saves us trouble on the next reboot. */
+	set_dor(0, ~0, 8 );
+	set_dor(1, ~8, 0 );
+#endif
 }
diff -u --recursive --new-file v1.1.40/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v1.1.40/linux/drivers/block/hd.c	Sat Aug  6 21:32:06 1994
+++ linux/drivers/block/hd.c	Sun Aug  7 00:28:07 1994
@@ -73,10 +73,6 @@
 static int reset = 0;
 static int hd_error = 0;
 
-#if (HD_DELAY > 0)
-unsigned long last_req, read_timer();
-#endif
-
 /*
  *  This struct defines the HD's and their types.
  */
@@ -100,17 +96,20 @@
 static int hd_blocksizes[MAX_HD<<6] = {0, };
 
 #if (HD_DELAY > 0)
+unsigned long last_req;
+
 unsigned long read_timer(void)
 {
-	unsigned long t;
+	unsigned long t, flags;
 	int i;
 
+	save_flags(flags);
 	cli();
 	t = jiffies * 11932;
     	outb_p(0, 0x43);
 	i = inb_p(0x40);
 	i |= inb(0x40) << 8;
-	sti();
+	restore_flags(flags);
 	return(t - i);
 }
 #endif
@@ -287,6 +286,9 @@
 		insw(HD_DATA,(char *)&id,sizeof(id)/2);
 		insw(HD_DATA,(char *)&id,sizeof(id)/2);
 	}
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
 	hd_request();
 	return;
 }
@@ -307,6 +309,9 @@
 		else
 			printk ("  hd%c: disabled multiple mode\n", dev+'a');
 	}
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
 	hd_request();
 	return;
 }
@@ -425,7 +430,6 @@
 
 	if (unmask_intr[dev])
 		sti();			/* permit other IRQs during xfer */
-read_next:
 	do {
 		i = (unsigned) inb_p(HD_STATUS);
 		if (i & BUSY_STAT)
@@ -442,7 +446,6 @@
 		printk("hd%c: read_intr: error = 0x%02x\n",dev+'a',hd_error);
 	}
 	bad_rw_intr();
-	cli();
 	hd_request();
 	return;
 ok_to_read:
@@ -467,7 +470,7 @@
 		end_request(1);
 	if (i > 0) {
 		if (msect)
-			goto read_next;
+			goto ok_to_read;
 		SET_INTR(&read_intr);
 		return;
 	}
@@ -535,7 +538,6 @@
 		printk("hd:%c multwrite_intr: error = 0x%02x\n",dev+'a',hd_error);
 	}
 	bad_rw_intr();
-	cli();
 	hd_request();
 }
 
@@ -544,6 +546,8 @@
 	int i;
 	int retries = 100000;
 
+	if (unmask_intr[DEVICE_NR(WCURRENT.dev)])
+		sti();
 	do {
 		i = (unsigned) inb_p(HD_STATUS);
 		if (i & BUSY_STAT)
@@ -560,7 +564,6 @@
 		printk("HD: write_intr: error = 0x%02x\n",hd_error);
 	}
 	bad_rw_intr();
-	cli();
 	hd_request();
 	return;
 ok_to_write:
@@ -587,6 +590,9 @@
 {
 	if (win_result())
 		bad_rw_intr();
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
 	hd_request();
 }
 
@@ -1067,17 +1073,19 @@
 	int max_p;
 	int start;
 	int i;
+	long flags;
 
 	target =  DEVICE_NR(MINOR(dev));
 	gdev = &GENDISK_STRUCT;
 
+	save_flags(flags);
 	cli();
 	if (DEVICE_BUSY || USAGE > maxusage) {
-		sti();
+		restore_flags(flags);
 		return -EBUSY;
 	};
 	DEVICE_BUSY = 1;
-	sti();
+	restore_flags(flags);
 
 	max_p = gdev->max_p;
 	start = target << gdev->minor_shift;
diff -u --recursive --new-file v1.1.40/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.1.40/linux/drivers/block/ll_rw_blk.c	Mon Jul 18 14:48:28 1994
+++ linux/drivers/block/ll_rw_blk.c	Sun Aug  7 00:46:57 1994
@@ -125,6 +125,7 @@
 
 	major = MAJOR(dev);
 	minor = MINOR(dev);
+	if ( major == FLOPPY_MAJOR && floppy_is_wp( minor) ) return 1;
 	if (major < 0 || major >= MAX_BLKDEV) return 0;
 	return ro_bits[major][minor >> 5] & (1 << (minor & 31));
 }
@@ -225,6 +226,7 @@
  * to add links to the top entry for scsi devices.
  */
 	if ((major == HD_MAJOR
+	     || major == FLOPPY_MAJOR
 	     || major == SCSI_DISK_MAJOR
 	     || major == SCSI_CDROM_MAJOR)
 	    && (req = blk_dev[major].current_request))
diff -u --recursive --new-file v1.1.40/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v1.1.40/linux/drivers/char/n_tty.c	Sat Jul  9 16:30:46 1994
+++ linux/drivers/char/n_tty.c	Sun Aug  7 00:28:44 1994
@@ -111,7 +111,7 @@
 			if (O_ONLCR(tty)) {
 				if (space < 2)
 					return -1;
-				tty->driver.write(tty, 0, "\r", 1);
+				tty->driver.put_char(tty, '\r');
 				tty->column = 0;
 			}
 			tty->canon_column = tty->column;
diff -u --recursive --new-file v1.1.40/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v1.1.40/linux/drivers/char/pty.c	Wed Jun 22 14:57:08 1994
+++ linux/drivers/char/pty.c	Sun Aug  7 00:28:44 1994
@@ -200,7 +200,7 @@
 	pty_driver.init_termios = tty_std_termios;
 	pty_driver.init_termios.c_iflag = 0;
 	pty_driver.init_termios.c_oflag = 0;
-	pty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD;
+	pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
 	pty_driver.init_termios.c_lflag = 0;
 	pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
 	pty_driver.refcount = &pty_refcount;
@@ -221,6 +221,7 @@
 	pty_slave_driver.subtype = PTY_TYPE_SLAVE;
 	pty_slave_driver.minor_start = 192;
 	pty_slave_driver.init_termios = tty_std_termios;
+	pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
 	pty_slave_driver.table = ttyp_table;
 	pty_slave_driver.termios = ttyp_termios;
 	pty_slave_driver.termios_locked = ttyp_termios_locked;
diff -u --recursive --new-file v1.1.40/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.1.40/linux/drivers/char/serial.c	Sat Jul  9 16:30:46 1994
+++ linux/drivers/char/serial.c	Sun Aug  7 00:28:44 1994
@@ -69,6 +69,7 @@
 
 #undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
 
 #define _INLINE_ inline
   
@@ -186,6 +187,18 @@
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #endif
 
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
 static inline int serial_paranoia_check(struct async_struct *info,
 					dev_t device, const char *routine)
 {
@@ -283,12 +296,6 @@
 	if (serial_paranoia_check(info, tty->device, "rs_stop"))
 		return;
 	
-	if (info->flags & ASYNC_CLOSING) {
-		tty->stopped = 0;
-		tty->hw_stopped = 0;
-		return;
-	}
-
 	save_flags(flags); cli();
 	if (info->IER & UART_IER_THRI) {
 		info->IER &= ~UART_IER_THRI;
@@ -405,7 +412,8 @@
 			*intr_done = 0;
 		return;
 	}
-	if (!info->xmit_cnt || info->tty->stopped || info->tty->hw_stopped) {
+	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+	    info->tty->hw_stopped) {
 		info->IER &= ~UART_IER_THRI;
 #ifdef CONFIG_SERIAL_NEW_ISR
 		serial_out(info, UART_IER, info->IER);
@@ -417,7 +425,7 @@
 	do {
 		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
 		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-		if (--info->xmit_cnt == 0)
+		if (--info->xmit_cnt <= 0)
 			break;
 	} while (--count > 0);
 	
@@ -430,7 +438,7 @@
 	if (intr_done)
 		*intr_done = 0;
 
-	if (info->xmit_cnt == 0) {
+	if (info->xmit_cnt <= 0) {
 		info->IER &= ~UART_IER_THRI;
 #ifdef CONFIG_SERIAL_NEW_ISR
 		serial_out(info, UART_IER, info->IER);
@@ -462,7 +470,7 @@
 	if (info->flags & ASYNC_CTS_FLOW) {
 		if (info->tty->hw_stopped) {
 			if (status & UART_MSR_CTS) {
-#ifdef SERIAL_DEBUG_INTR
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
 				printk("CTS tx start...");
 #endif
 				info->tty->hw_stopped = 0;
@@ -475,7 +483,7 @@
 			}
 		} else {
 			if (!(status & UART_MSR_CTS)) {
-#ifdef SERIAL_DEBUG_INTR
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
 				printk("CTS tx stop...");
 #endif
 				info->tty->hw_stopped = 1;
@@ -926,6 +934,8 @@
 		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
 		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
 	}
+	if (info->irq == 0)
+		info->MCR = info->MCR_noint;
 	serial_outp(info, UART_MCR, info->MCR);
 	
 	/*
@@ -1035,7 +1045,7 @@
 		free_page((unsigned long) info->xmit_buf);
 		info->xmit_buf = 0;
 	}
-			
+
 	info->IER = 0;
 	serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
 	if (info->flags & ASYNC_FOURPORT) {
@@ -1190,6 +1200,7 @@
 static void rs_put_char(struct tty_struct *tty, unsigned char ch)
 {
 	struct async_struct *info = tty->driver_data;
+	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
 		return;
@@ -1197,12 +1208,16 @@
 	if (!tty || tty->stopped || tty->hw_stopped || !info->xmit_buf)
 		return;
 
-	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
+	save_flags(flags); cli();
+	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
 		return;
+	}
 
 	info->xmit_buf[info->xmit_head++] = ch;
 	info->xmit_head &= SERIAL_XMIT_SIZE-1;
 	info->xmit_cnt++;
+	restore_flags(flags);
 }
 
 static void rs_flush_chars(struct tty_struct *tty)
@@ -1213,7 +1228,7 @@
 	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
 		return;
 
-	if (info->xmit_cnt == 0 || tty->stopped || tty->hw_stopped ||
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
 	    !info->xmit_buf)
 		return;
 
@@ -1233,29 +1248,33 @@
 	if (serial_paranoia_check(info, tty->device, "rs_write"))
 		return 0;
 
-	if (!tty || !info->xmit_buf)
+	if (!tty || !info->xmit_buf || !tmp_buf)
 		return 0;
 	    
+	save_flags(flags);
 	while (1) {
+		cli();		
 		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				   SERIAL_XMIT_SIZE - info->xmit_head));
-		if (!c)
+		if (c <= 0)
 			break;
 
-		if (from_user)
-			memcpy_fromfs(info->xmit_buf + info->xmit_head,
-				      buf, c);
-		else
+		if (from_user) {
+			down(&tmp_buf_sem);
+			memcpy_fromfs(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+			up(&tmp_buf_sem);
+		} else
 			memcpy(info->xmit_buf + info->xmit_head, buf, c);
 		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		cli();
 		info->xmit_cnt += c;
-		sti();
+		restore_flags(flags);
 		buf += c;
 		count -= c;
 		total += c;
 	}
-	save_flags(flags); cli();
 	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
 	    !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
@@ -1268,10 +1287,14 @@
 static int rs_write_room(struct tty_struct *tty)
 {
 	struct async_struct *info = tty->driver_data;
+	int	ret;
 				
 	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
 		return 0;
-	return SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
 }
 
 static int rs_chars_in_buffer(struct tty_struct *tty)
@@ -1770,7 +1793,6 @@
 	if (info->count)
 		return;
 	info->flags |= ASYNC_CLOSING;
-	info->flags &= ~ASYNC_CTS_FLOW;
 	/*
 	 * Save the termios structure, since this port may have
 	 * separate termios for callout and dialin.
@@ -1779,12 +1801,8 @@
 		info->normal_termios = *tty->termios;
 	if (info->flags & ASYNC_CALLOUT_ACTIVE)
 		info->callout_termios = *tty->termios;
-	tty->stopped = 0;		/* Force flush to succeed */
-	tty->hw_stopped = 0;
-	if (info->flags & ASYNC_INITIALIZED) {
-		rs_start(tty);
-		wait_until_sent(tty, 6000); /* 60 seconds timeout */
-	}
+	if (info->flags & ASYNC_INITIALIZED)
+		wait_until_sent(tty, 3000); /* 30 seconds timeout */
 	shutdown(info);
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
@@ -1971,7 +1989,7 @@
 	info = rs_table + line;
 	if (serial_paranoia_check(info, tty->device, "rs_open"))
 		return -ENODEV;
-	
+
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
 	       info->count);
@@ -1979,6 +1997,12 @@
 	info->count++;
 	tty->driver_data = info;
 	info->tty = tty;
+
+	if (!tmp_buf) {
+		tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+		if (!tmp_buf)
+			return -ENOMEM;
+	}
 	
 	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
 		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
diff -u --recursive --new-file v1.1.40/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v1.1.40/linux/drivers/char/tty_ioctl.c	Sun Aug  7 03:30:38 1994
+++ linux/drivers/char/tty_ioctl.c	Sun Aug  7 00:28:44 1994
@@ -31,6 +31,13 @@
 # define	PRINTK(x)	/**/
 #endif
 
+/*
+ * Internal flag options for termios setting behavior
+ */
+#define TERMIOS_FLUSH	1
+#define TERMIOS_WAIT	2
+#define TERMIOS_TERMIO	4
+
 void wait_until_sent(struct tty_struct * tty, int timeout)
 {
 	struct wait_queue wait = { current, NULL };
@@ -79,13 +86,41 @@
 			old->c_cc[i] : termios->c_cc[i];
 }
 
-static int set_termios_2(struct tty_struct * tty, struct termios * termios)
+static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
 {
+	struct termio tmp_termio;
+	struct termios tmp_termios;
 	struct termios old_termios = *tty->termios;
-	int canon_change;
+	int retval, canon_change;
+
+	retval = tty_check_change(tty);
+	if (retval)
+		return retval;
+
+	if (opt & TERMIOS_TERMIO) {
+		tmp_termios = *tty->termios;
+		memcpy_fromfs(&tmp_termio, (struct termio *) arg,
+			      sizeof (struct termio));
+
+#define SET_LOW_BITS(x,y)	((x) = (0xffff0000 & (x)) | (y))
+		SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
+		SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
+		SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
+		SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
+		memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
+#undef SET_LOW_BITS
+	} else
+		memcpy_fromfs(&tmp_termios, (struct termios *) arg,
+			      sizeof (struct termios));
+
+	if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
 
+	if (opt & TERMIOS_WAIT)
+		wait_until_sent(tty, 0);
+
 	cli();
-	*tty->termios = *termios;
+	*tty->termios = tmp_termios;
 	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
 	canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
 	if (canon_change) {
@@ -127,14 +162,6 @@
 	return 0;
 }
 
-static int set_termios(struct tty_struct * tty, struct termios * termios)
-{
-	struct termios tmp_termios;
-
-	memcpy_fromfs(&tmp_termios, termios, sizeof (struct termios));
-	return set_termios_2(tty, &tmp_termios);
-}
-
 static int get_termio(struct tty_struct * tty, struct termio * termio)
 {
 	int i;
@@ -154,27 +181,6 @@
 	return 0;
 }
 
-static int set_termio(struct tty_struct * tty, struct termio * termio)
-{
-	struct termio tmp_termio;
-	struct termios tmp_termios;
-
-	tmp_termios = *tty->termios;
-	memcpy_fromfs(&tmp_termio, termio, sizeof (struct termio));
-
-#define SET_LOW_BITS(x,y)	((x) = (0xffff0000 & (x)) | (y))
-
-	SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
-	SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
-	SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
-	SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
-	memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
-
-#undef SET_LOW_BITS
-
-	return set_termios_2(tty, &tmp_termios);
-}
-
 static unsigned long inq_canon(struct tty_struct * tty)
 {
 	int nr, head, tail;
@@ -199,6 +205,7 @@
 {
 	struct tty_struct * real_tty;
 	int retval;
+	int opt = 0;
 
 	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
 	    tty->driver.subtype == PTY_TYPE_MASTER)
@@ -217,34 +224,19 @@
 				    sizeof (struct termios));
 			return 0;
 		case TCSETSF:
+			opt |= TERMIOS_FLUSH;
 		case TCSETSW:
+			opt |= TERMIOS_WAIT;
 		case TCSETS:
-			retval = tty_check_change(real_tty);
-			if (retval)
-				return retval;
-			if (cmd == TCSETSF || cmd == TCSETSW) {
-				if (cmd == TCSETSF &&
-				    real_tty->ldisc.flush_buffer)
-					real_tty->ldisc.flush_buffer(real_tty);
-				wait_until_sent(real_tty, 0);
-			}
-			return set_termios(real_tty,
-					   (struct termios *) arg);
+			return set_termios(real_tty, arg, opt);
 		case TCGETA:
 			return get_termio(real_tty,(struct termio *) arg);
 		case TCSETAF:
+			opt |= TERMIOS_FLUSH;
 		case TCSETAW:
+			opt |= TERMIOS_WAIT;
 		case TCSETA:
-			retval = tty_check_change(real_tty);
-			if (retval)
-				return retval;
-			if (cmd == TCSETAF || cmd == TCSETAW) {
-				if (cmd == TCSETAF &&
-				    real_tty->ldisc.flush_buffer)
-					real_tty->ldisc.flush_buffer(real_tty);
-				wait_until_sent(real_tty, 0);
-			}
-			return set_termio(real_tty, (struct termio *) arg);
+			return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
 		case TCXONC:
 			retval = tty_check_change(tty);
 			if (retval)
diff -u --recursive --new-file v1.1.40/linux/fs/block_dev.c linux/fs/block_dev.c
--- v1.1.40/linux/fs/block_dev.c	Tue Apr 19 10:53:19 1994
+++ linux/fs/block_dev.c	Sun Aug  7 00:46:57 1994
@@ -32,6 +32,8 @@
 	register char * p;
 
 	dev = inode->i_rdev;
+	if ( is_read_only( inode->i_rdev ))
+		return -EPERM;
 	blocksize = BLOCK_SIZE;
 	if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
 		blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
diff -u --recursive --new-file v1.1.40/linux/fs/super.c linux/fs/super.c
--- v1.1.40/linux/fs/super.c	Sun Aug  7 03:30:42 1994
+++ linux/fs/super.c	Sun Aug  7 00:46:57 1994
@@ -479,6 +479,9 @@
 {
 	int retval;
 	
+	if (!(flags & MS_RDONLY ) && sb->s_dev && is_read_only(sb->s_dev))
+		return -EACCES;
+		/*flags |= MS_RDONLY;*/
 	/* If we are remounting RDONLY, make sure there are no rw files open */
 	if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
 		if (!fs_may_remount_ro(sb->s_dev))
diff -u --recursive --new-file v1.1.40/linux/include/i386/string.h linux/include/i386/string.h
--- v1.1.40/linux/include/i386/string.h	Thu Jan  1 02:00:00 1970
+++ linux/include/i386/string.h	Sun Aug  7 14:35:15 1994
@@ -0,0 +1,429 @@
+#ifndef _I386_STRING_H_
+#define _I386_STRING_H_
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ *		Copyright (C) 1991, 1992 Linus Torvalds
+ */
+ 
+extern inline char * strcpy(char * dest,const char *src)
+{
+__asm__ __volatile__(
+	"cld\n"
+	"1:\tlodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b"
+	: /* no output */
+	:"S" (src),"D" (dest):"si","di","ax","memory");
+return dest;
+}
+
+extern inline char * strncpy(char * dest,const char *src,size_t count)
+{
+__asm__ __volatile__(
+	"cld\n"
+	"1:\tdecl %2\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"rep\n\t"
+	"stosb\n"
+	"2:"
+	: /* no output */
+	:"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
+return dest;
+}
+
+extern inline char * strcat(char * dest,const char * src)
+{
+__asm__ __volatile__(
+	"cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"decl %1\n"
+	"1:\tlodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b"
+	: /* no output */
+	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
+return dest;
+}
+
+extern inline char * strncat(char * dest,const char * src,size_t count)
+{
+__asm__ __volatile__(
+	"cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"decl %1\n\t"
+	"movl %4,%3\n"
+	"1:\tdecl %3\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n"
+	"2:\txorl %2,%2\n\t"
+	"stosb"
+	: /* no output */
+	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
+	:"si","di","ax","cx","memory");
+return dest;
+}
+
+extern inline int strcmp(const char * cs,const char * ct)
+{
+register int __res;
+__asm__ __volatile__(
+	"cld\n"
+	"1:\tlodsb\n\t"
+	"scasb\n\t"
+	"jne 2f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"jmp 3f\n"
+	"2:\tsbbl %%eax,%%eax\n\t"
+	"orb $1,%%eax\n"
+	"3:"
+	:"=a" (__res):"S" (cs),"D" (ct):"si","di");
+return __res;
+}
+
+extern inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+	"cld\n"
+	"1:\tdecl %3\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"scasb\n\t"
+	"jne 3f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n"
+	"2:\txorl %%eax,%%eax\n\t"
+	"jmp 4f\n"
+	"3:\tsbbl %%eax,%%eax\n\t"
+	"orb $1,%%al\n"
+	"4:"
+	:"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx");
+return __res;
+}
+
+extern inline char * strchr(const char * s,char c)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movb %%al,%%ah\n"
+	"1:\tlodsb\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"je 2f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"movl $1,%1\n"
+	"2:\tmovl %1,%0\n\t"
+	"decl %0"
+	:"=a" (__res):"S" (s),"0" (c):"si");
+return __res;
+}
+
+extern inline char * strrchr(const char * s,char c)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movb %%al,%%ah\n"
+	"1:\tlodsb\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"jne 2f\n\t"
+	"leal -1(%%esi),%0\n"
+	"2:\ttestb %%al,%%al\n\t"
+	"jne 1b"
+	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
+return __res;
+}
+
+extern inline size_t strspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+extern inline size_t strcspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+extern inline char * strpbrk(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n\t"
+	"decl %0\n\t"
+	"jmp 3f\n"
+	"2:\txorl %0,%0\n"
+	"3:"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res;
+}
+
+extern inline char * strstr(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t" \
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
+	"movl %%ecx,%%edx\n"
+	"1:\tmovl %4,%%edi\n\t"
+	"movl %%esi,%%eax\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 2f\n\t"		/* also works for empty string, see above */
+	"xchgl %%eax,%%esi\n\t"
+	"incl %%esi\n\t"
+	"cmpb $0,-1(%%eax)\n\t"
+	"jne 1b\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"2:"
+	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
+	:"cx","dx","di","si");
+return __res;
+}
+
+extern inline size_t strlen(const char * s)
+{
+register int __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %0\n\t"
+	"decl %0"
+	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
+return __res;
+}
+
+extern char * ___strtok;
+
+extern inline char * strtok(char * s,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"testl %1,%1\n\t"
+	"jne 1f\n\t"
+	"testl %0,%0\n\t"
+	"je 8f\n\t"
+	"movl %0,%1\n"
+	"1:\txorl %0,%0\n\t"
+	"movl $-1,%%ecx\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"je 7f\n\t"			/* empty delimeter-string */
+	"movl %%ecx,%%edx\n"
+	"2:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 7f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 2b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 7f\n\t"
+	"movl %1,%0\n"
+	"3:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 5f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 3b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 5f\n\t"
+	"movb $0,(%1)\n\t"
+	"incl %1\n\t"
+	"jmp 6f\n"
+	"5:\txorl %1,%1\n"
+	"6:\tcmpb $0,(%0)\n\t"
+	"jne 7f\n\t"
+	"xorl %0,%0\n"
+	"7:\ttestl %0,%0\n\t"
+	"jne 8f\n\t"
+	"movl %0,%1\n"
+	"8:"
+	:"=b" (__res),"=S" (___strtok)
+	:"0" (___strtok),"1" (s),"g" (ct)
+	:"ax","cx","dx","di","memory");
+return __res;
+}
+
+extern inline void * memcpy(void * to, const void * from, size_t n)
+{
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %%edx, %%ecx\n\t"
+	"shrl $2,%%ecx\n\t"
+	"rep ; movsl\n\t"
+	"testb $1,%%dl\n\t"
+	"je 1f\n\t"
+	"movsb\n"
+	"1:\ttestb $2,%%dl\n\t"
+	"je 2f\n\t"
+	"movsw\n"
+	"2:\n"
+	: /* no output */
+	:"d" (n),"D" ((long) to),"S" ((long) from)
+	: "cx","di","si","memory");
+return (to);
+}
+
+extern inline void * memmove(void * dest,const void * src, size_t n)
+{
+if (dest<src)
+__asm__ __volatile__(
+	"cld\n\t"
+	"rep\n\t"
+	"movsb"
+	: /* no output */
+	:"c" (n),"S" (src),"D" (dest)
+	:"cx","si","di");
+else
+__asm__ __volatile__(
+	"std\n\t"
+	"rep\n\t"
+	"movsb\n\t"
+	"cld"
+	: /* no output */
+	:"c" (n),
+	 "S" (n-1+(const char *)src),
+	 "D" (n-1+(char *)dest)
+	:"cx","si","di","memory");
+return dest;
+}
+
+extern inline int memcmp(const void * cs,const void * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 1f\n\t"
+	"sbbl %%eax,%%eax\n\t"
+	"orb $1,%%al\n"
+	"1:"
+	:"=a" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
+	:"si","di","cx");
+return __res;
+}
+
+extern inline void * memchr(const void * cs,char c,size_t count)
+{
+register void * __res;
+if (!count)
+	return NULL;
+__asm__ __volatile__(
+	"cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1f\n\t"
+	"movl $1,%0\n"
+	"1:\tdecl %0"
+	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
+	:"cx");
+return __res;
+}
+
+extern inline void * memset(void * s,char c,size_t count)
+{
+__asm__ __volatile__(
+	"cld\n\t"
+	"rep\n\t"
+	"stosb"
+	: /* no output */
+	:"a" (c),"D" (s),"c" (count)
+	:"cx","di","memory");
+return s;
+}
+
+#endif
diff -u --recursive --new-file v1.1.40/linux/include/linux/fd.h linux/include/linux/fd.h
--- v1.1.40/linux/include/linux/fd.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/fd.h	Sun Aug  7 00:46:57 1994
@@ -3,8 +3,11 @@
 
 #define FDCLRPRM 0 /* clear user-defined parameters */
 #define FDSETPRM 1 /* set user-defined parameters for current media */
+#define FDSETMEDIAPRM 1
 #define FDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
+#define FDDEFMEDIAPRM 2
 #define FDGETPRM 3 /* get disk parameters */
+#define FDGETMEDIAPRM 3
 #define	FDMSGON  4 /* issue kernel messages on media type change */
 #define	FDMSGOFF 5 /* don't issue kernel messages on media type change */
 #define FDFMTBEG 6 /* begin formatting a disk */
@@ -14,17 +17,48 @@
 #define FDFLUSH  11 /* flush buffers for media; either for verifying media, or for
                        handling a media change without closing the file
 		       descriptor */
+#define FDSETMAXERRS 12 /* set abortion and read_track treshold */
+#define FDGETMAXERRS 14 /* get abortion and read_track treshold */
+#define FDGETDRVTYP 16          /* get drive type: 5 1/4 or 3 1/2 */
 
+#define FDSETDRVPRM 20 /* set drive parameters */
+#define FDGETDRVPRM 21 /* get drive parameters */
+#define FDGETDRVSTAT 22 /* get drive state */
+#define FDPOLLDRVSTAT 23 /* get drive state */
+#define FDGETFDCSTAT 25 /* get fdc state */
+
+#define FDRESET 24 /* reset FDC */
+#define FD_RESET_IF_NEEDED 0
+#define FD_RESET_IF_RAWCMD 1
+#define FD_RESET_ALWAYS 2
+
+#define FDBAILOUT 26 /* release all fdc locks */
+#define FD_CLEAR_RESET 0
+#define FD_COMPLETE_FORMAT 1
+#define FD_UNLOCK_FDC 2
+
+#define FDRAWCMD 30 /* send a raw command to the fdc */
+
+#define FDTWADDLE 40 /* flicker motor-on bit before reading a sector */
+
+/*
+ * Maximum number of sectors in a track buffer. Track buffering is disabled
+ * if tracks are bigger.
+ */
+#define MAX_BUFFER_SECTORS 24 /* was 18 -bb */
+
 #define FD_FILL_BYTE 0xF6 /* format fill byte */
 
-#define FORMAT_NONE	0	/* no format request */
-#define FORMAT_WAIT	1	/* format request is waiting */
-#define FORMAT_BUSY	2	/* formatting in progress */
-#define FORMAT_OKAY	3	/* successful completion */
-#define FORMAT_ERROR	4	/* formatting error */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((( (floppy)->rate ) & FD_SIZECODEMASK) >> 3)+ 2)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+			     512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
 
+#ifndef ASSEMBLER
 struct floppy_struct {
-	unsigned int	size,		/* nr of 512-byte sectors total */
+	unsigned int	size,		/* nr of sectors total */
 			sect,		/* sectors per track */
 			head,		/* nr of heads */
 			track,		/* nr of tracks */
@@ -39,5 +73,142 @@
 struct format_descr {
 	unsigned int device,head,track;
 };
+
+struct floppy_max_errors {
+  unsigned int
+	  abort,      /* number of errors to be reached before aborting */
+	  read_track, /* maximal number of errors permitted to read an
+		       * entire track at once */
+	  reset,      /* maximal number of errors before a reset is tried */
+	  recal,      /* maximal number of errors before a recalibrate is
+		       * tried */
+
+	  /*
+	   * Threshold for reporting FDC errors to the console.
+	   * Setting this to zero may flood your screen when using
+	   * ultra cheap floppies ;-)
+	   */
+	  reporting;
+
+};
+
+struct floppy_drive_params {
+  char cmos;			/* cmos type */
+
+  /* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
+   * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
+   */
+  unsigned long max_dtr;	/* Step rate, usec */
+  unsigned long hlt;		/* Head load/settle time, msec */
+  unsigned long hut;		/* Head unload time (remnant of 8" drives) */
+  unsigned long srt;		/* Step rate, usec */
+
+  unsigned long spinup;		/* time needed for spinup ( in jiffies) */
+  unsigned long spindown;	/* timeout needed for spindown */
+  unsigned char spindown_offset;/* decides in which position the disk
+				 * will stop */
+  unsigned char select_delay;	/* delay to wait after select */
+  unsigned char rps;	       	/* rotations per second */
+  unsigned char tracks;	       	/* maximum number of tracks */
+  unsigned long timeout;	/* timeout for interrupt requests */
+
+  unsigned char interleave_sect;/* if there are more sectors, use interleave */
+
+  struct floppy_max_errors max_errors;
+
+  char flags;			/* various flags, including ftd_msg */
+/*
+ * Announce successful media type detection and media information loss after
+ * disk changes.
+ * Also used to enable/disable printing of overrun warnings.
+ */
+#define FTD_MSG 0x10
+
+  char read_track;		/* use readtrack during probing? */
+
+/*
+ * Auto-detection. Each drive type has eight formats which are
+ * used in succession to try to read the disk. If the FDC cannot lock onto
+ * the disk, the next format is tried. This uses the variable 'probing'.
+ */
+  short autodetect[8];		/* autodetected formats */
+
+  int checkfreq; /* how often should the drive be checked for disk changes */
+  int native_format; /* native format of this drive */
+};
+
+struct floppy_drive_struct {
+  signed char flags;
+
+/* values for these flags */
+#define FD_NEED_TWADDLE 1 /* more magic */
+#define FD_VERIFY 2 /* this is set at bootup to force an initial drive status
+		   inquiry*/
+#define FD_DISK_NEWCHANGE 4 /* change detected, and no action undertaken yet to
+			    clear media change status */
+#define FD_DRIVE_PRESENT 8
+#define FD_DISK_WRITABLE 32
+
+  unsigned long volatile spinup_date;
+  unsigned long volatile select_date;
+  unsigned long volatile first_read_date;
+  short probed_format;
+  short track; /* current track */
+  short maxblock; /* id of highest block read */
+  short maxtrack; /* id of highest half track read */
+  int generation; /* how many diskchanges? */
+
+/*
+ * (User-provided) media information is _not_ discarded after a media change
+ * if the corresponding keep_data flag is non-zero. Positive values are
+ * decremented after each probe.
+ */
+  int keep_data;
+
+  /* Prevent "aliased" accesses. */
+  int fd_ref;
+  int fd_device;
+  int last_checked; /* when was the drive last checked for a disk change? */
+
+};
+
+struct floppy_fdc_state {	
+	int spec1; /* spec1 value last used */
+	int spec2; /* spec2 value last used */
+	int dtr;
+	unsigned char version;	/* FDC version code */
+	unsigned char dor;
+	int address; /* io address */
+	unsigned int rawcmd:2;
+	unsigned int reset:1;
+	unsigned int need_configure:1;
+	unsigned int perp_mode:2;
+	unsigned int has_fifo:1;
+};
+
+struct floppy_raw_cmd {
+  void *data;
+  long length;
+
+  unsigned char rate;
+  unsigned char flags;
+  unsigned char cmd_count;
+  unsigned char cmd[9];
+  unsigned char reply_count;
+  unsigned char reply[7];
+  int track;
+};
+#endif
+
+/* meaning of the various bytes */
+
+/* flags */
+#define FD_RAW_READ 1
+#define FD_RAW_WRITE 2
+#define FD_RAW_INTR 8
+#define FD_RAW_SPIN 16
+#define FD_RAW_NEED_DISK 64
+#define FD_RAW_NEED_SEEK 128
+#define FD_RAW_USER_SUPPLIED 256
 
 #endif
diff -u --recursive --new-file v1.1.40/linux/include/linux/fdreg.h linux/include/linux/fdreg.h
--- v1.1.40/linux/include/linux/fdreg.h	Tue Jul 19 10:19:13 1994
+++ linux/include/linux/fdreg.h	Sun Aug  7 00:46:58 1994
@@ -6,13 +6,30 @@
  * Handbook", Sanches and Canton.
  */
 
+#ifdef FDPATCHES
 /* Fd controller regs. S&C, about page 340 */
+#define FD_STATUS	(4 + fdc_state[fdc].address )
+#define FD_DATA		(5 + fdc_state[fdc].address )
+
+/* Digital Output Register */
+#define FD_DOR		(2 + fdc_state[fdc].address )
+
+/* Digital Input Register (read) */
+#define FD_DIR		(7 + fdc_state[fdc].address )
+
+/* Diskette Control Register (write)*/
+#define FD_DCR		(7 + fdc_state[fdc].address )
+
+#else
+
 #define FD_STATUS	0x3f4
 #define FD_DATA		0x3f5
 #define FD_DOR		0x3f2		/* Digital Output Register */
 #define FD_DIR		0x3f7		/* Digital Input Register (read) */
 #define FD_DCR		0x3f7		/* Diskette Control Register (write)*/
 
+#endif
+
 /* Bits of main status register */
 #define STATUS_BUSYMASK	0x0F		/* drive busy mask */
 #define STATUS_BUSY	0x10		/* FDC busy */
@@ -65,13 +82,23 @@
 #define FD_CONFIGURE		0x13	/* configure FIFO operation */
 #define FD_PERPENDICULAR	0x12	/* perpendicular r/w mode */
 #define FD_GETSTATUS		0x04	/* read ST3 */
+#define FD_DUMPREGS		0x0E	/* dump the contents of the fdc regs */
+#define FD_READID		0xEA	/* prints the header of a sector */
+#define FD_UNLOCK		0x14	/* Fifo config unlock */
+#define FD_LOCK			0x94	/* Fifo config lock */
 
 /* DMA commands */
 #define DMA_READ	0x46
 #define DMA_WRITE	0x4A
 
 /* FDC version return types */
-#define FDC_TYPE_STD	0x80	/* normal 8272A clone FDC */
-#define FDC_TYPE_82077	0x90	/* FIFO + perpendicular support */
+#define FDC_NONE	0x00
+#define FDC_UNKNOWN	0x10
+#define FDC_8272A	0x20	/* Intel 8272a, NEC 765 */
+#define FDC_765ED	0x30	/* Non-Intel 1MB-compatible FDC, can't detect */
+#define FDC_82072	0x40	/* Intel 82072; 8272a + FIFO + DUMPREGS */
+#define FDC_82077_ORIG	0x50	/* Original version of 82077AA, sans LOCK */
+#define FDC_82077	0x52	/* 82077AA-1 */
 
+#define FD_RESET_DELAY 20
 #endif
diff -u --recursive --new-file v1.1.40/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.40/linux/include/linux/fs.h	Sat Aug  6 21:32:07 1994
+++ linux/include/linux/fs.h	Sun Aug  7 00:46:58 1994
@@ -422,6 +422,7 @@
 extern int check_disk_change(dev_t dev);
 extern void invalidate_inodes(dev_t dev);
 extern void invalidate_buffers(dev_t dev);
+extern int floppy_is_wp(int minor);
 extern void sync_inodes(dev_t dev);
 extern void sync_dev(dev_t dev);
 extern int fsync_dev(dev_t dev);
@@ -446,6 +447,7 @@
 extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]);
 extern void ll_rw_page(int rw, int dev, int nr, char * buffer);
 extern void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buffer);
+extern int is_read_only(int dev);
 extern void brelse(struct buffer_head * buf);
 extern void set_blocksize(dev_t dev, int size);
 extern struct buffer_head * bread(dev_t dev, int block, int size);
diff -u --recursive --new-file v1.1.40/linux/include/linux/string.h linux/include/linux/string.h
--- v1.1.40/linux/include/linux/string.h	Mon Jul 18 14:48:30 1994
+++ linux/include/linux/string.h	Sun Aug  7 14:34:59 1994
@@ -11,430 +11,27 @@
 extern "C" {
 #endif
 
-/*
- * This string-include defines all string functions as inline
- * functions. Use gcc. It also assumes ds=es=data space, this should be
- * normal. Most of the string-functions are rather heavily hand-optimized,
- * see especially strtok,strstr,str[c]spn. They should work, but are not
- * very easy to understand. Everything is done entirely within the register
- * set, making the functions fast and clean. String instructions have been
- * used through-out, making for "slightly" unclear code :-)
- *
- *		Copyright (C) 1991, 1992 Linus Torvalds
- */
- 
-extern inline char * strcpy(char * dest,const char *src)
-{
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tlodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest):"si","di","ax","memory");
-return dest;
-}
+#include <i386/string.h>	/* inline functions for i386.. */
 
-extern inline char * strncpy(char * dest,const char *src,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tdecl %2\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"rep\n\t"
-	"stosb\n"
-	"2:"
-	: /* no output */
-	:"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
-return dest;
-}
-
-extern inline char * strcat(char * dest,const char * src)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"decl %1\n"
-	"1:\tlodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
-return dest;
-}
-
-extern inline char * strncat(char * dest,const char * src,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"decl %1\n\t"
-	"movl %4,%3\n"
-	"1:\tdecl %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txorl %2,%2\n\t"
-	"stosb"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
-	:"si","di","ax","cx","memory");
-return dest;
-}
-
-extern inline int strcmp(const char * cs,const char * ct)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tlodsb\n\t"
-	"scasb\n\t"
-	"jne 2f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"jmp 3f\n"
-	"2:\tsbbl %%eax,%%eax\n\t"
-	"orb $1,%%eax\n"
-	"3:"
-	:"=a" (__res):"S" (cs),"D" (ct):"si","di");
-return __res;
-}
-
-extern inline int strncmp(const char * cs,const char * ct,size_t count)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tdecl %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"scasb\n\t"
-	"jne 3f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txorl %%eax,%%eax\n\t"
-	"jmp 4f\n"
-	"3:\tsbbl %%eax,%%eax\n\t"
-	"orb $1,%%al\n"
-	"4:"
-	:"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx");
-return __res;
-}
-
-extern inline char * strchr(const char * s,char c)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movb %%al,%%ah\n"
-	"1:\tlodsb\n\t"
-	"cmpb %%ah,%%al\n\t"
-	"je 2f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"movl $1,%1\n"
-	"2:\tmovl %1,%0\n\t"
-	"decl %0"
-	:"=a" (__res):"S" (s),"0" (c):"si");
-return __res;
-}
-
-extern inline char * strrchr(const char * s,char c)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movb %%al,%%ah\n"
-	"1:\tlodsb\n\t"
-	"cmpb %%ah,%%al\n\t"
-	"jne 2f\n\t"
-	"leal -1(%%esi),%0\n"
-	"2:\ttestb %%al,%%al\n\t"
-	"jne 1b"
-	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
-return __res;
-}
-
-extern inline size_t strspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-extern inline size_t strcspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-extern inline char * strpbrk(const char * cs,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n\t"
-	"decl %0\n\t"
-	"jmp 3f\n"
-	"2:\txorl %0,%0\n"
-	"3:"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res;
-}
-
-extern inline char * strstr(const char * cs,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t" \
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
-	"movl %%ecx,%%edx\n"
-	"1:\tmovl %4,%%edi\n\t"
-	"movl %%esi,%%eax\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repe\n\t"
-	"cmpsb\n\t"
-	"je 2f\n\t"		/* also works for empty string, see above */
-	"xchgl %%eax,%%esi\n\t"
-	"incl %%esi\n\t"
-	"cmpb $0,-1(%%eax)\n\t"
-	"jne 1b\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"2:"
-	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
-	:"cx","dx","di","si");
-return __res;
-}
-
-extern inline size_t strlen(const char * s)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %0\n\t"
-	"decl %0"
-	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
-return __res;
-}
-
-extern char * ___strtok;
-
-extern inline char * strtok(char * s,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"testl %1,%1\n\t"
-	"jne 1f\n\t"
-	"testl %0,%0\n\t"
-	"je 8f\n\t"
-	"movl %0,%1\n"
-	"1:\txorl %0,%0\n\t"
-	"movl $-1,%%ecx\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"je 7f\n\t"			/* empty delimeter-string */
-	"movl %%ecx,%%edx\n"
-	"2:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 7f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 2b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 7f\n\t"
-	"movl %1,%0\n"
-	"3:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 5f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 3b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 5f\n\t"
-	"movb $0,(%1)\n\t"
-	"incl %1\n\t"
-	"jmp 6f\n"
-	"5:\txorl %1,%1\n"
-	"6:\tcmpb $0,(%0)\n\t"
-	"jne 7f\n\t"
-	"xorl %0,%0\n"
-	"7:\ttestl %0,%0\n\t"
-	"jne 8f\n\t"
-	"movl %0,%1\n"
-	"8:"
-	:"=b" (__res),"=S" (___strtok)
-	:"0" (___strtok),"1" (s),"g" (ct)
-	:"ax","cx","dx","di","memory");
-return __res;
-}
-
-extern inline void * memcpy(void * to, const void * from, size_t n)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %%edx, %%ecx\n\t"
-	"shrl $2,%%ecx\n\t"
-	"rep ; movsl\n\t"
-	"testb $1,%%dl\n\t"
-	"je 1f\n\t"
-	"movsb\n"
-	"1:\ttestb $2,%%dl\n\t"
-	"je 2f\n\t"
-	"movsw\n"
-	"2:\n"
-	: /* no output */
-	:"d" (n),"D" ((long) to),"S" ((long) from)
-	: "cx","di","si","memory");
-return (to);
-}
-
-extern inline void * memmove(void * dest,const void * src, size_t n)
-{
-if (dest<src)
-__asm__ __volatile__(
-	"cld\n\t"
-	"rep\n\t"
-	"movsb"
-	: /* no output */
-	:"c" (n),"S" (src),"D" (dest)
-	:"cx","si","di");
-else
-__asm__ __volatile__(
-	"std\n\t"
-	"rep\n\t"
-	"movsb\n\t"
-	"cld"
-	: /* no output */
-	:"c" (n),
-	 "S" (n-1+(const char *)src),
-	 "D" (n-1+(char *)dest)
-	:"cx","si","di","memory");
-return dest;
-}
-
-extern inline int memcmp(const void * cs,const void * ct,size_t count)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repe\n\t"
-	"cmpsb\n\t"
-	"je 1f\n\t"
-	"sbbl %%eax,%%eax\n\t"
-	"orb $1,%%al\n"
-	"1:"
-	:"=a" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
-	:"si","di","cx");
-return __res;
-}
-
-extern inline void * memchr(const void * cs,char c,size_t count)
-{
-register void * __res;
-if (!count)
-	return NULL;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 1f\n\t"
-	"movl $1,%0\n"
-	"1:\tdecl %0"
-	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
-	:"cx");
-return __res;
-}
-
-extern inline void * memset(void * s,char c,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"rep\n\t"
-	"stosb"
-	: /* no output */
-	:"a" (c),"D" (s),"c" (count)
-	:"cx","di","memory");
-return s;
-}
+extern char * strcpy(char *, const char *);
+extern char * strncpy(char *, const char *, size_t);
+extern char * strcat(char *, const char *);
+extern char * strncat(char *, const char *, size_t);
+extern int strcmp(const char *, const char *);
+extern int strncmp(const char *, const char *, size_t);
+extern char * strchr(const char *, char);
+extern char * strrchr(const char *, char);
+extern size_t strspn(const char *, const char *);
+extern size_t strcspn(const char *, const char *);
+extern char * strpbrk(const char *, const char *);
+extern char * strstr(const char *, const char *);
+extern size_t strlen(const char *);
+extern char * strtok(char *, const char *);
+extern void * memcpy(void *, const void *, size_t);
+extern void * memmove(void *, const void *, size_t);
+extern int memcmp(const void *, const void *, size_t);
+extern void * memchr(const void *, char, size_t);
+extern void * memset(void *, char, size_t);
 
 #ifdef __cplusplus
 }
diff -u --recursive --new-file v1.1.40/linux/kernel/vsprintf.c linux/kernel/vsprintf.c
--- v1.1.40/linux/kernel/vsprintf.c	Thu Dec  2 16:18:34 1993
+++ linux/kernel/vsprintf.c	Sun Aug  7 16:08:48 1994
@@ -57,61 +57,76 @@
 #define SPACE	8		/* space if plus */
 #define LEFT	16		/* left justified */
 #define SPECIAL	32		/* 0x */
-#define SMALL	64		/* use 'abcdef' instead of 'ABCDEF' */
+#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
 
 #define do_div(n,base) ({ \
 int __res; \
-__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
+__res = ((unsigned long) n) % (unsigned) base; \
+n = ((unsigned long) n) / (unsigned) base; \
 __res; })
 
-static char * number(char * str, int num, int base, int size, int precision
+static char * number(char * str, long num, int base, int size, int precision
 	,int type)
 {
 	char c,sign,tmp[36];
-	const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
 	int i;
 
-	if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
-	if (type&LEFT) type &= ~ZEROPAD;
-	if (base<2 || base>36)
+	if (type & LARGE)
+		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	if (type & LEFT)
+		type &= ~ZEROPAD;
+	if (base < 2 || base > 36)
 		return 0;
-	c = (type & ZEROPAD) ? '0' : ' ' ;
-	if (type&SIGN && num<0) {
-		sign='-';
-		num = -num;
-	} else
-		sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
-	if (sign) size--;
-	if (type&SPECIAL)
-		if (base==16) size -= 2;
-		else if (base==8) size--;
-	i=0;
-	if (num==0)
+	c = (type & ZEROPAD) ? '0' : ' ';
+	sign = 0;
+	if (type & SIGN) {
+		if (num < 0) {
+			sign = '-';
+			num = -num;
+			size--;
+		} else if (type & PLUS) {
+			sign = '+';
+			size--;
+		} else if (type & SPACE) {
+			sign = ' ';
+			size--;
+		}
+	}
+	if (type & SPECIAL) {
+		if (base == 16)
+			size -= 2;
+		else if (base == 8)
+			size--;
+	}
+	i = 0;
+	if (num == 0)
 		tmp[i++]='0';
-	else while (num!=0)
-		tmp[i++]=digits[do_div(num,base)];
-	if (i>precision) precision=i;
+	else while (num != 0)
+		tmp[i++] = digits[do_div(num,base)];
+	if (i > precision)
+		precision = i;
 	size -= precision;
 	if (!(type&(ZEROPAD+LEFT)))
 		while(size-->0)
 			*str++ = ' ';
 	if (sign)
 		*str++ = sign;
-	if (type&SPECIAL)
+	if (type & SPECIAL)
 		if (base==8)
 			*str++ = '0';
 		else if (base==16) {
 			*str++ = '0';
 			*str++ = digits[33];
 		}
-	if (!(type&LEFT))
-		while(size-->0)
+	if (!(type & LEFT))
+		while (size-- > 0)
 			*str++ = c;
-	while(i<precision--)
+	while (i < precision--)
 		*str++ = '0';
-	while(i-->0)
+	while (i-- > 0)
 		*str++ = tmp[i];
-	while(size-->0)
+	while (size-- > 0)
 		*str++ = ' ';
 	return str;
 }
@@ -119,10 +134,10 @@
 int vsprintf(char *buf, const char *fmt, va_list args)
 {
 	int len;
-	int i;
+	unsigned long num;
+	int i, base;
 	char * str;
 	char *s;
-	int *ip;
 
 	int flags;		/* flags to number() */
 
@@ -183,6 +198,9 @@
 			++fmt;
 		}
 
+		/* default base */
+		base = 10;
+
 		switch (*fmt) {
 		case 'c':
 			if (!(flags & LEFT))
@@ -191,7 +209,7 @@
 			*str++ = (unsigned char) va_arg(args, int);
 			while (--field_width > 0)
 				*str++ = ' ';
-			break;
+			continue;
 
 		case 's':
 			s = va_arg(args, char *);
@@ -210,12 +228,7 @@
 				*str++ = *s++;
 			while (len < field_width--)
 				*str++ = ' ';
-			break;
-
-		case 'o':
-			str = number(str, va_arg(args, unsigned long), 8,
-				field_width, precision, flags);
-			break;
+			continue;
 
 		case 'p':
 			if (field_width == -1) {
@@ -225,26 +238,34 @@
 			str = number(str,
 				(unsigned long) va_arg(args, void *), 16,
 				field_width, precision, flags);
+			continue;
+
+
+		case 'n':
+			if (qualifier == 'l') {
+				long * ip = va_arg(args, long *);
+				*ip = (str - buf);
+			} else {
+				int * ip = va_arg(args, int *);
+				*ip = (str - buf);
+			}
+			continue;
+
+		/* integer number formats - set up the flags and "break" */
+		case 'o':
+			base = 8;
 			break;
 
-		case 'x':
-			flags |= SMALL;
 		case 'X':
-			str = number(str, va_arg(args, unsigned long), 16,
-				field_width, precision, flags);
+			flags |= LARGE;
+		case 'x':
+			base = 16;
 			break;
 
 		case 'd':
 		case 'i':
 			flags |= SIGN;
 		case 'u':
-			str = number(str, va_arg(args, unsigned long), 10,
-				field_width, precision, flags);
-			break;
-
-		case 'n':
-			ip = va_arg(args, int *);
-			*ip = (str - buf);
 			break;
 
 		default:
@@ -254,8 +275,20 @@
 				*str++ = *fmt;
 			else
 				--fmt;
-			break;
+			continue;
 		}
+		if (qualifier == 'l')
+			num = va_arg(args, unsigned long);
+		else if (qualifier == 'h')
+			if (flags & SIGN)
+				num = va_arg(args, short);
+			else
+				num = va_arg(args, unsigned short);
+		else if (flags & SIGN)
+			num = va_arg(args, int);
+		else
+			num = va_arg(args, unsigned int);
+		str = number(str, num, base, field_width, precision, flags);
 	}
 	*str = '\0';
 	return str-buf;
