📄 floppy.c
字号:
if (!probing) { printk("sector not found"); tell_sector(); } else printk("probe failed..."); } else if (ST2 & ST2_WC) { /* seek error */ printk("wrong cylinder"); } else if (ST2 & ST2_BC) { /* cylinder marked as bad */ printk("bad cylinder"); } else { printk ("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2); tell_sector(); } printk("\n"); } if (ST2 & ST2_WC || ST2 & ST2_BC) /* wrong cylinder => recal */ DRS->track = NEED_2_RECAL; return bad; case 0x80: /* invalid command given */ DPRINT("Invalid FDC command given!\n"); cont->done(0); return 2; case 0xc0: DPRINT("Abnormal termination caused by polling\n"); cont->error(); return 2; default: /* (0) Normal command termination */ return 0; }}/* * This routine is called when everything should be correctly set up * for the transfer (i.e. floppy motor is on, the correct floppy is * selected, and the head is sitting on the right track). */static void setup_rw_floppy(void){ int i, r, flags, dflags; unsigned long ready_date; timeout_fn function; flags = raw_cmd->flags; if (flags & (FD_RAW_READ | FD_RAW_WRITE)) flags |= FD_RAW_INTR; if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) { ready_date = DRS->spinup_date + DP->spinup; /* If spinup will take a long time, rerun scandrives * again just before spinup completion. Beware that * after scandrives, we must again wait for selection. */ if ((signed)(ready_date - jiffies) > DP->select_delay) { ready_date -= DP->select_delay; function = (timeout_fn) floppy_start; } else function = (timeout_fn) setup_rw_floppy; /* wait until the floppy is spinning fast enough */ if (fd_wait_for_completion(ready_date, function)) return; } dflags = DRS->flags; if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE)) setup_DMA(); if (flags & FD_RAW_INTR) do_floppy = main_command_interrupt; r = 0; for (i = 0; i < raw_cmd->cmd_count; i++) r |= output_byte(raw_cmd->cmd[i]); debugt("rw_command: "); if (r) { cont->error(); reset_fdc(); return; } if (!(flags & FD_RAW_INTR)) { inr = result(); cont->interrupt(); } else if (flags & FD_RAW_NEED_DISK) fd_watchdog();}static int blind_seek;/* * This is the routine called after every seek (or recalibrate) interrupt * from the floppy controller. */static void seek_interrupt(void){ debugt("seek interrupt:"); if (inr != 2 || (ST0 & 0xF8) != 0x20) { DPRINT("seek failed\n"); DRS->track = NEED_2_RECAL; cont->error(); cont->redo(); return; } if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek) {#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT ("clearing NEWCHANGE flag because of effective seek\n"); DPRINT("jiffies=%lu\n", jiffies); }#endif CLEARF(FD_DISK_NEWCHANGE); /* effective seek */ DRS->select_date = jiffies; } DRS->track = ST1; floppy_ready();}static void check_wp(void){ if (TESTF(FD_VERIFY)) { /* check write protection */ output_byte(FD_GETSTATUS); output_byte(UNIT(current_drive)); if (result() != 1) { FDCS->reset = 1; return; } CLEARF(FD_VERIFY); CLEARF(FD_NEED_TWADDLE);#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT("checking whether disk is write protected\n"); DPRINT("wp=%x\n", ST3 & 0x40); }#endif if (!(ST3 & 0x40)) SETF(FD_DISK_WRITABLE); else CLEARF(FD_DISK_WRITABLE); }}static void seek_floppy(void){ int track; blind_seek = 0;#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT("calling disk change from seek\n"); }#endif if (!TESTF(FD_DISK_NEWCHANGE) && disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) { /* the media changed flag should be cleared after the seek. * If it isn't, this means that there is really no disk in * the drive. */ SETF(FD_DISK_CHANGED); cont->done(0); cont->redo(); return; } if (DRS->track <= NEED_1_RECAL) { recalibrate_floppy(); return; } else if (TESTF(FD_DISK_NEWCHANGE) && (raw_cmd->flags & FD_RAW_NEED_DISK) && (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) { /* we seek to clear the media-changed condition. Does anybody * know a more elegant way, which works on all drives? */ if (raw_cmd->track) track = raw_cmd->track - 1; else { if (DP->flags & FD_SILENT_DCL_CLEAR) { set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0); blind_seek = 1; raw_cmd->flags |= FD_RAW_NEED_SEEK; } track = 1; } } else { check_wp(); if (raw_cmd->track != DRS->track && (raw_cmd->flags & FD_RAW_NEED_SEEK)) track = raw_cmd->track; else { setup_rw_floppy(); return; } } do_floppy = seek_interrupt; output_byte(FD_SEEK); output_byte(UNIT(current_drive)); LAST_OUT(track); debugt("seek command:");}static void recal_interrupt(void){ debugt("recal interrupt:"); if (inr != 2) FDCS->reset = 1; else if (ST0 & ST0_ECE) { switch (DRS->track) { case NEED_1_RECAL: debugt("recal interrupt need 1 recal:"); /* after a second recalibrate, we still haven't * reached track 0. Probably no drive. Raise an * error, as failing immediately might upset * computers possessed by the Devil :-) */ cont->error(); cont->redo(); return; case NEED_2_RECAL: debugt("recal interrupt need 2 recal:"); /* If we already did a recalibrate, * and we are not at track 0, this * means we have moved. (The only way * not to move at recalibration is to * be already at track 0.) Clear the * new change flag */#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT ("clearing NEWCHANGE flag because of second recalibrate\n"); }#endif CLEARF(FD_DISK_NEWCHANGE); DRS->select_date = jiffies; /* fall through */ default: debugt("recal interrupt default:"); /* Recalibrate moves the head by at * most 80 steps. If after one * recalibrate we don't have reached * track 0, this might mean that we * started beyond track 80. Try * again. */ DRS->track = NEED_1_RECAL; break; } } else DRS->track = ST1; floppy_ready();}static void print_result(char *message, int inr){ int i; DPRINT("%s ", message); if (inr >= 0) for (i = 0; i < inr; i++) printk("repl[%d]=%x ", i, reply_buffer[i]); printk("\n");}/* interrupt handler. Note that this can be called externally on the Sparc */irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs){ void (*handler) (void) = do_floppy; int do_print; unsigned long f; lasthandler = handler; interruptjiffies = jiffies; f = claim_dma_lock(); fd_disable_dma(); release_dma_lock(f); floppy_enable_hlt(); do_floppy = NULL; if (fdc >= N_FDC || FDCS->address == -1) { /* we don't even know which FDC is the culprit */ printk("DOR0=%x\n", fdc_state[0].dor); printk("floppy interrupt on bizarre fdc %d\n", fdc); printk("handler=%p\n", handler); is_alive("bizarre fdc"); return IRQ_NONE; } FDCS->reset = 0; /* We have to clear the reset flag here, because apparently on boxes * with level triggered interrupts (PS/2, Sparc, ...), it is needed to * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the * emission of the SENSEI's. * It is OK to emit floppy commands because we are in an interrupt * handler here, and thus we have to fear no interference of other * activity. */ do_print = !handler && print_unex && !initialising; inr = result(); if (do_print) print_result("unexpected interrupt", inr); if (inr == 0) { int max_sensei = 4; do { output_byte(FD_SENSEI); inr = result(); if (do_print) print_result("sensei", inr); max_sensei--; } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2 && max_sensei); } if (!handler) { FDCS->reset = 1; return IRQ_NONE; } schedule_bh(handler); is_alive("normal interrupt end"); /* FIXME! Was it really for us? */ return IRQ_HANDLED;}static void recalibrate_floppy(void){ debugt("recalibrate floppy:"); do_floppy = recal_interrupt; output_byte(FD_RECALIBRATE); LAST_OUT(UNIT(current_drive));}/* * Must do 4 FD_SENSEIs after reset because of ``drive polling''. */static void reset_interrupt(void){ debugt("reset interrupt:"); result(); /* get the status ready for set_fdc */ if (FDCS->reset) { printk("reset set in interrupt, calling %p\n", cont->error); cont->error(); /* a reset just after a reset. BAD! */ } cont->redo();}/* * reset is done by pulling bit 2 of DOR low for a while (old FDCs), * or by setting the self clearing bit 7 of STATUS (newer FDCs) */static void reset_fdc(void){ unsigned long flags; do_floppy = reset_interrupt; FDCS->reset = 0; reset_fdc_info(0); /* Pseudo-DMA may intercept 'reset finished' interrupt. */ /* Irrelevant for systems with true DMA (i386). */ flags = claim_dma_lock(); fd_disable_dma(); release_dma_lock(flags); if (FDCS->version >= FDC_82072A) fd_outb(0x80 | (FDCS->dtr & 3), FD_STATUS); else { fd_outb(FDCS->dor & ~0x04, FD_DOR); udelay(FD_RESET_DELAY); fd_outb(FDCS->dor, FD_DOR); }}static void show_floppy(void){ int i; printk("\n"); printk("floppy driver state\n"); printk("-------------------\n"); printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n", jiffies, interruptjiffies, jiffies - interruptjiffies, lasthandler);#ifdef FLOPPY_SANITY_CHECK printk("timeout_message=%s\n", timeout_message); printk("last output bytes:\n"); for (i = 0; i < OLOGSIZE; i++) printk("%2x %2x %lu\n", output_log[(i + output_log_pos) % OLOGSIZE].data, output_log[(i + output_log_pos) % OLOGSIZE].status, output_log[(i + output_log_pos) % OLOGSIZE].jiffies); printk("last result at %lu\n", resultjiffies); printk("last redo_fd_request at %lu\n", lastredo); for (i = 0; i < resultsize; i++) { printk("%2x ", reply_buffer[i]); } printk("\n");#endif printk("status=%x\n", fd_inb(FD_STATUS)); printk("fdc_busy=%lu\n", fdc_busy); if (do_floppy) printk("do_floppy=%p\n", do_floppy); if (floppy_work.pending) printk("floppy_work.func=%p\n", floppy_work.func); if (timer_pending(&fd_timer)) printk("fd_timer.function=%p\n", fd_timer.function); if (timer_pending(&fd_timeout)) { printk("timer_function=%p\n", fd_timeout.function); printk("expires=%lu\n", fd_timeout.expires - jiffies); printk("now=%lu\n", jiffies); } printk("cont=%p\n", cont); printk("current_req=%p\n", current_req); printk("command_status=%d\n", command_status); printk("\n");}static void floppy_shutdown(unsigned long data){ unsigned long flags; if (!initialising) show_floppy(); cancel_activity(); floppy_enable_hlt(); flags = claim_dma_lock(); fd_disable_dma(); release_dma_lock(flags); /* avoid dma going to a random drive after shutdown */ if (!initialising) DPRINT("floppy timeout called\n"); FDCS->reset = 1; if (cont) { cont->done(0); cont->redo(); /* this will recall reset when needed */ } else { printk("no cont in shutdown!\n"); process_fd_request(); } is_alive("floppy shutdown");}/*typedef void (*timeout_fn)(unsigned long);*//* start motor, check media-changed condition and write protection */static int start_motor(void (*function) (void)){ int mask, data; mask = 0xfc; data = UNIT(current_drive); if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) { if (!(FDCS->dor & (0x10 << UNIT(current_drive)))) { set_debugt(); /* no read since this drive is running */ DRS->first_read_date = 0; /* note motor start time if motor is not yet running */ DRS->spinup_date = jiffies; data |= (0x10 << UNIT(current_drive)); } } else if (FDCS->dor & (0x10 << UNIT(current_drive))) mask &= ~(0x10 << UNIT(current_drive)); /* starts motor and selects floppy */ del_timer(motor_off_timer + current_drive); set_dor(fdc, mask, data); /* wait_for_completion also schedules reset if needed. */ return (fd_wait_for_completion(DRS->select_date + DP->select_delay, (timeout_fn) function));}static void floppy_ready(void){ CHECK_RESET; if (start_motor(floppy_ready))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -