?? floppy.c
字號:
* opcode. This routine sets up the DMA chip. Note that the chip is not * capable of doing a DMA across a 64K boundary (e.g., you can't read a * 512-byte block starting at physical address 65520). * * Warning! Also note that it's not possible to do DMA above 16 MB because * the ISA bus uses 24-bit addresses. Addresses above 16 MB therefore will * be interpreted modulo 16 MB, dangerously overwriting arbitrary memory. * A check here denies the I/O if the address is out of range. */ pvb_pair_t byte_out[9]; int s; /* First check the DMA memory address not to exceed maximum. */ if (tmp_phys != (tmp_phys & DMA_ADDR_MASK)) { report("FLOPPY", "DMA denied because address out of range", NO_NUM); return(EIO); } /* Set up the DMA registers. (The comment on the reset is a bit strong, * it probably only resets the floppy channel.) */ pv_set(byte_out[0], DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */ pv_set(byte_out[1], DMA_FLIPFLOP, 0); /* write anything to reset it */ pv_set(byte_out[2], DMA_MODE, opcode == DEV_SCATTER ? DMA_WRITE : DMA_READ); pv_set(byte_out[3], DMA_ADDR, (unsigned) tmp_phys >> 0); pv_set(byte_out[4], DMA_ADDR, (unsigned) tmp_phys >> 8); pv_set(byte_out[5], DMA_TOP, (unsigned) (tmp_phys >> 16)); pv_set(byte_out[6], DMA_COUNT, (((SECTOR_SIZE - 1) >> 0) & 0xff)); pv_set(byte_out[7], DMA_COUNT, (SECTOR_SIZE - 1) >> 8); pv_set(byte_out[8], DMA_INIT, 2); /* some sort of enable */ if ((s=sys_voutb(byte_out, 9)) != OK) panic("FLOPPY","Sys_voutb in dma_setup() failed", s); return(OK);}/*===========================================================================* * start_motor * *===========================================================================*/PRIVATE void start_motor(){/* Control of the floppy disk motors is a big pain. If a motor is off, you * have to turn it on first, which takes 1/2 second. You can't leave it on * all the time, since that would wear out the diskette. However, if you turn * the motor off after each operation, the system performance will be awful. * The compromise used here is to leave it on for a few seconds after each * operation. If a new operation is started in that interval, it need not be * turned on again. If no new operation is started, a timer goes off and the * motor is turned off. I/O port DOR has bits to control each of 4 drives. */ int s, motor_bit, running; message mess; motor_bit = 1 << f_drive; /* bit mask for this drive */ running = motor_status & motor_bit; /* nonzero if this motor is running */ motor_status |= motor_bit; /* want this drive running too */ if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT | f_drive)) != OK) panic("FLOPPY","Sys_outb in start_motor() failed", s); /* If the motor was already running, we don't have to wait for it. */ if (running) return; /* motor was already running */ /* Set an alarm timer to force a timeout if the hardware does not interrupt * in time. Expect HARD_INT message, but check for SYN_ALARM timeout. */ f_set_timer(&f_tmr_timeout, f_dp->start, f_timeout); f_busy = BSY_IO; do { receive(ANY, &mess); if (mess.m_type == SYN_ALARM) { f_expire_tmrs(NULL, NULL); } else if(mess.m_type == DEV_PING) { notify(mess.m_source); } else { f_busy = BSY_IDLE; } } while (f_busy == BSY_IO); f_fp->fl_sector = NO_SECTOR;}/*===========================================================================* * stop_motor * *===========================================================================*/PRIVATE void stop_motor(tp)timer_t *tp;{/* This routine is called from an alarm timer after several seconds have * elapsed with no floppy disk activity. It turns the drive motor off. */ int s; motor_status &= ~(1 << tmr_arg(tp)->ta_int); if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT)) != OK) panic("FLOPPY","Sys_outb in stop_motor() failed", s);}/*===========================================================================* * floppy_stop * *===========================================================================*/PRIVATE void floppy_stop(struct driver *dp, message *m_ptr){/* Stop all activity and cleanly exit with the system. */ int s; sigset_t sigset = m_ptr->NOTIFY_ARG; if (sigismember(&sigset, SIGTERM) || sigismember(&sigset, SIGKSTOP)) { if ((s=sys_outb(DOR, ENABLE_INT)) != OK) panic("FLOPPY","Sys_outb in floppy_stop() failed", s); exit(0); }}/*===========================================================================* * seek * *===========================================================================*/PRIVATE int seek(){/* Issue a SEEK command on the indicated drive unless the arm is already * positioned on the correct cylinder. */ struct floppy *fp = f_fp; int r; message mess; u8_t cmd[3]; /* Are we already on the correct cylinder? */ if (fp->fl_calibration == UNCALIBRATED) if (recalibrate() != OK) return(ERR_SEEK); if (fp->fl_curcyl == fp->fl_hardcyl) return(OK); /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */ cmd[0] = FDC_SEEK; cmd[1] = (fp->fl_head << 2) | f_drive; cmd[2] = fp->fl_hardcyl; if (fdc_command(cmd, 3) != OK) return(ERR_SEEK); if (f_intr_wait() != OK) return(ERR_TIMEOUT); /* Interrupt has been received. Check drive status. */ fdc_out(FDC_SENSE); /* probe FDC to make it return status */ r = fdc_results(); /* get controller status bytes */ if (r != OK || (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0 || f_results[ST1] != fp->fl_hardcyl) { /* seek failed, may need a recalibrate */ return(ERR_SEEK); } /* Give head time to settle on a format, no retrying here! */ if (f_device & FORMAT_DEV_BIT) { /* Set a synchronous alarm to force a timeout if the hardware does * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout. */ f_set_timer(&f_tmr_timeout, HZ/30, f_timeout); f_busy = BSY_IO; do { receive(ANY, &mess); if (mess.m_type == SYN_ALARM) { f_expire_tmrs(NULL, NULL); } else if(mess.m_type == DEV_PING) { notify(mess.m_source); } else { f_busy = BSY_IDLE; } } while (f_busy == BSY_IO); } fp->fl_curcyl = fp->fl_hardcyl; fp->fl_sector = NO_SECTOR; return(OK);}/*===========================================================================* * fdc_transfer * *===========================================================================*/PRIVATE int fdc_transfer(opcode)int opcode; /* DEV_GATHER or DEV_SCATTER */{/* The drive is now on the proper cylinder. Read, write or format 1 block. */ struct floppy *fp = f_fp; int r, s; u8_t cmd[9]; /* Never attempt a transfer if the drive is uncalibrated or motor is off. */ if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER); if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER); /* The command is issued by outputting several bytes to the controller chip. */ if (f_device & FORMAT_DEV_BIT) { cmd[0] = FDC_FORMAT; cmd[1] = (fp->fl_head << 2) | f_drive; cmd[2] = fmt_param.sector_size_code; cmd[3] = fmt_param.sectors_per_cylinder; cmd[4] = fmt_param.gap_length_for_format; cmd[5] = fmt_param.fill_byte_for_format; if (fdc_command(cmd, 6) != OK) return(ERR_TRANSFER); } else { cmd[0] = opcode == DEV_SCATTER ? FDC_WRITE : FDC_READ; cmd[1] = (fp->fl_head << 2) | f_drive; cmd[2] = fp->fl_cylinder; cmd[3] = fp->fl_head; cmd[4] = BASE_SECTOR + fp->fl_sector; cmd[5] = SECTOR_SIZE_CODE; cmd[6] = f_sectors; cmd[7] = f_dp->gap; /* sector gap */ cmd[8] = DTL; /* data length */ if (fdc_command(cmd, 9) != OK) return(ERR_TRANSFER); } /* Block, waiting for disk interrupt. */ if (f_intr_wait() != OK) { printf("%s: disk interrupt timed out.\n", f_name()); return(ERR_TIMEOUT); } /* Get controller status and check for errors. */ r = fdc_results(); if (r != OK) return(r); if (f_results[ST1] & WRITE_PROTECT) { printf("%s: diskette is write protected.\n", f_name()); return(ERR_WR_PROTECT); } if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_TRANSFER); if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER); if (f_device & FORMAT_DEV_BIT) return(OK); /* Compare actual numbers of sectors transferred with expected number. */ s = (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors; s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors; s += (f_results[ST_SEC] - BASE_SECTOR - fp->fl_sector); if (s != 1) return(ERR_TRANSFER); /* This sector is next for I/O: */ fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR;#if 0 if (processor < 386) fp->fl_sector++; /* Old CPU can't keep up. */#endif return(OK);}/*===========================================================================* * fdc_results * *===========================================================================*/PRIVATE int fdc_results(){/* Extract results from the controller after an operation, then allow floppy * interrupts again. */ int s, result_nr, status; clock_t t0,t1; /* Extract bytes from FDC until it says it has no more. The loop is * really an outer loop on result_nr and an inner loop on status. * A timeout flag alarm is set. */ result_nr = 0; getuptime(&t0); do { /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION * bit must be set instead of clear, but the CTL_BUSY bit destroys * the perfection of the mirror. */ if ((s=sys_inb(FDC_STATUS, &status)) != OK) panic("FLOPPY","Sys_inb in fdc_results() failed", s); status &= (MASTER | DIRECTION | CTL_BUSY); if (status == (MASTER | DIRECTION | CTL_BUSY)) { if (result_nr >= MAX_RESULTS) break; /* too many results */ if ((s=sys_inb(FDC_DATA, &f_results[result_nr])) != OK) panic("FLOPPY","Sys_inb in fdc_results() failed", s); result_nr ++; continue; } if (status == MASTER) { /* all read */ if ((s=sys_irqenable(&irq_hook_id)) != OK) panic("FLOPPY", "Couldn't enable IRQs", s); return(OK); /* only good exit */ } } while ( (s=getuptime(&t1))==OK && (t1-t0) < TIMEOUT_TICKS ); if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); need_reset = TRUE; /* controller chip must be reset */ if ((s=sys_irqenable(&irq_hook_id)) != OK) panic("FLOPPY", "Couldn't enable IRQs", s); return(ERR_STATUS);}/*===========================================================================* * fdc_command * *===========================================================================*/PRIVATE int fdc_command(cmd, len)u8_t *cmd; /* command bytes */int len; /* command length */{/* Output a command to the controller. */ /* Set a synchronous alarm to force a timeout if the hardware does * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout. * Note that the actual check is done by the code that issued the * fdc_command() call. */ f_set_timer(&f_tmr_timeout, WAKEUP, f_timeout); f_busy = BSY_IO; while (len > 0) { fdc_out(*cmd++); len--; } return(need_reset ? ERR_DRIVE : OK);}/*===========================================================================* * fdc_out * *===========================================================================*/PRIVATE void fdc_out(val)int val; /* write this byte to floppy disk controller */{/* Output a byte to the controller. This is not entirely trivial, since you * can only write to it when it is listening, and it decides when to listen.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -