diff options
author | wkj <devnull@localhost> | 2004-06-17 01:46:29 +0000 |
---|---|---|
committer | wkj <devnull@localhost> | 2004-06-17 01:46:29 +0000 |
commit | e1dddc053287874e82e2b67f95ccee7d7bc63e22 (patch) | |
tree | 15e76d20c16f5fc500a18a84ad905eadf28ca3ec /src/libdisk/scsi.c | |
parent | 778df25e996f8344a917fc5d3cf1b962ab728ada (diff) | |
download | plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.tar.gz plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.tar.bz2 plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.zip |
Import proto file parser for dump9660.
Diffstat (limited to 'src/libdisk/scsi.c')
-rw-r--r-- | src/libdisk/scsi.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/libdisk/scsi.c b/src/libdisk/scsi.c new file mode 100644 index 00000000..ccab244c --- /dev/null +++ b/src/libdisk/scsi.c @@ -0,0 +1,327 @@ +/* + * 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; +} |