📄 sdata.c
字号:
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); as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000); /* used to test as&Chk as failure too, but some CD readers use that for media change */ if(as < 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+Command, Cpkt); if((drive->info[Iconfig] & Mdrq) != 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); 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 > 20){ 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, 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); 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+Command, cmd); switch(drive->command){ case Cws: case Cwsm: microdelay(1); /* 10*1000 for flash ide drives - maybe detect them? */ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*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; vlong 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; return SDok; case 0x9E: /* long 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>>56; *p++ = len>>48; *p++ = len>>40; *p++ = len>>32; *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 += 12; return SDok; case 0x28: /* read */ case 0x2A: /* write */ break; case 0x5A: return atamodesense(drive, cmd); } ctlr = drive->ctlr; lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; count = (cmd[7]<<8)|cmd[8]; if(drive->data == nil) return SDok; if(drive->dlen < count*drive->secsize) count = drive->dlen/drive->secsize; qlock(ctlr); if(ctlr->maxio) maxio = ctlr->maxio; else if(drive->flags & Lba48) maxio = 65536; else maxio = 256; while(count){ if(count > maxio) drive->count = maxio; else drive->count = count; if(atageniostart(drive, lba)){ ilock(ctlr); atanop(drive, 0); iunlock(ctlr); qunlock(ctlr); return atagenioretry(drive); } while(waserror()) ; tsleep(ctlr, atadone, ctlr, 60*1000); poperror(); if(!ctlr->done){ /* * What should the above timeout be? In * standby and sleep modes it could take as * long as 30 seconds for a drive to respond. * Very hard to get out of this cleanly. */ atadumpstate(drive, cmd, lba, count); ataabort(drive, 1); qunlock(ctlr); return atagenioretry(drive); } if(drive->status & Err){ qunlock(ctlr); return atasetsense(drive, SDcheck, 4, 8, drive->error); } count -= drive->count; lba += drive->count; } qunlock(ctlr); return SDok;}static intatario(SDreq* r){ Ctlr *ctlr; Drive *drive; SDunit *unit; uchar cmd10[10], *cmdp, *p; int clen, reqstatus, status; unit = r->unit; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ r->status = SDtimeout; return SDtimeout; } drive = ctlr->drive[unit->subno]; /* * Most SCSI commands can be passed unchanged except for * the padding on the end. The few which require munging * are not used internally. Mode select/sense(6) could be * converted to the 10-byte form but it's not worth the * effort. Read/write(6) are easy. */ switch(r->cmd[0]){ case 0x08: /* read */ case 0x0A: /* write */ cmdp = cmd10; memset(cmdp, 0, sizeof(cmd10)); cmdp[0] = r->cmd[0]|0x20; cmdp[1] = r->cmd[1] & 0xE0; cmdp[5] = r->cmd[3]; cmdp[4] = r->cmd[2]; cmdp[3] = r->cmd[1] & 0x0F; cmdp[8] = r->cmd[4]; clen = sizeof(cmd10); break; default: cmdp = r->cmd; clen = r->clen; break; } qlock(drive);retry: drive->write = r->write; drive->data = r->data; drive->dlen = r->dlen; drive->status = 0; drive->error = 0; if(drive->pkt) status = atapktio(drive, cmdp, clen); else status = atagenio(drive, cmdp, clen); if(status == SDretry){ if(DbgDEBUG) print("%s: retry: dma %8.8uX rwm %4.4uX\n", unit->name, drive->dmactl, drive->rwmctl); goto retry; } if(status == SDok){ atasetsense(drive, SDok, 0, 0, 0); if(drive->data){ p = r->data; r->rlen = drive->data - p; } else r->rlen = 0; } else if(status == SDcheck && !(r->flags & SDnosense)){ drive->write = 0; memset(cmd10, 0, sizeof(cmd10));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -