/* * fileserver mv50xx driver. */ #include "all.h" #include "io.h" #include "mem.h" enum { NCtlr = 2, NCtlrdrv = 8, NDrive = NCtlr*NCtlrdrv, Read = 0, Write, }; #define dprint(...) //print(__VA_ARGS__) #define idprint(...) #define ioprint(...) enum { Rbufps = RBUFSIZE/512, /* Addresses of ATA register */ ARcmd = 027, ARdev = 026, ARerr = 021, ARfea = 021, ARlba2 = 025, ARlba1 = 024, ARlba0 = 023, ARseccnt = 022, ARstat = 027, ATAerr = 1<<0, ATAdrq = 1<<3, ATAdf = 1<<5, ATAdrdy = 1<<6, ATAbusy = 1<<7, ATAabort = 1<<2, ATAobs = 1<<1 | 1<<2 | 1<<4, ATAeIEN = 1<<1, ATAsrst = 1<<2, ATAhob = 1<<7, ATAbad = ATAbusy|ATAdf|ATAdrq|ATAerr, SFrun = 1<<0, SFdone = 1<<1, SFerror = 1<<2, SFretry = 1<<3, PRDeot = 1<<15, /* EDMA interrupt error cause register */ ePrtDataErr = 1<<0, ePrtPRDErr = 1<<1, eDevErr = 1<<2, eDevDis = 1<<3, eDevCon = 1<<4, eOverrun = 1<<5, eUnderrun = 1<<6, eSelfDis = 1<<8, ePrtCRQBErr = 1<<9, ePrtCRPBErr = 1<<10, ePrtIntErr = 1<<11, eIORdyErr = 1<<12, // flags for sata 2 version eSelfDis2 = 1<<7, SerrInt = 1<<5, /* EDMA Command Register */ eEnEDMA = 1<<0, eDsEDMA = 1<<1, eAtaRst = 1<<2, /* Interrupt mask for errors we care about */ IEM = (eDevDis | eDevCon | eSelfDis), IEM2 = (eDevDis | eDevCon | eSelfDis2), /* drive states */ Dnull = 0, Dnew, Dready, Derror, Dmissing, Dreset, Dlast, /* drive flags */ Dllba = 1<<0, // phyerrata magic crap Mpreamp = 0x7e0, Dpreamp = 0x720, REV60X1B2 = 0x7, REV60X1C0 = 0x9, }; static char* diskstates[Dlast] = { "null", "new", "ready", "error", "missing", }; typedef struct Ctlr Ctlr; typedef struct{ ulong status; ulong serror; ulong sctrl; ulong phyctrl; ulong phymode3; ulong phymode4; uchar fill0[0x14]; ulong phymode1; ulong phymode2; char fill1[8]; ulong ctrl; char fill2[0x34]; ulong phymode; char fill3[0x88]; }Bridge; // must be 0x100 hex in length typedef struct{ ulong config; /* satahc configuration register (sata2 only) */ ulong rqop; /* request queue out-pointer */ ulong rqip; /* response queue in pointer */ ulong ict; /* inerrupt caolescing threshold */ ulong itt; /* interrupt timer threshold */ ulong ic; /* interrupt cause */ ulong btc; /* bridges test control */ ulong bts; /* bridges test status */ ulong bpc; /* bridges pin configuration */ char fill1[0xdc]; Bridge bridge[4]; }Arb; typedef struct{ ulong config; /* configuration register */ ulong timer; ulong iec; /* interrupt error cause */ ulong iem; /* interrupt error mask */ ulong txbasehi; /* request queue base address high */ ulong txi; /* request queue in pointer */ ulong txo; /* request queue out pointer */ ulong rxbasehi; /* response queue base address high */ ulong rxi; /* response queue in pointer */ ulong rxo; /* response queue out pointer */ ulong ctl; /* command register */ ulong testctl; /* test control */ ulong status; ulong iordyto; /* IORDY timeout */ char fill[0x18]; ulong sataconfig; /* sata 2 */ char fill[0xac]; ushort pio; /* data register */ char pad0[2]; uchar err; /* features and error */ char pad1[3]; uchar seccnt; /* sector count */ char pad2[3]; uchar lba0; char pad3[3]; uchar lba1; char pad4[3]; uchar lba2; char pad5[3]; uchar lba3; char pad6[3]; uchar cmdstat; /* cmd/status */ char pad7[3]; uchar altstat; /* alternate status */ uchar fill2[0x1df]; Bridge port; char fill3[0x1c00]; /* pad to 0x2000 bytes */ }Edma; // there are 4 drives per chip. thus an 8-port // card has two chips. typedef struct{ Arb *arb; Edma *edma; }Chip; typedef struct{ ulong pa; /* byte address of physical memory */ ushort count; /* byte count (bit0 must be 0) */ ushort flag; ulong zero; /* high long of 64 bit address */ ulong reserved; }Prd; typedef struct{ ulong prdpa; /* physical region descriptor table structures */ ulong zero; /* must be zero (high long of prd address) */ ushort flag; /* control flags */ ushort regs[11]; }Tx; typedef struct{ ushort cid; /* cID of response */ uchar cEdmaSts; /* EDMA status */ uchar cDevSts; /* status from disk */ ulong ts; /* time stamp */ }Rx; enum{ DMautoneg, DMsatai, DMsataii, }; typedef struct{ Lock; Ctlr *ctlr; ulong magic; Bridge *bridge; Edma *edma; Chip *chip; int chipx; int state; int flag; uvlong sectors; ulong pm2; // phymode 2 init state ulong intick; // check for hung westerdigital drives. int wait; int mode; // DMautoneg, satai or sataii. char serial[20+1]; char firmware[8+1]; char model[40+1]; ushort info[256]; Prd *prd; Tx *tx; Rx *rx; QLock; Rendez; int cmdflag; int driveno; /* ctlr*NCtlrdrv + unit */ int fflag; Filter rate[2]; int init; }Drive; #pragma varargck type "m" Drive* struct Ctlr{ Lock; int irq; int tbdf; int rid; int type; Pcidev *pcidev; uchar *mmio; ulong *lmmio; Chip chip[2]; int nchip; Drive drive[NCtlrdrv]; int ndrive; }; static Ctlr mvctlr[NCtlr]; static int nmvctlr; static ushort gbit16(void *a) { uchar *i; ushort j; i = a; j = i[1]<<8; j |= i[0]; return j; } static u32int gbit32(void *a) { uchar *i; u32int j; i = a; j = i[3]<<24; j |= i[2]<<16; j |= i[1]<<8; j |= i[0]; return j; } static uvlong gbit64(void *a) { uchar *i; i = a; return (uvlong) gbit32(i+4)<<32|gbit32(a); } static void idmove(char *p, ushort *a, int n) { char *op, *e; int i; op = p; for(i = 0; i < n/2; i++){ *p++ = a[i]>>8; *p++ = a[i]; } *p = 0; while(p > op && *--p == ' ') *p = 0; e = p; p = op; while(*p == ' ') p++; memmove(op, p, n-(e-p)); } /* * Wait for a byte to be a particular value. */ static int satawait(volatile uchar *p, uchar mask, uchar v, int ms) { int i; for(i=0; ictlr->type == 1) return; microdelay(200); n = d->bridge->phymode2; while ((n & BadAutoCal) == BadAutoCal) { dprint("%m: badautocal\n", d); n &= ~(1<<16); n |= (1<<31); d->bridge->phymode2 = n; microdelay(200); d->bridge->phymode2 &= ~((1<<16) | (1<<31)); microdelay(200); n = d->bridge->phymode2; } n &= ~(1<<31); d->bridge->phymode2 = n; microdelay(200); /* abra cadabra! (random magic) */ m = d->bridge->phymode3; m &= ~0x7f800000; m |= 0x2a800000; d->bridge->phymode3 = m; /* fix phy mode 4 */ m = d->bridge->phymode3; n = d->bridge->phymode4; n &= ~(1<<1); n |= 1; switch(d->ctlr->rid){ case REV60X1B2: default: d->bridge->phymode4 = n; d->bridge->phymode3 = m; break; case REV60X1C0: d->bridge->phymode4 = n; break; } /* revert values of pre-emphasis and signal amps to the saved ones */ n = d->bridge->phymode2; n &= ~Mpreamp; n |= d->pm2; n &= ~(1<<16); d->bridge->phymode2 = n; } static void edmacleanout(Drive *d) { if(d->cmdflag&SFrun){ d->cmdflag |= SFerror|SFdone; wakeup(d); } } static void resetdisk(Drive *d) { ulong n; dprint("%m: resetdisk\n", d); d->sectors = 0; if (d->ctlr->type == 2) { // without bit 8 we can boot without disks, but // inserted disks will never appear. :-X n = d->edma->sataconfig; n &= 0xff; n |= 0x9b1100; d->edma->sataconfig = n; n = d->edma->sataconfig; //flush USED(n); } d->edma->ctl = eDsEDMA; microdelay(1); d->edma->ctl = eAtaRst; microdelay(25); d->edma->ctl = 0; if (satawait((uchar *)&d->edma->ctl, eEnEDMA, 0, 3*1000) == 0) print("%m: eEnEDMA never cleared on reset\n", d); edmacleanout(d); phyerrata(d); d->bridge->sctrl = 0x301 | (d->mode << 4); d->state = Dmissing; } static void edmainit(Drive *d) { if(d->tx != nil) return; d->tx = ialloc(32*sizeof(Tx), 1024); d->rx = ialloc(32*sizeof(Rx), 256); d->prd = ialloc(32*sizeof(Prd), 32); for(int i = 0; i < 32; i++) d->tx[i].prdpa = PADDR(d->prd+i); coherence(); } static int configdrive(Ctlr *ctlr, Drive *d) { dprint("%m: configdrive\n", d); edmainit(d); d->mode = DMsatai; if(d->ctlr->type == 1){ d->edma->iem = IEM; d->bridge = &d->chip->arb->bridge[d->chipx]; }else{ d->edma->iem = IEM2; d->bridge = &d->chip->edma[d->chipx].port; d->edma->iem = ~(1<<6); d->pm2 = Dpreamp; if(d->ctlr->lmmio[0x180d8/4] & 1) d->pm2 = d->bridge->phymode2 & Mpreamp; } resetdisk(d); unmask(ctlr->lmmio, d->driveno, 0); delay(100); if(d->bridge->status){ dprint("\t" "bridge %lx\n", d->bridge->status); delay(1400); // don't burn out the power supply. } return 0; } static int enabledrive(Drive *d) { Edma *edma; dprint("%m: enabledrive..", d); if((d->bridge->status & 0xf) != 3){ dprint("%m: not present\n", d); d->state = Dmissing; return -1; } edma = d->edma; if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ dprint("%m: busy timeout\n", d); d->state = Dmissing; return -1; } edma->iec = 0; d->chip->arb->ic &= ~(0x101 << d->chipx); edma->config = 0x51f; if (d->ctlr->type == 2) edma->config |= 7<<11; edma->txi = PADDR(d->tx); edma->txo = (ulong)d->tx & 0x3e0; edma->rxi = (ulong)d->rx & 0xf8; edma->rxo = PADDR(d->rx); edma->ctl |= 1; /* enable dma */ if(d->bridge->status == 0x113){ dprint("%m: new\n", d); d->state = Dnew; }else print("%m: status not forced (should be okay)\n", d); return 0; } static int setudmamode(Drive *d, uchar mode) { Edma *edma; dprint("%m: setudmamode %d\n", d, mode); edma = d->edma; if(edma == nil) panic("%m: setudamode: zero d->edma\m", d); if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 9*1000) == 0){ print("%m: cmdstat 0x%.2ux ready timeout\n", d, edma->cmdstat); return 0; } edma->altstat = ATAeIEN; edma->err = 3; edma->seccnt = 0x40 | mode; edma->cmdstat = 0xef; microdelay(1); if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ print("%m: cmdstat 0x%.2ux busy timeout\n", d, edma->cmdstat); return 0; } return 1; } static int identifydrive(Drive *d) { int i; ushort *id; Edma *edma; dprint("%m: identifydrive\n", d); if(setudmamode(d, 5) == 0) goto Error; id = d->info; memset(d->info, 0, sizeof d->info); edma = d->edma; if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 5*1000) == 0) goto Error; edma->altstat = ATAeIEN; /* no interrupts */ edma->cmdstat = 0xec; microdelay(1); if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0) goto Error; for(i = 0; i < 256; i++) id[i] = edma->pio; if(edma->cmdstat & ATAbad) goto Error; i = gbit16(id+83) | gbit16(id+86); if(i & (1<<10)){ d->flag |= Dllba; d->sectors = gbit64(id+100); }else{ d->flag &= ~Dllba; d->sectors = gbit32(id+60); } idmove(d->serial, id+10, 20); idmove(d->firmware, id+23, 8); idmove(d->model, id+27, 40); if(enabledrive(d) == 0) { d->state = Dready; print("%m: LLBA %lld sectors\n", d, d->sectors); return 0; } d->state = Derror; return -1; Error: dprint("error..."); d->state = Derror; return -1; } /* p. 163: M recovered error P protocol error N PhyRdy change W CommWake B 8-to-10 encoding error D disparity error C crc error H handshake error S link sequence error T transport state transition error F unrecognized fis type X device changed */ static char stab[] = { [1] 'M', [10] 'P', [16] 'N', [18] 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X' }; static ulong sbad = (7<<20)|(3<<23); static void serrdecode(ulong r, char *s, char *e) { int i; e -=3; for(i = 0; i < nelem(stab) && s < e; i++){ if((r&(1<edma; if((edma->ctl&eEnEDMA) == 0){ // FEr SATA#4 50xx cause = edma->cmdstat; USED(cause); } cause = edma->iec; if(cause == 0) return; dprint("%m: cause %08ulx [%s]\n", d, cause, iecdecode(cause)); if(cause & eDevCon) d->state = Dnew; if(cause&eDevDis && d->state == Dready) print("%m: pulled: st=%08ulx\n", d, cause); switch(d->ctlr->type){ case 1: if(cause&eSelfDis) d->state = Derror; break; case 2: if(cause&Cerror) d->state = Derror; if(cause&SerrInt){ serrdecode(d->bridge->serror, buf, buf+sizeof buf); dprint("%m: serror %08ulx [%s]\n", d, (ulong)d->bridge->serror, buf); d->bridge->serror = d->bridge->serror; } } edma->iec = ~cause; } #define Cmd(r, v) (((r)<<8) | ((v)&0xff)) static void mvsatarequest(ushort *cmd, int atacmd, uvlong lba, int sectors, int llba) { *cmd++ = Cmd(ARseccnt, 0); *cmd++ = Cmd(ARseccnt, sectors); *cmd++ = Cmd(ARfea, 0); if(llba){ *cmd++ = Cmd(ARlba0, lba>>24); *cmd++ = Cmd(ARlba0, lba); *cmd++ = Cmd(ARlba1, lba>>32); *cmd++ = Cmd(ARlba1, lba>>8); *cmd++ = Cmd(ARlba2, lba>>40); *cmd++ = Cmd(ARlba2, lba>>16); *cmd++ = Cmd(ARdev, 0xe0); }else{ *cmd++ = Cmd(ARlba0, lba); *cmd++ = Cmd(ARlba1, lba>>8); *cmd++ = Cmd(ARlba2, lba>>16); *cmd++ = Cmd(ARdev, lba>>24 | 0xe0); } *cmd = Cmd(ARcmd, atacmd) | 1<<15; } static uintptr advance(uintptr pa, int shift) { int n, mask; mask = 0x1F<intick = Ticks; edma = d->edma; tx = (Tx*)KADDR(edma->txi); tx->flag = (write == 0); prd = KADDR(tx->prdpa); prd->pa = PADDR(data); prd->zero = 0; prd->count = sectors*512; prd->flag = PRDeot; mvsatarequest(tx->regs, atacmd, lba, sectors, ext); coherence(); edma->txi = advance(edma->txi, 5); } #define Rpidx(rpreg) (((rpreg)>>3) & 0x1f) static void complete(Drive *d) { Edma *edma; Rx *rx; edma = d->edma; if((edma->ctl & eEnEDMA) == 0) return; // if(Rpidx(edma->rxi) == Rpidx(edma->rxo)) // return; rx = (Rx*)KADDR(edma->rxo); if(rx->cDevSts & ATAbad) d->cmdflag |= SFerror; if (rx->cEdmaSts) print("cEdmaSts: %02ux\n", rx->cEdmaSts); d->cmdflag |= SFdone; edma->rxo = advance(edma->rxo, 3); ioprint("%m: complete: wakeup\n", d); wakeup(d); } static int done(void *v) { Drive *d; d = v; return d->cmdflag&SFdone; } static void mv50interrupt(Ureg*, void *a) { int i; ulong cause; Ctlr *c; Drive *d; c = a; ilock(c); cause = c->lmmio[0x1d60/4]; for(i = 0; i < c->ndrive; i++) if(cause & (3<<(i*2+i/4))){ d = c->drive+i; // print("%m: int: %lux\n", d, cause); ilock(d); updatedrive(d); while(c->chip[i/4].arb->ic & (0x0101 << (i%4))){ c->chip[i/4].arb->ic = ~(0x101 << (i%4)); complete(d); } iunlock(d); } iunlock(c); } enum{ Nms = 512, Midwait = 16*1024/Nms-1, Mphywait = 512/Nms-1, }; static void westerndigitalhung(Drive *d) { if(d->cmdflag&SFrun) if(TK2MS(Ticks-d->intick) > 5*1000){ dprint("%m: westerndigital drive hung; resetting\n", d); d->state = Dreset; } } static void checkdrive(Drive *d, int i) { static ulong s, olds[NCtlr*NCtlrdrv]; ilock(d); s = d->bridge->status; if(s != olds[i]){ dprint("%m: status: %08lx -> %08lx: %s\n", d, olds[i], s, diskstates[d->state]); olds[i] = s; } // westerndigitalhung(d); switch(d->state){ case Dnew: case Dmissing: switch(s){ case 0x000: case 0x023: case 0x013: break; default: dprint("%m: unknown state %8lx\n", d, s); case 0x100: if(++d->wait&Mphywait) break; reset: d->mode ^= 1; dprint("%m: reset; new mode %d\n", d, d->mode); resetdisk(d); break; case 0x123: case 0x113: s = d->edma->cmdstat; if(s == 0x7f || (s&~ATAobs) != ATAdrdy){ if((++d->wait&Midwait) == 0) goto reset; }else if(identifydrive(d) == -1) goto reset; } break; case Dready: if(s != 0) break; print("%m: pulled: st=%08ulx\n", d, s); case Dreset: case Derror: dprint("%m: reset: mode %d\n", d, d->mode); resetdisk(d); break; } iunlock(d); } static void satakproc(void) { int i, j; Drive *d; Ctlr *c; static Rendez r; for(;;){ tsleep(&r, no, 0, Nms); for(i = 0; i < nmvctlr; i++){ c = mvctlr+i; for(j = 0; j < c->ndrive; j++){ d = c->drive+j; checkdrive(d, i*NCtlrdrv+j); } } } } void mv50pnp(void) { int i, nunit; uchar *base; ulong io, n, *mem; Ctlr *ctlr; Pcidev *p; Drive *d; dprint("mv50pnp\n"); p = nil; while((p = pcimatch(p, 0x11ab, 0)) != nil){ switch(p->did){ case 0x5040: case 0x5041: case 0x5080: case 0x5081: case 0x6041: case 0x6081: break; default: print("mv50pnp: unknown did %ux ignored\n", (ushort)p->did); continue; } if(nmvctlr == NCtlr){ print("mv50pnp: too many controllers\n"); break; } nunit = (p->did&0xf0) >> 4; print("marvell 88sx%ux: %d sata-%s ports with%s flash, irq %d\n", (ushort)p->did, nunit, ((p->did&0xf000)==0x6000? "ii": "i"), (p->did&1? "": "out"), p->intl); ctlr = mvctlr+nmvctlr; memset(ctlr, 0, sizeof *ctlr); io = p->mem[0].bar & ~0x0F; mem = (ulong*)vmap(io, p->mem[0].size); if(mem == 0){ print("mv50xx: address 0x%luX in use\n", io); continue; } ctlr->rid = p->rid; ctlr->type = (p->did >> 12) & 3; // avert thine eyes! (what does this do?) mem[0x104f0/4] = 0; // disable r/w combining if(ctlr->type == 1){ n = mem[0xc00/4]; n &= ~(3<<4); mem[0xc00/4] = n; } setvec(p->intl+24, mv50interrupt, ctlr); pcisetbme(p); ctlr->irq = p->intl; ctlr->tbdf = p->tbdf; ctlr->pcidev = p; ctlr->lmmio = mem; ctlr->mmio = (uchar*)mem; ctlr->nchip = (nunit+3)/4; ctlr->ndrive = nunit; for(i = 0; i < ctlr->nchip; i++){ base = ctlr->mmio+0x20000+0x10000*i; ctlr->chip[i].arb = (Arb*)base; if(ctlr->type == 2) ctlr->chip[i].arb->config |= 0xf<<24; ctlr->chip[i].edma = (Edma*)(base + 0x2000); } for (i = 0; i < nunit; i++) { d = &ctlr->drive[i]; memset(d, 0, sizeof *d); d->sectors = 0; d->ctlr = ctlr; d->driveno = nmvctlr*NCtlrdrv + i; d->chipx = i%4; d->chip =&ctlr->chip[i/4]; d->edma = &d->chip->edma[d->chipx]; configdrive(ctlr, d); } nmvctlr++; } userinit(satakproc, 0, "mvsata"); } static int waitready(Drive *d) { ulong s, i; Rendez r; for(i = 0; i < 120; i++){ ilock(d); s = d->bridge->status; iunlock(d); if(s == 0) return -1; if (d->state == Dready) if((d->edma->ctl&eEnEDMA) != 0) return 0; if ((i+1)%60 == 0){ ilock(d); resetdisk(d); iunlock(d); } memset(&r, 0, sizeof r); tsleep(&r, no, 0, 1000); } print("%m: not responding after 2 minutes\n", d); return -1; } static uint getatacmd(int write, int e) { static uchar cmd[2][2] = { 0xC8, 0x25, 0xCA, 0x35 }; return cmd[write][e!=0]; } static int rw(Drive *d, int write, uchar *db, long len, uvlong lba) { uchar *data; char *msg; uint count, try, ext, atacmd; ext = d->flag&Dllba; atacmd = getatacmd(write, ext); data = db; if(len > 64*1024) len = 64*1024; count = len/512; msg = "%m: bad disk\n"; qlock(d); for(try = 0; try < 10; try++){ if(waitready(d) == -1){ qunlock(d); return -1; } start(d, write, atacmd, data, lba, count, ext); d->cmdflag = SFrun; sleep(d, done, d); d->cmdflag &= ~SFrun; if(d->cmdflag&SFretry){ dprint("%m: retry\n", d); tsleep(d, no, 0, 1000); continue; } if(d->cmdflag&SFerror){ msg = "%m: i/o error\n"; break; } qunlock(d); return count*512; } qunlock(d); print(msg, d); return -1; } static int fmtm(Fmt *f) { Drive *d; char buf[8]; d = va_arg(f->args, Drive*); if(d == nil) snprint(buf, sizeof buf, "m[nil]"); else snprint(buf, sizeof buf, "m%d", d->driveno); return fmtstrcpy(f, buf); } static Drive* mvdev(Device *d) { int i, j; Drive *dr; i = d->wren.ctrl; j = d->wren.targ; for(; i < nmvctlr; i++){ if(j < mvctlr[i].ndrive){ dr = mvctlr[i].drive+j; if(dr->state&Dready) return dr; return 0; } j -= mvctlr[i].ndrive; } panic("mv: bad drive %Z\n", d); return 0; } static void cmd_stat(int, char*[]) { Ctlr *c; Drive *d; int i, j; for(i = 0; i < nmvctlr; i++){ c = mvctlr+i; for(j = 0; j < c->ndrive; j++){ d = c->drive+j; if(d->fflag == 0) continue; print("%m:\n", d); print(" r\t%W\n", d->rate+Read); print(" w\t%W\n", d->rate+Write); } } } void mvinit0(void) { fmtinstall('m', fmtm); mv50pnp(); if(nmvctlr > 0){ cmd_install("statm", "-- marvell sata stats", cmd_stat); } } void mvinit(Device *dv) { Drive *d; vlong s; char *lba; static int once; if(once++ == 0) mvinit0(); top: d = mvdev(dv); if(d == 0){ print("\t\t" "%d.%d.%d not ready yet\n", dv->wren.ctrl, dv->wren.targ, dv->wren.lun); delay(500); goto top; } if(d->init++== 0){ dofilter(d->rate+Read); dofilter(d->rate+Write); } s = d->sectors; lba = ""; if(d->flag&Dllba) lba = "L"; print("\t\t" "%lld sectors/%lld blocks %sLBA\n", s, s/Rbufps, lba); } Devsize mvsize(Device *dv) { Drive *d; d = mvdev(dv); if(d == 0) return 0; dprint("%m: mvsize %lld\n", d, d->sectors/(RBUFSIZE/512)); return d->sectors/(RBUFSIZE/512); } typedef struct{ QLock; uchar *buf; uvlong lba; }Cbuf; Cbuf cbuftab[NDrive]; enum{ Cbad = 1LL<<63 }; Cbuf* getcbuf(int i) { Cbuf *c; c = cbuftab+i; qlock(c); if(c->buf == 0){ c->buf = ialloc(64*1024, 0); c->lba = Cbad; } return c; } void putcbuf(Cbuf *c) { qunlock(c); } static int crw0(Drive *d, int write, uchar *buf, uvlong lba, Cbuf *c) { uvlong off; int rv; if(write == 0){ if(lba >= c->lba && lba < c->lba+128){ off = lba-c->lba; memmove(buf, c->buf+off*512, 8192); return 8192; } if(lba+128 > d->sectors) return rw(d, 0, buf, 8192, lba); rv = rw(d, 0, c->buf, 64*1024, lba); if(rv != 64*1024){ c->lba = Cbad; return rv; } memmove(buf, c->buf, 8192); c->lba = lba; return 8192; }else{ if(lba >= c->lba && lba < c->lba+128) c->lba = Cbad; return rw(d, 1, buf, 8192, lba); } } static int crw(Drive *d, int write, uchar *buf, uvlong lba) { Cbuf *c; int rv; c = getcbuf(d->driveno); rv = crw0(d, write, buf, lba, c); putcbuf(c); return rv; } int mvread(Device *dv, Devsize b, void *c) { Drive *d; int rv; d = mvdev(dv); if(d == 0) return 1; // rv = rw(d, 0, c, RBUFSIZE, b*Rbufps); rv = crw(d, 0, c, b*Rbufps); if(rv != RBUFSIZE) return 1; d->rate[Read].count++; d->fflag = 1; return 0; } int mvwrite(Device *dv, Devsize b, void *c) { Drive *d; int rv; d = mvdev(dv); if(d == 0) return 1; // rv = rw(d, 1, c, RBUFSIZE, b*Rbufps); rv = crw(d, 1, c, b*Rbufps); if(rv != RBUFSIZE) return 1; d->rate[Write].count++; d->fflag = 1; return 0; }