diff -ur linux/boot/head.S /linux/boot/head.S
--- linux/boot/head.S	Thu Feb  3 11:37:44 1994
+++ /linux/boot/head.S	Sat Feb  5 11:58:42 1994
@@ -286,10 +286,10 @@
  * 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 track of
- * data (18*2*512 bytes).
+ * data (21*2*512 bytes).
  */
 _floppy_track_buffer:
-	.fill 512*2*18,1,0
+	.fill 512*2*21,1,0
 
 /* This is the default interrupt "handler" :-) */
 int_msg:
diff -ur linux/config.in /linux/config.in
--- linux/config.in	Thu Feb  3 12:27:36 1994
+++ /linux/config.in	Tue Feb  8 22:55:03 1994
@@ -13,6 +13,22 @@
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
 *
+* fdpatches
+*
+bool 'Autoprobe 4 devices' CONFIG_AUTOPROBE_4 n
+if [ "$CONFIG_AUTOPROBE_4" = "n" ]
+:
+: Skipping number of autodetected tracks
+:
+else
+	*
+	* Number of autodetected tracks for extended types. 
+	: Answer no to both questions to autodetect 80 tracks.
+	*
+bool 'Autodetect 83 tracks' FD_detect_83 n
+bool 'Autodetect 82 tracks (overrides 83 tracks)' FD_detect_82 y
+fi
+*
 * Program binary formats
 *
 bool 'Elf executables' CONFIG_BINFMT_ELF y
diff -ur linux/drivers/block/floppy.c /linux/drivers/block/floppy.c
--- linux/drivers/block/floppy.c	Mon Dec 27 06:23:53 1993
+++ /linux/drivers/block/floppy.c	Tue Feb  8 23:04:58 1994
@@ -104,19 +104,19 @@
  * 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
+static struct floppy_max_errors max_errors[]={ {12, 4},{12, 4},{12 ,4},{12, 4}};
 
 /*
  * Maximum disk size (in kilobytes). This default is used whenever the
  * current disk size is unknown.
  */
-#define MAX_DISK_SIZE 1440
+#define MAX_DISK_SIZE 1743
 
 /*
  * Maximum number of sectors in a track buffer. Track buffering is disabled
  * if tracks are bigger.
  */
-#define MAX_BUFFER_SECTORS 18
+#define MAX_BUFFER_SECTORS 21
 
 /*
  * The DMA channel used by the floppy controller cannot access data at
@@ -146,37 +146,59 @@
  * 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 */
+	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL },	/*  0 no testing */
+	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k PC" },	/*  1 360kB PC diskettes */
+	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M" },	/*  2 1.2 MB AT-diskettes */
+	{  720, 9,2,40,1,0x2A,0x02,0xDF,0x50,"D360" },	/*  3 360kB in 720kB drive */
+	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k"},	/*  4 3.5" 720kB diskette */
+	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" },	/*  5 360kB in 1.2MB drive */
+	{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" },	/*  6 720kB in 1.2MB drive */
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" },	/*  7 1.44MB diskette */
+	{  800,10,2,40,1,0x25,0x02,0xDF,0x2E,"h400" },	/*  8 400k 5.25" */
+	{ 1600,10,2,80,0,0x25,0x01,0xDF,0x2E,"H800" },	/*  9 800k 3.5" */
+	{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
+	{ 3360,21,2,80,0,0x25,0x00,0xDF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
+	{  820,10,2,41,1,0x25,0x02,0xDF,0x2E,"h410" },	/* 12 410k 5.25" */
+	{ 1640,10,2,82,0,0x25,0x01,0xDF,0x2E,"H820" },	/* 13 820k 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,0x02,0xDF,0x2E,"h420" },	/* 16 420k 5.25" */
+	{ 1660,10,2,83,0,0x25,0x01,0xDF,0x2E,"H830" },	/* 17 830k 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.74MB 3.5" */
 };
 
 /*
- * Auto-detection. Each drive type has a pair of formats which are
+ * Auto-detection. Each drive type has four 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'.
  */
-static struct floppy_struct floppy_types[] = {
-	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */
-	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */
-	{ 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 */
-	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" },	  /* 3.5" 720kB diskette */
-	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" },	  /* 3.5" 720kB diskette */
-	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" },	  /* 1.44MB diskette */
-	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */
-};
+static int floppy_types[] = {
+#ifdef FD_detect_82
+	 1,  1,  1,  1,  /* 5.25" DD drive */
+	14,  2, 12,  5,  /* 5.25" HD drive */
+	13,  4, 13,  4,  /* 3.5" DD drive */
+	15,  7, 13,  4   /* 3.5" HD drive */
+#else
+#ifdef FD_detect_83
+	 1,  1,  1,  1,
+	18,  2, 16,  5,
+	17,  4, 17,  4,
+	19,  7, 17,  4
+#else   /* 80 tracks */
+	 1,  1,  1,  1,
+	10,  2,  8,  5,
+	 9,  4,  9,  4,
+	11,  7,  9,  4
+#endif
+#endif
+	};
 
 /* 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];
+int *base_type[4];
 
 /*
  * User-provided type information. current_type points to
@@ -192,7 +214,19 @@
 	 720, 720, 720, 720,
 	 360, 360, 360, 360,
 	 720, 720, 720, 720,
-	1440,1440,1440,1440
+	1440,1440,1440,1440,
+	 400, 400, 400, 400,
+	 800, 800, 800, 800,
+	1440,1440,1440,1440,
+	1680,1680,1680,1680,
+	 410, 410, 410, 410,
+	 820, 820, 820, 820,
+	1476,1476,1476,1476,
+	1722,1722,1722,1722,
+	 420, 420, 420, 420,
+	 830, 830, 830, 830,
+	1494,1494,1494,1494,
+	1743,1743,1743,1743
 };
 
 /*
@@ -516,6 +550,7 @@
 static void bad_flp_intr(void)
 {
 	int errors;
+	int maximum;
 
 	current_track = NO_TRACK;
 	if (format_status == FORMAT_BUSY)
@@ -526,10 +561,16 @@
 		return;
 	} else
 		errors = ++CURRENT->errors;
-	if (errors > MAX_ERRORS) {
+
+	if ( !CURRENT )
+		maximum = 12; /* formatting */
+	else
+		maximum = max_errors[ CURRENT_DEVICE & 0x3 ].abort;
+
+	if (errors > maximum) {
 		request_done(0);
 	}
-	if (errors > MAX_ERRORS/2)
+	if (errors > maximum/2)
 		reset = 1;
 	else
 		recalibrate = 1;
@@ -765,8 +806,9 @@
  */
 static void transfer(void)
 {
-	read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) &&
-	    (floppy->sect <= MAX_BUFFER_SECTORS);
+	read_track = (command == FD_READ) &&
+	  (CURRENT_ERRORS < max_errors[CURRENT_DEVICE & 0x3].read_track ) &&
+	  (floppy->sect <= MAX_BUFFER_SECTORS);
 
 	configure_fdc_mode();
 
@@ -968,23 +1010,41 @@
 
 static void setup_format_params(void)
 {
-    unsigned char *here = (unsigned char *) tmp_floppy_area;
-    int count,head_shift,track_shift,total_shift;
+    struct fparm {
+        unsigned char track,head,sect,size;
+        } *here = (void *)tmp_floppy_area;
+    int il,n,count,head_shift,track_shift;
 
     /* 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 */
+    /* position of logical sector 1 on this track */
+    n = (track_shift * track + head_shift * head) % floppy->sect;
+    /* determine interleave */
+    il = 1;
+    if (base_type[format_req.device & 3] && 
+	floppy->sect - floppy_type[base_type[format_req.device & 3][1]].sect >2)
+      il = 2;
+
+    /* initialize field */ 
+    for (count = 0; count < floppy->sect; ++count) {
+       here[count].track = track;
+       here[count].head = head;
+       here[count].sect = 0;
+       here[count].size = 2;
+       }
+    /* place logical sectors */
+    for (count = 1; count <= floppy->sect; ++count) {
+        here[n].sect = count;
+        n = (n+il) % floppy->sect;
+        if (here[n].sect) { /* sector busy, find next free sector */
+           ++n;
+           if (n>=floppy->sect) {
+             n-=floppy->sect;
+             while (here[n].sect) ++n;
+             }
+        }
     }
 }
 
@@ -1024,14 +1084,19 @@
 		floppy = current_type[device & 3];
 		if (!floppy) {
 			probing = 1;
-			floppy = base_type[device & 3];
-			if (!floppy) {
+			if (base_type[ device & 3] == NULL){
+				floppy = NULL;
 				request_done(0);
 				goto repeat;
 			}
-			if (CURRENT_ERRORS & 1)
-				floppy++;
-		}
+			/* ignore compiler warning about result known in advance */
+
+#ifdef CONFIG_AUTOPROBE_4
+			floppy = &floppy_type[base_type[device & 3][CURRENT_ERRORS & 3 ]];
+#else
+			floppy = &floppy_type[base_type[device & 3][((CURRENT_ERRORS & 1) << 1 ) + 1]];
+#endif
+		      }
 	}
 	if (format_status != FORMAT_BUSY) {
 		if (current_drive != CURRENT_DEV) {
@@ -1113,12 +1178,55 @@
 	}
 	drive = MINOR(inode->i_rdev);
 	switch (cmd) {
+		case FDGETEMSGTRESH:
+			i = verify_area(VERIFY_WRITE,(void *) param,
+					sizeof(struct floppy_struct));
+			if (i)
+				return i;
+			for ( cnt = 0; cnt < sizeof(short) ; cnt++)
+				put_fs_byte(((char *) 
+				      (min_report_error_cnt+DRIVE(drive)))[cnt],
+				      (char *) param+cnt);
+			return 0;
+		case FDGETDRVTYP:
+			i=verify_area(VERIFY_WRITE,(void *) param,16);
+			if (i)
+				return i;
+			if ( drive < 4 ){
+			  if ( !base_type[drive & 3] )
+			    return -ENXIO;
+			  drive = base_type[ drive & 3][1];
+			}
+			else
+			  drive >>=2;
+	 		memcpy_tofs( (char *)param, floppy_type[drive].name,16);
+			return 0;			
+		case FDSETMAXERRS:
+			if (!permission(inode, 2) || (drive > 3 && !suser() ))
+				return -EPERM;
+			for (cnt = 0; 
+			     cnt < sizeof(struct floppy_max_errors); 
+			     cnt++)
+				((char *)( &(max_errors[drive & 0x3])))[cnt]=
+				  get_fs_byte((char *) param+cnt);
+			return 0;
+		case FDGETMAXERRS:
+			i=verify_area(VERIFY_WRITE,(void *) param,sizeof(long));
+			if (i)
+				return i;
+			for (cnt = 0; 
+			     cnt < sizeof(struct floppy_max_errors); 
+			     cnt++)
+				put_fs_byte(((char *)
+					    (& (max_errors[drive & 0x3])))[cnt],
+					    (char *) param+cnt);
+			return 0;
 		case FDFMTBEG:
-			if (!suser())
+			if (!permission(inode, 2))
 				return -EPERM;
 			return 0;
 		case FDFMTEND:
-			if (!suser())
+			if (!permission(inode, 2))
 				return -EPERM;
 			cli();
 			fake_change |= 1 << (drive & 3);
@@ -1138,7 +1246,7 @@
 				    (char *) param+cnt);
 			return 0;
 		case FDFMTTRK:
-			if (!suser())
+			if (!permission(inode, 2))
 				return -EPERM;
 			if (fd_ref[drive & 3] != 1)
 				return -EBUSY;
@@ -1148,6 +1256,14 @@
 			for (cnt = 0; cnt < sizeof(struct format_descr); cnt++)
 				((char *) &format_req)[cnt] = get_fs_byte(
 				    (char *) param+cnt);
+			if((drive < 4 && 
+			    format_req.track >= current_type[drive]->track )||
+			   (drive >= 4 &&
+			    format_req.track >= floppy_type[drive >> 2].track)){
+			  sti();
+			  return -EINVAL;
+			}
+
 			format_req.device = drive;
 			format_status = FORMAT_WAIT;
 			format_errors = 0;
@@ -1177,7 +1293,7 @@
 			check_disk_change(inode->i_rdev);
 			return 0;
  	}
-	if (!suser())
+	if (!permission(inode, 2))
 		return -EPERM;
 	if (drive < 0 || drive > 3)
 		return -EINVAL;
@@ -1189,9 +1305,36 @@
 			break;
 		case FDSETPRM:
 		case FDDEFPRM:
+
+			/*
+			 * invalidate buffer cache and track buffer before
+			 * changing the geometry.
+			 */
+			buffer_track = -1;
+			cli();
+			fake_change |= 1 << (drive & 3);
+			sti();
+			check_disk_change(inode->i_rdev);
+			buffer_track = -1;
+
+			current_type[drive] = NULL;
+			floppy_sizes[drive] = MAX_DISK_SIZE;
+			keep_data[drive] = 0;
+
 			memcpy_fromfs(user_params+drive,
 				(void *) param,
 				sizeof(struct floppy_struct));
+
+			/* 
+			 * sanity checking for parameters.
+			 */
+			if (user_params[drive].sect <= 0 || 
+			    user_params[drive].head <= 0 ||
+			    user_params[drive].track <= 0 ||
+			    user_params[drive].track > 
+			    ( 84 >> user_params[drive].stretch ) )
+			  return -EINVAL;
+
 			current_type[drive] = &user_params[drive];
 			floppy_sizes[drive] = user_params[drive].size >> 1;
 			if (cmd == FDDEFPRM)
@@ -1233,13 +1376,13 @@
 inb_p(0x71); \
 })
 
-static struct floppy_struct *find_base(int drive,int code)
+static int *find_base(int drive,int code)
 {
-	struct floppy_struct *base;
+	int *base;
 
 	if (code > 0 && code < 5) {
-		base = &floppy_types[(code-1)*2];
-		printk("fd%d is %s",drive,base->name);
+		base = &floppy_types[(code-1)*4];
+		printk("fd%d is %s",drive,floppy_type[base[1]].name);
 		return base;
 	}
 	printk("fd%d is unknown type %d",drive,code);
@@ -1271,6 +1414,11 @@
 	int old_dev;
 
 	drive = inode->i_rdev & 3;
+
+	if (MINOR(inode->i_rdev) >> 2  >=
+	    sizeof(floppy_type) / sizeof(floppy_type[0]))
+	  return -ENXIO;
+
 	old_dev = fd_device[drive];
 	if (fd_ref[drive])
 		if (old_dev != inode->i_rdev)
diff -ur linux/include/linux/fd.h /linux/include/linux/fd.h
--- linux/include/linux/fd.h	Wed Dec  1 13:44:15 1993
+++ /linux/include/linux/fd.h	Sun Feb  6 11:47:16 1994
@@ -1,6 +1,8 @@
 #ifndef _LINUX_FD_H
 #define _LINUX_FD_H
 
+#include <linux/autoconf.h>
+
 #define FDCLRPRM 0 /* clear user-defined parameters */
 #define FDSETPRM 1 /* set user-defined parameters for current media */
 #define FDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
@@ -14,6 +16,10 @@
 #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 FDGETEMSGTRESH	15	/* get fdc error reporting treshold */
+#define FDGETDRVTYP 16          /* get drive type: 5 1/4 or 3 1/2 */
 
 #define FD_FILL_BYTE 0xF6 /* format fill byte */
 
@@ -31,6 +37,7 @@
 			stretch;	/* !=0 means double track steps */
 	unsigned char	gap,		/* gap1 size */
 			rate,		/* data rate. |= 0x40 for perpendicular */
+	                                /*            |= 0x20 for sector interleaving */
 			spec1,		/* stepping rate, head unload time */
 			fmt_gap;	/* gap2 size */
 	char 	      * name; /* used only for predefined formats */
@@ -39,5 +46,17 @@
 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 */
+};
+
+#ifdef CONFIG_AUTOPROBE_4
+#define FLOPPY_NR_PROBE 4
+#else
+#define FLOPPY_NR_PROBE 2
+#endif
 
 #endif
