ref: 9861f1a92b373a16939fd6f3c167e5dad9f253d3
dir: /sys/src/cmd/disk/smart/scsi.c/
#include <u.h> #include <libc.h> #include <disk.h> #include <fis.h> #include </sys/src/cmd/scuzz/scsireq.h> #include "smart.h" enum{ Replysz = 16, }; typedef struct Rcmd Rcmd; struct Rcmd{ uchar proto; uchar cdbsz; uchar cdb[16]; }; typedef struct Req Req; struct Req { char haverfis; Rcmd cmd; char sdstat[16]; uchar sense[0x100]; uchar data[0x200]; uint count; }; void turcdb(Req *r) { uchar *cmd; cmd = r->cmd.cdb; r->cmd.cdbsz = 6; r->cmd.proto = Pin; memset(cmd, 0, 6); r->count = 0; } void reqsensecdb(Req *r) { uchar *cmd; cmd = r->cmd.cdb; r->cmd.cdbsz = 6; r->cmd.proto = Pin; memset(cmd, 0, 6); cmd[0] = ScmdRsense; cmd[4] = 128; r->count = 128; } static void sensetrace(uchar *cdb, uchar *u) { char *e; USED(cdb, u); if(1) return; e = scsierror(u[12], u[13]); fprint(2, "sense %.2ux: %.2ux%.2ux%.2ux %s\n", cdb[0], u[2], u[12], u[13], e); } static int issuescsi(Req *r, Sdisk *d) { uchar *u; int ok, rv, n; Req sense; if(write(d->fd, r->cmd.cdb, r->cmd.cdbsz) != r->cmd.cdbsz){ eprint(d, "cdb write error: %r\n"); return -1; } werrstr(""); switch(r->cmd.proto){ default: case Pin: n = read(d->fd, r->data, r->count); ok = n >= 0; r->count = 0; if(ok) r->count = n; break; case Pout: n = write(d->fd, r->data, r->count); ok = n == r->count; break; } rv = 0; memset(r->sdstat, 0, sizeof r->sdstat); if(read(d->fd, r->sdstat, Replysz) < 1){ eprint(d, "status reply read error: %r\n"); return -1; } if(n == -1) rv = -1; /* scsi not supported; don't whine */ else if(rv == 0 && (rv = atoi(r->sdstat)) != 0){ memset(&sense, 0, sizeof sense); reqsensecdb(&sense); if(issuescsi(&sense, d) == 0){ memmove(r->sense, sense.data, sense.count); u = r->sense; rv = u[2]; sensetrace(r->cmd.cdb, u); }else rv = -1; } return ok? rv: -1; } void modesensecdb(Req *r, uchar page, uint n) { uchar *cmd; cmd = r->cmd.cdb; r->cmd.cdbsz = 10; r->cmd.proto = Pin; memset(cmd, 0, 10); cmd[0] = ScmdMsense10; cmd[2] = page; cmd[7] = n>>8; cmd[8] = n; r->count = n; } void modeselectcdb(Req *r, uint n) { uchar *cmd; cmd = r->cmd.cdb; r->cmd.proto = Pout; r->cmd.cdbsz = 10; memset(cmd, 0, 10); cmd[0] = ScmdMselect10; cmd[1] = 0x10; /* assume scsi2 ! */ cmd[7] = n>>8; cmd[8] = n; r->count = n; } int scsiprobe(Sdisk *d) { Req r; memset(&r, 0, sizeof r); turcdb(&r); if(issuescsi(&r, d) == -1) return -1; memset(&r, 0, sizeof r); modesensecdb(&r, 0x1c, sizeof r.data); if(issuescsi(&r, d) != 0 || r.count < 8) return -1; return 0; } enum{ /* mrie bits */ Mnone = 0, Masync = 1, /* obs */ Mattn = 2, /* generate unit attention */ Mcrerror = 3, /* conditionally generate recovered error */ Mrerror = 4, /* unconditionally " */ Mnosense = 5, /* generate no sense */ Mreqonly = 6, /* report only in response to req sense */ /* byte 2 bits */ Perf = 1<<7, /* smart may not cause delays */ Ebf = 1<<5, /* enable bacground functions */ Ewasc = 1<<4, /* enable warnings */ Dexcpt = 1<<3, /* disable smart */ Smarttst = 1<<4, /* generate spurious smart error 5dff */ Logerr = 1<<0, /* enable reporting */ }; int scsienable(Sdisk *d) { Req r; memset(&r, 0, sizeof r); r.data[8 + 0] = 0x1c; r.data[8 + 1] = 0xa; r.data[8 + 2] = Ebf | Ewasc | Logerr; r.data[8 + 3] = Mreqonly; r.data[8 +11] = 1; modeselectcdb(&r, 12 + 8); if(issuescsi(&r, d) != 0) return -1; return 0; } int scsistatus(Sdisk *d, char *s, int l) { char *err; uchar *u; int rv; Req r; memset(&r, 0, sizeof r); reqsensecdb(&r); rv = issuescsi(&r, d); if(rv == 0 && r.count > 12){ u = r.data; if(u[12] + u[13] == 0) err = "normal"; else{ err = scsierror(u[12], u[13]); rv = -1; } if(err == nil) err = "unknown"; snprint(s, l, "%s", err); }else snprint(s, l, "smart error"); return rv; }