📄 sdata.c
字号:
} return dma;}static intataidentify(int cmdport, int ctlport, int dev, int pkt, void* info){ int as, command, drdy; if(pkt){ command = Cidpkt; drdy = 0; } else{ command = Cid; drdy = Drdy; } as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); if(as < 0) return as; outb(cmdport+Command, command); microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); if(as < 0) return -1; if(as & Err) return as; memset(info, 0, 512); inss(cmdport+Data, info, 256); inb(cmdport+Status); if(DEBUG & DbgIDENTIFY){ int i; ushort *sp; sp = (ushort*)info; for(i = 0; i < 256; i++){ if(i && (i%16) == 0) print("\n"); print(" %4.4uX", *sp); sp++; } print("\n"); } return 0;}static Drive*atadrive(int cmdport, int ctlport, int dev){ Drive *drive; int as, i, pkt; uchar buf[512], *p; ushort iconfig, *sp; atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); pkt = 1;retry: as = ataidentify(cmdport, ctlport, dev, pkt, buf); if(as < 0) return nil; if(as & Err){ if(pkt == 0) return nil; pkt = 0; goto retry; } if((drive = malloc(sizeof(Drive))) == nil) return nil; drive->dev = dev; memmove(drive->info, buf, sizeof(drive->info)); drive->sense[0] = 0x70; drive->sense[7] = sizeof(drive->sense)-7; drive->inquiry[2] = 2; drive->inquiry[3] = 2; drive->inquiry[4] = sizeof(drive->inquiry)-4; p = &drive->inquiry[8]; sp = &drive->info[Imodel]; for(i = 0; i < 20; i++){ *p++ = *sp>>8; *p++ = *sp++; } drive->secsize = 512; /* * Beware the CompactFlash Association feature set. * Now, why this value in Iconfig just walks all over the bit * definitions used in the other parts of the ATA/ATAPI standards * is a mystery and a sign of true stupidity on someone's part. * Anyway, the standard says if this value is 0x848A then it's * CompactFlash and it's NOT a packet device. */ iconfig = drive->info[Iconfig]; if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ if(iconfig & 0x01) drive->pkt = 16; else drive->pkt = 12; } else{ if(drive->info[Ivalid] & 0x0001){ drive->c = drive->info[Iccyl]; drive->h = drive->info[Ichead]; drive->s = drive->info[Icsec]; } else{ drive->c = drive->info[Ilcyl]; drive->h = drive->info[Ilhead]; drive->s = drive->info[Ilsec]; } if(drive->info[Icapabilities] & Mlba){ if(drive->info[Icsfs+1] & Maddr48){ drive->sectors = drive->info[Ilba48] | (drive->info[Ilba48+1]<<16) | ((vlong)drive->info[Ilba48+2]<<32); drive->flags |= Lba48; } else{ drive->sectors = (drive->info[Ilba+1]<<16) |drive->info[Ilba]; } drive->dev |= Lba; } else drive->sectors = drive->c*drive->h*drive->s; atarwmmode(drive, cmdport, ctlport, dev); } atadmamode(drive); if(DEBUG & DbgCONFIG){ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", dev, cmdport, iconfig, drive->info[Icapabilities]); print(" mwdma %4.4uX", drive->info[Imwdma]); if(drive->info[Ivalid] & 0x04) print(" udma %4.4uX", drive->info[Iudma]); print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm); if(drive->flags&Lba48) print("\tLLBA sectors %lld", drive->sectors); 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; static int nonlegacy = 'C'; if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { print("ataprobe: Cannot allocate %X\n", cmdport); return nil; } if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ print("ataprobe: Cannot allocate %X\n", ctlport + As); 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; } memset(ctlr, 0, sizeof(Ctlr)); if((sdev = malloc(sizeof(SDev))) == nil){ free(ctlr); free(drive); goto release; } memset(sdev, 0, sizeof(SDev)); 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 */ switch(cmdport){ default: sdev->idno = nonlegacy; break; case 0x1F0: sdev->idno = 'C'; nonlegacy = 'E'; break; case 0x170: sdev->idno = 'D'; nonlegacy = 'E'; break; } sdev->ifc = &sdataifc; sdev->ctlr = ctlr; sdev->nunit = 2; ctlr->sdev = sdev; return sdev;}static voidataclear(SDev *sdev){ Ctlr* ctlr; ctlr = sdev->ctlr; iofree(ctlr->cmdport); iofree(ctlr->ctlport + As); if (ctlr->drive[0]) free(ctlr->drive[0]); if (ctlr->drive[1]) free(ctlr->drive[1]); if (sdev->name) free(sdev->name); if (sdev->unitflg) free(sdev->unitflg); if (sdev->unit) free(sdev->unit); free(ctlr); free(sdev);}static char *atastat(SDev *sdev, char *p, char *e){ Ctlr *ctlr = sdev->ctlr; return seprint(p, e, "%s ata port %X ctl %X irq %d\n", sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);}static SDev*ataprobew(DevConf *cf){ char *p; ISAConf isa; if (cf->nports != 2) error(Ebadarg); memset(&isa, 0, sizeof isa); isa.port = cf->ports[0].port; isa.irq = cf->intnum; if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0) error("cannot find controller"); return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);}/* * These are duplicated with sdsetsense, etc., in devsd.c, but * those assume that the disk is not SCSI while in fact here * ata drives are not SCSI but ATAPI ones kind of are. */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 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+Command, Cstandby); iunlock(ctlr); while(waserror()) ; tsleep(ctlr, atadone, ctlr, 60*1000); poperror(); done = ctlr->done; qunlock(ctlr); if(!done || (drive->status & Err)) return atasetsense(drive, SDcheck, 4, 8, drive->error); 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(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("disabling dma: not on a busmastering controller\n"); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -