Browse Source

Plan 9 from Bell Labs 2011-03-30

David du Colombier 13 years ago
parent
commit
d60e61a208

+ 3 - 2
sys/man/3/usb

@@ -526,9 +526,10 @@ root of the USB interface
 .IR usbd (4),
 .IR plan9.ini (8)
 .SH BUGS
+USB controllers limit the speed of all their ports
+to that of the slowest device connected to any one of them.
+.PP
 Isochronous input streams are not implemented for OHCI.
 .PP
 Some EHCI controllers drop completion interrupts and so must
 be polled, which hurts throughput.
-.PP
-Not heavily exercised yet.

+ 1 - 4
sys/man/4/usb

@@ -498,7 +498,7 @@ at once, with one action per line.
 The various device drivers are generic USB drivers and
 may work only for certain devices on each class.
 .PP
-ATA storage devices are not supported.
+USB ATA storage devices are not supported.
 .PP
 The Ethernet device works only for certain ASIX-based cards and for CDC devices.
 Both the Ethernet and printer drivers have not
@@ -512,6 +512,3 @@ and
 signals and some of the extra features are unimplemented.
 For Ftdi, only the Sheevaplug and Guruplug have been tried.
 There is support for the EHCI debug port, but it loses bytes.
-.PP
-The entire set of drivers is new and therefore potentially unreliable.
-A list of working devices must be compiled.

+ 153 - 104
sys/src/cmd/usb/disk/disk.c

@@ -1,8 +1,7 @@
 /*
  * usb/disk - usb mass storage file server
- * BUG: supports only the scsi command interface.
- * BUG: This should use /dev/sdfile to
- * use the kernel ether device code.
+ *
+ * supports only the scsi command interface, not ata.
  */
 
 #include <u.h>
@@ -43,10 +42,17 @@ static Dirtab dirtab[] =
  * These are used by scuzz scsireq
  */
 int exabyte, force6bytecmds;
-long maxiosize = MaxIOsize;
 
 int diskdebug;
 
+static void
+ding(void *, char *msg)
+{
+	if(strstr(msg, "alarm") != nil)
+		noted(NCONT);
+	noted(NDFLT);
+}
+
 static int
 getmaxlun(Dev *dev)
 {
@@ -101,6 +107,25 @@ umsfatal(Ums *ums)
 		usbfsdel(&ums->lun[i].fs);
 }
 
+static int
+ispow2(uvlong ul)
+{
+	return (ul & (ul - 1)) == 0;
+}
+
+/*
+ * return smallest power of 2 >= n
+ */
+static int
+log2(int n)
+{
+	int i;
+
+	for(i = 0; (1 << i) < n; i++)
+		;
+	return i;
+}
+
 static int
 umscapacity(Umsc *lun)
 {
@@ -191,7 +216,7 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 {
 	Cbw cbw;
 	Csw csw;
-	int n, nio;
+	int n, nio, left;
 	Ums *ums;
 
 	ums = umsc->ums;
@@ -210,25 +235,31 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
 
 	werrstr("");		/* we use %r later even for n == 0 */
-
 	if(diskdebug){
 		fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
 		for(n = 0; n < cbw.len; n++)
 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
 		fprint(2, " datalen: %ld\n", cbw.datalen);
 	}
+
+	/* issue tunnelled scsi command */
 	if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
 		fprint(2, "disk: cmd: %r\n");
 		goto Fail;
 	}
+
+	/* transfer the data */
 	nio = data->count;
-	if(data->count != 0){
+	if(nio != 0){
 		if(data->write)
-			n = nio = write(ums->epout->dfd, data->p, data->count);
+			n = write(ums->epout->dfd, data->p, nio);
 		else{
-			memset(data->p, data->count, 0);
-			n = nio = read(ums->epin->dfd, data->p, data->count);
+			n = read(ums->epin->dfd, data->p, nio);
+			left = nio - n;
+			if (n >= 0 && left > 0)	/* didn't fill data->p? */
+				memset(data->p + n, 0, left);
 		}
+		nio = n;
 		if(diskdebug)
 			if(n < 0)
 				fprint(2, "disk: data: %r\n");
@@ -238,12 +269,15 @@ umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
 			if(data->write == 0)
 				unstall(ums->dev, ums->epin, Ein);
 	}
+
+	/* read the transfer's status */
 	n = read(ums->epin->dfd, &csw, CswLen);
 	if(n <= 0){
 		/* n == 0 means "stalled" */
 		unstall(ums->dev, ums->epin, Ein);
 		n = read(ums->epin->dfd, &csw, CswLen);
 	}
+
 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
 		dprint(2, "disk: read n=%d: status: %r\n", n);
 		goto Fail;
@@ -377,6 +411,46 @@ dopen(Usbfs *fs, Fid *fid, int)
 	return 0;
 }
 
+/*
+ * check i/o parameters and compute values needed later.
+ * we shift & mask manually to avoid run-time calls to _divv and _modv,
+ * since we don't need general division nor its cost.
+ */
+static int
+setup(Umsc *lun, char *data, int count, vlong offset)
+{
+	long nb, lbsize, lbshift, lbmask;
+	uvlong bno;
+
+	if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
+	    lun->lbsize == 0)
+		return -1;
+	lbsize = lun->lbsize;
+	assert(ispow2(lbsize));
+	lbshift = log2(lbsize);
+	lbmask = lbsize - 1;
+
+	bno = offset >> lbshift;	/* offset / lbsize */
+	nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
+
+	if(bno + nb > lun->blocks)		/* past end of device? */
+		nb = lun->blocks - bno;
+	if(nb * lbsize > Maxiosize)
+		nb = Maxiosize / lbsize;
+	lun->nb = nb;
+	if(bno >= lun->blocks || nb == 0)
+		return 0;
+
+	lun->offset = bno;
+	lun->off = offset & lbmask;		/* offset % lbsize */
+	if(lun->off == 0 && (count & lbmask) == 0)
+		lun->bufp = data;
+	else
+		/* not transferring full, aligned blocks; need intermediary */
+		lun->bufp = lun->buf;
+	return count;
+}
+
 /*
  * Upon SRread/SRwrite errors we assume the medium may have changed,
  * and ask again for the capacity of the media.
@@ -385,9 +459,9 @@ dopen(Usbfs *fs, Fid *fid, int)
 static long
 dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 {
-	long bno, nb, len, off, n;
+	long n;
 	ulong path;
-	char buf[1024], *p;
+	char buf[1024];
 	char *s, *e;
 	Umsc *lun;
 	Ums *ums;
@@ -397,6 +471,7 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 	path = fid->qid.path & ~fs->qid;
 	ums = fs->dev->aux;
 	lun = fs->aux;
+
 	qlock(ums);
 	switch(path){
 	case Qdir:
@@ -408,15 +483,15 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 		if(lun->flags & Finqok)
 			s = seprint(s, e, "inquiry %s ", lun->inq);
 		if(lun->blocks > 0)
-			s = seprint(s, e, "geometry %llud %ld", lun->blocks,
-				lun->lbsize);
+			s = seprint(s, e, "geometry %llud %ld",
+				lun->blocks, lun->lbsize);
 		s = seprint(s, e, "\n");
 		count = usbreadbuf(data, count, offset, buf, s - buf);
 		break;
 	case Qraw:
 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
-			qunlock(ums);
-			return -1;
+			count = -1;
+			break;
 		}
 		switch(lun->phase){
 		case Pcmd:
@@ -424,16 +499,13 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 			werrstr("phase error");
 			return -1;
 		case Pdata:
-			lun->data.p = (uchar*)data;
+			lun->data.p = data;
 			lun->data.count = count;
 			lun->data.write = 0;
 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
 			lun->phase = Pstatus;
-			if(count < 0){
-				lun->lbsize = 0;	/* medium may have changed */
-				qunlock(ums);
-				return -1;
-			}
+			if(count < 0)
+				lun->lbsize = 0;  /* medium may have changed */
 			break;
 		case Pstatus:
 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
@@ -443,36 +515,25 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 		}
 		break;
 	case Qdata:
-		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
-			qunlock(ums);
-			return -1;
-		}
-		bno = offset / lun->lbsize;
-		nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno;
-		if(bno + nb > lun->blocks)
-			nb = lun->blocks - bno;
-		if(bno >= lun->blocks || nb == 0){
-			count = 0;
+		count = setup(lun, data, count, offset);
+		if (count <= 0)
 			break;
-		}
-		if(nb * lun->lbsize > maxiosize)
-			nb = maxiosize / lun->lbsize;
-		p = emallocz(nb * lun->lbsize, 0);	/* could use a static buffer */
-		lun->offset = offset / lun->lbsize;
-		n = SRread(lun, p, nb * lun->lbsize);
+		n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
 		if(n < 0){
-			free(p);
 			lun->lbsize = 0;	/* medium may have changed */
-			qunlock(ums);
-			return -1;
+			count = -1;
+		} else if (lun->bufp == data)
+			count = n;
+		else{
+			/*
+			 * if n == lun->nb*lun->lbsize (as expected),
+			 * just copy count bytes.
+			 */
+			if(lun->off + count > n)
+				count = n - lun->off; /* short read */
+			if(count > 0)
+				memmove(data, lun->bufp + lun->off, count);
 		}
-		len = count;
-		off = offset % lun->lbsize;
-		if(off + len > n)
-			len = n - off;
-		count = len;
-		memmove(data, p + off, len);
-		free(p);
 		break;
 	}
 	qunlock(ums);
@@ -480,33 +541,31 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 }
 
 static long
-dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
+dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 {
-	int bno, nb, len, off;
+	long len, ocount;
 	ulong path;
-	char *p;
+	uvlong bno;
 	Ums *ums;
 	Umsc *lun;
-	char *data;
 
 	ums = fs->dev->aux;
 	lun = fs->aux;
 	path = fid->qid.path & ~fs->qid;
-	data = buf;
+
 	qlock(ums);
 	switch(path){
 	default:
-		qunlock(ums);
 		werrstr(Eperm);
-		return -1;
+		count = -1;
+		break;
 	case Qctl:
-		qunlock(ums);
 		dprint(2, "usb/disk: ctl ignored\n");
-		return count;
+		break;
 	case Qraw:
 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
-			qunlock(ums);
-			return -1;
+			count = -1;
+			break;
 		}
 		switch(lun->phase){
 		case Pcmd:
@@ -522,67 +581,56 @@ dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
 			lun->phase = Pdata;
 			break;
 		case Pdata:
-			lun->data.p = (uchar*)data;
+			lun->data.p = data;
 			lun->data.count = count;
 			lun->data.write = 1;
 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
 			lun->phase = Pstatus;
-			if(count < 0){
-				lun->lbsize = 0;	/* medium may have changed */
-				qunlock(ums);
-				return -1;
-			}
+			if(count < 0)
+				lun->lbsize = 0;  /* medium may have changed */
 			break;
 		case Pstatus:
 			lun->phase = Pcmd;
-			qunlock(ums);
 			werrstr("phase error");
-			return -1;
+			count = -1;
+			break;
 		}
 		break;
 	case Qdata:
-		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
-			qunlock(ums);
-			return -1;
-		}
-		bno = offset / lun->lbsize;
-		nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno;
-		if(bno + nb > lun->blocks)
-			nb = lun->blocks - bno;
-		if(bno >= lun->blocks || nb == 0){
-			count = 0;
+		len = ocount = count;
+		count = setup(lun, data, count, offset);
+		if (count <= 0)
 			break;
-		}
-		if(nb * lun->lbsize > maxiosize)
-			nb = maxiosize / lun->lbsize;
-		p = emallocz(nb * lun->lbsize, 0);
-		off = offset % lun->lbsize;
-		len = count;
-		if(off || (len % lun->lbsize) != 0){
-			lun->offset = offset / lun->lbsize;
-			count = SRread(lun, p, nb * lun->lbsize);
-			if(count < 0){
-				free(p);
-				lun->lbsize = 0;	/* medium may have changed */
-				qunlock(ums);
-				return -1;
+		bno = lun->offset;
+		if (lun->bufp == lun->buf) {
+			count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
+			if(count < 0) {
+				lun->lbsize = 0;  /* medium may have changed */
+				break;
 			}
-			if(off + len > count)
-				len = count - off;
+			/*
+			 * if count == lun->nb*lun->lbsize, as expected, just
+			 * copy len (the original count) bytes of user data.
+			 */
+			if(lun->off + len > count)
+				len = count - lun->off; /* short read */
+			if(len > 0)
+				memmove(lun->bufp + lun->off, data, len);
 		}
-		memmove(p+off, data, len);
-		lun->offset = offset / lun->lbsize;
-		count = SRwrite(lun, p, nb * lun->lbsize);
-		if(count < 0){
-			free(p);
+
+		lun->offset = bno;
+		count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
+		if(count < 0)
 			lun->lbsize = 0;	/* medium may have changed */
-			qunlock(ums);
-			return -1;
+		else{
+			if(lun->off + len > count)
+				count -= lun->off; /* short write */
+			/* never report more bytes written than requested */
+			if(count < 0)
+				count = 0;
+			else if(count > ocount)
+				count = ocount;
 		}
-		if(off+len > count)
-			len = count - off;
-		count = len;
-		free(p);
 		break;
 	}
 	qunlock(ums);
@@ -712,6 +760,7 @@ diskmain(Dev *dev, int argc, char **argv)
 		return usage();
 	}
 
+//	notify(ding);
 	ums = dev->aux = emallocz(sizeof(Ums), 1);
 	ums->maxlun = -1;
 	ums->dev = dev;

+ 14 - 9
sys/src/cmd/usb/disk/scsireq.c

@@ -15,6 +15,7 @@
 
 #include <u.h>
 #include <libc.h>
+#include <fcall.h>
 #include "scsireq.h"
 
 enum {
@@ -208,14 +209,14 @@ SRread(ScsiReq *rp, void *buf, long nbytes)
 	uchar cmd[10];
 	long n;
 
-	if((nbytes % rp->lbsize) || nbytes > maxiosize){
+	if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
 		if(diskdebug)
 			if (nbytes % rp->lbsize)
 				fprint(2, "disk: i/o size %ld %% %ld != 0\n",
 					nbytes, rp->lbsize);
 			else
-				fprint(2, "disk: i/o size %ld > %ld\n",
-					nbytes, maxiosize);
+				fprint(2, "disk: i/o size %ld > %d\n",
+					nbytes, Maxiosize);
 		rp->status = Status_BADARG;
 		return -1;
 	}
@@ -272,14 +273,14 @@ SRwrite(ScsiReq *rp, void *buf, long nbytes)
 	uchar cmd[10];
 	long n;
 
-	if((nbytes % rp->lbsize) || nbytes > maxiosize){
+	if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
 		if(diskdebug)
 			if (nbytes % rp->lbsize)
 				fprint(2, "disk: i/o size %ld %% %ld != 0\n",
 					nbytes, rp->lbsize);
 			else
-				fprint(2, "disk: i/o size %ld > %ld\n",
-					nbytes, maxiosize);
+				fprint(2, "disk: i/o size %ld > %d\n",
+					nbytes, Maxiosize);
 		rp->status = Status_BADARG;
 		return -1;
 	}
@@ -353,8 +354,10 @@ SRseek(ScsiReq *rp, long offset, int type)
 	rp->data.count = 0;
 	rp->data.write = 1;
 	SRrequest(rp);
-	if(rp->status == STok)
-		return rp->offset = offset;
+	if(rp->status == STok) {
+		rp->offset = offset;
+		return offset;
+	}
 	return -1;
 }
 
@@ -568,6 +571,7 @@ request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
 
 	/* read or write actual data */
 	werrstr("");
+//	alarm(5*1000);
 	if(data->write)
 		n = write(fd, data->p, data->count);
 	else {
@@ -577,6 +581,7 @@ request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
 		else if (n < data->count)
 			memset(data->p + n, 0, data->count - n);
 	}
+//	alarm(0);
 	if (n != data->count && n <= 0) {
 		if (debug)
 			fprint(2,
@@ -781,7 +786,7 @@ retry:
 		werrstr("%s", scsierr(rp));
 		return -1;
 	case STbusy:
-		sleep(1000);
+		sleep(1000);		/* TODO: try a shorter sleep? */
 		goto retry;
 	default:
 		if(debug || exabyte)

+ 9 - 8
sys/src/cmd/usb/disk/scsireq.h

@@ -1,18 +1,21 @@
 /*
  * This is /sys/src/cmd/scuzz/scsireq.h
- * changed to add more debug support, to keep
+ * changed to add more debug support, and to keep
  * disk compiling without a scuzz that includes these changes.
  *
- * this file is also included by usb/disk and cdfs
+ * scsireq.h is also included by usb/disk and cdfs.
  */
 typedef struct Umsc Umsc;
 #pragma incomplete Umsc
 
 enum {					/* fundamental constants/defaults */
-	NTargetID	= 8,		/* number of target IDs */
-	CtlrID		= 7,		/* default controller target ID */
 	MaxDirData	= 255,		/* max. direct data returned */
-	LBsize		= 512,		/* default logical-block size */
+	/*
+	 * Because we are accessed via devmnt, we can never get i/o counts
+	 * larger than 8216 (Msgsize and devmnt's offered iounit) - 24
+	 * (IOHDRSZ) = 8K.
+	 */
+	Maxiosize	= 8216 - IOHDRSZ, /* max. I/O transfer size */
 };
 
 typedef struct {
@@ -26,7 +29,7 @@ typedef struct {
 	char	*unit;			/* unit directory */
 	int	lun;
 	ulong	lbsize;
-	ulong	offset;			/* in blocks of lbsize bytes */
+	uvlong	offset;			/* in blocks of lbsize bytes */
 	int	fd;
 	Umsc	*umsc;			/* lun */
 	ScsiPtr	cmd;
@@ -177,8 +180,6 @@ enum {
 #define GETBE24(p)	((ulong)(p)[0]<<16 | (p)[1]<<8 | (p)[2])
 #define PUTBE24(p, ul)	((p)[0] = (ul)>>16, (p)[1] = (ul)>>8, (p)[2] = (ul))
 
-extern long maxiosize;
-
 long	SRready(ScsiReq*);
 long	SRrewind(ScsiReq*);
 long	SRreqsense(ScsiReq*);

+ 11 - 2
sys/src/cmd/usb/disk/ums.h

@@ -26,7 +26,6 @@ enum
 	Umsreset =	0xFF,
 	Getmaxlun =	0xFE,
 
-	MaxIOsize	= 256*1024,	/* max. I/O size */
 //	Maxlun		= 256,
 	Maxlun		= 32,
 
@@ -45,17 +44,27 @@ enum
 	CswPhaseErr	= 2,
 };
 
-/* these are 600 bytes each; ScsiReq is not tiny */
+/*
+ * corresponds to a lun.
+ * these are ~600+Maxiosize bytes each; ScsiReq is not tiny.
+ */
 struct Umsc
 {
 	ScsiReq;
 	uvlong	blocks;
 	vlong	capacity;
+
+	/* from setup */
+	char	*bufp;
+	long	off;		/* offset within a block */
+	long	nb;		/* byte count */
+
 	uchar 	rawcmd[10];
 	uchar	phase;
 	char	*inq;
 	Ums	*ums;
 	Usbfs	fs;
+	char	buf[Maxiosize];
 };
 
 struct Ums

+ 1 - 1
sys/src/cmd/usb/lib/usbfs.h

@@ -4,7 +4,7 @@ typedef struct Fid Fid;
 enum
 {
 	Hdrsize	= 128,		/* plenty of room for headers */
-	Msgsize	= 8 * 1024,
+	Msgsize	= 8216,		/* our preferred iounit (also devmnt's) */
 	Bufsize	= Hdrsize + Msgsize,
 	Namesz = 40,
 	Errmax = 128,

+ 1 - 1
sys/src/cmd/usb/mkfile

@@ -21,7 +21,7 @@ none:VQ:
 
 all clean nuke:VQ:
 	for (i in $DIRS) @{
-		cd $i && mk $target
+		cd $i && echo $i: && mk $target
 	}
 
 install installall safeinstall safeinstallall:V: