📄 floppy.c
字号:
/* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */#define NO_TRACK -1#define NEED_1_RECAL -2#define NEED_2_RECAL -3/* */static int usage_count = 0;/* buffer related variables */static int buffer_track = -1;static int buffer_drive = -1;static int buffer_min = -1;static int buffer_max = -1;/* fdc related variables, should end up in a struct */static struct floppy_fdc_state fdc_state[N_FDC];static int fdc; /* current fdc */static struct floppy_struct *_floppy = floppy_type;static unsigned char current_drive = 0;static long current_count_sectors = 0;static unsigned char sector_t; /* sector in track */#ifndef fd_eject#define fd_eject(x) -EINVAL#endif#ifdef DEBUGTstatic long unsigned debugtimer;#endif/* * Debugging * ========= */static inline void set_debugt(void){#ifdef DEBUGT debugtimer = jiffies;#endif}static inline void debugt(const char *message){#ifdef DEBUGT if (DP->flags & DEBUGT) printk("%s dtime=%lu\n", message, jiffies-debugtimer);#endif}typedef void (*timeout_fn)(unsigned long);static struct timer_list fd_timeout ={ NULL, NULL, 0, 0, (timeout_fn) floppy_shutdown };static const char *timeout_message;#ifdef FLOPPY_SANITY_CHECKstatic void is_alive(const char *message){ /* this routine checks whether the floppy driver is "alive" */ if (fdc_busy && command_status < 2 && !fd_timeout.prev){ DPRINT("timeout handler died: %s\n",message); }}#endif#ifdef FLOPPY_SANITY_CHECK#define OLOGSIZE 20static void (*lasthandler)(void) = NULL;static int interruptjiffies=0;static int resultjiffies=0;static int resultsize=0;static int lastredo=0;static struct output_log { unsigned char data; unsigned char status; unsigned long jiffies;} output_log[OLOGSIZE];static int output_log_pos=0;#endif#define CURRENTD -1#define MAXTIMEOUT -2static void reschedule_timeout(int drive, const char *message, int marg){ if (drive == CURRENTD) drive = current_drive; del_timer(&fd_timeout); if (drive < 0 || drive > N_DRIVE) { fd_timeout.expires = jiffies + 20*HZ; drive=0; } else fd_timeout.expires = jiffies + UDP->timeout; add_timer(&fd_timeout); if (UDP->flags & FD_DEBUG){ DPRINT("reschedule timeout "); printk(message, marg); printk("\n"); } timeout_message = message;}static int maximum(int a, int b){ if(a > b) return a; else return b;}#define INFBOUND(a,b) (a)=maximum((a),(b));static int minimum(int a, int b){ if(a < b) return a; else return b;}#define SUPBOUND(a,b) (a)=minimum((a),(b));/* * Bottom half floppy driver. * ========================== * * This part of the file contains the code talking directly to the hardware, * and also the main service loop (seek-configure-spinup-command) *//* * disk change. * This routine is responsible for maintaining the FD_DISK_CHANGE flag, * and the last_checked date. * * last_checked is the date of the last check which showed 'no disk change' * FD_DISK_CHANGE is set under two conditions: * 1. The floppy has been changed after some i/o to that floppy already * took place. * 2. No floppy disk is in the drive. This is done in order to ensure that * requests are quickly flushed in case there is no disk in the drive. It * follows that FD_DISK_CHANGE can only be cleared if there is a disk in * the drive. * * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet. * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on * each seek. If a disk is present, the disk change line should also be * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk * change line is set, this means either that no disk is in the drive, or * that it has been removed since the last seek. * * This means that we really have a third possibility too: * The floppy has been changed after the last seek. */static int disk_change(int drive){ int fdc=FDC(drive);#ifdef FLOPPY_SANITY_CHECK if (jiffies - UDRS->select_date < UDP->select_delay) DPRINT("WARNING disk change called early\n"); if (!(FDCS->dor & (0x10 << UNIT(drive))) || (FDCS->dor & 3) != UNIT(drive) || fdc != FDC(drive)){ DPRINT("probing disk change on unselected drive\n"); DPRINT("drive=%d fdc=%d dor=%x\n",drive, FDC(drive), FDCS->dor); }#endif#ifdef DCL_DEBUG if (UDP->flags & FD_DEBUG){ DPRINT("checking disk change line for drive %d\n",drive); DPRINT("jiffies=%ld\n", jiffies); DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80); DPRINT("flags=%x\n",UDRS->flags); }#endif if (UDP->flags & FD_BROKEN_DCL) return UTESTF(FD_DISK_CHANGED); if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){ USETF(FD_VERIFY); /* verify write protection */ if (UDRS->maxblock){ /* mark it changed */ USETF(FD_DISK_CHANGED); } /* invalidate its geometry */ if (UDRS->keep_data >= 0) { if ((UDP->flags & FTD_MSG) && current_type[drive] != NULL) DPRINT("Disk type is undefined after " "disk change\n"); current_type[drive] = NULL; floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE; } /*USETF(FD_DISK_NEWCHANGE);*/ return 1; } else { UDRS->last_checked=jiffies; UCLEARF(FD_DISK_NEWCHANGE); } return 0;}static inline int is_selected(int dor, int unit){ return ((dor & (0x10 << unit)) && (dor &3) == unit);}static int set_dor(int fdc, char mask, char data){ register unsigned char drive, unit, newdor,olddor; if (FDCS->address == -1) return -1; olddor = FDCS->dor; newdor = (olddor & mask) | data; if (newdor != olddor){ unit = olddor & 0x3; if (is_selected(olddor, unit) && !is_selected(newdor,unit)){ drive = REVDRIVE(fdc,unit);#ifdef DCL_DEBUG if (UDP->flags & FD_DEBUG){ DPRINT("calling disk change from set_dor\n"); }#endif disk_change(drive); } FDCS->dor = newdor; fd_outb(newdor, FD_DOR); unit = newdor & 0x3; if (!is_selected(olddor, unit) && is_selected(newdor,unit)){ drive = REVDRIVE(fdc,unit); UDRS->select_date = jiffies; } } /* FIXME: we should be more graceful here */ if (newdor & FLOPPY_MOTOR_MASK) floppy_grab_irq_and_dma(); if (olddor & FLOPPY_MOTOR_MASK) floppy_release_irq_and_dma(); return olddor;}static void twaddle(void){ if (DP->select_delay) return; fd_outb(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR); fd_outb(FDCS->dor, FD_DOR); DRS->select_date = jiffies;}/* reset all driver information about the current fdc. This is needed after * a reset, and after a raw command. */static void reset_fdc_info(int mode){ int drive; FDCS->spec1 = FDCS->spec2 = -1; FDCS->need_configure = 1; FDCS->perp_mode = 1; FDCS->rawcmd = 0; for (drive = 0; drive < N_DRIVE; drive++) if (FDC(drive) == fdc && (mode || UDRS->track != NEED_1_RECAL)) UDRS->track = NEED_2_RECAL;}/* selects the fdc and drive, and enables the fdc's input/dma. */static void set_fdc(int drive){ if (drive >= 0 && drive < N_DRIVE){ fdc = FDC(drive); current_drive = drive; } if (fdc != 1 && fdc != 0) { printk("bad fdc value\n"); return; } set_dor(fdc,~0,8);#if N_FDC > 1 set_dor(1-fdc, ~8, 0);#endif if (FDCS->rawcmd == 2) reset_fdc_info(1); if (fd_inb(FD_STATUS) != STATUS_READY) FDCS->reset = 1;}/* locks the driver */static int lock_fdc(int drive, int interruptible){ unsigned long flags; if (!usage_count){ printk(KERN_ERR "trying to lock fdc while usage count=0\n"); return -1; } if(floppy_grab_irq_and_dma()==-1) return -EBUSY; INT_OFF; while (fdc_busy && NO_SIGNAL) interruptible_sleep_on(&fdc_wait); if (fdc_busy){ INT_ON; return -EINTR; } fdc_busy = 1; INT_ON; command_status = FD_COMMAND_NONE; reschedule_timeout(drive, "lock fdc", 0); set_fdc(drive); return 0;}#define LOCK_FDC(drive,interruptible) \if (lock_fdc(drive,interruptible)) return -EINTR;/* unlocks the driver */static inline void unlock_fdc(void){ raw_cmd = 0; if (!fdc_busy) DPRINT("FDC access conflict!\n"); if (DEVICE_INTR) DPRINT("device interrupt still active at FDC release: %p!\n", DEVICE_INTR); command_status = FD_COMMAND_NONE; del_timer(&fd_timeout); cont = NULL; fdc_busy = 0; floppy_release_irq_and_dma(); wake_up(&fdc_wait);}/* switches the motor off after a given timeout */static void motor_off_callback(unsigned long nr){ unsigned char mask = ~(0x10 << UNIT(nr)); set_dor(FDC(nr), mask, 0);}static struct timer_list motor_off_timer[N_DRIVE] = { { NULL, NULL, 0, 0, motor_off_callback }, { NULL, NULL, 0, 1, motor_off_callback }, { NULL, NULL, 0, 2, motor_off_callback }, { NULL, NULL, 0, 3, motor_off_callback }, { NULL, NULL, 0, 4, motor_off_callback }, { NULL, NULL, 0, 5, motor_off_callback }, { NULL, NULL, 0, 6, motor_off_callback }, { NULL, NULL, 0, 7, motor_off_callback }};/* schedules motor off */static void floppy_off(unsigned int drive){ unsigned long volatile delta; register int fdc=FDC(drive); if (!(FDCS->dor & (0x10 << UNIT(drive)))) return; del_timer(motor_off_timer+drive); /* make spindle stop in a position which minimizes spinup time * next time */ if (UDP->rps){ delta = jiffies - UDRS->first_read_date + HZ - UDP->spindown_offset; delta = ((delta * UDP->rps) % HZ) / UDP->rps; motor_off_timer[drive].expires = jiffies + UDP->spindown - delta; } add_timer(motor_off_timer+drive);}/* * cycle through all N_DRIVE floppy drives, for disk change testing. * stopping at current drive. This is done before any long operation, to * be sure to have up to date disk change information. */static void scandrives(void){ int i, drive, saved_drive; if (DP->select_delay) return; saved_drive = current_drive; for (i=0; i < N_DRIVE; i++){ drive = (saved_drive + i + 1) % N_DRIVE; if (UDRS->fd_ref == 0 || UDP->select_delay != 0) continue; /* skip closed drives */ set_fdc(drive); if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) & (0x10 << UNIT(drive)))) /* switch the motor off again, if it was off to * begin with */ set_dor(fdc, ~(0x10 << UNIT(drive)), 0); } set_fdc(saved_drive);}static void empty(void){}static struct tq_struct floppy_tq ={ 0, 0, 0, 0 };static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };static void cancel_activity(void){ CLEAR_INTR; floppy_tq.routine = (void *)(void *) empty; del_timer(&fd_timer);}/* this function makes sure that the disk stays in the drive during the * transfer */static void fd_watchdog(void){#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG){ DPRINT("calling disk change from watchdog\n"); }#endif if (disk_change(current_drive)){ DPRINT("disk removed during i/o\n"); cancel_activity(); cont->done(0); reset_fdc(); } else { del_timer(&fd_timer); fd_timer.function = (timeout_fn) fd_watchdog; fd_timer.expires = jiffies + HZ / 10; add_timer(&fd_timer); }}static void main_command_interrupt(void){ del_timer(&fd_timer); cont->interrupt();}/* waits for a delay (spinup or select) to pass */static int wait_for_completion(int delay, timeout_fn function){ if (FDCS->reset){ reset_fdc(); /* do the reset during sleep to win time * if we don't need to sleep, it's a good * occasion anyways */ return 1; } if ((signed) (jiffies - delay) < 0){ del_timer(&fd_timer); fd_timer.function = function; fd_timer.expires = delay; add_timer(&fd_timer); return 1; } return 0;}static int hlt_disabled=0;static void floppy_disable_hlt(void){ unsigned long flags; INT_OFF; if (!hlt_disabled){ hlt_disabled=1;#ifdef HAVE_DISABLE_HLT disable_hlt();#endif } INT_ON;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -