📄 floppy.c
字号:
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) SET_INTR(main_command_interrupt); r=0; for (i=0; i< raw_cmd->cmd_count; i++) r|=output_byte(raw_cmd->cmd[i]);#ifdef DEBUGT debugt("rw_command: ");#endif 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){#ifdef DEBUGT debugt("seek interrupt:");#endif 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; } } SET_INTR(seek_interrupt); output_byte(FD_SEEK); output_byte(UNIT(current_drive)); LAST_OUT(track);#ifdef DEBUGT debugt("seek command:");#endif}static void recal_interrupt(void){#ifdef DEBUGT debugt("recal interrupt:");#endif if (inr !=2) FDCS->reset = 1; else if (ST0 & ST0_ECE) { switch(DRS->track){ case NEED_1_RECAL:#ifdef DEBUGT debugt("recal interrupt need 1 recal:");#endif /* 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:#ifdef DEBUGT debugt("recal interrupt need 2 recal:");#endif /* 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:#ifdef DEBUGT debugt("recal interrupt default:");#endif /* 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 */void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs){ void (*handler)(void) = DEVICE_INTR; int do_print; unsigned long f; lasthandler = handler; interruptjiffies = jiffies; f=claim_dma_lock(); fd_disable_dma(); release_dma_lock(f); floppy_enable_hlt(); CLEAR_INTR; 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; } 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) { schedule_bh( (void *)(void *) handler); } else FDCS->reset = 1; is_alive("normal interrupt end");}static void recalibrate_floppy(void){#ifdef DEBUGT debugt("recalibrate floppy:");#endif SET_INTR(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){#ifdef DEBUGT debugt("reset interrupt:");#endif 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; SET_INTR(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 (DEVICE_INTR) printk("DEVICE_INTR=%p\n", DEVICE_INTR); if (floppy_tq.sync) printk("floppy_tq.routine=%p\n", floppy_tq.routine); 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=%p\n", CURRENT); printk("command_status=%d\n", command_status); printk("\n");}static void floppy_shutdown(void){ 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)) return; if (fdc_dtr()) return;#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG){ DPRINT("calling disk change from floppy_ready\n"); }#endif if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) && disk_change(current_drive) && !DP->select_delay) twaddle(); /* this clears the dcl on certain drive/controller * combinations */#ifdef fd_chose_dma_mode if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) { unsigned long flags = claim_dma_lock(); fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length); release_dma_lock(flags); }#endif if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){ perpendicular_mode(); fdc_specify(); /* must be done here because of hut, hlt ... */ seek_floppy(); } else { if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) fdc_specify(); setup_rw_floppy(); }}static void floppy_start(void){ reschedule_timeout(CURRENTD, "floppy start", 0); scandrives();#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG){ DPRINT("setting NEWCHANGE in floppy_start\n"); }#endif SETF(FD_DISK_NEWCHANGE); floppy_ready();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -