📄 floppy.c
字号:
* If the controller refuses to listen, the FDC chip is given a hard reset. */ clock_t t0, t1; int s, status; if (need_reset) return; /* if controller is not listening, return */ /* It may take several tries to get the FDC to accept a command. */ getuptime(&t0); do { if ( (s=getuptime(&t1))==OK && (t1-t0) > TIMEOUT_TICKS ) { if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); need_reset = TRUE; /* hit it over the head */ return; } if ((s=sys_inb(FDC_STATUS, &status)) != OK) panic("FLOPPY","Sys_inb in fdc_out() failed", s); } while ((status & (MASTER | DIRECTION)) != (MASTER | 0)); if ((s=sys_outb(FDC_DATA, val)) != OK) panic("FLOPPY","Sys_outb in fdc_out() failed", s);}/*===========================================================================* * recalibrate * *===========================================================================*/PRIVATE int recalibrate(){/* The floppy disk controller has no way of determining its absolute arm * position (cylinder). Instead, it steps the arm a cylinder at a time and * keeps track of where it thinks it is (in software). However, after a * SEEK, the hardware reads information from the diskette telling where the * arm actually is. If the arm is in the wrong place, a recalibration is done, * which forces the arm to cylinder 0. This way the controller can get back * into sync with reality. */ struct floppy *fp = f_fp; int r; u8_t cmd[2]; /* Issue the RECALIBRATE command and wait for the interrupt. */ cmd[0] = FDC_RECALIBRATE; /* tell drive to recalibrate itself */ cmd[1] = f_drive; /* specify drive */ if (fdc_command(cmd, 2) != OK) return(ERR_SEEK); if (f_intr_wait() != OK) return(ERR_TIMEOUT); /* Determine if the recalibration succeeded. */ fdc_out(FDC_SENSE); /* issue SENSE command to request results */ r = fdc_results(); /* get results of the FDC_RECALIBRATE command*/ fp->fl_curcyl = NO_CYL; /* force a SEEK next time */ fp->fl_sector = NO_SECTOR; if (r != OK || /* controller would not respond */ (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0 || f_results[ST_PCN] != 0) { /* Recalibration failed. FDC must be reset. */ need_reset = TRUE; return(ERR_RECALIBRATE); } else { /* Recalibration succeeded. */ fp->fl_calibration = CALIBRATED; fp->fl_curcyl = f_results[ST_PCN]; return(OK); }}/*===========================================================================* * f_reset * *===========================================================================*/PRIVATE void f_reset(){/* Issue a reset to the controller. This is done after any catastrophe, * like the controller refusing to respond. */ pvb_pair_t byte_out[2]; int s,i; message mess; /* Disable interrupts and strobe reset bit low. */ need_reset = FALSE; /* It is not clear why the next lock is needed. Writing 0 to DOR causes * interrupt, while the PC documentation says turning bit 8 off disables * interrupts. Without the lock: * 1) the interrupt handler sets the floppy mask bit in the 8259. * 2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt * line again, but the mask stops the cpu being interrupted. * 3) the sense interrupt clears the interrupt (not clear which one). * and for some reason the reset does not work. */ (void) fdc_command((u8_t *) 0, 0); /* need only the timer */ motor_status = 0; pv_set(byte_out[0], DOR, 0); /* strobe reset bit low */ pv_set(byte_out[1], DOR, ENABLE_INT); /* strobe it high again */ if ((s=sys_voutb(byte_out, 2)) != OK) panic("FLOPPY", "Sys_voutb in f_reset() failed", s); /* A synchronous alarm timer was set in fdc_command. Expect a HARD_INT * message to collect the reset interrupt, but be prepared to handle the * SYN_ALARM message on a timeout. */ 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 { /* expect HARD_INT */ f_busy = BSY_IDLE; } } while (f_busy == BSY_IO); /* The controller supports 4 drives and returns a result for each of them. * Collect all the results now. The old version only collected the first * result. This happens to work for 2 drives, but it doesn't work for 3 * or more drives, at least with only drives 0 and 2 actually connected * (the controller generates an extra interrupt for the middle drive when * drive 2 is accessed and the driver panics). * * It would be better to keep collecting results until there are no more. * For this, fdc_results needs to return the number of results (instead * of OK) when it succeeds. */ for (i = 0; i < 4; i++) { fdc_out(FDC_SENSE); /* probe FDC to make it return status */ (void) fdc_results(); /* flush controller */ } for (i = 0; i < NR_DRIVES; i++) /* clear each drive */ floppy[i].fl_calibration = UNCALIBRATED; /* The current timing parameters must be specified again. */ prev_dp = NULL;}/*===========================================================================* * f_intr_wait * *===========================================================================*/PRIVATE int f_intr_wait(){/* Wait for an interrupt, but not forever. The FDC may have all the time of * the world, but we humans do not. */ message mess; /* We expect a HARD_INT message from the interrupt handler, but if there is * a timeout, a SYN_ALARM notification is received instead. If a timeout * occurs, report an error. */ 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); if (f_busy == BSY_WAKEN) { /* No interrupt from the FDC, this means that there is probably no * floppy in the drive. Get the FDC down to earth and return error. */ need_reset = TRUE; return(ERR_TIMEOUT); } return(OK);}/*===========================================================================* * f_timeout * *===========================================================================*/PRIVATE void f_timeout(tp)timer_t *tp;{/* This routine is called when a timer expires. Usually to tell that a * motor has spun up, but also to forge an interrupt when it takes too long * for the FDC to interrupt (no floppy in the drive). It sets a flag to tell * what has happened. */ if (f_busy == BSY_IO) { f_busy = BSY_WAKEN; }}/*===========================================================================* * read_id * *===========================================================================*/PRIVATE int read_id(){/* Determine current cylinder and sector. */ struct floppy *fp = f_fp; int result; u8_t cmd[2]; /* Never attempt a read id if the drive is uncalibrated or motor is off. */ if (fp->fl_calibration == UNCALIBRATED) return(ERR_READ_ID); if ((motor_status & (1 << f_drive)) == 0) return(ERR_READ_ID); /* The command is issued by outputting 2 bytes to the controller chip. */ cmd[0] = FDC_READ_ID; /* issue the read id command */ cmd[1] = (fp->fl_head << 2) | f_drive; if (fdc_command(cmd, 2) != OK) return(ERR_READ_ID); if (f_intr_wait() != OK) return(ERR_TIMEOUT); /* Get controller status and check for errors. */ result = fdc_results(); if (result != OK) return(result); if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_READ_ID); if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID); /* The next sector is next for I/O: */ fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR + 1; return(OK);}/*===========================================================================* * f_do_open * *===========================================================================*/PRIVATE int f_do_open(dp, m_ptr)struct driver *dp;message *m_ptr; /* pointer to open message */{/* Handle an open on a floppy. Determine diskette type if need be. */ int dtype; struct test_order *top; /* Decode the message parameters. */ if (f_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); dtype = f_device & DEV_TYPE_BITS; /* get density from minor dev */ if (dtype >= MINOR_fd0p0) dtype = 0; if (dtype != 0) { /* All types except 0 indicate a specific drive/medium combination.*/ dtype = (dtype >> DEV_TYPE_SHIFT) - 1; if (dtype >= NT) return(ENXIO); f_fp->fl_density = dtype; (void) f_prepare(f_device); /* Recompute parameters. */ return(OK); } if (f_device & FORMAT_DEV_BIT) return(EIO); /* Can't format /dev/fdN */ /* The device opened is /dev/fdN. Experimentally determine drive/medium. * First check fl_density. If it is not NO_DENS, the drive has been used * before and the value of fl_density tells what was found last time. Try * that first. If the motor is still running then assume nothing changed. */ if (f_fp->fl_density != NO_DENS) { if (motor_status & (1 << f_drive)) return(OK); if (test_read(f_fp->fl_density) == OK) return(OK); } /* Either drive type is unknown or a different diskette is now present. * Use test_order to try them one by one. */ for (top = &test_order[0]; top < &test_order[NT-1]; top++) { dtype = top->t_density; /* Skip densities that have been proven to be impossible */ if (!(f_fp->fl_class & (1 << dtype))) continue; if (test_read(dtype) == OK) { /* The test succeeded, use this knowledge to limit the * drive class to match the density just read. */ f_fp->fl_class &= top->t_class; return(OK); } /* Test failed, wrong density or did it time out? */ if (f_busy == BSY_WAKEN) break; } f_fp->fl_density = NO_DENS; return(EIO); /* nothing worked */}/*===========================================================================* * test_read * *===========================================================================*/PRIVATE int test_read(density)int density;{/* Try to read the highest numbered sector on cylinder 2. Not all floppy * types have as many sectors per track, and trying cylinder 2 finds the * ones that need double stepping. */ int device; off_t position; iovec_t iovec1; int result; f_fp->fl_density = density; device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive; (void) f_prepare(device); position = (off_t) f_dp->test << SECTOR_SHIFT; iovec1.iov_addr = (vir_bytes) tmp_buf; iovec1.iov_size = SECTOR_SIZE; result = f_transfer(SELF, DEV_GATHER, position, &iovec1, 1); if (iovec1.iov_size != 0) return(EIO); partition(&f_dtab, f_drive, P_FLOPPY, 0); return(OK);}/*===========================================================================* * f_geometry * *===========================================================================*/PRIVATE void f_geometry(entry)struct partition *entry;{ entry->cylinders = f_dp->cyls; entry->heads = NR_HEADS; entry->sectors = f_sectors;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -