📄 ide-taskfile.c
字号:
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("%s", (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))) { if ((drive->id->command_set_2 & 0x0400) && (drive->id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { __u64 sectors = 0; u32 low = 0, high = 0; low = task_read_24(drive); OUT_BYTE(0x80, IDE_CONTROL_REG); high = task_read_24(drive); sectors = ((__u64)high << 24) | low; printk(", LBAsect=%lld", sectors); } else { 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=%llu", (__u64) HWGROUP(drive)->rq->sector); } }#endif /* FANCY_STATUS_DUMPS */ printk("\n"); } __restore_flags (flags); /* local CPU only */ return err;}/* * Clean up after success/failure of an explicit taskfile operation. */void ide_end_taskfile (ide_drive_t *drive, byte stat, byte err){ unsigned long flags; struct request *rq; ide_task_t *args; task_ioreg_t command; spin_lock_irqsave(&io_request_lock, flags); rq = HWGROUP(drive)->rq; spin_unlock_irqrestore(&io_request_lock, flags); args = (ide_task_t *) rq->special; command = args->tfRegister[IDE_COMMAND_OFFSET]; rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); args->tfRegister[IDE_ERROR_OFFSET] = err; args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); args->tfRegister[IDE_STATUS_OFFSET] = stat; if ((drive->id->command_set_2 & 0x0400) && (drive->id->cfs_enable_2 & 0x0400) && (drive->addressing == 1)) { OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); }/* taskfile_settings_update(drive, args, command); */ spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); HWGROUP(drive)->rq = NULL; end_that_request_last(rq); spin_unlock_irqrestore(&io_request_lock, flags);}/* * 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*. */void task_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) { u32 buffer[16]; unsigned int wcount = (i > 16) ? 16 : i; i -= wcount; taskfile_input_data (drive, buffer, wcount); }}/* * taskfile_error() takes action based on the error returned by the drive. */ide_startstop_t taskfile_error (ide_drive_t *drive, const char *msg, byte stat){ struct request *rq; byte err; err = taskfile_dump_status(drive, msg, stat); if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) return ide_stopped; /* retry only "normal" I/O: */ if (rq->cmd == IDE_DRIVE_TASKFILE) { rq->errors = 1; ide_end_taskfile(drive, stat, err); return ide_stopped; } 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 ide_stopped; /* some newer drives don't support WIN_SPECIFY */ } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { drive->crc_count++; /* 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) task_try_to_flush_leftover_data(drive); } if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ 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; return ide_do_reset(drive); } if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) drive->special.b.recalibrate = 1; ++rq->errors; } return ide_stopped;}#endif/* * Handler for special commands without a data phase from ide-disk *//* * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. */ide_startstop_t set_multmode_intr (ide_drive_t *drive){ byte stat; if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) { drive->mult_count = drive->mult_req; } else { drive->mult_req = drive->mult_count = 0; drive->special.b.recalibrate = 1; (void) ide_dump_status(drive, "set_multmode", stat); } return ide_stopped;}/* * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. */ide_startstop_t set_geometry_intr (ide_drive_t *drive){ byte stat; if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) return ide_stopped; if (stat & (ERR_STAT|DRQ_STAT)) return ide_error(drive, "set_geometry_intr", stat); ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL); return ide_started;}/* * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. */ide_startstop_t recal_intr (ide_drive_t *drive){ byte stat = GET_STAT(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) return ide_error(drive, "recal_intr", stat); return ide_stopped;}/* * Handler for commands without a data phase */ide_startstop_t task_no_data_intr (ide_drive_t *drive){ ide_task_t *args = HWGROUP(drive)->rq->special; byte stat = GET_STAT(); ide__sti(); /* local CPU only */ if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); /* calls ide_end_drive_cmd */ if (args) ide_end_drive_cmd (drive, stat, GET_ERR()); return ide_stopped;}/* * Handler for command with PIO data-in phase */ide_startstop_t task_in_intr (ide_drive_t *drive){ byte stat = GET_STAT(); byte io_32bit = drive->io_32bit; struct request *rq = HWGROUP(drive)->rq; char *pBuf = NULL; if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { if (stat & (ERR_STAT|DRQ_STAT)) { return ide_error(drive, "task_in_intr", stat); } if (!(stat & BUSY_STAT)) { DTF("task_in_intr to Soon wait for next interrupt\n"); ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); return ide_started; } } DTF("stat: %02x\n", stat); pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); DTF("Read: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors); drive->io_32bit = 0; taskfile_input_data(drive, pBuf, SECTOR_WORDS); drive->io_32bit = io_32bit; if (--rq->current_nr_sectors <= 0) { /* (hs): swapped next 2 lines */ DTF("Request Ended stat: %02x\n", GET_STAT()); ide_end_request(1, HWGROUP(drive)); } else { ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); return ide_started; } return ide_stopped;}#undef ALTSTAT_SCREW_UP#ifdef ALTSTAT_SCREW_UP/* * (ks/hs): Poll Alternate Status Register to ensure * that drive is not busy. */byte altstat_multi_busy (ide_drive_t *drive, byte stat, const char *msg){ int i; DTF("multi%s: ASR = %x\n", msg, stat); if (stat & BUSY_STAT) { /* (ks/hs): FIXME: Replace hard-coded 100, error handling? */ for (i=0; i<100; i++) { stat = GET_ALTSTAT(); if ((stat & BUSY_STAT) == 0) break; } } /* * (ks/hs): Read Status AFTER Alternate Status Register */ return(GET_STAT());}/* * (ks/hs): Poll Alternate status register to wait for drive * to become ready for next transfer */byte altstat_multi_poll (ide_drive_t *drive, byte stat, const char *msg){ /* (ks/hs): FIXME: Error handling, time-out? */ while (stat & BUSY_STAT) stat = GET_ALTSTAT(); DTF("multi%s: nsect=1, ASR = %x\n", msg, stat); return(GET_STAT()); /* (ks/hs): Clear pending IRQ */}#endif /* ALTSTAT_SCREW_UP *//* * Handler for command with Read Multiple */ide_startstop_t task_mulin_intr (ide_drive_t *drive){ unsigned int msect, nsect;#ifdef ALTSTAT_SCREW_UP byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "read");#else byte stat = GET_STAT();#endif /* ALTSTAT_SCREW_UP */ byte io_32bit = drive->io_32bit; struct request *rq = HWGROUP(drive)->rq; char *pBuf = NULL; if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { if (stat & (ERR_STAT|DRQ_STAT)) { return ide_error(drive, "task_mulin_intr", stat); } /* no data yet, so wait for another interrupt */ ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); return ide_started; } /* (ks/hs): Fixed Multi-Sector transfer */ msect = drive->mult_count;#ifdef ALTSTAT_SCREW_UP /* * Screw the request we do not support bad data-phase setups! * Either read and learn the ATA standard or crash yourself! */ if (!msect) { /* * (ks/hs): Drive supports multi-sector transfer, * drive->mult_count was not set */ nsect = 1; while (rq->current_nr_sectors) { pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); DTF("Multiread: %p, nsect: %d, rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); drive->io_32bit = 0; taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); drive->io_32bit = io_32bit; rq->errors = 0; rq->current_nr_sectors -= nsect; stat = altstat_multi_poll(drive, GET_ALTSTAT(), "read"); } ide_end_request(1, HWGROUP(drive)); return ide_stopped; }#endif /* ALTSTAT_SCREW_UP */ nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); drive->io_32bit = 0; taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); drive->io_32bit = io_32bit; rq->errors = 0; rq->current_nr_sectors -= nsect; if (rq->current_nr_sectors != 0) { ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); return ide_started; } ide_end_request(1, HWGROUP(drive)); return ide_stopped;}ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq){ ide_task_t *args = rq->special; ide_startstop_t startstop; if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); return startstop; } /* (ks/hs): Fixed Multi Write */ if ((args->tfRegister[IDE_COMMAND_OFFSET] != WIN_MULTWRITE) && (args->tfRegister[IDE_COMMAND_OFFSET] != WIN_MULTWRITE_EXT)) { /* For Write_sectors we need to stuff the first sector */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -