Subject: L68K: diffs for 2.1.85
To: Jes.Sorensen@cern.ch (Jes Sorensen),
        linux-m68k@lists.linux-m68k.org (linux-list)
Date: Tue, 24 Feb 1998 08:54:45 +0100 (MET)
From: "J." Dorchain <dorchain@gate.moebelwalther.de>
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: jdorchain@i-con.de

Hi,

these are my diffs for 2.1.85. Most of them were already sent for 2.1.79,
but got lost somehow.

Here the chenges in detail:

- added symbol to make the z2ram-driver work as module
- make lp_mfc.c compile again
- fix type in ser_mfc.c

and last but not least, again

- the revised amiflop driver (Hope it will be finally included in the next
  kernel release)

As these patches don't depend on any I've seen so far, they should all
apply cleanly.

Joerg



--- ./arch/m68k/amiga/amiga_ksyms.c.orig	Mon Feb 23 22:18:32 1998
+++ ./arch/m68k/amiga/amiga_ksyms.c	Mon Feb 23 22:18:35 1998
@@ -17,6 +17,7 @@
 EXPORT_SYMBOL(amiga_chip_alloc);
 EXPORT_SYMBOL(amiga_chip_free);
 EXPORT_SYMBOL(amiga_chip_avail);
+EXPORT_SYMBOL(amiga_chip_size);
 EXPORT_SYMBOL(amiga_audio_period);
 EXPORT_SYMBOL(amiga_audio_min_period);
 
--- ./drivers/char/lp_mfc.c.orig	Mon Feb 23 22:19:19 1998
+++ ./drivers/char/lp_mfc.c	Mon Feb 23 22:19:54 1998
@@ -20,6 +20,7 @@
 #include <asm/amigahw.h>
 #include <linux/zorro.h>
 #include <asm/irq.h>
+#include <asm/setup.h>
 #include <asm/amigaints.h>
 #include "multiface.h"
 #include "mc6821.h"
--- ./drivers/char/ser_mfc.c.orig	Mon Feb 23 22:20:31 1998
+++ ./drivers/char/ser_mfc.c	Mon Feb 23 22:21:09 1998
@@ -341,7 +341,7 @@
   if (!leave_dtr)
     dp->stop_ropc.ropc = 8;
   imask[(info->nr_uarts-1)/2] &= ~48;
-  acmask[(info->nr_uarts-1)/3] &= ~2;
+  acmask[(info->nr_uarts-1)/2] &= ~2;
 }
 intenar = (custom.intenar & IF_EXTER);
 custom.intena = IF_EXTER;
--- ./drivers/block/amiflop.c.orig	Mon Feb 23 21:26:37 1998
+++ ./drivers/block/amiflop.c	Mon Feb 23 22:10:31 1998
@@ -45,11 +45,16 @@
  *    major/minor handling that came with kdev_t. It seems to work for
  *    the time being, but I can't guarantee that it will stay like
  *    that when we start using 16 (24?) bit minors.
+ *
+ * restructured jan 1997 by Joerg Dorchain
+ * - Fixed Bug accessing multiple disks
+ * - some code cleanup
+ * - added trackbuffer for each drive to speed things up
+ * - fixed some race conditions (who finds the next may send it to me ;-)
  */
 
-#ifdef MODULE
 #include <linux/module.h>
-#endif
+
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/fcntl.h>
@@ -61,7 +66,7 @@
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/string.h>
-#include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 
 #include <asm/setup.h>
@@ -82,20 +87,9 @@
 #define IOCTL_RAW_TRACK 0x5254524B  /* 'RTRK' */
 #endif
 
-/* prototypes */
-
-static int amiga_read(int,unsigned char *, unsigned long, int);
-static void amiga_write(int, unsigned long, unsigned char *, int);
-static int dos_read(int, unsigned char *, unsigned long, int);
-static void dos_write(int, unsigned long, unsigned char *,int);
-static ushort dos_crc(void *, int, int, int);
-static void fd_probe(int);
-
-
 /*
  *  Defines
  */
-#define MAX_SECTORS	22
 
 /*
  *  Error codes
@@ -107,6 +101,11 @@
 #define FD_NOTACTIVE	3	/* unit is not active */
 #define FD_NOTREADY	4	/* unit is not ready (motor not on/no disk) */
 
+#define MFM_NOSYNC	1
+#define MFM_HEADER	2
+#define MFM_DATA	3
+#define MFM_TRACK	4
+
 /*
  *  Floppy ID values
  */
@@ -115,8 +114,9 @@
 #define FD_HD_3 	0x55555555  /* high-density 3.5" (1760K) drive */
 #define FD_DD_5 	0xaaaaaaaa  /* double-density 5.25" (440K) drive */
 
-static int fd_def_df0 = 0;     /* default for df0 if it doesn't identify */
+static long int fd_def_df0 = 0;     /* default for df0 if it doesn't identify */
 
+MODULE_PARM(fd_def_df0,"l");
 
 /*
  *  Macros
@@ -147,26 +147,34 @@
 static int floppy_blocksizes[256]={0,};
 /* hardsector size assumed to be 512 */
 
+static int amiga_read(int), dos_read(int);
+static void amiga_write(int), dos_write(int);
 static struct fd_data_type data_types[] = {
   { "Amiga", 11 , amiga_read, amiga_write},
   { "MS-Dos", 9, dos_read, dos_write}
 };
 
 /* current info on each unit */
-static struct amiga_floppy_struct unit[FD_MAX_UNITS];
+static struct amiga_floppy_struct unit[FD_MAX_UNITS] = {{ 0,}};
 
-static struct timer_list flush_track_timer;
+static struct timer_list flush_track_timer[FD_MAX_UNITS];
 static struct timer_list post_write_timer;
 static struct timer_list motor_on_timer;
 static struct timer_list motor_off_timer[FD_MAX_UNITS];
 static int on_attempts;
 
-/* track buffer */
-static int lastdrive = -1;
-static int savedtrack = -1;
+/* Synchronization of FDC access */
+/* request loop (trackbuffer) */
+static volatile int fdc_busy = -1;
+static volatile int fdc_nested = 0;
+static struct wait_queue *fdc_wait = NULL;
+ 
+static struct wait_queue *motor_wait = NULL;
+
+static volatile int selected = -1;	/* currently selected drive */
+
 static int writepending = 0;
 static int writefromint = 0;
-static unsigned char trackdata[MAX_SECTORS * 512];
 static char *raw_buf;
 
 #define RAW_BUF_SIZE 30000  /* size of raw disk data */
@@ -177,21 +185,8 @@
  * request.
  */
 static volatile char block_flag = 0;
-static volatile int selected = 0;
 static struct wait_queue *wait_fd_block = NULL;
 
-/* Synchronization of FDC access */
-/* request loop (trackbuffer) */
-static volatile int fdc_busy = -1;
-static volatile int fdc_nested = 0;
-static struct wait_queue *fdc_wait = NULL;
-/* hardware */
-static volatile int hw_busy = -1;
-static volatile int hw_nested = 0;
-static struct wait_queue *hw_wait = NULL;
- 
-static struct wait_queue *motor_wait = NULL;
-
 /* MS-Dos MFM Coding tables (should go quick and easy) */
 static unsigned char mfmencode[16]={
   0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
@@ -211,13 +206,6 @@
  */
 #define MAX_ERRORS 12
 
-/*
- * The driver is trying to determine the correct media format
- * while probing is set. rw_interrupt() clears it after a
- * successful access.
- */
-static int probing = 0;
-
 /* Prevent "aliased" accesses. */
 static int fd_ref[4] = { 0,0,0,0 };
 static int fd_device[4] = { 0,0,0,0 };
@@ -231,61 +219,15 @@
 /* Current error count. */
 #define CURRENT_ERRORS (CURRENT->errors)
 
-static void get_fdc(int drive)
-{
-unsigned long flags;
 
-       drive &= 3;
-       save_flags(flags);
-       cli();
-       if (fdc_busy != drive)
-         while (!(fdc_busy < 0))
-           sleep_on(&fdc_wait);
-       fdc_busy = drive;
-       fdc_nested++;
-       restore_flags(flags);
-}
 
-static inline void rel_fdc(void)
-{
-#ifdef DEBUG
-       if (fdc_nested == 0)
-         printk("fd: unmatched rel_fdc\n");
-#endif
-       fdc_nested--;
-       if (fdc_nested == 0) {
-         fdc_busy = -1;
-         wake_up(&fdc_wait);
-       }
-}
-
-static void get_hw(int drive)
-{
-unsigned long flags;
+/*
+ * Here come the actual hardware access and helper functions.
+ * They are not reentrant and single threaded because all drives
+ * share the same hardware and the same trackbuffer.
+ */
 
-       drive &= 3;
-       save_flags(flags);
-       cli();
-       if (hw_busy != drive)
-         while (!(hw_busy < 0))
-           sleep_on(&hw_wait);
-       hw_busy = drive;
-       hw_nested++;
-       restore_flags(flags);
-}
-
-static inline void rel_hw(void)
-{
-#ifdef DEBUG
-       if (hw_nested == 0)
-         printk("fd: unmatched hw_rel\n");
-#endif
-       hw_nested--;
-       if (hw_nested == 0) {
-         hw_busy = -1;
-         wake_up(&hw_wait);
-       }
-}
+/* Milliseconds timer */
 
 static void ms_isr(int irq, void *dummy, struct pt_regs *fp)
 {
@@ -314,53 +256,102 @@
   }
 }
 
-/*
- * Functions
- */
-/*======================================================================
-  Turn off the motor of the given drive.  Unit must already be active.
-  Returns standard floppy error code.
-======================================================================*/
-static void fd_motor_off(unsigned long drive)
+/* Hardware semaphore */
+
+/* returns true when we would get the semaphore */
+static inline int try_fdc(int drive)
+{
+	drive &= 3;
+	return ((fdc_busy < 0) || (fdc_busy == drive));
+}
+
+static void get_fdc(int drive)
+{
+unsigned long flags;
+
+       drive &= 3;
+#ifdef DEBUG
+       printk("get_fdc: drive %d  fdc_busy %d  fdc_nested %d\n",drive,fdc_busy,fdc_nested);
+#endif
+       save_flags(flags);
+       cli();
+       while (!try_fdc(drive))
+	       sleep_on(&fdc_wait);
+       fdc_busy = drive;
+       fdc_nested++;
+       restore_flags(flags);
+}
+
+static inline void rel_fdc(void)
+{
+#ifdef DEBUG
+       if (fdc_nested == 0)
+         printk("fd: unmatched rel_fdc\n");
+       printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested);
+#endif
+       fdc_nested--;
+       if (fdc_nested == 0) {
+         fdc_busy = -1;
+         wake_up(&fdc_wait);
+       }
+}
+
+static void fd_select (int drive)
 {
-	unsigned long flags;
 	unsigned char prb = ~0;
 
 	drive&=3;
-	get_hw(drive);
-	save_flags(flags);
-	cli();
+#ifdef DEBUG
+	printk("selecting %d\n",drive);
+#endif
+	if (drive == selected)
+		return;
+	get_fdc(drive);
+	selected = drive;
 
 	if (unit[drive].track % 2 != 0)
 		prb &= ~DSKSIDE;
+	if (unit[drive].motor == 1)
+		prb &= ~DSKMOTOR;
 	ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
 	ciab.prb = prb;
 	prb &= ~SELMASK(drive);
 	ciab.prb = prb;
-	udelay (1);
+	rel_fdc();
+}
+
+static void fd_deselect (int drive)
+{
+	unsigned char prb;
+	unsigned long flags;
+
+	drive&=3;
+#ifdef DEBUG
+	printk("deselecting %d\n",drive);
+#endif
+	if (drive != selected) {
+		printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected);
+		return;
+	}
+
+	get_fdc(drive);
+	save_flags (flags);
+	sti();
+
+	selected = -1;
+
+	prb = ciab.prb;
 	prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
 	ciab.prb = prb;
-	selected = -1;
-	unit[drive].motor = 0;
 
-	rel_hw();
-#ifdef MODULE
-/*
-this is the last interrupt for any drive access, happens after
-release. So we have to wait until now to decrease the use count.
-*/
-       if (fd_ref[drive] == 0)
-         MOD_DEC_USE_COUNT;
-#endif
-	restore_flags(flags);
+	restore_flags (flags);
+	rel_fdc();
+
 }
 
 static void motor_on_callback(unsigned long nr)
 {
-  nr &= 3;
-
 	if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) {
-		unit[nr].motor = 1;
 		wake_up (&motor_wait);
 	} else {
 		motor_on_timer.expires = jiffies + HZ/10;
@@ -368,39 +359,25 @@
 	}
 }
 
-static int motor_on(int nr)
+static int fd_motor_on(int nr)
 {
-	unsigned long flags;
-	unsigned char prb = ~0;
-
 	nr &= 3;
-	get_hw(nr);
-	save_flags (flags);
-	cli();
+
 	del_timer(motor_off_timer + nr);
 
 	if (!unit[nr].motor) {
+		unit[nr].motor = 1;
+		fd_select(nr);
+
 		del_timer(&motor_on_timer);
 		motor_on_timer.data = nr;
 		motor_on_timer.expires = jiffies + HZ/2;
 		add_timer(&motor_on_timer);
-		on_attempts = 10;
-
-
-		prb &= ~DSKMOTOR;
-		if (unit[nr].track % 2 != 0)
-			prb &= ~DSKSIDE;
-		ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-		ciab.prb = prb;
-		prb &= ~SELMASK(nr);
-		ciab.prb = prb;
-		selected = nr;
 
-		while (!unit[nr].motor)
-			sleep_on (&motor_wait);
+		on_attempts = 10;
+		sleep_on (&motor_wait);
+		fd_deselect(nr);
 	}
-	rel_hw();
-	restore_flags(flags);
 
 	if (on_attempts == 0) {
 		on_attempts = -1;
@@ -416,72 +393,58 @@
 	return 1;
 }
 
-static void floppy_off (unsigned int nr)
-{
-	nr&=3;
-	del_timer(motor_off_timer+nr);
-	motor_off_timer[nr].expires = jiffies + 3*HZ;
-	add_timer(motor_off_timer+nr);
-}
-
-static void fd_select (int drive)
+static void fd_motor_off(unsigned long drive)
 {
-	unsigned char prb = ~0;
+long calledfromint;
+#ifdef MODULE
+long decusecount;
 
+	decusecount = drive & 0x40000000;
+#endif
+	calledfromint = drive & 0x80000000;
 	drive&=3;
-	if (drive == selected)
+	if (calledfromint && !try_fdc(drive)) {
+		/* We would be blocked in an interrupt, so try again later */
+		motor_off_timer[drive].expires = jiffies + 1;
+		add_timer(motor_off_timer + drive);
 		return;
-	get_hw(drive);
-	selected = drive;
+	}
+	unit[drive].motor = 0;
+	fd_select(drive);
+	udelay (1);
+	fd_deselect(drive);
 
-	if (unit[drive].track % 2 != 0)
-		prb &= ~DSKSIDE;
-	if (unit[drive].motor == 1)
-		prb &= ~DSKMOTOR;
-	ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-	ciab.prb = prb;
-	prb &= ~SELMASK(drive);
-	ciab.prb = prb;
-	rel_hw();
+#ifdef MODULE
+/*
+this is the last interrupt for any drive access, happens after
+release (from floppy_off). So we have to wait until now to decrease
+the use count.
+*/
+       if (decusecount)
+         MOD_DEC_USE_COUNT;
+#endif
 }
 
-static void fd_deselect (int drive)
+static void floppy_off (unsigned int nr)
 {
-	unsigned char prb;
-	unsigned long flags;
-
-	drive&=3;
-	if (drive != selected)
-		return;
-
-	get_hw(drive);
-	save_flags (flags);
-	sti();
-
-	selected = -1;
-
-	prb = ciab.prb;
-	prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-	ciab.prb = prb;
-
-	restore_flags (flags);
-	rel_hw();
+int drive;
 
+	drive = nr & 3;
+	del_timer(motor_off_timer + drive);
+	motor_off_timer[drive].expires = jiffies + 3*HZ;
+	/* called this way it is always from interrupt */
+	motor_off_timer[drive].data = nr | 0x80000000;
+	add_timer(motor_off_timer + nr);
 }
 
-/*======================================================================
-  Seek the drive to track 0.
-  The drive must be active and the motor must be running.
-  Returns standard floppy error code.
-======================================================================*/
 static int fd_calibrate(int drive)
 {
 	unsigned char prb;
 	int n;
 
 	drive &= 3;
-	get_hw(drive);
-	if (!motor_on (drive))
+	get_fdc(drive);
+	if (!fd_motor_on (drive))
 		return 0;
 	fd_select (drive);
 	prb = ciab.prb;
@@ -511,46 +474,45 @@
 		if ((ciaa.pra & DSKTRACK0) == 0)
 			break;
 		if (--n == 0) {
-			printk (KERN_ERR "calibrate failed, turning motor off\n");
+			printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive);
 			fd_motor_off (drive);
 			unit[drive].track = -1;
-			rel_hw();
+			rel_fdc();
 			return 0;
 		}
 	}
 	unit[drive].track = 0;
 	ms_delay(unit[drive].type->settle_time);
 
-	rel_hw();
+	rel_fdc();
+	fd_deselect(drive);
 	return 1;
 }
 
-/*======================================================================
-  Seek the drive to the requested cylinder.
-  The drive must have been calibrated at some point before this.
-  The drive must also be active and the motor must be running.
-======================================================================*/
 static int fd_seek(int drive, int track)
 {
 	unsigned char prb;
 	int cnt;
 
+#ifdef DEBUG
+	printk("seeking drive %d to track %d\n",drive,track);
+#endif
 	drive &= 3;
-	get_hw(drive);
+	get_fdc(drive);
 	if (unit[drive].track == track) {
-		rel_hw();
+		rel_fdc();
 		return 1;
 	}
-	if (!motor_on(drive)) {
-		rel_hw();
+	if (!fd_motor_on(drive)) {
+		rel_fdc();
 		return 0;
 	}
-	fd_select (drive);
 	if (unit[drive].track < 0 && !fd_calibrate(drive)) {
-		rel_hw();
+		rel_fdc();
 		return 0;
 	}
 
+	fd_select (drive);
 	cnt = unit[drive].track/2 - track/2;
 	prb = ciab.prb;
 	prb |= DSKSIDE | DSKDIREC;
@@ -565,7 +527,8 @@
 		ms_delay (unit[drive].type->side_time);
 	unit[drive].track = track;
 	if (cnt == 0) {
-		rel_hw();
+		rel_fdc();
+		fd_deselect(drive);
 		return 1;
 	}
 	do {
@@ -578,119 +541,189 @@
 	} while (--cnt != 0);
 	ms_delay (unit[drive].type->settle_time);
 
-	rel_hw();
+	rel_fdc();
+	fd_deselect(drive);
 	return 1;
 }
 
-static void encode(unsigned long data, unsigned long *dest)
+static unsigned long fd_get_drive_id(int drive)
 {
-  unsigned long data2;
+	int i;
+	ulong id = 0;
 
-  data &= 0x55555555;
-  data2 = data ^ 0x55555555;
-  data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
+  	drive&=3;
+  	get_fdc(drive);
+	/* set up for ID */
+	MOTOR_ON;
+	udelay(2);
+	SELECT(SELMASK(drive));
+	udelay(2);
+	DESELECT(SELMASK(drive));
+	udelay(2);
+	MOTOR_OFF;
+	udelay(2);
+	SELECT(SELMASK(drive));
+	udelay(2);
+	DESELECT(SELMASK(drive));
+	udelay(2);
 
-  if (*(dest - 1) & 0x00000001)
-    data &= 0x7FFFFFFF;
+	/* loop and read disk ID */
+	for (i=0; i<32; i++) {
+		SELECT(SELMASK(drive));
+		udelay(2);
 
-  *dest = data;
+		/* read and store value of DSKRDY */
+		id <<= 1;
+		id |= (ciaa.pra & DSKRDY) ? 0 : 1;	/* cia regs are low-active! */
+
+		DESELECT(SELMASK(drive));
+	}
+
+	rel_fdc();
+
+        /*
+         * RB: At least A500/A2000's df0: don't identify themselves.
+         * As every (real) Amiga has at least a 3.5" DD drive as df0:
+         * we default to that if df0: doesn't identify as a certain
+         * type.
+         */
+        if(drive == 0 && id == FD_NODRIVE)
+         {
+                id = fd_def_df0;
+                printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0);
+         }
+	/* return the ID value */
+	return (id);
 }
 
-static void encode_block(unsigned long *dest, unsigned long *src, int len)
+static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
 {
-  int cnt, to_cnt = 0;
-  unsigned long data;
+  if (block_flag)
+    custom.dsklen = 0x4000;
 
-  /* odd bits */
-  for (cnt = 0; cnt < len / 4; cnt++) {
-    data = src[cnt] >> 1;
-    encode(data, dest + to_cnt++);
+  if (block_flag == 2) { /* writing */
+    writepending = 2;
+    post_write_timer.expires = jiffies + 1; /* at least 2 ms */
+    post_write_timer.data = selected;
+    add_timer(&post_write_timer);
   }
-
-  /* even bits */
-  for (cnt = 0; cnt < len / 4; cnt++) {
-    data = src[cnt];
-    encode(data, dest + to_cnt++);
+  else {                /* reading */
+    block_flag = 0;
+    wake_up (&wait_fd_block);
   }
 }
 
-unsigned long checksum(unsigned long *addr, int len)
+static void raw_read(int drive)
 {
-	unsigned long csum = 0;
+	drive&=3;
+	get_fdc(drive);
+	while (block_flag)
+		sleep_on(&wait_fd_block);
+	fd_select(drive);
+	/* setup adkcon bits correctly */
+	custom.adkcon = ADK_MSBSYNC;
+	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
 
-	len /= sizeof(*addr);
-	while (len-- > 0)
-		csum ^= *addr++;
-	csum = ((csum>>1) & 0x55555555)  ^  (csum & 0x55555555);
+	custom.dsksync = MFM_SYNC;
 
-	return csum;
-}
+	custom.dsklen = 0;
+	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
+	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
+	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
 
-struct header {
-	unsigned char magic;
-	unsigned char track;
-	unsigned char sect;
-	unsigned char ord;
-	unsigned char labels[16];
-	unsigned long hdrchk;
-	unsigned long datachk;
-};
+	block_flag = 1;
+
+	while (block_flag)
+		sleep_on (&wait_fd_block);
+
+	custom.dsklen = 0;
+	fd_deselect(drive);
+	rel_fdc();
+}
 
-static unsigned long *putsec(int disk, unsigned long *raw, int track, int cnt,
-			     unsigned char *data)
+static int raw_write(int drive)
 {
-	struct header hdr;
-	int i;
+	ushort adk;
 
-	disk&=3;
-	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
-	raw++;
-	*raw++ = 0x44894489;
+	drive&=3;
+	get_fdc(drive); /* corresponds to rel_fdc() in post_write() */
+	if ((ciaa.pra & DSKPROT) == 0) {
+		rel_fdc();
+		return 0;
+	}
+	while (block_flag)
+		sleep_on(&wait_fd_block);
+	fd_select(drive);
+	/* clear adkcon bits */
+	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
+	/* set appropriate adkcon bits */
+	adk = ADK_SETCLR|ADK_FAST;
+	if ((ulong)unit[drive].track >= unit[drive].type->precomp2)
+		adk |= ADK_PRECOMP1;
+	else if ((ulong)unit[drive].track >= unit[drive].type->precomp1)
+		adk |= ADK_PRECOMP0;
+	custom.adkcon = adk;
 
-	hdr.magic = 0xFF;
-	hdr.track = track;
-	hdr.sect = cnt;
-	hdr.ord = unit[disk].sects-cnt;
-	for (i = 0; i < 16; i++)
-		hdr.labels[i] = 0;
-	hdr.hdrchk = checksum((ulong *)&hdr,
-			      (char *)&hdr.hdrchk-(char *)&hdr);
-	hdr.datachk = checksum((ulong *)data, 512);
+	custom.dsklen = DSKLEN_WRITE;
+	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
+	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
 
-	encode_block(raw, (ulong *)&hdr.magic, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)&hdr.labels, 16);
-	raw += 8;
-	encode_block(raw, (ulong *)&hdr.hdrchk, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)&hdr.datachk, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)data, 512);
-	raw += 256;
+	block_flag = 2;
+	return 1;
+}
 
-	return raw;
+/*
+ * to be called at least 2ms after the write has finished but before any
+ * other access to the hardware.
+ */
+static void post_write (unsigned long drive)
+{
+#ifdef DEBUG
+  printk("post_write for drive %ld\n",drive);
+#endif
+  drive &= 3;
+  custom.dsklen = 0;
+  block_flag = 0;
+  writepending = 0;
+  writefromint = 0;
+  unit[drive].dirty = 0;
+  wake_up(&wait_fd_block);
+  fd_deselect(drive);
+  rel_fdc(); /* corresponds to get_fdc() in raw_write */
 }
 
 
-/*==========================================================================
-  amiga_write converts track/labels data to raw track data
-==========================================================================*/
-static void amiga_write(int disk, unsigned long raw, unsigned char *data,
-			int track)
+/*
+ * The following functions are to convert the block contents into raw data
+ * written to disk and vice versa.
+ * (Add other formats here ;-))
+ */
+
+static unsigned long scan_sync(unsigned long raw, unsigned long end)
 {
-	unsigned int cnt;
-	unsigned long *ptr = (unsigned long *)raw;
+	ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
 
-	disk&=3;
-	/* gap space */
-	for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
-		*ptr++ = 0xaaaaaaaa;
+	while (ptr < endp && *ptr++ != 0x4489)
+		;
+	if (ptr < endp) {
+		while (*ptr == 0x4489 && ptr < endp)
+			ptr++;
+		return (ulong)ptr;
+	}
+	return 0;
+}
 
-	/* sectors */
-	for (cnt = 0; cnt < unit[disk].sects; cnt++)
-		ptr = putsec (disk, ptr, track, cnt, data + cnt*512);
-	*(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
-	raw = (unsigned long)ptr + 2;
+static inline unsigned long checksum(unsigned long *addr, int len)
+{
+	unsigned long csum = 0;
+
+	len /= sizeof(*addr);
+	while (len-- > 0)
+		csum ^= *addr++;
+	csum = ((csum>>1) & 0x55555555)  ^  (csum & 0x55555555);
+
+	return csum;
 }
 
 static unsigned long decode (unsigned long *data, unsigned long *raw,
@@ -713,45 +746,29 @@
 	return (ulong)raw;
 }
 
-#define MFM_NOSYNC	1
-#define MFM_HEADER	2
-#define MFM_DATA	3
-#define MFM_TRACK	4
-
-/*==========================================================================
- scan_sync - looks for the next start of sector marked by a sync. d3 is the
-		sector number (10..0). When d3 = 10, can't be certain of a
-		starting sync.
-==========================================================================*/
-static unsigned long scan_sync(unsigned long raw, unsigned long end)
-{
-	ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
-
-	while (ptr < endp && *ptr++ != 0x4489)
-		;
-	if (ptr < endp) {
-		while (*ptr == 0x4489 && ptr < endp)
-			ptr++;
-		return (ulong)ptr;
-	}
-	return 0;
-}
+struct header {
+	unsigned char magic;
+	unsigned char track;
+	unsigned char sect;
+	unsigned char ord;
+	unsigned char labels[16];
+	unsigned long hdrchk;
+	unsigned long datachk;
+};
 
-/*==========================================================================
-  amiga_read reads a raw track of data into a track buffer
-==========================================================================*/
-static int amiga_read(int drive, unsigned char *track_data,
-		      unsigned long raw, int track)
+static int amiga_read(int drive)
 {
+	unsigned long raw;
 	unsigned long end;
 	int scnt;
 	unsigned long csum;
 	struct header hdr;
 
 	drive&=3;
+	raw = (long) raw_buf;
 	end = raw + unit[drive].type->read_size;
 
-	for (scnt = 0;scnt < unit[drive].sects; scnt++) {
+	for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
 		if (!(raw = scan_sync(raw, end))) {
 			printk (KERN_INFO "can't find sync for sector %d\n", scnt);
 			return MFM_NOSYNC;
@@ -778,24 +795,24 @@
 		}
 
 		/* verify track */
-		if (hdr.track != track) {
-			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, track);
+		if (hdr.track != unit[drive].track) {
+			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track);
 			return MFM_TRACK;
 		}
 
-		raw = decode ((ulong *)(track_data + hdr.sect*512),
+		raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512),
 			      (ulong *)raw, 512);
-		csum = checksum((ulong *)(track_data + hdr.sect*512), 512);
+		csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512);
 
 		if (hdr.datachk != csum) {
 			printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
 			       hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
 			       hdr.datachk, csum);
 			printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n",
-				((ulong *)(track_data+hdr.sect*512))[0],
-				((ulong *)(track_data+hdr.sect*512))[1],
-				((ulong *)(track_data+hdr.sect*512))[2],
-				((ulong *)(track_data+hdr.sect*512))[3]);
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]);
 			return MFM_DATA;
 		}
 	}
@@ -803,6 +820,89 @@
 	return 0;
 }
 
+static void encode(unsigned long data, unsigned long *dest)
+{
+  unsigned long data2;
+
+  data &= 0x55555555;
+  data2 = data ^ 0x55555555;
+  data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
+
+  if (*(dest - 1) & 0x00000001)
+    data &= 0x7FFFFFFF;
+
+  *dest = data;
+}
+
+static void encode_block(unsigned long *dest, unsigned long *src, int len)
+{
+  int cnt, to_cnt = 0;
+  unsigned long data;
+
+  /* odd bits */
+  for (cnt = 0; cnt < len / 4; cnt++) {
+    data = src[cnt] >> 1;
+    encode(data, dest + to_cnt++);
+  }
+
+  /* even bits */
+  for (cnt = 0; cnt < len / 4; cnt++) {
+    data = src[cnt];
+    encode(data, dest + to_cnt++);
+  }
+}
+
+static unsigned long *putsec(int disk, unsigned long *raw, int cnt)
+{
+	struct header hdr;
+	int i;
+
+	disk&=3;
+	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
+	raw++;
+	*raw++ = 0x44894489;
+
+	hdr.magic = 0xFF;
+	hdr.track = unit[disk].track;
+	hdr.sect = cnt;
+	hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt;
+	for (i = 0; i < 16; i++)
+		hdr.labels[i] = 0;
+	hdr.hdrchk = checksum((ulong *)&hdr,
+			      (char *)&hdr.hdrchk-(char *)&hdr);
+	hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512);
+
+	encode_block(raw, (ulong *)&hdr.magic, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)&hdr.labels, 16);
+	raw += 8;
+	encode_block(raw, (ulong *)&hdr.hdrchk, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)&hdr.datachk, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512);
+	raw += 256;
+
+	return raw;
+}
+
+static void amiga_write(int disk)
+{
+	unsigned int cnt;
+	unsigned long *ptr = (unsigned long *)raw_buf;
+
+	disk&=3;
+	/* gap space */
+	for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
+		*ptr++ = 0xaaaaaaaa;
+
+	/* sectors */
+	for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
+		ptr = putsec (disk, ptr, cnt);
+	*(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
+}
+
+
 struct dos_header {
 unsigned char track,   /* 0-80 */
               side,    /* 0-1 */
@@ -811,22 +911,12 @@
 unsigned short crc;     /* on 68000 we got an alignment problem, 
                            but this compiler solves it  by adding silently 
                            adding a pad byte so data won't fit
-                           and this cost about 3h to discover.... */
+                           and this took about 3h to discover.... */
 unsigned char gap1[22];     /* for longword-alignedness (0x4e) */
 };
 
 /* crc routines are borrowed from the messydos-handler  */
 
-static inline ushort dos_hdr_crc (struct dos_header *hdr)
-{
-return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
-}
-
-static inline ushort dos_data_crc(unsigned char *data)
-{
-return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
-}
-
 /* excerpt from the messydos-device           
 ; The CRC is computed not only over the actual data, but including
 ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
@@ -937,6 +1027,16 @@
 return (crch<<8)|crcl;
 }
 
+static inline ushort dos_hdr_crc (struct dos_header *hdr)
+{
+return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
+}
+
+static inline ushort dos_data_crc(unsigned char *data)
+{
+return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
+}
+
 static inline unsigned char dos_decode_byte(ushort word)
 {
 register ushort w2;
@@ -965,30 +1065,28 @@
 #ifdef DEBUG
 static void dbg(unsigned long ptr)
 {
-printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr,
-  ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]);
+  printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr,
+    ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]);
 }
 #endif
 
-/*******************************************************************
-   this reads a raw track of data into trackbuffer for ms-disks 
-*******************************************************************/
-static int dos_read(int drive, unsigned char *track_data,
-	unsigned long raw, int track)
+static int dos_read(int drive)
 {
   unsigned long end;
+  unsigned long raw;
   int scnt;
   unsigned short crc,data_crc[2];
   struct dos_header hdr;
 
   drive&=3;
+  raw = (long) raw_buf;
   end = raw + unit[drive].type->read_size;
 
-  for (scnt=0;scnt<unit[drive].sects;scnt++) {
+  for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
   do { /* search for the right sync of each sec-hdr */
     if (!(raw = scan_sync (raw, end))) {
       printk(KERN_INFO "dos_read: no hdr sync on track %d, unit %d for sector %d\n",
-        track,drive,scnt);
+        unit[drive].track,drive,scnt);
       return MFM_NOSYNC;
     }
 #ifdef DEBUG
@@ -1008,15 +1106,15 @@
     printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
     return MFM_HEADER;
   }
-  if (hdr.track != track/unit[drive].type->heads) {
+  if (hdr.track != unit[drive].track/unit[drive].type->heads) {
     printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", hdr.track,
-      track/unit[drive].type->heads);
+      unit[drive].track/unit[drive].type->heads);
     return MFM_TRACK;
   }
 
-  if (hdr.side != track%unit[drive].type->heads) {
+  if (hdr.side != unit[drive].track%unit[drive].type->heads) {
     printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", hdr.side,
-      track%unit[drive].type->heads);
+      unit[drive].track%unit[drive].type->heads);
     return MFM_TRACK;
   }
 
@@ -1029,7 +1127,7 @@
 #endif
   if (!(raw = scan_sync (raw, end))) {
     printk(KERN_INFO "dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
-      track, drive, scnt, hdr.sec);
+      unit[drive].track, drive, scnt, hdr.sec);
     return MFM_NOSYNC;
   }
 #ifdef DEBUG
@@ -1043,19 +1141,19 @@
   }
 
   raw+=2;  /* skip data mark (included in checksum) */
-  raw = dos_decode((unsigned char *)(track_data + (hdr.sec - 1) * 512), (ushort *) raw, 512);
+  raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512);
   raw = dos_decode((unsigned char  *)data_crc,(ushort *) raw,4);
-  crc = dos_data_crc(track_data + (hdr.sec - 1) * 512);
+  crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512);
 
   if (crc != data_crc[0]) {
     printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
       hdr.track, hdr.side, hdr.sec, hdr.len_desc,
       scnt,data_crc[0], crc);
     printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n",
-      ((ulong *)(track_data+(hdr.sec-1)*512))[0],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[1],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[2],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[3]);
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]);
     return MFM_DATA;
   }
   }
@@ -1086,8 +1184,7 @@
 }
 }
 
-static unsigned long *ms_putsec(int drive, unsigned long *raw, int track, int cnt,
-   unsigned char *data)
+static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt)
 {
 static struct dos_header hdr={0,0,0,2,0,
   {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}};
@@ -1104,8 +1201,8 @@
 *raw++=0x44895554;
 
 /* fill in the variable parts of the header */
-hdr.track=track/unit[drive].type->heads;
-hdr.side=track%unit[drive].type->heads;
+hdr.track=unit[drive].track/unit[drive].type->heads;
+hdr.side=unit[drive].track%unit[drive].type->heads;
 hdr.sec=cnt+1;
 hdr.crc=dos_hdr_crc(&hdr);
 
@@ -1122,229 +1219,82 @@
 *raw++=0x44895545;
 
 /* data */
-dos_encode_block((ushort *)raw,(unsigned char *)data,512);
-raw+=256;
-
-/*data crc + jd's special gap (long words :-/) */
-crc[0]=dos_data_crc(data);
-dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
-raw+=2;
-
-/* data gap */
-for(i=0;i<38;i++)
-  *raw++=0x92549254;
-
-return raw; /* wrote 652 MFM words */
-}
-
-
-/**************************************************************
-  builds encoded track data from trackbuffer data
-**************************************************************/
-static void dos_write(int disk, unsigned long raw, unsigned char *data,
-    int track)
-{
-int cnt;
-unsigned long *ptr=(unsigned long *)raw;
-
-disk&=3;
-/* really gap4 + indexgap , but we write it first and round it up */
-for (cnt=0;cnt<425;cnt++)
-  *ptr++=0x92549254;
-
-/* the following is just guessed */
-if (unit[disk].type->sect_mult==2)  /* check for HD-Disks */
-  for(cnt=0;cnt<473;cnt++)
-    *ptr++=0x92549254;
-
-/* now the index marks...*/
-for (cnt=0;cnt<20;cnt++)
-  *ptr++=0x92549254;
-for (cnt=0;cnt<6;cnt++)
-  *ptr++=0xaaaaaaaa;
-*ptr++=0x52245224;
-*ptr++=0x52245552;
-for (cnt=0;cnt<20;cnt++)
-  *ptr++=0x92549254;
-
-/* sectors */
-for(cnt=0;cnt<unit[disk].sects;cnt++)
-  ptr=ms_putsec(disk,ptr,track,cnt,data+cnt*512);
-
-*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
-}
-
-static void request_done(int uptodate)
-{
-  timer_active &= ~(1 << FLOPPY_TIMER);
-  end_request(uptodate);
-}
-
-/*
- * 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 amiga_floppy_change(kdev_t dev)
-{
-	int drive = dev & 3;
-	int changed;
-	static int first_time = 1;
-
-	if (MAJOR(dev) != MAJOR_NR) {
-		printk(KERN_CRIT "floppy_change: not a floppy\n");
-		return 0;
-	}
-
-	if (first_time)
-		changed = first_time--;
-	else {
-		get_hw(drive);
-		fd_select (drive);
-		changed = !(ciaa.pra & DSKCHANGE);
-		fd_deselect (drive);
-		rel_hw();
-	}
-
-	if (changed) {
-		fd_probe(dev);
-		unit[drive].track = -1;
-		selected = -1;
-		savedtrack = -1;
-		writepending = 0; /* if this was true before, too bad! */
-		writefromint = 0;
-		return 1;
-	}
-	return 0;
-}
-
-static __inline__ void copy_buffer(void *from, void *to)
-{
-  ulong *p1,*p2;
-  int cnt;
-
-  p1 = (ulong *)from;
-  p2 = (ulong *)to;
-
-  for (cnt = 0; cnt < 512/4; cnt++)
-    *p2++ = *p1++;
-}
-
-static void raw_read(int drive, int track, char *ptrack, int len)
-{
-	drive&=3;
-	get_hw(drive);
-	/* setup adkcon bits correctly */
-	custom.adkcon = ADK_MSBSYNC;
-	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
-
-	custom.dsksync = MFM_SYNC;
-
-	custom.dsklen = 0;
-#if 0
-	ms_delay (unit[drive].type->side_time);
-#endif
-	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
+dos_encode_block((ushort *)raw,(unsigned char *)unit[drive].trackbuf+cnt*512,512);
+raw+=256;
 
-	block_flag = 1;
+/*data crc + jd's special gap (long words :-/) */
+crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512);
+dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
+raw+=2;
 
-	while (block_flag == 1)
-		sleep_on (&wait_fd_block);
+/* data gap */
+for(i=0;i<38;i++)
+  *raw++=0x92549254;
 
-	custom.dsklen = 0;
-	rel_hw();
+return raw; /* wrote 652 MFM words */
 }
 
-static int raw_write(int drive, int track, char *ptrack, int len)
+static void dos_write(int disk)
 {
-	ushort adk;
+int cnt;
+unsigned long raw = (unsigned long) raw_buf;
+unsigned long *ptr=(unsigned long *)raw;
 
-	drive&=3;
-	if ((ciaa.pra & DSKPROT) == 0)
-		return 0;
+disk&=3;
+/* really gap4 + indexgap , but we write it first and round it up */
+for (cnt=0;cnt<425;cnt++)
+  *ptr++=0x92549254;
 
-	get_hw(drive); /* corresponds to rel_hw() in post_write() */
-	/* clear adkcon bits */
-	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
-	/* set appropriate adkcon bits */
-	adk = ADK_SETCLR|ADK_FAST;
-	if ((ulong)track >= unit[drive].type->precomp2)
-		adk |= ADK_PRECOMP1;
-	else if ((ulong)track >= unit[drive].type->precomp1)
-		adk |= ADK_PRECOMP0;
-	custom.adkcon = adk;
+/* the following is just guessed */
+if (unit[disk].type->sect_mult==2)  /* check for HD-Disks */
+  for(cnt=0;cnt<473;cnt++)
+    *ptr++=0x92549254;
 
-	custom.dsklen = DSKLEN_WRITE;
-#if 0
-	ms_delay (unit[drive].type->side_time);
-#endif
-	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+/* now the index marks...*/
+for (cnt=0;cnt<20;cnt++)
+  *ptr++=0x92549254;
+for (cnt=0;cnt<6;cnt++)
+  *ptr++=0xaaaaaaaa;
+*ptr++=0x52245224;
+*ptr++=0x52245552;
+for (cnt=0;cnt<20;cnt++)
+  *ptr++=0x92549254;
 
-	block_flag = 2;
-	return 1;
-}
+/* sectors */
+for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
+  ptr=ms_putsec(disk,ptr,cnt);
 
-static void post_write (unsigned long dummy)
-{
-  custom.dsklen = 0;
-  writepending = 0;
-  writefromint = 0;
-  rel_hw(); /* corresponds to get_hw() in raw_write */
+*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
 }
 
-static int get_track(int drive, int track)
-{
-	int error, errors;
-
-	drive&=3;
-        get_hw(drive);
-        if (!motor_on(drive)) {
-          rel_hw();
-          return -1;
-        }
-        fd_select(drive);
-        errors = 0;
-        while (errors < MAX_ERRORS) {
-          if (!fd_seek(drive, track)) {
-            rel_hw();
-            return -1; /* we can not calibrate - no chance */
-          }
-          if ((lastdrive == drive) && (savedtrack == track)) {
-            rel_hw();
-            return 0;
-          }
-          lastdrive = drive;
-          raw_read(drive, track, raw_buf, unit[drive].type->read_size);
-          savedtrack = -1;
-          error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track);
-          if (error == 0) {
-            savedtrack = track;
-            rel_hw();
-            return 0;
-          }
-          if (error == MFM_TRACK)
-            unit[drive].track = -1;
-          errors++;
-	}
-	rel_hw();
-	return -1;
-}
+/*
+ * Here comes the high level stuff (i.e. the filesystem interface)
+ * and helper functions.
+ * Normally this should be the only part that has to be adapted to
+ * different kernel versions.
+ */
 
+/* FIXME: this assumes the drive is still spinning -
+ * which is only true if we complete writing a track within three seconds
+ */
 static void flush_track_callback(unsigned long nr)
 {
-  nr&=3;
-  writefromint = 1;
-  (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
-  if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
-    printk (KERN_NOTICE "floppy disk write protected\n");
-    writefromint = 0;
-    writepending = 0;
-  }
+	nr&=3;
+	writefromint = 1;
+	if (!try_fdc(nr)) {
+		/* we might block in an interrupt, so try again later */
+		flush_track_timer[nr].expires = jiffies + 1;
+		add_timer(flush_track_timer + nr);
+		return;
+	}
+	get_fdc(nr);
+	(*unit[nr].dtype->write_fkt)(nr);
+	if (!raw_write(nr)) {
+		printk (KERN_NOTICE "floppy disk write protected\n");
+		writefromint = 0;
+		writepending = 0;
+	}
+	rel_fdc();
 }
 
 static int non_int_flush_track (unsigned long nr)
@@ -1354,12 +1304,18 @@
   nr&=3;
   writefromint = 0;
   del_timer(&post_write_timer);
+  get_fdc(nr);
+  if (!fd_motor_on(nr)) {
+  	writepending = 0;
+  	rel_fdc();
+  	return 0;
+  }
   save_flags(flags);
   cli();
   if (writepending != 2) {
     restore_flags(flags);
-    (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
-    if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
+    (*unit[nr].dtype->write_fkt)(nr);
+    if (!raw_write(nr)) {
       printk (KERN_NOTICE "floppy disk write protected in write!\n");
       writepending = 0;
       return 0;
@@ -1367,13 +1323,50 @@
     while (block_flag == 2)
       sleep_on (&wait_fd_block);
   }
-  else
+  else {
     restore_flags(flags);
-  ms_delay(2); /* 2 ms post_write delay */
-  post_write(0);
+    ms_delay(2); /* 2 ms post_write delay */
+    post_write(nr);
+  }
+  rel_fdc();
   return 1;
 }
 
+static int get_track(int drive, int track)
+{
+	int error, errcnt;
+
+	drive&=3;
+	if (unit[drive].track == track)
+		return 0;
+	get_fdc(drive);
+	if (!fd_motor_on(drive)) {
+		rel_fdc();
+		return -1;
+	}
+
+	if (unit[drive].dirty == 1) {
+		del_timer (flush_track_timer + drive);
+		non_int_flush_track (drive);
+	}
+	errcnt = 0;
+	while (errcnt < MAX_ERRORS) {
+		if (!fd_seek(drive, track))
+			return -1;
+		raw_read(drive);
+		error = (*unit[drive].dtype->read_fkt)(drive);
+		if (error == 0) {
+			rel_fdc();
+			return 0;
+		}
+		/* Read Error Handling: recalibrate and try again */
+		unit[drive].track = -1;
+		errcnt++;
+	}
+	rel_fdc();
+	return -1;
+}
+
 static void redo_fd_request(void)
 {
 	unsigned int cnt, block, track, sector;
@@ -1388,10 +1381,7 @@
 
     repeat:
 	if (!CURRENT) {
-		if (fdc_busy < 0)
-			printk(KERN_CRIT "FDC access conflict!");
-		rel_fdc();
-		CLEAR_INTR;
+		/* Nothing left to do */
 		return;
 	}
 
@@ -1401,74 +1391,64 @@
 	if (CURRENT->bh && !buffer_locked(CURRENT->bh))
 		panic(DEVICE_NAME ": block not locked");
 
-	probing = 0;
 	device = MINOR(CURRENT_DEVICE);
-	if (device > 3) {
+	if (device < 8) {
 		/* manual selection */
 		drive = device & 3;
 		floppy = unit + drive;
 	} else {
 		/* Auto-detection */
-		/* printk("redo_fd_request: can't handle auto detect\n");*/
-		/* printk("redo_fd_request: default to normal\n");*/
+#ifdef DEBUG
+		printk("redo_fd_request: can't handle auto detect\n");
+		printk("redo_fd_request: default to normal\n");
+#endif
 		drive = device & 3;
 		floppy = unit + drive;
 	}
 
-	save_flags (flags);
-	cli();
-	if (drive != selected && writepending) {
-	  del_timer (&flush_track_timer);
-	  restore_flags (flags);
-	  if (!non_int_flush_track (selected)) {
-	    end_request(0);
-	    goto repeat;
-	  }
-	} else
-	  restore_flags (flags);
-
  /* Here someone could investigate to be more efficient */
 	for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) { 
 #ifdef DEBUG
-		printk("fd: sector %d + %d requested\n",CURRENT->sector,cnt);
+		printk("fd: sector %ld + %d requested for %s\n",CURRENT->sector,cnt,
+			(CURRENT->cmd==READ)?"read":"write");
 #endif
 		block = CURRENT->sector + cnt;
 		if ((int)block > floppy->blocks) {
-			request_done(0);
+			end_request(0);
 			goto repeat;
 		}
 
-		track = block / floppy->sects;
-		sector = block % floppy->sects;
+		track = block / (floppy->dtype->sects * floppy->type->sect_mult);
+		sector = block % (floppy->dtype->sects * floppy->type->sect_mult);
 		data = CURRENT->buffer + 512 * cnt;
+#ifdef DEBUG
+		printk("access to track %d, sector %d, with buffer at 0x%08lx\n",
+			track, sector, data);
+#endif
 
-		save_flags (flags);
-		cli();
-		if (track != savedtrack && writepending) {
-		  del_timer (&flush_track_timer);
-		  restore_flags (flags);
-		  if (!non_int_flush_track (selected)) {
-		    end_request(0);
-		    goto repeat;
-		  }
-		} else
-		  restore_flags (flags);
+		if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) {
+			printk(KERN_WARNING "do_fd_request: unknown command\n");
+			end_request(0);
+			goto repeat;
+		}
+		if (get_track(drive, track) == -1) {
+			end_request(0);
+			goto repeat;
+		}
 
 		switch (CURRENT->cmd) {
 		    case READ:
-			if (get_track(drive, track) == -1) {
-				end_request(0);
-				goto repeat;
-			}
-			copy_buffer(trackdata + sector * 512, data);
+			memcpy(data, unit[drive].trackbuf + sector * 512, 512);
 			break;
 
 		    case WRITE:
-			if (get_track(drive, track) == -1) {
+			memcpy(unit[drive].trackbuf + sector * 512, data, 512);
+
+			/* keep the drive spinning while writes are scheduled */
+			if (!fd_motor_on(drive)) {
 				end_request(0);
 				goto repeat;
 			}
-			copy_buffer(data, trackdata + sector * 512);
 			/*
 			 * setup a callback to write the track buffer
 			 * after a short (1 tick) delay.
@@ -1476,33 +1456,25 @@
 			save_flags (flags);
 			cli();
 
-			if (writepending)
-			    /* reset the timer */
-			    del_timer (&flush_track_timer);
+			unit[drive].dirty = 1;
+		        /* reset the timer */
+		        del_timer (flush_track_timer + drive);
 			    
-			writepending = 1;
-			flush_track_timer.data = drive;
-			flush_track_timer.expires = jiffies + 1;
-			add_timer (&flush_track_timer);
+			flush_track_timer[drive].expires = jiffies + 1;
+			add_timer (flush_track_timer + drive);
 			restore_flags (flags);
 			break;
-
-		    default:
-			printk(KERN_WARNING "do_fd_request: unknown command\n");
-			request_done(0);
-			goto repeat;
 		}
 	}
 	CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
 	CURRENT->sector += CURRENT->current_nr_sectors;
 
-	request_done(1);
+	end_request(1);
 	goto repeat;
 }
 
 static void do_fd_request(void)
 {
-	get_fdc(CURRENT_DEVICE & 3);
 	redo_fd_request();
 }
 
@@ -1512,53 +1484,48 @@
 	int drive = inode->i_rdev & 3;
 	static struct floppy_struct getprm;
 	struct super_block * sb;
-	unsigned long flags;
 
 	switch(cmd){
 	case HDIO_GETGEO:
 	{
 		struct hd_geometry loc;
 		loc.heads = unit[drive].type->heads;
-		loc.sectors = unit[drive].sects;
+		loc.sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult;
 		loc.cylinders = unit[drive].type->tracks;
 		loc.start = 0;
 		if (copy_to_user((void *)param, (void *)&loc,
-				 sizeof(struct hd_geometry)))
+					  sizeof(struct hd_geometry)))
 			return -EFAULT;
 		break;
 	}
 	case FDFMTBEG:
-		get_hw(drive);
+		get_fdc(drive);
 		if (fd_ref[drive] > 1) {
-			rel_hw();
+			rel_fdc();
 			return -EBUSY;
 		}
 		fsync_dev(inode->i_rdev);
-		if (motor_on(drive) == 0) {
-			rel_hw();
+		if (fd_motor_on(drive) == 0) {
+			rel_fdc();
 			return -ENODEV;
 		}
 		if (fd_calibrate(drive) == 0) {
-			rel_hw();
+			rel_fdc();
 			return -ENXIO;
 		}
 		floppy_off(drive);
-		rel_hw();
+		rel_fdc();
 		break;
 	case FDFMTTRK:
 		if (param < unit[drive].type->tracks * unit[drive].type->heads)
 		{
 			get_fdc(drive);
-			get_hw(drive);
-			fd_select(drive);
-			if (fd_seek(drive,param)!=0){
-				savedtrack=param;
-				memset(trackdata, FD_FILL_BYTE,
-				       unit[drive].sects*512);
+			if (fd_seek(drive,param) != 0){
+				memset(unit[drive].trackbuf, FD_FILL_BYTE,
+				       unit[drive].dtype->sects * unit[drive].type->sect_mult * 512);
 				non_int_flush_track(drive);
 			}
 			floppy_off(drive);
-			rel_hw();
 			rel_fdc();
 		}
 		else
@@ -1575,33 +1542,27 @@
 		memset((void *)&getprm, 0, sizeof (getprm));
 		getprm.track=unit[drive].type->tracks;
 		getprm.head=unit[drive].type->heads;
-		getprm.sect=unit[drive].sects;
+		getprm.sect=unit[drive].dtype->sects * unit[drive].type->sect_mult;
 		getprm.size=unit[drive].blocks;
 		if (copy_to_user((void *)param,
-				 (void *)&getprm,
-				 sizeof(struct floppy_struct)))
+					  (void *)&getprm,
+					  sizeof(struct floppy_struct)))
 			return -EFAULT;
 	    break;
 	case BLKGETSIZE:
 		return put_user(unit[drive].blocks,(long *)param);
+		break;
 	case FDSETPRM:
 	case FDDEFPRM:
 		return -EINVAL;
-	case FDFLUSH:
-		save_flags(flags);
-		cli();
-		if ((drive == selected) && (writepending)) {
-			del_timer (&flush_track_timer);
-			restore_flags(flags);
-			non_int_flush_track(selected);
-		}
-		else
-			restore_flags(flags);
+	case FDFLUSH: /* unconditionally, even if not needed */
+		del_timer (flush_track_timer + drive);
+		non_int_flush_track(drive);
 		break;
 #ifdef RAW_IOCTL
 	case IOCTL_RAW_TRACK:
 		if (copy_to_user((void *)param, raw_buf,
-				 unit[drive].type->read_size))
+				     unit[drive].type->read_size))
 			return -EFAULT;
 		else
 			return unit[drive].type->read_size;
@@ -1613,76 +1574,16 @@
 	return 0;
 }
 
-/*======================================================================
-  Return unit ID number of given disk
-======================================================================*/
-static unsigned long get_drive_id(int drive)
-{
-	static int called = 0;
-	int i;
-	ulong id = 0;
-
-  	drive&=3;
-  	get_hw(drive);
-	/* set up for ID */
-	MOTOR_ON;
-	udelay(2);
-	SELECT(SELMASK(drive));
-	udelay(2);
-	DESELECT(SELMASK(drive));
-	udelay(2);
-	MOTOR_OFF;
-	udelay(2);
-	SELECT(SELMASK(drive));
-	udelay(2);
-	DESELECT(SELMASK(drive));
-	udelay(2);
-
-	/* loop and read disk ID */
-	for (i=0; i<32; i++) {
-		SELECT(SELMASK(drive));
-		udelay(2);
-
-		/* read and store value of DSKRDY */
-		id <<= 1;
-		id |= (ciaa.pra & DSKRDY) ? 0 : 1;	/* cia regs are low-active! */
-
-		DESELECT(SELMASK(drive));
-	}
-
-	selected = -1;
-	rel_hw();
-
-        /*
-         * RB: At least A500/A2000's df0: don't identify themselves.
-         * As every (real) Amiga has at least a 3.5" DD drive as df0:
-         * we default to that if df0: doesn't identify as a certain
-         * type.
-         */
-        if(drive == 0 && id == FD_NODRIVE)
-         {
-                id = fd_def_df0;
-                printk("%sfd: drive 0 didn't identify, setting default %08lx\n",
-			(called == 0)? KERN_NOTICE:"", (ulong)fd_def_df0);
-		if (called == 0)
-			called++;
-         }
-	/* return the ID value */
-	return (id);
-}
-
 static void fd_probe(int dev)
 {
 	unsigned long code;
 	int type;
 	int drive;
-	int system;
 
 	drive = dev & 3;
-	code = get_drive_id(drive);
+	code = fd_get_drive_id(drive);
 
 	/* get drive type */
-	unit[drive].type = NULL;
 	for (type = 0; type < num_dr_types; type++)
 		if (drive_types[type].code == code)
 			break;
@@ -1694,39 +1595,13 @@
 		return;
 	}
 
-	unit[drive].type = &drive_types[type];
+	unit[drive].type = drive_types + type;
 	unit[drive].track = -1;
 
 	unit[drive].disk = -1;
 	unit[drive].motor = 0;
 	unit[drive].busy = 0;
 	unit[drive].status = -1;
-
-
-	system=(dev & 4)>>2;
-	unit[drive].dtype=&data_types[system];
-	unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
-	unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
-	    unit[drive].sects;
-
-	floppy_sizes[MINOR(dev)] = unit[drive].blocks >> 1;
-
-}
-
-__initfunc(static void probe_drives(void))
-{
-	int drive,found;
-
-	printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
-	found=0;
-	for(drive=0;drive<FD_MAX_UNITS;drive++) {
-	  fd_probe(drive);
-	  if (unit[drive].type->code != FD_NODRIVE) {
-	    printk("fd%d ",drive);
-	    found=1;
-	  }
-	}
-	printk("%s\n",(found==0)?" no drives":"");
 }
 
 /*
@@ -1741,7 +1616,7 @@
   int system;
   unsigned long flags;
 
-  drive = inode->i_rdev & 3;
+  drive = MINOR(inode->i_rdev) & 3;
   old_dev = fd_device[drive];
 
   if (fd_ref[drive])
@@ -1751,17 +1626,20 @@
   if (unit[drive].type->code == FD_NODRIVE)
     return -ENODEV;
 
-  if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
+  if (filp && filp->f_mode & 3) {
+    check_disk_change(inode->i_rdev);
+    if (filp->f_mode & 2 ) {
 	  int wrprot;
 
-	  get_hw(drive);
+	  get_fdc(drive);
 	  fd_select (drive);
 	  wrprot = !(ciaa.pra & DSKPROT);
 	  fd_deselect (drive);
-	  rel_hw();
+	  rel_fdc();
 
 	  if (wrprot)
 		  return -EROFS;
+    }
   }
 
   save_flags(flags);
@@ -1777,55 +1655,87 @@
   if (old_dev && old_dev != inode->i_rdev)
     invalidate_buffers(old_dev);
 
-  if (filp && filp->f_mode)
-    check_disk_change(inode->i_rdev);
-
   system=(inode->i_rdev & 4)>>2;
   unit[drive].dtype=&data_types[system];
-  unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
   unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
-        unit[drive].sects;
+		data_types[system].sects*unit[drive].type->sect_mult;
+  floppy_sizes[MINOR(inode->i_rdev)] = unit[drive].blocks >> 1;
 
-printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
-  data_types[system].name);
+  printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
+	unit[drive].type->name, data_types[system].name);
 
   return 0;
 }
 
 static int floppy_release(struct inode * inode, struct file * filp)
 {
-  unsigned long flags;
+#ifdef DEBUG
   struct super_block * sb;
+#endif
+  int drive = MINOR(inode->i_rdev) & 3;
 
   fsync_dev(inode->i_rdev);
+
+#ifdef DEBUG
+  /* This is now handled in floppy_change, but still useful for debugging */
   sb = get_super(inode->i_rdev);
   if (sb)
 	  invalidate_inodes(sb);
   invalidate_buffers(inode->i_rdev);
-  save_flags (flags);
-  cli();
-  if ((inode->i_rdev & 3) == selected && writepending) {
-    del_timer (&flush_track_timer);
-    restore_flags (flags);
-    non_int_flush_track (selected);
-  } else
-    restore_flags (flags);
+#endif
+
+  if (unit[drive].dirty == 1) {
+	  del_timer (flush_track_timer + drive);
+	  non_int_flush_track (drive);
+  }
   
-  if (!fd_ref[inode->i_rdev & 3]--) {
+  if (!fd_ref[drive]--) {
     printk(KERN_CRIT "floppy_release with fd_ref == 0");
-    fd_ref[inode->i_rdev & 3] = 0;
+    fd_ref[drive] = 0;
   }
 #ifdef MODULE
 /* the mod_use counter is handled this way */
-  floppy_off (inode->i_rdev & 3);
+  floppy_off (drive | 0x40000000);
 #endif
   return 0;
 }
 
-__initfunc(void amiga_floppy_setup (char *str, int *ints))
+/*
+ * 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 amiga_floppy_change(kdev_t dev)
 {
-	printk ("amiflop: Setting default df0 to %x\n", ints[1]);
-	fd_def_df0 = ints[1];
+	int drive = MINOR(dev) & 3;
+	int changed;
+	static int first_time = 1;
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		printk(KERN_CRIT "floppy_change: not a floppy\n");
+		return 0;
+	}
+
+	if (first_time)
+		changed = first_time--;
+	else {
+		get_fdc(drive);
+		fd_select (drive);
+		changed = !(ciaa.pra & DSKCHANGE);
+		fd_deselect (drive);
+		rel_fdc();
+	}
+
+	if (changed) {
+		fd_probe(drive);
+		unit[drive].track = -1;
+		unit[drive].dirty = 0;
+		writepending = 0; /* if this was true before, too bad! */
+		writefromint = 0;
+		return 1;
+	}
+	return 0;
 }
 
 static struct file_operations floppy_fops = {
@@ -1844,24 +1754,40 @@
 	NULL,			/* revalidate */
 };
 
-static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
+__initfunc(void amiga_floppy_setup (char *str, int *ints))
 {
-  if (block_flag)
-    custom.dsklen = 0x4000;
-
-  block_flag = 0;
-  wake_up (&wait_fd_block);
+	printk (KERN_INFO "amiflop: Setting default df0 to %x\n", ints[1]);
+	fd_def_df0 = ints[1];
+}
 
-  if (writefromint) {
-    /* 
-     * if it was a write from an interrupt,
-     * we will call post_write from here
-     */
-    writepending = 2;
-    post_write_timer.expires = 1; /* at least 2 ms */
-    add_timer(&post_write_timer);
-  }
+__initfunc(static int fd_probe_drives(void))
+{
+	int drive,drives,nomem;
 
+	printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
+	drives=0;
+	nomem=0;
+	for(drive=0;drive<FD_MAX_UNITS;drive++) {
+	  fd_probe(drive);
+	  if (unit[drive].type->code != FD_NODRIVE) {
+	    drives++;
+	    if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
+	      printk("no mem for ");
+	      unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
+	      drives--;
+	      nomem = 1;
+	    }
+	    printk("fd%d ",drive);
+	  }
+	}
+	if ((drives > 0) || (nomem == 0)) {
+	  if (drives == 0)
+	    printk("no drives");
+	  printk("\n");
+	  return drives;
+	}
+	printk("\n");
+	return -ENOMEM;
 }
 
 __initfunc(int amiga_floppy_init(void))
@@ -1870,11 +1796,36 @@
 
   if (!AMIGAHW_PRESENT(AMI_FLOPPY))
     return -ENXIO;
-
   if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
-    printk("Unable to get major %d for floppy\n",MAJOR_NR);
+    printk("fd: Unable to get major %d for floppy\n",MAJOR_NR);
     return -EBUSY;
   }
+  if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE)) == NULL) {
+  	printk("fd: cannot get chip mem buffer\n");
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -ENOMEM;
+  }
+
+  if (request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL) != 0) {
+  	printk("fd: cannot get irq for dma\n");
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -EBUSY;
+  }
+  if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL) != 0) {
+  	printk("fd: cannot get irq for timer\n");
+  	free_irq(IRQ_FLOPPY, NULL);
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -EBUSY;
+  }
+  if (fd_probe_drives() < 1) { /* No usable drives */
+  	free_irq(IRQ_AMIGA_CIAA_TB, NULL);
+  	free_irq(IRQ_FLOPPY, NULL);
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -ENXIO;
+  }
 
   /* initialize variables */
   motor_on_timer.next = NULL;
@@ -1886,18 +1837,17 @@
 	  motor_off_timer[i].next = NULL;
 	  motor_off_timer[i].prev = NULL;
 	  motor_off_timer[i].expires = 0;
-	  motor_off_timer[i].data = i;
+	  motor_off_timer[i].data = i|0x80000000;
 	  motor_off_timer[i].function = fd_motor_off;
+	  flush_track_timer[i].next = NULL;
+	  flush_track_timer[i].prev = NULL;
+	  flush_track_timer[i].expires = 0;
+	  flush_track_timer[i].data = i;
+	  flush_track_timer[i].function = flush_track_callback;
 
 	  unit[i].track = -1;
   }
 
-  flush_track_timer.next = NULL;
-  flush_track_timer.prev = NULL;
-  flush_track_timer.expires = 0;
-  flush_track_timer.data = 0;
-  flush_track_timer.function = flush_track_callback;
-
   post_write_timer.next = NULL;
   post_write_timer.prev = NULL;
   post_write_timer.expires = 0;
@@ -1909,9 +1859,6 @@
   blk_size[MAJOR_NR] = floppy_sizes;
 
 
-  timer_table[FLOPPY_TIMER].fn = NULL;
-  timer_active &= ~(1 << FLOPPY_TIMER);
-
   #if 0 /* Doesn't seem to be correct */
   if (fd_def_df0==0) {
     if ((amiga.model == AMI_3000) || (amiga.model == AMI_3000T) ||
@@ -1925,14 +1872,10 @@
   fd_def_df0 = FD_DD_3;
   #endif
 
-  probe_drives();
-
-  raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE);
-
   for (i = 0; i < 128; i++)
-    mfmdecode[i]=255;
+	  mfmdecode[i]=255;
   for (i = 0; i < 16; i++)
-    mfmdecode[mfmencode[i]]=i;
+	  mfmdecode[mfmencode[i]]=i;
 
   /* make sure that disk DMA is enabled */
   custom.dmacon = DMAF_SETCLR | DMAF_DISK;
@@ -1940,9 +1883,7 @@
   /* init ms timer */
   ciaa.crb = 8; /* one-shot, stop */
 
-  request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL);
-  request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL);
-
+  (void)do_floppy; /* avoid warning about unused variable */
   return 0;
 }
 
@@ -1958,11 +1899,15 @@
 
 void cleanup_module(void)
 {
+int i;
+
+for( i = 0; i < FD_MAX_UNITS; i++)
+  if (unit[i].type->code != FD_NODRIVE)
+    kfree(unit[i].trackbuf);
 free_irq(IRQ_AMIGA_CIAA_TB, NULL);
 free_irq(IRQ_FLOPPY, NULL);
 custom.dmacon = DMAF_DISK; /* disable DMA */
 amiga_chip_free(raw_buf);
-timer_active &= ~(1 << FLOPPY_TIMER);
 blk_size[MAJOR_NR] = NULL;
 blksize_size[MAJOR_NR] = NULL;
 blk_dev[MAJOR_NR].request_fn = NULL;
--- ./include/asm-m68k/amifd.h.orig	Wed Nov  5 22:21:20 1997
+++ ./include/asm-m68k/amifd.h	Mon Feb 23 21:46:51 1998
@@ -5,21 +5,23 @@
 
 #include <linux/fd.h>
 
-#define FD_MAX_UNITS    4
+#define FD_MAX_UNITS    4	/* Max. Number of drives */
+#define FLOPPY_MAX_SECTORS	22	/* Max. Number of sectors per track */
+
+#ifndef ASSEMBLER
 
 struct fd_data_type {
     char *name;			/* description of data type */
     int sects;			/* sectors per track */
 #ifdef __STDC__
-    int (*read_fkt)(int, unsigned char *, unsigned long, int);
-    void (*write_fkt)(int, unsigned long, unsigned char *, int);
+    int (*read_fkt)(int);
+    void (*write_fkt)(int);
 #else
     int (*read_fkt)();		/* read whole track */
     void (*write_fkt)();		/* write whole track */
 #endif
 };
 
-#ifndef ASSEMBLER
 /*
 ** Floppy type descriptions
 */
@@ -43,13 +45,15 @@
     struct fd_drive_type *type;	/* type of floppy for this unit */
     struct fd_data_type *dtype;	/* type of floppy for this unit */
     int track;			/* current track (-1 == unknown) */
+    unsigned char *trackbuf;    /* current track (kmaloc()'d */
 
     int blocks;			/* total # blocks on disk */
-    int sects;			/* number of sectors per track */
 
+    int changed;		/* true when not known */
     int disk;			/* disk in drive (-1 == unknown) */
     int motor;			/* true when motor is at speed */
     int busy;			/* true when drive is active */
+    int dirty;			/* true when trackbuf is not on disk */
     int status;			/* current error code for unit */
 };
 #endif
