Hi -

I know from conversation that you got this from Harvey, but can you
put a line in the commit message about where you got it from? 

Something like:

Imported from Harvey OS (https://github.com/Harvey-OS/harvey), commit
5be5d9cc.

Or a version/tag, etc.

Thanks,

Barret


On 2016-08-09 at 14:32 "Ronald G. Minnich" <[email protected]> wrote:
> Change-Id: I5cd052ce7bde5f6eb6de9557dfe6151174d1ef5f
> Signed-off-by: Ronald G. Minnich <[email protected]>
> ---
>  kern/drivers/dev/sd.c      | 1683 ++++++++++++++++++++++++++++++
>  kern/drivers/dev/sdiahci.c | 2461
> ++++++++++++++++++++++++++++++++++++++++++++
> kern/drivers/dev/sdscsi.c  |  436 ++++++++ kern/include/ahci.h
> |  302 ++++++ kern/include/sd.h          |  203 ++++
>  5 files changed, 5085 insertions(+)
>  create mode 100644 kern/drivers/dev/sd.c
>  create mode 100644 kern/drivers/dev/sdiahci.c
>  create mode 100644 kern/drivers/dev/sdscsi.c
>  create mode 100644 kern/include/ahci.h
>  create mode 100644 kern/include/sd.h
> 
> diff --git a/kern/drivers/dev/sd.c b/kern/drivers/dev/sd.c
> new file mode 100644
> index 0000000..5ca4f5c
> --- /dev/null
> +++ b/kern/drivers/dev/sd.c
> @@ -0,0 +1,1683 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to
> the license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at
> http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be
> copied,
> + * modified, propagated, or distributed except according to the
> terms contained
> + * in the LICENSE file.
> + */
> +
> +/*
> + * Storage Device.
> + */
> +#include "u.h"
> +#include "../port/lib.h"
> +#include "mem.h"
> +#include "dat.h"
> +#include "fns.h"
> +#include "io.h"
> +#include "ureg.h"
> +#include "../port/error.h"
> +
> +#include "../port/sd.h"
> +
> +extern Dev sddevtab;
> +extern SDifc* sdifc[];
> +
> +static char Echange[] = "media or partition has changed";
> +
> +static char devletters[] = "0123456789"
> +     "abcdefghijklmnopqrstuvwxyz"
> +     "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
> +
> +static SDev *devs[sizeof devletters-1];
> +static QLock devslock;
> +
> +enum {
> +     Rawcmd,
> +     Rawdata,
> +     Rawstatus,
> +};
> +
> +enum {
> +     Qtopdir         = 1,            /* top level
> directory */
> +     Qtopbase,
> +     Qtopctl          = Qtopbase,
> +
> +     Qunitdir,                       /* directory per unit */
> +     Qunitbase,
> +     Qctl            = Qunitbase,
> +     Qraw,
> +     Qpart,
> +
> +     TypeLOG         = 4,
> +     NType           = (1<<TypeLOG),
> +     TypeMASK        = (NType-1),
> +     TypeSHIFT       = 0,
> +
> +     PartLOG         = 8,
> +     NPart           = (1<<PartLOG),
> +     PartMASK        = (NPart-1),
> +     PartSHIFT       = TypeLOG,
> +
> +     UnitLOG         = 8,
> +     NUnit           = (1<<UnitLOG),
> +     UnitMASK        = (NUnit-1),
> +     UnitSHIFT       = (PartLOG+TypeLOG),
> +
> +     DevLOG          = 8,
> +     NDev            = (1 << DevLOG),
> +     DevMASK         = (NDev-1),
> +     DevSHIFT         = (UnitLOG+PartLOG+TypeLOG),
> +
> +     Ncmd = 20,
> +};
> +
> +#define TYPE(q)              ((((uint32_t)(q).path)>>TypeSHIFT) &
> TypeMASK) +#define PART(q)
> ((((uint32_t)(q).path)>>PartSHIFT) & PartMASK) +#define
> UNIT(q)               ((((uint32_t)(q).path)>>UnitSHIFT) & UnitMASK)
> +#define DEV(q)               ((((uint32_t)(q).path)>>DevSHIFT) &
> DevMASK) +#define QID(d,u, p, t)
> (((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
> +
> ((p)<<PartSHIFT)|((t)<<TypeSHIFT)) +
> +
> +void
> +sdaddpart(SDunit* unit, char* name, uint64_t start, uint64_t end)
> +{
> +     SDpart *pp;
> +     int i, partno;
> +
> +     /*
> +      * Check name not already used
> +      * and look for a free slot.
> +      */
> +     if(unit->part != nil){
> +             partno = -1;
> +             for(i = 0; i < unit->npart; i++){
> +                     pp = &unit->part[i];
> +                     if(!pp->valid){
> +                             if(partno == -1)
> +                                     partno = i;
> +                             break;
> +                     }
> +                     if(strcmp(name, pp->SDperm.name) == 0){
> +                             if(pp->start == start && pp->end ==
> end)
> +                                     return;
> +                             error(Ebadctl);
> +                     }
> +             }
> +     }
> +     else{
> +             if((unit->part = malloc(sizeof(SDpart)*SDnpart)) ==
> nil)
> +                     error(Enomem);
> +             unit->npart = SDnpart;
> +             partno = 0;
> +     }
> +
> +     /*
> +      * If no free slot found then increase the
> +      * array size (can't get here with unit->part == nil).
> +      */
> +     if(partno == -1){
> +             if(unit->npart >= NPart)
> +                     error(Enomem);
> +             if((pp =
> malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil)
> +                     error(Enomem);
> +             memmove(pp, unit->part, sizeof(SDpart)*unit->npart);
> +             free(unit->part);
> +             unit->part = pp;
> +             partno = unit->npart;
> +             unit->npart += SDnpart;
> +     }
> +
> +     /*
> +      * Check size and extent are valid.
> +      */
> +     if(start > end || end > unit->sectors)
> +             error(Eio);
> +     pp = &unit->part[partno];
> +     pp->start = start;
> +     pp->end = end;
> +     kstrdup(&pp->SDperm.name, name);
> +     kstrdup(&pp->SDperm.user, eve);
> +     pp->SDperm.perm = 0640;
> +     pp->valid = 1;
> +}
> +
> +static void
> +sddelpart(SDunit* unit, char* name)
> +{
> +     Proc *up = externup();
> +     int i;
> +     SDpart *pp;
> +     /*
> +      * Look for the partition to delete.
> +      * Can't delete if someone still has it open.
> +      */
> +     pp = unit->part;
> +     for(i = 0; i < unit->npart; i++){
> +             if(strcmp(name, pp->SDperm.name) == 0)
> +                     break;
> +             pp++;
> +     }
> +     if(i >= unit->npart)
> +             error(Ebadctl);
> +     if(strcmp(up->user, pp->SDperm.user) && !iseve())
> +             error(Eperm);
> +     pp->valid = 0;
> +     pp->vers++;
> +}
> +
> +static void
> +sdincvers(SDunit *unit)
> +{
> +     int i;
> +
> +     unit->vers++;
> +     if(unit->part){
> +             for(i = 0; i < unit->npart; i++){
> +                     unit->part[i].valid = 0;
> +                     unit->part[i].vers++;
> +             }
> +     }
> +}
> +
> +static int
> +sdinitpart(SDunit* unit)
> +{
> +#if 0
> +     Mach *m;
> +     int nf;
> +     uint64_t start, end;
> +     char *f[4], *p, *q, buf[10];
> +
> +     m = machp();
> +#endif
> +     if(unit->sectors > 0){
> +             unit->sectors = unit->secsize = 0;
> +             sdincvers(unit);
> +     }
> +
> +     /* device must be connected or not; other values are trouble
> */
> +     if(unit->inquiry[0] & 0xC0)     /* see SDinq0periphqual */
> +             return 0;
> +     switch(unit->inquiry[0] & SDinq0periphtype){
> +     case SDperdisk:
> +     case SDperworm:
> +     case SDpercd:
> +     case SDpermo:
> +             break;
> +     default:
> +             return 0;
> +     }
> +
> +     if(unit->dev->ifc->online)
> +             unit->dev->ifc->online(unit);
> +     if(unit->sectors){
> +             sdincvers(unit);
> +             sdaddpart(unit, "data", 0, unit->sectors);
> +
> +             /*
> +              * Use partitions passed from boot program,
> +              * e.g.
> +              *      sdC0part=dos 63 123123/plan9 123123 456456
> +              * This happens before /boot sets hostname so the
> +              * partitions will have the null-string for user.
> +              * The gen functions patch it up.
> +              */
> +#if 0
> +             snprint(buf, sizeof buf, "%spart",
> unit->SDperm.name);
> +             for(p = getconf(buf); p != nil; p = q){
> +                     if(q = strchr(p, '/'))
> +                             *q++ = '\0';
> +                     nf = tokenize(p, f, nelem(f));
> +                     if(nf < 3)
> +                             continue;
> +
> +                     start = strtoull(f[1], 0, 0);
> +                     end = strtoull(f[2], 0, 0);
> +                     if(!waserror()){
> +                             sdaddpart(unit, f[0], start, end);
> +                             poperror();
> +                     }
> +             }
> +#endif
> +     }
> +
> +     return 1;
> +}
> +
> +static int
> +sdindex(int idno)
> +{
> +     char *p;
> +
> +     p = strchr(devletters, idno);
> +     if(p == nil)
> +             return -1;
> +     return p-devletters;
> +}
> +
> +static SDev*
> +sdgetdev(int idno)
> +{
> +     SDev *sdev;
> +     int i;
> +
> +     if((i = sdindex(idno)) < 0)
> +             return nil;
> +
> +     qlock(&devslock);
> +     if(sdev = devs[i])
> +             incref(&sdev->r);
> +     qunlock(&devslock);
> +     return sdev;
> +}
> +
> +static SDunit*
> +sdgetunit(SDev* sdev, int subno)
> +{
> +     SDunit *unit;
> +     char buf[32];
> +
> +     /*
> +      * Associate a unit with a given device and sub-unit
> +      * number on that device.
> +      * The device will be probed if it has not already been
> +      * successfully accessed.
> +      */
> +     qlock(&sdev->unitlock);
> +     if(subno > sdev->nunit){
> +             qunlock(&sdev->unitlock);
> +             return nil;
> +     }
> +
> +     unit = sdev->unit[subno];
> +     if(unit == nil){
> +             /*
> +              * Probe the unit only once. This decision
> +              * may be a little severe and reviewed later.
> +              */
> +             if(sdev->unitflg[subno]){
> +                     qunlock(&sdev->unitlock);
> +                     return nil;
> +             }
> +             if((unit = malloc(sizeof(SDunit))) == nil){
> +                     qunlock(&sdev->unitlock);
> +                     return nil;
> +             }
> +             sdev->unitflg[subno] = 1;
> +
> +             snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
> +             kstrdup(&unit->SDperm.name, buf);
> +             kstrdup(&unit->SDperm.user, eve);
> +             unit->SDperm.perm = 0555;
> +             unit->subno = subno;
> +             unit->dev = sdev;
> +
> +             if(sdev->enabled == 0 && sdev->ifc->enable)
> +                     sdev->ifc->enable(sdev);
> +             sdev->enabled = 1;
> +
> +             /*
> +              * No need to lock anything here as this is only
> +              * called before the unit is made available in the
> +              * sdunit[] array.
> +              */
> +             if(unit->dev->ifc->verify(unit) == 0){
> +                     qunlock(&sdev->unitlock);
> +                     free(unit);
> +                     return nil;
> +             }
> +             sdev->unit[subno] = unit;
> +     }
> +     qunlock(&sdev->unitlock);
> +     return unit;
> +}
> +
> +static void
> +sdreset(void)
> +{
> +     int i;
> +     SDev *sdev;
> +
> +     /*
> +      * Probe all known controller types and register any devices
> found.
> +      */
> +     for(i = 0; sdifc[i] != nil; i++){
> +             if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp())
> == nil)
> +                     continue;
> +             sdadddevs(sdev);
> +     }
> +}
> +
> +void
> +sdadddevs(SDev *sdev)
> +{
> +     int i, j, id;
> +     SDev *next;
> +
> +     for(; sdev; sdev=next){
> +             next = sdev->next;
> +
> +             sdev->unit = (SDunit**)malloc(sdev->nunit *
> sizeof(SDunit*));
> +             sdev->unitflg = (int*)malloc(sdev->nunit *
> sizeof(int));
> +             if(sdev->unit == nil || sdev->unitflg == nil){
> +                     print("sdadddevs: out of memory\n");
> +             giveup:
> +                     free(sdev->unit);
> +                     free(sdev->unitflg);
> +                     if(sdev->ifc->clear)
> +                             sdev->ifc->clear(sdev);
> +                     free(sdev);
> +                     continue;
> +             }
> +             id = sdindex(sdev->idno);
> +             if(id == -1){
> +                     print("sdadddevs: bad id number %d (%C)\n",
> id, id);
> +                     goto giveup;
> +             }
> +             qlock(&devslock);
> +             for(i=0; i<nelem(devs); i++){
> +                     if(devs[j = (id+i)%nelem(devs)] == nil){
> +                             sdev->idno = devletters[j];
> +                             devs[j] = sdev;
> +                             snprint(sdev->name, sizeof
> sdev->name, "sd%c", devletters[j]);
> +                             break;
> +                     }
> +             }
> +             qunlock(&devslock);
> +             if(i == nelem(devs)){
> +                     print("sdadddevs: out of device letters\n");
> +                     goto giveup;
> +             }
> +     }
> +}
> +
> +// void
> +// sdrmdevs(SDev *sdev)
> +// {
> +//   char buf[2];
> +//
> +//   snprint(buf, sizeof buf, "%c", sdev->idno);
> +//   unconfigure(buf);
> +// }
> +
> +void
> +sdaddallconfs(void (*addconf)(SDunit *))
> +{
> +     int i, u;
> +     SDev *sdev;
> +
> +     for(i = 0; i < nelem(devs); i++)                /* each
> controller */
> +             for(sdev = devs[i]; sdev; sdev = sdev->next)
> +                     for(u = 0; u < sdev->nunit; u++)        /*
> each drive */
> +                             (*addconf)(sdev->unit[u]);
> +}
> +
> +static int
> +sd2gen(Chan* c, int i, Dir* dp)
> +{
> +     Qid q;
> +     uint64_t l;
> +     SDpart *pp;
> +     SDperm *perm;
> +     SDunit *unit;
> +     SDev *sdev;
> +     int rv;
> +
> +     sdev = sdgetdev(DEV(c->qid));
> +     assert(sdev);
> +     unit = sdev->unit[UNIT(c->qid)];
> +
> +     rv = -1;
> +     switch(i){
> +     case Qctl:
> +             mkqid(&q, QID(DEV(c->qid), UNIT(c->qid),
> PART(c->qid), Qctl),
> +                     unit->vers, QTFILE);
> +             perm = &unit->ctlperm;
> +             if(emptystr(perm->user)){
> +                     kstrdup(&perm->user, eve);
> +                     perm->perm = 0644;      /* nothing secret
> in ctl */
> +             }
> +             devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
> +             rv = 1;
> +             break;
> +
> +     case Qraw:
> +             mkqid(&q, QID(DEV(c->qid), UNIT(c->qid),
> PART(c->qid), Qraw),
> +                     unit->vers, QTFILE);
> +             perm = &unit->rawperm;
> +             if(emptystr(perm->user)){
> +                     kstrdup(&perm->user, eve);
> +                     perm->perm = DMEXCL|0600;
> +             }
> +             devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
> +             rv = 1;
> +             break;
> +
> +     case Qpart:
> +             pp = &unit->part[PART(c->qid)];
> +             l = (pp->end - pp->start) * unit->secsize;
> +             mkqid(&q, QID(DEV(c->qid), UNIT(c->qid),
> PART(c->qid), Qpart),
> +                     unit->vers+pp->vers, QTFILE);
> +             if(emptystr(pp->SDperm.user))
> +                     kstrdup(&pp->SDperm.user, eve);
> +             devdir(c, q, pp->SDperm.name, l, pp->SDperm.user,
> pp->SDperm.perm, dp);
> +             rv = 1;
> +             break;
> +     }
> +
> +     decref(&sdev->r);
> +     return rv;
> +}
> +
> +static int
> +sd1gen(Chan* c, int i, Dir* dp)
> +{
> +     Qid q;
> +
> +     switch(i){
> +     case Qtopctl:
> +             mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
> +             devdir(c, q, "sdctl", 0, eve, 0644, dp);        /*
> no secrets */
> +             return 1;
> +     }
> +     return -1;
> +}
> +
> +static int
> +sdgen(Chan* c, char* d, Dirtab* dir, int j, int s, Dir* dp)
> +{
> +     Proc *up = externup();
> +     Qid q = {};
> +     int64_t l;
> +     int i, r;
> +     SDpart *pp;
> +     SDunit *unit;
> +     SDev *sdev;
> +
> +     switch(TYPE(c->qid)){
> +     case Qtopdir:
> +             if(s == DEVDOTDOT){
> +                     mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
> +                     sprint(up->genbuf, "#%C", sddevtab.dc);
> +                     devdir(c, q, up->genbuf, 0, eve, 0555, dp);
> +                     return 1;
> +             }
> +
> +             if(s+Qtopbase < Qunitdir)
> +                     return sd1gen(c, s+Qtopbase, dp);
> +             s -= (Qunitdir-Qtopbase);
> +
> +             qlock(&devslock);
> +             for(i=0; i<nelem(devs); i++){
> +                     if(devs[i]){
> +                             if(s < devs[i]->nunit)
> +                                     break;
> +                             s -= devs[i]->nunit;
> +                     }
> +             }
> +
> +             if(i == nelem(devs)){
> +                     /* Run off the end of the list */
> +                     qunlock(&devslock);
> +                     return -1;
> +             }
> +
> +             if((sdev = devs[i]) == nil){
> +                     qunlock(&devslock);
> +                     return 0;
> +             }
> +
> +             incref(&sdev->r);
> +             qunlock(&devslock);
> +
> +             if((unit = sdev->unit[s]) == nil)
> +                     if((unit = sdgetunit(sdev, s)) == nil){
> +                             decref(&sdev->r);
> +                             return 0;
> +                     }
> +
> +             mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR);
> +             if(emptystr(unit->SDperm.user))
> +                     kstrdup(&unit->SDperm.user, eve);
> +             devdir(c, q, unit->SDperm.name, 0,
> unit->SDperm.user, unit->SDperm.perm, dp);
> +             decref(&sdev->r);
> +             return 1;
> +
> +     case Qunitdir:
> +             if(s == DEVDOTDOT){
> +                     mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
> +                     sprint(up->genbuf, "#%C", sddevtab.dc);
> +                     devdir(c, q, up->genbuf, 0, eve, 0555, dp);
> +                     return 1;
> +             }
> +
> +             if((sdev = sdgetdev(DEV(c->qid))) == nil){
> +                     devdir(c, c->qid, "unavailable", 0, eve, 0,
> dp);
> +                     return 1;
> +             }
> +
> +             unit = sdev->unit[UNIT(c->qid)];
> +             qlock(&unit->ctl);
> +
> +             /*
> +              * Check for media change.
> +              * If one has already been detected, sectors will be
> zero.
> +              * If there is one waiting to be detected, online
> +              * will return > 1.
> +              * Online is a bit of a large hammer but does the
> job.
> +              */
> +             if(unit->sectors == 0
> +             || (unit->dev->ifc->online &&
> unit->dev->ifc->online(unit) > 1))
> +                     sdinitpart(unit);
> +
> +             i = s+Qunitbase;
> +             if(i < Qpart){
> +                     r = sd2gen(c, i, dp);
> +                     qunlock(&unit->ctl);
> +                     decref(&sdev->r);
> +                     return r;
> +             }
> +             i -= Qpart;
> +             if(unit->part == nil || i >= unit->npart){
> +                     qunlock(&unit->ctl);
> +                     decref(&sdev->r);
> +                     break;
> +             }
> +             pp = &unit->part[i];
> +             if(!pp->valid){
> +                     qunlock(&unit->ctl);
> +                     decref(&sdev->r);
> +                     return 0;
> +             }
> +             l = (pp->end - pp->start) * (int64_t)unit->secsize;
> +             mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart),
> +                     unit->vers+pp->vers, QTFILE);
> +             if(emptystr(pp->SDperm.user))
> +                     kstrdup(&pp->SDperm.user, eve);
> +             devdir(c, q, pp->SDperm.name, l, pp->SDperm.user,
> pp->SDperm.perm, dp);
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             return 1;
> +     case Qraw:
> +     case Qctl:
> +     case Qpart:
> +             if((sdev = sdgetdev(DEV(c->qid))) == nil){
> +                     devdir(c, q, "unavailable", 0, eve, 0, dp);
> +                     return 1;
> +             }
> +             unit = sdev->unit[UNIT(c->qid)];
> +             qlock(&unit->ctl);
> +             r = sd2gen(c, TYPE(c->qid), dp);
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             return r;
> +     case Qtopctl:
> +             return sd1gen(c, TYPE(c->qid), dp);
> +     default:
> +             break;
> +     }
> +
> +     return -1;
> +}
> +
> +static Chan*
> +sdattach(char* spec)
> +{
> +     Chan *c;
> +     char *p;
> +     SDev *sdev;
> +     int idno, subno;
> +
> +     if(*spec == '\0'){
> +             c = devattach(sddevtab.dc, spec);
> +             mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
> +             return c;
> +     }
> +
> +     if(spec[0] != 's' || spec[1] != 'd')
> +             error(Ebadspec);
> +     idno = spec[2];
> +     subno = strtol(&spec[3], &p, 0);
> +     if(p == &spec[3])
> +             error(Ebadspec);
> +
> +     if((sdev=sdgetdev(idno)) == nil)
> +             error(Enonexist);
> +     if(sdgetunit(sdev, subno) == nil){
> +             decref(&sdev->r);
> +             error(Enonexist);
> +     }
> +
> +     c = devattach(sddevtab.dc, spec);
> +     mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0,
> QTDIR);
> +     c->devno = (sdev->idno << UnitLOG) + subno;
> +     decref(&sdev->r);
> +     return c;
> +}
> +
> +static Walkqid*
> +sdwalk(Chan* c, Chan* nc, char** name, int nname)
> +{
> +     return devwalk(c, nc, name, nname, nil, 0, sdgen);
> +}
> +
> +static int32_t
> +sdstat(Chan* c, uint8_t* db, int32_t n)
> +{
> +     return devstat(c, db, n, nil, 0, sdgen);
> +}
> +
> +static Chan*
> +sdopen(Chan* c, int omode)
> +{
> +     Proc *up = externup();
> +     SDpart *pp;
> +     SDunit *unit;
> +     SDev *sdev;
> +     uint8_t tp;
> +
> +     c = devopen(c, omode, 0, 0, sdgen);
> +     if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
> +             return c;
> +
> +     sdev = sdgetdev(DEV(c->qid));
> +     if(sdev == nil)
> +             error(Enonexist);
> +
> +     unit = sdev->unit[UNIT(c->qid)];
> +
> +     switch(TYPE(c->qid)){
> +     case Qctl:
> +             c->qid.vers = unit->vers;
> +             break;
> +     case Qraw:
> +             c->qid.vers = unit->vers;
> +             if(TAS(&unit->rawinuse) != 0){
> +                     c->flag &= ~COPEN;
> +                     decref(&sdev->r);
> +                     error(Einuse);
> +             }
> +             unit->state = Rawcmd;
> +             break;
> +     case Qpart:
> +             qlock(&unit->ctl);
> +             if(waserror()){
> +                     qunlock(&unit->ctl);
> +                     c->flag &= ~COPEN;
> +                     decref(&sdev->r);
> +                     nexterror();
> +             }
> +             pp = &unit->part[PART(c->qid)];
> +             c->qid.vers = unit->vers+pp->vers;
> +             qunlock(&unit->ctl);
> +             poperror();
> +             break;
> +     }
> +     decref(&sdev->r);
> +     return c;
> +}
> +
> +static void
> +sdclose(Chan* c)
> +{
> +     SDunit *unit;
> +     SDev *sdev;
> +
> +     if(c->qid.type & QTDIR)
> +             return;
> +     if(!(c->flag & COPEN))
> +             return;
> +
> +     switch(TYPE(c->qid)){
> +     default:
> +             break;
> +     case Qraw:
> +             sdev = sdgetdev(DEV(c->qid));
> +             if(sdev){
> +                     unit = sdev->unit[UNIT(c->qid)];
> +                     unit->rawinuse = 0;
> +                     decref(&sdev->r);
> +             }
> +             break;
> +     }
> +}
> +
> +static int32_t
> +sdbio(Chan* c, int write, char* a, int32_t len, int64_t off)
> +{
> +     Proc *up = externup();
> +     int nchange;
> +     uint8_t *b;
> +     SDpart *pp;
> +     SDunit *unit;
> +     SDev *sdev;
> +     int64_t bno;
> +     int32_t l, max, nb, offset;
> +
> +     sdev = sdgetdev(DEV(c->qid));
> +     if(sdev == nil){
> +             decref(&sdev->r);
> +             error(Enonexist);
> +     }
> +     unit = sdev->unit[UNIT(c->qid)];
> +     if(unit == nil)
> +             error(Enonexist);
> +
> +     nchange = 0;
> +     qlock(&unit->ctl);
> +     while(waserror()){
> +             /* notification of media change; go around again */
> +             if(strcmp(up->errstr, Eio) == 0 && unit->sectors ==
> 0 && nchange++ == 0){
> +                     sdinitpart(unit);
> +                     continue;
> +             }
> +
> +             /* other errors; give up */
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             nexterror();
> +     }
> +     pp = &unit->part[PART(c->qid)];
> +     if(unit->vers+pp->vers != c->qid.vers)
> +             error(Echange);
> +
> +     /*
> +      * Check the request is within bounds.
> +      * Removeable drives are locked throughout the I/O
> +      * in case the media changes unexpectedly.
> +      * Non-removeable drives are not locked during the I/O
> +      * to allow the hardware to optimise if it can; this is
> +      * a little fast and loose.
> +      * It's assumed that non-removeable media parameters
> +      * (sectors, secsize) can't change once the drive has
> +      * been brought online.
> +      */
> +     bno = (off/unit->secsize) + pp->start;
> +     nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start -
> bno;
> +     max = SDmaxio/unit->secsize;
> +     if(nb > max)
> +             nb = max;
> +     if(bno+nb > pp->end)
> +             nb = pp->end - bno;
> +     if(bno >= pp->end || nb == 0){
> +             if(write)
> +                     error(Eio);
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             poperror();
> +             return 0;
> +     }
> +     if(!(unit->inquiry[1] & SDinq1removable)){
> +             qunlock(&unit->ctl);
> +             poperror();
> +     }
> +
> +     b = sdmalloc(nb*unit->secsize);
> +     if(b == nil)
> +             error(Enomem);
> +     if(waserror()){
> +             sdfree(b);
> +             if(!(unit->inquiry[1] & SDinq1removable))
> +                     decref(&sdev->r);               /*
> gadverdamme! */
> +             nexterror();
> +     }
> +
> +     offset = off%unit->secsize;
> +     if(offset+len > nb*unit->secsize)
> +             len = nb*unit->secsize - offset;
> +     if(write){
> +             if(offset || (len%unit->secsize)){
> +                     l = unit->dev->ifc->bio(unit, 0, 0, b, nb,
> bno);
> +                     if(l < 0)
> +                             error(Eio);
> +                     if(l < (nb*unit->secsize)){
> +                             nb = l/unit->secsize;
> +                             l = nb*unit->secsize - offset;
> +                             if(len > l)
> +                                     len = l;
> +                     }
> +             }
> +             memmove(b+offset, a, len);
> +             l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
> +             if(l < 0)
> +                     error(Eio);
> +             if(l < offset)
> +                     len = 0;
> +             else if(len > l - offset)
> +                     len = l - offset;
> +     }
> +     else{
> +             l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
> +             if(l < 0)
> +                     error(Eio);
> +             if(l < offset)
> +                     len = 0;
> +             else if(len > l - offset)
> +                     len = l - offset;
> +             memmove(a, b+offset, len);
> +     }
> +     sdfree(b);
> +     poperror();
> +
> +     if(unit->inquiry[1] & SDinq1removable){
> +             qunlock(&unit->ctl);
> +             poperror();
> +     }
> +
> +     decref(&sdev->r);
> +     return len;
> +}
> +
> +static int32_t
> +sdrio(SDreq* r, void* a, int32_t n)
> +{
> +     Proc *up = externup();
> +     void *data;
> +
> +     if(n >= SDmaxio || n < 0)
> +             error(Etoobig);
> +
> +     data = nil;
> +     if(n){
> +             if((data = sdmalloc(n)) == nil)
> +                     error(Enomem);
> +             if(r->write)
> +                     memmove(data, a, n);
> +     }
> +     r->data = data;
> +     r->dlen = n;
> +
> +     if(waserror()){
> +             sdfree(data);
> +             r->data = nil;
> +             nexterror();
> +     }
> +
> +     if(r->unit->dev->ifc->rio(r) != SDok)
> +             error(Eio);
> +
> +     if(!r->write && r->rlen > 0)
> +             memmove(a, data, r->rlen);
> +     sdfree(data);
> +     r->data = nil;
> +     poperror();
> +
> +     return r->rlen;
> +}
> +
> +/*
> + * SCSI simulation for non-SCSI devices
> + */
> +int
> +sdsetsense(SDreq *r, int status, int key, int asc, int ascq)
> +{
> +     int len;
> +     SDunit *unit;
> +
> +     unit = r->unit;
> +     unit->sense[2] = key;
> +     unit->sense[12] = asc;
> +     unit->sense[13] = ascq;
> +
> +     r->status = status;
> +     if(status == SDcheck && !(r->flags & SDnosense)){
> +             /* request sense case from sdfakescsi */
> +             len = sizeof unit->sense;
> +             if(len > sizeof r->sense-1)
> +                     len = sizeof r->sense-1;
> +             memmove(r->sense, unit->sense, len);
> +             unit->sense[2] = 0;
> +             unit->sense[12] = 0;
> +             unit->sense[13] = 0;
> +             r->flags |= SDvalidsense;
> +             return SDok;
> +     }
> +     return status;
> +}
> +
> +int
> +sdmodesense(SDreq *r, uint8_t *cmd, void *info, int ilen)
> +{
> +     int len;
> +     uint8_t *data;
> +
> +     /*
> +      * Fake a vendor-specific request with page code 0,
> +      * return the drive info.
> +      */
> +     if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
> +             return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
> +     len = (cmd[7]<<8)|cmd[8];
> +     if(len == 0)
> +             return SDok;
> +     if(len < 8+ilen)
> +             return sdsetsense(r, SDcheck, 0x05, 0x1A, 0);
> +     if(r->data == nil || r->dlen < len)
> +             return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
> +     data = r->data;
> +     memset(data, 0, 8);
> +     data[0] = ilen>>8;
> +     data[1] = ilen;
> +     if(ilen)
> +             memmove(data+8, info, ilen);
> +     r->rlen = 8+ilen;
> +     return sdsetsense(r, SDok, 0, 0, 0);
> +}
> +
> +int
> +sdfakescsi(SDreq *r, void *info, int ilen)
> +{
> +     uint8_t *cmd, *p;
> +     uint64_t len;
> +     SDunit *unit;
> +
> +     cmd = r->cmd;
> +     r->rlen = 0;
> +     unit = r->unit;
> +
> +     /*
> +      * Rewrite read(6)/write(6) into read(10)/write(10).
> +      */
> +     switch(cmd[0]){
> +     case 0x08:      /* read */
> +     case 0x0A:      /* write */
> +             cmd[9] = 0;
> +             cmd[8] = cmd[4];
> +             cmd[7] = 0;
> +             cmd[6] = 0;
> +             cmd[5] = cmd[3];
> +             cmd[4] = cmd[2];
> +             cmd[3] = cmd[1] & 0x0F;
> +             cmd[2] = 0;
> +             cmd[1] &= 0xE0;
> +             cmd[0] |= 0x20;
> +             break;
> +     }
> +
> +     /*
> +      * 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 sdsetsense(r, SDcheck, 0x05, 0x25, 0);
> +
> +     switch(cmd[0]){
> +     default:
> +             return sdsetsense(r, SDcheck, 0x05, 0x20, 0);
> +
> +     case 0x00:      /* test unit ready */
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x03:      /* request sense */
> +             if(cmd[4] < sizeof unit->sense)
> +                     len = cmd[4];
> +             else
> +                     len = sizeof unit->sense;
> +             if(r->data && r->dlen >= len){
> +                     memmove(r->data, unit->sense, len);
> +                     r->rlen = len;
> +             }
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x12:      /* inquiry */
> +             if(cmd[4] < sizeof unit->inquiry)
> +                     len = cmd[4];
> +             else
> +                     len = sizeof unit->inquiry;
> +             if(r->data && r->dlen >= len){
> +                     memmove(r->data, unit->inquiry, len);
> +                     r->rlen = len;
> +             }
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x1B:      /* start/stop unit */
> +             /*
> +              * nop for now, can use power management later.
> +              */
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x25:      /* read capacity */
> +             if((cmd[1] & 0x01) || cmd[2] || cmd[3])
> +                     return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
> +             if(r->data == nil || r->dlen < 8)
> +                     return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
> +
> +             /*
> +              * Read capacity returns the LBA of the last sector.
> +              */
> +             len = unit->sectors - 1;
> +             p = r->data;
> +             *p++ = len>>24;
> +             *p++ = len>>16;
> +             *p++ = len>>8;
> +             *p++ = len;
> +             len = 512;
> +             *p++ = len>>24;
> +             *p++ = len>>16;
> +             *p++ = len>>8;
> +             *p++ = len;
> +             r->rlen = p - (uint8_t*)r->data;
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x9E:      /* long read capacity */
> +             if((cmd[1] & 0x01) || cmd[2] || cmd[3])
> +                     return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
> +             if(r->data == nil || r->dlen < 8)
> +                     return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
> +             /*
> +              * Read capcity returns the LBA of the last sector.
> +              */
> +             len = unit->sectors - 1;
> +             p = r->data;
> +             *p++ = len>>56;
> +             *p++ = len>>48;
> +             *p++ = len>>40;
> +             *p++ = len>>32;
> +             *p++ = len>>24;
> +             *p++ = len>>16;
> +             *p++ = len>>8;
> +             *p++ = len;
> +             len = 512;
> +             *p++ = len>>24;
> +             *p++ = len>>16;
> +             *p++ = len>>8;
> +             *p++ = len;
> +             r->rlen = p - (uint8_t*)r->data;
> +             return sdsetsense(r, SDok, 0, 0, 0);
> +
> +     case 0x5A:      /* mode sense */
> +             return sdmodesense(r, cmd, info, ilen);
> +
> +     case 0x28:      /* read */
> +     case 0x2A:      /* write */
> +     case 0x88:      /* read16 */
> +     case 0x8a:      /* write16 */
> +             return SDnostatus;
> +     }
> +}
> +
> +static int32_t
> +sdread(Chan *c, void *a, int32_t n, int64_t off)
> +{
> +     Proc *up = externup();
> +     char *p, *e, *buf;
> +     SDpart *pp;
> +     SDunit *unit;
> +     SDev *sdev;
> +     int32_t offset;
> +     int i, l, mm, status;
> +
> +     offset = off;
> +     switch(TYPE(c->qid)){
> +     default:
> +             error(Eperm);
> +     case Qtopctl:
> +             mm = 64*1024;   /* room for register dumps */
> +             p = buf = malloc(mm);
> +             if(p == nil)
> +                     error(Enomem);
> +             e = p + mm;
> +             qlock(&devslock);
> +             for(i = 0; i < nelem(devs); i++){
> +                     sdev = devs[i];
> +                     if(sdev && sdev->ifc->rtopctl)
> +                             p = sdev->ifc->rtopctl(sdev, p, e);
> +             }
> +             qunlock(&devslock);
> +             n = readstr(offset, a, n, buf);
> +             free(buf);
> +             return n;
> +
> +     case Qtopdir:
> +     case Qunitdir:
> +             return devdirread(c, a, n, 0, 0, sdgen);
> +
> +     case Qctl:
> +             sdev = sdgetdev(DEV(c->qid));
> +             if(sdev == nil)
> +                     error(Enonexist);
> +
> +             unit = sdev->unit[UNIT(c->qid)];
> +             mm = 16*1024;   /* room for register dumps */
> +             p = malloc(mm);
> +             if(p == nil)
> +                     error(Enomem);
> +             l = snprint(p, mm, "inquiry %.48s\n",
> +                     (char*)unit->inquiry+8);
> +             qlock(&unit->ctl);
> +             /*
> +              * If there's a device specific routine it must
> +              * provide all information pertaining to night
> geometry
> +              * and the garscadden trains.
> +              */
> +             if(unit->dev->ifc->rctl)
> +                     l += unit->dev->ifc->rctl(unit, p+l, mm-l);
> +             if(unit->sectors == 0)
> +                     sdinitpart(unit);
> +             if(unit->sectors){
> +                     if(unit->dev->ifc->rctl == nil)
> +                             l += snprint(p+l, mm-l,
> +                                     "geometry %llu %lu\n",
> +                                     unit->sectors,
> unit->secsize);
> +                     pp = unit->part;
> +                     for(i = 0; i < unit->npart; i++){
> +                             if(pp->valid)
> +                                     l += snprint(p+l, mm-l,
> +                                             "part %s %llu
> %llu\n",
> +                                             pp->SDperm.name,
> pp->start, pp->end);
> +                             pp++;
> +                     }
> +             }
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             l = readstr(offset, a, n, p);
> +             free(p);
> +             return l;
> +
> +     case Qraw:
> +             sdev = sdgetdev(DEV(c->qid));
> +             if(sdev == nil)
> +                     error(Enonexist);
> +
> +             unit = sdev->unit[UNIT(c->qid)];
> +             qlock(&unit->raw);
> +             if(waserror()){
> +                     qunlock(&unit->raw);
> +                     decref(&sdev->r);
> +                     nexterror();
> +             }
> +             if(unit->state == Rawdata){
> +                     unit->state = Rawstatus;
> +                     i = sdrio(unit->req, a, n);
> +             }
> +             else if(unit->state == Rawstatus){
> +                     status = unit->req->status;
> +                     unit->state = Rawcmd;
> +                     free(unit->req);
> +                     unit->req = nil;
> +                     i = readnum(0, a, n, status, NUMSIZE);
> +             } else
> +                     i = 0;
> +             qunlock(&unit->raw);
> +             decref(&sdev->r);
> +             poperror();
> +             return i;
> +
> +     case Qpart:
> +             return sdbio(c, 0, a, n, off);
> +     }
> +}
> +
> +static void legacytopctl(Cmdbuf*);
> +
> +static int32_t
> +sdwrite(Chan* c, void* a, int32_t n, int64_t off)
> +{
> +     Proc *up = externup();
> +     char *f0;
> +     int i;
> +     uint64_t end, start;
> +     Cmdbuf *cb;
> +     SDifc *ifc;
> +     SDreq *req;
> +     SDunit *unit;
> +     SDev *sdev;
> +
> +     switch(TYPE(c->qid)){
> +     default:
> +             error(Eperm);
> +     case Qtopctl:
> +             cb = parsecmd(a, n);
> +             if(waserror()){
> +                     free(cb);
> +                     nexterror();
> +             }
> +             if(cb->nf == 0)
> +                     error("empty control message");
> +             f0 = cb->f[0];
> +             cb->f++;
> +             cb->nf--;
> +             if(strcmp(f0, "config") == 0){
> +                     /* wormhole into ugly legacy interface */
> +                     legacytopctl(cb);
> +                     poperror();
> +                     free(cb);
> +                     break;
> +             }
> +             /*
> +              * "ata arg..." invokes sdifc[i]->wtopctl(nil, cb),
> +              * where sdifc[i]->SDperm.name=="ata" and cb
> contains the args.
> +              */
> +             ifc = nil;
> +             sdev = nil;
> +             for(i=0; sdifc[i]; i++){
> +                     if(strcmp(sdifc[i]->name, f0) == 0){
> +                             ifc = sdifc[i];
> +                             sdev = nil;
> +                             goto subtopctl;
> +                     }
> +             }
> +             /*
> +              * "sd1 arg..." invokes sdifc[i]->wtopctl(sdev, cb),
> +              * where sdifc[i] and sdev match controller letter
> "1",
> +              * and cb contains the args.
> +              */
> +             if(f0[0]=='s' && f0[1]=='d' && f0[2] && f0[3] == 0){
> +                     if((sdev = sdgetdev(f0[2])) != nil){
> +                             ifc = sdev->ifc;
> +                             goto subtopctl;
> +                     }
> +             }
> +             error("unknown interface");
> +
> +     subtopctl:
> +             if(waserror()){
> +                     if(sdev)
> +                             decref(&sdev->r);
> +                     nexterror();
> +             }
> +             if(ifc->wtopctl)
> +                     ifc->wtopctl(sdev, cb);
> +             else
> +                     error(Ebadctl);
> +             poperror();
> +             poperror();
> +             if (sdev)
> +                     decref(&sdev->r);
> +             free(cb);
> +             break;
> +
> +     case Qctl:
> +             cb = parsecmd(a, n);
> +             sdev = sdgetdev(DEV(c->qid));
> +             if(sdev == nil)
> +                     error(Enonexist);
> +             unit = sdev->unit[UNIT(c->qid)];
> +
> +             qlock(&unit->ctl);
> +             if(waserror()){
> +                     qunlock(&unit->ctl);
> +                     decref(&sdev->r);
> +                     free(cb);
> +                     nexterror();
> +             }
> +             if(unit->vers != c->qid.vers)
> +                     error(Echange);
> +
> +             if(cb->nf < 1)
> +                     error(Ebadctl);
> +             if(strcmp(cb->f[0], "part") == 0){
> +                     if(cb->nf != 4)
> +                             error(Ebadctl);
> +                     if(unit->sectors == 0 && !sdinitpart(unit))
> +                             error(Eio);
> +                     start = strtoull(cb->f[2], 0, 0);
> +                     end = strtoull(cb->f[3], 0, 0);
> +                     sdaddpart(unit, cb->f[1], start, end);
> +             }
> +             else if(strcmp(cb->f[0], "delpart") == 0){
> +                     if(cb->nf != 2 || unit->part == nil)
> +                             error(Ebadctl);
> +                     sddelpart(unit, cb->f[1]);
> +             }
> +             else if(unit->dev->ifc->wctl)
> +                     unit->dev->ifc->wctl(unit, cb);
> +             else
> +                     error(Ebadctl);
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             poperror();
> +             free(cb);
> +             break;
> +
> +     case Qraw:
> +             sdev = sdgetdev(DEV(c->qid));
> +             if(sdev == nil)
> +                     error(Enonexist);
> +             unit = sdev->unit[UNIT(c->qid)];
> +             qlock(&unit->raw);
> +             if(waserror()){
> +                     qunlock(&unit->raw);
> +                     decref(&sdev->r);
> +                     nexterror();
> +             }
> +             switch(unit->state){
> +             case Rawcmd:
> +                     if(n < 6 || n > sizeof(req->cmd))
> +                             error(Ebadarg);
> +                     if((req = malloc(sizeof(SDreq))) == nil)
> +                             error(Enomem);
> +                     req->unit = unit;
> +                     memmove(req->cmd, a, n);
> +                     req->clen = n;
> +                     req->flags = SDnosense;
> +                     req->status = ~0;
> +
> +                     unit->req = req;
> +                     unit->state = Rawdata;
> +                     break;
> +
> +             case Rawstatus:
> +                     unit->state = Rawcmd;
> +                     free(unit->req);
> +                     unit->req = nil;
> +                     error(Ebadusefd);
> +
> +             case Rawdata:
> +                     unit->state = Rawstatus;
> +                     unit->req->write = 1;
> +                     n = sdrio(unit->req, a, n);
> +             }
> +             qunlock(&unit->raw);
> +             decref(&sdev->r);
> +             poperror();
> +             break;
> +     case Qpart:
> +             return sdbio(c, 1, a, n, off);
> +     }
> +
> +     return n;
> +}
> +
> +static int32_t
> +sdwstat(Chan* c, uint8_t* dp, int32_t n)
> +{
> +     Proc *up = externup();
> +     Dir *d;
> +     SDpart *pp;
> +     SDperm *perm;
> +     SDunit *unit;
> +     SDev *sdev;
> +
> +     if(c->qid.type & QTDIR)
> +             error(Eperm);
> +
> +     sdev = sdgetdev(DEV(c->qid));
> +     if(sdev == nil)
> +             error(Enonexist);
> +     unit = sdev->unit[UNIT(c->qid)];
> +     qlock(&unit->ctl);
> +     d = nil;
> +     if(waserror()){
> +             free(d);
> +             qunlock(&unit->ctl);
> +             decref(&sdev->r);
> +             nexterror();
> +     }
> +
> +     switch(TYPE(c->qid)){
> +     default:
> +             error(Eperm);
> +     case Qctl:
> +             perm = &unit->ctlperm;
> +             break;
> +     case Qraw:
> +             perm = &unit->rawperm;
> +             break;
> +     case Qpart:
> +             pp = &unit->part[PART(c->qid)];
> +             if(unit->vers+pp->vers != c->qid.vers)
> +                     error(Enonexist);
> +             perm = &pp->SDperm;
> +             break;
> +     }
> +
> +     if(strcmp(up->user, perm->user) && !iseve())
> +             error(Eperm);
> +
> +     d = smalloc(sizeof(Dir)+n);
> +     n = convM2D(dp, n, &d[0], (char*)&d[1]);
> +     if(n == 0)
> +             error(Eshortstat);
> +     if(!emptystr(d[0].uid))
> +             kstrdup(&perm->user, d[0].uid);
> +     if(d[0].mode != (uint32_t)~0UL)
> +             perm->perm = (perm->perm & ~0777) | (d[0].mode &
> 0777); +
> +     free(d);
> +     qunlock(&unit->ctl);
> +     decref(&sdev->r);
> +     poperror();
> +     return n;
> +}
> +
> +static int
> +configure(char* spec, DevConf* cf)
> +{
> +     SDev *s, *sdev;
> +     char *p;
> +     int i;
> +
> +     if(sdindex(*spec) < 0)
> +             error("bad sd spec");
> +
> +     if((p = strchr(cf->type, '/')) != nil)
> +             *p++ = '\0';
> +
> +     for(i = 0; sdifc[i] != nil; i++)
> +             if(strcmp(sdifc[i]->name, cf->type) == 0)
> +                     break;
> +     if(sdifc[i] == nil)
> +             error("sd type not found");
> +     if(p)
> +             *(p-1) = '/';
> +
> +     if(sdifc[i]->probe == nil)
> +             error("sd type cannot probe");
> +
> +     sdev = sdifc[i]->probe(cf);
> +     for(s=sdev; s; s=s->next)
> +             s->idno = *spec;
> +     sdadddevs(sdev);
> +     return 0;
> +}
> +
> +static int
> +unconfigure(char* spec)
> +{
> +     int i;
> +     SDev *sdev;
> +     SDunit *unit;
> +
> +     if((i = sdindex(*spec)) < 0)
> +             error(Enonexist);
> +
> +     qlock(&devslock);
> +     if((sdev = devs[i]) == nil){
> +             qunlock(&devslock);
> +             error(Enonexist);
> +     }
> +     if(sdev->r.ref){
> +             qunlock(&devslock);
> +             error(Einuse);
> +     }
> +     devs[i] = nil;
> +     qunlock(&devslock);
> +
> +     /* make sure no interrupts arrive anymore before removing
> resources */
> +     if(sdev->enabled && sdev->ifc->disable)
> +             sdev->ifc->disable(sdev);
> +
> +     for(i = 0; i != sdev->nunit; i++){
> +             if(unit = sdev->unit[i]){
> +                     free(unit->SDperm.name);
> +                     free(unit->SDperm.user);
> +                     free(unit);
> +             }
> +     }
> +
> +     if(sdev->ifc->clear)
> +             sdev->ifc->clear(sdev);
> +     free(sdev);
> +     return 0;
> +}
> +
> +static int
> +sdconfig(int on, char* spec, DevConf* cf)
> +{
> +     if(on)
> +             return configure(spec, cf);
> +     return unconfigure(spec);
> +}
> +
> +Dev sddevtab = {
> +     .dc = 'S',
> +     .name = "sd",
> +
> +     .reset = sdreset,
> +     .init = devinit,
> +     .shutdown = devshutdown,
> +     .attach = sdattach,
> +     .walk = sdwalk,
> +     .stat = sdstat,
> +     .open = sdopen,
> +     .create = devcreate,
> +     .close = sdclose,
> +     .read = sdread,
> +     .bread = devbread,
> +     .write = sdwrite,
> +     .bwrite = devbwrite,
> +     .remove = devremove,
> +     .wstat = sdwstat,
> +     .power = devpower,
> +     .config = sdconfig,     /* probe; only called for
> pcmcia-like devices */ +};
> +
> +/*
> + * This is wrong for so many reasons.  This code must go.
> + */
> +typedef struct Confdata Confdata;
> +struct Confdata {
> +     int     on;
> +     char*   spec;
> +     DevConf cf;
> +};
> +
> +static void
> +parseswitch(Confdata* cd, char* option)
> +{
> +     if(!strcmp("on", option))
> +             cd->on = 1;
> +     else if(!strcmp("off", option))
> +             cd->on = 0;
> +     else
> +             error(Ebadarg);
> +}
> +
> +static void
> +parsespec(Confdata* cd, char* option)
> +{
> +     if(strlen(option) > 1)
> +             error(Ebadarg);
> +     cd->spec = option;
> +}
> +
> +static Devport*
> +getnewport(DevConf* dc)
> +{
> +     Devport *p;
> +
> +     p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport));
> +     if(p == nil)
> +             error(Enomem);
> +     if(dc->nports > 0){
> +             memmove(p, dc->ports, dc->nports * sizeof(Devport));
> +             free(dc->ports);
> +     }
> +     dc->ports = p;
> +     p = &dc->ports[dc->nports++];
> +     p->size = -1;
> +     p->port = (uint32_t)-1;
> +     return p;
> +}
> +
> +static void
> +parseport(Confdata* cd, char* option)
> +{
> +     char *e;
> +     Devport *p;
> +
> +     if(cd->cf.nports == 0 ||
> cd->cf.ports[cd->cf.nports-1].port != (uint32_t)-1)
> +             p = getnewport(&cd->cf);
> +     else
> +             p = &cd->cf.ports[cd->cf.nports-1];
> +     p->port = strtol(option, &e, 0);
> +     if(e == nil || *e != '\0')
> +             error(Ebadarg);
> +}
> +
> +static void
> +parsesize(Confdata* cd, char* option)
> +{
> +     char *e;
> +     Devport *p;
> +
> +     if(cd->cf.nports == 0 ||
> cd->cf.ports[cd->cf.nports-1].size != -1)
> +             p = getnewport(&cd->cf);
> +     else
> +             p = &cd->cf.ports[cd->cf.nports-1];
> +     p->size = (int)strtol(option, &e, 0);
> +     if(e == nil || *e != '\0')
> +             error(Ebadarg);
> +}
> +
> +static void
> +parseirq(Confdata* cd, char* option)
> +{
> +     char *e;
> +
> +     cd->cf.intnum = strtoul(option, &e, 0);
> +     if(e == nil || *e != '\0')
> +             error(Ebadarg);
> +}
> +
> +static void
> +parsetype(Confdata* cd, char* option)
> +{
> +     cd->cf.type = option;
> +}
> +
> +static struct {
> +     char    *name;
> +     void    (*parse)(Confdata*, char*);
> +} options[] = {
> +     "switch",       parseswitch,
> +     "spec",         parsespec,
> +     "port",         parseport,
> +     "size",         parsesize,
> +     "irq",          parseirq,
> +     "type",         parsetype,
> +};
> +
> +static void
> +legacytopctl(Cmdbuf *cb)
> +{
> +     char *opt;
> +     int i, j;
> +     Confdata cd;
> +
> +     memset(&cd, 0, sizeof cd);
> +     cd.on = -1;
> +     for(i=0; i<cb->nf; i+=2){
> +             if(i+2 > cb->nf)
> +                     error(Ebadarg);
> +             opt = cb->f[i];
> +             for(j=0; j<nelem(options); j++)
> +                     if(strcmp(opt, options[j].name) == 0){
> +                             options[j].parse(&cd, cb->f[i+1]);
> +                             break;
> +                     }
> +             if(j == nelem(options))
> +                     error(Ebadarg);
> +     }
> +     /* this has been rewritten to accomodate sdaoe */
> +     if(cd.on < 0 || cd.spec == 0)
> +             error(Ebadarg);
> +     if(cd.on && cd.cf.type == nil)
> +             error(Ebadarg);
> +     sdconfig(cd.on, cd.spec, &cd.cf);
> +}
> diff --git a/kern/drivers/dev/sdiahci.c b/kern/drivers/dev/sdiahci.c
> new file mode 100644
> index 0000000..13665da
> --- /dev/null
> +++ b/kern/drivers/dev/sdiahci.c
> @@ -0,0 +1,2461 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to
> the license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at
> http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be
> copied,
> + * modified, propagated, or distributed except according to the
> terms contained
> + * in the LICENSE file.
> + */
> +
> +/*
> + * ahci serial ata driver
> + * copyright © 2007-8 coraid, inc.
> + */
> +
> +#include "u.h"
> +#include "../port/lib.h"
> +#include "mem.h"
> +#include "dat.h"
> +#include "fns.h"
> +#include "io.h"
> +#include "../port/error.h"
> +#include "../port/sd.h"
> +#include "ahci.h"
> +
> +enum {
> +     Vatiamd = 0x1002,
> +     Vintel  = 0x8086,
> +     Vmarvell= 0x1b4b,
> +};
> +
> +#define      dprint(...)     if(debug)
> iprint(__VA_ARGS__); else USED(debug) +#define
> idprint(...)  if(prid)        iprint(__VA_ARGS__);  else
> USED(prid) +#define   aprint(...)     if(datapi)
> iprint(__VA_ARGS__);  else USED(datapi) + +#define Tname(c)
> tname[(c)->type] +#define Intel(x)    ((x)->pci->vid == Vintel)
> +
> +enum {
> +     NCtlr   = 16,
> +     NCtlrdrv= 32,
> +     NDrive  = NCtlr*NCtlrdrv,
> +
> +     Read    = 0,
> +     Write,
> +
> +     Nms     = 256,                  /* ms. between
> drive checks */
> +     Mphywait=  2*1024/Nms - 1,
> +     Midwait = 16*1024/Nms - 1,
> +     Mcomrwait= 64*1024/Nms - 1,
> +
> +     Obs     = 0xa0,                 /* obsolete device
> bits */ +
> +     /*
> +      * if we get more than this many interrupts per tick for a
> drive,
> +      * either the hardware is broken or we've got a bug in this
> driver.
> +      */
> +     Maxintrspertick = 2000,         /* was 1000 */
> +};
> +
> +/* pci space configuration */
> +enum {
> +     Pmap    = 0x90,
> +     Ppcs    = 0x91,
> +     Prev    = 0xa8,
> +};
> +
> +enum {
> +     Tesb,
> +     Tich,
> +     Tsb600,
> +     Tunk,
> +};
> +
> +static char *tname[] = {
> +     "63xxesb",
> +     "ich",
> +     "sb600",
> +     "unknown",
> +};
> +
> +enum {
> +     Dnull,
> +     Dmissing,
> +     Dnew,
> +     Dready,
> +     Derror,
> +     Dreset,
> +     Doffline,
> +     Dportreset,
> +     Dlast,
> +};
> +
> +static char *diskstates[Dlast] = {
> +     "null",
> +     "missing",
> +     "new",
> +     "ready",
> +     "error",
> +     "reset",
> +     "offline",
> +     "portreset",
> +};
> +
> +enum {
> +     DMautoneg,
> +     DMsatai,
> +     DMsataii,
> +     DMsata3,
> +};
> +
> +static char *modename[] = {          /* used in control
> messages */
> +     "auto",
> +     "satai",
> +     "sataii",
> +     "sata3",
> +};
> +static char *descmode[] = {          /*  only printed */
> +     "auto",
> +     "sata 1",
> +     "sata 2",
> +     "sata 3",
> +};
> +
> +static char *flagname[] = {
> +     "llba",
> +     "smart",
> +     "power",
> +     "nop",
> +     "atapi",
> +     "atapi16",
> +};
> +
> +typedef struct Asleep Asleep;
> +typedef struct Ctlr Ctlr;
> +typedef struct Drive Drive;
> +
> +struct Drive {
> +     Lock Lock;
> +
> +     Ctlr    *ctlr;
> +     SDunit  *unit;
> +     char    name[10];
> +     Aport   *port;
> +     Aportm  portm;
> +     Aportc  portc;          /* redundant ptr to port
> and portm */ +
> +     unsigned char   mediachange;
> +     unsigned char   state;
> +     unsigned char   smartrs;
> +
> +     uint64_t        sectors;
> +     uint32_t        secsize;
> +     uint32_t        intick;         /* start tick of
> current transfer */
> +     uint32_t        lastseen;
> +     int     wait;
> +     unsigned char   mode;           /* DMautoneg,
> satai or sataii */
> +     unsigned char   active;
> +
> +     char    serial[20+1];
> +     char    firmware[8+1];
> +     char    model[40+1];
> +
> +     int     infosz;
> +     uint16_t        *info;
> +     uint16_t        tinyinfo[2];    /* used iff malloc fails
> */ +
> +     int     driveno;        /* ctlr*NCtlrdrv + unit */
> +     /* controller port # != driveno when not all ports are
> enabled */
> +     int     portno;
> +
> +     uint32_t        lastintr0;
> +     uint32_t        intrs;
> +};
> +
> +struct Ctlr {
> +     Lock Lock;
> +
> +     int     type;
> +     int     enabled;
> +     SDev    *sdev;
> +     Pcidev  *pci;
> +     void*   vector;
> +
> +     /* virtual register addresses */
> +     unsigned char   *mmio;
> +     uint32_t        *lmmio;
> +     Ahba    *hba;
> +
> +     /* phyical register address */
> +     unsigned char   *physio;
> +
> +     Drive   *rawdrive;
> +     Drive   *drive[NCtlrdrv];
> +     int     ndrive;
> +     int     mport;          /* highest drive #
> (0-origin) on ich9 at least */ +
> +     uint32_t        lastintr0;
> +     uint32_t        intrs;          /* not attributable to
> any drive */ +};
> +
> +struct Asleep {
> +     Aport   *p;
> +     int     i;
> +};
> +
> +extern SDifc sdiahciifc;
> +
> +static       Ctlr    iactlr[NCtlr];
> +static       SDev    sdevs[NCtlr];
> +static       int     niactlr;
> +
> +static       Drive   *iadrive[NDrive];
> +static       int     niadrive;
> +
> +/* these are fiddled in iawtopctl() */
> +static       int     debug;
> +static       int     prid = 1;
> +static       int     datapi;
> +
> +// TODO: does this get initialized correctly? 
> +static char stab[] = {
> +[0]  = 'i', 'm',
> +[8]  = 't', 'c', 'p', 'e',
> +[16] = 'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
> +};
> +
> +static void
> +serrstr(uint32_t r, char *s, char *e)
> +{
> +     int i;
> +
> +     e -= 3;
> +     for(i = 0; i < nelem(stab) && s < e; i++)
> +             if(r & (1<<i) && stab[i]){
> +                     *s++ = stab[i];
> +                     if(SerrBad & (1<<i))
> +                             *s++ = '*';
> +             }
> +     *s = 0;
> +}
> +
> +static char ntab[] = "0123456789abcdef";
> +
> +static void
> +preg(unsigned char *reg, int n)
> +{
> +     int i;
> +     char buf[25*3+1], *e;
> +
> +     e = buf;
> +     for(i = 0; i < n; i++){
> +             *e++ = ntab[reg[i]>>4];
> +             *e++ = ntab[reg[i]&0xf];
> +             *e++ = ' ';
> +     }
> +     *e++ = '\n';
> +     *e = 0;
> +     dprint(buf);
> +}
> +
> +static void
> +dreg(char *s, Aport *p)
> +{
> +     dprint("ahci: %stask=%#lx; cmd=%#lx; ci=%#lx; is=%#lx\n",
> +             s, p->task, p->cmd, p->ci, p->isr);
> +}
> +
> +static void
> +esleep(int ms)
> +{
> +     Proc *up = externup();
> +     if(waserror())
> +             return;
> +     tsleep(&up->sleep, return0, 0, ms);
> +     poperror();
> +}
> +
> +static int
> +ahciclear(void *v)
> +{
> +     Asleep *s;
> +
> +     s = v;
> +     return (s->p->ci & s->i) == 0;
> +}
> +
> +static void
> +aesleep(Aportm *pm, Asleep *a, int ms)
> +{
> +     Proc *up = externup();
> +     if(waserror())
> +             return;
> +     tsleep(&pm->Rendez, ahciclear, a, ms);
> +     poperror();
> +}
> +
> +static int
> +ahciwait(Aportc *c, int ms)
> +{
> +     Asleep as;
> +     Aport *p;
> +
> +     p = c->p;
> +     p->ci = 1;
> +     as.p = p;
> +     as.i = 1;
> +     aesleep(c->pm, &as, ms);
> +     if((p->task&1) == 0 && p->ci == 0)
> +             return 0;
> +     dreg("ahciwait timeout ", c->p);
> +     return -1;
> +}
> +
> +/* fill in cfis boilerplate */
> +static unsigned char *
> +cfissetup(Aportc *pc)
> +{
> +     unsigned char *cfis;
> +
> +     cfis = pc->pm->ctab->cfis;
> +     memset(cfis, 0, 0x20);
> +     cfis[0] = 0x27;
> +     cfis[1] = 0x80;
> +     cfis[7] = Obs;
> +     return cfis;
> +}
> +
> +/* initialise pc's list */
> +static void
> +listsetup(Aportc *pc, int flags)
> +{
> +     Alist *list;
> +
> +     list = pc->pm->list;
> +     list->flags = flags | 5;
> +     list->len = 0;
> +     list->ctab = PCIWADDR(pc->pm->ctab);
> +     list->ctabhi = 0;
> +}
> +
> +static int
> +nop(Aportc *pc)
> +{
> +     unsigned char *c;
> +
> +     if((pc->pm->feat & Dnop) == 0)
> +             return -1;
> +     c = cfissetup(pc);
> +     c[2] = 0;
> +     listsetup(pc, Lwrite);
> +     return ahciwait(pc, 3*1000);
> +}
> +
> +static int
> +setfeatures(Aportc *pc, unsigned char f)
> +{
> +     unsigned char *c;
> +
> +     c = cfissetup(pc);
> +     c[2] = 0xef;
> +     c[3] = f;
> +     listsetup(pc, Lwrite);
> +     return ahciwait(pc, 3*1000);
> +}
> +
> +static int
> +setudmamode(Aportc *pc, unsigned char f)
> +{
> +     unsigned char *c;
> +
> +     /* hack */
> +     if((pc->p->sig >> 16) == 0xeb14)
> +             return 0;
> +     c = cfissetup(pc);
> +     c[2] = 0xef;
> +     c[3] = 3;               /* set transfer mode */
> +     c[12] = 0x40 | f;       /* sector count */
> +     listsetup(pc, Lwrite);
> +     return ahciwait(pc, 3*1000);
> +}
> +
> +static void
> +asleep(int ms)
> +{
> +     Proc *up = externup();
> +     if(up == nil)
> +             delay(ms);
> +     else
> +             esleep(ms);
> +}
> +
> +static int
> +ahciportreset(Aportc *c)
> +{
> +     uint32_t *cmd, i;
> +     Aport *p;
> +
> +     p = c->p;
> +     cmd = &p->cmd;
> +     *cmd &= ~(Afre|Ast);
> +     for(i = 0; i < 500; i += 25){
> +             if((*cmd&Acr) == 0)
> +                     break;
> +             asleep(25);
> +     }
> +     p->sctl = 1|(p->sctl&~7);
> +     delay(1);
> +     p->sctl &= ~7;
> +     return 0;
> +}
> +
> +static int
> +smart(Aportc *pc, int n)
> +{
> +     unsigned char *c;
> +
> +     if((pc->pm->feat&Dsmart) == 0)
> +             return -1;
> +     c = cfissetup(pc);
> +     c[2] = 0xb0;
> +     c[3] = 0xd8 + n;        /* able smart */
> +     c[5] = 0x4f;
> +     c[6] = 0xc2;
> +     listsetup(pc, Lwrite);
> +     if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
> +             dprint("ahci: smart fail %#lx\n", pc->p->task);
> +//           preg(pc->m->fis.r, 20);
> +             return -1;
> +     }
> +     if(n)
> +             return 0;
> +     return 1;
> +}
> +
> +static int
> +smartrs(Aportc *pc)
> +{
> +     unsigned char *c;
> +
> +     c = cfissetup(pc);
> +     c[2] = 0xb0;
> +     c[3] = 0xda;            /* return smart status */
> +     c[5] = 0x4f;
> +     c[6] = 0xc2;
> +     listsetup(pc, Lwrite);
> +
> +     c = pc->pm->fis.r;
> +     if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
> +             dprint("ahci: smart fail %#lx\n", pc->p->task);
> +             preg(c, 20);
> +             return -1;
> +     }
> +     if(c[5] == 0x4f && c[6] == 0xc2)
> +             return 1;
> +     return 0;
> +}
> +
> +static int
> +ahciflushcache(Aportc *pc)
> +{
> +     unsigned char *c;
> +
> +     c = cfissetup(pc);
> +     c[2] = pc->pm->feat & Dllba? 0xea: 0xe7;
> +     listsetup(pc, Lwrite);
> +     if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
> +             dprint("ahciflushcache: fail %#lx\n", pc->p->task);
> +//           preg(pc->m->fis.r, 20);
> +             return -1;
> +     }
> +     return 0;
> +}
> +
> +static uint16_t
> +gbit16(void *a)
> +{
> +     unsigned char *i;
> +
> +     i = a;
> +     return i[1]<<8 | i[0];
> +}
> +
> +static uint32_t
> +gbit32(void *a)
> +{
> +     uint32_t j;
> +     unsigned char *i;
> +
> +     i = a;
> +     j  = i[3] << 24;
> +     j |= i[2] << 16;
> +     j |= i[1] << 8;
> +     j |= i[0];
> +     return j;
> +}
> +
> +static uint64_t
> +gbit64(void *a)
> +{
> +     unsigned char *i;
> +
> +     i = a;
> +     return (uint64_t)gbit32(i+4) << 32 | gbit32(a);
> +}
> +
> +static int
> +ahciidentify0(Aportc *pc, void *id, int atapi)
> +{
> +     unsigned char *c;
> +     Aprdt *p;
> +     static unsigned char tab[] = { 0xec, 0xa1, };
> +
> +     c = cfissetup(pc);
> +     c[2] = tab[atapi];
> +     listsetup(pc, 1<<16);
> +
> +     memset(id, 0, 0x100);                   /* magic */
> +     p = &pc->pm->ctab->prdt;
> +     p->dba = PCIWADDR(id);
> +     p->dbahi = 0;
> +     p->count = 1<<31 | (0x200-2) | 1;
> +     return ahciwait(pc, 3*1000);
> +}
> +
> +static int64_t
> +ahciidentify(Aportc *pc, uint16_t *id)
> +{
> +     int i, sig;
> +     int64_t s;
> +     Aportm *pm;
> +
> +     pm = pc->pm;
> +     pm->feat = 0;
> +     pm->smart = 0;
> +     i = 0;
> +     sig = pc->p->sig >> 16;
> +     if(sig == 0xeb14){
> +             pm->feat |= Datapi;
> +             i = 1;
> +     }
> +     if(ahciidentify0(pc, id, i) == -1)
> +             return -1;
> +
> +     i = gbit16(id+83) | gbit16(id+86);
> +     if(i & (1<<10)){
> +             pm->feat |= Dllba;
> +             s = gbit64(id+100);
> +     }else
> +             s = gbit32(id+60);
> +
> +     if(pm->feat&Datapi){
> +             i = gbit16(id+0);
> +             if(i&1)
> +                     pm->feat |= Datapi16;
> +     }
> +
> +     i = gbit16(id+83);
> +     if((i>>14) == 1) {
> +             if(i & (1<<3))
> +                     pm->feat |= Dpower;
> +             i = gbit16(id+82);
> +             if(i & 1)
> +                     pm->feat |= Dsmart;
> +             if(i & (1<<14))
> +                     pm->feat |= Dnop;
> +     }
> +     return s;
> +}
> +
> +#if 0
> +static int
> +ahciquiet(Aport *a)
> +{
> +     uint32_t *p, i;
> +
> +     p = &a->cmd;
> +     *p &= ~Ast;
> +     for(i = 0; i < 500; i += 50){
> +             if((*p & Acr) == 0)
> +                     goto stop;
> +             asleep(50);
> +     }
> +     return -1;
> +stop:
> +     if((a->task & (ASdrq|ASbsy)) == 0){
> +             *p |= Ast;
> +             return 0;
> +     }
> +
> +     *p |= Aclo;
> +     for(i = 0; i < 500; i += 50){
> +             if((*p & Aclo) == 0)
> +                     goto stop1;
> +             asleep(50);
> +     }
> +     return -1;
> +stop1:
> +     /* extra check */
> +     dprint("ahci: clo clear %#lx\n", a->task);
> +     if(a->task & ASbsy)
> +             return -1;
> +     *p |= Ast;
> +     return 0;
> +}
> +#endif
> +
> +#if 0
> +static int
> +ahcicomreset(Aportc *pc)
> +{
> +     unsigned char *c;
> +
> +     dprint("ahcicomreset\n");
> +     dreg("ahci: comreset ", pc->p);
> +     if(ahciquiet(pc->p) == -1){
> +             dprint("ahciquiet failed\n");
> +             return -1;
> +     }
> +     dreg("comreset ", pc->p);
> +
> +     c = cfissetup(pc);
> +     c[1] = 0;
> +     c[15] = 1<<2;           /* srst */
> +     listsetup(pc, Lclear | Lreset);
> +     if(ahciwait(pc, 500) == -1){
> +             dprint("ahcicomreset: first command failed\n");
> +             return -1;
> +     }
> +     microdelay(250);
> +     dreg("comreset ", pc->p);
> +
> +     c = cfissetup(pc);
> +     c[1] = 0;
> +     listsetup(pc, Lwrite);
> +     if(ahciwait(pc, 150) == -1){
> +             dprint("ahcicomreset: second command failed\n");
> +             return -1;
> +     }
> +     dreg("comreset ", pc->p);
> +     return 0;
> +}
> +#endif
> +
> +static int
> +ahciidle(Aport *port)
> +{
> +     uint32_t *p, i, r;
> +
> +     p = &port->cmd;
> +     if((*p & Arun) == 0)
> +             return 0;
> +     *p &= ~Ast;
> +     r = 0;
> +     for(i = 0; i < 500; i += 25){
> +             if((*p & Acr) == 0)
> +                     goto stop;
> +             asleep(25);
> +     }
> +     r = -1;
> +stop:
> +     if((*p & Afre) == 0)
> +             return r;
> +     *p &= ~Afre;
> +     for(i = 0; i < 500; i += 25){
> +             if((*p & Afre) == 0)
> +                     return 0;
> +             asleep(25);
> +     }
> +     return -1;
> +}
> +
> +/*
> + * § 6.2.2.1  first part; comreset handled by reset disk.
> + *   - remainder is handled by configdisk.
> + *   - ahcirecover is a quick recovery from a failed command.
> + */
> +static int
> +ahciswreset(Aportc *pc)
> +{
> +     int i;
> +
> +     i = ahciidle(pc->p);
> +     pc->p->cmd |= Afre;
> +     if(i == -1)
> +             return -1;
> +     if(pc->p->task & (ASdrq|ASbsy))
> +             return -1;
> +     return 0;
> +}
> +
> +static int
> +ahcirecover(Aportc *pc)
> +{
> +     ahciswreset(pc);
> +     pc->p->cmd |= Ast;
> +     if(setudmamode(pc, 5) == -1)
> +             return -1;
> +     return 0;
> +}
> +
> +static void*
> +malign(int size, int align)
> +{
> +     return mallocalign(size, align, 0, 0);
> +}
> +
> +static void
> +setupfis(Afis *f)
> +{
> +     f->base = malign(0x100, 0x100);         /* magic */
> +     f->d = f->base + 0;
> +     f->p = f->base + 0x20;
> +     f->r = f->base + 0x40;
> +     f->u = f->base + 0x60;
> +     f->devicebits = (uint32_t*)(f->base + 0x58);
> +}
> +
> +static void
> +ahciwakeup(Aport *p)
> +{
> +     uint16_t s;
> +
> +     s = p->sstatus;
> +     if((s & Intpm) != Intslumber && (s & Intpm) != Intpartpwr)
> +             return;
> +     if((s & Devdet) != Devpresent){ /* not (device, no
> phy) */
> +             iprint("ahci: slumbering drive unwakable %#x\n", s);
> +             return;
> +     }
> +     p->sctl = 3*Aipm | 0*Aspd | Adet;
> +     delay(1);
> +     p->sctl &= ~7;
> +//   iprint("ahci: wake %#x -> %#x\n", s, p->sstatus);
> +}
> +
> +static int
> +ahciconfigdrive(Drive *d)
> +{
> +     char *name;
> +     Ahba *h;
> +     Aport *p;
> +     Aportm *pm;
> +
> +     h = d->ctlr->hba;
> +     p = d->portc.p;
> +     pm = d->portc.pm;
> +     if(pm->list == 0){
> +             setupfis(&pm->fis);
> +             pm->list = malign(sizeof *pm->list, 1024);
> +             pm->ctab = malign(sizeof *pm->ctab, 128);
> +     }
> +
> +     if (d->unit)
> +             name = d->unit->SDperm.name;
> +     else
> +             name = nil;
> +     if(p->sstatus & (Devphycomm|Devpresent) && h->cap & Hsss){
> +             /* device connected & staggered spin-up */
> +             dprint("ahci: configdrive: %s: spinning up ...
> [%#lx]\n",
> +                     name, p->sstatus);
> +             p->cmd |= Apod|Asud;
> +             asleep(1400);
> +     }
> +
> +     p->serror = SerrAll;
> +
> +     p->list = PCIWADDR(pm->list);
> +     p->listhi = 0;
> +     p->fis = PCIWADDR(pm->fis.base);
> +     p->fishi = 0;
> +     p->cmd |= Afre|Ast;
> +
> +     /* drive coming up in slumbering? */
> +     if((p->sstatus & Devdet) == Devpresent &&
> +        ((p->sstatus & Intpm) == Intslumber ||
> +         (p->sstatus & Intpm) == Intpartpwr))
> +             ahciwakeup(p);
> +
> +     /* "disable power managment" sequence from book. */
> +     p->sctl = (3*Aipm) | (d->mode*Aspd) | (0*Adet);
> +     p->cmd &= ~Aalpe;
> +
> +     p->ie = IEM;
> +
> +     return 0;
> +}
> +
> +static void
> +ahcienable(Ahba *h)
> +{
> +     h->ghc |= Hie;
> +}
> +
> +static void
> +ahcidisable(Ahba *h)
> +{
> +     h->ghc &= ~Hie;
> +}
> +
> +static int
> +countbits(uint32_t u)
> +{
> +     int n;
> +
> +     n = 0;
> +     for (; u != 0; u >>= 1)
> +             if(u & 1)
> +                     n++;
> +     return n;
> +}
> +
> +static int
> +ahciconf(Ctlr *ctlr)
> +{
> +     Ahba *h;
> +     uint32_t u;
> +
> +     h = ctlr->hba = (Ahba*)ctlr->mmio;
> +     u = h->cap;
> +
> +     if((u&Hsam) == 0)
> +             h->ghc |= Hae;
> +
> +     dprint("#S/sd%c: type %s port %#p: sss %ld ncs %ld coal %ld "
> +             "%ld ports, led %ld clo %ld ems %ld\n",
> +             ctlr->sdev->idno, tname[ctlr->type], h,
> +             (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1,
> +             (u & 0x1f) + 1, (u>>25) & 1, (u>>24) & 1, (u>>6) &
> 1);
> +     return countbits(h->pi);
> +}
> +
> +#if 0
> +static int
> +ahcihbareset(Ahba *h)
> +{
> +     int wait;
> +
> +     h->ghc |= 1;
> +     for(wait = 0; wait < 1000; wait += 100){
> +             if(h->ghc == 0)
> +                     return 0;
> +             delay(100);
> +     }
> +     return -1;
> +}
> +#endif
> +
> +static void
> +idmove(char *p, uint16_t *a, int n)
> +{
> +     int i;
> +     char *op, *e;
> +
> +     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;
> +     for (p = op; *p == ' '; p++)
> +             ;
> +     memmove(op, p, n - (e - p));
> +}
> +
> +static int
> +identify(Drive *d)
> +{
> +     uint16_t *id;
> +     int64_t osectors, s;
> +     unsigned char oserial[21];
> +     SDunit *u;
> +
> +     if(d->info == nil) {
> +             d->infosz = 512 * sizeof(uint16_t);
> +             d->info = malloc(d->infosz);
> +     }
> +     if(d->info == nil) {
> +             d->info = d->tinyinfo;
> +             d->infosz = sizeof d->tinyinfo;
> +     }
> +     id = d->info;
> +     s = ahciidentify(&d->portc, id);
> +     if(s == -1){
> +             d->state = Derror;
> +             return -1;
> +     }
> +     osectors = d->sectors;
> +     memmove(oserial, d->serial, sizeof d->serial);
> +
> +     u = d->unit;
> +     d->sectors = s;
> +     d->secsize = u->secsize;
> +     if(d->secsize == 0)
> +             d->secsize = 512;               /* default */
> +     d->smartrs = 0;
> +
> +     idmove(d->serial, id+10, 20);
> +     idmove(d->firmware, id+23, 8);
> +     idmove(d->model, id+27, 40);
> +
> +     memset(u->inquiry, 0, sizeof u->inquiry);
> +     u->inquiry[2] = 2;
> +     u->inquiry[3] = 2;
> +     u->inquiry[4] = sizeof u->inquiry - 4;
> +     memmove(u->inquiry+8, d->model, 40);
> +
> +     if(osectors != s || memcmp(oserial, d->serial, sizeof
> oserial) != 0){
> +             d->mediachange = 1;
> +             u->sectors = 0;
> +     }
> +     return 0;
> +}
> +
> +static void
> +clearci(Aport *p)
> +{
> +     if(p->cmd & Ast) {
> +             p->cmd &= ~Ast;
> +             p->cmd |=  Ast;
> +     }
> +}
> +
> +static void
> +updatedrive(Drive *d)
> +{
> +     uint32_t cause, serr, s0, pr, ewake;
> +     char *name;
> +     Aport *p;
> +     static uint32_t last;
> +
> +     pr = 1;
> +     ewake = 0;
> +     p = d->port;
> +     cause = p->isr;
> +     serr = p->serror;
> +     p->isr = cause;
> +     name = "??";
> +     if(d->unit && d->unit->SDperm.name)
> +             name = d->unit->SDperm.name;
> +
> +     if(p->ci == 0){
> +             d->portm.flag |= Fdone;
> +             wakeup(&d->portm.Rendez);
> +             pr = 0;
> +     }else if(cause & Adps)
> +             pr = 0;
> +     if(cause & Ifatal){
> +             ewake = 1;
> +             dprint("ahci: updatedrive: %s: fatal\n", name);
> +     }
> +     if(cause & Adhrs){
> +             if(p->task & (1<<5|1)){
> +                     dprint("ahci: %s: Adhrs cause %#lx serr %#lx
> task %#lx\n",
> +                             name, cause, serr, p->task);
> +                     d->portm.flag |= Ferror;
> +                     ewake = 1;
> +             }
> +             pr = 0;
> +     }
> +     if(p->task & 1 && last != cause)
> +             dprint("%s: err ca %#lx serr %#lx task %#lx sstat
> %#lx\n",
> +                     name, cause, serr, p->task, p->sstatus);
> +     if(pr)
> +             dprint("%s: upd %#lx ta %#lx\n", name, cause,
> p->task); +
> +     if(cause & (Aprcs|Aifs)){
> +             s0 = d->state;
> +             switch(p->sstatus & Devdet){
> +             case 0:                         /* no device
> */
> +                     d->state = Dmissing;
> +                     break;
> +             case Devpresent:                /* device but no phy
> comm. */
> +                     if((p->sstatus & Intpm) == Intslumber ||
> +                        (p->sstatus & Intpm) == Intpartpwr)
> +                             d->state = Dnew;        /*
> slumbering */
> +                     else
> +                             d->state = Derror;
> +                     break;
> +             case Devpresent|Devphycomm:
> +                     /* power mgnt crap for surprise removal */
> +                     p->ie |= Aprcs|Apcs;    /* is this
> required? */
> +                     d->state = Dreset;
> +                     break;
> +             case Devphyoffline:
> +                     d->state = Doffline;
> +                     break;
> +             }
> +             dprint("%s: %s → %s [Apcrs] %#lx\n", name,
> +                     diskstates[s0], diskstates[d->state],
> p->sstatus);
> +             /* print pulled message here. */
> +             if(s0 == Dready && d->state != Dready)
> +                     idprint("%s: pulled\n",
> name);                /* wtf? */
> +             if(d->state != Dready)
> +                     d->portm.flag |= Ferror;
> +             ewake = 1;
> +     }
> +     p->serror = serr;
> +     if(ewake){
> +             clearci(p);
> +             wakeup(&d->portm.Rendez);
> +     }
> +     last = cause;
> +}
> +
> +static void
> +pstatus(Drive *d, uint32_t s)
> +{
> +     /*
> +      * s is masked with Devdet.
> +      *
> +      * bogus code because the first interrupt is currently
> dropped.
> +      * likely my fault.  serror may be cleared at the wrong time.
> +      */
> +     switch(s){
> +     case 0:                 /* no device */
> +             d->state = Dmissing;
> +             break;
> +     case Devpresent:        /* device but no phy. comm. */
> +             break;
> +     case Devphycomm:        /* should this be missing?  need
> testcase. */
> +             dprint("ahci: pstatus 2\n");
> +             /* fallthrough */
> +     case Devpresent|Devphycomm:
> +             d->wait = 0;
> +             d->state = Dnew;
> +             break;
> +     case Devphyoffline:
> +             d->state = Doffline;
> +             break;
> +     case Devphyoffline|Devphycomm:  /* does this make
> sense? */
> +             d->state = Dnew;
> +             break;
> +     }
> +}
> +
> +static int
> +configdrive(Drive *d)
> +{
> +     if(ahciconfigdrive(d) == -1)
> +             return -1;
> +     ilock(&d->Lock);
> +     pstatus(d, d->port->sstatus & Devdet);
> +     iunlock(&d->Lock);
> +     return 0;
> +}
> +
> +static void
> +setstate(Drive *d, int state)
> +{
> +     ilock(&d->Lock);
> +     d->state = state;
> +     iunlock(&d->Lock);
> +}
> +
> +static void
> +resetdisk(Drive *d)
> +{
> +     uint state, det, stat;
> +     Aport *p;
> +
> +     p = d->port;
> +     det = p->sctl & 7;
> +     stat = p->sstatus & Devdet;
> +     state = (p->cmd>>28) & 0xf;
> +     dprint("ahci: resetdisk: icc %#x  det %d sdet %d\n", state,
> det, stat); +
> +     ilock(&d->Lock);
> +     state = d->state;
> +     if(d->state != Dready || d->state != Dnew)
> +             d->portm.flag |= Ferror;
> +     clearci(p);                     /* satisfy sleep
> condition. */
> +     wakeup(&d->portm.Rendez);
> +     if(stat != (Devpresent|Devphycomm)){
> +             /* device absent or phy not communicating */
> +             d->state = Dportreset;
> +             iunlock(&d->Lock);
> +             return;
> +     }
> +     d->state = Derror;
> +     iunlock(&d->Lock);
> +
> +     qlock(&d->portm.ql);
> +     if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
> +             setstate(d, Dportreset);        /* get a bigger
> stick. */
> +     else {
> +             setstate(d, Dmissing);
> +             configdrive(d);
> +     }
> +     dprint("ahci: %s: resetdisk: %s → %s\n", (d->unit?
> d->unit->SDperm.name: nil),
> +             diskstates[state], diskstates[d->state]);
> +     qunlock(&d->portm.ql);
> +}
> +
> +static int
> +newdrive(Drive *d)
> +{
> +     char *name;
> +     Aportc *c;
> +     Aportm *pm;
> +
> +     c = &d->portc;
> +     pm = &d->portm;
> +
> +     name = d->unit->SDperm.name;
> +     if(name == 0)
> +             name = "??";
> +
> +     if(d->port->task == 0x80)
> +             return -1;
> +     qlock(&c->pm->ql);
> +     if(setudmamode(c, 5) == -1){
> +             dprint("%s: can't set udma mode\n", name);
> +             goto lose;
> +     }
> +     if(identify(d) == -1){
> +             dprint("%s: identify failure\n", name);
> +             goto lose;
> +     }
> +     if(pm->feat & Dpower && setfeatures(c, 0x85) == -1){
> +             pm->feat &= ~Dpower;
> +             if(ahcirecover(c) == -1)
> +                     goto lose;
> +     }
> +     setstate(d, Dready);
> +     qunlock(&c->pm->ql);
> +
> +     idprint("%s: %sLBA %,llu sectors: %s %s %s %s\n",
> d->unit->SDperm.name,
> +             (pm->feat & Dllba? "L": ""), d->sectors, d->model,
> d->firmware,
> +             d->serial, d->mediachange? "[mediachange]": "");
> +     return 0;
> +
> +lose:
> +     idprint("%s: can't be initialized\n", d->unit->SDperm.name);
> +     setstate(d, Dnull);
> +     qunlock(&c->pm->ql);
> +     return -1;
> +}
> +
> +static void
> +westerndigitalhung(Drive *d)
> +{
> +     if((d->portm.feat&Datapi) == 0 && d->active &&
> +         TK2MS(sys->ticks - d->intick) > 5000){
> +             dprint("%s: drive hung; resetting [%#lx] ci %#lx\n",
> +                     d->unit->SDperm.name, d->port->task,
> d->port->ci);
> +             d->state = Dreset;
> +     }
> +}
> +
> +static uint16_t olds[NCtlr*NCtlrdrv];
> +
> +static int
> +doportreset(Drive *d)
> +{
> +     int i;
> +
> +     i = -1;
> +     qlock(&d->portm.ql);
> +     if(ahciportreset(&d->portc) == -1)
> +             dprint("ahci: doportreset: fails\n");
> +     else
> +             i = 0;
> +     qunlock(&d->portm.ql);
> +     dprint("ahci: doportreset: portreset → %s  [task %#lx]\n",
> +             diskstates[d->state], d->port->task);
> +     return i;
> +}
> +
> +/* drive must be locked */
> +static void
> +statechange(Drive *d)
> +{
> +     switch(d->state){
> +     case Dnull:
> +     case Doffline:
> +             if(d->unit->sectors != 0){
> +                     d->sectors = 0;
> +                     d->mediachange = 1;
> +             }
> +             /* fallthrough */
> +     case Dready:
> +             d->wait = 0;
> +             break;
> +     }
> +}
> +
> +static void
> +checkdrive(Drive *d, int i)
> +{
> +     uint16_t s;
> +     char *name;
> +
> +     if(d == nil) {
> +             print("checkdrive: nil d\n");
> +             return;
> +     }
> +     ilock(&d->Lock);
> +     if(d->unit == nil || d->port == nil) {
> +             if(0)
> +                     print("checkdrive: nil d->%s\n",
> +                             d->unit == nil? "unit": "port");
> +             iunlock(&d->Lock);
> +             return;
> +     }
> +     name = d->unit->SDperm.name;
> +     s = d->port->sstatus;
> +     if(s)
> +             d->lastseen = sys->ticks;
> +     if(s != olds[i]){
> +             dprint("%s: status: %06#x -> %06#x: %s\n",
> +                     name, olds[i], s, diskstates[d->state]);
> +             olds[i] = s;
> +             d->wait = 0;
> +     }
> +     westerndigitalhung(d);
> +
> +     switch(d->state){
> +     case Dnull:
> +     case Dready:
> +             break;
> +     case Dmissing:
> +     case Dnew:
> +             switch(s & (Intactive | Devdet)){
> +             case Devpresent:  /* no device (pm), device but no
> phy. comm. */
> +                     ahciwakeup(d->port);
> +                     /* fall through */
> +             case 0:                 /* no device */
> +                     break;
> +             default:
> +                     dprint("%s: unknown status %06#x\n", name,
> s);
> +                     /* fall through */
> +             case Intactive:         /* active, no device
> */
> +                     if(++d->wait&Mphywait)
> +                             break;
> +reset:
> +                     if(++d->mode > DMsataii)
> +                             d->mode = 0;
> +                     if(d->mode == DMsatai){ /* we tried
> everything */
> +                             d->state = Dportreset;
> +                             goto portreset;
> +                     }
> +                     dprint("%s: reset; new mode %s\n", name,
> +                             modename[d->mode]);
> +                     iunlock(&d->Lock);
> +                     resetdisk(d);
> +                     ilock(&d->Lock);
> +                     break;
> +             case Intactive|Devphycomm|Devpresent:
> +                     if((++d->wait&Midwait) == 0){
> +                             dprint("%s: slow reset %06#x
> task=%#lx; %d\n",
> +                                     name, s, d->port->task,
> d->wait);
> +                             goto reset;
> +                     }
> +                     s = (unsigned char)d->port->task;
> +                     if(s == 0x7f || ((d->port->sig >> 16) !=
> 0xeb14 &&
> +                         (s & ~0x17) != (1<<6)))
> +                             break;
> +                     iunlock(&d->Lock);
> +                     newdrive(d);
> +                     ilock(&d->Lock);
> +                     break;
> +             }
> +             break;
> +     case Doffline:
> +             if(d->wait++ & Mcomrwait)
> +                     break;
> +             /* fallthrough */
> +     case Derror:
> +     case Dreset:
> +             dprint("%s: reset [%s]: mode %d; status %06#x\n",
> +                     name, diskstates[d->state], d->mode, s);
> +             iunlock(&d->Lock);
> +             resetdisk(d);
> +             ilock(&d->Lock);
> +             break;
> +     case Dportreset:
> +portreset:
> +             if(d->wait++ & 0xff && (s & Intactive) == 0)
> +                     break;
> +             /* device is active */
> +             dprint("%s: portreset [%s]: mode %d; status %06#x\n",
> +                     name, diskstates[d->state], d->mode, s);
> +             d->portm.flag |= Ferror;
> +             clearci(d->port);
> +             wakeup(&d->portm.Rendez);
> +             if((s & Devdet) == 0){  /* no device */
> +                     d->state = Dmissing;
> +                     break;
> +             }
> +             iunlock(&d->Lock);
> +             doportreset(d);
> +             ilock(&d->Lock);
> +             break;
> +     }
> +     statechange(d);
> +     iunlock(&d->Lock);
> +}
> +
> +static void
> +satakproc(void *v)
> +{
> +     Proc *up = externup();
> +     int i;
> +     for(;;){
> +             tsleep(&up->sleep, return0, 0, Nms);
> +             for(i = 0; i < niadrive; i++)
> +                     if(iadrive[i] != nil)
> +                             checkdrive(iadrive[i], i);
> +     }
> +}
> +
> +static void
> +isctlrjabbering(Ctlr *c, uint32_t cause)
> +{
> +     uint32_t now;
> +
> +     now = TK2MS(sys->ticks);
> +     if (now > c->lastintr0) {
> +             c->intrs = 0;
> +             c->lastintr0 = now;
> +     }
> +     if (++c->intrs > Maxintrspertick) {
> +             iprint("sdiahci: %lu intrs per tick for no serviced "
> +                     "drive; cause %#lx mport %d\n",
> +                     c->intrs, cause, c->mport);
> +             c->intrs = 0;
> +     }
> +}
> +
> +static void
> +isdrivejabbering(Drive *d)
> +{
> +     uint32_t now;
> +
> +     now = TK2MS(sys->ticks);
> +     if (now > d->lastintr0) {
> +             d->intrs = 0;
> +             d->lastintr0 = now;
> +     }
> +     if (++d->intrs > Maxintrspertick) {
> +             iprint("sdiahci: %lu interrupts per tick for %s\n",
> +                     d->intrs, d->unit->SDperm.name);
> +             d->intrs = 0;
> +     }
> +}
> +
> +static void
> +iainterrupt(Ureg *u, void *a)
> +{
> +     int i;
> +     uint32_t cause, mask;
> +     Ctlr *c;
> +     Drive *d;
> +
> +     c = a;
> +     ilock(&c->Lock);
> +     cause = c->hba->isr;
> +     if (cause == 0) {
> +             isctlrjabbering(c, cause);
> +             // iprint("sdiahci: interrupt for no drive\n");
> +             iunlock(&c->Lock);
> +             return;
> +     }
> +     for(i = 0; cause && i <= c->mport; i++){
> +             mask = 1 << i;
> +             if((cause & mask) == 0)
> +                     continue;
> +             d = c->rawdrive + i;
> +             ilock(&d->Lock);
> +             isdrivejabbering(d);
> +             if(d->port->isr && c->hba->pi & mask)
> +                     updatedrive(d);
> +             c->hba->isr = mask;
> +             iunlock(&d->Lock);
> +
> +             cause &= ~mask;
> +     }
> +     if (cause) {
> +             isctlrjabbering(c, cause);
> +             iprint("sdiachi: intr cause unserviced: %#lx\n",
> cause);
> +     }
> +     iunlock(&c->Lock);
> +}
> +
> +/* checkdrive, called from satakproc, will prod the drive while we
> wait */ +static void
> +awaitspinup(Drive *d)
> +{
> +     int ms;
> +     uint16_t s;
> +     char *name;
> +
> +     ilock(&d->Lock);
> +     if(d->unit == nil || d->port == nil) {
> +             panic("awaitspinup: nil d->unit or d->port");
> +             iunlock(&d->Lock);
> +             return;
> +     }
> +     name = (d->unit? d->unit->SDperm.name: nil);
> +     s = d->port->sstatus;
> +     if(!(s & Devpresent)) {                 /* never
> going to be ready */
> +             dprint("awaitspinup: %s absent, not waiting\n",
> name);
> +             iunlock(&d->Lock);
> +             return;
> +     }
> +
> +     for (ms = 20000; ms > 0; ms -= 50)
> +             switch(d->state){
> +             case Dnull:
> +                     /* absent; done */
> +                     iunlock(&d->Lock);
> +                     dprint("awaitspinup: %s in null state\n",
> name);
> +                     return;
> +             case Dready:
> +             case Dnew:
> +                     if(d->sectors || d->mediachange) {
> +                             /* ready to use; done */
> +                             iunlock(&d->Lock);
> +                             dprint("awaitspinup: %s ready!\n",
> name);
> +                             return;
> +                     }
> +                     /* fall through */
> +             default:
> +             case Dmissing:                  /* normal
> waiting states */
> +             case Dreset:
> +             case Doffline:                  /*
> transitional states */
> +             case Derror:
> +             case Dportreset:
> +                     iunlock(&d->Lock);
> +                     asleep(50);
> +                     ilock(&d->Lock);
> +                     break;
> +             }
> +     print("awaitspinup: %s didn't spin up after 20 seconds\n",
> name);
> +     iunlock(&d->Lock);
> +}
> +
> +static int
> +iaverify(SDunit *u)
> +{
> +     Ctlr *c;
> +     Drive *d;
> +
> +     c = u->dev->ctlr;
> +     d = c->drive[u->subno];
> +     ilock(&c->Lock);
> +     ilock(&d->Lock);
> +     d->unit = u;
> +     iunlock(&d->Lock);
> +     iunlock(&c->Lock);
> +     checkdrive(d, d->driveno);              /* c->d0 +
> d->driveno */ +
> +     /*
> +      * hang around until disks are spun up and thus available as
> +      * nvram, dos file systems, etc.  you wouldn't expect it, but
> +      * the intel 330 ssd takes a while to `spin up'.
> +      */
> +     awaitspinup(d);
> +     return 1;
> +}
> +
> +static int
> +iaenable(SDev *s)
> +{
> +     char name[32];
> +     Ctlr *c;
> +     static int once;
> +
> +     c = s->ctlr;
> +     ilock(&c->Lock);
> +     if(!c->enabled) {
> +             if(once == 0) {
> +                     once = 1;
> +                     kproc("ahci", satakproc, 0);
> +             }
> +             if(c->ndrive == 0)
> +                     panic("iaenable: zero s->ctlr->ndrive");
> +             pcisetbme(c->pci);
> +             snprint(name, sizeof name, "%s (%s)", s->name,
> s->ifc->name);
> +             c->vector = intrenable(c->pci->intl, iainterrupt, c,
> c->pci->tbdf, name);
> +             /* supposed to squelch leftover interrupts here. */
> +             ahcienable(c->hba);
> +             c->enabled = 1;
> +     }
> +     iunlock(&c->Lock);
> +     return 1;
> +}
> +
> +static int
> +iadisable(SDev *s)
> +{
> +     char name[32];
> +     Ctlr *c;
> +
> +     c = s->ctlr;
> +     ilock(&c->Lock);
> +     ahcidisable(c->hba);
> +     snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
> +     intrdisable(c->vector);
> +     c->enabled = 0;
> +     iunlock(&c->Lock);
> +     return 1;
> +}
> +
> +static int
> +iaonline(SDunit *unit)
> +{
> +     int r;
> +     Ctlr *c;
> +     Drive *d;
> +
> +     c = unit->dev->ctlr;
> +     d = c->drive[unit->subno];
> +     r = 0;
> +
> +     if(d->portm.feat & Datapi && d->mediachange){
> +             r = scsionline(unit);
> +             if(r > 0)
> +                     d->mediachange = 0;
> +             return r;
> +     }
> +
> +     ilock(&d->Lock);
> +     if(d->mediachange){
> +             r = 2;
> +             d->mediachange = 0;
> +             /* devsd resets this after online is called; why? */
> +             unit->sectors = d->sectors;
> +             unit->secsize = 512;            /* default size
> */
> +     } else if(d->state == Dready)
> +             r = 1;
> +     iunlock(&d->Lock);
> +     return r;
> +}
> +
> +/* returns locked list! */
> +static Alist*
> +ahcibuild(Drive *d, unsigned char *cmd, void *data, int n, int64_t
> lba) +{
> +     unsigned char *c, acmd, dir, llba;
> +     Alist *l;
> +     Actab *t;
> +     Aportm *pm;
> +     Aprdt *p;
> +     static unsigned char tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, };
> +
> +     pm = &d->portm;
> +     dir = *cmd != 0x28;
> +     llba = pm->feat&Dllba? 1: 0;
> +     acmd = tab[dir][llba];
> +     qlock(&pm->ql);
> +     l = pm->list;
> +     t = pm->ctab;
> +     c = t->cfis;
> +
> +     c[0] = 0x27;
> +     c[1] = 0x80;
> +     c[2] = acmd;
> +     c[3] = 0;
> +
> +     c[4] = lba;             /* sector               lba
> low   7:0 */
> +     c[5] = lba >> 8;        /* cylinder low         lba
> mid   15:8 */
> +     c[6] = lba >> 16;       /* cylinder hi          lba
> hi    23:16 */
> +     c[7] = Obs | 0x40;      /* 0x40 == lba */
> +     if(llba == 0)
> +             c[7] |= (lba>>24) & 7;
> +
> +     c[8] = lba >> 24;       /* sector (exp)         lba
>       31:24 */
> +     c[9] = lba >> 32;       /* cylinder low (exp)
> lba   39:32 */
> +     c[10] = lba >> 48;      /* cylinder hi (exp)
> lba   48:40 */
> +     c[11] = 0;              /* features (exp); */
> +
> +     c[12] = n;              /* sector count */
> +     c[13] = n >> 8;         /* sector count (exp) */
> +     c[14] = 0;              /* r */
> +     c[15] = 0;              /* control */
> +
> +     *(uint32_t*)(c + 16) = 0;
> +
> +     l->flags = 1<<16 | Lpref | 0x5; /* Lpref ?? */
> +     if(dir == Write)
> +             l->flags |= Lwrite;
> +     l->len = 0;
> +     l->ctab = PCIWADDR(t);
> +     l->ctabhi = 0;
> +
> +     p = &t->prdt;
> +     p->dba = PCIWADDR(data);
> +     p->dbahi = 0;
> +     if(d->unit == nil)
> +             panic("ahcibuild: nil d->unit");
> +     p->count = 1<<31 | (d->unit->secsize*n - 2) | 1;
> +
> +     return l;
> +}
> +
> +static Alist*
> +ahcibuildpkt(Aportm *pm, SDreq *r, void *data, int n)
> +{
> +     int fill, len;
> +     unsigned char *c;
> +     Alist *l;
> +     Actab *t;
> +     Aprdt *p;
> +
> +     qlock(&pm->ql);
> +     l = pm->list;
> +     t = pm->ctab;
> +     c = t->cfis;
> +
> +     fill = pm->feat&Datapi16? 16: 12;
> +     if((len = r->clen) > fill)
> +             len = fill;
> +     memmove(t->atapi, r->cmd, len);
> +     memset(t->atapi+len, 0, fill-len);
> +
> +     c[0] = 0x27;
> +     c[1] = 0x80;
> +     c[2] = 0xa0;
> +     if(n != 0)
> +             c[3] = 1;       /* dma */
> +     else
> +             c[3] = 0;       /* features (exp); */
> +
> +     c[4] = 0;               /* sector               lba
> low   7:0 */
> +     c[5] = n;               /* cylinder low         lba
> mid   15:8 */
> +     c[6] = n >> 8;          /* cylinder hi
> lba hi        23:16 */
> +     c[7] = Obs;
> +
> +     *(uint32_t*)(c + 8) = 0;
> +     *(uint32_t*)(c + 12) = 0;
> +     *(uint32_t*)(c + 16) = 0;
> +
> +     l->flags = 1<<16 | Lpref | Latapi | 0x5;
> +     if(r->write != 0 && data)
> +             l->flags |= Lwrite;
> +     l->len = 0;
> +     l->ctab = PCIWADDR(t);
> +     l->ctabhi = 0;
> +
> +     if(data == 0)
> +             return l;
> +
> +     p = &t->prdt;
> +     p->dba = PCIWADDR(data);
> +     p->dbahi = 0;
> +     p->count = 1<<31 | (n - 2) | 1;
> +
> +     return l;
> +}
> +
> +static int
> +waitready(Drive *d)
> +{
> +     uint32_t s, i, delta;
> +
> +     for(i = 0; i < 15000; i += 250){
> +             if(d->state == Dreset || d->state == Dportreset ||
> +                 d->state == Dnew)
> +                     return 1;
> +             delta = sys->ticks - d->lastseen;
> +             if(d->state == Dnull || delta > 10*1000)
> +                     return -1;
> +             ilock(&d->Lock);
> +             s = d->port->sstatus;
> +             iunlock(&d->Lock);
> +             if((s & Intpm) == 0 && delta > 1500)
> +                     return -1;      /* no detect */
> +             if(d->state == Dready &&
> +                 (s & Devdet) == (Devphycomm|Devpresent))
> +                     return 0;       /* ready, present & phy.
> comm. */
> +             esleep(250);
> +     }
> +     print("%s: not responding; offline\n", d->unit->SDperm.name);
> +     setstate(d, Doffline);
> +     return -1;
> +}
> +
> +static int
> +lockready(Drive *d)
> +{
> +     int i;
> +
> +     qlock(&d->portm.ql);
> +     while ((i = waitready(d)) == 1) {       /* could wait
> forever? */
> +             qunlock(&d->portm.ql);
> +             esleep(1);
> +             qlock(&d->portm.ql);
> +     }
> +     return i;
> +}
> +
> +static int
> +flushcache(Drive *d)
> +{
> +     int i;
> +
> +     i = -1;
> +     if(lockready(d) == 0)
> +             i = ahciflushcache(&d->portc);
> +     qunlock(&d->portm.ql);
> +     return i;
> +}
> +
> +static int
> +iariopkt(SDreq *r, Drive *d)
> +{
> +     Proc *up = externup();
> +     int n, count, try, max, flag, task, wormwrite;
> +     char *name;
> +     unsigned char *cmd, *data;
> +     Aport *p;
> +     Asleep as;
> +
> +     cmd = r->cmd;
> +     name = d->unit->SDperm.name;
> +     p = d->port;
> +
> +     aprint("ahci: iariopkt: %04#x %04#x %c %d %p\n",
> +             cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
> +     if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
> +             return sdmodesense(r, cmd, d->info, d->infosz);
> +     r->rlen = 0;
> +     count = r->dlen;
> +     max = 65536;
> +
> +     try = 0;
> +retry:
> +     data = r->data;
> +     n = count;
> +     if(n > max)
> +             n = max;
> +     ahcibuildpkt(&d->portm, r, data, n);
> +     switch(waitready(d)){
> +     case -1:
> +             qunlock(&d->portm.ql);
> +             return SDeio;
> +     case 1:
> +             qunlock(&d->portm.ql);
> +             esleep(1);
> +             goto retry;
> +     }
> +     /* d->portm qlock held here */
> +
> +     ilock(&d->Lock);
> +     d->portm.flag = 0;
> +     iunlock(&d->Lock);
> +     p->ci = 1;
> +
> +     as.p = p;
> +     as.i = 1;
> +     d->intick = sys->ticks;
> +     d->active++;
> +
> +     while(waserror())
> +             ;
> +     /* don't sleep here forever */
> +     tsleep(&d->portm.Rendez, ahciclear, &as, 3*1000);
> +     poperror();
> +     if(!ahciclear(&as)) {
> +             qunlock(&d->portm.ql);
> +             print("%s: ahciclear not true after 3 seconds\n",
> name);
> +             r->status = SDcheck;
> +             return SDcheck;
> +     }
> +
> +     d->active--;
> +     ilock(&d->Lock);
> +     flag = d->portm.flag;
> +     task = d->port->task;
> +     iunlock(&d->Lock);
> +
> +     if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state ==
> Dready){
> +             d->port->ci = 0;
> +             ahcirecover(&d->portc);
> +             task = d->port->task;
> +             flag &= ~Fdone;         /* either an error or
> do-over */
> +     }
> +     qunlock(&d->portm.ql);
> +     if(flag == 0){
> +             if(++try == 10){
> +                     print("%s: bad disk\n", name);
> +                     r->status = SDcheck;
> +                     return SDcheck;
> +             }
> +             /*
> +              * write retries cannot succeed on write-once media,
> +              * so just accept any failure.
> +              */
> +             wormwrite = 0;
> +             switch(d->unit->inquiry[0] & SDinq0periphtype){
> +             case SDperworm:
> +             case SDpercd:
> +                     switch(cmd[0]){
> +                     case 0x0a:              /* write (6?) */
> +                     case 0x2a:              /* write (10) */
> +                     case 0x8a:              /* int32_t write
> (16) */
> +                     case 0x2e:              /* write and
> verify (10) */
> +                             wormwrite = 1;
> +                             break;
> +                     }
> +                     break;
> +             }
> +             if (!wormwrite) {
> +                     print("%s: retry\n", name);
> +                     goto retry;
> +             }
> +     }
> +     if(flag & Ferror){
> +             if((task&Eidnf) == 0)
> +                     print("%s: i/o error task=%#x\n", name,
> task);
> +             r->status = SDcheck;
> +             return SDcheck;
> +     }
> +
> +     data += n;
> +
> +     r->rlen = data - (unsigned char*)r->data;
> +     r->status = SDok;
> +     return SDok;
> +}
> +
> +static int
> +iario(SDreq *r)
> +{
> +     Proc *up = externup();
> +     int i, n, count, try, max, flag, task;
> +     int64_t lba;
> +     char *name;
> +     unsigned char *cmd, *data;
> +     Aport *p;
> +     Asleep as;
> +     Ctlr *c;
> +     Drive *d;
> +     SDunit *unit;
> +
> +     unit = r->unit;
> +     c = unit->dev->ctlr;
> +     d = c->drive[unit->subno];
> +     if(d->portm.feat & Datapi)
> +             return iariopkt(r, d);
> +     cmd = r->cmd;
> +     name = d->unit->SDperm.name;
> +     p = d->port;
> +
> +     if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
> +             if(flushcache(d) == 0)
> +                     return sdsetsense(r, SDok, 0, 0, 0);
> +             return sdsetsense(r, SDcheck, 3, 0xc, 2);
> +     }
> +
> +     if((i = sdfakescsi(r, d->info, d->infosz)) != SDnostatus){
> +             r->status = i;
> +             return i;
> +     }
> +
> +     if(*cmd != 0x28 && *cmd != 0x2a){
> +             print("%s: bad cmd %.2#x\n", name, cmd[0]);
> +             r->status = SDcheck;
> +             return SDcheck;
> +     }
> +
> +     lba   = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
> +     count = cmd[7]<<8 | cmd[8];
> +     if(r->data == nil)
> +             return SDok;
> +     if(r->dlen < count * unit->secsize)
> +             count = r->dlen / unit->secsize;
> +     max = 128;
> +
> +     try = 0;
> +retry:
> +     data = r->data;
> +     while(count > 0){
> +             n = count;
> +             if(n > max)
> +                     n = max;
> +             ahcibuild(d, cmd, data, n, lba);
> +             switch(waitready(d)){
> +             case -1:
> +                     qunlock(&d->portm.ql);
> +                     return SDeio;
> +             case 1:
> +                     qunlock(&d->portm.ql);
> +                     esleep(1);
> +                     goto retry;
> +             }
> +             /* d->portm qlock held here */
> +             ilock(&d->Lock);
> +             d->portm.flag = 0;
> +             iunlock(&d->Lock);
> +             p->ci = 1;
> +
> +             as.p = p;
> +             as.i = 1;
> +             d->intick = sys->ticks;
> +             d->active++;
> +
> +             while(waserror())
> +                     ;
> +             /* don't sleep here forever */
> +             tsleep(&d->portm.Rendez, ahciclear, &as, 3*1000);
> +             poperror();
> +             if(!ahciclear(&as)) {
> +                     qunlock(&d->portm.ql);
> +                     print("%s: ahciclear not true after 3
> seconds\n", name);
> +                     r->status = SDcheck;
> +                     return SDcheck;
> +             }
> +
> +             d->active--;
> +             ilock(&d->Lock);
> +             flag = d->portm.flag;
> +             task = d->port->task;
> +             iunlock(&d->Lock);
> +
> +             if(task & (Efatal<<8) ||
> +                 task & (ASbsy|ASdrq) && d->state == Dready){
> +                     d->port->ci = 0;
> +                     ahcirecover(&d->portc);
> +                     task = d->port->task;
> +             }
> +             qunlock(&d->portm.ql);
> +             if(flag == 0){
> +                     if(++try == 10){
> +                             print("%s: bad disk\n", name);
> +                             r->status = SDeio;
> +                             return SDeio;
> +                     }
> +                     print("%s: retry blk %lld\n", name, lba);
> +                     goto retry;
> +             }
> +             if(flag & Ferror){
> +                     print("%s: i/o error task=%#x @%,lld\n",
> +                             name, task, lba);
> +                     r->status = SDeio;
> +                     return SDeio;
> +             }
> +
> +             count -= n;
> +             lba   += n;
> +             data += n * unit->secsize;
> +     }
> +     r->rlen = data - (unsigned char*)r->data;
> +     r->status = SDok;
> +     return SDok;
> +}
> +
> +/*
> + * configure drives 0-5 as ahci sata (c.f. errata).
> + * what about 6 & 7, as claimed by marvell 0x9123?
> + */
> +static int
> +iaahcimode(Pcidev *p)
> +{
> +     dprint("iaahcimode: %#x %#x %#x\n", pcicfgr8(p, 0x91),
> pcicfgr8(p, 92),
> +             pcicfgr8(p, 93));
> +     pcicfgw16(p, 0x92, pcicfgr16(p, 0x92) | 0x3f);  /*
> ports 0-5 */
> +     return 0;
> +}
> +
> +static void
> +iasetupahci(Ctlr *c)
> +{
> +     /* disable cmd block decoding. */
> +     pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
> +     pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
> +
> +     c->lmmio[0x4/4] |= 1 << 31;     /* enable ahci mode (ghc
> register) */
> +     c->lmmio[0xc/4] = (1 << 6) - 1; /* 5 ports.
> (supposedly ro pi reg.) */ +
> +     /* enable ahci mode and 6 ports; from ich9 datasheet */
> +     pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
> +}
> +
> +static int
> +didtype(Pcidev *p)
> +{
> +     switch(p->vid){
> +     case Vintel:
> +             if((p->did & 0xfffc) == 0x2680)
> +                     return Tesb;
> +             /*
> +              * 0x27c4 is the intel 82801 in compatibility (not
> sata) mode.
> +              */
> +             if (p->did == 0x1e02 ||                 /*
> c210 */
> +                 p->did == 0x24d1 ||                 /*
> 82801eb/er */
> +                 (p->did & 0xfffb) == 0x27c1 ||      /*
> 82801g[bh]m ich7 */
> +                 p->did == 0x2821 ||                 /*
> 82801h[roh] */
> +                 (p->did & 0xfffe) == 0x2824 ||      /*
> 82801h[b] */
> +                 (p->did & 0xfeff) == 0x2829 ||      /* ich8/9m
> */
> +                 (p->did & 0xfffe) == 0x2922 ||      /* ich9 */
> +                 p->did == 0x3a02 ||                 /*
> 82801jd/do */
> +                 (p->did & 0xfefe) == 0x3a22 ||      /* ich10,
> pch */
> +                 (p->did & 0xfff8) == 0x3b28)        /* pchm */
> +                     return Tich;
> +             break;
> +     case Vatiamd:
> +             if(p->did == 0x4380 || p->did == 0x4390 || p->did ==
> 0x4391){
> +                     print("detected sb600 vid %#x did %#x\n",
> p->vid, p->did);
> +                     return Tsb600;
> +             }
> +             break;
> +     case Vmarvell:
> +             if (p->did == 0x9123)
> +                     print("ahci: marvell sata 3 controller has
> delusions "
> +                             "of something on unit 7\n");
> +             break;
> +     }
> +     if(p->ccrb == Pcibcstore && p->ccru == Pciscsata && p->ccrp
> == 1){
> +             print("ahci: Tunk: vid %#4.4x did %#4.4x\n", p->vid,
> p->did);
> +             return Tunk;
> +     }
> +     return -1;
> +}
> +
> +static int
> +newctlr(Ctlr *ctlr, SDev *sdev, int nunit)
> +{
> +     int i, n;
> +     Drive *drive;
> +
> +     ctlr->ndrive = sdev->nunit = nunit;
> +     ctlr->mport = ctlr->hba->cap & ((1<<5)-1);
> +
> +     i = (ctlr->hba->cap >> 20) & ((1<<4)-1);                /*
> iss */
> +     print("#S/sd%c: %s: %#p %s, %d ports, irq %d\n", sdev->idno,
> +             Tname(ctlr), ctlr->physio, descmode[i], nunit,
> ctlr->pci->intl);
> +     /* map the drives -- they don't all need to be enabled. */
> +     n = 0;
> +     ctlr->rawdrive = malloc(NCtlrdrv * sizeof(Drive));
> +     if(ctlr->rawdrive == nil) {
> +             print("ahci: out of memory\n");
> +             return -1;
> +     }
> +     for(i = 0; i < NCtlrdrv; i++) {
> +             drive = ctlr->rawdrive + i;
> +             drive->portno = i;
> +             drive->driveno = -1;
> +             drive->sectors = 0;
> +             drive->serial[0] = ' ';
> +             drive->ctlr = ctlr;
> +             if((ctlr->hba->pi & (1<<i)) == 0)
> +                     continue;
> +             drive->port = (Aport*)(ctlr->mmio + 0x80*i + 0x100);
> +             drive->portc.p = drive->port;
> +             drive->portc.pm = &drive->portm;
> +             drive->driveno = n++;
> +             ctlr->drive[drive->driveno] = drive;
> +             iadrive[niadrive + drive->driveno] = drive;
> +     }
> +     for(i = 0; i < n; i++)
> +             if(ahciidle(ctlr->drive[i]->port) == -1){
> +                     dprint("ahci: %s: port %d wedged; abort\n",
> +                             Tname(ctlr), i);
> +                     return -1;
> +             }
> +     for(i = 0; i < n; i++){
> +             ctlr->drive[i]->mode = DMsatai;
> +             configdrive(ctlr->drive[i]);
> +     }
> +     return n;
> +}
> +
> +static SDev*
> +iapnp(void)
> +{
> +     int n, nunit, type;
> +     uintptr_t io;
> +     Ctlr *c;
> +     Pcidev *p;
> +     SDev *head, *tail, *s;
> +     static int done;
> +
> +     if(done++)
> +             return nil;
> +
> +     memset(olds, 0xff, sizeof olds);
> +     p = nil;
> +     head = tail = nil;
> +     while((p = pcimatch(p, 0, 0)) != nil){
> +             type = didtype(p);
> +             if (type == -1 || p->mem[Abar].bar == 0)
> +                     continue;
> +             if(niactlr == NCtlr){
> +                     print("ahci: iapnp: %s: too many
> controllers\n",
> +                             tname[type]);
> +                     break;
> +             }
> +             c = iactlr + niactlr;
> +             s = sdevs  + niactlr;
> +             memset(c, 0, sizeof *c);
> +             memset(s, 0, sizeof *s);
> +             io = p->mem[Abar].bar & ~0xf;
> +             c->physio = (unsigned char *)io;
> +             c->mmio = vmap(io, p->mem[Abar].size);
> +             if(c->mmio == 0){
> +                     print("ahci: %s: address %#lX in use
> did=%#x\n",
> +                             Tname(c), io, p->did);
> +                     continue;
> +             }
> +             c->lmmio = (uint32_t*)c->mmio;
> +             c->pci = p;
> +             c->type = type;
> +
> +             s->ifc = &sdiahciifc;
> +             s->idno = 'E' + niactlr;
> +             s->ctlr = c;
> +             c->sdev = s;
> +
> +             if(Intel(c) && p->did != 0x2681)
> +                     iasetupahci(c);
> +             nunit = ahciconf(c);
> +//           ahcihbareset((Ahba*)c->mmio);
> +             if(Intel(c) && iaahcimode(p) == -1)
> +                     break;
> +             if(nunit < 1){
> +                     vunmap(c->mmio, p->mem[Abar].size);
> +                     continue;
> +             }
> +             n = newctlr(c, s, nunit);
> +             if(n < 0)
> +                     continue;
> +             niadrive += n;
> +             niactlr++;
> +             if(head)
> +                     tail->next = s;
> +             else
> +                     head = s;
> +             tail = s;
> +     }
> +     return head;
> +}
> +
> +static char* smarttab[] = {
> +     "unset",
> +     "error",
> +     "threshold exceeded",
> +     "normal"
> +};
> +
> +static char *
> +pflag(char *s, char *e, unsigned char f)
> +{
> +     unsigned char i;
> +
> +     for(i = 0; i < 8; i++)
> +             if(f & (1 << i))
> +                     s = seprint(s, e, "%s ", flagname[i]);
> +     return seprint(s, e, "\n");
> +}
> +
> +static int
> +iarctl(SDunit *u, char *p, int l)
> +{
> +     char buf[32];
> +     char *e, *op;
> +     Aport *o;
> +     Ctlr *c;
> +     Drive *d;
> +
> +     c = u->dev->ctlr;
> +     if(c == nil) {
> +print("iarctl: nil u->dev->ctlr\n");
> +             return 0;
> +     }
> +     d = c->drive[u->subno];
> +     o = d->port;
> +
> +     e = p+l;
> +     op = p;
> +     if(d->state == Dready){
> +             p = seprint(p, e, "model\t%s\n", d->model);
> +             p = seprint(p, e, "serial\t%s\n", d->serial);
> +             p = seprint(p, e, "firm\t%s\n", d->firmware);
> +             if(d->smartrs == 0xff)
> +                     p = seprint(p, e, "smart\tenable error\n");
> +             else if(d->smartrs == 0)
> +                     p = seprint(p, e, "smart\tdisabled\n");
> +             else
> +                     p = seprint(p, e, "smart\t%s\n",
> +                             smarttab[d->portm.smart]);
> +             p = seprint(p, e, "flag\t");
> +             p = pflag(p, e, d->portm.feat);
> +     }else
> +             p = seprint(p, e, "no disk present [%s]\n",
> diskstates[d->state]);
> +     serrstr(o->serror, buf, buf + sizeof buf - 1);
> +     p = seprint(p, e, "reg\ttask %#lx cmd %#lx serr %#lx %s ci
> %#lx "
> +             "is %#lx; sig %#lx sstatus %06#lx\n",
> +             o->task, o->cmd, o->serror, buf,
> +             o->ci, o->isr, o->sig, o->sstatus);
> +     if(d->unit == nil)
> +             panic("iarctl: nil d->unit");
> +     p = seprint(p, e, "geometry %llu %lu\n", d->sectors,
> d->unit->secsize);
> +     return p - op;
> +}
> +
> +static void
> +runflushcache(Drive *d)
> +{
> +     int32_t t0;
> +
> +     t0 = sys->ticks;
> +     if(flushcache(d) != 0)
> +             error(Eio);
> +     dprint("ahci: flush in %ld ms\n", sys->ticks - t0);
> +}
> +
> +static void
> +forcemode(Drive *d, char *mode)
> +{
> +     int i;
> +
> +     for(i = 0; i < nelem(modename); i++)
> +             if(strcmp(mode, modename[i]) == 0)
> +                     break;
> +     if(i == nelem(modename))
> +             i = 0;
> +     ilock(&d->Lock);
> +     d->mode = i;
> +     iunlock(&d->Lock);
> +}
> +
> +static void
> +runsmartable(Drive *d, int i)
> +{
> +     Proc *up = externup();
> +     if(waserror()){
> +             qunlock(&d->portm.ql);
> +             d->smartrs = 0;
> +             nexterror();
> +     }
> +     if(lockready(d) == -1)
> +             error(Eio);
> +     d->smartrs = smart(&d->portc, i);
> +     d->portm.smart = 0;
> +     qunlock(&d->portm.ql);
> +     poperror();
> +}
> +
> +static void
> +forcestate(Drive *d, char *state)
> +{
> +     int i;
> +
> +     for(i = 0; i < nelem(diskstates); i++)
> +             if(strcmp(state, diskstates[i]) == 0)
> +                     break;
> +     if(i == nelem(diskstates))
> +             error(Ebadctl);
> +     setstate(d, i);
> +}
> +
> +/*
> + * force this driver to notice a change of medium if the hardware
> doesn't
> + * report it.
> + */
> +static void
> +changemedia(SDunit *u)
> +{
> +     Ctlr *c;
> +     Drive *d;
> +
> +     c = u->dev->ctlr;
> +     d = c->drive[u->subno];
> +     ilock(&d->Lock);
> +     d->mediachange = 1;
> +     u->sectors = 0;
> +     iunlock(&d->Lock);
> +}
> +
> +static int
> +iawctl(SDunit *u, Cmdbuf *cmd)
> +{
> +     Proc *up = externup();
> +     char **f;
> +     Ctlr *c;
> +     Drive *d;
> +     uint i;
> +
> +     c = u->dev->ctlr;
> +     d = c->drive[u->subno];
> +     f = cmd->f;
> +
> +     if(strcmp(f[0], "change") == 0)
> +             changemedia(u);
> +     else if(strcmp(f[0], "flushcache") == 0)
> +             runflushcache(d);
> +     else if(strcmp(f[0], "identify") ==  0){
> +             i = strtoul(f[1]? f[1]: "0", 0, 0);
> +             if(i > 0xff)
> +                     i = 0;
> +             dprint("ahci: %04d %#x\n", i, d->info[i]);
> +     }else if(strcmp(f[0], "mode") == 0)
> +             forcemode(d, f[1]? f[1]: "satai");
> +     else if(strcmp(f[0], "nop") == 0){
> +             if((d->portm.feat & Dnop) == 0){
> +                     cmderror(cmd, "no drive support");
> +                     return -1;
> +             }
> +             if(waserror()){
> +                     qunlock(&d->portm.ql);
> +                     nexterror();
> +             }
> +             if(lockready(d) == -1)
> +                     error(Eio);
> +             nop(&d->portc);
> +             qunlock(&d->portm.ql);
> +             poperror();
> +     }else if(strcmp(f[0], "reset") == 0)
> +             forcestate(d, "reset");
> +     else if(strcmp(f[0], "smart") == 0){
> +             if(d->smartrs == 0){
> +                     cmderror(cmd, "smart not enabled");
> +                     return -1;
> +             }
> +             if(waserror()){
> +                     qunlock(&d->portm.ql);
> +                     d->smartrs = 0;
> +                     nexterror();
> +             }
> +             if(lockready(d) == -1)
> +                     error(Eio);
> +             d->portm.smart = 2 + smartrs(&d->portc);
> +             qunlock(&d->portm.ql);
> +             poperror();
> +     }else if(strcmp(f[0], "smartdisable") == 0)
> +             runsmartable(d, 1);
> +     else if(strcmp(f[0], "smartenable") == 0)
> +             runsmartable(d, 0);
> +     else if(strcmp(f[0], "state") == 0)
> +             forcestate(d, f[1]? f[1]: "null");
> +     else{
> +             cmderror(cmd, Ebadctl);
> +             return -1;
> +     }
> +     return 0;
> +}
> +
> +static char *
> +portr(char *p, char *e, uint x)
> +{
> +     int i, a;
> +
> +     p[0] = 0;
> +     a = -1;
> +     for(i = 0; i < 32; i++){
> +             if((x & (1<<i)) == 0){
> +                     if(a != -1 && i - 1 != a)
> +                             p = seprint(p, e, "-%d", i - 1);
> +                     a = -1;
> +                     continue;
> +             }
> +             if(a == -1){
> +                     if(i > 0)
> +                             p = seprint(p, e, ", ");
> +                     p = seprint(p, e, "%d", a = i);
> +             }
> +     }
> +     if(a != -1 && i - 1 != a)
> +             p = seprint(p, e, "-%d", i - 1);
> +     return p;
> +}
> +
> +/* must emit exactly one line per controller (sd(3)) */
> +static char*
> +iartopctl(SDev *sdev, char *p, char *e)
> +{
> +     uint32_t cap;
> +     char pr[25];
> +     Ahba *hba;
> +     Ctlr *ctlr;
> +
> +#define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str))
> +
> +     ctlr = sdev->ctlr;
> +     hba = ctlr->hba;
> +     p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno,
> ctlr->physio);
> +     cap = hba->cap;
> +     has(Hs64a, "64a");
> +     has(Hsalp, "alp");
> +     has(Hsam, "am");
> +     has(Hsclo, "clo");
> +     has(Hcccs, "coal");
> +     has(Hems, "ems");
> +     has(Hsal, "led");
> +     has(Hsmps, "mps");
> +     has(Hsncq, "ncq");
> +     has(Hssntf, "ntf");
> +     has(Hspm, "pm");
> +     has(Hpsc, "pslum");
> +     has(Hssc, "slum");
> +     has(Hsss, "ss");
> +     has(Hsxs, "sxs");
> +     portr(pr, pr + sizeof pr, hba->pi);
> +     return seprint(p, e,
> +             "iss %ld ncs %ld np %ld; ghc %#lx isr %#lx pi %#lx
> %s ver %#lx\n",
> +             (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
> +             hba->ghc, hba->isr, hba->pi, pr, hba->ver);
> +#undef has
> +}
> +
> +static int
> +iawtopctl(SDev *sdev, Cmdbuf *cmd)
> +{
> +     int *v;
> +     char **f;
> +
> +     f = cmd->f;
> +     v = 0;
> +
> +     if (f[0] == nil)
> +             return 0;
> +     if(strcmp(f[0], "debug") == 0)
> +             v = &debug;
> +     else if(strcmp(f[0], "idprint") == 0)
> +             v = &prid;
> +     else if(strcmp(f[0], "aprint") == 0)
> +             v = &datapi;
> +     else
> +             cmderror(cmd, Ebadctl);
> +
> +     switch(cmd->nf){
> +     default:
> +             cmderror(cmd, Ebadarg);
> +     case 1:
> +             *v ^= 1;
> +             break;
> +     case 2:
> +             if(f[1])
> +                     *v = strcmp(f[1], "on") == 0;
> +             else
> +                     *v ^= 1;
> +             break;
> +     }
> +     return 0;
> +}
> +
> +SDifc sdiahciifc = {
> +     "iahci",
> +
> +     iapnp,
> +     nil,            /* legacy */
> +     iaenable,
> +     iadisable,
> +
> +     iaverify,
> +     iaonline,
> +     iario,
> +     iarctl,
> +     iawctl,
> +
> +     scsibio,
> +     nil,            /* probe */
> +     nil,            /* clear */
> +     iartopctl,
> +     iawtopctl,
> +};
> diff --git a/kern/drivers/dev/sdscsi.c b/kern/drivers/dev/sdscsi.c
> new file mode 100644
> index 0000000..380064c
> --- /dev/null
> +++ b/kern/drivers/dev/sdscsi.c
> @@ -0,0 +1,436 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to
> the license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at
> http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be
> copied,
> + * modified, propagated, or distributed except according to the
> terms contained
> + * in the LICENSE file.
> + */
> +
> +#include "u.h"
> +#include "../port/lib.h"
> +#include "mem.h"
> +#include "dat.h"
> +#include "fns.h"
> +#include "io.h"
> +#include "ureg.h"
> +#include "../port/error.h"
> +
> +#include "../port/sd.h"
> +
> +static int
> +scsitest(SDreq* r)
> +{
> +     r->write = 0;
> +     memset(r->cmd, 0, sizeof(r->cmd));
> +     r->cmd[1] = r->lun<<5;
> +     r->clen = 6;
> +     r->data = nil;
> +     r->dlen = 0;
> +     r->flags = 0;
> +
> +     r->status = ~0;
> +
> +     return r->unit->dev->ifc->rio(r);
> +}
> +
> +int
> +scsiverify(SDunit* unit)
> +{
> +     SDreq *r;
> +     int i, status;
> +     uint8_t *inquiry;
> +
> +     if((r = malloc(sizeof(SDreq))) == nil)
> +             return 0;
> +     if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
> +             free(r);
> +             return 0;
> +     }
> +     r->unit = unit;
> +     r->lun = 0;             /* ??? */
> +
> +     memset(unit->inquiry, 0, sizeof(unit->inquiry));
> +     r->write = 0;
> +     r->cmd[0] = 0x12;
> +     r->cmd[1] = r->lun<<5;
> +     r->cmd[4] = sizeof(unit->inquiry)-1;
> +     r->clen = 6;
> +     r->data = inquiry;
> +     r->dlen = sizeof(unit->inquiry)-1;
> +     r->flags = 0;
> +
> +     r->status = ~0;
> +     if(unit->dev->ifc->rio(r) != SDok){
> +             free(r);
> +             return 0;
> +     }
> +     memmove(unit->inquiry, inquiry, r->dlen);
> +     free(inquiry);
> +
> +     SET(status);
> +     for(i = 0; i < 3; i++){
> +             while((status = scsitest(r)) == SDbusy)
> +                     ;
> +             if(status == SDok || status != SDcheck)
> +                     break;
> +             if(!(r->flags & SDvalidsense))
> +                     break;
> +             if((r->sense[2] & 0x0F) != 0x02)
> +                     continue;
> +
> +             /*
> +              * Unit is 'not ready'.
> +              * If it is in the process of becoming ready or needs
> +              * an initialising command, set status so it will be
> spun-up
> +              * below.
> +              * If there's no medium, that's OK too, but don't
> +              * try to spin it up.
> +              */
> +             if(r->sense[12] == 0x04){
> +                     if(r->sense[13] == 0x02 || r->sense[13] ==
> 0x01){
> +                             status = SDok;
> +                             break;
> +                     }
> +             }
> +             if(r->sense[12] == 0x3A)
> +                     break;
> +     }
> +
> +     if(status == SDok){
> +             /*
> +              * Try to ensure a direct-access device is spinning.
> +              * Don't wait for completion, ignore the result.
> +              */
> +             if((unit->inquiry[0] & SDinq0periphtype) ==
> SDperdisk){
> +                     memset(r->cmd, 0, sizeof(r->cmd));
> +                     r->write = 0;
> +                     r->cmd[0] = 0x1B;
> +                     r->cmd[1] = (r->lun<<5)|0x01;
> +                     r->cmd[4] = 1;
> +                     r->clen = 6;
> +                     r->data = nil;
> +                     r->dlen = 0;
> +                     r->flags = 0;
> +
> +                     r->status = ~0;
> +                     unit->dev->ifc->rio(r);
> +             }
> +     }
> +     free(r);
> +
> +     if(status == SDok || status == SDcheck)
> +             return 1;
> +     return 0;
> +}
> +
> +static int
> +scsirio(SDreq* r)
> +{
> +     Proc *up = externup();
> +     /*
> +      * Perform an I/O request, returning
> +      *      -1      failure
> +      *       0      ok
> +      *       1      no medium present
> +      *       2      retry
> +      * The contents of r may be altered so the
> +      * caller should re-initialise if necesary.
> +      */
> +     r->status = ~0;
> +     switch(r->unit->dev->ifc->rio(r)){
> +     default:
> +             break;
> +     case SDcheck:
> +             if(!(r->flags & SDvalidsense))
> +                     break;
> +             switch(r->sense[2] & 0x0F){
> +             case 0x00:              /* no sense */
> +             case 0x01:              /* recovered error */
> +                     return 2;
> +             case 0x06:              /* check condition */
> +                     /*
> +                      * 0x28 - not ready to ready transition,
> +                      *        medium may have changed.
> +                      * 0x29 - power on or some type of reset.
> +                      */
> +                     if(r->sense[12] == 0x28 && r->sense[13] == 0)
> +                             return 2;
> +                     if(r->sense[12] == 0x29)
> +                             return 2;
> +                     break;
> +             case 0x02:              /* not ready */
> +                     /*
> +                      * If no medium present, bail out.
> +                      * If unit is becoming ready, rather than not
> +                      * not ready, wait a little then poke it
> again.                                 */
> +                     if(r->sense[12] == 0x3A)
> +                             break;
> +                     if(r->sense[12] != 0x04 || r->sense[13] !=
> 0x01)
> +                             break;
> +
> +                     while(waserror())
> +                             ;
> +                     tsleep(&up->sleep, return0, 0, 500);
> +                     poperror();
> +                     scsitest(r);
> +                     return 2;
> +             default:
> +                     break;
> +             }
> +             break;
> +     case SDok:
> +             return 0;
> +     }
> +     return -1;
> +}
> +
> +int
> +scsionline(SDunit* unit)
> +{
> +     SDreq *r;
> +     uint8_t *p;
> +     int ok, retries;
> +
> +     if((r = malloc(sizeof(SDreq))) == nil)
> +             return 0;
> +     if((p = sdmalloc(8)) == nil){
> +             free(r);
> +             return 0;
> +     }
> +
> +     ok = 0;
> +
> +     r->unit = unit;
> +     r->lun = 0;                             /* ??? */
> +     for(retries = 0; retries < 10; retries++){
> +             /*
> +              * Read-capacity is mandatory for DA, WORM, CD-ROM
> and
> +              * MO. It may return 'not ready' if type DA is not
> +              * spun up, type MO or type CD-ROM are not loaded or
> just
> +              * plain slow getting their act together after a
> reset.
> +              */
> +             r->write = 0;
> +             memset(r->cmd, 0, sizeof(r->cmd));
> +             r->cmd[0] = 0x25;
> +             r->cmd[1] = r->lun<<5;
> +             r->clen = 10;
> +             r->data = p;
> +             r->dlen = 8;
> +             r->flags = 0;
> +
> +             r->status = ~0;
> +             switch(scsirio(r)){
> +             default:
> +                     break;
> +             case 0:
> +                     unit->sectors =
> (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
> +                     unit->secsize =
> (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; +
> +                     /*
> +                      * Some ATAPI CD readers lie about the block
> size.
> +                      * Since we don't read audio via this
> interface
> +                      * it's okay to always fudge this.
> +                      */
> +                     if(unit->secsize == 2352)
> +                             unit->secsize = 2048;
> +                     /*
> +                      * Devices with removable media may return 0
> sectors
> +                      * when they have empty media (e.g. sata dvd
> writers);
> +                      * if so, keep the count zero.
> +                      *
> +                      * Read-capacity returns the LBA of the last
> sector,
> +                      * therefore the number of sectors must be
> incremented.
> +                      */
> +                     if(unit->sectors != 0)
> +                             unit->sectors++;
> +                     ok = 1;
> +                     break;
> +             case 1:
> +                     ok = 1;
> +                     break;
> +             case 2:
> +                     continue;
> +             }
> +             break;
> +     }
> +     free(p);
> +     free(r);
> +
> +     if(ok)
> +             return ok+retries;
> +     else
> +             return 0;
> +}
> +
> +int
> +scsiexec(SDunit* unit, int write, uint8_t* cmd, int clen, void* data,
> +      int* dlen)
> +{
> +     SDreq *r;
> +     int status;
> +
> +     if((r = malloc(sizeof(SDreq))) == nil)
> +             return SDmalloc;
> +     r->unit = unit;
> +     r->lun = cmd[1]>>5;             /* ??? */
> +     r->write = write;
> +     memmove(r->cmd, cmd, clen);
> +     r->clen = clen;
> +     r->data = data;
> +     if(dlen)
> +             r->dlen = *dlen;
> +     r->flags = 0;
> +
> +     r->status = ~0;
> +
> +     /*
> +      * Call the device-specific I/O routine.
> +      * There should be no calls to 'error()' below this
> +      * which percolate back up.
> +      */
> +     switch(status = unit->dev->ifc->rio(r)){
> +     case SDok:
> +             if(dlen)
> +                     *dlen = r->rlen;
> +             /*FALLTHROUGH*/
> +     case SDcheck:
> +             /*FALLTHROUGH*/
> +     default:
> +             /*
> +              * It's more complicated than this. There are
> conditions
> +              * which are 'ok' but for which the returned status
> code
> +              * is not 'SDok'.
> +              * Also, not all conditions require a reqsense, might
> +              * need to do a reqsense here and make it available
> to the
> +              * caller somehow.
> +              *
> +              * Mañana.
> +              */
> +             break;
> +     }
> +     sdfree(r);
> +
> +     return status;
> +}
> +
> +static void
> +scsifmt10(SDreq *r, int write, int lun, uint32_t nb, uint64_t bno)
> +{
> +     uint8_t *c;
> +
> +     c = r->cmd;
> +     if(write == 0)
> +             c[0] = 0x28;
> +     else
> +             c[0] = 0x2A;
> +     c[1] = lun<<5;
> +     c[2] = bno>>24;
> +     c[3] = bno>>16;
> +     c[4] = bno>>8;
> +     c[5] = bno;
> +     c[6] = 0;
> +     c[7] = nb>>8;
> +     c[8] = nb;
> +     c[9] = 0;
> +
> +     r->clen = 10;
> +}
> +
> +static void
> +scsifmt16(SDreq *r, int write, int lun, uint32_t nb, uint64_t bno)
> +{
> +     uint8_t *c;
> +
> +     c = r->cmd;
> +     if(write == 0)
> +             c[0] = 0x88;
> +     else
> +             c[0] = 0x8A;
> +     c[1] = lun<<5;          /* so wrong */
> +     c[2] = bno>>56;
> +     c[3] = bno>>48;
> +     c[4] = bno>>40;
> +     c[5] = bno>>32;
> +     c[6] = bno>>24;
> +     c[7] = bno>>16;
> +     c[8] = bno>>8;
> +     c[9] = bno;
> +     c[10] = nb>>24;
> +     c[11] = nb>>16;
> +     c[12] = nb>>8;
> +     c[13] = nb;
> +     c[14] = 0;
> +     c[15] = 0;
> +
> +     r->clen = 16;
> +}
> +
> +int32_t
> +scsibio(SDunit* unit, int lun, int write, void* data, int32_t nb,
> +     uint64_t bno)
> +{
> +     SDreq *r;
> +     int32_t rlen;
> +
> +     if((r = malloc(sizeof(SDreq))) == nil)
> +             error(Enomem);
> +     r->unit = unit;
> +     r->lun = lun;
> +again:
> +     r->write = write;
> +     if(bno >= (1ULL<<32))
> +             scsifmt16(r, write, lun, nb, bno);
> +     else
> +             scsifmt10(r, write, lun, nb, bno);
> +     r->data = data;
> +     r->dlen = nb*unit->secsize;
> +     r->flags = 0;
> +
> +     r->status = ~0;
> +     switch(scsirio(r)){
> +     default:
> +             rlen = -1;
> +             break;
> +     case 0:
> +             rlen = r->rlen;
> +             break;
> +     case 2:
> +             rlen = -1;
> +             if(!(r->flags & SDvalidsense))
> +                     break;
> +             switch(r->sense[2] & 0x0F){
> +             default:
> +                     break;
> +             case 0x01:              /* recovered error */
> +                     print("%s: recovered error at sector %llu\n",
> +                             unit->SDperm.name, bno);
> +                     rlen = r->rlen;
> +                     break;
> +             case 0x06:              /* check condition */
> +                     /*
> +                      * Check for a removeable media change.
> +                      * If so, mark it by zapping the geometry
> info
> +                      * to force an online request.
> +                      */
> +                     if(r->sense[12] != 0x28 || r->sense[13] != 0)
> +                             break;
> +                     if(unit->inquiry[1] & SDinq1removable)
> +                             unit->sectors = 0;
> +                     break;
> +             case 0x02:              /* not ready */
> +                     /*
> +                      * If unit is becoming ready,
> +                      * rather than not not ready, try again.
> +                      */
> +                     if(r->sense[12] == 0x04 && r->sense[13] ==
> 0x01)
> +                             goto again;
> +                     break;
> +             }
> +             break;
> +     }
> +     free(r);
> +
> +     return rlen;
> +}
> +
> diff --git a/kern/include/ahci.h b/kern/include/ahci.h
> new file mode 100644
> index 0000000..5a1566c
> --- /dev/null
> +++ b/kern/include/ahci.h
> @@ -0,0 +1,302 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to
> the license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at
> http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be
> copied,
> + * modified, propagated, or distributed except according to the
> terms contained
> + * in the LICENSE file.
> + */
> +
> +/*
> + * advanced host controller interface (sata)
> + * © 2007  coraid, inc
> + */
> +
> +/* ata errors */
> +enum {
> +     Emed    = 1<<0,         /* media error */
> +     Enm     = 1<<1,         /* no media */
> +     Eabrt   = 1<<2,         /* abort */
> +     Emcr    = 1<<3,         /* media change request */
> +     Eidnf   = 1<<4,         /* no user-accessible
> address */
> +     Emc     = 1<<5,         /* media change */
> +     Eunc    = 1<<6,         /* data error */
> +     Ewp     = 1<<6,         /* write protect */
> +     Eicrc   = 1<<7,         /* interface crc error */
> +
> +     Efatal  = Eidnf|Eicrc,  /* must sw reset */
> +};
> +
> +/* ata status */
> +enum {
> +     ASerr   = 1<<0,         /* error */
> +     ASdrq   = 1<<3,         /* request */
> +     ASdf    = 1<<5,         /* fault */
> +     ASdrdy  = 1<<6,         /* ready */
> +     ASbsy   = 1<<7,         /* busy */
> +
> +     ASobs   = 1<<1|1<<2|1<<4,
> +};
> +
> +/* pci configuration */
> +enum {
> +     Abar    = 5,
> +};
> +
> +/*
> + * ahci memory configuration
> + *
> + * 0000-0023 generic host control
> + * 0024-009f reserved
> + * 00a0-00ff vendor specific.
> + * 0100-017f port 0
> + * ...
> + * 1080-1100 port 31
> + */
> +
> +/* cap bits: supported features */
> +enum {
> +     Hs64a   = 1<<31,        /* 64-bit addressing */
> +     Hsncq   = 1<<30,        /* ncq */
> +     Hssntf  = 1<<29,        /* snotification reg. */
> +     Hsmps   = 1<<28,        /* mech pres switch */
> +     Hsss    = 1<<27,        /* staggered spinup */
> +     Hsalp   = 1<<26,        /* aggressive link pm */
> +     Hsal    = 1<<25,        /* activity led */
> +     Hsclo   = 1<<24,        /* command-list override */
> +     Hiss    = 1<<20,        /* for interface speed */
> +//   Hsnzo   = 1<<19,
> +     Hsam    = 1<<18,        /* ahci-mode only */
> +     Hspm    = 1<<17,        /* port multiplier */
> +//   Hfbss   = 1<<16,
> +     Hpmb    = 1<<15,        /* multiple-block pio */
> +     Hssc    = 1<<14,        /* slumber state */
> +     Hpsc    = 1<<13,        /* partial-slumber state */
> +     Hncs    = 1<<8,         /* n command slots */
> +     Hcccs   = 1<<7,         /* coal */
> +     Hems    = 1<<6,         /* enclosure mgmt. */
> +     Hsxs    = 1<<5,         /* external sata */
> +     Hnp     = 1<<0,         /* n ports */
> +};
> +
> +/* ghc bits */
> +enum {
> +     Hae     = 1<<31,        /* enable ahci */
> +     Hie     = 1<<1,         /* " interrupts */
> +     Hhr     = 1<<0,         /* hba reset */
> +};
> +
> +typedef struct {
> +     uint32_t        cap;
> +     uint32_t        ghc;
> +     uint32_t        isr;
> +     uint32_t        pi;             /* ports implemented */
> +     uint32_t        ver;
> +     uint32_t        ccc;            /* coaleasing control */
> +     uint32_t        cccports;
> +     uint32_t        emloc;
> +     uint32_t        emctl;
> +} Ahba;
> +
> +enum {
> +     Acpds   = 1<<31,        /* cold port detect status */
> +     Atfes   = 1<<30,        /* task file error status */
> +     Ahbfs   = 1<<29,        /* hba fatal */
> +     Ahbds   = 1<<28,        /* hba error (parity error) */
> +     Aifs    = 1<<27,        /* interface fatal  §6.1.2 */
> +     Ainfs   = 1<<26,        /* interface error (recovered)
> */
> +     Aofs    = 1<<24,        /* too many bytes from disk */
> +     Aipms   = 1<<23,        /* incorrect prt mul status */
> +     Aprcs   = 1<<22,        /* PhyRdy change status
> Pxserr.diag.n */
> +     Adpms   = 1<<7,         /* mechanical presence
> status */
> +     Apcs    = 1<<6,         /* port connect  diag.x
> */
> +     Adps    = 1<<5,         /* descriptor processed
> */
> +     Aufs    = 1<<4,         /* unknown fis diag.f */
> +     Asdbs   = 1<<3,         /* set device bits fis
> received w/ i bit set */
> +     Adss    = 1<<2,         /* dma setup */
> +     Apio    = 1<<1,         /* pio setup fis */
> +     Adhrs   = 1<<0,         /* device to host
> register fis */ +
> +     IEM     =
> Acpds|Atfes|Ahbds|Ahbfs|Ahbds|Aifs|Ainfs|Aprcs|Apcs|Adps|
> +                     Aufs|Asdbs|Adss|Adhrs,
> +     Ifatal  = Atfes|Ahbfs|Ahbds|Aifs,
> +};
> +
> +/* serror bits */
> +enum {
> +     SerrX   = 1<<26,        /* exchanged */
> +     SerrF   = 1<<25,        /* unknown fis */
> +     SerrT   = 1<<24,        /* transition error */
> +     SerrS   = 1<<23,        /* link sequence */
> +     SerrH   = 1<<22,        /* handshake */
> +     SerrC   = 1<<21,        /* crc */
> +     SerrD   = 1<<20,        /* not used by ahci */
> +     SerrB   = 1<<19,        /* 10-tp-8 decode */
> +     SerrW   = 1<<18,        /* comm wake */
> +     SerrI   = 1<<17,        /* phy internal */
> +     SerrN   = 1<<16,        /* phyrdy change */
> +
> +     ErrE    = 1<<11,        /* internal */
> +     ErrP    = 1<<10,        /* ata protocol violation */
> +     ErrC    = 1<<9,         /* communication */
> +     ErrT    = 1<<8,         /* transient */
> +     ErrM    = 1<<1,         /* recoverd comm */
> +     ErrI    = 1<<0,         /* recovered data
> integrety */ +
> +     ErrAll  = ErrE|ErrP|ErrC|ErrT|ErrM|ErrI,
> +     SerrAll =
> SerrX|SerrF|SerrT|SerrS|SerrH|SerrC|SerrD|SerrB|SerrW|
> +                     SerrI|SerrN|ErrAll,
> +     SerrBad = 0x7f<<19,
> +};
> +
> +/* cmd register bits */
> +enum {
> +     Aicc    = 1<<28,        /* interface communcations
> control. 4 bits */
> +     Aasp    = 1<<27,        /* aggressive slumber & partial
> sleep */
> +     Aalpe   = 1<<26,        /* aggressive link pm enable */
> +     Adlae   = 1<<25,        /* drive led on atapi */
> +     Aatapi  = 1<<24,        /* device is atapi */
> +     Aesp    = 1<<21,        /* external sata port */
> +     Acpd    = 1<<20,        /* cold presence detect */
> +     Ampsp   = 1<<19,        /* mechanical pres. */
> +     Ahpcp   = 1<<18,        /* hot plug capable */
> +     Apma    = 1<<17,        /* pm attached */
> +     Acps    = 1<<16,        /* cold presence state */
> +     Acr     = 1<<15,        /* cmdlist running */
> +     Afr     = 1<<14,        /* fis running */
> +     Ampss   = 1<<13,        /* mechanical presence switch
> state */
> +     Accs    = 1<<8,         /* current command slot
> 12:08 */
> +     Afre    = 1<<4,         /* fis enable receive */
> +     Aclo    = 1<<3,         /* command list override
> */
> +     Apod    = 1<<2,         /* power on dev (requires
> cold-pres. detect) */
> +     Asud    = 1<<1,         /* spin-up device;
> requires ss capability */
> +     Ast     = 1<<0,         /* start */
> +
> +     Arun    = Ast|Acr|Afre|Afr,
> +};
> +
> +/* ctl register bits */
> +enum {
> +     Aipm    = 1<<8,         /* interface power mgmt.
> 3=off */
> +     Aspd    = 1<<4,
> +     Adet    = 1<<0,         /* device detection */
> +};
> +
> +#define      sstatus scr0
> +#define      sctl    scr2
> +#define      serror  scr1
> +#define      sactive scr3
> +
> +typedef struct {
> +     uint32_t        list;           /* PxCLB must be 1kb
> aligned. */
> +     uint32_t        listhi;
> +     uint32_t        fis;            /* 256-byte aligned */
> +     uint32_t        fishi;
> +     uint32_t        isr;
> +     uint32_t        ie;             /* interrupt enable */
> +     uint32_t        cmd;
> +     uint32_t        res1;
> +     uint32_t        task;
> +     uint32_t        sig;
> +     uint32_t        scr0;
> +     uint32_t        scr2;
> +     uint32_t        scr1;
> +     uint32_t        scr3;
> +     uint32_t        ci;             /* command issue */
> +     uint32_t        ntf;
> +     unsigned char   res2[8];
> +     uint32_t        vendor;
> +} Aport;
> +
> +enum {
> +     /*
> +      * Aport sstatus bits (actually states):
> +      * 11-8 interface power management
> +      *  7-4 current interface speed (generation #)
> +      *  3-0 device detection
> +      */
> +     Intslumber      = 0x600,
> +     Intpartpwr      = 0x200,
> +     Intactive       = 0x100,
> +     Intpm           = 0xf00,
> +
> +     Devphyoffline   = 4,
> +     Devphycomm      = 2,            /* phy communication
> established */
> +     Devpresent      = 1,
> +     Devdet          = Devpresent | Devphycomm |
> Devphyoffline, +};
> +
> +/* in host's memory; not memory mapped */
> +typedef struct {
> +     unsigned char   *base;
> +     unsigned char   *d;
> +     unsigned char   *p;
> +     unsigned char   *r;
> +     unsigned char   *u;
> +     uint32_t        *devicebits;
> +} Afis;
> +
> +enum {
> +     Lprdtl  = 1<<16,        /* physical region descriptor
> table len */
> +     Lpmp    = 1<<12,        /* port multiplier port */
> +     Lclear  = 1<<10,        /* clear busy on R_OK */
> +     Lbist   = 1<<9,
> +     Lreset  = 1<<8,
> +     Lpref   = 1<<7,         /* prefetchable */
> +     Lwrite  = 1<<6,
> +     Latapi  = 1<<5,
> +     Lcfl    = 1<<0,         /* command fis length in
> double words */ +};
> +
> +/* in hosts memory; memory mapped */
> +typedef struct {
> +     uint32_t        flags;
> +     uint32_t        len;
> +     uint32_t        ctab;
> +     uint32_t        ctabhi;
> +     unsigned char   reserved[16];
> +} Alist;
> +
> +typedef struct {
> +     uint32_t        dba;
> +     uint32_t        dbahi;
> +     uint32_t        pad;
> +     uint32_t        count;
> +} Aprdt;
> +
> +typedef struct {
> +     unsigned char   cfis[0x40];
> +     unsigned char   atapi[0x10];
> +     unsigned char   pad[0x30];
> +     Aprdt   prdt;
> +} Actab;
> +
> +enum {
> +     Ferror  = 1,
> +     Fdone   = 2,
> +};
> +
> +enum {
> +     Dllba   = 1,
> +     Dsmart  = 1<<1,
> +     Dpower  = 1<<2,
> +     Dnop    = 1<<3,
> +     Datapi  = 1<<4,
> +     Datapi16= 1<<5,
> +};
> +
> +typedef struct {
> +     QLock ql;
> +     Rendez Rendez;
> +     unsigned char   flag;
> +     unsigned char   feat;
> +     unsigned char   smart;
> +     Afis    fis;
> +     Alist   *list;
> +     Actab   *ctab;
> +} Aportm;
> +
> +typedef struct {
> +     Aport   *p;
> +     Aportm  *pm;
> +} Aportc;
> diff --git a/kern/include/sd.h b/kern/include/sd.h
> new file mode 100644
> index 0000000..1afa3e3
> --- /dev/null
> +++ b/kern/include/sd.h
> @@ -0,0 +1,203 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to
> the license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at
> http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be
> copied,
> + * modified, propagated, or distributed except according to the
> terms contained
> + * in the LICENSE file.
> + */
> +
> +/*
> + * Storage Device.
> + */
> +typedef struct SDev SDev;
> +typedef struct SDifc SDifc;
> +typedef struct SDio SDio;
> +typedef struct SDpart SDpart;
> +typedef struct SDperm SDperm;
> +typedef struct SDreq SDreq;
> +typedef struct SDunit SDunit;
> +
> +struct SDperm {
> +     char*   name;
> +     char*   user;
> +     uint32_t        perm;
> +};
> +
> +struct SDpart {
> +     uint64_t        start;
> +     uint64_t        end;
> +     SDperm SDperm;
> +     int     valid;
> +     uint32_t        vers;
> +};
> +
> +struct SDunit {
> +     SDev*   dev;
> +     int     subno;
> +     unsigned char   inquiry[255];           /* format
> follows SCSI spec */
> +     unsigned char   sense[18];              /* format
> follows SCSI spec */
> +     SDperm SDperm;
> +
> +     QLock   ctl;
> +     uint64_t        sectors;
> +     uint32_t        secsize;
> +     SDpart* part;                   /* nil or array
> of size npart */
> +     int     npart;
> +     uint32_t        vers;
> +     SDperm  ctlperm;
> +
> +     QLock   raw;                    /* raw read or
> write in progress */
> +     uint32_t        rawinuse;               /* really just a
> test-and-set */
> +     int     state;
> +     SDreq*  req;
> +     SDperm  rawperm;
> +};
> +
> +/*
> + * Each controller is represented by a SDev.
> + */
> +struct SDev {
> +     Ref     r;                      /* Number of callers
> using device */
> +     SDifc*  ifc;                    /* pnp/legacy */
> +     void*   ctlr;
> +     int     idno;
> +     char    name[8];
> +     SDev*   next;
> +
> +     QLock ql;                               /* enable/disable */
> +     int     enabled;
> +     int     nunit;                  /* Number of units
> */
> +     QLock   unitlock;               /* `Loading' of units
> */
> +     int*    unitflg;                /* Unit flags */
> +     SDunit**unit;
> +};
> +
> +struct SDifc {
> +     char*   name;
> +
> +     SDev*   (*pnp)(void);
> +     SDev*   (*legacy)(int, int);
> +     int     (*enable)(SDev*);
> +     int     (*disable)(SDev*);
> +
> +     int     (*verify)(SDunit*);
> +     int     (*online)(SDunit*);
> +     int     (*rio)(SDreq*);
> +     int     (*rctl)(SDunit*, char*, int);
> +     int     (*wctl)(SDunit*, Cmdbuf*);
> +
> +     int32_t (*bio)(SDunit*, int, int, void*, int32_t,
> uint64_t);
> +     SDev*   (*probe)(DevConf*);
> +     void    (*clear)(SDev*);
> +     char*   (*rtopctl)(SDev*, char*, char*);
> +     int     (*wtopctl)(SDev*, Cmdbuf*);
> +};
> +
> +struct SDreq {
> +     SDunit* unit;
> +     int     lun;
> +     int     write;
> +     unsigned char   cmd[16];
> +     int     clen;
> +     void*   data;
> +     int     dlen;
> +
> +     int     flags;
> +
> +     int     status;
> +     int32_t rlen;
> +     unsigned char   sense[256];
> +};
> +
> +enum {
> +     SDnosense       = 0x00000001,
> +     SDvalidsense    = 0x00010000,
> +
> +     SDinq0periphqual= 0xe0,
> +     SDinq0periphtype= 0x1f,
> +     SDinq1removable = 0x80,
> +
> +     /* periphtype values */
> +     SDperdisk       = 0,    /* Direct access (disk) */
> +     SDpertape       = 1,    /* Sequential eg, tape */
> +     SDperpr         = 2,    /* Printer */
> +     SDperworm       = 4,    /* Worm */
> +     SDpercd         = 5,    /* CD-ROM */
> +     SDpermo         = 7,    /* rewriteable MO */
> +     SDperjuke       = 8,    /* medium-changer */
> +};
> +
> +enum {
> +     SDretry         = -5,           /* internal to
> controllers */
> +     SDmalloc        = -4,
> +     SDeio           = -3,
> +     SDtimeout       = -2,
> +     SDnostatus      = -1,
> +
> +     SDok            = 0,
> +
> +     SDcheck         = 0x02,         /* check
> condition */
> +     SDbusy          = 0x08,         /* busy */
> +
> +     SDmaxio         = 2048*1024,
> +     SDnpart         = 16,
> +};
> +
> +/*
> + * Allow the default #defines for sdmalloc & sdfree to be overridden
> by
> + * system-specific versions.  This can be used to avoid extra copying
> + * by making sure sd buffers are cache-aligned (some ARM systems) or
> + * page-aligned (xen) for DMA.
> + */
> +#ifndef sdmalloc
> +#define sdmalloc(n)  malloc(n)
> +#define sdfree(p)    free(p)
> +#endif
> +
> +/*
> + * mmc/sd/sdio host controller interface
> + */
> +
> +struct SDio {
> +     char    *name;
> +     int     (*init)(void);
> +     void    (*enable)(void);
> +     int     (*inquiry)(char*, int);
> +     int     (*cmd)(uint32_t, uint32_t, uint32_t*);
> +     void    (*iosetup)(int, void*, int, int);
> +     void    (*io)(int, unsigned char*, int);
> +};
> +
> +extern SDio sdio;
> +
> +/* devsd.c */
> +extern void sdadddevs(SDev*);
> +extern void sdaddconf(SDunit*);
> +extern void sdaddallconfs(void (*f)(SDunit*));
> +extern void sdaddpart(SDunit*, char*, uint64_t, uint64_t);
> +extern int sdsetsense(SDreq*, int, int, int, int);
> +extern int sdmodesense(SDreq*, unsigned char*, void*, int);
> +extern int sdfakescsi(SDreq*, void*, int);
> +
> +/* sdscsi.c */
> +extern int scsiverify(SDunit*);
> +extern int scsionline(SDunit*);
> +extern int32_t scsibio(SDunit*, int, int, void*, int32_t, uint64_t);
> +extern SDev* scsiid(SDev*, SDifc*);
> +
> +/*
> + *  hardware info about a device
> + */
> +typedef struct {
> +     uint32_t        port;
> +     int     size;
> +} Devport;
> +
> +struct DevConf
> +{
> +     uint32_t        intnum;                 /* interrupt
> number */
> +     char    *type;                  /* card type,
> malloced */
> +     int     nports;                 /* Number of ports
> */
> +     Devport *ports;                 /* The ports
> themselves */ +};

-- 
You received this message because you are subscribed to the Google Groups 
"Akaros" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to