Browse Source

Plan 9 from Bell Labs 2012-11-05

David du Colombier 11 years ago
parent
commit
55a502f6fa

+ 4 - 0
sys/man/4/cdfs

@@ -245,6 +245,10 @@ rm /mnt/cd/wa
 .TP
 .B http://www.t10.org
 optical disc interface standards
+.br
+in particular,
+.I "Multi-Media Commands"
+(MMC)
 .PD
 .SH BUGS
 Fixating a BD-R disc records only the first track in the disc's TOC.

+ 18 - 0
sys/src/9/pc/ahci.h

@@ -199,6 +199,24 @@ typedef struct {
 	ulong	vendor;
 } Aport;
 
+enum {
+	/*
+	 * Aport sstatus bits (actually states):
+	 * 11-8 interface power management
+	 *  7-4 current interface speed (generation #)
+	 *  3-0 device detection
+	 */
+	Intslumber	= 0x600,
+	Intpartpwr	= 0x200,
+	Intactive	= 0x100,
+	Intpm		= 0xf00,
+
+	Devphyoffline	= 4,
+	Devphycomm	= 2,		/* phy communication established */
+	Devpresent	= 1,
+	Devdet		= Devpresent | Devphycomm | Devphyoffline,
+};
+
 /* in host's memory; not memory mapped */
 typedef struct {
 	uchar	*base;

+ 56 - 43
sys/src/9/pc/sdiahci.c

@@ -701,9 +701,9 @@ ahciwakeup(Aport *p)
 	ushort s;
 
 	s = p->sstatus;
-	if((s & 0xF00) != 0x600)
+	if((s & Intpm) != Intslumber && (s & Intpm) != Intpartpwr)
 		return;
-	if((s & 7) != 1){		/* not (device, no phy) */
+	if((s & Devdet) != Devpresent){	/* not (device, no phy) */
 		iprint("ahci: slumbering drive unwakable %#ux\n", s);
 		return;
 	}
@@ -714,24 +714,30 @@ ahciwakeup(Aport *p)
 }
 
 static int
-ahciconfigdrive(Ahba *h, Aportc *c, int mode)
+ahciconfigdrive(Drive *d)
 {
-	Aportm *m;
+	char *name;
+	Ahba *h;
 	Aport *p;
+	Aportm *m;
 
-	p = c->p;
-	m = c->m;
-
+	h = d->ctlr->hba;
+	p = d->portc.p;
+	m = d->portc.m;
 	if(m->list == 0){
 		setupfis(&m->fis);
 		m->list = malign(sizeof *m->list, 1024);
 		m->ctab = malign(sizeof *m->ctab, 128);
 	}
 
-	if(p->sstatus & 3 && h->cap & Hsss){
+	if (d->unit)
+		name = d->unit->name;
+	else
+		name = nil;
+	if(p->sstatus & (Devphycomm|Devpresent) && h->cap & Hsss){
 		/* device connected & staggered spin-up */
-		dprint("ahci: configdrive:  spinning up ... [%#lux]\n",
-			p->sstatus);
+		dprint("ahci: configdrive: %s: spinning up ... [%#lux]\n",
+			name, p->sstatus);
 		p->cmd |= Apod|Asud;
 		asleep(1400);
 	}
@@ -744,11 +750,14 @@ ahciconfigdrive(Ahba *h, Aportc *c, int mode)
 	p->fishi = 0;
 	p->cmd |= Afre|Ast;
 
-	if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */
+	/* drive coming up in slumbering? */
+	if((p->sstatus & Devdet) == Devpresent &&
+	   ((p->sstatus & Intpm) == Intslumber ||
+	    (p->sstatus & Intpm) == Intpartpwr))
 		ahciwakeup(p);
 
-	/* disable power managment sequence from book. */
-	p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet);
+	/* "disable power managment" sequence from book. */
+	p->sctl = (3*Aipm) | (d->mode*Aspd) | (0*Adet);
 	p->cmd &= ~Aalpe;
 
 	p->ie = IEM;
@@ -920,12 +929,12 @@ updatedrive(Drive *d)
 		pr = 0;
 	if(cause & Ifatal){
 		ewake = 1;
-		dprint("ahci: updatedrive: fatal\n");
+		dprint("ahci: updatedrive: %s: fatal\n", name);
 	}
 	if(cause & Adhrs){
 		if(p->task & (1<<5|1)){
-			dprint("ahci: Adhrs cause %#lux serr %#lux task %#lux\n",
-				cause, serr, p->task);
+			dprint("ahci: %s: Adhrs cause %#lux serr %#lux task %#lux\n",
+				name, cause, serr, p->task);
 			d->portm.flag |= Ferror;
 			ewake = 1;
 		}
@@ -939,22 +948,23 @@ updatedrive(Drive *d)
 
 	if(cause & (Aprcs|Aifs)){
 		s0 = d->state;
-		switch(p->sstatus & 7){
+		switch(p->sstatus & Devdet){
 		case 0:				/* no device */
 			d->state = Dmissing;
 			break;
-		case 1:				/* device but no phy comm. */
-			if((p->sstatus & 0xF00) == 0x600)
+		case Devpresent:		/* device but no phy comm. */
+			if((p->sstatus & Intpm) == Intslumber ||
+			   (p->sstatus & Intpm) == Intpartpwr)
 				d->state = Dnew; /* slumbering */
 			else
 				d->state = Derror;
 			break;
-		case 3:				/* device & phy comm. estab. */
+		case Devpresent|Devphycomm:
 			/* power mgnt crap for surprise removal */
 			p->ie |= Aprcs|Apcs;	/* is this required? */
 			d->state = Dreset;
 			break;
-		case 4:				/* phy off-line */
+		case Devphyoffline:
 			d->state = Doffline;
 			break;
 		}
@@ -979,6 +989,8 @@ static void
 pstatus(Drive *d, ulong s)
 {
 	/*
+	 * s is masked with Devdet.
+	 *
 	 * bogus code because the first interrupt is currently dropped.
 	 * likely my fault.  serror may be cleared at the wrong time.
 	 */
@@ -986,19 +998,19 @@ pstatus(Drive *d, ulong s)
 	case 0:			/* no device */
 		d->state = Dmissing;
 		break;
-	case 1:			/* device but no phy. comm. */
+	case Devpresent:	/* device but no phy. comm. */
 		break;
-	case 2:			/* should this be missing?  need testcase. */
+	case Devphycomm:	/* should this be missing?  need testcase. */
 		dprint("ahci: pstatus 2\n");
 		/* fallthrough */
-	case 3:			/* device & phy. comm. */
+	case Devpresent|Devphycomm:
 		d->wait = 0;
 		d->state = Dnew;
 		break;
-	case 4:			/* offline */
+	case Devphyoffline:
 		d->state = Doffline;
 		break;
-	case 6:			/* does this make sense? */
+	case Devphyoffline|Devphycomm:	/* does this make sense? */
 		d->state = Dnew;
 		break;
 	}
@@ -1007,10 +1019,10 @@ pstatus(Drive *d, ulong s)
 static int
 configdrive(Drive *d)
 {
-	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
+	if(ahciconfigdrive(d) == -1)
 		return -1;
 	ilock(d);
-	pstatus(d, d->port->sstatus & 7);
+	pstatus(d, d->port->sstatus & Devdet);
 	iunlock(d);
 	return 0;
 }
@@ -1023,7 +1035,7 @@ resetdisk(Drive *d)
 
 	p = d->port;
 	det = p->sctl & 7;
-	stat = p->sstatus & 7;
+	stat = p->sstatus & Devdet;
 	state = (p->cmd>>28) & 0xf;
 	dprint("ahci: resetdisk: icc %#ux  det %d sdet %d\n", state, det, stat);
 
@@ -1033,7 +1045,8 @@ resetdisk(Drive *d)
 		d->portm.flag |= Ferror;
 	clearci(p);			/* satisfy sleep condition. */
 	wakeup(&d->portm);
-	if(stat != 3){		/* device absent or phy not communicating? */
+	if(stat != (Devpresent|Devphycomm)){
+		/* device absent or phy not communicating */
 		d->state = Dportreset;
 		iunlock(d);
 		return;
@@ -1053,7 +1066,7 @@ resetdisk(Drive *d)
 
 		configdrive(d);
 	}
-	dprint("ahci: resetdisk: %s → %s\n",
+	dprint("ahci: %s: resetdisk: %s → %s\n", (d->unit? d->unit->name: nil),
 		diskstates[state], diskstates[d->state]);
 	qunlock(&d->portm);
 }
@@ -1180,7 +1193,7 @@ checkdrive(Drive *d, int i)
 	if(s)
 		d->lastseen = MACHP(0)->ticks;
 	if(s != olds[i]){
-		dprint("%s: status: %04#ux -> %04#ux: %s\n",
+		dprint("%s: status: %06#ux -> %06#ux: %s\n",
 			name, olds[i], s, diskstates[d->state]);
 		olds[i] = s;
 		d->wait = 0;
@@ -1192,16 +1205,16 @@ checkdrive(Drive *d, int i)
 		break;
 	case Dmissing:
 	case Dnew:
-		switch(s & 0x107){
-		case 1:		/* no device (pm), device but no phy. comm. */
+		switch(s & (Intactive | Devdet)){
+		case Devpresent:  /* no device (pm), device but no phy. comm. */
 			ahciwakeup(d->port);
 			/* fall through */
-		case 0:		/* no device */
+		case 0:			/* no device */
 			break;
 		default:
-			dprint("%s: unknown status %04#ux\n", name, s);
+			dprint("%s: unknown status %06#ux\n", name, s);
 			/* fall through */
-		case 0x100:		/* active, no device */
+		case Intactive:		/* active, no device */
 			if(++d->wait&Mphywait)
 				break;
 reset:
@@ -1217,9 +1230,9 @@ reset:
 			resetdisk(d);
 			ilock(d);
 			break;
-		case 0x103:		/* active, device, phy. comm. */
+		case Intactive|Devphycomm|Devpresent:
 			if((++d->wait&Midwait) == 0){
-				dprint("%s: slow reset %04#ux task=%#lux; %d\n",
+				dprint("%s: slow reset %06#ux task=%#lux; %d\n",
 					name, s, d->port->task, d->wait);
 				goto reset;
 			}
@@ -1239,7 +1252,7 @@ reset:
 		/* fallthrough */
 	case Derror:
 	case Dreset:
-		dprint("%s: reset [%s]: mode %d; status %04#ux\n",
+		dprint("%s: reset [%s]: mode %d; status %06#ux\n",
 			name, diskstates[d->state], d->mode, s);
 		iunlock(d);
 		resetdisk(d);
@@ -1250,7 +1263,7 @@ portreset:
 		if(d->wait++ & 0xff && (s & 0x100) == 0)
 			break;
 		/* device is active */
-		dprint("%s: portreset [%s]: mode %d; status %04#ux\n",
+		dprint("%s: portreset [%s]: mode %d; status %06#ux\n",
 			name, diskstates[d->state], d->mode, s);
 		d->portm.flag |= Ferror;
 		clearci(d->port);
@@ -1623,7 +1636,7 @@ iariopkt(SDreq *r, Drive *d)
 	name = d->unit->name;
 	p = d->port;
 
-	aprint("ahci: iariopkt: %02#ux %02#ux %c %d %p\n",
+	aprint("ahci: iariopkt: %04#ux %04#ux %c %d %p\n",
 		cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
 	if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
 		return sdmodesense(r, cmd, d->info, d->infosz);
@@ -2074,7 +2087,7 @@ print("iarctl: nil u->dev->ctlr\n");
 		p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
 	serrstr(o->serror, buf, buf + sizeof buf - 1);
 	p = seprint(p, e, "reg\ttask %#lux cmd %#lux serr %#lux %s ci %#lux "
-		"is %#lux; sig %#lux sstatus %04#lux\n",
+		"is %#lux; sig %#lux sstatus %06#lux\n",
 		o->task, o->cmd, o->serror, buf,
 		o->ci, o->isr, o->sig, o->sstatus);
 	if(d->unit == nil)

+ 1 - 0
sys/src/cmd/cdfs/mkfile

@@ -6,6 +6,7 @@ OFILES=\
 	buf.$O\
 	main.$O\
 	mmc.$O\
+	scsi.$O\
 
 HFILES=\
 	dat.h\

+ 21 - 9
sys/src/cmd/cdfs/mmc.c

@@ -70,6 +70,7 @@ enum {
 
 Intfeat intfeats[] = {
 //	0x21,		"incr. streaming writable",
+//	0x25,		"write once",
 	Featdfctmgmt,	"hw defect mgmt.",
 	Featedfctrpt,	"enhanced defect reporting",
 	0x38,		"pseudo-overwrite",
@@ -1641,29 +1642,41 @@ static long
 mmcxwrite(Otrack *o, void *v, long nblk)
 {
 	int r;
+	long bs;
 	uchar cmd[10];
+	Drive *drive;
 	Mmcaux *aux;
 
 	assert(o->omode == OWRITE);
-	aux = o->drive->aux;
-	if (aux->mmcnwa == -1 && scsiready(o->drive) < 0) {
+	bs = o->track->bs;
+	drive = o->drive;
+	aux = drive->aux;
+	if (aux->mmcnwa == -1 && scsiready(drive) < 0) {
 		werrstr("device not ready to write");
 		return -1;
 	}
 	if (aux->mmcnwa == -1 ||
-	    o->drive->end != 0 && aux->mmcnwa + nblk > o->drive->end) {
+	    drive->end != 0 && aux->mmcnwa + nblk > drive->end) {
 		werrstr("writing past last block");
 		return -1;
 	}
 	if (nblk <= 0)
 		fprint(2, "mmcxwrite: nblk %ld <= 0\n", nblk);
-	aux->ntotby += nblk*o->track->bs;
+	aux->ntotby += nblk * bs;
 	aux->ntotbk += nblk;
 
-	while (scsiready(o->drive) < 0)		/* paranoia */
+	while (scsiready(drive) < 0)		/* paranoia */
 		sleep(0);
 
-	initcdb(cmd, sizeof cmd, ScmdExtwritever);
+	/*
+	 * "write and verify" (ScmdExtwritever) only works on write-once media
+	 * and not on CDs (mmc-6 §6.48.1).
+	 */
+	if (drive->mmctype != Mmccd &&
+	    drive->recordable == Yes && drive->erasable == No)
+		initcdb(cmd, sizeof cmd, ScmdExtwritever);
+	else
+		initcdb(cmd, sizeof cmd, ScmdExtwrite);	/* write (10) */
 	cmd[2] = aux->mmcnwa>>24;
 	cmd[3] = aux->mmcnwa>>16;
 	cmd[4] = aux->mmcnwa>>8;
@@ -1680,12 +1693,11 @@ mmcxwrite(Otrack *o, void *v, long nblk)
 	 * but explicit attempts to rewrite a given sector on write-once
 	 * media are guaranteed to fail.
 	 */
-	r = scsi(o->drive, cmd, sizeof(cmd), v, nblk*o->track->bs, Swrite);
+	r = scsi(drive, cmd, sizeof(cmd), v, nblk * bs, Swrite);
 	if (r < 0)
 		fprint(2, "%s: write+verify error at blk offset %,ld = "
 			"offset %,lld / bs %ld: %r\n",
-			argv0, aux->mmcnwa, (vlong)aux->mmcnwa * o->track->bs,
-			o->track->bs);
+			argv0, aux->mmcnwa, (vlong)aux->mmcnwa * bs, bs);
 	else
 		aux->mmcnwa += nblk;
 	return r;

+ 380 - 0
sys/src/cmd/cdfs/scsi.c

@@ -0,0 +1,380 @@
+/*
+ * 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);
+}

+ 3 - 3
sys/src/cmd/unix/u9fs/random.c

@@ -35,17 +35,17 @@ randombytes(uchar *r, uint nr)
 
 	if(!seeded){
 		seeded=1;
-		srand48(getseed());
+		srandom(getseed());
 	}
 	for(i=0; i+4<=nr; i+=4,r+=4){
-		l = (ulong)mrand48();
+		l = (ulong)random();
 		r[0] = l;
 		r[1] = l>>8;
 		r[2] = l>>16;
 		r[3] = l>>24;
 	}
 	if(i<nr){
-		l = (ulong)mrand48();
+		l = (ulong)random();
 		switch(nr-i){
 		case 3:
 			r[2] = l>>16;