📄 fdc-io.c
字号:
if (result >= 0) { cmd2[1] = (fdc_drv_spec[0] & 0x03) | 0x04; cmd2[2] = (fdc_drv_spec[1] & 0x03) | 0x24; cmd2[3] = (fdc_drv_spec[2] & 0x03) | 0x44; cmd2[4] = (fdc_drv_spec[3] & 0x03) | 0x64; fdc_command(cmd2, NR_ITEMS(cmd2)); if (result < 0) { TRACE(1, "Setting of drive specs failed"); return; } } else { TRACE(2, "Save of drive specs failed"); } } TRACE_EXIT;}/* Restore the previously saved Drive Specification values */void fdc_restore_drive_specs(void){ byte cmd[] = {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; int result; TRACE_FUN(8, "fdc_restore_drive_specs"); if (fdc.type > i82078_1) { cmd[1] = (fdc_drv_spec[0] & 0x1f) | 0x00; cmd[2] = (fdc_drv_spec[1] & 0x1f) | 0x20; cmd[3] = (fdc_drv_spec[2] & 0x1f) | 0x40; cmd[4] = (fdc_drv_spec[3] & 0x1f) | 0x60; result = fdc_command(cmd, NR_ITEMS(cmd)); if (result < 0) { TRACE(2, "Restoration of drive specs failed"); } } TRACE_EXIT;}/* Select clock for fdc, must correspond with tape drive setting ! * This also influences the fdc timing so we must adjust some values. */void fdc_set_data_rate(int rate){ /* Select clock for fdc, must correspond with tape drive setting ! * This also influences the fdc timing so we must adjust some values. */ fdc_data_rate = rate; fdc_update_dsr(); fdc_set_seek_rate(fdc_seek_rate); /* re-adjust for changed clock */}/* Reset the floppy disk controller. Leave the ftape_unit selected. */void fdc_reset(void){ TRACE_FUN(8, "fdc_reset"); int unit = FTAPE_UNIT; byte fdc_ctl = unit | FDC_DMA_MODE; int st0; int i; int result; int dummy; if (ftape_motor) { fdc_ctl |= FDC_MOTOR_0 << unit; }#ifdef MACH2 outb_p(fdc_ctl & 0x0f, fdc.dor); outb_p(fdc_ctl, fdc.dor2);#else outb_p(fdc_ctl, fdc.dor); /* assert reset, keep unit selected */#endif fdc_usec_wait(10 /* usec */ ); /* delay >= 14 fdc clocks */ fdc_ctl |= FDC_RESET_NOT; fdc_mode = fdc_idle;#ifdef MACH2 outb_p(fdc_ctl & 0x0f, fdc.dor); outb_p(fdc_ctl, fdc.dor2);#else outb_p(fdc_ctl, fdc.dor); /* release reset */#endif result = fdc_interrupt_wait(1 * SECOND); if (result < 0) { TRACE(1, "missing interrupt after reset"); } fdc_set_data_rate(fdc_data_rate); /* keep original setting */ fdc_usec_wait(1000 /* usec */ ); /* don't know why, but needed */ for (i = 0; i < 4; ++i) { /* clear disk-change status */ fdc_sense_interrupt_status(&st0, &dummy); if (i == unit) { current_cylinder = dummy; } } fdc_set_seek_rate(2); TRACE_EXIT;}/* When we're done, put the fdc into reset mode so that the regular floppy disk driver will figure out that something is wrong and initialize the controller the way it wants. */void fdc_disable(void){ TRACE_FUN(8, "fdc_disable"); int result; byte cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00}; byte cmd2[] = {FDC_LOCK}; byte cmd3[] = {FDC_UNLOCK}; byte stat[1]; if (CLK_48MHZ && fdc.type >= i82078) cmd1[0] |= FDC_CLK48_BIT; if (fdc_fifo_locked) { result = fdc_issue_command(cmd3, 1, stat, 1); if (result < 0 || stat[0] != 0x00) { TRACE(-1, "couldn't unlock fifo, configuration remains changed"); } else { cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1); result = fdc_command(cmd1, NR_ITEMS(cmd1)); if (result < 0) { TRACE(-1, "couldn't reconfigure fifo to old state"); } else if (fdc_lock_state) { result = fdc_issue_command(cmd2, 1, stat, 1); if (result < 0) { TRACE(-1, "couldn't lock old state again"); } } TRACEx3(5, "fifo restored: %sabled, thr. %d, %slocked", fdc_fifo_state ? "en" : "dis", fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); } fdc_fifo_locked = 0; }#ifdef MACH2 outb_p(FTAPE_UNIT & 0x0f, fdc.dor); outb_p(FTAPE_UNIT, fdc.dor2); udelay(10); outb_p(FDC_RESET_NOT & 0x0f, fdc.dor); outb_p(FDC_RESET_NOT, fdc.dor2);#else outb_p(FTAPE_UNIT, fdc.dor); udelay(10); outb_p(FDC_RESET_NOT, fdc.dor);#endif TRACE_EXIT;}/* Specify FDC seek-rate */int fdc_set_seek_rate(int seek_rate){ byte in[3]; const int hut = 1; /* minimize head unload time */ const int hlt = 1; /* minimize head load time */ const int rates[] = {250, 2000, 500, 1000}; in[0] = FDC_SPECIFY; in[1] = (((16 - (rates[fdc_data_rate & 0x03] * seek_rate) / 500) << 4) | hut); in[2] = (hlt << 1) | 0; fdc_seek_rate = seek_rate; return fdc_command(in, 3);}/* Sense drive status: get unit's drive status (ST3) */int fdc_sense_drive_status(int *st3){ TRACE_FUN(8, "fdc_sense_drive_status"); int result; byte out[2]; byte in[1]; out[0] = FDC_SENSED; out[1] = FTAPE_UNIT; result = fdc_issue_command(out, 2, in, 1); if (result < 0) { TRACE(1, "issue_command failed"); } else { *st3 = in[0]; result = 0; } TRACE_EXIT; return result;}/* Sense Interrupt Status command: * should be issued at the end of each seek. * get ST0 and current cylinder. */int fdc_sense_interrupt_status(int *st0, int *current_cylinder){ TRACE_FUN(8, "fdc_sense_interrupt_status"); int result; byte out[1]; byte in[2]; out[0] = FDC_SENSEI; result = fdc_issue_command(out, 1, in, 2); if (result) { TRACE(1, "issue_command failed"); } else { *st0 = in[0]; *current_cylinder = in[1]; result = 0; } TRACE_EXIT; return result;}/* step to track */int fdc_seek(int track){ TRACE_FUN(8, "fdc_seek"); int result; byte out[3]; int st0, pcn; out[0] = FDC_SEEK; out[1] = FTAPE_UNIT; out[2] = track; seek_completed = 0; result = fdc_command(out, 3); if (result != 0) { TRACEi(1, "failed, status =", result); TRACEx1(4, "destination was: %d, resetting FDC...", track); /* We really need this command to work ! */ fdc_reset(); TRACE_EXIT; return result; } /* Handle interrupts until seek_completed or timeout. */ for (;;) { result = fdc_interrupt_wait(2 * SECOND); if (result < 0) { TRACEi(2, "fdc_interrupt_wait timeout, status =", result); TRACE_EXIT; return result; } else if (seek_completed) { result = fdc_sense_interrupt_status(&st0, &pcn); if (result != 0) { TRACEi(1, "fdc_sense_interrupt_status failed, status =", result); TRACE_EXIT; return result; } if ((st0 & ST0_SEEK_END) == 0) { TRACE(1, "no seek-end after seek completion !??"); TRACE_EXIT; return -EIO; } break; } } /* Verify whether we issued the right tape command. */ /* Verify that we seek to the proper track. */ if (pcn != track) { TRACE(1, "bad seek.."); TRACE_EXIT; return -EIO; } current_cylinder = pcn; TRACE_EXIT; return 0;}/* Recalibrate and wait until home. */int fdc_recalibrate(void){ TRACE_FUN(8, "fdc_recalibrate"); int result; byte out[2]; int st0; int pcn; int retry; result = fdc_set_seek_rate(6); if (result) { TRACEi(1, "fdc_set_seek_rate failed, status =", result); TRACE_EXIT; return result; } out[0] = FDC_RECAL; out[1] = FTAPE_UNIT; seek_completed = 0; result = fdc_command(out, 2); if (result) { TRACEi(1, "fdc_command failed, status =", result); TRACE_EXIT; return result; } /* Handle interrupts until seek_completed or timeout. */ for (retry = 0;; ++retry) { result = fdc_interrupt_wait(2 * SECOND); if (result < 0) { TRACE(1, "fdc_interrupt_wait failed"); TRACE_EXIT; return result; } else if (result == 0 && seek_completed) { result = fdc_sense_interrupt_status(&st0, &pcn); if (result != 0) { TRACEi(1, "fdc_sense_interrupt_status failed, status =", result); TRACE_EXIT; return result; } if ((st0 & ST0_SEEK_END) == 0) { if (retry < 1) { continue; /* some drives/fdc's give an extra interrupt */ } else { TRACE(1, "no seek-end after seek completion !??"); TRACE_EXIT; return -EIO; } } break; } } current_cylinder = pcn; if (pcn != 0) { TRACEi(1, "failed: resulting track =", pcn); } result = fdc_set_seek_rate(2); if (result != 0) { TRACEi(1, "fdc_set_seek_rate failed, status =", result); TRACE_EXIT; return result; } TRACE_EXIT; return 0;}/* Setup Floppy Disk Controller and DMA to read or write the next cluster * of good sectors from or to the current segment. */int setup_fdc_and_dma(buffer_struct * buff, unsigned char operation){ TRACE_FUN(8, "setup_fdc_and_dma"); unsigned long flags; byte perpend[] = {FDC_PERPEND, 0x00}; unsigned char out[9]; int result; int dma_mode; if (operation == FDC_READ || operation == FDC_READ_DELETED) { dma_mode = DMA_MODE_READ; if (qic_std == QIC_TAPE_QIC3020) { if (fdc.type < i82077AA) { /* fdc does not support perpendicular mode. complain */ TRACE(0, "Your FDC does not support QIC-3020."); return -EIO; } /* enable perpendicular mode */ perpend[1] = 0x83 + (0x04 << FTAPE_UNIT); result = fdc_command(perpend, 2); if (result < 0) { TRACE(1, "Perpendicular mode entry failed!"); } else { TRACE(4, "Perpendicular mode entered"); perpend_mode = 1; } } else if (perpend_mode) { /* Turn off perpendicular mode */ perpend[1] = 0x80; result = fdc_command(perpend, 2); if (result < 0) { TRACE(1, "Perpendicular mode exit failed!"); } else { TRACE(4, "Perpendicular mode exited"); perpend_mode = 0; } } TRACEx2(5, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr); } else if (operation == FDC_WRITE || operation == FDC_WRITE_DELETED) { dma_mode = DMA_MODE_WRITE; /* When writing QIC-3020 tapes, turn on perpendicular mode. */ if (qic_std == QIC_TAPE_QIC3020) { if (fdc.type < i82077AA) { /* fdc does not support perpendicular mode: complain */ TRACE(0, "Your FDC does not support QIC-3020."); return -EIO; } perpend[1] = 0x83 + (0x4 << FTAPE_UNIT); result = fdc_command(perpend, 2); if (result < 0) { TRACE(1, "Perpendicular mode entry failed!"); } else { TRACE(4, "Perpendicular mode entered"); perpend_mode = 1; } } else if (perpend_mode) { perpend[1] = 0x80; result = fdc_command(perpend, 2); if (result < 0) { TRACE(1, "Perpendicular mode exit failed!"); } else { TRACE(4, "Perpendicular mode exited"); perpend_mode = 0; } } TRACEx2(5, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr); } else { TRACE(-1, "bug: illegal operation parameter"); TRACE_EXIT; return -EIO; } /* Program the DMA controller. */ save_flags(flags); cli(); /* could be called from ISR ! */ disable_dma(fdc.dma); clear_dma_ff(fdc.dma); set_dma_mode(fdc.dma, dma_mode); set_dma_addr(fdc.dma, (unsigned) buff->ptr); set_dma_count(fdc.dma, SECTOR_SIZE * buff->sector_count);#ifdef GCC_2_4_5_BUG /* This seemingly stupid construction confuses the gcc-2.4.5 * code generator enough to create correct code. */ if (1) { int i; for (i = 0; i < 1; ++i) { udelay(1); } }#endif enable_dma(fdc.dma); /* Issue FDC command to start reading/writing.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -