📄 sdata.c
字号:
drive->c = drive->info[Ilcyl]; drive->h = drive->info[Ilhead]; drive->s = drive->info[Ilsec]; } else{ drive->c = drive->info[Iccyl]; drive->h = drive->info[Ichead]; drive->s = drive->info[Icsec]; } if(drive->info[Icapabilities] & 0x0200){ drive->sectors = (drive->info[Ilba1]<<16) |drive->info[Ilba0]; drive->dev |= Lba; } else drive->sectors = drive->c*drive->h*drive->s; atasetrwmode(drive, cmdport, ctlport, dev); } drive->dma = 0; if(DEBUG & DbgCONFIG){ print("dev %2.2uX capabilities %4.4uX config %4.4uX", dev, drive->info[Icapabilities], drive->info[Iconfig]); print(" mwdma %4.4uX dma %8.8uX", drive->info[Imwdma], drive->dma); if(drive->info[Ivalid] & 0x04) print(" udma %4.4uX", drive->info[Iudma]); print("\n"); } return drive;}static voidatasrst(int ctlport){ /* * Srst is a big stick and may cause problems if further * commands are tried before the drives become ready again. * Also, there will be problems here if overlapped commands * are ever supported. */ microdelay(5); outb(ctlport+Dc, Srst); microdelay(5); outb(ctlport+Dc, 0); microdelay(2*1000);}static SDev*ataprobe(int cmdport, int ctlport, int irq){ Ctlr* ctlr; SDev *sdev; Drive *drive; int dev, error, rhi, rlo; /* * Try to detect a floating bus. * Bsy should be cleared. If not, see if the cylinder registers * are read/write capable. * If the master fails, try the slave to catch slave-only * configurations. * There's no need to restore the tested registers as they will * be reset on any detected drives by the Cedd command. * All this indicates is that there is at least one drive on the * controller; when the non-existent drive is selected in a * single-drive configuration the registers of the existing drive * are often seen, only command execution fails. */ dev = Dev0; if(inb(ctlport+As) & Bsy){ outb(cmdport+Dh, dev);trydev1: atadebug(cmdport, ctlport, "ataprobe bsy"); outb(cmdport+Cyllo, 0xAA); outb(cmdport+Cylhi, 0x55); outb(cmdport+Sector, 0xFF); rlo = inb(cmdport+Cyllo); rhi = inb(cmdport+Cylhi); if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ if(dev == Dev1){release: return nil; } dev = Dev1; if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) goto trydev1; } } /* * Disable interrupts on any detected controllers. */ outb(ctlport+Dc, Nien);tryedd1: if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 100*1000) < 0){ /* * There's something there, but it didn't come up clean, * so try hitting it with a big stick. The timing here is * wrong but this is a last-ditch effort and it sometimes * gets some marginal hardware back online. */ atasrst(ctlport); if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 100*1000) < 0) goto release; } /* * Can only get here if controller is not busy. * If there are drives Bsy will be set within 400nS, * must wait 2mS before testing Status. * Wait for the command to complete (6 seconds max). */ outb(cmdport+Command, Cedd); delay(2); if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) goto release; /* * If bit 0 of the error register is set then the selected drive * exists. This is enough to detect single-drive configurations. * However, if the master exists there is no way short of executing * a command to determine if a slave is present. * It appears possible to get here testing Dev0 although it doesn't * exist and the EDD won't take, so try again with Dev1. */ error = inb(cmdport+Error); atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); if((error & ~0x80) != 0x01){ if(dev == Dev1) goto release; dev = Dev1; goto tryedd1; } /* * At least one drive is known to exist, try to * identify it. If that fails, don't bother checking * any further. * If the one drive found is Dev0 and the EDD command * didn't indicate Dev1 doesn't exist, check for it. */ if((drive = ataidentify(cmdport, ctlport, dev)) == nil) goto release; if((ctlr = malloc(sizeof(Ctlr))) == nil){ free(drive); goto release; } if((sdev = malloc(sizeof(SDev))) == nil){ free(ctlr); free(drive); goto release; } drive->ctlr = ctlr; if(dev == Dev0){ ctlr->drive[0] = drive; if(!(error & 0x80)){ /* * Always leave Dh pointing to a valid drive, * otherwise a subsequent call to ataready on * this controller may try to test a bogus Status. * Ataprobe is the only place possibly invalid * drives should be selected. */ drive = ataidentify(cmdport, ctlport, Dev1); if(drive != nil){ drive->ctlr = ctlr; ctlr->drive[1] = drive; } else outb(cmdport+Dh, Dev0); } } else ctlr->drive[1] = drive; ctlr->cmdport = cmdport; ctlr->ctlport = ctlport; ctlr->irq = irq; sdev->ifc = &sdataifc; sdev->ctlr = ctlr; sdev->nunit = 2; return sdev;}static intatasetsense(Drive* drive, int status, int key, int asc, int ascq){ drive->sense[2] = key; drive->sense[12] = asc; drive->sense[13] = ascq; return status;}static intatamodesense(Drive* drive, uchar* cmd){ int len; /* * Fake a vendor-specific request with page code 0, * return the drive info. */ if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) return atasetsense(drive, SDcheck, 0x05, 0x24, 0); len = (cmd[7]<<8)|cmd[8]; if(len == 0) return SDok; if(len < 8+sizeof(drive->info)) return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); if(drive->data == nil || drive->dlen < len) return atasetsense(drive, SDcheck, 0x05, 0x20, 1); memset(drive->data, 0, 8); drive->data[0] = sizeof(drive->info)>>8; drive->data[1] = sizeof(drive->info); memmove(drive->data+8, drive->info, sizeof(drive->info)); drive->data += 8+sizeof(drive->info); return SDok;}static voidatanop(Drive* drive, int subcommand){ Ctlr* ctlr; int as, cmdport, ctlport, timeo; /* * Attempt to abort a command by using NOP. * In response, the drive is supposed to set Abrt * in the Error register, set (Drdy|Err) in Status * and clear Bsy when done. However, some drives * (e.g. ATAPI Zip) just go Bsy then clear Status * when done, hence the timeout loop only on Bsy * and the forced setting of drive->error. */ ctlr = drive->ctlr; cmdport = ctlr->cmdport; outb(cmdport+Features, subcommand); outb(cmdport+Dh, drive->dev); outb(cmdport+Command, 0); microdelay(1); ctlport = ctlr->ctlport; for(timeo = 0; timeo < 1000; timeo++){ as = inb(ctlport+As); if(!(as & Bsy)) break; microdelay(1); } drive->error |= Abrt;}static intatapktiodone(void* arg){ return ((Ctlr*)arg)->done;}static voidatapktinterrupt(Drive* drive){ Ctlr* ctlr; int cmdport, len; ctlr = drive->ctlr; cmdport = ctlr->cmdport; switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){ case Cd: outss(cmdport+Data, drive->pktcmd, drive->pkt/2); break; case 0: len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); if(drive->data+len > drive->limit){ atanop(drive, 0); break; } outss(cmdport+Data, drive->data, len/2); drive->data += len; break; case Io: len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); if(drive->data+len > drive->limit){ atanop(drive, 0); break; } inss(cmdport+Data, drive->data, len/2); drive->data += len; break; case Io|Cd:ctlr->done=1; break; }}static intatapktio(Drive* drive, uchar* cmd, int clen){ Ctlr *ctlr; int as, cmdport, ctlport, len, r; if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) return atamodesense(drive, cmd); r = SDok; drive->command = Cpkt; memmove(drive->pktcmd, cmd, clen); memset(drive->pktcmd+clen, 0, drive->pkt-clen); drive->limit = drive->data+drive->dlen; ctlr = drive->ctlr; cmdport = ctlr->cmdport; ctlport = ctlr->ctlport; qlock(ctlr); if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 100*1000) < 0) return -1; ilock(ctlr);drive->pktdma = 0; outb(cmdport+Features, drive->pktdma); outb(cmdport+Count, 0); outb(cmdport+Sector, 0); len = 16*drive->secsize; outb(cmdport+Bytelo, len); outb(cmdport+Bytehi, len>>8); outb(cmdport+Dh, drive->dev); ctlr->done = 0; outb(cmdport+Command, Cpkt); if((drive->info[Iconfig] & 0x0060) != 0x0020){ as = ataready(cmdport, ctlport, drive->dev, Bsy, Drq|Chk, 4*1000); if(as < 0) r = SDtimeout; else if(as & Chk) r = SDcheck; else atapktinterrupt(drive); } ctlr->curdrive = drive; iunlock(ctlr); tsleep(ctlr, atapktiodone, ctlr, 10*1000); if(!ctlr->done) panic("atapktiodone"); qunlock(ctlr); if(drive->status & Chk) r = SDcheck; return r;}static intatageniodone(void* arg){ return ((Ctlr*)arg)->done;}static intatageniostart(Drive* drive, int lba){ Ctlr *ctlr; int as, c, cmdport, ctlport, h, len, s; if(drive->dev & Lba){ c = (lba>>8) & 0xFFFF; h = (lba>>24) & 0x0F; s = lba & 0xFF; } else{ c = lba/(drive->s*drive->h); h = ((lba/drive->s) % drive->h); s = (lba % drive->s) + 1; } ctlr = drive->ctlr; cmdport = ctlr->cmdport; ctlport = ctlr->ctlport; if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 100*1000) < 0) return -1; ilock(ctlr); if(drive->write) drive->command = drive->piow; else drive->command = drive->pior; outb(cmdport+Count, drive->count); outb(cmdport+Sector, s); outb(cmdport+Dh, drive->dev|h); outb(cmdport+Cyllo, c); outb(cmdport+Cylhi, c>>8); ctlr->done = 0; outb(cmdport+Command, drive->command); microdelay(1); switch(drive->command){ case Cws: case Cwsm: as = ataready(cmdport, ctlport, drive->dev, Bsy, Drq|Err, 1000); if(as < 0 || (as & Err)){ iunlock(ctlr); return -1; } len = drive->block; if(drive->data+len > drive->limit) len = drive->limit-drive->data; outss(cmdport+Data, drive->data, len/2); break; case Crd: case Cwd:panic("atadmastart"); break; } ctlr->curdrive = drive; iunlock(ctlr); return 0;}static intatagenio(Drive* drive, uchar* cmd, int){ uchar *p; Ctlr *ctlr; int count, lba, len; /* * Map SCSI commands into ATA commands for discs. * Fail any command with a LUN except INQUIRY which * will return 'logical unit not supported'. */ if((cmd[1]>>5) && cmd[0] != 0x12) return atasetsense(drive, SDcheck, 0x05, 0x25, 0); switch(cmd[0]){ default: return atasetsense(drive, SDcheck, 0x05, 0x20, 0); case 0x00: /* test unit ready */ return SDok; case 0x03: /* request sense */ if(cmd[4] < sizeof(drive->sense)) len = cmd[4]; else len = sizeof(drive->sense); if(drive->data && drive->dlen >= len){ memmove(drive->data, drive->sense, len); drive->data += len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -