2 * marvell odin ii 88se64xx sata/sas controller
3 * copyright © 2009 erik quanstrom
8 #include "../port/lib.h"
13 #include "../port/error.h"
14 #include "../port/sd.h"
16 #include "../port/led.h"
18 #define dprint(...) if(debug) print(__VA_ARGS__); else USED(debug)
19 #define idprint(...) if(idebug) print(__VA_ARGS__); else USED(idebug)
20 #define aprint(...) if(adebug) print(__VA_ARGS__); else USED(adebug)
21 #define Pciwaddrh(a) 0
22 #define Pciw64(x) (uvlong)PCIWADDR(x)
23 #define Ticks MACHP(0)->ticks
25 /* copied from sdiahci */
39 static char *diskstates[Dlast] = {
51 static char *type[] = {
60 Ndrive = Nctlr*Nctlrdrv,
63 Nqueue = 32, /* cmd queue size */
67 Nms = 256, /* drive check rate */
98 Gis = 0x008/4, /* global interrupt status */
99 Pi = 0x00c/4, /* ports implemented */
100 Flashctl = 0x030/4, /* spi flash control */
101 Flashcmd = 0x034/4, /* flash wormhole */
103 I²cctl = 0x040/4, /* i²c control */
106 Ptype = 0x0a0/4, /* 15:8 auto detect enable; 7:0 sas=1. sata=0 */
107 Portcfg0 = 0x100/4, /* 31:16 register sets 31:16 */
108 Portcfg1 = 0x104/4, /* 31:16 register sets 15:8 tx enable; 7 rx enable */
109 Clbase = 0x108/4, /* cmd list base; 64 bits */
110 Fisbase = 0x110/4, /* 64 bits */
111 Dqcfg = 0x120/4, /* bits 11:0 specify size */
113 Dqwp = 0x12c/4, /* delivery queue write pointer */
117 Cqwp = 0x140/4, /* hw */
119 Coalto = 0x14c/4, /* coal timeout µs */
120 Cis = 0x150/4, /* centeral irq status */
121 Cie = 0x154/4, /* centeral irq enable */
122 Csis = 0x158/4, /* cmd set irq status */
128 Gpiooff = 0x100, /* second gpio offset */
130 /* port conf registers; mapped through wormhole */
133 Painfo = 0x00c, /* attached device info */
137 Psig = 0x020, /* 16 bytes */
141 Pwwn = 0x080, /* 12 wwn + ict */
143 /* port cmd registers; mapped through “cmd” wormhole */
144 Ci = 0x040, /* cmd active (16) */
151 /* “vendor specific” wormhole */
159 Sgis = 0x010, /* interrupt set */
160 Sgie = 0x014, /* interrupt enable */
161 Drivesrc = 0x020, /* 4 drives/register; 4 bits/drive */
162 Drivectl = 0x038, /* same deal */
168 /* Portcfg0/1 bits */
169 Regen = 1<<16, /* enable sata regsets 31:16 or 15:0 */
170 Xmten = 1<<8, /* enable port n transmission */
172 Rsple = 1<<2, /* response frames in le format */
173 Oabe = 1<<1, /* oa frame in big endian format */
174 Framele = 1<<0, /* frame contents in le format */
176 Allresrx = 1<<7, /* receive all responses */
178 Cmdirq = 1<<5, /* 1 == self clearing */
179 Fisen = 1<<4, /* enable fis rx */
180 Errstop = 1<<3, /* set -> stop on ssp/smp error */
181 Resetiss = 1<<1, /* reset cmd issue; self clearing */
188 Noattn = 1<<17, /* don't post entries with attn bit */
197 Parity = 1<<28, /* parity error; fatal */
199 Portstop = 1<<16, /* bitmapped */
200 Portirq = 1<<8, /* bitmapped */
204 Iclr = Swirq1 | Swirq0,
207 Caf = 1<<29, /* clear affiliation fail */
208 Sync = 1<<25, /* sync during fis rx */
215 Anot = 1<<18, /* async notification */
217 Sigrx = 1<<16, /* native sata signature rx */
218 Phyunrdy = 1<<12, /* phy went offline*/
222 Bnot = 1<<8, /* broadcast noticication */
232 Pisataup = Phyrdy | Comw | Sigrx,
233 Pisasup = Phyrdy | Comw | Phyidok,
234 Piburp = Sync | Phyerr | Stperr | Crcerr | Linktx |
236 Pireset = Phyidfail | Bnot | Phyunrdy | Bist |
237 Anot | Martianfis | Bist | Phyidto |
243 Linkrate = 1<<18, /* 4 bits */
255 Itype = 1<<0, /* two bits */
258 Powerctl = 1<<30, /* 00 wake; 10 partial 01 slumb */
259 Srst = 1<<29, /* soft reset */
266 /* phy status bits */
271 /* Task bits; modeled after ahci */
284 Autolen = 1<<24, /* 8 bits */
285 Manlen = 1<<16, /* 8 bits */
286 Sdincapt = 1<<8, /* capture sdatain on + edge */
287 Sdoutch = 1<<7, /* change sdataout on - edge
288 Sldch = 1<<6, /* change sload on - edge
289 Sdoutivt = 1<<5, /* invert sdataout polarity */
292 Blinkben = 1<<2, /* enable blink b */
293 Blinkaen = 1<<1, /* enable blink a */
296 /* Sgconf1 bits; 4 bits each */
297 Sactoff = 1<<28, /* stretch activity off; 0/64 - 15/64 */
298 Sacton = 1<<24, /* 1/64th - 16/64 */
299 Factoff = 1<<20, /* 0/8 - 15/8; default 1 */
300 Facton = 1<<16, /* 0/4 - 15/4; default 2 */
301 Bhi = 1<<12, /* 1/8 - 16/8 */
302 Blo = 1<<8, /* 1/8 - 16/8 */
303 Ahi = 1<<4, /* 1/8 - 16/8 */
304 Alo = 1<<0, /* 1/8 - 16/8 */
307 Autopat = 1<<20, /* 4 bits of start pattern */
309 Manrep = 1<<4, /* repeats; 7ff ≡ ∞ */
319 Sgreprem = 1<<8, /* 12 bits; not irq related */
320 Manrep0 = 1<<1, /* write 1 to clear */
321 Capdone = 1<<0, /* capture done */
323 /* drive control bits (repeated 4x per drive) */
324 Aled = 1<<5, /* 3 bits */
325 Locled = 1<<3, /* 2 bits */
326 Errled = 1<<0, /* 3 bits */
339 Dsata = 3<<29, /* also stp */
340 Ditor = 1<<28, /* initiator */
345 /* completion queue bits */
346 Cgood = 1<<23, /* ssp only */
348 Crx = 1<<20, /* target mode */
353 Cslot = 1<<0, /* 12 bits */
355 /* error bits — first word */
356 Eissuestp = 1<<31, /* cmd issue stopped */
357 Epi = 1<<30, /* protection info error */
358 Eoflow = 1<<29, /* buffer overflow */
359 Eretry = 1<<28, /* retry limit exceeded */
361 Edmat = 1<<26, /* dma terminate */
362 Esync = 1<<25, /* sync rx during tx */
364 Ererr = 1<<23, /* r error received */
366 Eroff = 1<<20, /* read data offset error */
367 Exoff = 1<<19, /* xfer rdy offset error */
368 Euxr = 1<<18, /* unexpected xfer rdy */
369 Exflow = 1<<16, /* buffer over/underflow */
370 Elock = 1<<15, /* interlock error */
373 Enoak = 1<<12, /* conn closed wo nak */
374 Eopento = 1<<11, /* open conn timeout */
375 Epath = 1<<10, /* open reject - path blocked */
376 Enodst = 1<<9, /* open reject - no dest */
377 Estpbsy = 1<<8, /* stp resource busy */
378 Ebreak = 1<<7, /* break while sending */
379 Ebaddst = 1<<6, /* open reject - bad dest */
380 Ebadprot = 1<<5, /* open reject - proto not supp */
381 Erate = 1<<4, /* open reject - rate not supp */
382 Ewdest = 1<<3, /* open reject - wrong dest */
383 Ecreditto = 1<<2, /* credit timeout */
384 Edog = 1<<1, /* watchdog timeout */
385 Eparity = 1<<0, /* buffer parity error */
387 /* sas ctl cmd header bits */
388 Ssptype = 1<<5, /* 3 bits */
389 Ssppt = 1<<4, /* build your own header *.
390 Firstburst = 1<<3, /* first burst */
391 Vrfylen = 1<<2, /* verify length */
392 Tlretry = 1<<1, /* transport layer retry */
393 Piren = 1<<0, /* pir present */
395 /* sata ctl cmd header bits */
397 Lfpdma = 1<<6, /* first-party dma. (what's that?) */
399 Lpm = 1<<0, /* 4 bits */
403 Sspxfrdy = 4*Ssptype,
406 Sspwrite = 7*Ssptype,
433 /* protection information record */
443 /* open address frame */
476 typedef struct Cmd Cmd;
488 typedef struct Drive Drive;
489 typedef struct Ctlr Ctlr;
500 /* sdscsi doesn't differentiate drivechange/mediachange */
506 Sfis; /* sata and media info*/
507 Cfis; /* sas and media info */
540 Drive drive[Nctlrdrv];
544 static Ctlr msctlr[Nctlr];
545 static SDev sdevs[Nctlr];
547 static Drive *msdrive[Ndrive];
548 static uint nmsdrive;
552 static uint olds[Nctlr*Nctlrdrv];
555 /* a good register is hard to find */
557 0x160/4, 0x168/4, 0x170/4, 0x178/4,
558 0x200/4, 0x208/4, 0x210/4, 0x218/4,
560 static int pcfg[] = {
561 0x1c0/4, 0x1c8/4, 0x1d0/4, 0x1d8/4,
562 0x230/4, 0x238/4, 0x240/4, 0x248/4,
565 0x180/4, 0x184/4, 0x188/4, 0x18c/4,
566 0x220/4, 0x224/4, 0x228/4, 0x22c/4,
568 static int vscfg[] = {
569 0x1e0/4, 0x1e8/4, 0x1f0/4, 0x1f8/4,
570 0x250/4, 0x258/4, 0x260/4, 0x268/4,
572 #define sstatus(d) (d)->ctlr->reg[psc[(d)->driveno]]
581 return diskstates[i];
588 return d->unit->name;
592 static uvlong border = 0x0001020304050607ull;
593 static uvlong lorder = 0x0706050403020100ull;
596 getle(uchar *t, int w)
608 putle(uchar *t, uvlong r, int w)
615 for(i = 0; i < w; i++)
620 getbe(uchar *t, int w)
626 for(i = 0; i < w; i++)
632 putbe(uchar *t, uvlong r, int w)
638 o = (uchar*)&border + (sizeof border-w);
639 for(i = 0; i < w; i++)
643 static int phyrtab[] = {Phy0, Phy1};
645 phyenable(Ctlr *c, Drive *d)
650 reg = phyrtab[i > 3];
653 u = pcicfgr32(c->pci, reg);
654 m = i*(Phypdwn | Phydisable | Phyen);
657 m = i*(Phypdwn | Phydisable);
660 pcicfgw32(c->pci, reg, u);
670 u = c->reg[Portcfg1];
671 m = (Regen|Xmten)<<i;
673 c->reg[Portcfg1] = u;
675 c->reg[Portcfg1] = u | m;
689 reg = phyrtab[i > 3];
692 u = pcicfgr32(c->pci, reg);
693 pcicfgw32(c->pci, reg, u | i*Phyrst);
695 pcicfgw32(c->pci, reg, u);
697 sstatus(d) |= Shreset;
698 while(sstatus(d) & Shreset);
710 * sata/sas register reads through wormhole
713 ssread(Ctlr *c, int port, uint r)
715 c->reg[Cmda] = r + 4*port;
720 sswrite(Ctlr *c, int port, int r, uint u)
722 c->reg[Cmda] = r + 4*port;
727 * port configuration r/w through wormhole
730 pcread(Ctlr *c, uint port, uint r)
732 c->reg[pcfg[port]] = r;
733 return c->reg[pcfg[port] + 1];
737 pcwrite(Ctlr *c, uint port, uint r, uint u)
739 c->reg[pcfg[port] + 0] = r;
740 c->reg[pcfg[port] + 1] = u;
744 * vendor specific r/w through wormhole
747 vsread(Ctlr *c, uint port, uint r)
749 c->reg[vscfg[port]] = r;
750 return c->reg[vscfg[port] + 1];
754 vswrite(Ctlr *c, uint port, uint r, uint u)
756 c->reg[vscfg[port] + 0] = r;
757 c->reg[vscfg[port] + 1] = u;
764 gpread(Ctlr *c, uint r)
767 return c->reg[Gpiod];
771 gpwrite(Ctlr *c, uint r, uint u)
778 getsigfis(Drive *d, uint *fis)
782 for(i = 0; i < 4; i++)
783 fis[i] = pcread(d->ctlr, d->driveno, Psig + 4*i);
792 return fistosig((uchar*)getsigfis(d, fis));
798 return ssread(d->ctlr, d->driveno, Ci);
807 sswrite(d->ctlr, d->driveno, Ci, i);
815 return ssread(d->ctlr, d->driveno, Task);
819 tprint(Drive *d, uint t)
824 dprint("%s: err task %ux sstat %ux\n", dnam(d), t, s);
833 return (x->cflag & Done) != 0;
837 mswait(Cmd *x, int ms)
845 tsleep(x, cmdactive, x, ms);
847 ms -= TK2MS(Ticks - tk0);
849 while(ms-- && cmdactive(x))
856 if(u == (Done | Active))
859 u |= Noverdict | Creset | Timeout;
860 print("cmd timeout ms:%d %ux\n", ms, u);
866 setstate(Drive *d, int state)
878 tsleep(&up->sleep, return0, 0, ms);
887 for(i = 0; i < 15000; i += 250){
888 if(d->state & (Dreset | Dportreset | Dnew))
890 δ = Ticks - d->lastseen;
891 if(d->state == Dnull || δ > 10*1000)
896 if((s & Sphyrdy) == 0 && δ > 1500)
898 if(d->state == Dready && (s & Sphyrdy))
902 print("%s: not responding; offline: %.8ux\n", dnam(d), sstatus(d));
903 setstate(d, Doffline);
914 if((r = waitready(d)) != 1)
925 command(Drive *d, uint cmd, int ms)
933 n = cmd | Ditor | i*Dsatareg | m*Dphyno | i*Dcslot;
934 // print("cqwp\t%.8ux : n %ux : d%d; \n", c->cq[0], n, i);
936 * xinc doesn't return the previous value and i can't
937 * figure out how to do this without a lock
938 * s = _xinc(&c->dqwp);
940 d->cmd->cflag = Active;
944 c->reg[Dqwp] = s&Qmask;
946 // print(" dq slot %d\n", s);
947 d->intick = Ticks; /* move to mswait? */
948 return mswait(d->cmd, ms);
952 buildfis(Drive *d, SDreq *r, void *data, int n)
959 memmove(x->cfis, r->cmd, r->clen);
969 p->dba = PCIWADDR(data);
970 p->dbahi = Pciwaddrh(data);
973 return command(d, Dsata, 10*1000);
977 build(Drive *d, int rw, void *data, int n, vlong lba)
984 rwfis(d, x->cfis, rw, n, lba);
989 h->len[0] = 1; /* one prdt entry */
992 p->dba = PCIWADDR(data);
993 p->dbahi = Pciwaddrh(data);
994 p->count = d->secsize*n;
996 return command(d, Dsata, 10*1000);
1001 Rdma = 0x00, /* dma setup; length 0x1b */
1002 Rpio = 0x20, /* pio setup; length 0x13 */
1003 Rd2h = 0x40, /* d2h register;length 0x13 */
1004 Rsdb = 0x58, /* set device bits; length 0x08 */
1007 static uint fisotab[8] = {
1019 fisoffset(Drive *d, int mustbe)
1023 t = gettask(d) & 0x70000;
1024 r = fisotab[t >> 16];
1025 if(r == Rnone || (mustbe != 0 && r != mustbe))
1027 return 0x800 + 0x100*d->driveno + r;
1030 /* need to find a non-atapi-specific way of doing this */
1032 atapixfer(Drive *d, uint n)
1037 if((i = fisoffset(d, Rd2h)) == 0)
1039 u = d->ctlr->fis + i;
1040 x = u[Flba16]<<8 | u[Flba8];
1043 print("%s: atapixfer %ux %ux\n", dnam(d), x, n);
1049 buildpkt(Drive *d, SDreq *r, void *data, int n)
1057 atapirwfis(d, x->cfis, r->cmd, r->clen, n);
1061 h->satactl = Latapi;
1063 h->len[0] = 1; /* one prdt entry */
1067 p->dba = PCIWADDR(data);
1068 p->dbahi = Pciwaddrh(data);
1071 rv = command(d, Dsata, 10*1000);
1073 r->rlen = atapixfer(d, n);
1078 * ata 7, required for sata, requires that all devices "support"
1079 * udma mode 5, however sata:pata bridges allow older devices
1080 * which may not. the innodisk satadom, for example allows
1081 * only udma mode 2. on the assumption that actual udma is
1082 * taking place on these bridges, we set the highest udma mode
1083 * available, or pio if there is no udma mode available.
1086 settxmode(Drive *d, uchar f)
1092 if(txmodefis(d, x->cfis, f) == -1)
1099 return command(d, Dsata, 3*1000);
1103 setfeatures(Drive *d, uchar f, uint w)
1109 featfis(d, x->cfis, f);
1115 return command(d, Dsata, w);
1119 mvflushcache(Drive *d)
1125 flushcachefis(d, x->cfis);
1131 return command(d, Dsata, 60*1000);
1135 identify0(Drive *d, void *id)
1142 identifyfis(d, x->cfis);
1147 h->len[0] = 1; /* one prdt entry */
1149 memset(id, 0, 0x200);
1151 p->dba = PCIWADDR(id);
1152 p->dbahi = Pciwaddrh(id);
1155 return command(d, Dsata, 3*1000);
1169 if(i > 5 || identify0(d, id) != 0)
1172 if(n & Pspinup && setfeatures(d, 7, 20*1000) == -1)
1173 dprint("%s: puis spinup fail\n", dnam(d));
1181 if((d->feat&Dlba) == 0){
1182 dprint("%s: no lba support\n", dnam(d));
1185 osectors = d->sectors;
1186 memmove(oserial, d->serial, sizeof d->serial);
1189 d->secsize = idss(d, id);
1191 idmove(d->serial, id+10, 20);
1192 idmove(d->firmware, id+23, 8);
1193 idmove(d->model, id+27, 40);
1194 d->wwn = idwwn(d, id);
1197 memset(u->inquiry, 0, sizeof u->inquiry);
1200 u->inquiry[4] = sizeof u->inquiry - 4;
1201 memmove(u->inquiry+8, d->model, 40);
1203 if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
1210 /* open address fises */
1223 oafis(Cfis *f, uchar *c, int type)
1225 c[0] = Initiator | type<<4 | Openaddr;
1226 c[1] = Spd30; /* botch; just try 3gbps */
1228 memset(c + 2, 0xff, 2);
1230 memmove(c + 2, f->ict, 2);
1231 memmove(c + 4, f->tsasaddr, 8); /* dest "port identifier" §4.2.6 */
1232 memmove(c + 12, f->ssasaddr, 8);
1237 sasfis(Cfis*, uchar *c, SDreq *r)
1239 memmove(c, r->cmd, r->clen);
1241 memset(c + r->clen, 0, 16 - r->clen);
1245 /* sam3 §4.9.4 single-level lun structure */
1247 scsilun8(uchar *c, int l)
1256 print("bad lun %d\n", l);
1260 iuhdr(SDreq *r, uchar *c, int fburst)
1262 scsilun8(c, r->lun);
1270 ssphdr(Cfis *x, uchar *c, int ftype)
1274 sasbhash(c + 1, x->tsasaddr);
1275 sasbhash(c + 5, x->ssasaddr);
1280 dump(uchar *u, uint n)
1286 for(i = 0; i < n; i += 4){
1287 print("%.2d %.2ux%.2ux%.2ux%.2ux", i, u[i], u[i + 1], u[i + 2], u[i + 3]);
1293 prsense(uchar *u, uint n)
1295 print("sense data %d: \n", n);
1300 priu(uchar *u, uint n)
1302 print("iu %d: \n", n);
1309 * 02 0401 becoming ready
1310 * 040b target port in standby state
1312 * 0b0[345] background *
1313 * 0c01 write error - recovered with auto reallocation
1314 * 0c02 write error - auto reallocation failed
1315 * 0c03 write error - recommend reassignment
1316 * 17* recovered data
1317 * 18* recovered data
1318 * 5d* smart-style reporting (disk/smart handles)
1319 * 5e* power state change
1323 classifykey(int asckey)
1325 if(asckey == 0x062901 || asckey == 0x062900){
1327 dprint("power on sense\n");
1335 sasrspck(Drive *d, SDreq *r, int min)
1340 uint l, fmt, n, keyasc;
1344 dprint("status %d datapres %d\n", u[11], u[10]);
1347 l = getbe(u + 20, 4);
1349 * this is always a bug because we don't do
1352 print("%s: bug: task data %d min %d\n", dnam(d), l, min);
1355 l = getbe(u + 16, 4);
1356 n = sizeof r->sense;
1359 memmove(r->sense, s, n);
1361 keyasc = (s[2] & 0xf)<<16 | s[12]<<8 | s[13];
1363 /* spc3 §4.5.3; 0x71 is deferred. */
1364 if(n >= 18 && (fmt == 0x70 || fmt == 0x71)){
1365 rv = classifykey(keyasc);
1368 r->flags |= SDvalidsense;
1371 dprint("sense %.6ux %s\n", keyasc, p);
1376 print("%s: sasrspck: spurious\n", dnam(d));
1384 buildsas(Drive *d, SDreq *r, uchar *data, int n)
1393 fburst = 0; /* Firstburst? */
1395 /* ssphdr(d, x->sspfh, 6); */
1396 iuhdr(r, x->sasiu, fburst);
1399 w = r->clen - 16 + 3>> 2;
1401 sasfis(d, x->sasiu + 12, r);
1405 h->sasctl = Tlretry | /*Vrfylen |*/ Sspcmd | fburst;
1406 h->fislen[0] = sizeof x->sspfh + 12 + 16 + 4*w >> 2;
1411 *(uint*)h->dlen = n;
1415 p->dba = PCIWADDR(data);
1416 p->dbahi = Pciwaddrh(data);
1419 switch(w = command(d, Dssp, 10*1000)){
1421 r->status = sdsetsense(r, SDok, 0, 0, 0);
1423 case Response | Done | Active:
1424 r->status = sasrspck(d, r, 0);
1425 if(r->status == SDok)
1427 if(r->status == SDretry){
1430 r->status |= SDvalidsense;
1434 r->status = sdsetsense(r, SDcheck, 4, 24, 0);
1440 analyze(Drive *d, Statb *b)
1445 u = *(uint*)b->error;
1450 if(u & Etask && (d->feat & Datapi) == 0){
1454 if(t & Efatal<<8 || t & (ASbsy|ASdrq))
1455 r |= Noverdict|Atareset;
1457 r |= Noverdict|Atareset;
1461 if(u & (Ererr | Ebadprot)){
1463 print("%s: sas error %.8ux\n", dnam(d), u);
1466 if(u & ~(Ebadprot | Ererr | Etask | Eissuestp))
1467 print("%s: analyze %.8ux\n", dnam(d), u);
1475 uint a, e, i, u, slot;
1483 print("sdodin: bug: bad cqrp %ux\n", e);
1485 for(i = c->cqrp; i != e; i = i+1 & Qmask){
1490 d = c->drive + slot;
1496 if(u & (Crxfr | Cgood)){
1497 if((u & Cgood) == 0)
1498 x->cflag |= Response;
1499 u &= ~(Crxfr | Cgood);
1502 dprint("%s: Cerr ..\n", dnam(d));
1504 x->cflag |= Done | a;
1510 print("%s: odd bits %.8ux\n", dnam(d), u);
1512 if(i == c->cqrp)print("odin: spur done\n");
1517 updatedrive(Drive *d)
1519 uint cause, s0, ewake;
1522 static uint last, tk;
1525 cause = d->ctlr->reg[pis[d->driveno]];
1526 d->ctlr->reg[pis[d->driveno]] = cause;
1530 if(last != cause || Ticks - tk > 5*1000){
1531 dprint("%s: ca %ux ta %ux\n", name, cause, gettask(d));
1534 if(cause & (Phyunrdy | Phyidto | Pisataup | Pisasup)){
1536 if(cause == (Phyrdy | Comw)){
1538 d->state = Dnopower;
1540 switch(cause & (Phyunrdy | Phyidto | Phyidok | Sigrx)){
1542 d->state = Dmissing;
1543 if(sstatus(d) & Sphyrdy){
1552 d->state = Dmissing;
1563 dprint("%s: %s → %s [Apcrs] %s %ux\n", name, dstate(s0),
1564 dstate(d->state), type[d->type], sstatus(d));
1565 if(s0 == Dready && d->state != Dready)
1566 idprint("%s: pulled\n", name);
1567 if(d->state != Dready || ci(d))
1568 ewake |= Done | Noverdict;
1569 }else if(cause & Piburp)
1570 ewake |= Done | Noverdict;
1571 else if(cause & Pireset)
1572 ewake |= Done | Noverdict | Creset;
1573 else if(cause & Piunsupp){
1574 print("%s: unsupported h/w: %.8ux\n", name, cause);
1575 ewake |= Done | Error;
1577 d->state = Doffline;
1580 dprint("%s: ewake %.8ux\n", name, ewake);
1594 if(gettask(d) & (ASdrq|ASbsy))
1596 if(settxmode(d, d->udma) != 0)
1602 msriopkt(SDreq *r, Drive *d)
1604 int n, count, try, max, flag, task;
1608 aprint("%02ux %02ux %c %d %p\n", cmd[0], cmd[2], "rw"[r->write],
1614 for(try = 0; try < 10; try++){
1618 if(lockready(d) == -1)
1620 flag = buildpkt(d, r, r->data, n);
1622 if(flag & Atareset && satareset(d) == -1)
1623 setstate(d, Dreset);
1625 if(flag & Noverdict){
1627 setstate(d, Dreset);
1628 print("%s: retry\n", dnam(d));
1632 if((task & Eidnf) == 0)
1633 print("%s: i/o error %ux\n", dnam(d), task);
1634 return r->status = SDcheck;
1636 return r->status = SDok;
1638 print("%s: bad disk\n", dnam(d));
1639 return r->status = SDcheck;
1643 msriosas(SDreq *r, Drive *d)
1647 for(try = 0; try < 10; try++){
1648 if(lockready(d) == -1)
1650 flag = buildsas(d, r, r->data, r->dlen);
1652 if(flag & Noverdict){
1654 setstate(d, Dreset);
1655 print("%s: retry\n", dnam(d));
1659 print("%s: i/o error\n", dnam(d));
1660 return r->status = SDcheck;
1662 r->rlen = r->dlen; /* fishy */
1663 return r->status; /* set in sasrspck */
1666 print("%s: bad disk\n", dnam(d));
1667 sdsetsense(r, SDcheck, 3, r->write? 0xc00: 0x11, 0);
1668 return r->status = SDcheck;
1672 flushcache(Drive *d)
1677 if(lockready(d) == 0)
1678 i = mvflushcache(d);
1684 msriosata(SDreq *r, Drive *d)
1687 int i, n, count, try, max, flag, task;
1696 if(cmd[0] == 0x35 || cmd[0] == 0x91){
1697 if(flushcache(d) == 0)
1698 return sdsetsense(r, SDok, 0, 0, 0);
1699 return sdsetsense(r, SDcheck, 3, 0xc, 2);
1701 if((i = sdfakescsi(r)) != SDnostatus){
1705 if((i = sdfakescsirw(r, &lba, &count, nil)) != SDnostatus)
1716 if(lockready(d) == -1)
1718 flag = build(d, r->write, data, n, lba);
1720 if(flag & Atareset && satareset(d) == -1)
1721 setstate(d, Dreset);
1723 if(flag & Noverdict){
1725 setstate(d, Dreset);
1727 print("%s: bad disk\n", name);
1728 return r->status = SDeio;
1730 iprint("%s: retry %lld [%.8ux]\n", name, lba, task);
1734 iprint("%s: i/o error %ux @%,lld\n", name, task, lba);
1735 return r->status = SDeio;
1739 data += n*unit->secsize;
1741 r->rlen = data - (uchar*)r->data;
1755 d = c->drive + u->subno;
1756 if(d->feat & Datapi)
1757 return msriopkt(r, d);
1759 return msriosas(r, d);
1761 return msriosata(r, d);
1762 return sdsetsense(r, SDcheck, 3, 0x04, 0x24);
1767 * not clear that this is necessary
1768 * we should know that it's a d2h from the status.
1769 * pio returns pio setup fises. hw bug?
1772 sdr(SDreq *r, Drive *d, int st)
1776 if(i = fisoffset(d, 0/*Rd2h*/))
1777 memmove(r->cmd, d->ctlr->fis + i, 16);
1779 memset(r->cmd, 0xff, 16);
1785 * handle oob requests;
1786 * restrict & sanitize commands
1789 fisreqchk(Sfis *f, SDreq *r)
1793 if((r->ataproto & Pprotom) == Ppkt)
1796 error("bad command length"); //error(Eio);
1799 sigtofis(f, r->cmd);
1800 return r->status = SDok;
1812 int try, flag, task;
1816 int (*build)(Drive*, SDreq*, void*, int);
1820 d = c->drive + u->subno;
1825 if(r->cmd[0] == 0xf1){
1827 return r->status = SDok;
1829 if((r->status = fisreqchk(d, r)) != SDnostatus)
1832 if((r->ataproto & Pprotom) == Ppkt)
1835 for(try = 0; try < 10; try++){
1836 if(lockready(d) == -1)
1838 flag = build(d, r, r->data, r->dlen);
1840 if(flag & Atareset && satareset(d) == -1)
1841 setstate(d, Dreset);
1843 if(flag & Noverdict){
1844 if(flag & (Timeout | Creset))
1845 setstate(d, Dreset);
1846 else if(task & Eabrt<<8){
1847 /* assume bad cmd */
1851 print("%s: retry\n", name);
1855 print("%s: i/o error %.8ux\n", name, task);
1859 if(build != buildpkt)
1861 return sdr(r, d, SDok);
1863 print("%s: bad disk\n", name);
1864 return sdr(r, d, SDeio);
1868 msinterrupt(Ureg *, void *a)
1881 c->reg[Cis] = u & ~Iclr;
1882 if(u != Cdone && cnt++ < 15)
1883 print("sdodin: irq %s %.8ux\n", c->sdev->ifc->name, u);
1884 for(i = 0; i < 8; i++)
1885 if(u & (1<<i)*(Portirq|Portstop))
1886 updatedrive(c->drive + i);
1890 for(i = 0; i < 8; i++)
1892 updatedrive(c->drive + i);
1896 c->reg[Cis] = Cdone;
1913 newsatadrive(Drive *d)
1918 if((task & 0xffff) == 0x80)
1920 setfissig(d, getsig(d));
1921 if(identify(d) != 0){
1922 dprint("%s: identify failure\n", dnam(d));
1925 if(d->feat & Dpower && setfeatures(d, 0x85, 3*1000) != 0){
1927 if(satareset(d) == -1)
1930 if(settxmode(d, d->udma) != 0){
1931 dprint("%s: can't set tx mode\n", dnam(d));
1938 newoaf(Drive *d, int type)
1947 sa = pcread(c, i, Pawwn + 0);
1948 sa |= (uvlong)pcread(c, i, Pawwn + 4)<<32;
1949 putbe(d->tsasaddr, sa, 8);
1950 memmove(d->ssasaddr, d->ssasaddr, 8);
1951 ict = pcread(c, i, Pwwn + 8);
1952 putbe(d->ict, ict, 2);
1953 oafis(d, d->cmd->oaf, type);
1957 sasinquiry(Drive *d)
1963 memset(&r, 0, sizeof r);
1969 return buildsas(d, &r, u->inquiry, sizeof u->inquiry);
1979 memset(&r, 0, sizeof r);
1982 return buildsas(d, &r, 0, 0);
1986 sasvpd(Drive *d, uchar *buf, int l)
1992 memset(&r, 0, sizeof r);
1999 return buildsas(d, &r, buf, l);
2003 sascapacity10(Drive *d, uchar *buf, int l)
2009 memset(&r, 0, sizeof r);
2013 return buildsas(d, &r, buf, l);
2017 sascapacity16(Drive *d, uchar *buf, int l)
2023 memset(&r, 0, sizeof r);
2029 return buildsas(d, &r, buf, l);
2033 frmove(char *p, uchar *c, int n)
2040 for(p = p + n - 1; p > op && *p == ' '; p--)
2046 memmove(op, p, n - (e - p));
2050 chkinquiry(Drive *d, uchar *c)
2052 char buf[32], buf2[32], omod[sizeof d->model];
2054 memmove(omod, d->model, sizeof d->model);
2055 frmove(buf, c + 8, 8);
2056 frmove(buf2, c + 16, 16);
2057 snprint(d->model, sizeof d->model, "%s %s", buf, buf2);
2058 frmove(d->firmware, c + 23, 4);
2059 if(memcmp(omod, d->model, sizeof omod) != 0)
2064 chkvpd(Drive *d, uchar *c, int n)
2066 char buf[sizeof d->serial];
2072 frmove(buf, c + 4, l);
2073 if(strcmp(buf, d->serial) != 0)
2075 memmove(d->serial, buf, sizeof buf);
2079 adjcapacity(Drive *d, uvlong ns, uint nss)
2085 if(d->sectors != ns || d->secsize != nss){
2094 chkcapacity10(uchar *p, uvlong *ns, uint *nss)
2097 *nss = getbe(p + 4, 4);
2102 chkcapacity16(uchar *p, uvlong *ns, uint *nss)
2105 *nss = getbe(p + 8, 4);
2117 if((r = sastur(d)) != 0)
2119 if((r = sasinquiry(d)) != 0)
2121 chkinquiry(d, d->unit->inquiry);
2122 /* vpd 0x80 (unit serial) is not mandatory */
2123 if((r = sasvpd(d, buf, sizeof buf)) == 0)
2124 chkvpd(d, buf, sizeof buf);
2125 else if(r & (Error | Timeout))
2132 if((r = sascapacity10(d, buf, sizeof buf)) != 0)
2134 chkcapacity10(buf, &ns, &nss);
2135 if(ns == 0xffffffff){
2136 if((r = sascapacity16(d, buf, sizeof buf)) != 0)
2138 chkcapacity16(buf, &ns, &nss);
2140 adjcapacity(d, ns, nss);
2146 newsasdrive(Drive *d)
2148 memset(d->cmd->rsp, 0, sizeof d->cmd->rsp);
2150 switch(sasprobe(d) & (Error | Noverdict | Timeout | Sense)){
2167 memset(&d->Sfis, 0, sizeof d->Sfis);
2168 memset(&d->Cfis, 0, sizeof d->Cfis);
2172 r = newsatadrive(d);
2178 print("%s: bug: martian drive %d\n", dnam(d), d->type);
2185 idprint("%s: %s %,lld sectors\n", dnam(d), t, d->sectors);
2186 idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial, mc(d));
2187 setstate(d, Dready);
2190 idprint("%s: %s can't be initialized\n", dnam(d), t);
2191 setstate(d, Derror);
2200 statechange(Drive *d)
2207 d->unit->sectors = 0;
2216 * we don't respect running commands. botch?
2219 checkdrive(Drive *d, int i)
2229 d->lastseen = Ticks;
2231 dprint("%s: status: %.6ux -> %.6ux: %s\n",
2232 dnam(d), olds[i], s, dstate(d->state));
2239 if(d->type != 0 && s & Sphyrdy)
2243 phyreset(d); /* spinup */
2246 if(d->wait % 6 != 0)
2259 if(d->wait % 40 != 0)
2276 tsleep(&up->sleep, return0, 0, Nms);
2277 for(i = 0; i < nmsdrive; i++)
2278 checkdrive(msdrive[i], i);
2283 ledcfg(Ctlr *c, int port, uint cfg)
2287 r = Drivectl + (port>>2)*Gpiooff;
2296 static uchar ses2ledstd[Ibpilast] = {
2297 [Ibpinone] Lhigh*Aled,
2298 [Ibpinormal] Lsof*Aled | Llow*Locled | Llow*Errled,
2299 [Ibpirebuild] Lsof*Aled | Llow*Locled | Llow*Errled,
2300 [Ibpilocate] Lsof*Aled | Lblinka*Locled | Llow*Errled,
2301 [Ibpispare] Lsof*Aled | Llow*Locled| Lblinka*Errled,
2302 [Ibpipfa] Lsof*Aled | Lblinkb*Locled | Llow*Errled,
2303 [Ibpifail] Lsof*Aled | Llow*Locled | Lhigh*Errled,
2304 [Ibpicritarray] Lsof*Aled,
2305 [Ibpifailarray] Lsof*Aled,
2308 static uchar ses2led[Ibpilast] = {
2309 [Ibpinone] Lhigh*Aled,
2310 [Ibpinormal] Lsof*Aled | Llow*Locled | Llow*Errled,
2311 [Ibpirebuild] Lsof*Aled | Lblinkaneg*Locled | Llow*Errled,
2312 [Ibpilocate] Lsof*Aled | Lhigh*Locled | Llow*Errled,
2313 [Ibpispare] Lsof*Aled | Lblinka*Locled| Llow*Errled,
2314 [Ibpipfa] Lsof*Aled | Lblinkb*Locled | Llow*Errled,
2315 [Ibpifail] Lsof*Aled | Llow*Locled | Lhigh*Errled,
2316 [Ibpicritarray] Lsof*Aled,
2317 [Ibpifailarray] Lsof*Aled,
2324 pcicfgw32(c->pci, Gpio, pcicfgr32(c->pci, Gpio) | 1<<7);
2327 * configure a for 4hz (1/8s on and 1/8s off)
2328 * configure b for 1hz (2/8s on and 6/8s off)
2330 l = 3 + c->ndrive >> 2;
2332 for(i = 0; i < l*Gpiooff; i += Gpiooff){
2333 gpwrite(c, Sgconf0 + i, blen*Autolen | Blinkben | Blinkaen | Sgpioen);
2334 gpwrite(c, Sgconf1 + i, 1*Bhi | 1*Blo | 1*Ahi | 7*Alo);
2335 gpwrite(c, Sgconf3 + i, 7<<20 | Sdoutauto);
2340 trebuild(Ctlr *c, Drive *d, int dno, uint i)
2344 if(0 && d->led == Ibpirebuild){
2350 bits = ses2led[Ibpirebuild] | Lblinka*Locled;
2353 bits = ses2led[Ibpirebuild] | Lblinkb*Locled;
2357 bits = ses2led[d->led];
2358 if(d->ledbits != bits)
2359 ledcfg(c, dno, bits);
2363 odinledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
2369 d = c->drive + u->subno;
2370 return ledr(d, ch, a, n, off);
2374 odinledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
2380 d = c->drive + u->subno;
2381 return ledw(d, ch, a, n, off);
2385 * this kproc can probablly go when i figure out
2386 * how to program the manual blinker
2394 for(i = 0; i < nmsdrive; i++){
2396 d->nled = 2; /* how to know? */
2398 for(i = 0; i < nmsctlr; i++)
2399 pcicfgw32(msctlr[i].pci, Gpio, pcicfgr32(msctlr[i].pci, Gpio) | 1<<7);
2400 for(i = 0; i < nmsctlr; i++)
2401 setupled(msctlr + i);
2404 for(j = 0; j < nmsdrive; j++){
2406 trebuild(d->ctlr, d, j, i);
2422 kproc("odin", mskproc, 0);
2424 snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name);
2425 intrenable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf);
2426 // c->reg[Cis] |= Swirq1; /* force initial interrupt. */
2442 snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name);
2443 intrdisable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf);
2452 return d->type == Sas || d->feat & Datapi;
2463 d = c->drive + u->subno;
2478 u->sectors = d->sectors;
2479 u->secsize = d->secsize;
2480 } else if(d->state == Dready)
2492 checkdrive(d, d->driveno);
2493 for(w = 0; w < 12000; w += 210){
2494 if(d->state == Dready)
2496 if(w > 2000 && d->state != Dnew)
2498 if((sstatus(d) & Sphyrdy) == 0)
2501 checkdrive(d, d->driveno);
2515 d = c->drive + u->subno;
2521 sdaddfile(u, "led", 0644, eve, odinledr, odinledw);
2524 kproc("mvled", ledkproc, 0);
2531 * since devsd doesn't know much about hot-plug drives,
2532 * we need to give detected drives a chance.
2542 map(Pcidev *p, int bar)
2546 io = p->mem[bar].bar & ~0xf;
2547 return (uint*)vmap(io, p->mem[bar].size);
2554 c->fis = smalloc(0x800 + 0x100*16); /* §6.1.9.3 */
2555 c->reg[Fisbase + 0] = PCIWADDR(c->fis);
2556 c->reg[Fisbase + 1] = Pciwaddrh(c->fis);
2557 c->reg[Cqbase + 0] = PCIWADDR(c->cq);
2558 c->reg[Cqbase + 1] = Pciwaddrh(c->cq);
2559 c->reg[Cqcfg] = Cqen | Noattn | nelem(c->cq) - 1;
2560 c->reg[Dqbase + 0] = PCIWADDR(c->dq);
2561 c->reg[Dqbase + 1] = Pciwaddrh(c->dq);
2562 c->reg[Dqcfg] = Dqen | nelem(c->dq);
2563 c->cl = smalloc(nelem(c->cq)*sizeof *c->cl);
2564 c->reg[Clbase + 0] = PCIWADDR(c->cl);
2565 c->reg[Clbase + 1] = Pciwaddrh(c->cl);
2566 c->cmdtab = smalloc(Nctlrdrv*sizeof *c->cmdtab);
2573 c->reg[Gctl] |= Reset;
2574 while(c->reg[Gctl] & Reset)
2577 c->reg[Cie] = Swirq1 | 0xff*Portstop | 0xff*Portirq | Srsirq | Issstop | Cdone;
2578 c->reg[Gctl] |= Intenable;
2579 c->reg[Portcfg0] = Rmask*Regen | Dataunke | Rsple | Framele;
2580 c->reg[Portcfg1] = Rmask*Regen | 0xff*Xmten | /*Cmdirq |*/ Fisen | Resetiss | Issueen;
2582 sswrite(c, 0, Pwdtimer, 0x7fffff);
2589 * if we want to force sas/sata, here's where to do it.
2603 d->cmd = c->cmdtab + i;
2604 d->cmd->cmdh = c->cl + i;
2608 /* prep the precomputable bits in the cmd hdr §6.1.4 */
2609 putle(h->ctab, Pciw64(&cmd->Ctab), sizeof h->ctab);
2610 putle(h->oaf, Pciw64(&cmd->Oaf), sizeof h->oaf);
2611 putle(h->statb, Pciw64(&cmd->Statb), sizeof h->statb);
2612 putle(h->prd, Pciw64(&cmd->Aprdt), sizeof h->prd);
2614 /* finally, set up the wide-port participating bit */
2615 pcwrite(c, i, Pwidecfg, 1<<i);
2619 phychk(Ctlr *c, Drive *d)
2623 static uchar src[8] = {0x50, 0x03, 0x04, 0x80};
2626 memmove(d->ssasaddr, src, 8);
2627 u = getbe(d->ssasaddr, 8);
2628 pcwrite(c, i, Paddr + 0, u);
2629 pcwrite(c, i, Paddr + 4, u>>32);
2646 for(p = nil; (p = pcimatch(p, 0x11ab, 0x6485)) != nil; ){
2647 if(nmsctlr == Nctlr){
2648 print("sdodin: too many controllers\n");
2651 c = msctlr + nmsctlr;
2652 s = sdevs + nmsctlr;
2653 memset(c, 0, sizeof *c);
2654 memset(s, 0, sizeof *s);
2655 if((c->reg = map(p, Mebar)) == 0){
2656 print("sdodin: bar %#p in use\n", c->reg);
2659 nunit = p->did>>4 & 0xf;
2660 s->ifc = &sdodinifc;
2661 s->idno = 'a' + nmsctlr;
2665 c->ndrive = s->nunit = nunit;
2666 i = pcicfgr32(p, Dctl) & ~(7<<12);
2667 pcicfgw32(p, Dctl, i | 4<<12);
2669 print("#S/sd%c: odin ii sata/sas with %d ports\n", s->idno, nunit);
2672 for(i = 0; i < nunit; i++){
2678 snprint(d->name, sizeof d->name, "odin%d.%d", nmsctlr, i);
2679 msdrive[nmsdrive + i] = d;
2681 c->reg[pis[i] + 1] =
2682 Sync | Phyerr | Stperr | Crcerr |
2683 Linkrx | Martianfis | Anot | Bist | Sigrx |
2684 Phyunrdy | Martiantag | Bnot | Comw |
2685 Portsel | Hreset | Phyidto | Phyidok |
2697 msrctlsata(Drive *d, char *p, char *e)
2699 p = seprint(p, e, "flag\t");
2701 p = seprint(p, e, "udma\t%d\n", d->udma);
2706 rctldebug(char *p, char *e, Ctlr *c, Drive *d)
2712 p = seprint(p, e, "sstatus\t%.8ux\n", sstatus(d));
2713 // p = seprint(p, e, "cis\t%.8ux %.8ux\n", c->reg[Cis], c->reg[Cie]);
2714 // p = seprint(p, e, "gis\t%.8ux\n", c->reg[Gis]);
2715 p = seprint(p, e, "pis\t%.8ux %.8ux\n", c->reg[pis[i]], c->reg[pis[i] + 1]);
2716 p = seprint(p, e, "sis\t%.8ux\n", c->reg[Csis]);
2717 p = seprint(p, e, "cqwp\t%.8ux\n", c->cq[0]);
2718 p = seprint(p, e, "cerror\t%.8ux %.8ux\n", *(uint*)d->cmd->error, *(uint*)(d->cmd->error+4));
2719 p = seprint(p, e, "task\t%.8ux\n", gettask(d));
2720 p = seprint(p, e, "ptype\t%.8ux\n", c->reg[Ptype]);
2721 p = seprint(p, e, "satactl\t%.8ux\n", pcread(c, i, Psatactl)); /* appears worthless */
2722 p = seprint(p, e, "info %.8ux %.8ux\n", pcread(c, i, Pinfo), pcread(c, i, Painfo));
2723 p = seprint(p, e, "physts %.8ux\n", pcread(c, i, Pphysts));
2724 p = seprint(p, e, "widecfg %.8ux\n", pcread(c, i, Pwidecfg));
2725 sasid = pcread(c, i, Pwwn + 0);
2726 sasid |= (uvlong)pcread(c, i, Pwwn + 4)<<32;
2727 p = seprint(p, e, "wwn %.16llux %.8ux\n", sasid, pcread(c, i, Pwwn + 8));
2728 sasid = pcread(c, i, Pawwn + 0);
2729 sasid |= (uvlong)pcread(c, i, Pawwn + 4)<<32;
2730 p = seprint(p, e, "awwn %.16llux\n", sasid);
2731 sasid = pcread(c, i, Paddr + 0);
2732 sasid |= (uvlong)pcread(c, i, Paddr + 4)<<32;
2733 p = seprint(p, e, "sasid %.16llux\n", sasid);
2738 msrctl(SDunit *u, char *p, int l)
2744 if((c = u->dev->ctlr) == nil)
2746 d = c->drive + u->subno;
2749 p = seprint(p, e, "state\t%s\n", dstate(d->state));
2750 p = seprint(p, e, "type\t%s", type[d->type]);
2752 p = seprint(p, e, " sig %.8ux", getsig(d));
2753 p = seprint(p, e, "\n");
2754 if(d->state == Dready){
2755 p = seprint(p, e, "model\t%s\n", d->model);
2756 p = seprint(p, e, "serial\t%s\n", d->serial);
2757 p = seprint(p, e, "firm\t%s\n", d->firmware);
2758 p = seprint(p, e, "wwn\t%llux\n", d->wwn);
2759 p = msrctlsata(d, p, e);
2761 p = rctldebug(p, e, c, d);
2762 p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
2767 forcestate(Drive *d, char *state)
2771 for(i = 1; i < nelem(diskstates); i++)
2772 if(strcmp(state, diskstates[i]) == 0)
2774 if(i == nelem(diskstates))
2777 d->state = 1 << i - 1;
2783 mswctl(SDunit *u, Cmdbuf *cmd)
2790 d = c->drive + u->subno;
2792 if(strcmp(f[0], "state") == 0)
2793 forcestate(d, f[1]? f[1]: "null");
2795 cmderror(cmd, Ebadctl);
2800 mswtopctl(SDev*, Cmdbuf *cmd)
2807 if(strcmp(f[0], "debug") == 0)
2809 else if(strcmp(f[0], "idprint") == 0)
2811 else if(strcmp(f[0], "aprint") == 0)
2814 cmderror(cmd, Ebadctl);
2817 else if(cmd->nf == 2)
2818 *v = strcmp(f[1], "on") == 0;
2820 cmderror(cmd, Ebadarg);