📄 sdata.c
字号:
{ drive->sense[2] = key; drive->sense[12] = asc; drive->sense[13] = ascq; return status;}static intatastandby(Drive* drive, int period){ Ctlr* ctlr; int cmdport, done; ctlr = drive->ctlr; drive->command = Cstandby; qlock(ctlr); cmdport = ctlr->cmdport; ilock(ctlr); outb(cmdport+Count, period); outb(cmdport+Dh, drive->dev); ctlr->done = 0; ctlr->curdrive = drive; ctlr->command = Cstandby; /* debugging */ outb(cmdport+Cmd, Cstandby); iunlock(ctlr); while(waserror()) ; tsleep(ctlr, atadone, ctlr, 30*1000); poperror(); done = ctlr->done; qunlock(ctlr); if(!done || (drive->status & Err)) return atasetsense(drive, SDcheck, 4, 8, drive->error); return SDok;}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+Cmd, 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(drive->info[Icsfs] & Mnop) atanop(drive, 0); else{ atasrst(drive->ctlr->ctlport); drive->error |= Abrt; } if(dolock) iunlock(drive->ctlr);}static intatadmasetup(Drive* drive, int len){ Prd *prd; ulong pa; Ctlr *ctlr; int bmiba, bmisx, count, i, span; ctlr = drive->ctlr; pa = PCIWADDR(drive->data); if(pa & 0x03) return -1; /* * Sometimes drives identify themselves as being DMA capable * although they are not on a busmastering controller. */ prd = ctlr->prdt; if(prd == nil){ drive->dmactl = 0; print("h%d: disabling dma: not on a busmastering controller\n", drive->driveno); return -1; } for(i = 0; len && i < Nprd; i++){ prd->pa = pa; span = ROUNDUP(pa, ctlr->span); if(span == pa) span += ctlr->span; count = span - pa; if(count >= len){ prd->count = PrdEOT|len; break; } prd->count = count; len -= count; pa += count; prd++; } if(i == Nprd) (prd-1)->count |= PrdEOT; bmiba = ctlr->bmiba; outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); if(drive->write) outb(ctlr->bmiba+Bmicx, 0); else outb(ctlr->bmiba+Bmicx, Rwcon); bmisx = inb(bmiba+Bmisx); outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); return 0;}static voidatadmastart(Ctlr* ctlr, int write){ if(write) outb(ctlr->bmiba+Bmicx, Ssbm); else outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);}static intatadmastop(Ctlr* ctlr){ int bmiba; bmiba = ctlr->bmiba; outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm); return inb(bmiba+Bmisx);}static voidatadmainterrupt(Drive* drive, int count){ Ctlr* ctlr; int bmiba, bmisx; ctlr = drive->ctlr; bmiba = ctlr->bmiba; bmisx = inb(bmiba+Bmisx); switch(bmisx & (Ideints|Idedmae|Bmidea)){ case Bmidea: /* * Data transfer still in progress, nothing to do * (this should never happen). */ return; case Ideints: case Ideints|Bmidea: /* * Normal termination, tidy up. */ drive->data += count; break; default: /* * What's left are error conditions (memory transfer * problem) and the device is not done but the PRD is * exhausted. For both cases must somehow tell the * drive to abort. */ ataabort(drive, 0); break; } atadmastop(ctlr); ctlr->done = 1;}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, timeo; 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, 107*1000) < 0){ 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, 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+Cmd, Cpkt); if((drive->info[Iconfig] & Mdrq) != 0x0020){ microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); if(as < 0) r = SDtimeout; else if(as & Chk) r = SDcheck; else atapktinterrupt(drive); } iunlock(ctlr); while(waserror()) ; if(!drive->pktdma) sleep(ctlr, atadone, ctlr); else for(timeo = 0; !ctlr->done; timeo++){ tsleep(ctlr, atadone, ctlr, 1000); if(ctlr->done) break; ilock(ctlr); atadmainterrupt(drive, 0); if(!drive->error && timeo > 10){ ataabort(drive, 0); atadmastop(ctlr); drive->dmactl = 0; drive->error |= Abrt; } if(drive->error){ drive->status |= Chk; ctlr->curdrive = nil; } iunlock(ctlr); } poperror(); qunlock(ctlr); if(drive->status & Chk) r = SDcheck; return r;}static uchar cmd48[256] = { [Crs] Crs48, [Crd] Crd48, [Crdq] Crdq48, [Crsm] Crsm48, [Cws] Cws48, [Cwd] Cwd48, [Cwdq] Cwdq48, [Cwsm] Cwsm48,};static intatageniostart(Drive* drive, Devsize 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); if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){ if(drive->write) drive->command = Cwd; else drive->command = Crd; } else if(drive->rwmctl){ drive->block = drive->rwm*drive->secsize; if(drive->write) drive->command = Cwsm; else drive->command = Crsm; } else{ 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+Cmd, 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){ if(drive->dmactl){ drive->dmactl = 0; print("atagenioretry: disabling dma\n"); } else if(drive->rwmctl) drive->rwmctl = 0; else return atasetsense(drive, SDcheck, 4, 8, drive->error); return SDretry;}static intatagenio(Drive* drive, uchar* cmd, int){ uchar *p; Ctlr *ctlr; Devsize lba, len; int count, maxio; /* * 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; *p++ = len>>16; *p++ = len>>8; *p++ = len; len = drive->secsize; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p = len; drive->data += 8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -