diff options
author | google <google@daverabbitz.ath.cx> | 2012-09-20 22:39:48 +1200 |
---|---|---|
committer | google <google@daverabbitz.ath.cx> | 2012-09-20 22:39:48 +1200 |
commit | fa7fb8b66b9ff50029532d09315f03896f2ac4c4 (patch) | |
tree | 262cfe8966f3c07a5ad6ed61fda028f6fa928a36 /sys/src/cmd/atazz/main.c | |
parent | 913afc39c9b4750e630c7f4ff3161a37602b006b (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.c | 1928 |
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(""); +} |