📄 sdata.c
字号:
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; } 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 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); while(count){ if(count > 256) drive->count = 256; else drive->count = count; if(atageniostart(drive, lba)){ ilock(ctlr); atanop(drive, 0); iunlock(ctlr); qunlock(ctlr); return atagenioretry(drive); } while(waserror()) ; tsleep(ctlr, atageniodone, ctlr, 10*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); 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)); cmd10[0] = 0x03; cmd10[1] = r->lun<<5; cmd10[4] = sizeof(r->sense)-1; drive->data = r->sense; drive->dlen = sizeof(r->sense)-1; drive->status = 0; drive->error = 0; if(drive->pkt) reqstatus = atapktio(drive, cmd10, 6); else reqstatus = atagenio(drive, cmd10, 6); if(reqstatus == SDok){ r->flags |= SDvalidsense; atasetsense(drive, SDok, 0, 0, 0); } } qunlock(drive); r->status = status; if(status != SDok) return status; /* * Fix up any results. * Many ATAPI CD-ROMs ignore the LUN field completely and * return valid INQUIRY data. Patch the response to indicate * 'logical unit not supported' if the LUN is non-zero. */ switch(cmdp[0]){ case 0x12: /* inquiry */ if((p = r->data) == nil) break; if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) p[0] = 0x7F; /*FALLTHROUGH*/ default: break; } return SDok;}static voidatainterrupt(Ureg*, void* arg){ Ctlr *ctlr; Drive *drive; int cmdport, len, status; ctlr = arg; ilock(ctlr); if(inb(ctlr->ctlport+As) & Bsy){ iunlock(ctlr); if(DEBUG & DbgDEBUG) print("IBsy+"); return; } cmdport = ctlr->cmdport; status = inb(cmdport+Status); if((drive = ctlr->curdrive) == nil){ iunlock(ctlr); if((DEBUG & DbgDEBUG) && ctlr->command != Cedd) print("Inil%2.2uX+", ctlr->command); return; } if(status & Err) drive->error = inb(cmdport+Error); else switch(drive->command){ default: drive->error = Abrt; break; case Crs: case Crsm: if(!(status & Drq)){ drive->error = Abrt; break; } len = drive->block; if(drive->data+len > drive->limit) len = drive->limit-drive->data; inss(cmdport+Data, drive->data, len/2); drive->data += len; if(drive->data >= drive->limit) ctlr->done = 1; break; case Cws: case Cwsm: len = drive->block; if(drive->data+len > drive->limit) len = drive->limit-drive->data; drive->data += len; if(drive->data >= drive->limit){ ctlr->done = 1; break; } if(!(status & Drq)){ drive->error = Abrt; break; } len = drive->block; if(drive->data+len > drive->limit) len = drive->limit-drive->data; outss(cmdport+Data, drive->data, len/2); break; case Cpkt: atapktinterrupt(drive); break; case Crd: case Cwd: atadmainterrupt(drive, drive->count*drive->secsize); break; } iunlock(ctlr); if(drive->error){ status |= Err; ctlr->done = 1; } if(ctlr->done){ ctlr->curdrive = nil; drive->status = status; wakeup(ctlr); }}static SDev*atapnp(void){ Ctlr *ctlr; Pcidev *p; int channel, ispc87415, pi, r; SDev *legacy[2], *sdev, *head, *tail; legacy[0] = legacy[1] = head = tail = nil; if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ head = tail = sdev; legacy[0] = sdev; } if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ if(head != nil) tail->next = sdev; else head = sdev; tail = sdev; legacy[1] = sdev; } p = nil; while(p = pcimatch(p, 0, 0)){ /* * Look for devices with the correct class and sub-class * code and known device and vendor ID; add native-mode * channels to the list to be probed, save info for the * compatibility mode channels. * Note that the legacy devices should not be considered * PCI devices by the interrupt controller. * For both native and legacy, save info for busmastering * if capable. * Promise Ultra ATA/66 (PDC20262) appears to * 1) give a sub-class of 'other mass storage controller' * instead of 'IDE controller', regardless of whether it's * the only controller or not; * 2) put 0 in the programming interface byte (probably * as a consequence of 1) above). */ if(p->ccrb != 0x01 || (p->ccru != 0x01 && p->ccru != 0x80)) continue; pi = p->ccrp; ispc87415 = 0; switch((p->did<<16)|p->vid){ default: continue; case (0x0002<<16)|0x100B: /* NS PC87415 */ /* * Disable interrupts on both channels until * after they are probed for drives. * This must be called before interrupts are * enabled because the IRQ may be shared. */ ispc87415 = 1; pcicfgw32(p, 0x40, 0x00000300); break; case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */ /* * Turn off prefetch. Overkill, but cheap. */ r = pcicfgr32(p, 0x40); r &= ~0x2000; pcicfgw32(p, 0x40, r); break; case (0x4D38<<16)|0x105A: /* Promise PDC20262 */ pi = 0x85; break; case (0x0640<<16)|0x1095: /* CMD 640B */ /* * Bugfix code here... */ break; case (0x0646<<16)|0x1095: /* CMD 646 */ case (0x0571<<16)|0x1106: /* VIA 82C686 */ case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ break; } for(channel = 0; channel < 2; channel++){ if(pi & (1<<(2*channel))){ sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01, p->mem[1+2*channel].bar & ~0x01, p->intl); if(sdev == nil) continue; ctlr = sdev->ctlr; if(ispc87415) ctlr->ienable = pc87415ienable; if(head != nil) tail->next = sdev; else head = sdev; tail = sdev; ctlr->tbdf = p->tbdf; } else if((sdev = legacy[channel]) == nil) continue; else ctlr = sdev->ctlr; ctlr->pcidev = p; if(!(pi & 0x80)) continue; ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; } } return head;}static SDev*atalegacy(int port, int irq){ return ataprobe(port, port+0x204, irq);}static SDev*ataid(SDev* sdev){ int i; Ctlr *ctlr; /* * Legacy controllers are always 'C' and 'D' and if * they exist and have drives will be first in the list. * If there are no active legacy controllers, native * controllers start at 'C'. */ if(sdev == nil) return nil; ctlr = sdev->ctlr; if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170) i = 2; else i = 0; while(sdev){ if(sdev->ifc == &sdataifc){ ctlr = sdev->ctlr; if(ctlr->cmdport == 0x1F0) sdev->idno = 'C'; else if(ctlr->cmdport == 0x170) sdev->idno = 'D'; else{ sdev->idno = 'C'+i; i++; } snprint(sdev->name, NAMELEN, "sd%c", sdev->idno); } sdev = sdev->next; } return nil;}static intataenable(SDev* sdev){ Ctlr *ctlr; char name[NAMELEN]; ctlr = sdev->ctlr; if(ctlr->bmiba){ if(ctlr->pcidev != nil) pcisetbme(ctlr->pcidev); ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); } snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name); intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); outb(ctlr->ctlport+Dc, 0); if(ctlr->ienable) ctlr->ienable(ctlr); return 1;}static intatarctl(SDunit* unit, char* p, int l){ int n; Ctlr *ctlr; Drive *drive; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) return 0; drive = ctlr->drive[unit->subno]; qlock(drive); n = snprint(p, l, "config %4.4uX capabilities %4.4uX", drive->info[Iconfig], drive->info[Icapabilities]); if(drive->dma) n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX", drive->dma, drive->dmactl); if(drive->rwm) n += snprint(p+n, l-n, " rwm %ud rwmctl %ud", drive->rwm, drive->rwmctl); n += snprint(p+n, l-n, "\n"); if(!unit->changed && unit->sectors){ n += snprint(p+n, l-n, "geometry %ld %ld", unit->sectors, unit->secsize); if(drive->pkt == 0) n += snprint(p+n, l-n, " %d %d %d", drive->c, drive->h, drive->s); n += snprint(p+n, l-n, "\n"); } qunlock(drive); return n;}static intatawctl(SDunit* unit, Cmdbuf* cb){ Ctlr *ctlr; Drive *drive; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) return 0; drive = ctlr->drive[unit->subno]; qlock(drive); if(waserror()){ qunlock(drive); nexterror(); } /* * Dma and rwm control is passive at the moment, * i.e. it is assumed that the hardware is set up * correctly already either by the BIOS or when * the drive was initially identified. */ if(strcmp(cb->f[0], "dma") == 0){ if(cb->nf != 2 || drive->dma == 0) error(Ebadctl); if(strcmp(cb->f[1], "on") == 0) drive->dmactl = drive->dma; else if(strcmp(cb->f[1], "off") == 0) drive->dmactl = 0; else error(Ebadctl); } else if(strcmp(cb->f[0], "rwm") == 0){ if(cb->nf != 2 || drive->rwm == 0) error(Ebadctl); if(strcmp(cb->f[1], "on") == 0) drive->rwmctl = drive->rwm; else if(strcmp(cb->f[1], "off") == 0) drive->rwmctl = 0; else error(Ebadctl); } else error(Ebadctl); qunlock(drive); poperror(); return 0;}SDifc sdataifc = { "ata", /* name */ atapnp, /* pnp */ atalegacy, /* legacy */ ataid, /* id */ ataenable, /* enable */ nil, /* disable */ scsiverify, /* verify */ scsionline, /* online */ atario, /* rio */ atarctl, /* rctl */ atawctl, /* wctl */ scsibio, /* bio */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -