aboutsummaryrefslogtreecommitdiff
path: root/src/libdisk/scsi.c
diff options
context:
space:
mode:
authorwkj <devnull@localhost>2004-06-17 01:46:29 +0000
committerwkj <devnull@localhost>2004-06-17 01:46:29 +0000
commite1dddc053287874e82e2b67f95ccee7d7bc63e22 (patch)
tree15e76d20c16f5fc500a18a84ad905eadf28ca3ec /src/libdisk/scsi.c
parent778df25e996f8344a917fc5d3cf1b962ab728ada (diff)
downloadplan9port-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.c327
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;
+}