📄 amiflop.c
字号:
/* * linux/amiga/amiflop.c * * Copyright (C) 1993 Greg Harp * Portions of this driver are based on code contributed by Brad Pepers * * revised 28.5.95 by Joerg Dorchain * - now no bugs(?) any more for both HD & DD * - added support for 40 Track 5.25" drives, 80-track hopefully behaves * like 3.5" dd (no way to test - are there any 5.25" drives out there * that work on an A4000?) * - wrote formatting routine (maybe dirty, but works) * * june/july 1995 added ms-dos support by Joerg Dorchain * (portions based on messydos.device and various contributors) * - currently only 9 and 18 sector disks * * - fixed a bug with the internal trackbuffer when using multiple * disks the same time * - made formatting a bit safer * - added command line and machine based default for "silent" df0 * * december 1995 adapted for 1.2.13pl4 by Joerg Dorchain * - 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 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 * trackbuffer * - 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, * and so the delay will be different depending on the given * processor :-( * - The driver could use a major cleanup because of the new * 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 ;-) */#include <linux/module.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/fd.h>#include <linux/hdreg.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/amifdreg.h>#include <linux/amifd.h>#include <linux/ioport.h>#include <asm/setup.h>#include <asm/uaccess.h>#include <asm/amigahw.h>#include <asm/amigaints.h>#include <asm/irq.h>#define MAJOR_NR FLOPPY_MAJOR#include <linux/blk.h>#undef DEBUG /* print _LOTS_ of infos */#define RAW_IOCTL#ifdef RAW_IOCTL#define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */#endif/* * Defines *//* * Error codes */#define FD_OK 0 /* operation succeeded */#define FD_ERROR -1 /* general error (seek, read, write, etc) */#define FD_NOUNIT 1 /* unit does not exist */#define FD_UNITBUSY 2 /* unit already active */#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 */#define FD_NODRIVE 0x00000000 /* response when no unit is present */#define FD_DD_3 0xffffffff /* double-density 3.5" (880K) drive */#define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */#define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */static long int fd_def_df0 = FD_DD_3; /* default for df0 if it doesn't identify */MODULE_PARM(fd_def_df0,"l");MODULE_LICENSE("GPL");/* * Macros */#define MOTOR_ON (ciab.prb &= ~DSKMOTOR)#define MOTOR_OFF (ciab.prb |= DSKMOTOR)#define SELECT(mask) (ciab.prb &= ~mask)#define DESELECT(mask) (ciab.prb |= mask)#define SELMASK(drive) (1 << (3 + (drive & 3)))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", 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,720,};static int floppy_blocksizes[256];/* 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 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;/* Synchronization of FDC access *//* request loop (trackbuffer) */static volatile int fdc_busy = -1;static volatile int fdc_nested;static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); static DECLARE_WAIT_QUEUE_HEAD(motor_wait);static volatile int selected = -1; /* currently selected drive */static int writepending;static int writefromint;static char *raw_buf;#define RAW_BUF_SIZE 30000 /* size of raw disk data *//* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */static volatile char block_flag;static DECLARE_WAIT_QUEUE_HEAD(wait_fd_block);/* MS-Dos MFM Coding tables (should go quick and easy) */static unsigned char mfmencode[16]={ 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55};static unsigned char mfmdecode[128];/* floppy internal millisecond timer stuff */static volatile int ms_busy = -1;static DECLARE_WAIT_QUEUE_HEAD(ms_wait);#define MS_TICKS ((amiga_eclock+50)/1000)/* * 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/* Prevent "aliased" accesses. */static int fd_ref[4] = { 0,0,0,0 };static int 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)/* * 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. *//* Milliseconds timer */static void ms_isr(int irq, void *dummy, struct pt_regs *fp){ ms_busy = -1; wake_up(&ms_wait);}/* 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) { 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 eclock, force load, one-shoot, start */ sleep_on(&ms_wait); }}/* 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 char prb = ~0; drive&=3;#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; 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; restore_flags (flags); rel_fdc();}static void motor_on_callback(unsigned long nr){ if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) { wake_up (&motor_wait); } else { motor_on_timer.expires = jiffies + HZ/10; add_timer(&motor_on_timer); }}static int fd_motor_on(int nr){ nr &= 3; 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; sleep_on (&motor_wait); fd_deselect(nr); } if (on_attempts == 0) { on_attempts = -1;#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;}static void fd_motor_off(unsigned long drive){ long calledfromint;#ifdef MODULE long decusecount; decusecount = drive & 0x40000000;#endif calledfromint = drive & 0x80000000; drive&=3; 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; } unit[drive].motor = 0; fd_select(drive); udelay (1); fd_deselect(drive);#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 floppy_off (unsigned int nr){ 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);}static int fd_calibrate(int drive){ unsigned char prb; int n; drive &= 3; get_fdc(drive); if (!fd_motor_on (drive)) return 0; fd_select (drive); prb = ciab.prb; prb |= DSKSIDE; prb &= ~DSKDIREC; ciab.prb = prb; for (n = unit[drive].type->tracks/2; n != 0; --n) { if (ciaa.pra & DSKTRACK0) break; prb &= ~DSKSTEP; ciab.prb = prb; prb |= DSKSTEP; 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 + 20; for (;;) { prb &= ~DSKSTEP; ciab.prb = prb; prb |= DSKSTEP; udelay (2); ciab.prb = prb; ms_delay(unit[drive].type->step_delay + 1); if ((ciaa.pra & DSKTRACK0) == 0) break; if (--n == 0) { printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive); fd_motor_off (drive); unit[drive].track = -1; rel_fdc();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -