📄 floppy.c
字号:
clear_dma_ff(FLOPPY_DMA);
set_dma_mode(FLOPPY_DMA, (command == FD_READ)? DMA_MODE_READ : DMA_MODE_WRITE);
set_dma_addr(FLOPPY_DMA, addr);
set_dma_count(FLOPPY_DMA, count);
enable_dma(FLOPPY_DMA);
sti();
}
static void output_byte(char byte)
{
int counter;
unsigned char status;
if (reset)
return;
for(counter = 0 ; counter < 10000 ; counter++) {
status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
if (status == STATUS_READY) {
outb(byte,FD_DATA);
return;
}
}
current_track = NO_TRACK;
reset = 1;
printk("Unable to send byte to FDC\n");
}
static int result(void)
{
int i = 0, counter, status;
if (reset)
return -1;
for (counter = 0 ; counter < 10000 ; counter++) {
status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
if (status == STATUS_READY) {
return i;
}
if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
if (i >= MAX_REPLIES) {
printk("floppy_stat reply overrun\n");
break;
}
reply_buffer[i++] = inb_p(FD_DATA);
}
}
reset = 1;
current_track = NO_TRACK;
printk("Getstatus times out\n");
return -1;
}
static void bad_flp_intr(void)
{
int errors;
current_track = NO_TRACK;
if (format_status == FORMAT_BUSY)
errors = ++format_errors;
else if (!CURRENT) {
printk(DEVICE_NAME ": no current request\n");
reset = recalibrate = 1;
return;
} else
errors = ++CURRENT->errors;
if (errors > MAX_ERRORS) {
request_done(0);
}
if (errors > MAX_ERRORS/2)
reset = 1;
else
recalibrate = 1;
}
/* Set perpendicular mode as required, based on data rate, if supported.
* 82077 Untested! 1Mbps data rate only possible with 82077-1.
* TODO: increase MAX_BUFFER_SECTORS, add floppy_type entries.
*/
static inline void perpendicular_mode(unsigned char rate)
{
if (fdc_version == FDC_TYPE_82077) {
output_byte(FD_PERPENDICULAR);
if (rate & 0x40) {
unsigned char r = rate & 0x03;
if (r == 0)
output_byte(2); /* perpendicular, 500 kbps */
else if (r == 3)
output_byte(3); /* perpendicular, 1Mbps */
else {
printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n");
reset = 1;
}
} else
output_byte(0); /* conventional mode */
} else {
if (rate & 0x40) {
printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n");
reset = 1;
}
}
} /* perpendicular_mode */
/*
* This has only been tested for the case fdc_version == FDC_TYPE_STD.
* In case you have a 82077 and want to test it, you'll have to compile
* with `FDC_FIFO_UNTESTED' defined. You may also want to add support for
* recognizing drives with vertical recording support.
*/
static void configure_fdc_mode(void)
{
if (need_configure && (fdc_version == FDC_TYPE_82077)) {
/* Enhanced version with FIFO & vertical recording. */
output_byte(FD_CONFIGURE);
output_byte(0);
output_byte(0x1A); /* FIFO on, polling off, 10 byte threshold */
output_byte(0); /* precompensation from track 0 upwards */
need_configure = 0;
printk(DEVICE_NAME ": FIFO enabled\n");
}
if (cur_spec1 != floppy->spec1) {
cur_spec1 = floppy->spec1;
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
}
if (cur_rate != floppy->rate) {
/* use bit 6 of floppy->rate to indicate perpendicular mode */
perpendicular_mode(floppy->rate);
outb_p((cur_rate = (floppy->rate)) & ~0x40, FD_DCR);
}
} /* configure_fdc_mode */
static void tell_sector(int nr)
{
if (nr!=7) {
printk(" -- FDC reply errror");
reset = 1;
} else
printk(": track %d, head %d, sector %d", reply_buffer[3],
reply_buffer[4], reply_buffer[5]);
} /* tell_sector */
/*
* Ok, this interrupt 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.
*/
static void rw_interrupt(void)
{
char * buffer_area;
int nr;
char bad;
nr = result();
/* check IC to find cause of interrupt */
switch ((ST0 & ST0_INTR)>>6) {
case 1: /* error occured during command execution */
bad = 1;
if (ST1 & ST1_WP) {
printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive);
request_done(0);
bad = 0;
} else if (ST1 & ST1_OR) {
if (ftd_msg[ST0 & ST0_DS])
printk(DEVICE_NAME ": Over/Underrun - retrying\n");
/* could continue from where we stopped, but ... */
bad = 0;
} else if (CURRENT_ERRORS > min_report_error_cnt[ST0 & ST0_DS]) {
printk(DEVICE_NAME " %d: ", ST0 & ST0_DS);
if (ST0 & ST0_ECE) {
printk("Recalibrate failed!");
} else if (ST2 & ST2_CRC) {
printk("data CRC error");
tell_sector(nr);
} else if (ST1 & ST1_CRC) {
printk("CRC error");
tell_sector(nr);
} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
if (!probing) {
printk("sector not found");
tell_sector(nr);
} 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..3] are: 0x%x 0x%x 0x%x 0x%x\n", ST0, ST1, ST2, ST3);
}
printk("\n");
}
if (bad)
bad_flp_intr();
redo_fd_request();
return;
case 2: /* invalid command given */
printk(DEVICE_NAME ": Invalid FDC command given!\n");
request_done(0);
return;
case 3:
printk(DEVICE_NAME ": Abnormal termination caused by polling\n");
bad_flp_intr();
redo_fd_request();
return;
default: /* (0) Normal command termination */
break;
}
if (probing) {
int drive = MINOR(CURRENT->dev);
if (ftd_msg[drive])
printk("Auto-detected floppy type %s in fd%d\n",
floppy->name,drive);
current_type[drive] = floppy;
floppy_sizes[drive] = floppy->size >> 1;
probing = 0;
}
if (read_track) {
buffer_track = seek_track;
buffer_drive = current_drive;
buffer_area = floppy_track_buffer +
((sector-1 + head*floppy->sect)<<9);
copy_buffer(buffer_area,CURRENT->buffer);
} else if (command == FD_READ &&
(unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR)
copy_buffer(tmp_floppy_area,CURRENT->buffer);
request_done(1);
redo_fd_request();
}
/*
* We try to read tracks, but if we get too many errors, we
* go back to reading just one sector at a time.
*
* This means we should be able to read a sector even if there
* are other bad sectors on this track.
*/
inline void setup_rw_floppy(void)
{
setup_DMA();
do_floppy = rw_interrupt;
output_byte(command);
if (command != FD_FORMAT) {
if (read_track) {
output_byte(current_drive);
output_byte(track);
output_byte(0);
output_byte(1);
} else {
output_byte(head<<2 | current_drive);
output_byte(track);
output_byte(head);
output_byte(sector);
}
output_byte(2); /* sector size = 512 */
output_byte(floppy->sect);
output_byte(floppy->gap);
output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
} else {
output_byte(head<<2 | current_drive);
output_byte(2);
output_byte(floppy->sect);
output_byte(floppy->fmt_gap);
output_byte(FD_FILL_BYTE);
}
if (reset)
redo_fd_request();
}
/*
* This is the routine called after every seek (or recalibrate) interrupt
* from the floppy controller. Note that the "unexpected interrupt" routine
* also does a recalibrate, but doesn't come here.
*/
static void seek_interrupt(void)
{
/* sense drive status */
output_byte(FD_SENSEI);
if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
printk(DEVICE_NAME ": seek failed\n");
recalibrate = 1;
bad_flp_intr();
redo_fd_request();
return;
}
current_track = ST1;
setup_rw_floppy();
}
/*
* This routine is called when everything should be correctly set up
* for the transfer (ie floppy motor is on and the correct floppy is
* selected).
*/
static void transfer(void)
{
read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) &&
(floppy->sect <= MAX_BUFFER_SECTORS);
configure_fdc_mode();
if (reset) {
redo_fd_request();
return;
}
if (!seek) {
setup_rw_floppy();
return;
}
do_floppy = seek_interrupt;
output_byte(FD_SEEK);
if (read_track)
output_byte(current_drive);
else
output_byte((head<<2) | current_drive);
output_byte(seek_track);
if (reset)
redo_fd_request();
}
/*
* Special case - used after a unexpected interrupt (or reset)
*/
static void recalibrate_floppy(void);
static void recal_interrupt(void)
{
output_byte(FD_SENSEI);
current_track = NO_TRACK;
if (result()!=2 || (ST0 & 0xE0) == 0x60)
reset = 1;
/* Recalibrate until track 0 is reached. Might help on some errors. */
if ((ST0 & 0x10) == 0x10)
recalibrate_floppy(); /* FIXME: should limit nr of recalibrates */
else
redo_fd_request();
}
static void unexpected_floppy_interrupt(void)
{
current_track = NO_TRACK;
output_byte(FD_SENSEI);
printk(DEVICE_NAME ": unexpected interrupt\n");
if (result()!=2 || (ST0 & 0xE0) == 0x60)
reset = 1;
else
recalibrate = 1;
}
static void recalibrate_floppy(void)
{
recalibrate = 0;
current_track = 0;
do_floppy = recal_interrupt;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
if (reset)
redo_fd_request();
}
/*
* Must do 4 FD_SENSEIs after reset because of ``drive polling''.
*/
static void reset_interrupt(void)
{
short i;
for (i=0; i<4; i++) {
output_byte(FD_SENSEI);
(void) result();
}
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
configure_fdc_mode(); /* reprogram fdc */
if (initial_reset_flag) {
initial_reset_flag = 0;
recalibrate = 1;
reset = 0;
return;
}
if (!recover)
redo_fd_request();
else {
recalibrate_floppy();
recover = 0;
}
}
/*
* reset is done by pulling bit 2 of DOR low for a while.
*/
static void reset_floppy(void)
{
int i;
do_floppy = reset_interrupt;
reset = 0;
current_track = NO_TRACK;
cur_spec1 = -1;
cur_rate = -1;
recalibrate = 1;
need_configure = 1;
if (!initial_reset_flag)
printk("Reset-floppy called\n");
cli();
outb_p(current_DOR & ~0x04, FD_DOR);
for (i=0 ; i<1000 ; i++)
__asm__("nop");
outb(current_DOR, FD_DOR);
sti();
}
static void floppy_shutdown(void)
{
cli();
do_floppy = NULL;
request_done(0);
recover = 1;
reset_floppy();
sti();
redo_fd_request();
}
static void shake_done(void)
{
current_track = NO_TRACK;
if (inb(FD_DIR) & 0x80)
request_done(0);
redo_fd_request();
}
static int retry_recal(void (*proc)(void))
{
output_byte(FD_SENSEI);
if (result() == 2 && (ST0 & 0x10) != 0x10) return 0;
do_floppy = proc;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
return 1;
}
static void shake_zero(void)
{
if (!retry_recal(shake_zero)) shake_done();
}
static void shake_one(void)
{
if (retry_recal(shake_one)) return;
do_floppy = shake_done;
output_byte(FD_SEEK);
output_byte(head << 2 | current_drive);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -