📄 floppy.c
字号:
static void floppy_enable_hlt(void){ unsigned long flags; INT_OFF; if (hlt_disabled){ hlt_disabled=0;#ifdef HAVE_DISABLE_HLT enable_hlt();#endif } INT_ON;}static void setup_DMA(void){ unsigned long flags;#ifdef FLOPPY_SANITY_CHECK if (raw_cmd->length == 0){ int i; printk("zero dma transfer size:"); for (i=0; i < raw_cmd->cmd_count; i++) printk("%x,", raw_cmd->cmd[i]); printk("\n"); cont->done(0); FDCS->reset = 1; return; } if ((long) raw_cmd->kernel_data % 512){ printk("non aligned address: %p\n", raw_cmd->kernel_data); cont->done(0); FDCS->reset=1; return; } if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) { printk("DMA crossing 64-K boundary %p-%p\n", raw_cmd->kernel_data, raw_cmd->kernel_data + raw_cmd->length); cont->done(0); FDCS->reset=1; return; }#endif INT_OFF; fd_disable_dma(); fd_clear_dma_ff(); fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length); fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)? DMA_MODE_READ : DMA_MODE_WRITE); fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data)); fd_set_dma_count(raw_cmd->length); virtual_dma_port = FDCS->address; fd_enable_dma(); INT_ON; floppy_disable_hlt();}void show_floppy(void);/* waits until the fdc becomes ready */static int wait_til_ready(void){ int counter, status; if(FDCS->reset) return -1; for (counter = 0; counter < 10000; counter++) { status = fd_inb(FD_STATUS); if (status & STATUS_READY) return status; } if (!initialising) { DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc); show_floppy(); } FDCS->reset = 1; return -1;}/* sends a command byte to the fdc */static int output_byte(char byte){ int status; if ((status = wait_til_ready()) < 0) return -1; if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY){ fd_outb(byte,FD_DATA);#ifdef FLOPPY_SANITY_CHECK output_log[output_log_pos].data = byte; output_log[output_log_pos].status = status; output_log[output_log_pos].jiffies = jiffies; output_log_pos = (output_log_pos + 1) % OLOGSIZE;#endif return 0; } FDCS->reset = 1; if (!initialising) { DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n", byte, fdc, status); show_floppy(); } return -1;}#define LAST_OUT(x) if (output_byte(x)<0){ reset_fdc();return;}/* gets the response from the fdc */static int result(void){ int i, status; for(i=0; i < MAX_REPLIES; i++) { if ((status = wait_til_ready()) < 0) break; status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA; if ((status & ~STATUS_BUSY) == STATUS_READY){#ifdef FLOPPY_SANITY_CHECK resultjiffies = jiffies; resultsize = i;#endif return i; } if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) reply_buffer[i] = fd_inb(FD_DATA); else break; } if(!initialising) { DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n", fdc, status, i); show_floppy(); } FDCS->reset = 1; return -1;}#define MORE_OUTPUT -2/* does the fdc need more output? */static int need_more_output(void){ int status; if( (status = wait_til_ready()) < 0) return -1; if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY) return MORE_OUTPUT; return result();}/* Set perpendicular mode as required, based on data rate, if supported. * 82077 Now tested. 1Mbps data rate only possible with 82077-1. */static inline void perpendicular_mode(void){ unsigned char perp_mode; if (raw_cmd->rate & 0x40){ switch(raw_cmd->rate & 3){ case 0: perp_mode=2; break; case 3: perp_mode=3; break; default: DPRINT("Invalid data rate for perpendicular mode!\n"); cont->done(0); FDCS->reset = 1; /* convenient way to return to * redo without to much hassle (deep * stack et al. */ return; } } else perp_mode = 0; if (FDCS->perp_mode == perp_mode) return; if (FDCS->version >= FDC_82077_ORIG) { output_byte(FD_PERPENDICULAR); output_byte(perp_mode); FDCS->perp_mode = perp_mode; } else if (perp_mode) { DPRINT("perpendicular mode not supported by this FDC.\n"); }} /* perpendicular_mode */static int fifo_depth = 0xa;static int no_fifo = 0;static int fdc_configure(void){ /* Turn on FIFO */ output_byte(FD_CONFIGURE); if(need_more_output() != MORE_OUTPUT) return 0; output_byte(0); output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf)); output_byte(0); /* pre-compensation from track 0 upwards */ return 1;} #define NOMINAL_DTR 500/* Issue a "SPECIFY" command to set the step rate time, head unload time, * head load time, and DMA disable flag to values needed by floppy. * * The value "dtr" is the data transfer rate in Kbps. It is needed * to account for the data rate-based scaling done by the 82072 and 82077 * FDC types. This parameter is ignored for other types of FDCs (i.e. * 8272a). * * Note that changing the data transfer rate has a (probably deleterious) * effect on the parameters subject to scaling for 82072/82077 FDCs, so * fdc_specify is called again after each data transfer rate * change. * * srt: 1000 to 16000 in microseconds * hut: 16 to 240 milliseconds * hlt: 2 to 254 milliseconds * * These values are rounded up to the next highest available delay time. */static void fdc_specify(void){ unsigned char spec1, spec2; int srt, hlt, hut; unsigned long dtr = NOMINAL_DTR; unsigned long scale_dtr = NOMINAL_DTR; int hlt_max_code = 0x7f; int hut_max_code = 0xf; if (FDCS->need_configure && FDCS->version >= FDC_82072A) { fdc_configure(); FDCS->need_configure = 0; /*DPRINT("FIFO enabled\n");*/ } switch (raw_cmd->rate & 0x03) { case 3: dtr = 1000; break; case 1: dtr = 300; if (FDCS->version >= FDC_82078) { /* chose the default rate table, not the one * where 1 = 2 Mbps */ output_byte(FD_DRIVESPEC); if(need_more_output() == MORE_OUTPUT) { output_byte(UNIT(current_drive)); output_byte(0xc0); } } break; case 2: dtr = 250; break; } if (FDCS->version >= FDC_82072) { scale_dtr = dtr; hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ } /* Convert step rate from microseconds to milliseconds and 4 bits */ srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR; SUPBOUND(srt, 0xf); INFBOUND(srt, 0); hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR; if (hlt < 0x01) hlt = 0x01; else if (hlt > 0x7f) hlt = hlt_max_code; hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR; if (hut < 0x1) hut = 0x1; else if (hut > 0xf) hut = hut_max_code; spec1 = (srt << 4) | hut; spec2 = (hlt << 1) | (use_virtual_dma & 1); /* If these parameters did not change, just return with success */ if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) { /* Go ahead and set spec1 and spec2 */ output_byte(FD_SPECIFY); output_byte(FDCS->spec1 = spec1); output_byte(FDCS->spec2 = spec2); }} /* fdc_specify *//* Set the FDC's data transfer rate on behalf of the specified drive. * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue * of the specify command (i.e. using the fdc_specify function). */static int fdc_dtr(void){ /* If data rate not already set to desired value, set it. */ if ((raw_cmd->rate & 3) == FDCS->dtr) return 0; /* Set dtr */ fd_outb(raw_cmd->rate & 3, FD_DCR); /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) * need a stabilization period of several milliseconds to be * enforced after data rate changes before R/W operations. * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) */ FDCS->dtr = raw_cmd->rate & 3; return(wait_for_completion(jiffies+2*HZ/100, (timeout_fn) floppy_ready));} /* fdc_dtr */static void tell_sector(void){ printk(": track %d, head %d, sector %d, size %d", R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);} /* tell_sector *//* * OK, this error interpreting routine is called after a * DMA read/write has succeeded * or failed, so we check the results, and copy any buffers. * hhb: Added better error reporting. * ak: Made this into a separate routine. */static int interpret_errors(void){ char bad; if (inr!=7) { DPRINT("-- FDC reply error"); FDCS->reset = 1; return 1; } /* check IC to find cause of interrupt */ switch (ST0 & ST0_INTR) { case 0x40: /* error occurred during command execution */ if (ST1 & ST1_EOC) return 0; /* occurs with pseudo-DMA */ bad = 1; if (ST1 & ST1_WP) { DPRINT("Drive is write protected\n"); CLEARF(FD_DISK_WRITABLE); cont->done(0); bad = 2; } else if (ST1 & ST1_ND) { SETF(FD_NEED_TWADDLE); } else if (ST1 & ST1_OR) { if (DP->flags & FTD_MSG) DPRINT("Over/Underrun - retrying\n"); bad = 0; }else if (*errors >= DP->max_errors.reporting){ DPRINT(""); if (ST0 & ST0_ECE) { printk("Recalibrate failed!"); } else if (ST2 & ST2_CRC) { printk("data CRC error"); tell_sector(); } else if (ST1 & ST1_CRC) { printk("CRC error"); tell_sector(); } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) { 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,ready_date,r, flags,dflags; 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 (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=%ld\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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -