1137 lines · 21.9 KB · C
RawDownload
/* * usb/disk - usb mass storage file server * * supports only the scsi command interface, not ata. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include "scsireq.h"#include "usb.h"#include "ums.h" enum{	Qdir = 0,	Qctl,	Qraw,	Qdata,	Qpart,	Qmax = Maxparts,}; Dev *dev;Ums *ums; typedef struct Dirtab Dirtab;struct Dirtab{	char	*name;	int	mode;}; ulong ctlmode = 0664; /* * Partition management (adapted from disk/partfs) */ Part *lookpart(Umsc *lun, char *name){	Part *part, *p;		part = lun->part;	for(p=part; p < &part[Qmax]; p++){		if(p->inuse && strcmp(p->name, name) == 0)			return p;	}	return nil;} Part *freepart(Umsc *lun){	Part *part, *p;		part = lun->part;	for(p=part; p < &part[Qmax]; p++){		if(!p->inuse)			return p;	}	return nil;} intaddpart(Umsc *lun, char *name, vlong start, vlong end, ulong mode){	Part *p; 	if(start < 0 || start > end || end > lun->blocks){		werrstr("bad partition boundaries");		return -1;	}	p = lookpart(lun, name);	if(p != nil){		/* adding identical partition is no-op */		if(p->offset == start && p->length == end - start && p->mode == mode)			return 0;		werrstr("partition name already in use");		return -1;	}	p = freepart(lun);	if(p == nil){		werrstr("no free partition slots");		return -1;	}	p->inuse = 1;	free(p->name);	p->id = p - lun->part;	p->name = estrdup(name);	p->offset = start;	p->length = end - start;	p->mode = mode;	return 0;} intdelpart(Umsc *lun, char *s){	Part *p; 	p = lookpart(lun, s);	if(p == nil || p->id <= Qdata){		werrstr("partition not found");		return -1;	}	p->inuse = 0;	free(p->name);	p->name = nil;	p->vers++;	return 0;} voidfixlength(Umsc *lun, vlong blocks){	Part *part, *p;		part = lun->part;	part[Qdata].length = blocks;	for(p=&part[Qdata+1]; p < &part[Qmax]; p++){		if(p->inuse && p->offset + p->length > blocks){			if(p->offset > blocks){				p->offset =blocks;				p->length = 0;			}else				p->length = blocks - p->offset;		}	}} voidmakeparts(Umsc *lun){	addpart(lun, "/", 0, 0, DMDIR | 0555);	addpart(lun, "ctl", 0, 0, 0664);	addpart(lun, "raw", 0, 0, 0640);	addpart(lun, "data", 0, lun->blocks, 0640);} /* * ctl parsing & formatting (adapted from partfs) */ static char*ctlstring(Umsc *lun){	Part *p, *part;	Fmt fmt;		part = &lun->part[0]; 	fmtstrinit(&fmt);	if(lun->flags & Finqok)		fmtprint(&fmt, "inquiry %s\n", lun->inq);	fmtprint(&fmt, "dev %s\n", dev->dir);	fmtprint(&fmt, "lun %zd\n", lun - &ums->lun[0]);	if(lun->blocks > 0)		fmtprint(&fmt, "geometry %llud %ld\n", lun->blocks, lun->lbsize);	for (p = &part[Qdata+1]; p < &part[Qmax]; p++)		if (p->inuse)			fmtprint(&fmt, "part %s %lld %lld\n",				p->name, p->offset, p->offset + p->length);	return fmtstrflush(&fmt);} static intctlparse(Umsc *lun, char *msg){	vlong start, end;	char *argv[16];	int argc;		argc = tokenize(msg, argv, nelem(argv)); 	if(argc < 1){		werrstr("empty control message");		return -1;	} 	if(strcmp(argv[0], "part") == 0){		if(argc != 4){			werrstr("part takes 3 args");			return -1;		}		start = strtoll(argv[2], 0, 0);		end = strtoll(argv[3], 0, 0);		return addpart(lun, argv[1], start, end, 0640);	}else if(strcmp(argv[0], "delpart") == 0){		if(argc != 2){			werrstr("delpart takes 1 arg");			return -1;		}		return delpart(lun, argv[1]);	}	werrstr("unknown ctl");	return -1;} /* * These are used by scuzz scsireq */int exabyte, force6bytecmds; int diskdebug; static intgetmaxlun(void){	uchar max;	int r; 	max = 0;	r = Rd2h|Rclass|Riface;	if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){		dprint(2, "%s: %s: getmaxlun failed: %r\n", argv0, dev->dir);	}else{		max &= 017;			/* 15 is the max. allowed */		dprint(2, "%s: %s: maxlun %d\n", argv0, dev->dir, max);	}	/* 0e8d:1887 does not respond to followup commands without a delay */	sleep(1);	return max;} static intumsreset(void){	int r; 	r = Rh2d|Rclass|Riface;	if(usbcmd(dev, r, Umsreset, 0, 0, nil, 0) < 0){		fprint(2, "%s: reset: %r\n", argv0);		return -1;	}	sleep(100);	return 0;} static voidumsrecover(void){	if(umsreset() < 0){		/* kill the 9p procs */		postnote(PNGROUP, getpid(), "shutdown");		sysfatal("umsrecover: %r");	}	if(unstall(dev, ums->epin, Ein) < 0)		dprint(2, "%s: unstall epin: %r\n", argv0);	/* do we need this when epin == epout? */	if(unstall(dev, ums->epout, Eout) < 0)		dprint(2, "%s: unstall epout: %r\n", argv0);} static intispow2(uvlong ul){	return (ul & (ul - 1)) == 0;} /* * return smallest power of 2 >= n */static intlog2(int n){	int i; 	for(i = 0; (1 << i) < n; i++)		;	return i;} static intumsready(Umsc *lun){	int i; 	for(i=0; i<3; i++){		if(SRready(lun) != -1)			break; 		if(lun->status != Status_SD)			continue; 		/*		 * logical unit is in process of becoming ready		 * or initializing command required		 */		if(lun->sense[12] == 0x04)		if(lun->sense[13] == 0x02 || lun->sense[13] == 0x01)			break; 		/* medium not present */		if(lun->sense[12] == 0x3A)			return -1;	} 	/* start unit */	if((lun->inquiry[0] & 0x1F) == 0){		SRstart(lun, 1);		sleep(250);	} 	return 0;} static intumscapacity(Umsc *lun){	uchar data[32]; 	lun->blocks = 0;	lun->capacity = 0;	lun->lbsize = 0;	memset(data, 0, sizeof data); 	if(umsready(lun) < 0)		return -1; 	if(SRrcapacity(lun, data) < 0)		return -1; 	lun->blocks = GETBELONG(data);	lun->lbsize = GETBELONG(data+4);	if(lun->blocks == 0xFFFFFFFF){		if(SRrcapacity16(lun, data) < 0){			lun->lbsize = 0;			lun->blocks = 0;			return -1;		}else{			lun->lbsize = GETBELONG(data + 8);			lun->blocks = (uvlong)GETBELONG(data)<<32 |				GETBELONG(data + 4);		}	}	lun->blocks++; /* SRcapacity returns LBA of last block */	lun->capacity = (vlong)lun->blocks * lun->lbsize;	fixlength(lun, lun->blocks);	if(diskdebug)		fprint(2, "%s: logical block size %lud, # blocks %llud\n",			argv0, lun->lbsize, lun->blocks);	return 0;} static intumsinit(void){	uchar i;	Umsc *lun;	int some; 	umsreset();	ums->maxlun = getmaxlun();	ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);	some = 0;	for(i = 0; i <= ums->maxlun; i++){		lun = &ums->lun[i];		lun->ums = ums;		lun->umsc = lun;		lun->lun = i;		lun->flags = Fopen | Fusb | Frw10;		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){			dprint(2, "%s: lun %d inquiry failed\n", argv0, i);			continue;		}		switch(lun->inquiry[0]){		case Devdir:		case Devworm:		/* a little different than the others */		case Devcd:		case Devmo:			break;		default:			fprint(2, "%s: lun %d is not a disk (type %#02x)\n",				argv0, i, lun->inquiry[0]);			continue;		} 		/*		 * we ignore the device type reported by inquiry.		 * Some devices return a wrong value but would still work.		 */		some++;		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);		umscapacity(lun);	}	if(some == 0){		dprint(2, "%s: all luns failed\n", argv0);		return -1;	}	return 0;}  /* * called by SR*() commands provided by scuzz's scsireq */longumsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status){	Cbw cbw;	Csw csw;	int n, nio;	Ums *ums; 	ums = umsc->ums; 	memcpy(cbw.signature, "USBC", 4);	cbw.tag = ++ums->seq;	cbw.datalen = data->count;	cbw.flags = data->write? CbwDataOut: CbwDataIn;	cbw.lun = umsc->lun;	if(cmd->count < 1 || cmd->count > 16)		fprint(2, "%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count); 	cbw.len = cmd->count;	assert(cmd->count <= sizeof(cbw.command));	memcpy(cbw.command, cmd->p, cmd->count);	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); 	werrstr("");		/* we use %r later even for n == 0 */	if(diskdebug){		fprint(2, "%s: cmd: tag %#lx: ", argv0, 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, "%s: cmd: %r\n", argv0);		goto Fail;	} 	/* transfer the data */	nio = data->count;	if(nio != 0){		if(data->write)			n = write(ums->epout->dfd, data->p, nio);		else{			n = read(ums->epin->dfd, data->p, nio);			if (n >= 0 && n < nio)	/* didn't fill data->p? */				memset(data->p + n, 0, nio - n);		}		if(diskdebug)			if(n < 0)				fprint(2, "%s: data: %r\n", argv0);			else				fprint(2, "%s: data: %d bytes (nio: %d)\n", argv0, n, nio);		nio = n;		if(n <= 0){			nio = 0;			if(data->write == 0)				unstall(dev, ums->epin, Ein);		}	} Again:	/* read the transfer's status */	n = read(ums->epin->dfd, &csw, CswLen);	if(n <= 0){		/* n == 0 means "stalled" */		unstall(dev, ums->epin, Ein);		n = read(ums->epin->dfd, &csw, CswLen);	} 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){		dprint(2, "%s: read n=%d: status: %r\n", argv0, n);		goto Fail;	}	if(csw.tag != cbw.tag){		dprint(2, "%s: status tag mismatch: %lux != %lux\n", argv0, csw.tag, cbw.tag);		goto Again;	}	if(csw.status >= CswPhaseErr){		dprint(2, "%s: phase error\n", argv0);		goto Fail;	}	if(csw.dataresidue == 0 || ums->wrongresidues)		csw.dataresidue = data->count - nio;	if(diskdebug){		fprint(2, "%s: status: %2.2ux residue: %ld\n",			argv0, csw.status, csw.dataresidue);		if(cbw.command[0] == ScmdRsense){			fprint(2, "sense data:");			for(n = 0; n < data->count - csw.dataresidue; n++)				fprint(2, " %2.2x", data->p[n]);			fprint(2, "\n");		}	}	switch(csw.status){	case CswOk:		*status = STok;		break;	case CswFailed:		*status = STcheck;

Lines 1–500 of 1137.