📄 sdata.c
字号:
} else if(dev){ outb(cmdport+Dh, dev); dev = 0; } else if(ready == 0 || (as & ready)){ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); return as; } if(micro-- <= 0){ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); break; } microdelay(1); } atadebug(cmdport, ctlport, "ataready: timeout"); return -1;}/*static intatacsf(Drive* drive, vlong csf, int supported){ ushort *info; int cmdset, i, x; if(supported) info = &drive->info[Icsfs]; else info = &drive->info[Icsfe]; for(i = 0; i < 3; i++){ x = (csf>>(16*i)) & 0xFFFF; if(x == 0) continue; cmdset = info[i]; if(cmdset == 0 || cmdset == 0xFFFF) return 0; return cmdset & x; } return 0;}*/static intatadone(void* arg){ return ((Ctlr*)arg)->done;}static intatarwmmode(Drive* drive, int cmdport, int ctlport, int dev){ int as, maxrwm, rwm; maxrwm = (drive->info[Imaxrwm] & 0xFF); if(maxrwm == 0) return 0; /* * Sometimes drives come up with the current count set * to 0; if so, set a suitable value, otherwise believe * the value in Irwm if the 0x100 bit is set. */ if(drive->info[Irwm] & 0x100) rwm = (drive->info[Irwm] & 0xFF); else rwm = 0; if(rwm == 0) rwm = maxrwm; if(rwm > 16) rwm = 16; if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0) return 0; outb(cmdport+Count, rwm); outb(cmdport+Cmd, Csm); microdelay(1); as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); inb(cmdport+Status); if(as < 0 || (as & (Df|Err))) return 0; drive->rwm = rwm; if (conf.idedma) drive->rwmctl = drive->rwm; /* FS special */ else drive->rwm = 0; return rwm;}static intatadmamode(Drive* drive){ int dma; /* * Check if any DMA mode enabled. * Assumes the BIOS has picked and enabled the best. * This is completely passive at the moment, no attempt is * made to ensure the hardware is correctly set up. */ dma = drive->info[Imwdma] & 0x0707; drive->dma = (dma>>8) & dma; if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){ dma = drive->info[Iudma] & 0x7F7F; drive->dma = (dma>>8) & dma; if(drive->dma) drive->dma |= 'U'<<16; } if (conf.idedma) drive->dmactl = drive->dma; /* FS special */ else drive->dma = 0; 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+Cmd, 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;}/* * DEBUGGING only. * write, read and verify block 1 (never used in an fs otherwise) * to see if dma and rwm actually work. * if not, turn them off, though the kernel could be corrupt by then. */static voidataverify(Drive *dp){ int n, nb, dev = dp->driveno; uchar *buf = dp->buf; if (dp->ctlr == nil) panic("ataverify: nil ctlr for drive"); atadriveprobe(dev); print("ataverify h%d...", dev); for (n = 0; n < RBUFSIZE; n++) buf[n] = n; if (ataseek(dev, RBUFSIZE) < 0) panic("ataverify: seek 1"); nb = atawrite(dev, buf, RBUFSIZE); if (nb != RBUFSIZE) print("short write of %d bytes to block 1\n", nb); else { for (n = 0; n < RBUFSIZE; n++) buf[n] = ~n; if (ataseek(dev, RBUFSIZE) < 0) panic("ataverify: seek 1"); nb = ataread(dev, buf, RBUFSIZE); if (nb != RBUFSIZE) print("short read of %d bytes to block 1\n", nb); else { for (n = 0; n < RBUFSIZE; n++) if ((uchar)buf[n] != (uchar)n) break; if (n == RBUFSIZE) { print("OK\n"); return; /* verified OK */ } print("byte comparison failed\n"); } } print("ataverify: disabling dma and rwm\n"); dp->dmactl = dp->rwmctl = 0;}static Drive*atagetdrive(int cmdport, int ctlport, int dev){ Drive *drive; int as, i, pkt, driveno; uchar buf[512], *p; ushort iconfig, *sp; driveno = (cmdport == Ctlr0cmd? 0: cmdport == Ctlr1cmd? NCtlrdrv: 2*NCtlrdrv); if (dev == Dev1) driveno++; 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; drive->driveno = -1; /* unset */ 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){ print("atagetdrive: port 0x%uX dev 0x%2.2uX: packet device\n", cmdport, dev); if(iconfig & 0x01) drive->pkt = 16; else drive->pkt = 12; } else{ if (iconfig == 0x848A) print("atagetdrive: port 0x%uX dev 0x%2.2uX: non-packet CF device\n", cmdport, dev); 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) | ((Devsize)drive->info[Ilba48+2]<<32); drive->flags |= Lba48; }else drive->sectors = (drive->info[Ilba+1]<<16) |drive->info[Ilba]; drive->dev |= Lba; drive->lba = 1; }else drive->sectors = drive->c * drive->h * drive->s; atarwmmode(drive, cmdport, ctlport, dev); } atadmamode(drive); if(DEBUG & DbgCONFIG){ print("ata h%d: dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", driveno, 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\n", drive->dma, drive->rwm); if(drive->flags&Lba48) print("\tLLBA sectors %lld\n", (Wideoff)drive->sectors); } 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 int drivenum = 0; /* hope that we probe in order */static voidupdprobe(int cmdport){ if(cmdport == Ctlr0cmd) drivenum = NCtlrdrv; else if (cmdport == Ctlr1cmd) drivenum = 2*NCtlrdrv;}static SDev*ataprobe(int cmdport, int ctlport, int irq){ Ctlr* ctlr; SDev *sdev; Drive *drive; int i, dev, error, rhi, rlo; if(cmdport == Ctlr0cmd) drivenum = 0; else if (cmdport == Ctlr1cmd) drivenum = NCtlrdrv; if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { print("ataprobe: Cannot allocate %X\n", cmdport); updprobe(cmdport); return nil; } if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ print("ataprobe: Cannot allocate %X\n", ctlport + As); iofree(cmdport); updprobe(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); updprobe(cmdport); 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+Cmd, 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 = atagetdrive(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; atactlr[drivenum/NCtlrdrv] = ctlr; atadrive[drivenum++] = drive; sdevs[drivenum/NCtlrdrv] = sdev; 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 = atagetdrive(cmdport, ctlport, Dev1); if(drive != nil){ drive->ctlr = ctlr; ctlr->drive[1] = drive; } else{ outb(cmdport+Dh, Dev0); microdelay(1); } atadrive[drivenum] = drive; } } else ctlr->drive[1] = drive; drivenum++; print("ata%d: cmd 0x%ux ctl 0x%ux irq %d\n", (drivenum-1)/NCtlrdrv, cmdport, ctlport, irq); ctlr->cmdport = cmdport; ctlr->ctlport = ctlport; ctlr->irq = irq; ctlr->tbdf = BUSUNKNOWN; ctlr->command = Cedd; /* debugging */ sdev->ifc = &sdataifc; sdev->ctlr = ctlr; sdev->nunit = NCtlrdrv; ctlr->sdev = sdev; if (0) for (i = drivenum - 2; i < drivenum; i++) if (atadrive[i]) ataverify(atadrive[i]); updprobe(cmdport); 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]); /* TODO: clear entries in atadrive[] too */ if (sdev->name) free(sdev->name); 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);}#ifndef FSstatic SDev*ataprobew(DevConf *cf){ if (cf->nports != 2) error(Ebadarg); return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);}#endifstatic intatasetsense(Drive* drive, int status, int key, int asc, int ascq)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -