xref: /plan9/sys/src/cmd/usb/disk/disk.c (revision 14dd39c1d685049bb1c596d88db256ffe72f42bd)
10641ea09SDavid du Colombier /*
20641ea09SDavid du Colombier  * usb/disk - usb mass storage file server
3906943f9SDavid du Colombier  * BUG: supports only the scsi command interface.
4906943f9SDavid du Colombier  * BUG: This should use /dev/sdfile to
5906943f9SDavid du Colombier  * use the kernel ether device code.
60641ea09SDavid du Colombier  */
7906943f9SDavid du Colombier 
80641ea09SDavid du Colombier #include <u.h>
90641ea09SDavid du Colombier #include <libc.h>
100641ea09SDavid du Colombier #include <ctype.h>
110641ea09SDavid du Colombier #include <fcall.h>
120641ea09SDavid du Colombier #include <thread.h>
130641ea09SDavid du Colombier #include "scsireq.h"
140641ea09SDavid du Colombier #include "usb.h"
15906943f9SDavid du Colombier #include "usbfs.h"
16906943f9SDavid du Colombier #include "ums.h"
170641ea09SDavid du Colombier 
18906943f9SDavid du Colombier enum
19906943f9SDavid du Colombier {
200641ea09SDavid du Colombier 	Qdir = 0,
210641ea09SDavid du Colombier 	Qctl,
220641ea09SDavid du Colombier 	Qraw,
230641ea09SDavid du Colombier 	Qdata,
24906943f9SDavid du Colombier 	Qmax,
250641ea09SDavid du Colombier };
260641ea09SDavid du Colombier 
270641ea09SDavid du Colombier typedef struct Dirtab Dirtab;
28906943f9SDavid du Colombier struct Dirtab
29906943f9SDavid du Colombier {
300641ea09SDavid du Colombier 	char	*name;
310641ea09SDavid du Colombier 	int	mode;
320641ea09SDavid du Colombier };
33906943f9SDavid du Colombier 
34906943f9SDavid du Colombier static Dirtab dirtab[] =
35906943f9SDavid du Colombier {
36906943f9SDavid du Colombier 	[Qdir]	"/",	DMDIR|0555,
37906943f9SDavid du Colombier 	[Qctl]	"ctl",	0444,
38906943f9SDavid du Colombier 	[Qraw]	"raw",	0640,
39906943f9SDavid du Colombier 	[Qdata]	"data",	0640,
400641ea09SDavid du Colombier };
410641ea09SDavid du Colombier 
420641ea09SDavid du Colombier /*
43906943f9SDavid du Colombier  * These are used by scuzz scsireq
440641ea09SDavid du Colombier  */
45906943f9SDavid du Colombier int exabyte, force6bytecmds;
46906943f9SDavid du Colombier long maxiosize = MaxIOsize;
470641ea09SDavid du Colombier 
48906943f9SDavid du Colombier static int diskdebug;
490641ea09SDavid du Colombier 
50906943f9SDavid du Colombier static int
51906943f9SDavid du Colombier getmaxlun(Dev *dev)
520641ea09SDavid du Colombier {
530641ea09SDavid du Colombier 	uchar max;
54906943f9SDavid du Colombier 	int r;
550641ea09SDavid du Colombier 
566a5dc222SDavid du Colombier 	max = 0;
57906943f9SDavid du Colombier 	r = Rd2h|Rclass|Riface;
58906943f9SDavid du Colombier 	if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
59906943f9SDavid du Colombier 		dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
60*14dd39c1SDavid du Colombier 	}else{
61*14dd39c1SDavid du Colombier 		max &= 017;			/* 15 is the max. allowed */
62906943f9SDavid du Colombier 		dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
63*14dd39c1SDavid du Colombier 	}
64906943f9SDavid du Colombier 	return max;
650641ea09SDavid du Colombier }
660641ea09SDavid du Colombier 
67906943f9SDavid du Colombier static int
68906943f9SDavid du Colombier umsreset(Ums *ums)
690641ea09SDavid du Colombier {
70906943f9SDavid du Colombier 	int r;
710641ea09SDavid du Colombier 
72906943f9SDavid du Colombier 	r = Rh2d|Rclass|Riface;
73906943f9SDavid du Colombier 	if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
74906943f9SDavid du Colombier 		fprint(2, "disk: reset: %r\n");
750641ea09SDavid du Colombier 		return -1;
760641ea09SDavid du Colombier 	}
77906943f9SDavid du Colombier 	return 0;
780641ea09SDavid du Colombier }
790641ea09SDavid du Colombier 
80906943f9SDavid du Colombier static int
81906943f9SDavid du Colombier umsrecover(Ums *ums)
82906943f9SDavid du Colombier {
83906943f9SDavid du Colombier 	if(umsreset(ums) < 0)
840641ea09SDavid du Colombier 		return -1;
85906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epin, Ein) < 0)
86906943f9SDavid du Colombier 		dprint(2, "disk: unstall epin: %r\n");
87906943f9SDavid du Colombier 
88906943f9SDavid du Colombier 	/* do we need this when epin == epout? */
89906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epout, Eout) < 0)
90906943f9SDavid du Colombier 		dprint(2, "disk: unstall epout: %r\n");
91906943f9SDavid du Colombier 	return 0;
92906943f9SDavid du Colombier }
93906943f9SDavid du Colombier 
94906943f9SDavid du Colombier static void
95906943f9SDavid du Colombier umsfatal(Ums *ums)
96906943f9SDavid du Colombier {
97906943f9SDavid du Colombier 	int i;
98906943f9SDavid du Colombier 
99906943f9SDavid du Colombier 	devctl(ums->dev, "detach");
100906943f9SDavid du Colombier 	for(i = 0; i < ums->maxlun; i++)
101906943f9SDavid du Colombier 		usbfsdel(&ums->lun[i].fs);
102906943f9SDavid du Colombier }
103906943f9SDavid du Colombier 
104906943f9SDavid du Colombier static int
105906943f9SDavid du Colombier umscapacity(Umsc *lun)
106906943f9SDavid du Colombier {
107906943f9SDavid du Colombier 	uchar data[32];
108906943f9SDavid du Colombier 
1090641ea09SDavid du Colombier 	lun->blocks = 0;
1100641ea09SDavid du Colombier 	lun->capacity = 0;
1110641ea09SDavid du Colombier 	lun->lbsize = 0;
112906943f9SDavid du Colombier 	if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data)  < 0)
113906943f9SDavid du Colombier 		return -1;
114906943f9SDavid du Colombier 	lun->blocks = GETBELONG(data);
115906943f9SDavid du Colombier 	lun->lbsize = GETBELONG(data+4);
116906943f9SDavid du Colombier 	if(lun->blocks == 0xFFFFFFFF){
117906943f9SDavid du Colombier 		if(SRrcapacity16(lun, data) < 0){
118906943f9SDavid du Colombier 			lun->lbsize = 0;
119906943f9SDavid du Colombier 			lun->blocks = 0;
120906943f9SDavid du Colombier 			return -1;
1210641ea09SDavid du Colombier 		}else{
122906943f9SDavid du Colombier 			lun->lbsize = GETBELONG(data + 8);
123906943f9SDavid du Colombier 			lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
124906943f9SDavid du Colombier 		}
125906943f9SDavid du Colombier 	}
1260641ea09SDavid du Colombier 	lun->blocks++; /* SRcapacity returns LBA of last block */
1270641ea09SDavid du Colombier 	lun->capacity = (vlong)lun->blocks * lun->lbsize;
128906943f9SDavid du Colombier 	return 0;
1290641ea09SDavid du Colombier }
130906943f9SDavid du Colombier 
131906943f9SDavid du Colombier static int
132906943f9SDavid du Colombier umsinit(Ums *ums)
133906943f9SDavid du Colombier {
134906943f9SDavid du Colombier 	uchar i;
135906943f9SDavid du Colombier 	Umsc *lun;
136906943f9SDavid du Colombier 	int some;
137906943f9SDavid du Colombier 
138906943f9SDavid du Colombier 	umsreset(ums);
139906943f9SDavid du Colombier 	ums->maxlun = getmaxlun(ums->dev);
140906943f9SDavid du Colombier 	ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
141906943f9SDavid du Colombier 	some = 0;
142906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
143906943f9SDavid du Colombier 		lun = &ums->lun[i];
144906943f9SDavid du Colombier 		lun->ums = ums;
145906943f9SDavid du Colombier 		lun->umsc = lun;
146906943f9SDavid du Colombier 		lun->lun = i;
147906943f9SDavid du Colombier 		lun->flags = Fopen | Fusb | Frw10;
148906943f9SDavid du Colombier 		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0)
149906943f9SDavid du Colombier 			continue;
150906943f9SDavid du Colombier 		if(lun->inquiry[0] != 0x00){
151906943f9SDavid du Colombier 			/* not a disk */
152906943f9SDavid du Colombier 			fprint(2, "%s: lun %d is not a disk (type %#02x)\n",
153906943f9SDavid du Colombier 				argv0, i, lun->inquiry[0]);
154906943f9SDavid du Colombier 			continue;
155906943f9SDavid du Colombier 		}
156906943f9SDavid du Colombier 		SRstart(lun, 1);
157906943f9SDavid du Colombier 		/*
158906943f9SDavid du Colombier 		 * we ignore the device type reported by inquiry.
159906943f9SDavid du Colombier 		 * Some devices return a wrong value but would still work.
160906943f9SDavid du Colombier 		 */
161906943f9SDavid du Colombier 		some++;
162906943f9SDavid du Colombier 		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
163906943f9SDavid du Colombier 		umscapacity(lun);
164906943f9SDavid du Colombier 	}
165906943f9SDavid du Colombier 	if(some == 0){
166906943f9SDavid du Colombier 		devctl(ums->dev, "detach");
167906943f9SDavid du Colombier 		return -1;
1680641ea09SDavid du Colombier 	}
1690641ea09SDavid du Colombier 	return 0;
1700641ea09SDavid du Colombier }
1710641ea09SDavid du Colombier 
1720641ea09SDavid du Colombier 
173906943f9SDavid du Colombier /*
174906943f9SDavid du Colombier  * called by SR*() commands provided by scuzz's scsireq
175906943f9SDavid du Colombier  */
1760641ea09SDavid du Colombier long
1770641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
1780641ea09SDavid du Colombier {
1790641ea09SDavid du Colombier 	Cbw cbw;
1800641ea09SDavid du Colombier 	Csw csw;
1810641ea09SDavid du Colombier 	int n;
182906943f9SDavid du Colombier 	Ums *ums;
183906943f9SDavid du Colombier 
184906943f9SDavid du Colombier 	ums = umsc->ums;
1850641ea09SDavid du Colombier 
1860641ea09SDavid du Colombier 	memcpy(cbw.signature, "USBC", 4);
187906943f9SDavid du Colombier 	cbw.tag = ++ums->seq;
1880641ea09SDavid du Colombier 	cbw.datalen = data->count;
1890641ea09SDavid du Colombier 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
1900641ea09SDavid du Colombier 	cbw.lun = umsc->lun;
191906943f9SDavid du Colombier 	if(cmd->count < 1 || cmd->count > 16)
192906943f9SDavid du Colombier 		print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count);
193906943f9SDavid du Colombier 
1940641ea09SDavid du Colombier 	cbw.len = cmd->count;
195906943f9SDavid du Colombier 	assert(cmd->count <= sizeof(cbw.command));
1960641ea09SDavid du Colombier 	memcpy(cbw.command, cmd->p, cmd->count);
1970641ea09SDavid du Colombier 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
1980641ea09SDavid du Colombier 
199906943f9SDavid du Colombier 	werrstr("");		/* we use %r later even for n == 0 */
200906943f9SDavid du Colombier 
201906943f9SDavid du Colombier 	if(diskdebug){
202906943f9SDavid du Colombier 		fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
2030641ea09SDavid du Colombier 		for(n = 0; n < cbw.len; n++)
2040641ea09SDavid du Colombier 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
2050641ea09SDavid du Colombier 		fprint(2, " datalen: %ld\n", cbw.datalen);
2060641ea09SDavid du Colombier 	}
207906943f9SDavid du Colombier 	if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
208906943f9SDavid du Colombier 		fprint(2, "disk: cmd: %r\n");
209906943f9SDavid du Colombier 		goto Fail;
2100641ea09SDavid du Colombier 	}
2110641ea09SDavid du Colombier 	if(data->count != 0){
2120641ea09SDavid du Colombier 		if(data->write)
213906943f9SDavid du Colombier 			n = write(ums->epout->dfd, data->p, data->count);
214906943f9SDavid du Colombier 		else{
215906943f9SDavid du Colombier 			memset(data->p, data->count, 0);
216906943f9SDavid du Colombier 			n = read(ums->epin->dfd, data->p, data->count);
2170641ea09SDavid du Colombier 		}
218906943f9SDavid du Colombier 		if(diskdebug)
219906943f9SDavid du Colombier 			if(n < 0)
220906943f9SDavid du Colombier 				fprint(2, "disk: data: %r\n");
221906943f9SDavid du Colombier 			else
222906943f9SDavid du Colombier 				fprint(2, "disk: data: %d bytes\n", n);
223906943f9SDavid du Colombier 		if(n <= 0)
224906943f9SDavid du Colombier 			if(data->write == 0)
225906943f9SDavid du Colombier 				unstall(ums->dev, ums->epin, Ein);
2260641ea09SDavid du Colombier 	}
227906943f9SDavid du Colombier 	n = read(ums->epin->dfd, &csw, CswLen);
228906943f9SDavid du Colombier 	if(n <= 0){
229906943f9SDavid du Colombier 		/* n == 0 means "stalled" */
230906943f9SDavid du Colombier 		unstall(ums->dev, ums->epin, Ein);
231906943f9SDavid du Colombier 		n = read(ums->epin->dfd, &csw, CswLen);
2320641ea09SDavid du Colombier 	}
2330641ea09SDavid du Colombier 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
234906943f9SDavid du Colombier 		dprint(2, "disk: read n=%d: status: %r\n", n);
235906943f9SDavid du Colombier 		goto Fail;
2360641ea09SDavid du Colombier 	}
2370641ea09SDavid du Colombier 	if(csw.tag != cbw.tag){
238906943f9SDavid du Colombier 		dprint(2, "disk: status tag mismatch\n");
239906943f9SDavid du Colombier 		goto Fail;
2400641ea09SDavid du Colombier 	}
2410641ea09SDavid du Colombier 	if(csw.status >= CswPhaseErr){
242906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
243906943f9SDavid du Colombier 		goto Fail;
2440641ea09SDavid du Colombier 	}
245906943f9SDavid du Colombier 	if(diskdebug){
2460641ea09SDavid du Colombier 		fprint(2, "status: %2.2ux residue: %ld\n",
2470641ea09SDavid du Colombier 			csw.status, csw.dataresidue);
2480641ea09SDavid du Colombier 		if(cbw.command[0] == ScmdRsense){
2490641ea09SDavid du Colombier 			fprint(2, "sense data:");
2500641ea09SDavid du Colombier 			for(n = 0; n < data->count - csw.dataresidue; n++)
2510641ea09SDavid du Colombier 				fprint(2, " %2.2x", data->p[n]);
2520641ea09SDavid du Colombier 			fprint(2, "\n");
2530641ea09SDavid du Colombier 		}
2540641ea09SDavid du Colombier 	}
255906943f9SDavid du Colombier 	switch(csw.status){
256906943f9SDavid du Colombier 	case CswOk:
2570641ea09SDavid du Colombier 		*status = STok;
258906943f9SDavid du Colombier 		break;
259906943f9SDavid du Colombier 	case CswFailed:
2600641ea09SDavid du Colombier 		*status = STcheck;
261906943f9SDavid du Colombier 		break;
262906943f9SDavid du Colombier 	default:
263906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
264906943f9SDavid du Colombier 		goto Fail;
265906943f9SDavid du Colombier 	}
266906943f9SDavid du Colombier 	ums->nerrs = 0;
2670641ea09SDavid du Colombier 	return data->count - csw.dataresidue;
2680641ea09SDavid du Colombier 
269906943f9SDavid du Colombier Fail:
2700641ea09SDavid du Colombier 	*status = STharderr;
271906943f9SDavid du Colombier 	if(ums->nerrs++ > 15){
272906943f9SDavid du Colombier 		fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
273906943f9SDavid du Colombier 		umsfatal(ums);
2740641ea09SDavid du Colombier 	}else
275906943f9SDavid du Colombier 		umsrecover(ums);
2760641ea09SDavid du Colombier 	return -1;
2770641ea09SDavid du Colombier }
2780641ea09SDavid du Colombier 
279906943f9SDavid du Colombier static int
280906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name)
2810641ea09SDavid du Colombier {
282906943f9SDavid du Colombier 	int i;
283906943f9SDavid du Colombier 	Qid qid;
284906943f9SDavid du Colombier 
285906943f9SDavid du Colombier 	qid = fid->qid;
286906943f9SDavid du Colombier 	if((qid.type & QTDIR) == 0){
287906943f9SDavid du Colombier 		werrstr("walk in non-directory");
288906943f9SDavid du Colombier 		return -1;
2890641ea09SDavid du Colombier 	}
2900641ea09SDavid du Colombier 
2910641ea09SDavid du Colombier 	if(strcmp(name, "..") == 0)
292906943f9SDavid du Colombier 		return 0;
2930641ea09SDavid du Colombier 
294906943f9SDavid du Colombier 	for(i = 1; i < nelem(dirtab); i++)
2950641ea09SDavid du Colombier 		if(strcmp(name, dirtab[i].name) == 0){
296906943f9SDavid du Colombier 			qid.path = i | fs->qid;
297906943f9SDavid du Colombier 			qid.vers = 0;
298906943f9SDavid du Colombier 			qid.type = dirtab[i].mode >> 24;
299906943f9SDavid du Colombier 			fid->qid = qid;
300906943f9SDavid du Colombier 			return 0;
3010641ea09SDavid du Colombier 		}
302906943f9SDavid du Colombier 	werrstr(Enotfound);
303906943f9SDavid du Colombier 	return -1;
3040641ea09SDavid du Colombier }
3050641ea09SDavid du Colombier 
306906943f9SDavid du Colombier static void
307906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d)
3080641ea09SDavid du Colombier {
3090641ea09SDavid du Colombier 	Dirtab *t;
3100641ea09SDavid du Colombier 	Umsc *lun;
3110641ea09SDavid du Colombier 
312906943f9SDavid du Colombier 	t = &dirtab[path];
313906943f9SDavid du Colombier 	d->qid.path = path;
314906943f9SDavid du Colombier 	d->qid.type = t->mode >> 24;
315906943f9SDavid du Colombier 	d->mode = t->mode;
316906943f9SDavid du Colombier 	d->name = t->name;
317906943f9SDavid du Colombier 	lun = fs->aux;
318906943f9SDavid du Colombier 	if(path == Qdata)
319906943f9SDavid du Colombier 		d->length = lun->capacity;
320906943f9SDavid du Colombier 	else
321906943f9SDavid du Colombier 		d->length = 0;
322906943f9SDavid du Colombier }
323906943f9SDavid du Colombier 
324906943f9SDavid du Colombier static int
325906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
326906943f9SDavid du Colombier {
327906943f9SDavid du Colombier 	i++;	/* skip dir */
328906943f9SDavid du Colombier 	if(i >= Qmax)
329906943f9SDavid du Colombier 		return -1;
330906943f9SDavid du Colombier 	else{
331906943f9SDavid du Colombier 		dostat(fs, i, d);
332906943f9SDavid du Colombier 		d->qid.path |= fs->qid;
333906943f9SDavid du Colombier 		return 0;
334906943f9SDavid du Colombier 	}
335906943f9SDavid du Colombier }
336906943f9SDavid du Colombier 
337906943f9SDavid du Colombier static int
338906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d)
339906943f9SDavid du Colombier {
340906943f9SDavid du Colombier 	int path;
341906943f9SDavid du Colombier 
342906943f9SDavid du Colombier 	path = qid.path & ~fs->qid;
343906943f9SDavid du Colombier 	dostat(fs, path, d);
344906943f9SDavid du Colombier 	d->qid.path |= fs->qid;
345906943f9SDavid du Colombier 	return 0;
346906943f9SDavid du Colombier }
347906943f9SDavid du Colombier 
348906943f9SDavid du Colombier static int
349906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int)
350906943f9SDavid du Colombier {
351906943f9SDavid du Colombier 	ulong path;
352906943f9SDavid du Colombier 	Umsc *lun;
353906943f9SDavid du Colombier 
354906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
355906943f9SDavid du Colombier 	lun = fs->aux;
356906943f9SDavid du Colombier 	switch(path){
357906943f9SDavid du Colombier 	case Qraw:
358906943f9SDavid du Colombier 		lun->phase = Pcmd;
3590641ea09SDavid du Colombier 		break;
360906943f9SDavid du Colombier 	}
361906943f9SDavid du Colombier 	return 0;
362906943f9SDavid du Colombier }
363906943f9SDavid du Colombier 
364906943f9SDavid du Colombier /*
365906943f9SDavid du Colombier  * Upon SRread/SRwrite errors we assume the medium may have changed,
366906943f9SDavid du Colombier  * and ask again for the capacity of the media.
367906943f9SDavid du Colombier  * BUG: How to proceed to avoid confussing dossrv??
368906943f9SDavid du Colombier  */
369906943f9SDavid du Colombier static long
370906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
371906943f9SDavid du Colombier {
372906943f9SDavid du Colombier 	long bno, nb, len, off, n;
373906943f9SDavid du Colombier 	ulong path;
374906943f9SDavid du Colombier 	char buf[1024], *p;
375906943f9SDavid du Colombier 	char *s;
376906943f9SDavid du Colombier 	char *e;
377906943f9SDavid du Colombier 	Umsc *lun;
378906943f9SDavid du Colombier 	Ums *ums;
379906943f9SDavid du Colombier 	Qid q;
380906943f9SDavid du Colombier 
381906943f9SDavid du Colombier 	q = fid->qid;
382906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
383906943f9SDavid du Colombier 	ums = fs->dev->aux;
384906943f9SDavid du Colombier 	lun = fs->aux;
385906943f9SDavid du Colombier 	qlock(ums);
386906943f9SDavid du Colombier 	switch(path){
387906943f9SDavid du Colombier 	case Qdir:
388906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
3890641ea09SDavid du Colombier 		break;
3900641ea09SDavid du Colombier 	case Qctl:
391906943f9SDavid du Colombier 		e = buf + sizeof(buf);
392906943f9SDavid du Colombier 		s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]);
3930641ea09SDavid du Colombier 		if(lun->flags & Finqok)
394906943f9SDavid du Colombier 			s = seprint(s, e, "inquiry %s ", lun->inq);
3950641ea09SDavid du Colombier 		if(lun->blocks > 0)
396906943f9SDavid du Colombier 			s = seprint(s, e, "geometry %llud %ld", lun->blocks,
3970641ea09SDavid du Colombier 				lun->lbsize);
398906943f9SDavid du Colombier 		s = seprint(s, e, "\n");
399906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
4000641ea09SDavid du Colombier 		break;
4010641ea09SDavid du Colombier 	case Qraw:
402906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
403906943f9SDavid du Colombier 			qunlock(ums);
404906943f9SDavid du Colombier 			return -1;
4050641ea09SDavid du Colombier 		}
4060641ea09SDavid du Colombier 		switch(lun->phase){
4070641ea09SDavid du Colombier 		case Pcmd:
408906943f9SDavid du Colombier 			qunlock(ums);
409906943f9SDavid du Colombier 			werrstr("phase error");
410906943f9SDavid du Colombier 			return -1;
4110641ea09SDavid du Colombier 		case Pdata:
412906943f9SDavid du Colombier 			lun->data.p = (uchar*)data;
413906943f9SDavid du Colombier 			lun->data.count = count;
4140641ea09SDavid du Colombier 			lun->data.write = 0;
415906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
4160641ea09SDavid du Colombier 			lun->phase = Pstatus;
417906943f9SDavid du Colombier 			if(count < 0){
418906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
419906943f9SDavid du Colombier 				qunlock(ums);
420906943f9SDavid du Colombier 				return -1;
4210641ea09SDavid du Colombier 			}
4220641ea09SDavid du Colombier 			break;
4230641ea09SDavid du Colombier 		case Pstatus:
4240641ea09SDavid du Colombier 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
425906943f9SDavid du Colombier 			count = usbreadbuf(data, count, 0LL, buf, n);
4260641ea09SDavid du Colombier 			lun->phase = Pcmd;
4270641ea09SDavid du Colombier 			break;
4280641ea09SDavid du Colombier 		}
4290641ea09SDavid du Colombier 		break;
4300641ea09SDavid du Colombier 	case Qdata:
431906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
432906943f9SDavid du Colombier 			qunlock(ums);
433906943f9SDavid du Colombier 			return -1;
4340641ea09SDavid du Colombier 		}
435906943f9SDavid du Colombier 		bno = offset / lun->lbsize;
436906943f9SDavid du Colombier 		nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno;
4370641ea09SDavid du Colombier 		if(bno + nb > lun->blocks)
4380641ea09SDavid du Colombier 			nb = lun->blocks - bno;
4390641ea09SDavid du Colombier 		if(bno >= lun->blocks || nb == 0){
440906943f9SDavid du Colombier 			count = 0;
4410641ea09SDavid du Colombier 			break;
4420641ea09SDavid du Colombier 		}
4430641ea09SDavid du Colombier 		if(nb * lun->lbsize > maxiosize)
4440641ea09SDavid du Colombier 			nb = maxiosize / lun->lbsize;
445906943f9SDavid du Colombier 		p = emallocz(nb * lun->lbsize, 0);	/* could use a static buffer */
446906943f9SDavid du Colombier 		lun->offset = offset / lun->lbsize;
4470641ea09SDavid du Colombier 		n = SRread(lun, p, nb * lun->lbsize);
448906943f9SDavid du Colombier 		if(n < 0){
4490641ea09SDavid du Colombier 			free(p);
450906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
451906943f9SDavid du Colombier 			qunlock(ums);
452906943f9SDavid du Colombier 			return -1;
4530641ea09SDavid du Colombier 		}
454906943f9SDavid du Colombier 		len = count;
455906943f9SDavid du Colombier 		off = offset % lun->lbsize;
456906943f9SDavid du Colombier 		if(off + len > n)
457906943f9SDavid du Colombier 			len = n - off;
458906943f9SDavid du Colombier 		count = len;
459906943f9SDavid du Colombier 		memmove(data, p + off, len);
4600641ea09SDavid du Colombier 		free(p);
4610641ea09SDavid du Colombier 		break;
4620641ea09SDavid du Colombier 	}
463906943f9SDavid du Colombier 	qunlock(ums);
464906943f9SDavid du Colombier 	return count;
4650641ea09SDavid du Colombier }
4660641ea09SDavid du Colombier 
467906943f9SDavid du Colombier static long
468906943f9SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
4690641ea09SDavid du Colombier {
470906943f9SDavid du Colombier 	int bno, nb, len, off;
4710641ea09SDavid du Colombier 	ulong path;
4720641ea09SDavid du Colombier 	char *p;
473906943f9SDavid du Colombier 	Ums *ums;
4740641ea09SDavid du Colombier 	Umsc *lun;
475906943f9SDavid du Colombier 	char *data;
4760641ea09SDavid du Colombier 
477906943f9SDavid du Colombier 	ums = fs->dev->aux;
478906943f9SDavid du Colombier 	lun = fs->aux;
479906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
480906943f9SDavid du Colombier 	data = buf;
481906943f9SDavid du Colombier 	qlock(ums);
482906943f9SDavid du Colombier 	switch(path){
483906943f9SDavid du Colombier 	default:
484906943f9SDavid du Colombier 		qunlock(ums);
485906943f9SDavid du Colombier 		werrstr(Eperm);
486906943f9SDavid du Colombier 		return -1;
4870641ea09SDavid du Colombier 	case Qraw:
488906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
489906943f9SDavid du Colombier 			qunlock(ums);
490906943f9SDavid du Colombier 			return -1;
4910641ea09SDavid du Colombier 		}
4920641ea09SDavid du Colombier 		switch(lun->phase){
4930641ea09SDavid du Colombier 		case Pcmd:
494906943f9SDavid du Colombier 			if(count != 6 && count != 10){
495906943f9SDavid du Colombier 				qunlock(ums);
496906943f9SDavid du Colombier 				werrstr("bad command length");
497906943f9SDavid du Colombier 				return -1;
4980641ea09SDavid du Colombier 			}
499906943f9SDavid du Colombier 			memmove(lun->rawcmd, data, count);
5000641ea09SDavid du Colombier 			lun->cmd.p = lun->rawcmd;
501906943f9SDavid du Colombier 			lun->cmd.count = count;
5020641ea09SDavid du Colombier 			lun->cmd.write = 1;
5030641ea09SDavid du Colombier 			lun->phase = Pdata;
5040641ea09SDavid du Colombier 			break;
5050641ea09SDavid du Colombier 		case Pdata:
506906943f9SDavid du Colombier 			lun->data.p = (uchar*)data;
507906943f9SDavid du Colombier 			lun->data.count = count;
5080641ea09SDavid du Colombier 			lun->data.write = 1;
509906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
5100641ea09SDavid du Colombier 			lun->phase = Pstatus;
511906943f9SDavid du Colombier 			if(count < 0){
512906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
513906943f9SDavid du Colombier 				qunlock(ums);
514906943f9SDavid du Colombier 				return -1;
5150641ea09SDavid du Colombier 			}
5160641ea09SDavid du Colombier 			break;
5170641ea09SDavid du Colombier 		case Pstatus:
5180641ea09SDavid du Colombier 			lun->phase = Pcmd;
519906943f9SDavid du Colombier 			qunlock(ums);
520906943f9SDavid du Colombier 			werrstr("phase error");
521906943f9SDavid du Colombier 			return -1;
5220641ea09SDavid du Colombier 		}
5230641ea09SDavid du Colombier 		break;
5240641ea09SDavid du Colombier 	case Qdata:
525906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
526906943f9SDavid du Colombier 			qunlock(ums);
527906943f9SDavid du Colombier 			return -1;
5280641ea09SDavid du Colombier 		}
529906943f9SDavid du Colombier 		bno = offset / lun->lbsize;
530906943f9SDavid du Colombier 		nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno;
5310641ea09SDavid du Colombier 		if(bno + nb > lun->blocks)
5320641ea09SDavid du Colombier 			nb = lun->blocks - bno;
5330641ea09SDavid du Colombier 		if(bno >= lun->blocks || nb == 0){
534906943f9SDavid du Colombier 			count = 0;
5350641ea09SDavid du Colombier 			break;
5360641ea09SDavid du Colombier 		}
5370641ea09SDavid du Colombier 		if(nb * lun->lbsize > maxiosize)
5380641ea09SDavid du Colombier 			nb = maxiosize / lun->lbsize;
539906943f9SDavid du Colombier 		p = emallocz(nb * lun->lbsize, 0);
540906943f9SDavid du Colombier 		off = offset % lun->lbsize;
541906943f9SDavid du Colombier 		len = count;
542906943f9SDavid du Colombier 		if(off || (len % lun->lbsize) != 0){
543906943f9SDavid du Colombier 			lun->offset = offset / lun->lbsize;
544906943f9SDavid du Colombier 			count = SRread(lun, p, nb * lun->lbsize);
545906943f9SDavid du Colombier 			if(count < 0){
5460641ea09SDavid du Colombier 				free(p);
547906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
548906943f9SDavid du Colombier 				qunlock(ums);
549906943f9SDavid du Colombier 				return -1;
5500641ea09SDavid du Colombier 			}
551906943f9SDavid du Colombier 			if(off + len > count)
552906943f9SDavid du Colombier 				len = count - off;
5530641ea09SDavid du Colombier 		}
554906943f9SDavid du Colombier 		memmove(p+off, data, len);
555906943f9SDavid du Colombier 		lun->offset = offset / lun->lbsize;
556906943f9SDavid du Colombier 		count = SRwrite(lun, p, nb * lun->lbsize);
557906943f9SDavid du Colombier 		if(count < 0){
5580641ea09SDavid du Colombier 			free(p);
559906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
560906943f9SDavid du Colombier 			qunlock(ums);
561906943f9SDavid du Colombier 			return -1;
5620641ea09SDavid du Colombier 		}
563906943f9SDavid du Colombier 		if(off+len > count)
564906943f9SDavid du Colombier 			len = count - off;
565906943f9SDavid du Colombier 		count = len;
5660641ea09SDavid du Colombier 		free(p);
5670641ea09SDavid du Colombier 		break;
5680641ea09SDavid du Colombier 	}
569906943f9SDavid du Colombier 	qunlock(ums);
570906943f9SDavid du Colombier 	return count;
5710641ea09SDavid du Colombier }
5720641ea09SDavid du Colombier 
573906943f9SDavid du Colombier int
574906943f9SDavid du Colombier findendpoints(Ums *ums)
575906943f9SDavid du Colombier {
576906943f9SDavid du Colombier 	Ep *ep;
577906943f9SDavid du Colombier 	Usbdev *ud;
578906943f9SDavid du Colombier 	ulong csp;
579906943f9SDavid du Colombier 	ulong sc;
580906943f9SDavid du Colombier 	int i;
581906943f9SDavid du Colombier 	int epin, epout;
5820641ea09SDavid du Colombier 
583906943f9SDavid du Colombier 	epin = epout = -1;
584906943f9SDavid du Colombier 	ud = ums->dev->usb;
585906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->ep); i++){
586906943f9SDavid du Colombier 		if((ep = ud->ep[i]) == nil)
587906943f9SDavid du Colombier 			continue;
588906943f9SDavid du Colombier 		csp = ep->iface->csp;
589906943f9SDavid du Colombier 		sc = Subclass(csp);
590906943f9SDavid du Colombier 		if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
591906943f9SDavid du Colombier 			continue;
592906943f9SDavid du Colombier 		if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
593906943f9SDavid du Colombier 			fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
594906943f9SDavid du Colombier 		if(ep->type == Ebulk){
595906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Ein)
596906943f9SDavid du Colombier 				if(epin == -1)
597906943f9SDavid du Colombier 					epin =  ep->id;
598906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Eout)
599906943f9SDavid du Colombier 				if(epout == -1)
600906943f9SDavid du Colombier 					epout = ep->id;
601906943f9SDavid du Colombier 		}
602906943f9SDavid du Colombier 	}
603906943f9SDavid du Colombier 	dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
604906943f9SDavid du Colombier 	if(epin == -1 || epout == -1)
605906943f9SDavid du Colombier 		return -1;
606906943f9SDavid du Colombier 	ums->epin = openep(ums->dev, epin);
607906943f9SDavid du Colombier 	if(ums->epin == nil){
608906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epin);
609906943f9SDavid du Colombier 		return -1;
610906943f9SDavid du Colombier 	}
611906943f9SDavid du Colombier 	if(epout == epin){
612906943f9SDavid du Colombier 		incref(ums->epin);
613906943f9SDavid du Colombier 		ums->epout = ums->epin;
614906943f9SDavid du Colombier 	}else
615906943f9SDavid du Colombier 		ums->epout = openep(ums->dev, epout);
616906943f9SDavid du Colombier 	if(ums->epout == nil){
617906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epout);
618906943f9SDavid du Colombier 		closedev(ums->epin);
619906943f9SDavid du Colombier 		return -1;
620906943f9SDavid du Colombier 	}
621906943f9SDavid du Colombier 	if(ums->epin == ums->epout)
622906943f9SDavid du Colombier 		opendevdata(ums->epin, ORDWR);
623906943f9SDavid du Colombier 	else{
624906943f9SDavid du Colombier 		opendevdata(ums->epin, OREAD);
625906943f9SDavid du Colombier 		opendevdata(ums->epout, OWRITE);
626906943f9SDavid du Colombier 	}
627906943f9SDavid du Colombier 	if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
628906943f9SDavid du Colombier 		fprint(2, "disk: open i/o ep data: %r\n");
629906943f9SDavid du Colombier 		closedev(ums->epin);
630906943f9SDavid du Colombier 		closedev(ums->epout);
631906943f9SDavid du Colombier 		return -1;
632906943f9SDavid du Colombier 	}
633906943f9SDavid du Colombier 	dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
634d37e33ffSDavid du Colombier 
635d37e33ffSDavid du Colombier 	devctl(ums->epin, "timeout 2000");
636d37e33ffSDavid du Colombier 	devctl(ums->epout, "timeout 2000");
6370641ea09SDavid du Colombier 
638906943f9SDavid du Colombier 	if(usbdebug > 1 || diskdebug > 2){
639906943f9SDavid du Colombier 		devctl(ums->epin, "debug 1");
640906943f9SDavid du Colombier 		devctl(ums->epout, "debug 1");
641906943f9SDavid du Colombier 		devctl(ums->dev, "debug 1");
642906943f9SDavid du Colombier 	}
643906943f9SDavid du Colombier 	return 0;
644906943f9SDavid du Colombier }
645906943f9SDavid du Colombier 
646906943f9SDavid du Colombier static int
6470641ea09SDavid du Colombier usage(void)
6480641ea09SDavid du Colombier {
649906943f9SDavid du Colombier 	werrstr("usage: usb/disk [-d]");
650906943f9SDavid du Colombier 	return -1;
6510641ea09SDavid du Colombier }
6520641ea09SDavid du Colombier 
653906943f9SDavid du Colombier static void
654906943f9SDavid du Colombier umsdevfree(void *a)
6550641ea09SDavid du Colombier {
656906943f9SDavid du Colombier 	Ums *ums = a;
6570641ea09SDavid du Colombier 
658906943f9SDavid du Colombier 	if(ums == nil)
659906943f9SDavid du Colombier 		return;
660906943f9SDavid du Colombier 	closedev(ums->epin);
661906943f9SDavid du Colombier 	closedev(ums->epout);
662906943f9SDavid du Colombier 	ums->epin = ums->epout = nil;
663906943f9SDavid du Colombier 	free(ums->lun);
664906943f9SDavid du Colombier 	free(ums);
665906943f9SDavid du Colombier }
666906943f9SDavid du Colombier 
667906943f9SDavid du Colombier static Usbfs diskfs = {
668906943f9SDavid du Colombier 	.walk = dwalk,
669906943f9SDavid du Colombier 	.open =	 dopen,
670906943f9SDavid du Colombier 	.read =	 dread,
671906943f9SDavid du Colombier 	.write = dwrite,
672906943f9SDavid du Colombier 	.stat =	 dstat,
673906943f9SDavid du Colombier };
674906943f9SDavid du Colombier 
675906943f9SDavid du Colombier int
676906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv)
677906943f9SDavid du Colombier {
678906943f9SDavid du Colombier 	Ums *ums;
679906943f9SDavid du Colombier 	Umsc *lun;
680906943f9SDavid du Colombier 	int i;
6810641ea09SDavid du Colombier 
6820641ea09SDavid du Colombier 	ARGBEGIN{
6830641ea09SDavid du Colombier 	case 'd':
684906943f9SDavid du Colombier 		scsidebug(diskdebug);
685906943f9SDavid du Colombier 		diskdebug++;
6860641ea09SDavid du Colombier 		break;
6870641ea09SDavid du Colombier 	default:
688906943f9SDavid du Colombier 		return usage();
6890641ea09SDavid du Colombier 	}ARGEND
690906943f9SDavid du Colombier 	if(argc != 0)
691906943f9SDavid du Colombier 		return usage();
6920641ea09SDavid du Colombier 
693906943f9SDavid du Colombier 	ums = dev->aux = emallocz(sizeof(Ums), 1);
694906943f9SDavid du Colombier 	ums->maxlun = -1;
695906943f9SDavid du Colombier 	ums->dev = dev;
696906943f9SDavid du Colombier 	dev->free = umsdevfree;
697906943f9SDavid du Colombier 	if(findendpoints(ums) < 0){
698906943f9SDavid du Colombier 		werrstr("disk: endpoints not found");
699906943f9SDavid du Colombier 		return -1;
7000641ea09SDavid du Colombier 	}
701906943f9SDavid du Colombier 	if(umsinit(ums) < 0){
702906943f9SDavid du Colombier 		dprint(2, "disk: umsinit: %r\n");
703906943f9SDavid du Colombier 		return -1;
704906943f9SDavid du Colombier 	}
7050641ea09SDavid du Colombier 
706906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
707906943f9SDavid du Colombier 		lun = &ums->lun[i];
708906943f9SDavid du Colombier 		lun->fs = diskfs;
709906943f9SDavid du Colombier 		snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i);
710906943f9SDavid du Colombier 		lun->fs.dev = dev;
711906943f9SDavid du Colombier 		incref(dev);
712906943f9SDavid du Colombier 		lun->fs.aux = lun;
713906943f9SDavid du Colombier 		usbfsadd(&lun->fs);
714906943f9SDavid du Colombier 	}
715906943f9SDavid du Colombier 	closedev(dev);
716906943f9SDavid du Colombier 	return 0;
7170641ea09SDavid du Colombier }
718