blob: 3f351053a369d74a85114140d68a959b905961f2 [file] [log] [blame]
/*
* 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>
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) {
sprint(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;
}
sprint(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;
}
}
sprint(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->lk);
if(write(s->rawfd, cmd, ccount) != ccount) {
werrstr("cmd write: %r");
if(dolock)
qunlock(&s->lk);
return -1;
}
switch(io){
case Sread:
n = read(s->rawfd, data, dcount);
if(n < 0 && scsiverbose)
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->lk);
return -1;
}
if(dolock)
qunlock(&s->lk);
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)
{
uchar cmd[6], resp[16];
int status, i;
if(dolock)
qlock(&s->lk);
for(i=0; i<3; i++) {
memset(cmd, 0, sizeof(cmd));
cmd[0] = 0x00; /* unit ready */
if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
if(scsiverbose)
fprint(2, "ur cmd write: %r\n");
goto bad;
}
write(s->rawfd, resp, 0);
if(read(s->rawfd, resp, sizeof(resp)) < 0) {
if(scsiverbose)
fprint(2, "ur resp read: %r\n");
goto bad;
}
resp[sizeof(resp)-1] = '\0';
status = atoi((char*)resp);
if(status == 0 || status == 0x02) {
if(dolock)
qunlock(&s->lk);
return 0;
}
if(scsiverbose)
fprint(2, "target: bad status: %x\n", status);
bad:;
}
if(dolock)
qunlock(&s->lk);
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); SET(code);
qlock(&s->lk);
for(tries=0; tries<2; tries++) {
n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);
if(n >= 0) {
qunlock(&s->lk);
return n;
}
/*
* request sense
*/
memset(req, 0, sizeof(req));
req[0] = 0x03;
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];
code = sense[12];
if(code == 0x17 || code == 0x18) { /* recovered errors */
qunlock(&s->lk);
return dcount;
}
if(code == 0x28 && cmd[0] == 0x43) { /* get info and media changed */
s->nchange++;
s->changetime = time(0);
continue;
}
}
/* drive not ready, or medium not present */
if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) {
s->changetime = 0;
qunlock(&s->lk);
return -1;
}
qunlock(&s->lk);
if(cmd[0] == 0x43 && key == 5 && code == 0x24) /* blank media */
return -1;
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 == 0) */
/* 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) {
free(name);
Error:
close(rawfd);
return nil;
}
free(name);
n = readn(ctlfd, buf, sizeof buf);
close(ctlfd);
if(n <= 0)
goto Error;
if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil)
goto Error;
*p = '\0';
if((p = strdup(buf+8)) == nil)
goto Error;
s = malloc(sizeof(*s));
if(s == nil) {
Error1:
free(p);
goto Error;
}
memset(s, 0, sizeof(*s));
s->rawfd = rawfd;
s->inquire = p;
s->changetime = time(0);
if(scsiready(s) < 0)
goto Error1;
return s;
}