📄 ide_drive.cxx
字号:
}uint32_tide_drive::DoProbe(uint8_t cmd){ if (present && (media != IDE::med_disk) && (cmd == WIN_IDENTIFY)) { MsgLog::fatal("Drive obviously not IDE\n"); return 4; } MsgLog::printf("probing for %s: present=%d, media=%d, probetype=%s hwif=0x%08x\n\n", name, present, media, (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI", hwif); /* The following OUGHT to be redundant: */ hwif->SelectDrive(ndx); Machine::SpinWaitMs(DRIVE_SELECT_DELAY); if (hwif->Get8(IDE_DRV_HD) != select.all && !present) { hwif->Put8(IDE_DRV_HD, 0xa0); /* select drive 0 */ MsgLog::printf("Drive didn't select and not present\n"); return 3; } uint8_t status = hwif->Get8(IDE_STATUS); uint8_t rc; if (OK_STAT(status,READY_STAT,BUSY_STAT) || present || cmd == WIN_PIDENTIFY) { /* try to identify the drive twice: */ if ((rc = Identify(cmd))) rc = Identify(cmd); /* Make sure interrupt status is clear: */ uint8_t status = hwif->Get8(IDE_STATUS); if (rc) MsgLog::printf("%s: no response(status = 0x%02x)\n", name, status); } else { MsgLog::printf("Drive didn't select\n"); rc = 3; /* not present or maybe ATAPI */ } if (select.b.unit != 0) { /* unit not 0 */ MsgLog::printf("Reselect drive 0. "); hwif->Put8(IDE_DRV_HD, 0xa0); /* select drive 0 */ Machine::SpinWaitMs(DRIVE_SELECT_DELAY); /* Make sure interrupt status is clear: */ uint8_t result=hwif->Get8(IDE_CTL_ALTSTATUS); MsgLog::printf("Alt status : 0x%02x\n", result); (void) hwif->Get8(IDE_STATUS); } return rc;}voidide_drive::Probe(){#if 0 if (noprobe) /* skip probing? */ return;#endif if (DoProbe(WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ (void) DoProbe(WIN_PIDENTIFY); /* look for ATAPI device */ } if (!present) return; /* drive not found */ needsMount = true; /* Double check the results of the probe: */ if (id == NULL) { /* identification failed? */ if (media == IDE::med_disk) { MsgLog::printf("%s: old drive, CHS=%d/%d/%d\n", name, b_chs.cyl, b_chs.hd, b_chs.sec); } else if (media == IDE::med_cdrom) { MsgLog::printf("%s: ATAPI CDROM?\n", name); } else { present = false; /* nuke it */ } }}voidide_drive::DoDriveCommand(Request * /* req */){ MsgLog::fatal("Drive commands not implemented\n");}/* * do_rw_disk() issues READ and WRITE commands to a disk, * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */#if 0static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)#endifvoidide_drive::DoDiskRequest(Request *req){ /* NOTE: Plug requests are handled in ide_hwif::StartRequest() */ hwif->Put8(IDE_CTL_ALTSTATUS, ctl); /* We can do the whole transfer until proven otherwise. */ req->nsec = req->req_nsec; uint8_t sec, hd; uint16_t cyl; uint8_t sel = 0; if (select.b.lba) {#if defined(IDE_DEBUG) MsgLog::printf("%s: %sing: LBAsect=%d, sectors=%d, buffer=0x%08x\n", name, (req->cmd==Request::Read)?"read":"writ", req->req_start, req->nsec, (unsigned long) req->req_ioaddr);#endif sec = req->req_start; cyl = req->req_start >> 8; hd = (req->req_start >> 24) & 0x0f; } else { uint32_t track = req->req_start / l_chs.sec; sec = req->req_start % l_chs.sec; hd = track % l_chs.hd; cyl = track / l_chs.hd; sec++;#if defined(IDE_DEBUG) MsgLog::printf("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08x\n", name, (req->cmd==Request::Read)?"read":"writ", cyl, hd, sec, req->nsec, (unsigned long) req->req_ioaddr);#endif } sel = hd | select.all; /* IDE can always issue a request for more sectors than we need. We * go ahead and issue the request for the complete read here. * * FIX: Someday we need a strategy here for error recovery. */ hwif->Put8(IDE_NSECS, req->nsec); hwif->Put8(IDE_SECTOR, sec); hwif->Put8(IDE_CYL_LO, cyl); hwif->Put8(IDE_CYL_HI, cyl >> 8); hwif->Put8(IDE_DRV_HD, sel);#if defined(IDE_DEBUG) MsgLog::printf("Start %s: start %d nsec %d at 0x%08x\n", ((req->cmd == Request::Read) ? "read" : "write"), req->req_start, req->req_nsec, req->req_ioaddr);#endif#if 0 if (IS_PROMISE_DRIVE) { if (hwif->is_promise2 || rq->cmd == READ) { do_promise_io (drive, rq); return; } }#endif if (req->cmd == IoCmd::Read) {#if defined(IDE_DEBUG) uint8_t status = hwif->Get8(IDE_STATUS); MsgLog::printf("Status prior to initating: 0x%x\n", status);#endif /* If we are able to use DMA, do so. */ assert (use_dma == false); if (use_dma && hwif->dma_handler(ide_hwif::dma_read, this)) return; hwif->SetHandler(ndx, &ide_hwif::ReadIntr); hwif->Put8(IDE_CMD, multCount ? WIN_MULTREAD : WIN_READ);#if defined(IDE_DEBUG) MsgLog::dprintf(false, "Return from DoDiskRequest\n");#endif return; } if (req->cmd == IoCmd::Write) { /* Use multwrite whenever possible. */ assert (use_dma == false); if (use_dma && hwif->dma_handler(ide_hwif::dma_write, this)) return; hwif->Put8(IDE_CMD, multCount ? WIN_MULTWRITE : WIN_WRITE); if ( WaitStatus(DATA_READY, BAD_W_STAT, WAIT_DRQ) ) { MsgLog::printf("%s: no DRQ after issuing %s\n", name, multCount ? "MULTWRITE" : "WRITE"); return; } if (multCount) { hwif->group->curReq = req; hwif->SetHandler (ndx, &ide_hwif::MultWriteIntr); MultWrite(req); } else { hwif->SetHandler (ndx, &ide_hwif::WriteIntr); OutputData((void*) req->req_ioaddr, EROS_SECTOR_SIZE >> 2); } return; }#if 0 MsgLog::printf("%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive));#endif}voidide_drive::EndDriveCmd(uint8_t /* stat */, uint8_t /* err */){ MsgLog::fatal("Drive end command not implemented\n");}/* * Error reporting, in human readable form (luxurious, but a memory hog). */uint8_tide_drive::DumpStatus (const char *msg, uint8_t stat, Request* req){ uint8_t err = 0; MsgLog::printf("%s: %s: status=0x%02x", name, msg, stat);#ifdef FANCY_STATUS_DUMPS if (media == IDE::med_disk) { MsgLog::printf(" { "); if (stat & BUSY_STAT) MsgLog::printf("Busy "); else { if (stat & READY_STAT) MsgLog::printf("DriveReady "); if (stat & WRERR_STAT) MsgLog::printf("DeviceFault "); if (stat & SEEK_STAT) MsgLog::printf("SeekComplete "); if (stat & DRQ_STAT) MsgLog::printf("DataRequest "); if (stat & ECC_STAT) MsgLog::printf("CorrectedError "); if (stat & INDEX_STAT) MsgLog::printf("Index "); if (stat & ERR_STAT) MsgLog::printf("Error "); } MsgLog::printf("}"); }#endif /* FANCY_STATUS_DUMPS */ MsgLog::printf("\n"); if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { err = hwif->Get8(IDE_ERROR); MsgLog::printf("%s: %s: error=0x%02x", name, msg, err);#ifdef FANCY_STATUS_DUMPS if (media == IDE::med_disk) { MsgLog::printf(" { "); if (err & BBD_ERR) MsgLog::printf("BadSector "); if (err & ECC_ERR) MsgLog::printf("UncorrectableError "); if (err & ID_ERR) MsgLog::printf("SectorIdNotFound "); if (err & ABRT_ERR) MsgLog::printf("DriveStatusError "); if (err & TRK0_ERR) MsgLog::printf("TrackZeroNotFound "); if (err & MARK_ERR) MsgLog::printf("AddrMarkNotFound "); MsgLog::printf("}"); if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { uint8_t cur = hwif->Get8(IDE_DRV_HD); if (cur & 0x40) { /* using LBA? */ MsgLog::printf(", LBAsect=%ld", (unsigned long) ((cur&0xf)<<24) |(hwif->Get8(IDE_CYL_HI)<<16) |(hwif->Get8(IDE_CYL_LO)<<8) | hwif->Get8(IDE_SECTOR)); } else { MsgLog::printf(", CHS=%d/%d/%d", (hwif->Get8(IDE_CYL_HI)<<8) + hwif->Get8(IDE_CYL_LO), cur & 0xf, /* ?? WHY the limit ?? */ hwif->Get8(IDE_SECTOR)); } if (req) MsgLog::printf(", sector=%ld", req->req_start); } }#endif /* FANCY_STATUS_DUMPS */ MsgLog::printf("\n"); } return err;}/* * Error() takes action based on the error returned by the controller. */voidide_drive::Error(const char *msg, uint8_t stat){ Request *req = rq.GetNextRequest(); uint8_t err; err = DumpStatus(msg, stat, req); if (req == NULL) return; /* retry only "normal" I/O: */ if (req->cmd == IoCmd::NUM_IOCMD) { req->nError = 1; EndDriveCmd(stat, err); return; } if (stat & BUSY_STAT) { /* other bits are useless when BUSY */ req->nError |= ERROR_RESET; } else { if (media == IDE::med_disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom and tape */ if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ req->nError = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ req->nError |= ERROR_RECAL; } if ((stat & DRQ_STAT) && req->cmd != IoCmd::Write) FlushResidue(); } uint8_t status = hwif->Get8(IDE_STATUS); if (status & (BUSY_STAT|DRQ_STAT)) req->nError |= ERROR_RESET; /* Mmmm.. timing problem */ if (req->nError >= ERROR_MAX) {#ifdef CONFIG_BLK_DEV_IDETAPE if (drive->media == ide_tape) { req->nError = 0; idetape_end_request(0, HWGROUP(drive)); } else#endif /* CONFIG_BLK_DEV_IDETAPE */ /* Terminate the request: */ req->Terminate(); } else { if ((req->nError & ERROR_RESET) == ERROR_RESET) { ++req->nError; hwif->DoReset(ndx); return; } else if ((req->nError & ERROR_RECAL) == ERROR_RECAL) flags.b.recal = 1; ++req->nError; } MsgLog::dprintf(true, "A drive error occurred on drive %s\n", name);}/* FlushResidue() 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*. */voidide_drive::FlushResidue(){ uint32_t i = (multCount ? multCount : 1) * EROS_SECTOR_SIZE / 2; /* 16-bit xfers */ while (i > 0) { unsigned long buffer[16]; unsigned int wcount = (i > 16) ? 16 : i; i -= wcount; InputData(buffer, wcount); }}/* * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT * commands to a drive. It used to do much more, but has been scaled back. */voidide_drive::HandleDriveFlags(){#if 0next:#endif MsgLog::printf("%s: do_special: 0x%02x\n", name, flags.all); if (flags.b.setGeom) { flags.b.setGeom = 0; if (media == IDE::med_disk) { hwif->Put8(IDE_SECTOR, l_chs.sec); hwif->Put8(IDE_CYL_LO, l_chs.cyl); hwif->Put8(IDE_CYL_HI, l_chs.cyl >> 8); hwif->Put8(IDE_DRV_HD, (l_chs.hd - 1 | select.all) & 0xBFu); if (!IS_PROMISE_DRIVE) hwif->DoCmd(ndx, WIN_SPECIFY, l_chs.sec, &ide_hwif::SetGeometryIntr); } } else if (flags.b.recal) { flags.b.recal = 0; if (media == IDE::med_disk && !IS_PROMISE_DRIVE) hwif->DoCmd(ndx, WIN_RESTORE, l_chs.sec, &ide_hwif::RecalibrateIntr); }#if 0 else if (flags.b.set_pio) { ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc; flags.b.set_pio = 0; if (tuneproc != NULL) tuneproc(drive, drive->pio_req); goto next; }#endif else if (flags.b.setMultMode) { flags.b.setMultMode = 0; if (media == IDE::med_disk) { if (id && multReq > id->max_multsect) multReq = id->max_multsect; MsgLog::printf("ship a WIN_SETMULT command multReq %d max %d\n", multReq, id->max_multsect); if (!IS_PROMISE_DRIVE) hwif->DoCmd(ndx, WIN_SETMULT, multReq, &ide_hwif::SetMultmodeIntr); } else multReq = 0; } else if (flags.all) { uint8_t old_flags = flags.all; flags.all = 0; MsgLog::printf("%s: bad special flag: 0x%02x\n", name, old_flags); }}/* Busy wait for the drive to return the expected status, but not for * longer than timelimit. Return true if there was an error. */boolide_drive::WaitStatus(uint8_t good, uint8_t bad, uint32_t timelimit){ timelimit = Machine::MillisecondsToTicks(timelimit); timelimit += SysTimer::Now(); do { /* The IDE spec allows the drive 400ns to get it's act together, * This is incredibly stupid, as it guarantees at least a 1ms * delay whenever the status needs to be checked. Common case is * that we will do the loop only once. * * Note that this code needs to be redesigned, because the busy * wait loop has very bad implications for real-time. */ #ifdef VERBOSE_BUG uint32_t f = GetFlags(); MsgLog::printf("Before SpinWait: flags 0x%08x timer %s enabled.\n", f, IRQ::IsEnabled(0) ? "is" : "is not");#endif Machine::SpinWaitUs(1); uint8_t status = hwif->Get8(IDE_STATUS); if ( OK_STAT(status,good, bad) ) return false; /* If the drive no longer purports to be busy, life is bad: */ if ( (status & BUSY_STAT) == 0 ) { Error("status error", status); return true; } } while( timelimit < SysTimer::Now() ); uint8_t status = hwif->Get8(IDE_STATUS); Error("status timeout", status); return true;}/* This routine will not be called unless there is really a request to * process. */voidide_drive::DoRequest(Request * req){#if defined(IDE_DEBUG) MsgLog::printf("selecting drive %d\n", ndx);#endif hwif->SelectDrive(ndx); /* This causes infinite loop for some reason: * Machine::SpinWaitMs(DRIVE_SELECT_DELAY); */ if ( WaitStatus(READY_STAT, BUSY_STAT|DRQ_STAT, WAIT_READY) ) { MsgLog::fatal("Drive not ready\n"); return; } if (flags.all) { HandleDriveFlags();#if defined(IDE_DEBUG) MsgLog::dprintf(false, "Handling driver flags\n");#endif return; } if ( req->cmd >= IoCmd::NUM_IOCMD ) DoDriveCommand(req); else { switch (media) { case IDE::med_disk: DoDiskRequest(req); break; default: MsgLog::fatal("Unimplemented media!\n"); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -