ase 0x0049: /* SCSI_RESIDUAL_MISMATCH */ case 0x004a: /* SCSI_TASK_MGMT_FAILED */ case 0x004b: /* SCSI_IOC_TERMINATED */ case 0x004c: /* SCSI_EXT_TERMINATED */ return 0; } break; case Config: switch (status) { case 0x0020: /* CONFIG_INVALID_ACTION */ case 0x0021: /* CONFIG_INVALID_TYPE */ case 0x0022: /* CONFIG_INVALID_PAGE */ case 0x0023: /* CONFIG_INVALID_DATA */ case 0x0024: /* CONFIG_NO_DEFAULTS */ case 0x0025: /* CONFIG_CANT_COMMIT */ return 0; } break; } dprint("ckstatus: %s: bad status 0x%04ux\n", ctlr->name, status); ctlrresetwakeup(ctlr, "bad ckstatus"); return -1; } static int doorbell(Ctlr *ctlr, ulong *req, int reqsize, int nwords) { ulong val; int i; ushort *reply, replyval; ilock(&ctlr->doorlock); iocwait(ctlr, Doorbell, DoorbellUsed, 0); iocwrite(ctlr, HostInterruptStatus, 0); val = Handshake | nwords<<16; iocwrite(ctlr, Doorbell, val); iocwait(ctlr, HostInterruptStatus, Ioc2SysDbStatus, 1); iocwrite(ctlr, HostInterruptStatus, 0); iocwait(ctlr, HostInterruptStatus, Sys2IocDbStatus, 0); for (i = 0; i < nwords; ++i) { iocwrite(ctlr, Doorbell, req[i]); iocwait(ctlr, HostInterruptStatus, Sys2IocDbStatus, 0); } /* * We do something sneaky here; replies are written back * into the request buffer during handshake. Buffers must * be sized to accomodate the larger of the two messages. * * Doorbell reads yield 16 bits at a time; upper bits are * considered reserved. The reply MsgLength is located * in the first 32-bit word; a reply will always contain * at least DefaultReplyLength words. */ reply = (ushort *)req; nwords = DefaultReplyLength; for (i = 0; i < nwords * 2; ++i) { iocwait(ctlr, HostInterruptStatus, Ioc2SysDbStatus, 1); /* We must still read the value even if we don't use it */ replyval = iocread(ctlr, Doorbell); if (i < reqsize * 2) reply[i] = replyval; iocwrite(ctlr, HostInterruptStatus, 0); if (i == 1) nwords = reply[i] & 0xff; /* MsgLength */ } iocwait(ctlr, HostInterruptStatus, Ioc2SysDbStatus, 1); iocwrite(ctlr, HostInterruptStatus, 0); iocwait(ctlr, Doorbell, DoorbellUsed, 0); iunlock(&ctlr->doorlock); return nwords; } #define UNITNO(ctlr, un) ((un)->disknum) static Unit * finddisknum(Ctlr *ctlr, int n) { Unit *un; for (un = ctlr->units; un; un = un->next) if (un->disknum == n) return un; return nil; } #define UNIT(ctlr, n) finddisknum((ctlr), (n)) static Unit * finddiskhnd(Ctlr *ctlr, ushort hnd) { Unit *un; for (un = ctlr->units; un; un = un->next) { if (un->devh == hnd) return un; } return nil; } static Unit * addunit(Ctlr *c, ushort devh, ushort exp, ulong *page) { Unit *un; Unitmap *m; uvlong wwn; un = finddiskhnd(c, devh); if (un == nil) { un = ialloc(sizeof *un, 0); if (un == nil) { print("addunit: out of memory\n"); return nil; } un->disknum = drivenextnum(); un->next = c->units; c->units = un; un->ctlr = c; m = &units[unitscnt++]; m->unit = un; m->ctlr = c; } un->devh = devh; un->expdr = exp; un->info = page[7]; /* DeviceInfo */ un->flags = page[8] & 0xffff; /* Flags */ un->dinfo.state = Dsinit; if (un->info & 1<<7) un->dinfo.proto = Dpata; else un->dinfo.proto = Dpscsi; wwn = getwwn(page + 9); /* * Some devices violate SPL 4.2.6. If a DEVICE NAME is not * present in the IDENTIFY address frame, we fallback to the * SAS ADDRESS. Unfortnately, we cannot rely on SAS ADDRESS * exclusively as the controller mangles the underlying WWID * for SATA devices. */ wwn = getwwn(page+9); /* DeviceName */ if (wwn == 0) wwn = getwwn(page+3); /* SASAddress */ putwwn(un->wwn, wwn); return un; } static int getunitcaps(Unit *un, char *p, int l) { char *o, *e; int i; static char *caps[] = { [4] "fua", /* SATA FUA */ [5] "ncq", /* SATA NCQ */ [6] "smart", /* SATA SMART */ [7] "lba48", /* SATA 48-bit LBA */ [9] "ssp", /* SATA Software Settings Preservation */ [10] "async", /* SATA Asynchronous Notification */ [11] "partial", /* Partial Power Management Mode */ [12] "slumber", /* Slumber Power Management Mode */ [13] "fp", /* Fast Path */ }; o = p; e = p + l; for (i = 0; i < nelem(caps); ++i) if (caps[i]) if (un->flags & 1<<i) { if (p != o) p = seprint(p, e, " "); p = seprint(p, e, "%s", caps[i]); } if (p == o) p = seprint(p, e, "none"); return p - o; } static char * unittype(Unit *un) { if (un->info & 1<<7) /* Device Type */ return "sata"; return "sas"; } static char * unitlink(Unit *un) { switch (un->link>>4) { /* Current Link Rate */ case 0x8: return "1.5Gb/s"; case 0x9: return "3.0Gb/s"; case 0xa: return "6.0Gb/s"; case 0xb: return "12.0Gb/s"; default: return "unknown"; } } #define REQ(ctlr, n) ((Req *)((ctlr)->req + (n)*(ctlr)->reqfsz + \ (ctlr)->iocreqfsz)) static ulong * reallocreq(Ctlr *ctlr) { ushort n; //print("reallocreq %p\n", ctlr->req); // free(ctlr->req); if (ctlr->req) return ctlr->req; /* * System Request Message Frames must be allocated * contiguously, aligned on a 16-byte boundary, and be a * multiple of 16 bytes in length. */ n = W2B(ctlr->iocreqfsz) + ROUNDUP(sizeof (Req), 16); ctlr->reqfsz = B2W(n); ctlr->req = ialloc(ctlr->reqcredit * n, 16); if (ctlr->req == nil) print("reallocreq: %s: out of memory\n", ctlr->name); return ctlr->req; } static Req * nextreq(Ctlr *ctlr) { Req *r; Rpool *start, *pool, *end; int s; s = splhi(); pool = start = ctlr->rpool + m->machno; end = ctlr->rpool + conf.nmach; r = nil; do { if (pool->head) { lock(pool); if (r = pool->head) pool->head = r->next; unlock(pool); if (r != nil) break; } if (++pool >= end) pool = ctlr->rpool;