patch-2.1.17 linux/drivers/block/amiflop.c
Next file: linux/drivers/block/ataflop.c
Previous file: linux/drivers/block/acsi_slm.c
Back to the patch index
Back to the overall index
- Lines: 1047
- Date:
Fri Dec 20 11:20:00 1996
- Orig file:
v2.1.16/linux/drivers/block/amiflop.c
- Orig date:
Wed Oct 16 10:48:07 1996
diff -u --recursive --new-file v2.1.16/linux/drivers/block/amiflop.c linux/drivers/block/amiflop.c
@@ -24,7 +24,7 @@
* - works but I think it's inefficient. (look in redo_fd_request)
* But the changes were very efficient. (only three and a half lines)
*
- * january 1995 added special ioctl for tracking down read/write problems
+ * january 1996 added special ioctl for tracking down read/write problems
* - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data
* is copied to area. (area should be large enough since no checking is
* done - 30K is currently sufficient). return the actual size of the
@@ -32,6 +32,10 @@
* - replaced udelays() by a timer (CIAA timer B) for the waits
* needed for the disk mechanic.
*
+ * february 1996 fixed error recovery and multiple disk access
+ * - both got broken the first time I tampered with the driver :-(
+ * - still not safe, but better than before
+ *
* revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel.
* - Minor changes to accept the kdev_t.
* - Replaced some more udelays with ms_delays. Udelay is just a loop,
@@ -43,6 +47,9 @@
* that when we start using 16 (24?) bit minors.
*/
+#ifdef MODULE
+#include <linux/module.h>
+#endif
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
@@ -55,13 +62,13 @@
#include <linux/string.h>
#include <linux/mm.h>
+#include <asm/setup.h>
+#include <asm/uaccess.h>
#include <asm/amifdreg.h>
#include <asm/amifd.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include <asm/irq.h>
-#include <asm/bootinfo.h>
-#include <asm/amigatypes.h>
#define MAJOR_NR FLOPPY_MAJOR
#include <linux/blk.h>
@@ -126,15 +133,15 @@
static struct fd_drive_type drive_types[] = {
/* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/
/* warning: times are now in milliseconds (ms) */
- { FD_DD_3, "DD 3.5", 160, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
- { FD_HD_3, "HD 3.5", 160, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
- { FD_DD_5, "DD 5.25", 80, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
+ { FD_DD_3, "DD 3.5", 80, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
+ { FD_HD_3, "HD 3.5", 80, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
+ { FD_DD_5, "DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
{ FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
static int num_dr_types = sizeof(drive_types) / sizeof(drive_types[0]);
/* defaults for 3 1/2" HD-Disks */
-static int floppy_sizes[256]={880,880,880,880,720,720,720,};
+static int floppy_sizes[256]={880,880,880,880,720,720,720,720,};
static int floppy_blocksizes[256]={0,};
/* hardsector size assumed to be 512 */
@@ -142,7 +149,6 @@
{ "Amiga", 11 , amiga_read, amiga_write},
{ "MS-Dos", 9, dos_read, dos_write}
};
-static int num_da_types = sizeof(data_types) / sizeof(data_types[0]);
/* current info on each unit */
static struct amiga_floppy_struct unit[FD_MAX_UNITS];
@@ -168,13 +174,20 @@
* information to interrupts. They are the data used for the current
* request.
*/
-static char block_flag = 0;
-static int selected = 0;
+static volatile char block_flag = 0;
+static volatile int selected = 0;
static struct wait_queue *wait_fd_block = NULL;
-/* Synchronization of FDC access. */
-static volatile int fdc_busy = 0;
+/* 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) */
@@ -185,28 +198,117 @@
static unsigned char mfmdecode[128];
/* floppy internal millisecond timer stuff */
-static struct semaphore ms_sem = MUTEX;
+static volatile int ms_busy = -1;
static struct wait_queue *ms_wait = NULL;
#define MS_TICKS ((amiga_eclock+50)/1000)
-static void ms_isr(int irq, struct pt_regs *fp, void *dummy)
+/*
+ * 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
+
+/*
+ * 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 fd_ref[4] = { 0,0,0,0 };
+static fd_device[4] = { 0,0,0,0 };
+
+/*
+ * Current device number. Taken either from the block header or from the
+ * format request descriptor.
+ */
+#define CURRENT_DEVICE (CURRENT->rq_dev)
+
+/* 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;
+
+ 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);
+ }
+}
+
+static void ms_isr(int irq, void *dummy, struct pt_regs *fp)
+{
+ms_busy = -1;
wake_up(&ms_wait);
}
-/* with the semaphore waits are queued up
+/* all waits are queued up
A more generic routine would do a schedule a la timer.device */
static void ms_delay(int ms)
{
+ unsigned long flags;
int ticks;
if (ms > 0) {
- down(&ms_sem);
- ticks=MS_TICKS*ms-1;
+ save_flags(flags);
+ cli();
+ while (ms_busy == 0)
+ sleep_on(&ms_wait);
+ ms_busy = 0;
+ restore_flags(flags);
+ ticks = MS_TICKS*ms-1;
ciaa.tblo=ticks%256;
ciaa.tbhi=ticks/256;
- ciaa.crb=0x19; /* count clock, force load, one-shot, start */
+ ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */
sleep_on(&ms_wait);
- up(&ms_sem);
}
}
@@ -223,6 +325,7 @@
unsigned char prb = ~0;
drive&=3;
+ get_hw(drive);
save_flags(flags);
cli();
@@ -238,6 +341,15 @@
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);
}
@@ -260,6 +372,7 @@
unsigned char prb = ~0;
nr &= 3;
+ get_hw(nr);
save_flags (flags);
cli();
del_timer(motor_off_timer + nr);
@@ -284,12 +397,17 @@
while (!unit[nr].motor)
sleep_on (&motor_wait);
}
+ rel_hw();
restore_flags(flags);
if (on_attempts == 0) {
- printk ("motor_on failed, turning motor off\n");
+#if 0
+ printk (KERN_ERR "motor_on failed, turning motor off\n");
fd_motor_off (nr);
return 0;
+#else
+ printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n");
+#endif
}
return 1;
@@ -310,6 +428,7 @@
drive&=3;
if (drive == selected)
return;
+ get_hw(drive);
selected = drive;
if (unit[drive].track % 2 != 0)
@@ -320,6 +439,7 @@
ciab.prb = prb;
prb &= ~SELMASK(drive);
ciab.prb = prb;
+ rel_hw();
}
static void fd_deselect (int drive)
@@ -331,6 +451,7 @@
if (drive != selected)
return;
+ get_hw(drive);
save_flags (flags);
sti();
@@ -341,6 +462,7 @@
ciab.prb = prb;
restore_flags (flags);
+ rel_hw();
}
@@ -355,6 +477,7 @@
int n;
drive &= 3;
+ get_hw(drive);
if (!motor_on (drive))
return 0;
fd_select (drive);
@@ -362,38 +485,40 @@
prb |= DSKSIDE;
prb &= ~DSKDIREC;
ciab.prb = prb;
- for (n = unit[drive].type->tracks/4; n != 0; --n) {
+ for (n = unit[drive].type->tracks/2; n != 0; --n) {
if (ciaa.pra & DSKTRACK0)
break;
prb &= ~DSKSTEP;
ciab.prb = prb;
prb |= DSKSTEP;
- ms_delay (2);
+ udelay (2);
ciab.prb = prb;
ms_delay(unit[drive].type->step_delay);
}
ms_delay (unit[drive].type->settle_time);
prb |= DSKDIREC;
- n = unit[drive].type->tracks/2 + 20;
+ n = unit[drive].type->tracks + 20;
for (;;) {
prb &= ~DSKSTEP;
ciab.prb = prb;
prb |= DSKSTEP;
- ms_delay (2);
+ udelay (2);
ciab.prb = prb;
ms_delay(unit[drive].type->step_delay + 1);
if ((ciaa.pra & DSKTRACK0) == 0)
break;
if (--n == 0) {
- printk ("calibrate failed, turning motor off\n");
+ printk (KERN_ERR "calibrate failed, turning motor off\n");
fd_motor_off (drive);
unit[drive].track = -1;
+ rel_hw();
return 0;
}
}
unit[drive].track = 0;
ms_delay(unit[drive].type->settle_time);
+ rel_hw();
return 1;
}
@@ -408,13 +533,20 @@
int cnt;
drive &= 3;
- if (unit[drive].track == track)
+ get_hw(drive);
+ if (unit[drive].track == track) {
+ rel_hw();
return 1;
- if (!motor_on(drive))
+ }
+ if (!motor_on(drive)) {
+ rel_hw();
return 0;
+ }
fd_select (drive);
- if (unit[drive].track < 0 && !fd_calibrate(drive))
+ if (unit[drive].track < 0 && !fd_calibrate(drive)) {
+ rel_hw();
return 0;
+ }
cnt = unit[drive].track/2 - track/2;
prb = ciab.prb;
@@ -429,18 +561,21 @@
if (track % 2 != unit[drive].track % 2)
ms_delay (unit[drive].type->side_time);
unit[drive].track = track;
- if (cnt == 0)
+ if (cnt == 0) {
+ rel_hw();
return 1;
+ }
do {
prb &= ~DSKSTEP;
ciab.prb = prb;
prb |= DSKSTEP;
- ms_delay (1);
+ udelay (1);
ciab.prb = prb;
ms_delay (unit[drive].type->step_delay);
} while (--cnt != 0);
ms_delay (unit[drive].type->settle_time);
+ rel_hw();
return 1;
}
@@ -504,9 +639,6 @@
struct header hdr;
int i;
- if (!AMIGAHW_PRESENT(AMI_FLOPPY))
- return 0;
-
disk&=3;
*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
raw++;
@@ -618,7 +750,7 @@
for (scnt = 0;scnt < unit[drive].sects; scnt++) {
if (!(raw = scan_sync(raw, end))) {
- printk ("can't find sync for sector %d\n", scnt);
+ printk (KERN_INFO "can't find sync for sector %d\n", scnt);
return MFM_NOSYNC;
}
@@ -638,13 +770,13 @@
#endif
if (hdr.hdrchk != csum) {
- printk("MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
+ printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
return MFM_HEADER;
}
/* verify track */
if (hdr.track != track) {
- printk("MFM_TRACK: %d, %d\n", hdr.track, track);
+ printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, track);
return MFM_TRACK;
}
@@ -653,10 +785,10 @@
csum = checksum((ulong *)(track_data + hdr.sect*512), 512);
if (hdr.datachk != csum) {
- printk("MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
+ 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 ("data=(%lx,%lx,%lx,%lx)\n",
+ 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],
@@ -852,7 +984,7 @@
for (scnt=0;scnt<unit[drive].sects;scnt++) {
do { /* search for the right sync of each sec-hdr */
if (!(raw = scan_sync (raw, end))) {
- printk("dos_read: no hdr sync on track %d, unit %d for sector %d\n",
+ printk(KERN_INFO "dos_read: no hdr sync on track %d, unit %d for sector %d\n",
track,drive,scnt);
return MFM_NOSYNC;
}
@@ -870,30 +1002,30 @@
#endif
if (crc != hdr.crc) {
- printk("dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
+ printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
return MFM_HEADER;
}
if (hdr.track != track/unit[drive].type->heads) {
- printk("dos_read: MFM_TRACK %d, %d\n", hdr.track,
+ printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", hdr.track,
track/unit[drive].type->heads);
return MFM_TRACK;
}
if (hdr.side != track%unit[drive].type->heads) {
- printk("dos_read: MFM_SIDE %d, %d\n", hdr.side,
+ printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", hdr.side,
track%unit[drive].type->heads);
return MFM_TRACK;
}
if (hdr.len_desc != 2) {
- printk("dos_read: unknown sector len descriptor %d\n", hdr.len_desc);
+ printk(KERN_INFO "dos_read: unknown sector len descriptor %d\n", hdr.len_desc);
return MFM_DATA;
}
#ifdef DEBUG
printk("hdr accepted\n");
#endif
if (!(raw = scan_sync (raw, end))) {
- printk("dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
+ 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);
return MFM_NOSYNC;
}
@@ -902,7 +1034,7 @@
#endif
if (*((ushort *)raw)!=0x5545) {
- printk("dos_read: no data mark after sync (%d,%d,%d,%d) sc=%d\n",
+ printk(KERN_INFO "dos_read: no data mark after sync (%d,%d,%d,%d) sc=%d\n",
hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt);
return MFM_NOSYNC;
}
@@ -913,10 +1045,10 @@
crc = dos_data_crc(track_data + (hdr.sec - 1) * 512);
if (crc != data_crc[0]) {
- printk("dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
+ 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("data=(%lx,%lx,%lx,%lx,...)\n",
+ 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],
@@ -1039,33 +1171,6 @@
*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
}
-/*
- * 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
-
-/*
- * 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 fd_ref[4] = { 0,0,0,0 };
-static fd_device[4] = { 0,0,0,0 };
-
-/*
- * Current device number. Taken either from the block header or from the
- * format request descriptor.
- */
-#define CURRENT_DEVICE (CURRENT->rq_dev)
-
-/* Current error count. */
-#define CURRENT_ERRORS (CURRENT->errors)
-
static void request_done(int uptodate)
{
timer_active &= ~(1 << FLOPPY_TIMER);
@@ -1082,15 +1187,22 @@
{
int drive = dev & 3;
int changed;
+ static int first_time = 1;
if (MAJOR(dev) != MAJOR_NR) {
- printk("floppy_change: not a floppy\n");
+ printk(KERN_CRIT "floppy_change: not a floppy\n");
return 0;
}
- fd_select (drive);
- changed = !(ciaa.pra & DSKCHANGE);
- fd_deselect (drive);
+ 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);
@@ -1119,6 +1231,7 @@
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;
@@ -1139,6 +1252,7 @@
sleep_on (&wait_fd_block);
custom.dsklen = 0;
+ rel_hw();
}
static int raw_write(int drive, int track, char *ptrack, int len)
@@ -1149,6 +1263,7 @@
if ((ciaa.pra & DSKPROT) == 0)
return 0;
+ 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 */
@@ -1176,30 +1291,45 @@
custom.dsklen = 0;
writepending = 0;
writefromint = 0;
+ rel_hw(); /* corresponds to get_hw() in raw_write */
}
static int get_track(int drive, int track)
{
- int error;
+ int error, errors;
drive&=3;
- if ((lastdrive == drive) && (savedtrack == track))
- 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);
- switch (error) {
- case 0:
- savedtrack = track;
- return 0;
- case MFM_TRACK:
- unit[drive].track = -1;
- /* fall through */
- default:
- return -1;
+ 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;
}
static void flush_track_callback(unsigned long nr)
@@ -1208,7 +1338,7 @@
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 ("floppy disk write protected\n");
+ printk (KERN_NOTICE "floppy disk write protected\n");
writefromint = 0;
writepending = 0;
}
@@ -1227,7 +1357,7 @@
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)) {
- printk ("floppy disk write protected in write!\n");
+ printk (KERN_NOTICE "floppy disk write protected in write!\n");
writepending = 0;
return 0;
}
@@ -1255,10 +1385,9 @@
repeat:
if (!CURRENT) {
- if (!fdc_busy)
- printk("FDC access conflict!");
- fdc_busy = 0;
- wake_up(&fdc_wait);
+ if (fdc_busy < 0)
+ printk(KERN_CRIT "FDC access conflict!");
+ rel_fdc();
CLEAR_INTR;
return;
}
@@ -1324,15 +1453,6 @@
switch (CURRENT->cmd) {
case READ:
- if (!motor_on (drive)) {
- end_request(0);
- goto repeat;
- }
- fd_select (drive);
- if (!fd_seek(drive, track)) {
- end_request(0);
- goto repeat;
- }
if (get_track(drive, track) == -1) {
end_request(0);
goto repeat;
@@ -1341,15 +1461,6 @@
break;
case WRITE:
- if (!motor_on (drive)) {
- end_request(0);
- goto repeat;
- }
- fd_select (drive);
- if (!fd_seek(drive, track)) {
- end_request(0);
- goto repeat;
- }
if (get_track(drive, track) == -1) {
end_request(0);
goto repeat;
@@ -1374,7 +1485,7 @@
break;
default:
- printk("do_fd_request: unknown command\n");
+ printk(KERN_WARNING "do_fd_request: unknown command\n");
request_done(0);
goto repeat;
}
@@ -1388,13 +1499,7 @@
static void do_fd_request(void)
{
-unsigned long flags;
-
- save_flags(flags);
- cli();
- while (fdc_busy) sleep_on(&fdc_wait);
- fdc_busy = 1;
- restore_flags(flags); /* sti(); */
+ get_fdc(CURRENT_DEVICE & 3);
redo_fd_request();
}
@@ -1404,22 +1509,33 @@
int drive = inode->i_rdev & 3;
static struct floppy_struct getprm;
int error;
+ unsigned long flags;
switch(cmd)
{
case FDFMTBEG:
- if (fd_ref[drive] > 1)
+ get_hw(drive);
+ if (fd_ref[drive] > 1) {
+ rel_hw();
return -EBUSY;
+ }
fsync_dev(inode->i_rdev);
- if (motor_on(drive) == 0)
+ if (motor_on(drive) == 0) {
+ rel_hw();
return -ENODEV;
- if (fd_calibrate(drive) == 0)
+ }
+ if (fd_calibrate(drive) == 0) {
+ rel_hw();
return -ENXIO;
+ }
floppy_off(drive);
+ rel_hw();
break;
case FDFMTTRK:
- if (param < unit[drive].type->tracks)
+ 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)
{
@@ -1428,6 +1544,8 @@
non_int_flush_track(drive);
}
floppy_off(drive);
+ rel_hw();
+ rel_fdc();
}
else
return -EINVAL;
@@ -1443,27 +1561,29 @@
if (error)
return error;
memset((void *)&getprm, 0, sizeof (getprm));
- getprm.track=unit[drive].type->tracks/unit[drive].type->heads;
+ getprm.track=unit[drive].type->tracks;
getprm.head=unit[drive].type->heads;
getprm.sect=unit[drive].sects;
getprm.size=unit[drive].blocks;
copy_to_user((void *)param,(void *)&getprm,sizeof(struct floppy_struct));
break;
case BLKGETSIZE:
- error = verify_area(VERIFY_WRITE, (void *)param,
- sizeof(long));
- if (error)
- return error;
- put_fs_long(unit[drive].blocks,(long *)param);
+ if (put_user(unit[drive].blocks,(long *)param))
+ return -EFAULT;
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);
break;
#ifdef RAW_IOCTL
case IOCTL_RAW_TRACK:
@@ -1475,7 +1595,7 @@
return unit[drive].type->read_size;
#endif
default:
- printk("fd_ioctl: unknown cmd %d for drive %d.",cmd,drive);
+ printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.",cmd,drive);
return -ENOSYS;
}
return 0;
@@ -1486,10 +1606,12 @@
======================================================================*/
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);
@@ -1517,6 +1639,7 @@
}
selected = -1;
+ rel_hw();
/*
* RB: At least A500/A2000's df0: don't identify themselves.
@@ -1527,7 +1650,10 @@
if(drive == 0 && id == FD_NODRIVE)
{
id = fd_def_df0;
- printk("fd: drive 0 didn't identify, setting default %08lx\n",(ulong)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);
@@ -1550,7 +1676,7 @@
break;
if (type >= num_dr_types) {
- printk("fd_probe: unsupported drive type %08lx found\n",
+ printk(KERN_WARNING "fd_probe: unsupported drive type %08lx found\n",
code);
return;
}
@@ -1578,7 +1704,7 @@
{
int drive,found;
- printk("FD: probing units\nfound ");
+ printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
found=0;
for(drive=0;drive<FD_MAX_UNITS;drive++) {
fd_probe(drive);
@@ -1600,6 +1726,7 @@
int drive;
int old_dev;
int system;
+ unsigned long flags;
drive = inode->i_rdev & 3;
old_dev = fd_device[drive];
@@ -1611,33 +1738,42 @@
if (unit[drive].type->code == FD_NODRIVE)
return -ENODEV;
- fd_ref[drive]++;
- fd_device[drive] = inode->i_rdev;
-
- if (old_dev && old_dev != inode->i_rdev)
- invalidate_buffers(old_dev);
-
- if (filp && filp->f_mode)
- check_disk_change(inode->i_rdev);
-
if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
int wrprot;
+ get_hw(drive);
fd_select (drive);
wrprot = !(ciaa.pra & DSKPROT);
fd_deselect (drive);
+ rel_hw();
if (wrprot)
return -EROFS;
}
+ save_flags(flags);
+ cli();
+ fd_ref[drive]++;
+ fd_device[drive] = inode->i_rdev;
+#ifdef MODULE
+ if (unit[drive].motor == 0)
+ MOD_INC_USE_COUNT;
+#endif
+ restore_flags(flags);
+
+ 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;
-printk("fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
+printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
data_types[system].name);
return 0;
@@ -1660,9 +1796,13 @@
restore_flags (flags);
if (!fd_ref[inode->i_rdev & 3]--) {
- printk("floppy_release with fd_ref == 0");
+ printk(KERN_CRIT "floppy_release with fd_ref == 0");
fd_ref[inode->i_rdev & 3] = 0;
}
+#ifdef MODULE
+/* the mod_use counter is handled this way */
+ floppy_off (inode->i_rdev & 3);
+#endif
}
void amiga_floppy_setup (char *str, int *ints)
@@ -1687,7 +1827,7 @@
NULL, /* revalidate */
};
-static void fd_block_done(int irq, struct pt_regs *fp, void *dummy)
+static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
{
if (block_flag)
custom.dsklen = 0x4000;
@@ -1755,13 +1895,18 @@
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 ((boot_info.bi_amiga.model == AMI_3000) ||
- (boot_info.bi_amiga.model == AMI_4000))
+ if ((amiga.model == AMI_3000) || (amiga.model == AMI_3000T) ||
+ (amiga.model == AMI_3000PLUS) || (amiga.model == AMI_4000))
fd_def_df0=FD_HD_3;
else
fd_def_df0=FD_DD_3;
}
+ #else
+ /* Now we hope that every HD drive will identify itself correctly */
+ fd_def_df0 = FD_DD_3;
+ #endif
probe_drives();
@@ -1775,8 +1920,35 @@
/* make sure that disk DMA is enabled */
custom.dmacon = DMAF_SETCLR | DMAF_DISK;
- add_isr(IRQ_FLOPPY, fd_block_done, 0, NULL, "floppy_dma");
- add_isr(IRQ_AMIGA_CIAA_TB, ms_isr, 0, NULL, "floppy_timer");
+ /* 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);
return 0;
}
+
+#ifdef MODULE
+#include <linux/version.h>
+
+int init_module(void)
+{
+ if (!MACH_IS_AMIGA)
+ return -ENXIO;
+ return amiga_floppy_init();
+}
+
+void cleanup_module(void)
+{
+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;
+unregister_blkdev(MAJOR_NR, "fd");
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov