📄 sdata.c
字号:
* 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;// if(ioalloc(cmdport, 8, 0, "atacmd") < 0)// return nil;// if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){// iofree(cmdport);// return nil;// } /* * 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); microdelay(1);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: // iofree(cmdport); // iofree(ctlport+As); 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, 105*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, 106*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 = atadrive(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 = atadrive(cmdport, ctlport, Dev1); if(drive != nil){ drive->ctlr = ctlr; ctlr->drive[1] = drive; } else{ outb(cmdport+Dh, Dev0); microdelay(1); } } } else ctlr->drive[1] = drive; ctlr->cmdport = cmdport; ctlr->ctlport = ctlport; ctlr->irq = irq; ctlr->tbdf = BUSUNKNOWN; ctlr->command = Cedd; /* debugging */ sdev->ifc = &sdataifc; sdev->ctlr = ctlr; sdev->nunit = 2; ctlr->sdev = sdev; 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); ctlr->command = Cnop; /* debugging */ outb(cmdport+Command, Cnop); 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 voidataabort(Drive* drive, int dolock){ /* * If NOP is available (packet commands) use it otherwise * must try a software reset. */ if(dolock) ilock(drive->ctlr); if(atacsfenabled(drive, 0x0000000000004000LL)) atanop(drive, 0); else{ atasrst(drive->ctlr->ctlport); drive->error |= Abrt; } if(dolock) iunlock(drive->ctlr);}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: // if(drive->pktdma) // atadmainterrupt(drive, drive->dlen); // else 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); as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000); if(as < 0 || (as&Chk)){ qunlock(ctlr); return -1; } ilock(ctlr);// if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))// drive->pktdma = Dma;// else// drive->pktdma = 0; outb(cmdport+Features, 0/*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; ctlr->curdrive = drive; ctlr->command = Cpkt; /* debugging */// if(drive->pktdma)// atadmastart(ctlr, drive->write); outb(cmdport+Command, Cpkt); if((drive->info[Iconfig] & 0x0060) != 0x0020){ microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); if(as < 0 || (as & (Bsy|Chk))){ drive->status = as<0 ? 0 : as; ctlr->curdrive = nil; ctlr->done = 1; r = SDtimeout; }else atapktinterrupt(drive); } iunlock(ctlr); sleep(ctlr, atapktiodone, ctlr); qunlock(ctlr); if(drive->status & Chk) r = SDcheck; return r;}static intatageniodone(void* arg){ return ((Ctlr*)arg)->done;}static uchar cmd48[256] = { [Crs] Crs48, [Crd] Crd48, [Crdq] Crdq48, [Crsm] Crsm48, [Cws] Cws48, [Cwd] Cwd48, [Cwdq] Cwdq48, [Cwsm] Cwsm48,};static intatageniostart(Drive* drive, vlong lba){ Ctlr *ctlr; uchar cmd; int as, c, cmdport, ctlport, h, len, s, use48; use48 = 0; if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){ if(!(drive->flags & Lba48)) return -1; use48 = 1; c = h = s = 0; }else 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, 101*1000) < 0) return -1; ilock(ctlr); drive->block = drive->secsize; if(drive->write) drive->command = Cws; else drive->command = Crs; drive->limit = drive->data + drive->count*drive->secsize; cmd = drive->command; if(use48){ outb(cmdport+Count, (drive->count>>8) & 0xFF); outb(cmdport+Count, drive->count & 0XFF); outb(cmdport+Lbalo, (lba>>24) & 0xFF); outb(cmdport+Lbalo, lba & 0xFF); outb(cmdport+Lbamid, (lba>>32) & 0xFF); outb(cmdport+Lbamid, (lba>>8) & 0xFF); outb(cmdport+Lbahi, (lba>>40) & 0xFF); outb(cmdport+Lbahi, (lba>>16) & 0xFF); outb(cmdport+Dh, drive->dev|Lba); cmd = cmd48[cmd]; if(DEBUG & Dbg48BIT) print("using 48-bit commands\n"); }else{ outb(cmdport+Count, drive->count); outb(cmdport+Sector, s); outb(cmdport+Cyllo, c); outb(cmdport+Cylhi, c>>8); outb(cmdport+Dh, drive->dev|h); } ctlr->done = 0; ctlr->curdrive = drive; ctlr->command = drive->command; /* debugging */ outb(cmdport+Command, cmd); switch(drive->command){ case Cws: case Cwsm: microdelay(1); as = ataready(cmdport, ctlport, 0, 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: // atadmastart(ctlr, drive->write); break; } iunlock(ctlr); return 0;}static intatagenioretry(Drive* drive){ return atasetsense(drive, SDcheck, 4, 8, drive->error);}static intatagenio(Drive* drive, uchar* cmd, int){ uchar *p; Ctlr *ctlr; int count, max; vlong 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; } return SDok; case 0x12: /* inquiry */ if(cmd[4] < sizeof(drive->inquiry)) len = cmd[4]; else len = sizeof(drive->inquiry); if(drive->data && drive->dlen >= len){ memmove(drive->data, drive->inquiry, len); drive->data += len; } return SDok; case 0x1B: /* start/stop unit */ /* * NOP for now, can use the power management feature * set later. */ return SDok; case 0x25: /* read capacity */ if((cmd[1] & 0x01) || cmd[2] || cmd[3]) return atasetsense(drive, SDcheck, 0x05, 0x24, 0); if(drive->data == nil || drive->dlen < 8) return atasetsense(drive, SDcheck, 0x05, 0x20, 1); /* * Read capacity returns the LBA of the last sector. */ len = drive->sectors-1; p = drive->data; *p++ = len>>24;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -