/*
* Now thread-safe.
*
* The codeqlock guarantees that once codes != nil, that pointer will never
* change nor become invalid.
*
* The QLock in the Scsi structure moderates access to the raw device.
* We should probably export some of the already-locked routines, but
* there hasn't been a need.
*/
#include <u.h>
#include <libc.h>
#include <disk.h>
enum {
/* commands */
Testrdy = 0x00,
Reqsense = 0x03,
Write10 = 0x2a,
Writever10 = 0x2e,
Readtoc = 0x43,
/* sense[2] (key) sense codes */
Sensenone = 0,
Sensenotrdy = 2,
Sensebadreq = 5,
/* sense[12] (asc) sense codes */
Lunnotrdy = 0x04,
Recovnoecc = 0x17,
Recovecc = 0x18,
Badcdb = 0x24,
Newmedium = 0x28,
Nomedium = 0x3a,
};
int scsiverbose;
#define codefile "/sys/lib/scsicodes"
static char *codes;
static QLock codeqlock;
static void
getcodes(void)
{
Dir *d;
int n, fd;
if(codes != nil)
return;
qlock(&codeqlock);
if(codes != nil) {
qunlock(&codeqlock);
return;
}
if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) {
qunlock(&codeqlock);
return;
}
codes = malloc(1+d->length+1);
if(codes == nil) {
close(fd);
qunlock(&codeqlock);
free(d);
return;
}
codes[0] = '\n'; /* for searches */
n = readn(fd, codes+1, d->length);
close(fd);
free(d);
if(n < 0) {
free(codes);
codes = nil;
qunlock(&codeqlock);
return;
}
codes[n] = '\0';
qunlock(&codeqlock);
}
char*
scsierror(int asc, int ascq)
{
char *p, *q;
static char search[32];
static char buf[128];
getcodes();
if(codes) {
snprint(search, sizeof search, "\n%.2ux%.2ux ", asc, ascq);
if(p = strstr(codes, search)) {
p += 6;
if((q = strchr(p, '\n')) == nil)
q = p+strlen(p);
snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);
return buf;
}
snprint(search, sizeof search, "\n%.2ux00", asc);
if(p = strstr(codes, search)) {
p += 6;
if((q = strchr(p, '\n')) == nil)
q = p+strlen(p);
snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);
return buf;
}
}
snprint(buf, sizeof buf, "scsi #%.2ux %.2ux", asc, ascq);
return buf;
}
static int
_scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock)
{
uchar resp[16];
int n;
long status;
if(dolock)
qlock(s);
if(write(s->rawfd, cmd, ccount) != ccount) {
werrstr("cmd write: %r");
if(dolock)
qunlock(s);
return -1;
}
switch(io){
case Sread:
n = read(s->rawfd, data, dcount);
/* read toc errors are frequent and not very interesting */
if(n < 0 && (scsiverbose == 1 ||
scsiverbose == 2 && cmd[0] != Readtoc))
fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);
break;
case Swrite:
n = write(s->rawfd, data, dcount);
if(n != dcount && scsiverbose)
fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);
break;
default:
case Snone:
n = write(s->rawfd, resp, 0);
if(n != 0 && scsiverbose)
fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);
break;
}
memset(resp, 0, sizeof(resp));
if(read(s->rawfd, resp, sizeof(resp)) < 0) {
werrstr("resp read: %r\n");
if(dolock)
qunlock(s);
return -1;
}
if(dolock)
qunlock(s);
resp[sizeof(resp)-1] = '\0';
status = atoi((char*)resp);
if(status == 0)
return n;
werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);
return -1;
}
int
scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io)
{
return _scsicmd(s, cmd, ccount, data, dcount, io, 1);
}
static int
_scsiready(Scsi *s, int dolock)
{
char err[ERRMAX];
uchar cmd[6], resp[16];
int status, i;
if(dolock)
qlock(s);
werrstr("");
for(i=0; i<3; i++) {
memset(cmd, 0, sizeof(cmd));
cmd[0] = Testrdy; /* unit ready */
if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
if(scsiverbose)
fprint(2, "ur cmd write: %r\n");
werrstr("short unit-ready raw write");
continue;
}
write(s->rawfd, resp, 0);
if(read(s->rawfd, resp, sizeof(resp)) < 0) {
if(scsiverbose)
fprint(2, "ur resp read: %r\n");
continue;
}
resp[sizeof(resp)-1] = '\0';
status = atoi((char*)resp);
if(status == 0 || status == 0x02) {
if(dolock)
qunlock(s);
return 0;
}
if(scsiverbose)
fprint(2, "target: bad status: %x\n", status);
}
rerrstr(err, sizeof err);
if(err[0] == '\0')
werrstr("unit did not become ready");
if(dolock)
qunlock(s);
return -1;
}
int
scsiready(Scsi *s)
{
return _scsiready(s, 1);
}
int
scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io)
{
uchar req[6], sense[255], *data;
int tries, code, key, n;
char *p;
data = v;
SET(key, code);
qlock(s);
for(tries=0; tries<2; tries++) {
n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);
if(n >= 0) {
qunlock(s);
return n;
}
/*
* request sense
*/
memset(req, 0, sizeof(req));
req[0] = Reqsense;
req[4] = sizeof(sense);
memset(sense, 0xFF, sizeof(sense));
if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14)
if(scsiverbose)
fprint(2, "reqsense scsicmd %d: %r\n", n);
if(_scsiready(s, 0) < 0)
if(scsiverbose)
fprint(2, "unit not ready\n");
key = sense[2] & 0xf;
code = sense[12]; /* asc */
if(code == Recovnoecc || code == Recovecc) { /* recovered errors */
qunlock(s);
return dcount;
}
/* retry various odd cases */
if(code == Newmedium && cmd[0] == Readtoc) {
/* read toc and media changed */
s->nchange++;
s->changetime = time(0);
} else if((cmd[0] == Write10 || cmd[0] == Writever10) &&
key == Sensenotrdy &&
code == Lunnotrdy && sense[13] == 0x08) {
/* long write in progress, per mmc-6 */
tries = 0;
sleep(1);
} else if(cmd[0] == Write10 || cmd[0] == Writever10)
break; /* don't retry worm writes */
}
/* drive not ready, or medium not present */
if(cmd[0] == Readtoc && key == Sensenotrdy &&
(code == Nomedium || code == Lunnotrdy)) {
s->changetime = 0;
qunlock(s);
return -1;
}
qunlock(s);
if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb)
return -1; /* blank media */
p = scsierror(code, sense[13]);
werrstr("cmd #%.2ux: %s", cmd[0], p);
if(scsiverbose)
fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n",
cmd[0], key, code, sense[13], p);
// if(key == Sensenone)
// return dcount;
return -1;
}
Scsi*
openscsi(char *dev)
{
Scsi *s;
int rawfd, ctlfd, l, n;
char *name, *p, buf[512];
l = strlen(dev)+1+3+1;
name = malloc(l);
if(name == nil)
return nil;
snprint(name, l, "%s/raw", dev);
if((rawfd = open(name, ORDWR)) < 0) {
free(name);
return nil;
}
snprint(name, l, "%s/ctl", dev);
if((ctlfd = open(name, ORDWR)) < 0) {
Error:
free(name);
close(rawfd);
return nil;
}
n = readn(ctlfd, buf, sizeof buf);
close(ctlfd);
if(n <= 0) {
if(n == 0)
werrstr("eof on %s", name);
goto Error;
}
if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) {
werrstr("inquiry mal-formatted in %s", name);
goto Error;
}
*p = '\0';
free(name);
name = nil;
if((p = strdup(buf+8)) == nil)
goto Error;
s = mallocz(sizeof(*s), 1);
if(s == nil) {
Error1:
free(p);
goto Error;
}
s->rawfd = rawfd;
s->inquire = p;
s->changetime = time(0);
if(scsiready(s) < 0)
goto Error1;
return s;
}
void
closescsi(Scsi *s)
{
close(s->rawfd);
free(s->inquire);
free(s);
}
|