📄 pc_floppy.c
字号:
hut = (head_unload_time*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR; if (hut < 0x1) hut = 0x1; else if (hut > 0xf) hut = hut_max_code; cmd[0] = FD_SPECIFY; cmd[1] = (srt << 4) | hut; cmd[2] = (hlt << 1) | 1; /* Always disable DMA */ /* If these parameters did not change, just return with success */ if (!fdc_state.in_sync || fdc_state.spec1 != cmd[1] || fdc_state.spec2 != cmd[2]) { /* Go ahead and set spec1 and spec2 */ output_command(cmd, 3); /* FIXME how do I handle errors here... */#ifdef MDEBUG printf("FD_SPECIFY(%hx, %hx)\n", cmd[1], cmd[2]);#endif }} /* fdc_specify *//* * 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){ unsigned char reply[MAX_REPLIES]; fdc_state.in_sync = 0; /* Pseudo-DMA may intercept 'reset finished' interrupt. */ /* Irrelevant for systems with true DMA (i386). */ if (fdc_state.version >= FDC_82072A) outb(0x80 | (fdc_state.dtr &3), FD_DSR); else { outb(fdc_state.dor & ~DOR_NO_RESET, FD_DOR); udelay(FD_RESET_DELAY); outb(fdc_state.dor, FD_DOR); } result(reply, MAX_REPLIES);}static void show_floppy(void){#ifdef MDEBUG printf("\n"); printf("floppy driver state\n"); printf("-------------------\n"); printf("fdc_bytes: %hx %hx xx %hx %hx %hx xx %hx\n", inb(FD_BASE + 0), inb(FD_BASE + 1), inb(FD_BASE + 3), inb(FD_BASE + 4), inb(FD_BASE + 5), inb(FD_BASE + 7)); printf("status=%x\n", inb(FD_STATUS)); printf("\n");#endif}static void floppy_recalibrate(void){ unsigned char cmd[2]; unsigned char reply[MAX_REPLIES]; int nr, success; success = 0; do {#ifdef MDEBUG printf("floppy_recalibrate\n");#endif /* Send the recalibrate command to the controller. * We don't have interrupts or anything we can poll * so we have to guess when it is done. */ cmd[0] = FD_RECALIBRATE; cmd[1] = 0; if (output_command(cmd, 2) < 0) continue; /* Sleep for the maximum time the recalibrate command * can run. */ mdelay(80*DRIVE_H1440_SRT/1000); /* Now call FD_SENSEI to end the command * and collect up the reply. */ if (output_byte(FD_SENSEI) < 0) continue; nr = result(reply, MAX_REPLIES); /* Now see if we have succeeded in our seek */ success = /* We have the right size result */ (nr == 2) && /* The command didn't terminate in error */ ((reply[0] & ST0_INTR) == ST0_INTR_OK) && /* We finished a seek */ (reply[0] & ST0_SE) && /* We are at cylinder 0 */ (reply[1] == 0); } while(!success); /* Remember we are at track 0 */ drive_state[FD_DRIVE].track = 0;}static int __floppy_seek(unsigned track){ unsigned char cmd[3]; unsigned char reply[MAX_REPLIES]; int nr, success; unsigned distance, old_track; /* Look up the old track and see if we need to * do anything. */ old_track = drive_state[FD_DRIVE].track; if (old_track == track) { return 1; } /* Compute the distance we are about to move, * We need to know this so we know how long to sleep... */ distance = (old_track > track)?(old_track - track):(track - old_track); distance += 1; /* Send the seek command to the controller. * We don't have interrupts or anything we can poll * so we have to guess when it is done. */ cmd[0] = FD_SEEK; cmd[1] = FD_DRIVE; cmd[2] = track; if (output_command(cmd, 3) < 0) return 0; /* Sleep for the time it takes to step through distance tracks. */ mdelay(distance*DRIVE_H1440_SRT/1000); /* Now call FD_SENSEI to end the command * and collect up the reply. */ cmd[0] = FD_SENSEI; if (output_command(cmd, 1) < 0) return 0; nr = result(reply, MAX_REPLIES); /* Now see if we have succeeded in our seek */ success = /* We have the right size result */ (nr == 2) && /* The command didn't terminate in error */ ((reply[0] & ST0_INTR) == ST0_INTR_OK) && /* We finished a seek */ (reply[0] & ST0_SE) && /* We are at cylinder 0 */ (reply[1] == track); if (success) drive_state[FD_DRIVE].track = track; else {#ifdef MDEBUG printf("seek failed\n"); printf("nr = %d\n", nr); printf("ST0 = %hx\n", reply[0]); printf("PCN = %hx\n", reply[1]); printf("status = %d\n", inb(FD_STATUS));#endif } return success;}static int floppy_seek(unsigned track){ unsigned old_track; int result; /* assume success */ result = 1; /* Look up the old track and see if we need to * do anything. */ old_track = drive_state[FD_DRIVE].track; if (old_track == track) { return result; } /* For some reason seeking many tracks at once is * problematic so only seek a single track at a time. */ while(result && (old_track > track)) { old_track--; result = __floppy_seek(old_track); } while(result && (track > old_track)) { old_track++; result = __floppy_seek(old_track); } return result;}static int read_ok(unsigned head){ unsigned char results[7]; int result_ok; int nr; /* read back the read results */ nr = result(results, 7); /* Now see if they say we are o.k. */ result_ok = 0; /* Are my result bytes o.k.? */ if (nr == 7) { /* Are we o.k. */ if ((results[0] & ST0_INTR) == ST0_INTR_OK) { result_ok = 1; } /* Or did we get just an overflow error */ else if (((results[0] & ST0_INTR) == ST0_INTR_ERROR) && (results[1]== ST1_OR) && (results[2] == 0)) { result_ok = 1; } /* Verify the reply had the correct head */ if (((results[0] & ST0_HA) >> 2) != head) { result_ok = 0; } /* Verify the reply had the correct drive */ if (((results[0] & ST0_DS) != FD_DRIVE)) { result_ok = 0; } } if (!result_ok) {#ifdef MDEBUG printf("result_bytes = %d\n", nr); printf("ST0 = %hx\n", results[0]); printf("ST1 = %hx\n", results[1]); printf("ST2 = %hx\n", results[2]); printf(" C = %hx\n", results[3]); printf(" H = %hx\n", results[4]); printf(" R = %hx\n", results[5]); printf(" N = %hx\n", results[6]);#endif } return result_ok;}static int floppy_read_sectors( char *dest, unsigned byte_offset, unsigned length, unsigned sector, unsigned head, unsigned track){ /* MT == Multitrack */ /* MFM == MFM or FM Mode */ /* SK == Skip deleted data addres Mark */ /* HDS == Head number select */ /* DS0 == Disk Drive Select 0 */ /* DS1 == Disk Drive Select 1 */ /* C == Cylinder number 0 - 255 */ /* H == Head number */ /* R == Record */ /* N == The number of data bytes written in a sector */ /* EOT == End of Track */ /* GPL == Gap Length */ /* DTL == Data Length */ /* MT MFM SK 0 1 1 0 0 */ /* 0 0 0 0 0 HDS DS1 DS0 */ /* C, H, R, N, EOT, GPL, DTL */ int i, status, result_ok; int max_bytes, bytes_read; int ret; unsigned char cmd[9]; unsigned end_offset; end_offset = byte_offset + length; max_bytes = 512*(DISK_H1440_SECT - sector + 1); if (byte_offset >= max_bytes) { return 0; } cmd[0] = FD_READ | (((DISK_H1440_HEAD ==2)?1:0) << 6); cmd[1] = (head << 2) | FD_DRIVE; cmd[2] = track; cmd[3] = head; cmd[4] = sector; cmd[5] = 2; /* 2^N *128 == Sector size. Hard coded to 512 bytes */ cmd[6] = DISK_H1440_SECT; cmd[7] = DISK_H1440_GAP; cmd[8] = 0xff; /* Output the command bytes */ if (output_command(cmd, 9) < 0) return -1; /* The execution stage begins when STATUS_READY&STATUS_NON_DMA is set */ do {#if 1 poll_interruptions();#endif status = inb(FD_STATUS); status &= STATUS_READY | STATUS_NON_DMA; } while(status != (STATUS_READY|STATUS_NON_DMA)); for(i = 0; i < max_bytes; i++) { unsigned char byte; if ((status = wait_til_ready()) < 0) { break; } status &= STATUS_READY|STATUS_DIR|STATUS_NON_DMA; if (status != (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) { break; } byte = inb(FD_DATA); if ((i >= byte_offset) && (i < end_offset)) { dest[i - byte_offset] = byte; } } bytes_read = i; /* The result stage begins when STATUS_NON_DMA is cleared */ while((status = inb(FD_STATUS)) & STATUS_NON_DMA) { /* We get extra bytes in the fifo past * the end of the sector and drop them on the floor. * Otherwise the fifo is polluted. */ inb(FD_DATA); } /* Did I get an error? */ result_ok = read_ok(head); /* Did I read enough bytes? */ ret = -1; if (result_ok && (bytes_read == max_bytes)) { ret = bytes_read - byte_offset; if (ret > length) { ret = length; } } if (ret < 0) {#ifdef MDEBUG printf("ret = %d\n", ret); printf("bytes_read = %d\n", bytes_read); printf("status = %x\n", status);#endif } return ret;}static int floppy_read(struct disk *disk, sector_t base_sector){ unsigned head, track, sector, byte_offset; unsigned long block; int ret; disk->sector = 0; disk->bytes = 0; block = base_sector; block /= disk->sectors_per_read; /* break the offset up into sectors and bytes */ byte_offset = 0; /* Find the disk block we are starting with... */ sector = 1; head = block % DISK_H1440_HEAD; track = (block / DISK_H1440_HEAD)% DISK_H1440_TRACK; /* First seek to our start track */ if (!floppy_seek(track)) { return -1; } /* Then read the data */ ret = floppy_read_sectors( disk->buffer, byte_offset, SECTOR_SIZE*disk->sectors_per_read, sector, head, track); if (ret >= 0) { disk->sector = block * disk->sectors_per_read; disk->bytes = SECTOR_SIZE * disk->sectors_per_read; return ret; } /* If we failed reset the fdc... */ floppy_reset(); return -1;}/* Determine the floppy disk controller type *//* This routine was written by David C. Niemi */static char get_fdc_version(void){ int bytes, ret; unsigned char reply_buffer[MAX_REPLIES]; ret = output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ if (ret < 0) return FDC_NONE; if ((bytes = result(reply_buffer, MAX_REPLIES)) <= 0x00) return FDC_NONE; /* No FDC present ??? */ if ((bytes==1) && (reply_buffer[0] == 0x80)){ printf("FDC %d is an 8272A\n"); return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ } if (bytes != 10) {#ifdef MDEBUG printf("init: DUMPREGS: unexpected return of %d bytes.\n", bytes);#endif return FDC_UNKNOWN; } if (!fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, TRACK_PRECOMPENSATION)) { printf("FDC is an 82072\n"); return FDC_82072; /* 82072 doesn't know CONFIGURE */ } output_byte(FD_PERPENDICULAR); if (need_more_output() == MORE_OUTPUT) { output_byte(0); } else { printf("FDC is an 82072A\n"); return FDC_82072A; /* 82072A as found on Sparcs. */ } output_byte(FD_UNLOCK); bytes = result(reply_buffer, MAX_REPLIES); if ((bytes == 1) && (reply_buffer[0] == 0x80)){ printf("FDC is a pre-1991 82077\n"); return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know * LOCK/UNLOCK */ } if ((bytes != 1) || (reply_buffer[0] != 0x00)) {#ifdef MDEBUG printf("FDC init: UNLOCK: unexpected return of %d bytes.\n", bytes);#endif return FDC_UNKNOWN; } output_byte(FD_PARTID); bytes = result(reply_buffer, MAX_REPLIES); if (bytes != 1) {#ifdef MDEBUG printf("FDC init: PARTID: unexpected return of %d bytes.\n", bytes);#endif return FDC_UNKNOWN; } if (reply_buffer[0] == 0x80) { printf("FDC is a post-1991 82077\n"); return FDC_82077; /* Revised 82077AA passes all the tests */ } switch (reply_buffer[0] >> 5) { case 0x0: /* Either a 82078-1 or a 82078SL running at 5Volt */ printf("FDC is an 82078.\n"); return FDC_82078; case 0x1: printf("FDC is a 44pin 82078\n"); return FDC_82078; case 0x2: printf("FDC is a S82078B\n"); return FDC_S82078B; case 0x3: printf("FDC is a National Semiconductor PC87306\n"); return FDC_87306; default: printf("FDC init: 82078 variant with unknown PARTID=%d.\n", reply_buffer[0] >> 5); return FDC_82078_UNKN; }} /* get_fdc_version */static int floppy_init(void){#ifdef MDEBUG printf("floppy_init\n");#endif fdc_state.in_sync = 0; fdc_state.spec1 = -1; fdc_state.spec2 = -1; fdc_state.dtr = -1; fdc_state.dor = DOR_NO_RESET; fdc_state.version = FDC_UNKNOWN; reset_fdc(); /* Try to determine the floppy controller type */ fdc_state.version = get_fdc_version(); if (fdc_state.version == FDC_NONE) { return -1; } floppy_reset();#ifdef MDEBUG printf("fdc_state.version = %x\n", fdc_state.version);#endif return 0;}static void floppy_reset(void){#ifdef MDEBUG printf("floppy_reset\n");#endif floppy_motor_off(FD_DRIVE); reset_fdc(); fdc_dtr(DISK_H1440_RATE); /* program data rate via ccr */ collect_interrupt(); fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, TRACK_PRECOMPENSATION); fdc_specify(DRIVE_H1440_HLT, DRIVE_H1440_HUT, DRIVE_H1440_SRT); set_drive(FD_DRIVE); floppy_recalibrate(); fdc_state.in_sync = 1;}static void floppy_fini(struct dev *dev __unused){ /* Disable the floppy and the floppy drive controller */ set_dor(0, 0);}static int floppy_probe(struct dev *dev, unsigned short *probe_addrs){ struct disk *disk = (struct disk *)dev; unsigned short addr; int index; if (!probe_addrs || !*probe_addrs) return 0; index = dev->index +1; if (dev->how_probe == PROBE_AWAKE) { index--; } for(; (index >= 0) && (addr = probe_addrs[index]); index++) { /* FIXME handle multiple drives per controller */ /* FIXME test to see if I have a drive or a disk in * the driver during the probe routine. */ /* FIXME make this work under the normal bios */ FD_BASE = addr; if (floppy_init() != 0) { /* nothing at this address */ continue; } /* O.k. I have a floppy controller */ disk->hw_sector_size = SECTOR_SIZE; disk->sectors_per_read = DISK_H1440_SECT; disk->sectors = DISK_H1440_HEAD*DISK_H1440_TRACK*DISK_H1440_SECT; dev->index = index; dev->disable = floppy_fini; disk->read = floppy_read; return 1; } dev->index = -1; return 0;}static unsigned short floppy_ioaddrs[] ={ 0x3F0, 0x370, 0};ISA_ROM("pc_floppy", "Generic PC Floppy support")static struct isa_driver floppy_isa_driver __isa_driver = { .type = FLOPPY_DRIVER, .name = "PC flopyy", .probe = floppy_probe, .ioaddrs = floppy_ioaddrs,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -