📄 floppy.c
字号:
return; if (fdc_dtr()) return;#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT("calling disk change from floppy_ready\n"); }#endif if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) && disk_change(current_drive) && !DP->select_delay) twaddle(); /* this clears the dcl on certain drive/controller * combinations */#ifdef fd_chose_dma_mode if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) { unsigned long flags = claim_dma_lock(); fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length); release_dma_lock(flags); }#endif if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) { perpendicular_mode(); fdc_specify(); /* must be done here because of hut, hlt ... */ seek_floppy(); } else { if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) fdc_specify(); setup_rw_floppy(); }}static void floppy_start(void){ reschedule_timeout(current_reqD, "floppy start", 0); scandrives();#ifdef DCL_DEBUG if (DP->flags & FD_DEBUG) { DPRINT("setting NEWCHANGE in floppy_start\n"); }#endif SETF(FD_DISK_NEWCHANGE); floppy_ready();}/* * ======================================================================== * 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 = NULL; command_status += 2; wake_up(&command_done);}static struct cont_t wakeup_cont = { .interrupt = empty, .redo = do_wakeup, .error = empty, .done = (done_f) empty};static struct cont_t intr_cont = { .interrupt = empty, .redo = process_fd_request, .error = empty, .done = (done_f) empty};static int wait_til_done(void (*handler) (void), int interruptible){ int ret; schedule_bh(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){ int err_count; if (probing) { DRS->probed_format++; if (!next_valid_format()) return; } err_count = ++(*errors); INFBOUND(DRWE->badness, err_count); if (err_count > DP->max_errors.abort) cont->done(0); if (err_count > DP->max_errors.reset) FDCS->reset = 1; else if (err_count > DP->max_errors.recal) DRS->track = NEED_2_RECAL;}static void set_floppy(int drive){ int type = ITYPE(UDRS->fd_device); if (type) _floppy = floppy_type + type; else _floppy = current_type[drive];}/* * 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; } } } if (_floppy->stretch & FD_ZEROBASED) { for (count = 0; count < F_SECT_PER_TRACK; count++) here[count].sect--; }}static void redo_format(void){ buffer_track = -1; setup_format_params(format_req.track << STRETCH(_floppy)); floppy_start(); debugt("queue format request");}static struct cont_t format_cont = { .interrupt = format_interrupt, .redo = redo_format, .error = bad_flp_intr, .done = generic_done};static int do_format(int drive, struct format_descr *tmp_format_req){ int ret; LOCK_FDC(drive, 1); set_floppy(drive); 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 * ============================= */static void floppy_end_request(struct request *req, int uptodate){ unsigned int nr_sectors = current_count_sectors; /* current_count_sectors can be zero if transfer failed */ if (!uptodate) nr_sectors = req->current_nr_sectors; if (end_that_request_first(req, uptodate, nr_sectors)) return; add_disk_randomness(req->rq_disk); floppy_off((long)req->rq_disk->private_data); blkdev_dequeue_request(req); end_that_request_last(req); /* We're done with the request */ current_req = NULL;}/* new request_done. Can handle physical sectors which are smaller than a * logical buffer */static void request_done(int uptodate){ struct request_queue *q = floppy_queue; struct request *req = current_req; unsigned long flags; int block; probing = 0; reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate); if (!req) { printk("floppy.c: no request in request_done\n"); return; } if (uptodate) { /* maintain values for invalidation on geometry * change */ block = current_count_sectors + req->sector; INFBOUND(DRS->maxblock, block); if (block > _floppy->sect) DRS->maxtrack = 1; /* unlock chained buffers */ spin_lock_irqsave(q->queue_lock, flags); floppy_end_request(req, 1); spin_unlock_irqrestore(q->queue_lock, flags); } else { if (rq_data_dir(req) == WRITE) { /* record write error information */ DRWE->write_errors++; if (DRWE->write_errors == 1) { DRWE->first_error_sector = req->sector; DRWE->first_error_generation = DRS->generation; } DRWE->last_error_sector = req->sector; DRWE->last_error_generation = DRS->generation; } spin_lock_irqsave(q->queue_lock, flags); floppy_end_request(req, 0); spin_unlock_irqrestore(q->queue_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, fsector_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; 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; probing = 0; } if (CT(COMMAND) != FD_READ || raw_cmd->kernel_data == current_req->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 + fsector_t); } cont->redo();}/* Compute max
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -