📄 ide.c
字号:
udelay(1); /* need to guarantee 400ns since last command was issued */#endif if (GET_STAT() & BUSY_STAT) return 0; /* drive busy: definitely not interrupting */ return 1; /* drive ready: *might* be interrupting */}/* * This is our end_request replacement function. */void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup){ struct request *rq; unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); rq = hwgroup->rq; if (!end_that_request_first(rq, uptodate, hwgroup->drive->name)) { add_blkdev_randomness(MAJOR(rq->rq_dev)); hwgroup->drive->queue = rq->next; blk_dev[MAJOR(rq->rq_dev)].current_request = NULL; hwgroup->rq = NULL; end_that_request_last(rq); } spin_unlock_irqrestore(&io_request_lock, flags);}/* * This should get invoked any time we exit the driver to * wait for an interrupt response from a drive. handler() points * at the appropriate code to handle the next interrupt, and a * timer is started to prevent us from waiting forever in case * something goes wrong (see the ide_timer_expiry() handler later on). */void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout){ unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); spin_lock_irqsave(&hwgroup->spinlock, flags);#ifdef DEBUG if (hwgroup->handler != NULL) { printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n", drive->name, hwgroup->handler, handler); }#endif hwgroup->handler = handler; hwgroup->timer.expires = jiffies + timeout; add_timer(&(hwgroup->timer)); spin_unlock_irqrestore(&hwgroup->spinlock, flags);}/* * current_capacity() returns the capacity (in sectors) of a drive * according to its current geometry/LBA settings. */static unsigned long current_capacity (ide_drive_t *drive){ if (!drive->present) return 0; if (drive->driver != NULL) return DRIVER(drive)->capacity(drive); return 0;}/* * ide_geninit() is called exactly *once* for each major, from genhd.c, * at the beginning of the initial partition check for the drives. */void ide_geninit (struct gendisk *gd){ unsigned int unit; ide_hwif_t *hwif = gd->real_devices; for (unit = 0; unit < gd->nr_real; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; drive->part[0].nr_sects = current_capacity(drive); if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) || drive->driver == NULL || !drive->part[0].nr_sects) drive->part[0].start_sect = -1; /* skip partition check */ }}static void do_reset1 (ide_drive_t *, int); /* needed below *//* * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms * during an atapi drive reset operation. If the drive has not yet responded, * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */static void atapi_reset_pollfunc (ide_drive_t *drive){ ide_hwgroup_t *hwgroup = HWGROUP(drive); byte stat; SELECT_DRIVE(HWIF(drive),drive); udelay (10); if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) { printk("%s: ATAPI reset complete\n", drive->name); } else { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); return; /* continue polling */ } hwgroup->poll_timeout = 0; /* end of polling */ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); do_reset1 (drive, 1); /* do it the old fashioned way */ return; } hwgroup->poll_timeout = 0; /* done polling */}/* * reset_pollfunc() gets invoked to poll the interface for completion every 50ms * during an ide reset operation. If the drives have not yet responded, * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */static void reset_pollfunc (ide_drive_t *drive){ ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); byte tmp; if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &reset_pollfunc, HZ/20); return; /* continue polling */ } printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); } else { printk("%s: reset: ", hwif->name); if ((tmp = GET_ERR()) == 1) printk("success\n"); else {#if FANCY_STATUS_DUMPS printk("master: "); switch (tmp & 0x7f) { case 1: printk("passed"); break; case 2: printk("formatter device error"); break; case 3: printk("sector buffer error"); break; case 4: printk("ECC circuitry error"); break; case 5: printk("controlling MPU error"); break; default:printk("error (0x%02x?)", tmp); } if (tmp & 0x80) printk("; slave: failed"); printk("\n");#else printk("failed\n");#endif /* FANCY_STATUS_DUMPS */ } } hwgroup->poll_timeout = 0; /* done polling */}static void pre_reset (ide_drive_t *drive){ if (!drive->keep_settings) { drive->unmask = 0; drive->io_32bit = 0; if (drive->using_dma) (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (drive->driver != NULL) DRIVER(drive)->pre_reset(drive);}/* * do_reset1() attempts to recover a confused drive by resetting it. * Unfortunately, resetting a disk drive actually resets all devices on * the same interface, so it can really be thought of as resetting the * interface rather than resetting the drive. * * ATAPI devices have their own reset mechanism which allows them to be * individually reset without clobbering other devices on the same interface. * * Unfortunately, the IDE interface does not generate an interrupt to let * us know when the reset operation has finished, so we must poll for this. * Equally poor, though, is the fact that this may a very long time to complete, * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, * we set a timer to poll at 50ms intervals. */static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi){ unsigned int unit; unsigned long flags; ide_hwif_t *hwif = HWIF(drive); ide_hwgroup_t *hwgroup = HWGROUP(drive); __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { pre_reset(drive); SELECT_DRIVE(hwif,drive); udelay (20); OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); __restore_flags (flags); /* local CPU only */ return; } /* * First, reset any device state data we were maintaining * for any of the drives on this interface. */ for (unit = 0; unit < MAX_DRIVES; ++unit) pre_reset(&hwif->drives[unit]);#if OK_TO_RESET_CONTROLLER /* * Note that we also set nIEN while resetting the device, * to mask unwanted interrupts from the interface during the reset. * However, due to the design of PC hardware, this will cause an * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. */ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */ udelay(10); /* more than enough time */ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ udelay(10); /* more than enough time */ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &reset_pollfunc, HZ/20);#endif /* OK_TO_RESET_CONTROLLER */ __restore_flags (flags); /* local CPU only */}/* * ide_do_reset() is the entry point to the drive/interface reset code. */void ide_do_reset (ide_drive_t *drive){ do_reset1 (drive, 0);}/* * Clean up after success/failure of an explicit drive cmd */void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err){ unsigned long flags; struct request *rq = HWGROUP(drive)->rq; if (rq->cmd == IDE_DRIVE_CMD) { byte *args = (byte *) rq->buffer; rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); if (args) { args[0] = stat; args[1] = err; args[2] = IN_BYTE(IDE_NSECTOR_REG); } } spin_lock_irqsave(&io_request_lock, flags); drive->queue = rq->next; blk_dev[MAJOR(rq->rq_dev)].current_request = NULL; HWGROUP(drive)->rq = NULL; rq->rq_status = RQ_INACTIVE; spin_unlock_irqrestore(&io_request_lock, flags); save_flags(flags); /* all CPUs; overkill? */ cli(); /* all CPUs; overkill? */ if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ restore_flags(flags); /* all CPUs; overkill? */}/* * Error reporting, in human readable form (luxurious, but a memory hog). */byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat){ unsigned long flags; byte err = 0; __save_flags (flags); /* local CPU only */ ide__sti(); /* local CPU only */ printk("%s: %s: status=0x%02x", drive->name, msg, stat);#if FANCY_STATUS_DUMPS printk(" { "); if (stat & BUSY_STAT) printk("Busy "); else { if (stat & READY_STAT) printk("DriveReady "); if (stat & WRERR_STAT) printk("DeviceFault "); if (stat & SEEK_STAT) printk("SeekComplete "); if (stat & DRQ_STAT) printk("DataRequest "); if (stat & ECC_STAT) printk("CorrectedError "); if (stat & INDEX_STAT) printk("Index "); if (stat & ERR_STAT) printk("Error "); } printk("}");#endif /* FANCY_STATUS_DUMPS */ printk("\n"); if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { err = GET_ERR(); printk("%s: %s: error=0x%02x", drive->name, msg, err);#if FANCY_STATUS_DUMPS if (drive->media == ide_disk) { printk(" { "); if (err & ABRT_ERR) printk("DriveStatusError "); if (err & ICRC_ERR) printk((err & ABRT_ERR) ? "BadCRC " : "BadSector "); if (err & ECC_ERR) printk("UncorrectableError "); if (err & ID_ERR) printk("SectorIdNotFound "); if (err & TRK0_ERR) printk("TrackZeroNotFound "); if (err & MARK_ERR) printk("AddrMarkNotFound "); printk("}"); if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { byte cur = IN_BYTE(IDE_SELECT_REG); if (cur & 0x40) { /* using LBA? */ printk(", LBAsect=%ld", (unsigned long) ((cur&0xf)<<24) |(IN_BYTE(IDE_HCYL_REG)<<16) |(IN_BYTE(IDE_LCYL_REG)<<8) | IN_BYTE(IDE_SECTOR_REG)); } else { printk(", CHS=%d/%d/%d", (IN_BYTE(IDE_HCYL_REG)<<8) + IN_BYTE(IDE_LCYL_REG), cur & 0xf, IN_BYTE(IDE_SECTOR_REG)); } if (HWGROUP(drive)->rq) printk(", sector=%ld", HWGROUP(drive)->rq->sector); } }#endif /* FANCY_STATUS_DUMPS */ printk("\n"); } __restore_flags (flags); /* local CPU only */ return err;}/* * try_to_flush_leftover_data() is invoked in response to a drive * unexpectedly having its DRQ_STAT bit set. As an alternative to * resetting the drive, this routine tries to clear the condition * by read a sector's worth of data from the drive. Of course, * this may not help if the drive is *waiting* for data from *us*. */static void try_to_flush_leftover_data (ide_drive_t *drive){ int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; if (drive->media != ide_disk) return; while (i > 0) { unsigned long buffer[16]; unsigned int wcount = (i > 16) ? 16 : i; i -= wcount; ide_input_data (drive, buffer, wcount); }}/* * ide_error() takes action based on the error returned by the drive. */void ide_error (ide_drive_t *drive, const char *msg, byte stat){ struct request *rq; byte err; err = ide_dump_status(drive, msg, stat); if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) return; /* retry only "normal" I/O: */ if (rq->cmd == IDE_DRIVE_CMD) { rq->errors = 1; ide_end_drive_cmd(drive, stat, err); return; } if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { if (drive->media == ide_disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom and tape */ if (err == ABRT_ERR) { if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) return; /* some newer drives don't support WIN_SPECIFY */ } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) ; /* UDMA crc error -- just retry the operation */ else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; } if ((stat & DRQ_STAT) && rq->cmd != WRITE) try_to_flush_leftover_data(drive); } if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */ if (rq->errors >= ERROR_MAX) { if (drive->driver != NULL) DRIVER(drive)->end_request(0, HWGROUP(drive)); else ide_end_request(0, HWGROUP(drive)); } else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; ide_do_reset(drive); return; } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) drive->special.b.recalibrate = 1; ++rq->errors; }}/* * Issue a simple drive command * The drive must be selected beforehand. */void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler){ ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */ OUT_BYTE(nsect,IDE_NSECTOR_REG); OUT_BYTE(cmd,IDE_COMMAND_REG);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -