📄 floppy.c
字号:
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 */void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs){ void (*handler)(void) = DEVICE_INTR; int do_print; lasthandler = handler; interruptjiffies = jiffies; fd_disable_dma(); 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) { if(intr_count >= 2) { /* expected interrupt */ floppy_tq.routine = (void *)(void *) handler; queue_task_irq(&floppy_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); } else 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){ 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). */ fd_disable_dma(); 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); }}void show_floppy(void){ int i; printk("\n"); printk("floppy driver state\n"); printk("-------------------\n"); printk("now=%ld last interrupt=%d last called handler=%p\n", 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 %ld\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 %d\n", resultjiffies); printk("last redo_fd_request at %d\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=%d\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 (fd_timer.prev) printk("fd_timer.function=%p\n", fd_timer.function); if (fd_timeout.prev){ printk("timer_table=%p\n",fd_timeout.function); printk("expires=%ld\n",fd_timeout.expires-jiffies); printk("now=%ld\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){ if (!initialising) show_floppy(); cancel_activity(); sti(); floppy_enable_hlt(); fd_disable_dma(); /* 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(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 */ 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 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();}/* * ======================================================================== * here ends the bottom half. Exported routines are: * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc, * start_motor, reset_fdc, reset_fdc_info, interpret_errors. * Initialization also uses output_byte, result, set_dor, floppy_interrupt * and set_dor. * ======================================================================== *//* * General purpose continuations. * ============================== */static void do_wakeup(void){ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0); cont = 0; command_status += 2; wake_up(&command_done);}static struct cont_t wakeup_cont={ empty, do_wakeup, empty, (done_f)empty};static struct cont_t intr_cont={ empty, process_fd_request, empty, (done_f) empty};static int wait_til_done(void (*handler)(void), int interruptible){ int ret; unsigned long flags; floppy_tq.routine = (void *)(void *) handler; queue_task(&floppy_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); INT_OFF; while(command_status < 2 && NO_SIGNAL){ is_alive("wait_til_done"); if (interruptible) interruptible_sleep_on(&command_done); else sleep_on(&command_done); } if (command_status < 2){ cancel_activity(); cont = &intr_cont; reset_fdc(); INT_ON; return -EINTR; } INT_ON; if (FDCS->reset) command_status = FD_COMMAND_ERROR; if (command_status == FD_COMMAND_OKAY) ret=0; else ret=-EIO; command_status = FD_COMMAND_NONE; return ret;}static void generic_done(int result){ command_status = result; cont = &wakeup_cont;}static void generic_success(void){ cont->done(1);}static void generic_failure(void){ cont->done(0);}static void success_and_wakeup(void){ generic_success(); cont->redo();}/* * formatting and rw support. * ========================== */static int next_valid_format(void){ int probed_format;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -