Import proto file parser for dump9660.
diff --git a/src/libdisk/scsi.c b/src/libdisk/scsi.c
new file mode 100644
index 0000000..ccab244
--- /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;
+}