summaryrefslogtreecommitdiff
path: root/sys/src/cmd/atazz/main.c
diff options
context:
space:
mode:
authorgoogle <google@daverabbitz.ath.cx>2012-09-20 22:39:48 +1200
committergoogle <google@daverabbitz.ath.cx>2012-09-20 22:39:48 +1200
commitfa7fb8b66b9ff50029532d09315f03896f2ac4c4 (patch)
tree262cfe8966f3c07a5ad6ed61fda028f6fa928a36 /sys/src/cmd/atazz/main.c
parent913afc39c9b4750e630c7f4ff3161a37602b006b (diff)
Add Erik Quanstrom's atazz
(needed to disable power management/head unload on 2.5" drive)
Diffstat (limited to 'sys/src/cmd/atazz/main.c')
-rw-r--r--sys/src/cmd/atazz/main.c1928
1 files changed, 1928 insertions, 0 deletions
diff --git a/sys/src/cmd/atazz/main.c b/sys/src/cmd/atazz/main.c
new file mode 100644
index 000000000..0d0565eaf
--- /dev/null
+++ b/sys/src/cmd/atazz/main.c
@@ -0,0 +1,1928 @@
+#include <u.h>
+#include <libc.h>
+#include <fis.h>
+#include "atazz.h"
+#include "tabs.h"
+
+#pragma varargck argpos eprint 1
+#pragma varargck type "π" char**
+
+enum {
+ Dontread = -2,
+};
+
+int interrupted;
+int rflag;
+int squelch;
+int scttrace;
+uchar issuetr[0x100];
+
+Atatab *idcmd;
+Atatab *idpktcmd;
+Atatab *sigcmd;
+Atatab *sctread;
+Atatab *sctissue;
+
+int
+πfmt(Fmt *f)
+{
+ char **p;
+
+ p = va_arg(f->args, char**);
+ if(p == nil)
+ return fmtstrcpy(f, "<nil**>");
+ for(; *p; p++){
+ fmtstrcpy(f, *p);
+ if(p[1] != nil)
+ fmtstrcpy(f, " ");
+ }
+ return 0;
+}
+
+int
+eprint(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ if(squelch)
+ return 0;
+// Bflush(&out);
+
+ va_start(args, fmt);
+ n = vfprint(2, fmt, args);
+ va_end(args);
+ return n;
+}
+
+void
+fisset(Req *r, uint i, uint v)
+{
+ if(r->fisbits & 1<<i)
+ return;
+ r->fisbits |= 1<<i;
+ r->cmd.fis[i] = v;
+}
+
+void
+prreq(Req *r)
+{
+ uchar *u;
+
+ print("%.2ux %.2ux\n", r->cmd.sdcmd, r->cmd.ataproto);
+ u = r->cmd.fis;
+ print("%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux ",
+ u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]);
+ u += 8;
+ print("%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux\n",
+ u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]);
+}
+
+char*
+protostr(char *p, char *e, int pr)
+{
+ char *s;
+
+ *p = 0;
+ if(pr & P28)
+ p = seprint(p, e, "28:");
+ else
+ p = seprint(p, e, "28:");
+ switch(pr & Pprotom){
+ default:
+ s = "unk";
+ break;
+ case Ppkt:
+ s = "pkt";
+ break;
+ case Pdiag:
+ s = "dig";
+ break;
+ case Preset:
+ s = "rst";
+ break;
+ case Pdmq:
+ s = "dmq";
+ break;
+ case Pdma:
+ s = "dma";
+ break;
+ case Ppio:
+ s = "pio";
+ break;
+ }
+ p = seprint(p, e, "%s:", s);
+ switch(pr & Pdatam){
+ default:
+ s = "nd";
+ break;
+ case Pin:
+ s = "in";
+ break;
+ case Pout:
+ s = "out";
+ break;
+ }
+ p = seprint(p, e, "%s", s);
+ return p;
+}
+
+void
+displaycmd(Req *r, Atatab *a)
+{
+ char buf[32];
+ int i;
+
+ if(a->cc > nelem(issuetr) || !issuetr[a->cc])
+ return;
+ protostr(buf, buf + sizeof buf, a->protocol);
+ fprint(2, "cmd %s:%2ux ", buf, a->cc);
+ for(i = 0; i < 16; i++)
+ fprint(2, "%.2ux", r->cmd.fis[i]);
+ fprint(2, "\n");
+}
+
+int
+issueata(Req *r, Atatab *a, Dev *d)
+{
+ uchar u;
+ int n, ok, pr, rv;
+
+ r->haverfis = 0;
+ pr = a->protocol & Pdatam;
+ r->data = realloc(r->data, r->count);
+ if(r->data == nil && r->count > 0)
+ sysfatal("realloc: %r");
+ if(r->data == nil && pr != Pnd)
+ sysfatal("no data for cmd %.2ux", a->cc);
+ if(0 && r->fisbits)
+ print("fisbits %.16b\n", r->fisbits);
+ r->cmd.sdcmd = 0xff;
+ r->cmd.ataproto = a->protocol;
+ if(a->cc & 0xff00)
+ fisset(r, 0, a->cc >> 8);
+ else
+ fisset(r, 0, H2dev);
+ fisset(r, 1, Fiscmd);
+ fisset(r, 2, a->cc);
+ switch(pr){
+ case Pout:
+ if(r->rfd != Dontread){
+ n = readn(r->rfd, r->data, r->count);
+ if(n != r->count){
+ if(n == -1)
+ eprint("!short src read %r\n");
+ else
+ eprint("!short src read %d wanted %lld\n", n, r->count);
+ return -1;
+ }
+ }
+ case Pnd:
+ case Pin:
+ if(0 && (d->feat & Dlba) == 0 && (d->c | d->h | d->s)){
+ int c, h, s;
+ c = r->lba / (d->s * d->h);
+ h = (r->lba / d->s) % d->h;
+ s = (r->lba % d->s) + 1;
+print("%d %d %d\n", c, h, s);
+ fisset(r, 4, s);
+ fisset(r, 5, c);
+ fisset(r, 6, c >> 8);
+ fisset(r, 7, Ataobs | h);
+ }else{
+ fisset(r, 4, r->lba);
+ fisset(r, 5, r->lba >> 8);
+ fisset(r, 6, r->lba >> 16);
+ u = Ataobs;
+ if(pr == Pin || pr == Pout)
+ u |= Atalba;
+ if((d->feat & Dllba) == 0)
+ u |= (r->lba >> 24) & 7;
+ fisset(r, 7, u);
+ fisset(r, 8, r->lba >> 24);
+ fisset(r, 9, r->lba >> 32);
+ fisset(r, 10, r->lba >> 48);
+ }
+ fisset(r, 12, r->nsect);
+ fisset(r, 13, r->nsect >> 8);
+ break;
+ }
+ fisset(r, 7, Ataobs);
+ displaycmd(r, a);
+ if(write(d->fd, &r->cmd, Cmdsz) != Cmdsz){
+ eprint("fis write error: %r\n");
+ return -1;
+ }
+
+ werrstr("");
+ switch(pr){
+ default:
+ ok = read(d->fd, "", 0) == 0;
+ break;
+ case Pin:
+ ok = read(d->fd, r->data, r->count) == r->count;
+ r->lba += r->nsect;
+ break;
+ case Pout:
+ ok = write(d->fd, r->data, r->count) == r->count;
+ r->lba += r->nsect;
+ break;
+ }
+ rv = 0;
+ if(ok == 0){
+ eprint("xfer error: %.2ux %r\n", a->cc);
+ rv = -1;
+ }
+ switch(n = read(d->fd, &r->reply, Replysz)){
+ case Replysz:
+ r->haverfis = 1;
+ return rv;
+ case -1:
+ eprint("status fis read error: %r\n");
+ return -1;
+ default:
+ eprint("status fis read error: short read: %d of %d\n", n, Replysz);
+ return -1;
+ }
+}
+
+/*
+ * cheezy code; just issue a inquiry. use scuzz
+ * for real work with atapi devices
+ */
+int
+issuepkt(Req *r, Atatab *a, Dev *d)
+{
+ char *p;
+ uchar *u;
+ int n, rv;
+
+ r->haverfis = 0;
+ r->count = 128;
+ r->data = realloc(r->data, r->count);
+ if(r->data == nil && r->count > 0)
+ sysfatal("realloc: %r");
+ r->cmd.sdcmd = 0xff;
+ r->cmd.ataproto = a->protocol;
+ memset(r->cmd.fis, 0, Fissize);
+
+ u = r->cmd.fis;
+ u[0] = 0x12;
+ u[4] = 128-1;
+ displaycmd(r, a);
+
+ if(write(d->fd, &r->cmd, 6 + 2) != 6 + 2){
+ eprint("fis write error: %r\n");
+ return -1;
+ }
+ n = read(d->fd, r->data, r->count);
+ rv = 0;
+ if(n == -1){
+ eprint("xfer error: %.2ux %r\n", a->cc);
+ rv = -1;
+ }
+
+ print("n is %d (%lld)\n", n, r->count);
+ if(n > 32){
+ p = (char*)r->data;
+ print("%.8s %.16s\n", p + 8, p + 16);
+ }
+
+ u = (uchar*)&r->reply;
+ n = read(d->fd, u, Replysz);
+ if(n < 0){
+ eprint("status fis read error (%d): %r\n", n);
+ return -1;
+ }
+
+ if(n < Replysz)
+ memset(u + n, 0, Replysz - n);
+ r->haverfis = 1;
+ return rv;
+}
+
+/*
+ * silly protocol:
+ * 1. use write log ext 0xe0 to fill out the command
+ * 2. use write log ext 0xe1 to write or data (if any)
+ * 3. use read log ext 0xe0 to nab status. polled
+ */
+void
+sctreq(Req *r)
+{
+ memset(r, 0, sizeof *r);
+ r->rfd = Dontread;
+}
+
+char*
+sctrsp(Req *r)
+{
+ uint i;
+ static char buf[32];
+
+ if(!r->haverfis)
+ return "no rfis";
+ if((r->reply.fis[Frerror] & (Eidnf | Eabrt)) == 0)
+ return nil;
+ i = r->reply.fis[Fsc] | r->reply.fis[Flba0]<<8;
+ if(i == 0xffff)
+ return "in progress";
+ else if(i == 0){
+ snprint(buf, sizeof buf, "unknown %.2ux", r->reply.fis[Frerror]);
+ return buf;
+ }else if(i < nelem(sctetab))
+ return sctetab[i];
+ else
+ return "<bad>";
+}
+
+char*
+sctready(Dev *d, int sec)
+{
+ char *s;
+ int i;
+ Req r;
+ static char e[ERRMAX];
+
+ for(;;){
+ if(interrupted){
+ s = "interrupted";
+ break;
+ }
+ sctreq(&r);
+ fisset(&r, Fsc, 1);
+ fisset(&r, Flba0, 0xe0);
+ r.count = 512;
+ i = issueata(&r, sctread, d);
+ free(r.data);
+ if(i == -1){
+ rerrstr(e, ERRMAX);
+ s = e;
+ break;
+ }
+ if((r.cmd.fis[Fsc] | r.cmd.fis[Fsc8]<<8) != 0xffff){
+ s = sctrsp(&r);
+ break;
+ }
+ if(sec == 0){
+ s = "timeout";
+ break;
+ }
+ sleep(1000);
+ sec--;
+ }
+ return s;
+}
+
+typedef struct Sttab Sttab;
+struct Sttab {
+ int o;
+ int sz;
+ char *name;
+};
+
+Sttab sctt[] = {
+ 0, 2, "version",
+ 2, 2, "period",
+ 4, 2, "intval",
+ 6, 1, "max op",
+ 7, 1, "max",
+ 8, 1, "min op",
+ 9, 1, "min",
+};
+
+void
+sctttab(Req *r)
+{
+ char c, buf[10];
+ int i, n, l, d;
+ uchar *u;
+
+ u = r->data;
+ for(i = 0; i < nelem(sctt); i++){
+ switch(sctt[i].sz){
+ case 1:
+ c = u[sctt[i].o];
+ print("%s\t%d\n", sctt[i].name, c);
+ break;
+ case 2:
+ d = w(u + sctt[i].o);
+ print("%s\t%ud\n", sctt[i].name, d);
+ break;
+ }
+ }
+ n = w(u + 30);
+ l = w(u + 32);
+ for(i = 0; i < n; i++){
+ c = u[34 + (l + i) % n];
+ if((uchar)c == 0x80)
+ snprint(buf, sizeof buf, "xx");
+ else
+ snprint(buf, sizeof buf, "%d", c);
+ d = i%10;
+ if(d == 0)
+ print("\nt%d\t%d", i, c);
+ else
+ print("% .2d", c);
+ }
+ if(i%10)
+ print("\n");
+}
+
+static struct {
+ uint code;
+ char *s;
+ char *ms;
+} fxtab[] = {
+ 0x00010001, "set features", 0,
+ 0x00010002, "enabled", 0,
+ 0x00010003, "disabled", 0,
+
+ 0x00020001, "enabled", 0,
+ 0x00020002, "disabled", 0,
+
+ 0x0003ffff, "minute", "minutes",
+};
+
+void
+sctfcout(ushort *u, Req *r)
+{
+ uchar *f;
+ ushort v;
+ uint c, m, i;
+
+ f = r->reply.fis;
+ switch(u[1]){
+ case 1:
+ case 2:
+ v = f[Fsc] | f[Flba0]<<8;
+ c = u[2]<<16 | v;
+ m = u[2]<<16 | 0xffff;
+ for(i = 0; i < nelem(fxtab); i++)
+ if(fxtab[i].code == c)
+ print("%s\n", fxtab[i].s);
+ else if(fxtab[i].code == m)
+ print("%d %s\n", v, v>1? fxtab[i].ms: fxtab[i].s);
+ break;
+ case 3:
+ v = f[Fsc] | f[Flba0]<<8;
+ if(v & 1)
+ print("preserve\n");
+ else
+ print("volatile\n");
+ break;
+ }
+}
+
+void
+scterout(ushort *u, Req *r)
+{
+ uchar *f;
+ uint v;
+
+ f = r->reply.fis;
+ switch(u[1]){
+ case 2:
+ v = f[Fsc] | f[Flba0]<<8;
+ v *= 100;
+ print("%dms\n", v);
+ }
+}
+
+void
+sctout(ushort *u, Req *r)
+{
+ switch(u[0]){
+ case 5:
+ sctttab(r);
+ break;
+ case 4:
+ sctfcout(u, r);
+ break;
+ case 3:
+ scterout(u, r);
+ break;
+ }
+}
+
+int
+issuesct0(Req *r0, Atatab *a, Dev *d)
+{
+ char *s;
+ uchar proto;
+ Atatab *txa;
+ Req r;
+
+ if((d->feat & Dsct) == 0){
+ eprint("sct not supported\n");
+ return -1;
+ }
+
+ /* 1. issue command */
+ sctreq(&r);
+ r.data = malloc(r0->count);
+ memcpy(r.data, r0->data, r0->count);
+ r.count = r0->count;
+ fisset(&r, Fsc, 1);
+ fisset(&r, Flba0, 0xe0);
+ if(issueata(&r, sctissue, d) == -1)
+ return -1;
+ if(s = sctrsp(&r)){
+ eprint("sct error: %s\n", s);
+ return -1;
+ }
+
+ /* 1a. check response */
+ if((s = sctready(d, 1)) != nil){
+ eprint("sct cmd: %s\n", s);
+ return -1;
+ }
+ /* 2. transfer data */
+
+ proto = a->protocol;
+ if(r0->fisbits & 1 << 16){
+ proto &= ~Pdatam;
+ proto |= r0->cmd.ataproto;
+ }
+ switch(proto & Pdatam){
+ default:
+ txa = nil;
+ break;
+ case Pin:
+ txa = sctread;
+ break;
+/* case Pout:
+ txa = sctout;
+ break;
+*/
+ }
+
+ if(txa != nil){
+ sctreq(&r);
+ r.count = 512;
+ fisset(&r, Fsc, 1);
+ fisset(&r, Flba0, 0xe1);
+ if(issueata(&r, txa, d) == -1)
+ return -1;
+
+ /* 2a. check response */
+ if((s = sctready(d, 1)) != nil){
+ eprint("sct cmd: %s\n", s);
+ return -1;
+ }
+ }
+
+ sctout((ushort*)r0->data, &r);
+ free(r.data);
+ return 0;
+}
+
+static void*
+pushtrace(int i)
+{
+ void *tr0;
+
+ tr0 = malloc(sizeof issuetr);
+ if(tr0 == 0)
+ return 0;
+ memcpy(tr0, issuetr, sizeof issuetr);
+ memset(issuetr, i, sizeof issuetr);
+ return tr0;
+}
+
+static void
+poptrace(void *tr0)
+{
+ if(tr0 == nil)
+ return;
+ memcpy(issuetr, tr0, sizeof issuetr);
+ free(tr0);
+}
+
+int
+issuesct(Req *r0, Atatab *a, Dev *d)
+{
+ int r;
+ void *t;
+
+ t = nil;
+ if(scttrace)
+ t = pushtrace(1);
+ r = issuesct0(r0, a, d);
+ if(scttrace)
+ poptrace(t);
+ return r;
+}
+
+int
+issue(Req *r, Atatab *a, Dev *d)
+{
+ int rv;
+ int (*f)(Req*, Atatab*, Dev*);
+
+ if(a->protocol & Psct)
+ f = issuesct;
+ else if((a->protocol & Pprotom) == Ppkt)
+ f = issuepkt;
+ else
+ f = issueata;
+ rv = f(r, a, d);
+ if(r->haverfis)
+ if(r->reply.fis[Fstatus] & ASerr){
+ werrstr("ata error");
+ rv = -1;
+ }
+ return rv;
+}
+
+void
+sigfmt(Req *r)
+{
+ print("%.8ux\n", fistosig(r->reply.fis));
+}
+
+int
+opendev(char *dev, Dev *d)
+{
+ char buf[128];
+ int rv;
+ ushort *u;
+ Req r;
+
+ if(d->fd != -1)
+ close(d->fd);
+ memset(d, 0, sizeof *d);
+ snprint(buf, sizeof buf, "%s/raw", dev);
+ d->fd = open(buf, ORDWR);
+ if(d->fd == -1)
+ return -1;
+ memset(&r, 0, sizeof r);
+ if(issue(&r, sigcmd, d) == -1){
+lose:
+ close(d->fd);
+ return -1;
+ }
+ setfissig(d, fistosig(r.reply.fis));
+ memset(&r, 0, sizeof r);
+ r.count = 512;
+ r.nsect = 1;
+ if(d->sig>>16 == 0xeb14)
+ rv = issue(&r, idpktcmd, d);
+ else
+ rv = issue(&r, idcmd, d);
+ if(rv == -1)
+ goto lose;
+ u = (ushort*)r.data;
+ d->nsect = idfeat(d, u);
+ d->secsize = idss(d, u);
+ d->wwn = idwwn(d, u);
+ return 0;
+}
+
+void
+rawout(Req *r)
+{
+ int n;
+
+ n = write(r->wfd, r->data, r->count);
+ if(n != r->count)
+ eprint("!short write %ud wanted %lld\n", n, r->count);
+}
+
+static ushort
+gbit16(void *a)
+{
+ ushort j;
+ uchar *i;
+
+ i = a;
+ j = i[1] << 8;
+ j |= i[0];
+ return j;
+}
+
+static Btab extra[] = {
+ 12, "ncqpri",
+ 11, "ncqunload",
+ 10, "phyevent",
+ 9, "hpwrctl",
+ 3, "6.0gbit",
+ 2, "3.0gbit",
+ 1, "1.5gbit",
+};
+
+static Btab suptab[] = {
+ 8, "wwn",
+ 5, "mediaserial",
+ 1, "smartst",
+ 0, "smartlog"
+};
+
+char*
+pextraid(char *p, char *e, ushort *id, uint *medserial)
+{
+ char *p0;
+ ushort u;
+
+ *p = 0;
+ *medserial = 0;
+ p0 = p;
+ p = sebtab(p, e, extra, nelem(extra), gbit16(id + 76));
+ if(p != p0)
+ p = seprint(p, e, " ");
+ u = gbit16(id + 83);
+ if(u & 1<<5)
+ p = seprint(p, e, "gpl ");
+ p0 = p;
+ p = sebtab(p, e, suptab, nelem(suptab), gbit16(id + 84));
+ if(p != p0)
+ p = seprint(p, e, " ");
+ u = gbit16(id + 120);
+ if(u & 1<<2)
+ p = seprint(p, e, "wunc ");
+ return p;
+}
+
+static char *patatab[] = {
+ "ata8-apt",
+ "ata/atapi-7",
+};
+
+static char *satatab[] = {
+ "ata8-ast",
+ "sata1.0a",
+ "sataiiext",
+ "sata2.5",
+ "sata2.6",
+ "sata3.0",
+};
+
+char*
+ptransport(char *p, char *e, ushort *id)
+{
+ char *s;
+ ushort u, i;
+
+ u = gbit16(id + 222);
+ if(u == 0 || u == 0xffff)
+ return seprint(p, e, "unreported ");
+ i = (u>>5) & 0x7f;
+ switch(u & 7<<12){
+ default:
+ s = "unktransport";
+ break;
+ case 0:
+ s = "unkparallel";
+ if(i < nelem(patatab))
+ s = patatab[i];
+ break;
+ case 1<<12:
+ s = "unkserial";
+ if(i < nelem(satatab))
+ s = satatab[i];
+ break;
+ }
+ return seprint(p, e, "%s ", s);
+}
+
+Btab entab[] = {
+ 10, "hpa",
+ 9, "reset",
+ 8, "service",
+ 7, "release",
+ 6, "rdlookahd",
+ 5, "vwc",
+ 4, "packet",
+ 3, "pm",
+ 2, "security",
+ 1, "smart",
+};
+
+Btab addlen[] = {
+ 15, "cfast",
+// 14, "trim", /* check 169 */
+ 13, "lpsalignerr",
+ 12, "iddma",
+ 11, "rbufdma",
+ 10, "wbufdma",
+ 9, "pwddma",
+ 8, "dlmcdma",
+};
+
+char*
+penabled(char *p, char *e, ushort *id)
+{
+ char *p0;
+ ushort u;
+
+ p0 = p;
+ p = sebtab(p, e, addlen, nelem(addlen), gbit16(id + 69));
+ u = gbit16(id + 87);
+ if(u>>14 == 1){
+ if(p != p0)
+ p = seprint(p, e, " ");
+ p = sebtab(p, e, entab, nelem(entab), gbit16(id + 85));
+ }
+ return p;
+}
+
+static char *fftab[] = {
+ nil,
+ "5¼",
+ "3½",
+ "2½",
+ "1.8",
+ "<1.8",
+};
+
+char*
+pff(char *p, char *e, ushort *id)
+{
+ char *p0;
+ ushort u;
+
+ p0 = p;
+ u = gbit16(id + 168);
+ if(u < nelem(fftab) && fftab[u] != nil)
+ p = seprint(p, e, "%s ", fftab[u]);
+ u = gbit16(id + 217);
+ if(u == 1)
+ p = seprint(p, e, "solid-state ");
+ else if(u != 0 && u != 0xfffe)
+ p = seprint(p, e, "%udrpm ", u);
+ if(p != p0)
+ p--;
+ *p = 0;
+ return p;
+}
+
+Btab scttab[] = {
+ 5, "tables",
+ 4, "feactl",
+ 3, "errctl",
+ 2, "wsame",
+ 1, "rwlong",
+ 0, "sct",
+};
+
+char*
+psct(char *p, char *e, ushort *id)
+{
+ return sebtab(p, e, scttab, nelem(scttab), gbit16(id + 206));
+}
+
+void
+idfmt(Req *r)
+{
+ char buf[100];
+ uint ss, i;
+ ushort *id;
+ uvlong nsect;
+ Sfis f;
+
+ if(r->fmtrw == 0){
+ rawout(r);
+ return;
+ }
+ id = (ushort*)r->data;
+ nsect = idfeat(&f, id);
+ ss = idss(&f, id);
+
+ idmove(buf, id+10, 20);
+ print("serial\t%s\n", buf);
+ idmove(buf, id+23, 8);
+ print("firm\t%s\n", buf);
+ idmove(buf, id+27, 40);
+ print("model\t%s\n", buf);
+ print("wwn\t%ullx\n", idwwn(&f, id));
+ pflag(buf, buf + sizeof buf, &f);
+ print("flags\t%s", buf);
+ print("geometry %llud %ud", nsect, ss);
+ if(f.c | f.h | f.s)
+ print(" %ud %ud %ud", f.c, f.h, f.s);
+ print("\n");
+ penabled(buf, buf + sizeof buf, id);
+ print("enabled\t%s\n", buf);
+ pextraid(buf, buf + sizeof buf, id, &i);
+ print("extra\t%s\n", buf);
+ if(i){
+ idmove(buf, id + 176, 60);
+ if(buf[0] != 0)
+ print("medias\t%s\n", buf);
+ }
+ psct(buf, buf + sizeof buf, id);
+ if(buf[0])
+ print("sct\t%s\n", buf);
+ ptransport(buf, buf + sizeof buf, id);
+ print("trans\t%s\n", buf);
+ pff(buf, buf + sizeof buf, id);
+ if(buf[0])
+ print("ff\t%s\n", buf);
+}
+
+void
+smfmt(Req *r)
+{
+ uchar *fis;
+
+ if(r->cmd.fis[Ffeat] == 0xda){
+ fis = r->reply.fis;
+ if(fis[5] == 0x4f &&
+ fis[6] == 0xc2)
+ eprint("normal\n");
+ else
+ eprint("threshold exceeded\n");
+ return;
+ }
+}
+
+void
+iofmt(Req *r)
+{
+ uchar *u;
+ int i;
+
+ if(r->fmtrw == 0){
+ rawout(r);
+ return;
+ }
+ u = r->data;
+ for(i = 0; i < r->count; i += 16)
+ fprint(2, "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux"
+ "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux\n",
+ u[i + 0], u[i + 1], u[i + 2], u[i + 3], u[i + 4], u[i + 5], u[i + 6], u[i + 7],
+ u[i + 8], u[i + 9], u[i +10], u[i +11], u[i +12], u[i +13], u[i +14], u[i +15]);
+}
+
+static char *csbyte[] = {
+ "never started",
+ nil,
+ "competed without error",
+ "in progress",
+ "suspended by cmd from host",
+ "aborted by cmd from host",
+ "aborted by device with fatal error",
+};
+
+static char *exe[] = {
+ "no error or never run",
+ "aborted by host",
+ "interrupted by host",
+ "fatal error; unable to complete",
+ "failed",
+ "failed: electricial",
+ "failed: servo",
+ "failed: read",
+ "failed: shipping damage",
+[0xf] "in progress",
+};
+
+char*
+tabtr(uint u, char **tab, int ntab)
+{
+ char *s;
+
+ if(u >= ntab || (s = tab[u]) == nil)
+ s = "reserved";
+ return s;
+}
+
+void
+sdfmt(Req *r)
+{
+ char *s;
+ uchar *b;
+ ushort u;
+
+ if(r->fmtrw == 0){
+ rawout(r);
+ return;
+ }
+ b = r->data;
+ u = b[362];
+ if((u & 0xf0) == 0x80 && u != 0x81 && u != 0x83)
+ u &= 0xf;
+ s = tabtr(u, csbyte, nelem(csbyte));
+ print("col status: %.2ux %s\n", b[362], s);
+ u = b[363];
+ s = tabtr(u>>4, exe, nelem(exe));
+ if(u & 0xf)
+ print("exe status: %.2ux %s, %d0%% left\n", u, s, u & 0xf);
+ else
+ print("exe status: %.2ux %s\n", u, s);
+ u = b[364] | b[365]<<8;
+ print("time left: %uds\n", u);
+ print("shrt poll: %udm\n", b[373]);
+ u = b[374];
+ if(u == 0xff)
+ u = b[375] | b[376]<<8;
+ print("ext poll: %udm\n", u);
+}
+
+void
+pagemapfmt(Req *r)
+{
+ int i;
+ ushort *u;
+
+ u = (ushort*)r->data;
+ if(u[0] != 1){
+ print("unsupported\n");
+ return;
+ }
+ for(i = 1; i < 128; i++)
+ if(u[i] > 0)
+ print("page %d: %d\n", i, u[i]);
+}
+
+void
+slfmt(Req *r)
+{
+ switch(r->cmd.fis[Flba0]){
+ default:
+ iofmt(r);
+ break;
+ case 0:
+ pagemapfmt(r);
+ break;
+ }
+}
+
+enum{
+ Physz = 7<<12,
+};
+
+static char *phyec[] = {
+ "no event",
+ "icrc",
+ "err data",
+ "err d2h data",
+ "err h2d data",
+[0x05] "err nd",
+ "err d2h nd",
+ "err h2d nd",
+ "retry d2h nd",
+ "nready",
+[0x0a] "comreset",
+ "h2d crc",
+ nil,
+ "bad h2d",
+ nil,
+ "err h2d data crc",
+[0x10] "err h2d data",
+ nil,
+ "err h2d nd crc",
+ "err h2d nd",
+};
+
+void
+phyfmt(Req *r)
+{
+ char *ec;
+ uchar *p;
+ ushort *u, *e, id, sz;
+
+ u = (ushort*)r->data;
+ e = u + 510/sizeof *u;
+ for(u += 2; u < e; u += sz){
+ id = w((uchar*)u);
+ sz = (id & Physz) >> 12;
+ id &= ~Physz;
+ if(sz == 0)
+ break;
+ ec = "unk";
+ if(id < nelem(phyec) && phyec[id] != nil)
+ ec = phyec[id];
+ print("%.4ux\t%-15s\t", id, ec);
+ p = (uchar*)u + 2;
+ switch(sz<<1){
+ default:
+ print("\n");
+ break;
+ case 2:
+ print("%.4ux\n", w(p));
+ break;
+ case 4:
+ print("%.8ux\n", dw(p));
+ break;
+ case 8:
+ print("%.16llux\n", qw(p));
+ break;
+ }
+ sz += 1;
+ }
+}
+
+typedef struct Gltab Gltab;
+struct Gltab{
+ int offset;
+ char *name;
+};
+
+Gltab page3[] = {
+ 8, "power-on hrs",
+ 16, "head flying hrs",
+ 24, "head loads",
+ 32, "realloc'd sec",
+ 40, "read recovery att",
+ 48, "start failures"
+};
+
+void
+qpfmt(Req *r, Gltab *t, int ntab)
+{
+ uchar *u;
+ int i;
+ uvlong v;
+
+ u = r->data;
+ for(i = 0; i < ntab; i++){
+ v = qw(u + t[i].offset);
+ if((v & 3ll<<63) != 3ll<<63)
+ continue;
+ print("%lud\t%s\n", (ulong)v, t[i].name);
+ }
+}
+
+static char *sctsttab[] = {
+ "active waiting",
+ "standby",
+ "sleep",
+ "dst bgnd",
+ "smart bgnd",
+ "sct bgnd",
+};
+
+void
+sctstatfmt(Req *r)
+{
+ char *s;
+ uchar *id, c;
+
+ id = r->data;
+ print("version\t%d\n", gbit16(id + 0));
+ print("vnd ver\t%2ux\n", gbit16(id + 2));
+ print("flags\t%.8ux\n", dw(id + 6));
+ c = id[10];
+ s = "unk";
+ if(c < nelem(sctsttab))
+ s = sctsttab[c];
+ print("state\t%s\n", s);
+ print("ext stat\t%.4ux\n", gbit16(id + 14));
+ print("act code\t%.4ux\n", gbit16(id + 16));
+ print("fn code\t%.4ux\n", gbit16(id + 18));
+ print("lba\t%llud\n", qw(id + 40));
+ print("temp\t%d\n", id[200]);
+ print("min t\t%d %d\n", id[201], id[203]);
+ print("max t\t%d %d\n", id[202], id[204]);
+ print("ot\t%d\n", dw(id + 206));
+ print("ut\t%d\n", dw(id + 210));
+}
+
+
+void
+glfmt(Req *r)
+{
+ switch(r->cmd.fis[Flba0]){
+ case 0:
+ pagemapfmt(r);
+ break;
+ case 3:
+ qpfmt(r, page3, nelem(page3));
+ break;
+ case 17:
+ phyfmt(r);
+ break;
+ case 0xe0:
+ sctstatfmt(r);
+ break;
+ default:
+ iofmt(r);
+ break;
+ }
+}
+
+char*
+readline(char *prompt, char *line, int len)
+{
+ char *p, *e, *q;
+ int n, dump;
+
+ e = line + len;
+retry:
+ dump = 0;
+ if(interrupted)
+ eprint("\n%s", prompt);
+ else
+ eprint("%s", prompt);
+ interrupted = 0;
+ for(p = line;; p += n){
+ if(p == e){
+ dump = 1;
+ p = line;
+ }
+ n = read(0, p, e - p);
+ if(n < 0){
+ if(interrupted)
+ goto retry;
+ return nil;
+ }
+ if(n == 0)
+ return nil;
+ if(q = memchr(p, '\n', n)){
+ if(dump){
+ eprint("!line too long\n");
+ goto retry;
+ }
+ p = q;
+ break;
+ }
+ }
+ *p = 0;
+ return line;
+}
+
+void
+suggesttab(char *cmd, Atatab *a, int n)
+{
+ int i, l;
+
+ l = strlen(cmd);
+ for(i = 0; i < n; i++)
+ if(cistrncmp(cmd, a[i].name, l) == 0)
+ eprint("%s\n", a[i].name);
+}
+
+Atatab*
+findtab(char **cmd, Atatab *a, int n)
+{
+ char *p, *c;
+ int i, cc, max, l;
+
+ cc = strtoul(*cmd, &p, 0);
+ if(p != *cmd && (*p == 0 || *p == ' ')){
+ for(i = 0; i < n; i++)
+ if(a[i].cc == cc){
+ *cmd = p + 1;
+ return a + cc;
+ }
+ return 0;
+ }
+ max = 0;
+ cc = 0;
+ c = *cmd;
+ for(i = 0; i < n; i++){
+ l = strlen(a[i].name);
+ if(l > max && cistrncmp(*cmd, a[i].name, l) == 0)
+ if(c[l] == ' ' || c[l] == 0){
+ max = l + (c[l] == ' ');
+ cc = i;
+ }
+ }
+ if(max > 0){
+ *cmd = *cmd + max;
+ return a + cc;
+ }
+ return 0;
+}
+
+int
+catch(void*, char *note)
+{
+ if(strstr(note, "interrupt") != nil)
+ return interrupted = 1;
+ return 0;
+}
+
+char**
+ndargs(Atatab*, Req *, char **p)
+{
+ return p;
+}
+
+char**
+ioargs(Atatab *, Req *r, char **p)
+{
+ if(r->nsect == 0)
+ r->nsect = 1;
+ if(p[0] == 0)
+ return p;
+ r->lba = strtoull(p[0], 0, 0);
+ p++;
+ if(p[0] == 0)
+ return p;
+ r->nsect = strtoul(p[0], 0, 0);
+ return p + 1;
+}
+
+char**
+stdargs(Atatab *, Req *r, char **p)
+{
+ char *s;
+ Rune x;
+
+ for(; p[0] && p[0][0] == '-' && p[0][1]; p++){
+ s = p[0] + 1;
+ if(*s == '-'){
+ p++;
+ break;
+ }
+ while(*s && (s += chartorune(&x, s)))
+ switch(x){
+ case 'r':
+ r->raw = 1;
+ break;
+ default:
+ return p;
+ }
+ }
+ return p;
+}
+
+static void
+chopoff(char *s, char *extra)
+{
+ char *p;
+ int l, ls;
+
+ l = strlen(extra);
+ ls = strlen(s);
+ if(l >= ls)
+ return;
+ p = s + ls - l;
+ if(strcmp(p, extra) == 0)
+ *p = 0;
+}
+
+char*
+trim(char *s)
+{
+ char *p;
+
+ while(*s && (*s == ' ' || *s == '\t'))
+ s++;
+ if(*s == 0)
+ return nil;
+ p = s + strlen(s) - 1;
+ while(*p == ' ' || *p == '\t')
+ p--;
+ p[1] = 0;
+ return s;
+}
+
+int
+doredir(Req *r, char **f, int nf, int mode, int *fd1, int *fd2)
+{
+ int fd;
+
+ if(nf != 1 && nf != 2){
+ eprint("!args\n");
+ return -1;
+ }
+ fd = -1;
+ if(nf == 2){
+ fd = open(f[1], mode);
+ if(mode != OREAD){
+ if(fd == -1)
+ fd = create(f[1], mode, 0660);
+ else
+ seek(fd, 0, 2);
+ }
+ }
+ if(fd1){
+ close(*fd1);
+ *fd1 = fd;
+ }
+ if(fd2){
+ r->fmtrw = fd == -1;
+ close(*fd2);
+ *fd2 = fd;
+ }
+ return fd;
+}
+
+int
+special(char *s, Dev *d, Req *r)
+{
+ char buf[512], path[128], *f[20], sbuf[512], s2[512], *p, *e, *t;
+ uchar *u;
+ int i, j, nf;
+ Atatab *a;
+
+ p = buf;
+ e = buf + sizeof buf;
+ if(!strcmp(s, "close")){
+ r->haverfis = 0;
+ close(d->fd);
+ d->fd = -1;
+ return 0;
+ }
+ if(!strcmp(s, "scttrace")){
+ scttrace = 1;
+ return 0;
+ }
+ if(!strcmp(s, "dev")){
+ if(d->fd == -1){
+ eprint("!bad cmd (device closed)\n");
+ return 0;
+ }
+ if(fd2path(d->fd, path, sizeof path) == -1)
+ sysfatal("fd2path: %r");
+ chopoff(path, "/raw");
+ p = seprint(p, e, "dev\t%s\n", path);
+ p = seprint(p, e, "flags\t");
+ p = pflag(p, e, d);
+ p = seprint(p, e, "lsectsz\t" "%ud ptol %ud\n", d->lsectsz, 1<<d->physshift);
+ p = seprint(p, e, "geometry %llud %ud\n", d->nsect, d->secsize);
+ if(d->c | d->h | d->s)
+ seprint(p, e, "chs\t%d %d %d\n", d->c, d->h, d->s);
+ print("%s", buf);
+ return 0;
+ }
+ if(!strcmp(s, "help")){
+ suggesttab(buf, atatab, nelem(atatab));
+ return 0;
+ }
+ if(!strcmp(s, "probe")){
+ probe();
+ return 0;
+ }
+ if(!strcmp(s, "rfis")){
+ if(r->haverfis == 0){
+ eprint("!no rfis\n");
+ return 0;
+ }
+ p = seprint(p, e, "%.2x\n", r->reply.sdcmd);
+ u = r->reply.fis;
+ for(i = 0; i < 16; i++)
+ p = seprint(p, e, "%.2ux", u[i]);
+ seprint(p, e, "\n");
+ print("%s", buf);
+ return 0;
+ }
+ for(t = s; *t == '<' || *t == '>'; t++)
+ ;
+ if(t != s)
+ snprint(sbuf, sizeof buf, "%.*s %s", (int)(t - s), s, t);
+ else
+ snprint(sbuf, sizeof sbuf, "%s", s);
+ nf = tokenize(sbuf, f, nelem(f));
+ if(!strcmp(f[0], "issuetr")){
+ if(nf == 1)
+ for(i = 0; i < nelem(issuetr); i++)
+ issuetr[i] ^= 1;
+ else{
+ p = s2;
+ e = s2 + sizeof s2;
+ for(i = 1; i < nf - 1; i++)
+ p = seprint(p, e, "%s ", f[i]);
+ p = seprint(p, e, "%s", f[i]);
+ e = s2;
+ for(i = 1; i < nf; i++){
+ j = strtoul(f[i], &p, 0);
+ if(*p == 0 && j < nelem(issuetr))
+ issuetr[i] ^= 1;
+ else if(a = findtab(&e, atatab, nelem(atatab)))
+ issuetr[a->cc & 0xff] ^= 1;
+ }
+ }
+ return 0;
+ }
+ if(!strcmp(f[0], "open")){
+ r->lba = 0;
+ if(nf == 2)
+ opendev(f[1], d);
+ else
+ eprint("!bad args to open\n");
+ return 0;
+ }
+ if(!strcmp(f[0], ">")){
+ doredir(r, f, nf, OWRITE, 0, &r->wfd);
+ return 0;
+ }
+ if(!strcmp(f[0], "<")){
+ doredir(r, f, nf, OREAD, &r->rfd, 0);
+ return 0;
+ }
+ if(!strcmp(f[0], "<>")){
+ doredir(r, f, nf, OWRITE, &r->rfd, &r->wfd);
+ return 0;
+ }
+ return -1;
+}
+
+static char *regtab[] = {
+ "Ftype",
+ "Fflags",
+ "Fcmd",
+ "Ffeat",
+ "Flba0",
+ "Flba8",
+ "Flba16",
+ "Fdev",
+ "Flba24",
+ "Flba32",
+ "Flba40",
+ "Ffeat8",
+ "Fsc",
+ "Fsc8",
+ "Fr",
+ "Fcontrol",
+};
+
+void
+setreg(Req *r, uint reg, uvlong v)
+{
+ uchar *o;
+ int x;
+
+ switch(reg & (Sbase | Pbase)){
+ case 0:
+ r->fisbits |= 1 << reg;
+ r->cmd.fis[reg] = v;
+// print("%s: %.2ux\n", regtab[reg], (uchar)v);
+ break;
+ case Sbase:
+ case Sbase | Pbase:
+ x = reg & ~(Sbase | Ssz);
+ o = r->data + x*2;
+ assert(x < r->count);
+ switch(reg & Ssz){
+ default:
+ print("reg & Ssz %ux\n", reg & Ssz);
+ _assert("bad table");
+ case Sw:
+ pw(o, v);
+ break;
+ case Sdw:
+ pdw(o, v);
+ break;
+ case Sqw:
+ pqw(o, v);
+ break;
+ }
+ break;
+ case Pbase:
+ /* fix me please: this is teh suck */
+ r->fisbits |= 1 << 16;
+ r->cmd.ataproto = v;
+ break;
+ }
+}
+
+int
+setfis0(Req *r, Txtab *t, char *p)
+{
+ char *e;
+ uvlong v;
+
+ v = strtoull(p, &e, 0);
+ setreg(r, t->val, v);
+ return *e != 0;
+}
+
+char**
+setfis(Atatab*, Req *r, char **p)
+{
+ char *s;
+ int i;
+
+loop:
+ if((s = p[0]) == 0)
+ return p;
+ for(i = 0; i < nelem(regtx); i++)
+ if(strcmp(s, regtx[i].name) == 0 && p[1] != nil){
+// print("setfis0 %s %s\n", p[0], p[1]);
+ setfis0(r, regtx + i, p[1]);
+ p += 2;
+ goto loop;
+ }
+ return p;
+}
+
+char*
+rname(char *buf, int n, int r)
+{
+ int i;
+
+ for(i = 0; i < nelem(regtx); i++)
+ if(regtx[i].val == r){
+ snprint(buf, n, "%s", regtx[i].name);
+ return buf;
+ }
+ snprint(buf, n, "%.2ux", r);
+ return buf;
+}
+
+int
+mwcmp(char *a, char ***l)
+{
+ char buf[128], *f[20], **p;
+ int nf, i;
+
+ if(*a == 0)
+ return 0;
+ p = *l;
+ if(p[0] == 0)
+ return -1;
+ snprint(buf, sizeof buf, "%s", a);
+ nf = tokenize(buf, f, nelem(f));
+ for(i = 0; i < nf; i++)
+ if(p[i] == nil || cistrcmp(p[i], f[i]) != 0)
+ return -1;
+ *l = p + i - 1;
+ return 0;
+}
+
+char **dofetab(Fetab*, Req*, char**);
+
+static char hexdig[] = "ABCDEFabcdef0123456789";
+static char hexonly[] = "ABCDEFabcdef";
+static char Enum[] = "expecting number";
+
+int
+fenum(Fetab *, int v, char ***p)
+{
+ char *e, *s, *r;
+ int base;
+
+ if(v >= 0)
+ return v;
+ s = *(*p + 1);
+ e = nil;
+ if(s == nil || *s == 0)
+ e = Enum;
+ else{
+ base = 0;
+ if(strspn(s, hexdig) == strlen(s) &&
+ strpbrk(s, hexonly) != nil)
+ base = 0x10;
+ v = strtoul(s, &r, base);
+ if(*r)
+ e = Enum;
+ }
+ if(e == nil)
+ (*p)++;
+ else
+ print("error: %s [%s]\n", e, s);
+ return v;
+}
+
+char**
+dofetab0(Fetab *t, Req *r, char **p)
+{
+ int i, v;
+ Txtab *tab;
+
+ if(t == nil)
+ return p;
+ tab = t->tab;
+loop:
+ for(i = 0; i < t->ntab; i++)
+ if(mwcmp(tab[i].name, &p) == 0){
+ v = fenum(t, tab[i].val, &p);
+ setreg(r, t->reg, v);
+ if(tab[i].name[0] != 0){
+ p = dofetab(tab[i].fe, r, p + 1);
+ goto loop;
+ }
+ }
+ return p;
+
+}
+
+char**
+dofetab(Fetab *t, Req *r, char **p)
+{
+ for(; t != nil && t->ntab > 0; t++)
+ p = dofetab0(t, r, p);
+ return p;
+}
+
+char**
+dotab(Atatab *a, Req *r, char **p)
+{
+ if(a->tab == nil)
+ return p;
+ return dofetab(a->tab, r, p);
+}
+
+void
+initreq(Req *r)
+{
+ memset(r, 0, sizeof *r);
+// r->wfd = open("/dev/null", OWRITE);
+ r->wfd = dup(1, -1);
+ if(rflag == 0)
+ r->fmtrw = 1;
+ r->rfd = open("/dev/zero", OREAD);
+}
+
+void
+setup(void)
+{
+ int i;
+
+ for(i = 0; i < nelem(atatab); i++)
+ if(atatab[i].cc == 0x2f){
+ sctread = atatab + i;
+ break;
+ }
+ for(; i < nelem(atatab); i++)
+ if(atatab[i].cc == 0x3f){
+ sctissue = atatab + i;
+ break;
+ }
+ for(; i < nelem(atatab); i++)
+ if(atatab[i].cc == 0xa1){
+ idpktcmd = atatab + i;
+ break;
+ }
+ for(; i < nelem(atatab); i++)
+ if(atatab[i].cc == 0xec){
+ idcmd = atatab + i;
+ break;
+ }
+ for(; i < nelem(atatab); i++)
+ if(atatab[i].cc == 0xf000){
+ sigcmd = atatab + i;
+ break;
+ }
+}
+
+typedef struct Htab Htab;
+struct Htab {
+ ulong bit;
+ char *name;
+};
+
+Htab ertab[] = {
+ Eicrc, "icrc",
+ Ewp, "wp",
+ Emc, "mc",
+ Eidnf, "idnf",
+ Emcr, "mcr",
+ Eabrt, "abrt",
+ Enm, "nm",
+ Emed, "med",
+ Eunc, "unc",
+};
+
+Htab sttab[] = {
+ ASbsy, "bsy",
+ ASdrdy, "drdy",
+ ASdf, "df",
+ ASdrq, "drq",
+ ASerr, "err",
+};
+
+static char*
+htabfmt(char *p, char *e, Htab *t, int n, ulong u)
+{
+ char *p0;
+ uint i;
+
+ p0 = p;
+ for(i = 0; i < n; i++)
+ if(u & t[i].bit)
+ p = seprint(p, e, "%s | ", t[i].name);
+ if(p - 3 >= p0)
+ p -= 3;
+ if(p < e)
+ p[0] = 0;
+ return p;
+}
+
+void
+prerror(Req *r)
+{
+ char st[64], er[64];
+ uchar *u;
+
+ u = r->reply.fis;
+ if(r->haverfis == 0 || (u[Fstatus] & ASerr) == 0)
+ return;
+ htabfmt(er, er + sizeof er, ertab, nelem(ertab), u[Frerror]);
+ htabfmt(st, st + sizeof st, sttab, nelem(sttab), u[Fstatus] & ~ASobs);
+ fprint(2, "err %.2ux %.2ux (%s, %s)\n", u[Frerror], u[Fstatus], er, st);
+}
+
+void
+usage(void)
+{
+ eprint("usage: atazz dev\n");
+ eprint(" or -c cmd\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char buf[1024], *p, *f[20], **fp;
+ int nf, cflag, i;
+ Atatab *a;
+ Req r;
+ Dev d;
+
+ cflag = 0;
+ ARGBEGIN{
+ case 'c':
+ cflag = atoi(EARGF(usage()));
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(cflag){
+ for(i = 0; i < nelem(atatab); i++)
+ if(atatab[i].cc == cflag)
+ print("%s\n", atatab[i].name);
+ exits("");
+ }
+
+ setup();
+ fmtinstall(L'π', πfmt);
+ if(argc > 1)
+ usage();
+ initreq(&r);
+ d.fd = -1;
+ if(argc == 1 && opendev(*argv, &d) == -1)
+ sysfatal("opendev: %r");
+ atnotify(catch, 1);
+ for(;;){
+ memset(&r.cmd, 0, sizeof r.cmd);
+ r.fisbits = 0;
+ if(readline("az> ", buf, sizeof buf-1) == nil)
+ break;
+ if((p = trim(buf)) == nil)
+ continue;
+ if(special(buf, &d, &r) == 0)
+ continue;
+ if(d.fd == -1){
+ eprint("!bad cmd (device closed)\n");
+ continue;
+ }
+ a = findtab(&p, atatab, nelem(atatab));
+ if(!a){
+ suggesttab(buf, atatab, nelem(atatab));
+ eprint("!unknown cmd\n");
+ continue;
+ }
+ nf = tokenize(p, f, nelem(f) - 1);
+ f[nf] = 0;
+ fp = stdargs(a, &r, f);
+ fp = setfis(a, &r, fp);
+ if(a->protocol & Psct){
+ r.count = 1 * 512;
+ r.data = realloc(r.data, r.count);
+ memset(r.data, 0, r.count);
+ }
+ fp = dotab(a, &r, fp);
+ switch(a->protocol & Pprotom){
+ default:
+ eprint("!bad proto1 %.2ux\n", a->protocol & Pprotom);
+ continue;
+ case Pnd:
+ fp = ndargs(a, &r, fp);
+ case Preset:
+ case Pdiag:
+ r.count = 0;
+ r.lba = 0;
+ r.nsect = 0;
+ break;
+ case Ppio:
+ case Pdma:
+ case Pdmq:
+ case Ppkt:
+ if(a->flags & Cmd5sc){
+ r.nsect = r.cmd.fis[Fsc];
+ if(r.nsect == 0)
+ r.nsect = 1;
+ r.cmd.fis[Fsc] = r.nsect;
+ r.count = r.nsect * 0x200;
+ }else if((a->protocol & Pssm) == P512){
+ r.lba = 0;
+ r.nsect = 0;
+ r.count = 512;
+ }else{
+ fp = ioargs(a, &r, fp);
+ r.count = d.secsize * r.nsect;
+ }
+ break;
+ }
+ if(fp[0]){
+ eprint("!extra args %π\n", fp);
+ continue;
+ }
+ if(issue(&r, a, &d) == -1){
+ prerror(&r);
+ continue;
+ }
+ if(a->fmt)
+ a->fmt(&r);
+ }
+ exits("");
+}