📄 floppy.c
字号:
}/* * ======================================================================== * here ends the bottom half. Exported routines are: * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc, * start_motor, reset_fdc, reset_fdc_info, interpret_errors. * Initialization also uses output_byte, result, set_dor, floppy_interrupt * and set_dor. * ======================================================================== *//* * General purpose continuations. * ============================== */static void do_wakeup(void){ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0); cont = 0; command_status += 2; wake_up(&command_done);}static struct cont_t wakeup_cont={ empty, do_wakeup, empty, (done_f)empty};static struct cont_t intr_cont={ empty, process_fd_request, empty, (done_f) empty};static int wait_til_done(void (*handler)(void), int interruptible){ int ret; schedule_bh((void *)(void *)handler); if (command_status < 2 && NO_SIGNAL) { DECLARE_WAITQUEUE(wait, current); add_wait_queue(&command_done, &wait); for (;;) { set_current_state(interruptible? TASK_INTERRUPTIBLE: TASK_UNINTERRUPTIBLE); if (command_status >= 2 || !NO_SIGNAL) break; is_alive("wait_til_done"); schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&command_done, &wait); } if (command_status < 2){ cancel_activity(); cont = &intr_cont; reset_fdc(); return -EINTR; } if (FDCS->reset) command_status = FD_COMMAND_ERROR; if (command_status == FD_COMMAND_OKAY) ret=0; else ret=-EIO; command_status = FD_COMMAND_NONE; return ret;}static void generic_done(int result){ command_status = result; cont = &wakeup_cont;}static void generic_success(void){ cont->done(1);}static void generic_failure(void){ cont->done(0);}static void success_and_wakeup(void){ generic_success(); cont->redo();}/* * formatting and rw support. * ========================== */static int next_valid_format(void){ int probed_format; probed_format = DRS->probed_format; while(1){ if (probed_format >= 8 || !DP->autodetect[probed_format]){ DRS->probed_format = 0; return 1; } if (floppy_type[DP->autodetect[probed_format]].sect){ DRS->probed_format = probed_format; return 0; } probed_format++; }}static void bad_flp_intr(void){ if (probing){ DRS->probed_format++; if (!next_valid_format()) return; } (*errors)++; INFBOUND(DRWE->badness, *errors); if (*errors > DP->max_errors.abort) cont->done(0); if (*errors > DP->max_errors.reset) FDCS->reset = 1; else if (*errors > DP->max_errors.recal) DRS->track = NEED_2_RECAL;}static void set_floppy(kdev_t device){ if (TYPE(device)) _floppy = TYPE(device) + floppy_type; else _floppy = current_type[ DRIVE(device) ];}/* * formatting support. * =================== */static void format_interrupt(void){ switch (interpret_errors()){ case 1: cont->error(); case 2: break; case 0: cont->done(1); } cont->redo();}#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))#define CT(x) ((x) | 0xc0)static void setup_format_params(int track){ struct fparm { unsigned char track,head,sect,size; } *here = (struct fparm *)floppy_track_buffer; int il,n; int count,head_shift,track_shift; raw_cmd = &default_raw_cmd; raw_cmd->track = track; raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK; raw_cmd->rate = _floppy->rate & 0x43; raw_cmd->cmd_count = NR_F; COMMAND = FM_MODE(_floppy,FD_FORMAT); DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,format_req.head); F_SIZECODE = FD_SIZECODE(_floppy); F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE; F_GAP = _floppy->fmt_gap; F_FILL = FD_FILL_BYTE; raw_cmd->kernel_data = floppy_track_buffer; raw_cmd->length = 4 * F_SECT_PER_TRACK; /* allow for about 30ms for data transport per track */ head_shift = (F_SECT_PER_TRACK + 5) / 6; /* a ``cylinder'' is two tracks plus a little stepping time */ track_shift = 2 * head_shift + 3; /* position of logical sector 1 on this track */ n = (track_shift * format_req.track + head_shift * format_req.head) % F_SECT_PER_TRACK; /* determine interleave */ il = 1; if (_floppy->fmt_gap < 0x22) il++; /* initialize field */ for (count = 0; count < F_SECT_PER_TRACK; ++count) { here[count].track = format_req.track; here[count].head = format_req.head; here[count].sect = 0; here[count].size = F_SIZECODE; } /* place logical sectors */ for (count = 1; count <= F_SECT_PER_TRACK; ++count) { here[n].sect = count; n = (n+il) % F_SECT_PER_TRACK; if (here[n].sect) { /* sector busy, find next free sector */ ++n; if (n>= F_SECT_PER_TRACK) { n-=F_SECT_PER_TRACK; while (here[n].sect) ++n; } } }}static void redo_format(void){ buffer_track = -1; setup_format_params(format_req.track << STRETCH(_floppy)); floppy_start();#ifdef DEBUGT debugt("queue format request");#endif}static struct cont_t format_cont={ format_interrupt, redo_format, bad_flp_intr, generic_done };static int do_format(kdev_t device, struct format_descr *tmp_format_req){ int ret; int drive=DRIVE(device); LOCK_FDC(drive,1); set_floppy(device); if (!_floppy || _floppy->track > DP->tracks || tmp_format_req->track >= _floppy->track || tmp_format_req->head >= _floppy->head || (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) || !_floppy->fmt_gap) { process_fd_request(); return -EINVAL; } format_req = *tmp_format_req; format_errors = 0; cont = &format_cont; errors = &format_errors; IWAIT(redo_format); process_fd_request(); return ret;}/* * Buffer read/write and support * ============================= *//* new request_done. Can handle physical sectors which are smaller than a * logical buffer */static void request_done(int uptodate){ int block; unsigned long flags; probing = 0; reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate); if (QUEUE_EMPTY){ DPRINT("request list destroyed in floppy request done\n"); return; } if (uptodate){ /* maintain values for invalidation on geometry * change */ block = current_count_sectors + CURRENT->sector; INFBOUND(DRS->maxblock, block); if (block > _floppy->sect) DRS->maxtrack = 1; /* unlock chained buffers */ spin_lock_irqsave(&io_request_lock, flags); while (current_count_sectors && !QUEUE_EMPTY && current_count_sectors >= CURRENT->current_nr_sectors){ current_count_sectors -= CURRENT->current_nr_sectors; CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; end_request(1); } spin_unlock_irqrestore(&io_request_lock, flags); if (current_count_sectors && !QUEUE_EMPTY){ /* "unlock" last subsector */ CURRENT->buffer += current_count_sectors <<9; CURRENT->current_nr_sectors -= current_count_sectors; CURRENT->nr_sectors -= current_count_sectors; CURRENT->sector += current_count_sectors; return; } if (current_count_sectors && QUEUE_EMPTY) DPRINT("request list destroyed in floppy request done\n"); } else { if (CURRENT->cmd == WRITE) { /* record write error information */ DRWE->write_errors++; if (DRWE->write_errors == 1) { DRWE->first_error_sector = CURRENT->sector; DRWE->first_error_generation = DRS->generation; } DRWE->last_error_sector = CURRENT->sector; DRWE->last_error_generation = DRS->generation; } spin_lock_irqsave(&io_request_lock, flags); end_request(0); spin_unlock_irqrestore(&io_request_lock, flags); }}/* Interrupt handler evaluating the result of the r/w operation */static void rw_interrupt(void){ int nr_sectors, ssize, eoc, heads; if (R_HEAD >= 2) { /* some Toshiba floppy controllers occasionnally seem to * return bogus interrupts after read/write operations, which * can be recognized by a bad head number (>= 2) */ return; } if (!DRS->first_read_date) DRS->first_read_date = jiffies; nr_sectors = 0; CODE2SIZE; if (ST1 & ST1_EOC) eoc = 1; else eoc = 0; if (COMMAND & 0x80) heads = 2; else heads = 1; nr_sectors = (((R_TRACK-TRACK) * heads + R_HEAD-HEAD) * SECT_PER_TRACK + R_SECTOR-SECTOR + eoc) << SIZECODE >> 2;#ifdef FLOPPY_SANITY_CHECK if (nr_sectors / ssize > (in_sector_offset + current_count_sectors + ssize - 1) / ssize) { DPRINT("long rw: %x instead of %lx\n", nr_sectors, current_count_sectors); printk("rs=%d s=%d\n", R_SECTOR, SECTOR); printk("rh=%d h=%d\n", R_HEAD, HEAD); printk("rt=%d t=%d\n", R_TRACK, TRACK); printk("heads=%d eoc=%d\n", heads, eoc); printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, sector_t, ssize); printk("in_sector_offset=%d\n", in_sector_offset); }#endif nr_sectors -= in_sector_offset; INFBOUND(nr_sectors,0); SUPBOUND(current_count_sectors, nr_sectors); switch (interpret_errors()){ case 2: cont->redo(); return; case 1: if (!current_count_sectors){ cont->error(); cont->redo(); return; } break; case 0: if (!current_count_sectors){ cont->redo(); return; } current_type[current_drive] = _floppy; floppy_sizes[TOMINOR(current_drive) ]= (_floppy->size+1)>>1; break; } if (probing) { if (DP->flags & FTD_MSG) DPRINT("Auto-detected floppy type %s in fd%d\n", _floppy->name,current_drive); current_type[current_drive] = _floppy; floppy_sizes[TOMINOR(current_drive)] = (_floppy->size+1) >> 1; probing = 0; } if (CT(COMMAND) != FD_READ || raw_cmd->kernel_data == CURRENT->buffer){ /* transfer directly from buffer */ cont->done(1); } else if (CT(COMMAND) == FD_READ){ buffer_track = raw_cmd->track; buffer_drive = current_drive; INFBOUND(buffer_max, nr_sectors + sector_t); } cont->redo();}/* Compute maximal contiguous buffer size. */static int buffer_chain_size(void){ struct buffer_head *bh; int size; char *base; base = CURRENT->buffer; size = CURRENT->current_nr_sectors << 9; bh = CURRENT->bh; if (bh){ bh = bh->b_reqnext; while (bh && bh->b_data == base + size){ size += bh->b_size; bh = bh->b_reqnext; } } return size >> 9;}/* Compute the maximal transfer size */static int transfer_size(int ssize, int max_sector, int max_size){ SUPBOUND(max_sector, sector_t + max_size); /* alignment */ max_sector -= (max_sector % _floppy->sect) % ssize; /* transfer size, beginning not aligned */ current_count_sectors = max_sector - sector_t ; return max_sector;}/* * Move data from/to the track buffer to/from the buffer cache. */static void copy_buffer(int ssize, int max_sector, int max_sector_2){ int remaining; /* number of transferred 512-byte sectors */ struct buffer_head *bh; char *buffer, *dma_buffer; int size; max_sector = transfer_size(ssize, minimum(max_sector, max_sector_2), CURRENT->nr_sectors); if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE && buffer_max > sector_t + CURRENT->nr_sectors) current_count_sectors = minimum(buffer_max - sector_t, CURRENT->nr_sectors); remaining = current_count_sectors << 9;#ifdef FLOPPY_SANITY_CHECK if ((remaining >> 9) > CURRENT->nr_sectors && C
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -