From 1ab3f5e46b66d2a6220fe84fce7c6dbdbf462cd3 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 6 Dec 1995 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1995-12-06 --- pc/devata.c | 1252 ++++++++++++++++++++++++++++++++++++++++----------- pc/pci.c | 45 +- 2 files changed, 1029 insertions(+), 268 deletions(-) diff --git a/pc/devata.c b/pc/devata.c index ed147100ab74063f0424b1d8f1c0ed1283b8c767..3ec8a20c5e60dcf450f8a3a461f2e2898481cce3 100644 --- a/pc/devata.c +++ b/pc/devata.c @@ -1,3 +1,7 @@ +/* + * This has gotten a bit messy with the addition of multiple controller + * and ATAPI support; needs a rewrite before adding any 'ctl' functions. + */ #include "u.h" #include "../port/lib.h" #include "mem.h" @@ -9,6 +13,9 @@ #include "devtab.h" #define DPRINT if(0)print +#define XPRINT if(0)print +#define ILOCK(x) +#define IUNLOCK(x) typedef struct Drive Drive; typedef struct Ident Ident; @@ -19,55 +26,62 @@ typedef struct Repl Repl; enum { /* ports */ - Pbase0= 0x1F0, - Pbase1= 0x170, + Pbase0= 0x1F0, /* primary */ + Pbase1= 0x170, /* secondary */ + Pbase2= 0x1E8, /* tertiary */ + Pbase3= 0x168, /* quaternary */ Pdata= 0, /* data port (16 bits) */ Perror= 1, /* error port (read) */ - Pprecomp= 1, /* buffer mode port (write) */ + Eabort= (1<<2), + Pfeature= 1, /* buffer mode port (write) */ Pcount= 2, /* sector count port */ Psector= 3, /* sector number port */ Pcyllsb= 4, /* least significant byte cylinder # */ Pcylmsb= 5, /* most significant byte cylinder # */ Pdh= 6, /* drive/head port */ + DHmagic= 0xA0, + DHslave= 0x10, Pstatus= 7, /* status port (read) */ Sbusy= (1<<7), Sready= (1<<6), + Sdf= (1<<5), Sdrq= (1<<3), Serr= (1<<0), Pcmd= 7, /* cmd port (write) */ /* commands */ - Crecal= 0x10, + Cfirst= 0xFF, /* pseudo command for initialisation */ Cread= 0x20, Cwrite= 0x30, + Cedd= 0x90, /* execute device diagnostics */ Cident= 0xEC, - Cident2= 0xFF, /* pseudo command for post Cident interrupt */ - Csetbuf= 0xEF, - Cinitparam= 0x91, + Cident2= 0xFE, /* pseudo command for post Cident interrupt */ + Cfeature= 0xEF, - /* conner specific commands */ Cstandby= 0xE2, - Cidle= 0xE1, - Cpowerdown= 0xE3, + + Cpktcmd= 0xA0, + Cidentd= 0xA1, + Ccapacity= 0x25, + Cread2= 0x28, /* disk states */ Sspinning, Sstandby, - Sidle, - Spowerdown, - - /* something we have to or into the drive/head reg */ - DHmagic= 0xA0, /* file types */ Qdir= 0, Maxxfer= BY2PG, /* maximum transfer size/cmd */ - Npart= 8+2, /* 8 sub partitions, disk, and partition */ + Npart= 20+2, /* 8 sub partitions, disk, and partition */ Nrepl= 64, /* maximum replacement blocks */ - Hardtimeout= 4000, /* disk access timeout */ + Hardtimeout= 30000, /* disk access timeout (ms) */ + + NCtlr= 4, + NDrive= NCtlr*2, }; + #define PART(x) ((x)&0xF) #define DRIVE(x) (((x)>>4)&0x7) #define MKQID(d,p) (((d)<<4) | (p)) @@ -97,10 +111,13 @@ struct Drive QLock; Controller *cp; - int drive; - int confused; /* needs to be recalibrated (or worse) */ - int online; + uchar driveno; + uchar dh; /* DHmagic|Am-I-A-Slave */ + uchar atapi; + uchar online; + int npart; /* number of real partitions */ + int partok; Partition p[Npart]; Repl repl; ulong usetime; @@ -112,9 +129,14 @@ struct Drive int sectors; /* sectors/track */ int heads; /* heads/cyl */ long cyl; /* cylinders/drive */ + ulong lbasecs; + + uchar lba; /* true if drive has logical block addressing */ + uchar multi; /* true if drive can do multiple block xfers (unused) */ + uchar drqintr; /* ATAPI */ + ulong vers; /* ATAPI */ - char lba; /* true if drive has logical block addressing */ - char multi; /* non-zero if drive does multiple block xfers */ + int spindown; }; /* @@ -122,41 +144,61 @@ struct Drive */ struct Controller { - QLock; /* exclusive access to the controller */ + QLock* ctlrlock; /* exclusive access to the controller */ Lock reglock; /* exclusive access to the registers */ - int confused; /* needs to be recalibrated (or worse) */ int pbase; /* base port */ + uchar ctlrno; + uchar noatapi; /* * current operation */ - int cmd; /* current command */ - int lastcmd; /* debugging info */ Rendez r; /* wait here for command termination */ - char *buf; /* xfer buffer */ + uchar cmd; /* current command */ + uchar cmdblk[12]; /* ATAPI */ + int len; /* ATAPI */ + int count; /* ATAPI */ + uchar lastcmd; /* debugging info */ + uchar status; + uchar error; + uchar* buf; /* xfer buffer */ int nsecs; /* length of transfer (sectors) */ int sofar; /* sectors transferred so far */ - int status; - int error; - Drive *dp; /* drive being accessed */ + Drive* dp; /* drive being accessed */ }; -Controller *atac; -Drive *ata; -static int spindowntime; +static QLock ataprobelock; +static int ataprobedone; +static Controller *atactlr[NCtlr]; +static QLock atactlrlock[NCtlr]; +static Drive *atadrive[NDrive]; +static int spindownmask; +static int have640b = -1; +static int pbase[NCtlr] = { + Pbase0, Pbase1, Pbase2, Pbase3, +}; +static int defirq[NCtlr] = { + 14, 15, 0, 0, +}; static void ataintr(Ureg*, void*); -static long ataxfer(Drive*, Partition*, int, long, long, char*); +static long ataxfer(Drive*, Partition*, int, long, long, uchar*); static void ataident(Drive*); -static void atasetbuf(Drive*, int); +static void atafeature(Drive*, uchar); static void ataparams(Drive*); static void atapart(Drive*); static int ataprobe(Drive*, int, int, int); +static void atasleep(Controller*); + +static int isatapi(Drive*); +static long atapirwio(Chan*, char*, ulong, ulong); +static void atapipart(Drive*); +static void atapiintr(Controller*); static int -atagen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp) +atagen(Chan *c, Dirtab*, long, long s, Dir *dirp) { Qid qid; int drive; @@ -165,13 +207,15 @@ atagen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp) Partition *pp; ulong l; - USED(tab, ntab); qid.vers = 0; drive = s/Npart; s = s % Npart; - if(drive >= conf.nhard) + + if(drive >= NDrive) return -1; - dp = &ata[drive]; + if(atadrive[drive] == 0) + return 0; + dp = atadrive[drive]; if(dp->online == 0 || s >= dp->npart) return 0; @@ -185,45 +229,294 @@ atagen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp) return 1; } -void -atareset(void) +static void +cmd640b(void) { - Drive *dp; - Controller *cp; - uchar equip; - char *p; + PCIcfg* pcicfg; + uchar r50[12]; + int devno; + extern void pcicfgw8(int, int, int, int, void*, int); - equip = nvramread(0x12); - if(equip == 0) - equip = 0x10; /* the Globalyst 250 lies */ + /* + * Look for CMD640B dual PCI controllers. Amongst other + * bugs only one of the controllers can be active at a time. + * Unfortunately there's no way to tell which pair of + * controllers this is, so if one is found then all controller + * pairs are synchronised. + */ + pcicfg = malloc(sizeof(PCIcfg)); + pcicfg->vid = 0x1095; + pcicfg->did = 0x0640; + devno = 0; + while((devno = pcimatch(0, devno, pcicfg)) != -1){ + have640b = devno-1; + /* + * If one is found, make sure read-ahead is disabled on all + * drives and that the 2nd controller is enabled: + * reg 0x51: bit 7 - drive 1 read ahead disable + * bit 6 - drive 0 read ahead disable + * bit 3 - 2nd controller enable + * reg 0x57: bit 3 - drive 1 read ahead disable + * bit 2 - drive 0 read ahead disable + * Doing byte-writes to PCI configuration space is not in the + * spec... + */ + pcicfgr(0, have640b, 0, 0x50, r50, sizeof(r50)); + r50[0x01] |= 0xC8; + pcicfgw8(0, have640b, 0, 0x51, &r50[0x01], sizeof(r50[0x01])); + r50[0x07] |= 0x0C; + pcicfgw8(0, have640b, 0, 0x57, &r50[0x07], sizeof(r50[0x07])); + } + free(pcicfg); +} - ata = xalloc(2 * sizeof(Drive)); - atac = xalloc(sizeof(Controller)); +static void +rz1000(void) +{ + PCIcfg* pcicfg; + ulong r40; + int devno; - cp = atac; - cp->buf = 0; - cp->lastcmd = cp->cmd; - cp->cmd = 0; - cp->pbase = Pbase0; - setvec(ATAvec0, ataintr, 0); - - dp = ata; - if(equip & 0xf0){ - dp->drive = 0; - dp->online = 0; - dp->cp = cp; - dp++; + /* + * Look for PC-Tech RZ1000 controllers and turn off prefetch. + * This is overkill, but cheap. + */ + pcicfg = malloc(sizeof(PCIcfg)); + pcicfg->vid = 0x1042; + pcicfg->did = 0; + devno = 0; + while((devno = pcimatch(0, devno, pcicfg)) != -1){ + if(pcicfg->did != 0x1000 && pcicfg->did != 0x1001) + continue; + pcicfgr(0, devno-1, 0, 0x40, &r40, sizeof(r40)); + r40 &= ~0x2000; + pcicfgw(0, devno-1, 0, 0x40, &r40, sizeof(r40)); } - if((equip & 0x0f)){ - dp->drive = 1; - dp->online = 0; - dp->cp = cp; - dp++; + free(pcicfg); +} + +static int +atactlrwait(Controller* ctlr, uchar pdh, uchar ready, ulong ticks) +{ + int port; + uchar dh, status; + + port = ctlr->pbase; + dh = (inb(port+Pdh) & DHslave)^(pdh & DHslave); + ticks += m->ticks+1; + + do{ + status = inb(port+Pstatus); + if(status & Sbusy) + continue; + if(dh){ + outb(port+Pdh, pdh); + dh = 0; + continue; + } + if((status & ready) == ready) + return 0; + }while(m->ticks < ticks); + + DPRINT("ata%d: ctlrwait failed %uX\n", ctlr->ctlrno, status); + outb(port+Pdh, DHmagic); + return 1; +} + +static void +atadrivealloc(Controller* ctlr, int driveno, int atapi) +{ + Drive *drive; + + if((drive = xalloc(sizeof(Drive))) == 0){ + DPRINT("ata%d: can't xalloc drive0\n", ctlr->ctlrno); + return; + } + drive->cp = ctlr; + drive->driveno = driveno; + sprint(drive->vol, "hd%d", drive->driveno); + drive->dh = DHmagic; + if(driveno & 0x01) + drive->dh |= DHslave; + drive->vers = 1; + if(atapi) + drive->atapi = 1; + + atadrive[driveno] = drive; +} + +static int +atactlrprobe(int ctlrno, int irq) +{ + Controller *ctlr; + int atapi, mask, port; + uchar error, status; + + /* + * Check the existence of a controller by verifying a sensible + * value can be written to and read from the drive/head register. + * We define the primary/secondary/tertiary and quaternary controller + * port addresses to be at fixed values. + * If it's OK, allocate and initialise a Controller structure. + */ + port = pbase[ctlrno]; + outb(port+Pdh, DHmagic); + microdelay(1); + if((inb(port+Pdh) & 0xFF) != DHmagic){ + DPRINT("ata%d: DHmagic not ok\n", ctlrno); + return -1; + } + DPRINT("ata%d: DHmagic ok\n", ctlrno); + if((ctlr = xalloc(sizeof(Controller))) == 0) + return -1; + ctlr->pbase = port; + ctlr->ctlrno = ctlrno; + ctlr->lastcmd = 0xFF; + + /* + * Attempt to check the existence of drives on the controller + * by issuing a 'check device diagnostics' command. + * Issuing a device reset here would possibly destroy any BIOS + * drive remapping and, anyway, some controllers (Vibra16) don't + * seem to implement the control-block registers. + * Unfortunately the vector must be set at this point as the Cedd + * command will generate an interrupt, which means the ataintr routine + * will be left on the interrupt call chain even if there are no + * drives found. + * At least one controller/ATAPI-drive combination doesn't respond + * to the Edd (Micronics M54Li + Sanyo CDR-XXX) so let's check for the + * ATAPI signature straight off. If we find it there will be no probe + * done for a slave. Tough. + */ + atapi = 0; + mask = 0; + status = inb(port+Pstatus); + DPRINT("ata%d: ATAPI %uX %uX %uX\n", ctlrno, status, + inb(port+Pcylmsb), inb(port+Pcyllsb)); + if(status == 0 && inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){ + DPRINT("ata%d: ATAPI ok\n", ctlrno); + setvec(irq, ataintr, ctlr); + atapi |= 0x01; + mask |= 0x01; + goto skipedd; + } + if(atactlrwait(ctlr, DHmagic, 0, MS2TK(1)) || waserror()){ + xfree(ctlr); + return -1; + } + setvec(irq, ataintr, ctlr); + ctlr->cmd = Cedd; + outb(port+Pcmd, Cedd); + atasleep(ctlr); + poperror(); + + /* + * The diagnostic returns a code in the error register, good + * status is bits 6-0 == 0x01. + * The existence of the slave is more difficult to determine, + * different generations of controllers may respond in different + * ways. The standards here offer little light but only more and + * more heat: + * 1) the slave must be done and have dropped Sbusy by now (six + * seconds for the master, 5 seconds for the slave). If it + * hasn't, then it has either failed or the controller is + * broken in some way (e.g. Vibra16 returns status of 0xFF); + * 2) theory says the status of a non-existent slave should be 0. + * Of course, it's valid for all the bits to be 0 for a slave + * that exists too... + * 3) a valid ATAPI drive can have status 0 and the ATAPI signature + * in the cylinder registers after reset. Of course, if the drive + * has been messed about by the BIOS or some other O/S then the + * signature may be gone. + */ + error = inb(port+Perror); + DPRINT("ata%d: master diag error %ux\n", ctlr->ctlrno, error); + if((error & ~0x80) == 0x01) + mask |= 0x01; + + outb(port+Pdh, DHmagic|DHslave); + microdelay(1); + status = inb(port+Pstatus); + error = inb(port+Perror); + DPRINT("ata%d: slave diag status %ux, error %ux\n", ctlr->ctlrno, status, error); + if(status && (status & (Sbusy|Serr)) == 0 && (error & ~0x80) == 0x01) + mask |= 0x02; + else if(status == 0 && inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){ + atapi |= 0x02; + mask |= 0x02; } - conf.nhard = dp - ata; + +skipedd: + if(mask == 0){ + xfree(ctlr); + return -1; + } + atactlr[ctlrno] = ctlr; + + if(have640b >= 0 && (ctlrno & 0x01)) + ctlr->ctlrlock = &atactlrlock[ctlrno-1]; + else + ctlr->ctlrlock = &atactlrlock[ctlrno]; + + if(mask & 0x01) + atadrivealloc(ctlr, ctlrno*2, atapi & 0x01); + if(mask & 0x02) + atadrivealloc(ctlr, ctlrno*2+1, atapi & 0x02); + + return 0; +} + +void +atactlrreset(void) +{ + int ctlrno, driveno, i, slave, spindown; + ISAConf isa; + + cmd640b(); + rz1000(); + + for(ctlrno = 0; ctlrno < NCtlr; ctlrno++){ + memset(&isa, 0, sizeof(ISAConf)); + if(isaconfig("ata", ctlrno, &isa) == 0 && ctlrno) + continue; + if(isa.irq == 0 && (isa.irq = defirq[ctlrno]) == 0) + continue; + + if(atactlrprobe(ctlrno, Int0vec+isa.irq)) + continue; - if(conf.nhard && (p = getconf("spindowntime"))) - spindowntime = atoi(p); + for(i = 0; i < isa.nopt; i++){ + DPRINT("ata%d: opt %s\n", ctlrno, isa.opt[i]); + if(strncmp(isa.opt[i], "spindown", 8) == 0){ + if(isa.opt[i][9] != '=') + continue; + if(isa.opt[i][8] == '0') + slave = 0; + else if(isa.opt[i][8] == '1') + slave = 1; + else + continue; + driveno = ctlrno*2+slave; + if(atadrive[driveno] == 0) + continue; + if((spindown = strtol(&isa.opt[i][10], 0, 0)) == 0) + continue; + if(spindown < (Hardtimeout+2000)/1000) + spindown = (Hardtimeout+2000)/1000; + atadrive[driveno]->spindown = spindown; + spindownmask |= (1<noatapi = 1; + } + } +} + +void +atareset(void) +{ } void @@ -238,9 +531,21 @@ atainit(void) Chan* ataattach(char *spec) { + int driveno; Drive *dp; - for(dp = ata; dp < &ata[conf.nhard]; dp++){ + DPRINT("ataattach\n"); + + qlock(&ataprobelock); + if(ataprobedone == 0){ + atactlrreset(); + ataprobedone = 1; + } + qunlock(&ataprobelock); + + for(driveno = 0; driveno < NDrive; driveno++){ + if((dp = atadrive[driveno]) == 0) + continue; if(waserror()){ dp->online = 0; qunlock(dp); @@ -248,20 +553,20 @@ ataattach(char *spec) } qlock(dp); if(!dp->online){ - /* - * Make sure ataclock() doesn't - * interfere. - */ - dp->usetime = m->ticks; ataparams(dp); dp->online = 1; - atasetbuf(dp, 1); + atafeature(dp, 0xAA); /* read look ahead */ } /* * read Plan 9 partition table */ - atapart(dp); + if(dp->partok == 0){ + if(dp->atapi) + atapipart(dp); + else + atapart(dp); + } qunlock(dp); poperror(); } @@ -293,47 +598,45 @@ ataopen(Chan *c, int omode) } void -atacreate(Chan *c, char *name, int omode, ulong perm) +atacreate(Chan*, char*, int, ulong) { - USED(c, name, omode, perm); error(Eperm); } void ataclose(Chan *c) { - Drive *d; + Drive *dp; Partition *p; if(c->mode != OWRITE && c->mode != ORDWR) return; - d = &ata[DRIVE(c->qid.path)]; - p = &d->p[PART(c->qid.path)]; + dp = atadrive[DRIVE(c->qid.path)]; + p = &dp->p[PART(c->qid.path)]; if(strcmp(p->name, "partition") != 0) return; if(waserror()){ - qunlock(d); + qunlock(dp); nexterror(); } - qlock(d); - atapart(d); - qunlock(d); + qlock(dp); + dp->partok = 0; + atapart(dp); + qunlock(dp); poperror(); } void -ataremove(Chan *c) +ataremove(Chan*) { - USED(c); error(Eperm); } void -atawstat(Chan *c, char *dp) +atawstat(Chan*, char*) { - USED(c, dp); error(Eperm); } @@ -345,20 +648,34 @@ ataread(Chan *c, void *a, long n, ulong offset) int skip; uchar *aa = a; Partition *pp; - char *buf; + uchar *buf; if(c->qid.path == CHDIR) return devdirread(c, a, n, 0, 0, atagen); + dp = atadrive[DRIVE(c->qid.path)]; + if(dp->atapi){ + if(dp->online == 0) + error(Eio); + if(waserror()){ + qunlock(dp); + nexterror(); + } + qlock(dp); + if(dp->partok == 0) + atapipart(dp); + qunlock(dp); + poperror(); + return atapirwio(c, a, n, offset); + } + pp = &dp->p[PART(c->qid.path)]; + buf = smalloc(Maxxfer); if(waserror()){ free(buf); nexterror(); } - dp = &ata[DRIVE(c->qid.path)]; - pp = &dp->p[PART(c->qid.path)]; - skip = offset % dp->bytes; for(rv = 0; rv < n; rv += i){ i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf); @@ -390,13 +707,16 @@ atawrite(Chan *c, void *a, long n, ulong offset) long rv, i, partial; uchar *aa = a; Partition *pp; - char *buf; + uchar *buf; if(c->qid.path == CHDIR) error(Eisdir); - dp = &ata[DRIVE(c->qid.path)]; + dp = atadrive[DRIVE(c->qid.path)]; + if(dp->atapi) + error(Eperm); pp = &dp->p[PART(c->qid.path)]; + buf = smalloc(Maxxfer); if(waserror()){ free(buf); @@ -472,28 +792,26 @@ cmddone(void *a) /* * Wait for the controller to be ready to accept a command. - * This is protected from intereference by ataclock() by - * setting dp->usetime before it is called. */ -static void +static int cmdreadywait(Drive *dp) { - long start; - int period; - Controller *cp = dp->cp; + ulong ticks; + uchar ready; /* give it 2 seconds to spin down and up */ + dp->usetime = m->ticks; if(dp->state == Sspinning) - period = 10; + ticks = MS2TK(10); else - period = 2000; + ticks = MS2TK(2000); - start = m->ticks; - while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready) - if(TK2MS(m->ticks - start) > period){ - DPRINT("cmdreadywait failed\n"); - error(Eio); - } + if(dp->atapi) + ready = 0; + else + ready = Sready; + + return atactlrwait(dp->cp, dp->dh, ready, ticks); } static void @@ -505,7 +823,7 @@ atarepl(Drive *dp, long bblk) return; for(i = 0; i < dp->repl.nrepl; i++){ if(dp->repl.blk[i] == bblk) - DPRINT("found bblk %ld at offset %ld\n", bblk, i); + DPRINT("%s: found bblk %ld at offset %ld\n", dp->vol, bblk, i); } } @@ -514,7 +832,7 @@ atasleep(Controller *cp) { tsleep(&cp->r, cmddone, cp, Hardtimeout); if(cp->cmd && cp->cmd != Cident2){ - DPRINT("hard drive timeout\n"); + DPRINT("ata%d: cmd 0x%uX timeout\n", cp->ctlrno, cp->cmd); error("ata drive timeout"); } } @@ -525,7 +843,7 @@ atasleep(Controller *cp) * parts. */ static long -ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) +ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, uchar *buf) { Controller *cp; long lblk; @@ -563,24 +881,22 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) head = ((lblk/dp->sectors) % dp->heads); } -/*print("ataxfer cyl %d sec %d head %d\n", cyl, sec, head);/**/ + XPRINT("%s: ataxfer cyl %d sec %d head %d len %d\n", dp->vol, cyl, sec, head, len); cp = dp->cp; - qlock(cp); + qlock(cp->ctlrlock); if(waserror()){ + cp->dp = 0; cp->buf = 0; - qunlock(cp); + qunlock(cp->ctlrlock); nexterror(); } - /* - * Make sure hardclock() doesn't - * interfere. - */ - dp->usetime = m->ticks; - cmdreadywait(dp); + if(cmdreadywait(dp)){ + error(Eio); + } - ilock(&cp->reglock); + ILOCK(&cp->reglock); cp->sofar = 0; cp->buf = buf; cp->nsecs = len; @@ -590,20 +906,21 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) outb(cp->pbase+Pcount, cp->nsecs); outb(cp->pbase+Psector, sec); - outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head); + outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head); outb(cp->pbase+Pcyllsb, cyl); outb(cp->pbase+Pcylmsb, cyl>>8); outb(cp->pbase+Pcmd, cmd); if(cmd == Cwrite){ loop = 0; + microdelay(1); while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0) if(++loop > 10000) - panic("ataxfer"); + panic("%s: ataxfer", dp->vol); outss(cp->pbase+Pdata, cp->buf, dp->bytes/2); } else stat = 0; - iunlock(&cp->reglock); + IUNLOCK(&cp->reglock); if(stat & Serr) error(Eio); @@ -615,30 +932,31 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) */ loop = 0; while(waserror()){ - DPRINT("interrupted ataxfer\n"); + DPRINT("%s: interrupted ataxfer\n", dp->vol); if(loop++ > 10){ - print("ata disk error\n"); + print("%s: disk error\n", dp->vol); nexterror(); } } atasleep(cp); - dp->state = Sspinning; dp->usetime = m->ticks; + dp->state = Sspinning; poperror(); if(loop) nexterror(); if(cp->status & Serr){ - DPRINT("hd%d err: lblk %ld status %lux, err %lux\n", - dp-ata, lblk, cp->status, cp->error); + DPRINT("%s err: lblk %ld status %lux, err %lux\n", + dp->vol, lblk, cp->status, cp->error); DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head); DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar); atarepl(dp, lblk+cp->sofar); error(Eio); } + cp->dp = 0; cp->buf = 0; len = cp->sofar*dp->bytes; - qunlock(cp); + qunlock(cp->ctlrlock); poperror(); return len; @@ -648,33 +966,41 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf) * set read ahead mode */ static void -atasetbuf(Drive *dp, int on) +atafeature(Drive *dp, uchar arg) { Controller *cp = dp->cp; - qlock(cp); + if(dp->atapi) + return; + + qlock(cp->ctlrlock); if(waserror()){ - qunlock(cp); + cp->dp = 0; + qunlock(cp->ctlrlock); nexterror(); } - cmdreadywait(dp); + if(cmdreadywait(dp)){ + error(Eio); + } - ilock(&cp->reglock); - cp->cmd = Csetbuf; - outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); /* read look ahead */ - outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); - outb(cp->pbase+Pcmd, Csetbuf); - iunlock(&cp->reglock); + ILOCK(&cp->reglock); + cp->cmd = Cfeature; + cp->dp = dp; + outb(cp->pbase+Pfeature, arg); + outb(cp->pbase+Pdh, dp->dh); + outb(cp->pbase+Pcmd, Cfeature); + IUNLOCK(&cp->reglock); atasleep(cp); -/* if(cp->status & Serr) - DPRINT("hd%d setbuf err: status %lux, err %lux\n", - dp-ata, cp->status, cp->error);/**/ + if(cp->status & Serr) + DPRINT("%s: setbuf err: status %lux, err %lux\n", + dp->vol, cp->status, cp->error); + cp->dp = 0; poperror(); - qunlock(cp); + qunlock(cp->ctlrlock); } /* @@ -727,35 +1053,56 @@ static void ataident(Drive *dp) { Controller *cp; - char *buf; + uchar *buf; Ident *ip; char id[21]; ulong lbasecs; + uchar cmd; cp = dp->cp; buf = smalloc(Maxxfer); - qlock(cp); + qlock(cp->ctlrlock); if(waserror()){ - qunlock(cp); + cp->dp = 0; + free(buf); + qunlock(cp->ctlrlock); nexterror(); } - cmdreadywait(dp); + if(dp->atapi || isatapi(dp)) + cmd = Cidentd; + else{ + cmd = Cident; + if(cmdreadywait(dp)){ + dp->atapi = 1; + if(isatapi(dp) == 0) + error(Eio); + cmd = Cidentd; + } + } - ilock(&cp->reglock); +retryatapi: + ILOCK(&cp->reglock); cp->nsecs = 1; cp->sofar = 0; - cp->cmd = Cident; + cp->cmd = cmd; cp->dp = dp; cp->buf = buf; - outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4)); - outb(cp->pbase+Pcmd, Cident); - iunlock(&cp->reglock); + outb(cp->pbase+Pdh, dp->dh); + outb(cp->pbase+Pcmd, cmd); + IUNLOCK(&cp->reglock); atasleep(cp); if(cp->status & Serr){ - DPRINT("bad disk ident status\n"); + DPRINT("%s: bad disk ident status\n", dp->vol); + if(dp->atapi == 0 && (cp->error & Eabort)){ + dp->atapi = 1; + if(isatapi(dp)){ + cmd = Cidentd; + goto retryatapi; + } + } error(Eio); } ip = (Ident*)buf; @@ -773,37 +1120,54 @@ ataident(Drive *dp) memmove(id, ip->model, sizeof(id)-1); id[sizeof(id)-1] = 0; + DPRINT("%s: config 0x%uX capabilities 0x%uX\n", + dp->vol, ip->config, ip->capabilities); + if(dp->atapi){ + dp->bytes = 2048; + if((ip->config & 0x0060) == 0x0020) + dp->drqintr = 1; + } + if(dp->spindown && (ip->capabilities & (1<<13))) + dp->spindown /= 5; + + /* use default (unformatted) settings */ + dp->cyl = ip->cyls; + dp->heads = ip->heads; + dp->sectors = ip->s2t; + DPRINT("%s: %s %d/%d/%d CHS %d bytes\n", + dp->vol, id, dp->cyl, dp->heads, dp->sectors, dp->cap); + + if(ip->cvalid&(1<<0)){ + /* use current settings */ + dp->cyl = ip->ccyls; + dp->heads = ip->cheads; + dp->sectors = ip->cs2t; + DPRINT("%s: changed to %d cyl %d head %d sec\n", + dp->vol, dp->cyl, dp->heads, dp->sectors); + } + lbasecs = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); if((ip->capabilities & (1<<9)) && (lbasecs & 0xf0000000) == 0){ dp->lba = 1; - dp->sectors = lbasecs; - dp->cap = dp->bytes * dp->sectors; -/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/ + dp->lbasecs = lbasecs; + dp->cap = dp->bytes * dp->lbasecs; + DPRINT("%s: LBA: %s %d sectors %d bytes\n", + dp->vol, id, dp->lbasecs, dp->cap); } else { dp->lba = 0; - - /* use default (unformatted) settings */ - dp->cyl = ip->cyls; - dp->heads = ip->heads; - dp->sectors = ip->s2t; -/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive, - id, dp->cyl, dp->heads, dp->sectors);/**/ - - if(ip->cvalid&(1<<0)){ - /* use current settings */ - dp->cyl = ip->ccyls; - dp->heads = ip->cheads; - dp->sectors = ip->cs2t; -/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/ - } + dp->lbasecs = 0; dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; } - cp->lastcmd = cp->cmd; - cp->cmd = 0; + + if(cp->cmd){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + } + cp->dp = 0; cp->buf = 0; free(buf); poperror(); - qunlock(cp); + qunlock(cp->ctlrlock); } /* @@ -813,21 +1177,24 @@ static int ataprobe(Drive *dp, int cyl, int sec, int head) { Controller *cp; - char *buf; + uchar *buf; int rv; cp = dp->cp; buf = smalloc(Maxxfer); - qlock(cp); + qlock(cp->ctlrlock); if(waserror()){ + cp->dp = 0; free(buf); - qunlock(cp); + qunlock(cp->ctlrlock); nexterror(); } - cmdreadywait(dp); + if(cmdreadywait(dp)){ + error(Eio); + } - ilock(&cp->reglock); + ILOCK(&cp->reglock); cp->cmd = Cread; cp->dp = dp; cp->status = 0; @@ -837,23 +1204,27 @@ ataprobe(Drive *dp, int cyl, int sec, int head) outb(cp->pbase+Pcount, 1); outb(cp->pbase+Psector, sec+1); - outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4)); + outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head); outb(cp->pbase+Pcyllsb, cyl); outb(cp->pbase+Pcylmsb, cyl>>8); outb(cp->pbase+Pcmd, Cread); - iunlock(&cp->reglock); + IUNLOCK(&cp->reglock); atasleep(cp); - if(cp->status & Serr) + if(cp->status & Serr){ + DPRINT("%s: probe err: status %lux, err %lux\n", + dp->vol, cp->status, cp->error); rv = -1; + } else rv = 0; + cp->dp = 0; cp->buf = 0; free(buf); poperror(); - qunlock(cp); + qunlock(cp->ctlrlock); return rv; } @@ -871,8 +1242,10 @@ ataparams(Drive *dp) */ dp->bytes = 512; ataident(dp); + if(dp->atapi) + return; if(dp->lba){ - i = dp->sectors - 1; + i = dp->lbasecs - 1; if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) return; } else { @@ -907,6 +1280,10 @@ ataparams(Drive *dp) } dp->cyl = lo + 1; dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors; + DPRINT("%s: probed: %d/%d/%d CHS %d bytes\n", + dp->vol, dp->cyl, dp->heads, dp->sectors, dp->cap); + if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0) + error(Eio); } /* @@ -920,7 +1297,7 @@ atareplinit(Drive *dp) char *field[1]; ulong n; int i; - char *buf; + uchar *buf; /* * check the partition is big enough @@ -945,7 +1322,7 @@ atareplinit(Drive *dp) /* * parse replacement table. */ - n = getfields(buf, line, Nrepl+1, "\n"); + n = getfields((char*)buf, line, Nrepl+1, "\n"); if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){ dp->repl.p = 0; } else { @@ -969,12 +1346,15 @@ atapart(Drive *dp) { Partition *pp; char *line[Npart+1]; - char *field[3]; + char *field[3], namebuf[NAMELEN], *p; ulong n; int i; - char *buf; + uchar *buf; - sprint(dp->vol, "hd%d", dp - ata); + DPRINT("%s: partok %d\n", dp->vol, dp->partok); + + if(dp->partok) + return; /* * we always have a partition for the whole disk @@ -988,6 +1368,7 @@ atapart(Drive *dp) strcpy(pp->name, "partition"); pp->start = dp->p[0].end - 1; pp->end = dp->p[0].end; + pp++; dp->npart = 2; /* @@ -997,58 +1378,70 @@ atapart(Drive *dp) buf = smalloc(Maxxfer); if(waserror()){ + DPRINT("%s: atapart error\n", dp->vol); free(buf); nexterror(); } + /* - * read last sector from disk, null terminate. This used - * to be the sector we used for the partition tables. - * However, this sector is special on some PC's so we've - * started to use the second last sector as the partition - * table instead. To avoid reconfiguring all our old systems - * we first look to see if there is a valid partition - * table in the last sector. If so, we use it. Otherwise - * we switch to the second last. + * Check if the partitions are described in plan9.ini. + * If not, read the disc. */ - ataxfer(dp, pp, Cread, 0, dp->bytes, buf); - buf[dp->bytes-1] = 0; - n = getfields(buf, line, Npart+1, "\n"); - if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){ - dp->p[0].end--; - dp->p[1].start--; - dp->p[1].end--; - ataxfer(dp, pp, Cread, 0, dp->bytes, buf); + sprint(namebuf, "%spartition", dp->vol); + if((p = getconf(namebuf)) == 0){ + /* + * read last sector from disk, null terminate. This used + * to be the sector we used for the partition tables. + * However, this sector is special on some PC's so we've + * started to use the second last sector as the partition + * table instead. To avoid reconfiguring all our old systems + * we first look to see if there is a valid partition + * table in the last sector. If so, we use it. Otherwise + * we switch to the second last. + */ + ataxfer(dp, &dp->p[1], Cread, 0, dp->bytes, buf); buf[dp->bytes-1] = 0; - n = getfields(buf, line, Npart+1, "\n"); + n = getfields((char*)buf, line, Npart+1, "\n"); + if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){ + dp->p[0].end--; + dp->p[1].start--; + dp->p[1].end--; + ataxfer(dp, &dp->p[1], Cread, 0, dp->bytes, buf); + buf[dp->bytes-1] = 0; + n = getfields((char*)buf, line, Npart+1, "\n"); + } + } + else{ + strcpy((char*)buf, p); + n = getfields((char*)buf, line, Npart+1, "\n"); } /* * parse partition table. */ - if(n && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ + if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ for(i = 1; i < n; i++){ + if(getfields(line[i], field, 3, " ") != 3) + break; + if(pp >= &dp->p[Npart]) + break; + strncpy(pp->name, field[0], NAMELEN); + if(strncmp(pp->name, "repl", NAMELEN) == 0) + dp->repl.p = pp; + pp->start = strtoul(field[1], 0, 0); + pp->end = strtoul(field[2], 0, 0); + if(pp->start > pp->end || pp->start >= dp->p[0].end) + break; pp++; - switch(getfields(line[i], field, 3, " ")) { - case 2: - if(strcmp(field[0], "unit") == 0) - strncpy(dp->vol, field[1], NAMELEN); - break; - case 3: - strncpy(pp->name, field[0], NAMELEN); - if(strncmp(pp->name, "repl", NAMELEN) == 0) - dp->repl.p = pp; - pp->start = strtoul(field[1], 0, 0); - pp->end = strtoul(field[2], 0, 0); - if(pp->start > pp->end || pp->start >= dp->p[0].end) - break; - dp->npart++; - } } } + dp->npart = pp - dp->p; free(buf); poperror(); + dp->partok = 1; + if(dp->repl.p) atareplinit(dp); } @@ -1062,29 +1455,26 @@ enum * we get an interrupt for every sector transferred */ static void -ataintr(Ureg *ur, void *arg) +ataintr(Ureg*, void* arg) { Controller *cp; Drive *dp; - long loop; - char *addr; + int loop; + uchar *addr; - USED(ur, arg); - - /* - * BUG!! if there is ever more than one controller, we need a way to - * distinguish which interrupted (use arg). - */ - cp = &atac[0]; - dp = cp->dp; + cp = arg; + if((dp = cp->dp) == 0 && cp->cmd != Cedd) + return; - ilock(&cp->reglock); + ILOCK(&cp->reglock); loop = 0; while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){ - if(++loop > Maxloop) - panic("ataintr: wait busy cmd=%lux status=%lux", - cp->cmd, inb(cp->pbase+Pstatus)); + if(++loop > Maxloop){ + print("ata%d: cmd=%lux, lastcmd=%lux status=%lux\n", + cp->ctlrno, cp->cmd, cp->lastcmd, inb(cp->pbase+Pstatus)); + panic("%s: wait busy\n", dp->vol); + } } switch(cp->cmd){ @@ -1101,8 +1491,8 @@ ataintr(Ureg *ur, void *arg) loop = 0; while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0) if(++loop > Maxloop) - panic("ataintr: write cmd=%lux status=%lux\n", - cp->cmd, inb(cp->pbase+Pstatus)); + panic("%s: write cmd=%lux status=%lux\n", + dp->vol, cp->cmd, inb(cp->pbase+Pstatus)); addr = cp->buf; if(addr){ addr += cp->sofar*dp->bytes; @@ -1116,11 +1506,12 @@ ataintr(Ureg *ur, void *arg) break; case Cread: case Cident: + case Cidentd: loop = 0; while((cp->status & (Serr|Sdrq)) == 0){ if(++loop > Maxloop){ - print("ataintr: read/ident cmd=%lux status=%lux\n", - cp->cmd, inb(cp->pbase+Pstatus)); + print("%s: read/ident cmd=%lux status=%lux\n", + dp->vol, cp->cmd, inb(cp->pbase+Pstatus)); cp->status |= Serr; break; } @@ -1140,21 +1531,24 @@ ataintr(Ureg *ur, void *arg) } cp->sofar++; if(cp->sofar > cp->nsecs) - print("ataintr %d %d\n", cp->sofar, cp->nsecs); + print("%s: intr %d %d\n", dp->vol, cp->sofar, cp->nsecs); if(cp->sofar >= cp->nsecs){ cp->lastcmd = cp->cmd; - if (cp->cmd == Cread) - cp->cmd = 0; - else + if(cp->cmd == Cidentd) + cp->cmd = Cident2; + else if(cp->cmd == Cident && (cp->status & Sready) == 0) cp->cmd = Cident2; + else + cp->cmd = 0; + inb(cp->pbase+Pstatus); + DPRINT("status %uX, alt %uX\n", + inb(cp->pbase+Pstatus), inb(cp->pbase+0x206)); wakeup(&cp->r); } break; - case Cinitparam: - case Csetbuf: - case Cidle: + case Cfeature: case Cstandby: - case Cpowerdown: + case Cedd: cp->lastcmd = cp->cmd; cp->cmd = 0; wakeup(&cp->r); @@ -1163,39 +1557,367 @@ ataintr(Ureg *ur, void *arg) cp->lastcmd = cp->cmd; cp->cmd = 0; break; + case Cpktcmd: + atapiintr(cp); + break; default: - print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n", - cp->cmd, cp->lastcmd, cp->status); + if(cp->cmd == 0 && cp->lastcmd == Cpktcmd && cp->cmdblk[0] == Ccapacity) + break; + if(cp->status & Serr) + cp->error = inb(cp->pbase+Perror); + print("%s: weird interrupt, cmd=%.2ux, lastcmd=%.2ux, ", + dp->vol, cp->cmd, cp->lastcmd); + print("status=%.2ux, error=%.2ux, count=%.2ux\n", + cp->ctlrno, cp->error, inb(cp->pbase+Pcount)); break; } - iunlock(&cp->reglock); + IUNLOCK(&cp->reglock); } void hardclock(void) { - int drive; + int driveno, mask; Drive *dp; Controller *cp; int diff; - if(spindowntime <= 0) + if((mask = spindownmask) == 0) return; - for(drive = 0; drive < conf.nhard; drive++){ - dp = &ata[drive]; + for(driveno = 0; driveno < NDrive && mask; driveno++){ + mask &= ~(1<cp; diff = TK2SEC(m->ticks - dp->usetime); - if((dp->state == Sspinning) && (diff >= spindowntime)){ - ilock(&cp->reglock); + if((dp->state == Sspinning) && (diff >= dp->spindown)){ + DPRINT("%s: spindown\n", dp->vol); + ILOCK(&cp->reglock); cp->cmd = Cstandby; outb(cp->pbase+Pcount, 0); - outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0); + outb(cp->pbase+Pdh, dp->dh); outb(cp->pbase+Pcmd, cp->cmd); - iunlock(&cp->reglock); + IUNLOCK(&cp->reglock); dp->state = Sstandby; } } } + +static int +isatapi(Drive *dp) +{ + Controller *cp; + + cp = dp->cp; + outb(cp->pbase+Pdh, dp->dh); + DPRINT("%s: isatapi %d\n", dp->vol, dp->atapi); + if(dp->atapi){ + outb(cp->pbase+Pcmd, 0x08); + delay(20); + } + dp->atapi = 0; + dp->bytes = 512; + microdelay(1); + if(inb(cp->pbase+Pstatus)){ + DPRINT("%s: isatapi status %ux\n", dp->vol, inb(cp->pbase+Pstatus)); + return 0; + } + if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14){ + DPRINT("%s: isatapi cyl %ux %ux\n", + dp->vol, inb(cp->pbase+Pcylmsb), inb(cp->pbase+Pcyllsb)); + return 0; + } + dp->atapi = 1; + sprint(dp->vol, "atapi%d", dp->driveno); + dp->spindown = 0; + spindownmask &= ~(1<driveno); + return 1; +} + +static void +atapiexec(Drive *dp) +{ + Controller *cp; + int loop; + + cp = dp->cp; + + if(cmdreadywait(dp)){ + error(Eio); + } + + ILOCK(&cp->reglock); + cp->nsecs = 1; + cp->sofar = 0; + cp->error = 0; + cp->cmd = Cpktcmd; + outb(cp->pbase+Pcount, 0); + outb(cp->pbase+Psector, 0); + outb(cp->pbase+Pfeature, 0); + outb(cp->pbase+Pcyllsb, cp->len); + outb(cp->pbase+Pcylmsb, cp->len>>8); + outb(cp->pbase+Pdh, dp->dh); + outb(cp->pbase+Pcmd, cp->cmd); + + if(dp->drqintr == 0){ + microdelay(1); + for(loop = 0; (inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0; loop++){ + if(loop < 10000) + continue; + panic("%s: cmddrqwait: cmd=%lux status=%lux\n", + dp->vol, cp->cmd, inb(cp->pbase+Pstatus)); + } + outss(cp->pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2); + } + IUNLOCK(&cp->reglock); + + loop = 0; + while(waserror()){ + DPRINT("%s: interrupted atapiexec\n", dp->vol); + if(loop++ > 10){ + print("%s: disk error\n", dp->vol); + nexterror(); + } + } + atasleep(cp); + poperror(); + if(loop) + nexterror(); + + if(cp->status & Serr){ + DPRINT("%s: Bad packet command %ux\n", dp->vol, cp->error); + error(Eio); + } +} + +static long +atapiio(Drive *dp, char *a, ulong len, ulong offset) +{ + ulong bn, n, o, m; + Controller *cp; + uchar *buf; + int retrycount; + + cp = dp->cp; + + buf = smalloc(Maxxfer); + qlock(cp->ctlrlock); + retrycount = 1; +retry: + if(waserror()){ + dp->partok = 0; + if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){ + dp->vers++; + if(retrycount){ + retrycount--; + goto retry; + } + } + cp->dp = 0; + free(buf); + qunlock(cp->ctlrlock); + nexterror(); + } + + cp->buf = buf; + cp->dp = dp; + cp->len = dp->bytes; + + n = len; + while(n > 0){ + bn = offset / dp->bytes; + if(offset > dp->cap-dp->bytes) + break; + o = offset % dp->bytes; + m = dp->bytes - o; + if(m > n) + m = n; + memset(cp->cmdblk, 0, 12); + cp->cmdblk[0] = Cread2; + cp->cmdblk[2] = bn >> 24; + cp->cmdblk[3] = bn >> 16; + cp->cmdblk[4] = bn >> 8; + cp->cmdblk[5] = bn; + cp->cmdblk[7] = 0; + cp->cmdblk[8] = 1; + atapiexec(dp); + if(cp->count != dp->bytes){ + print("short read\n"); + break; + } + memmove(a, cp->buf + o, m); + n -= m; + offset += m; + a += m; + } + poperror(); + free(buf); + cp->dp = 0; + qunlock(cp->ctlrlock); + return len-n; +} + +static long +atapirwio(Chan *c, char *a, ulong len, ulong offset) +{ + Drive *dp; + ulong vers; + long rv; + + dp = atadrive[DRIVE(c->qid.path)]; + + qlock(dp); + if(waserror()){ + qunlock(dp); + nexterror(); + } + + vers = c->qid.vers; + c->qid.vers = dp->vers; + if(vers && vers != dp->vers) + error(Eio); + rv = atapiio(dp, a, len, offset); + + poperror(); + qunlock(dp); + return rv; +} + +static void +atapipart(Drive *dp) +{ + Controller *cp; + uchar *buf, err; + Partition *pp; + int retrycount; + + cp = dp->cp; + + pp = &dp->p[0]; + strcpy(pp->name, "disk"); + pp->start = 0; + pp->end = 0; + dp->npart = 1; + + buf = smalloc(Maxxfer); + qlock(cp->ctlrlock); + retrycount = 1; +retry: + if(waserror()){ + if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){ + dp->vers++; + if(retrycount){ + retrycount--; + goto retry; + } + } + cp->dp = 0; + free(buf); + if((cp->status & Serr) && (cp->error & 0xF0) == 0x20) + err = cp->error; + else + err = 0; + qunlock(cp->ctlrlock); + if(err == 0x20) + return; + nexterror(); + } + + cp->buf = buf; + cp->dp = dp; + + cp->len = 8; + cp->count = 0; + memset(cp->cmdblk, 0, sizeof(cp->cmdblk)); + cp->cmdblk[0] = Ccapacity; + atapiexec(dp); + if(cp->count != 8){ + print("cmd=%2.2uX, lastcmd=%2.2uX ", cp->cmd, cp->lastcmd); + print("cdsize count %d, status 0x%2.2uX, error 0x%2.2uX\n", + cp->count, cp->status, cp->error); + error(Eio); + } + dp->lbasecs = (cp->buf[0]<<24)|(cp->buf[1]<<16)|(cp->buf[2]<<8)|cp->buf[3]; + dp->cap = dp->lbasecs*dp->bytes; + cp->dp = 0; + free(cp->buf); + poperror(); + qunlock(cp->ctlrlock); + + pp->end = dp->cap / dp->bytes; + dp->partok = 1; +} + +static void +atapiintr(Controller *cp) +{ + uchar cause; + int count, loop, pbase; + uchar *addr; + + pbase = cp->pbase; + cause = inb(pbase+Pcount) & 0x03; + DPRINT("%s: atapiintr %uX\n", cp->dp->vol, cause); + switch(cause){ + + case 0: /* data out */ + cp->status |= Serr; + /*FALLTHROUGH*/ + case 1: /* command */ + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(pbase+Perror); + wakeup(&cp->r); + break; + } + outss(pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2); + break; + + case 2: /* data in */ + addr = cp->buf; + if(addr == 0){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + if(cp->status & Serr) + cp->error = inb(pbase+Perror); + wakeup(&cp->r); + break; + } + loop = 0; + while((cp->status & (Serr|Sdrq)) == 0){ + if(++loop > Maxloop){ + cp->status |= Serr; + break; + } + cp->status = inb(pbase+Pstatus); + } + if(cp->status & Serr){ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + cp->error = inb(pbase+Perror); + print("%s: Cpktcmd status=%uX, error=%uX\n", + cp->dp->vol, cp->status, cp->error); + wakeup(&cp->r); + break; + } + count = inb(pbase+Pcyllsb)|(inb(pbase+Pcylmsb)<<8); + if (count > Maxxfer) + count = Maxxfer; + inss(pbase+Pdata, addr, count/2); + cp->count = count; + cp->lastcmd = cp->cmd; + break; + + case 3: /* status */ + cp->lastcmd = cp->cmd; + cp->cmd = 0; + if(cp->status & Serr) + cp->error = inb(cp->pbase+Perror); + wakeup(&cp->r); + break; + } +} diff --git a/pc/pci.c b/pc/pci.c index 08c9e329fd8b90926c878d0fdcf426691cdf6be1..4bf9ced7eaa84b350a9ad376ab59af815fd4b246 100644 --- a/pc/pci.c +++ b/pc/pci.c @@ -106,11 +106,11 @@ pcicfgw(int busno, int devno, int funcno, int regno, void* data, int nbytes) case 1: addr = 0x80000000|((busno & 0xFF)<<16)|((devno & 0x1F)<<11)|((funcno & 0x03)<<8); p = data; - for(len = nbytes/sizeof(ulong); len > 0; len--){ + for(len = nbytes/sizeof(*p); len > 0; len--){ outl(PCIaddr, addr|(regno & 0xFF)); outl(PCIdata, *p); p++; - regno += sizeof(ulong); + regno += sizeof(*p); } outl(PCIaddr, 0); @@ -124,7 +124,7 @@ pcicfgw(int busno, int devno, int funcno, int regno, void* data, int nbytes) base = (0xC000|(devno<<8)) + regno; p = data; - for(len = nbytes/sizeof(ulong); len > 0; len--){ + for(len = nbytes/sizeof(*p); len > 0; len--){ outl(base, *p); p++; base += sizeof(*p); @@ -136,6 +136,45 @@ pcicfgw(int busno, int devno, int funcno, int regno, void* data, int nbytes) unlock(&pcicfglock); } +/* + * This is not in the spec, but at least the CMD640B requires it. + */ +void +pcicfgw8(int busno, int devno, int funcno, int regno, void* data, int nbytes) +{ + uchar *p; + int base, len; + + lock(&pcicfglock); + if(pcicfgmode == -1) + pcicfginit(busno); + + switch(pcicfgmode){ + + default: + panic("pcicfgw8: pcicfgmode %d\n", pcicfgmode); + break; + + case 2: + if(devno >= 16) + break; + outb(PCIcse, 0x80|((funcno & 0x07)<<1)); + outb(PCIforward, busno); + + base = (0xC000|(devno<<8)) + regno; + p = data; + for(len = nbytes/sizeof(*p); len > 0; len--){ + outb(base, *p); + p++; + base += sizeof(*p); + } + + outb(PCIcse, 0); + } + + unlock(&pcicfglock); +} + int pcimatch(int busno, int devno, PCIcfg* pcicfg) {