xref: /plan9/sys/src/cmd/usb/disk/disk.c (revision 906943f9f6b8411972abb5e3a03ed19f74be7ccc)
10641ea09SDavid du Colombier /*
20641ea09SDavid du Colombier  * usb/disk - usb mass storage file server
3*906943f9SDavid du Colombier  * BUG: supports only the scsi command interface.
4*906943f9SDavid du Colombier  * BUG: This should use /dev/sdfile to
5*906943f9SDavid du Colombier  * use the kernel ether device code.
60641ea09SDavid du Colombier  */
7*906943f9SDavid 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"
15*906943f9SDavid du Colombier #include "usbfs.h"
16*906943f9SDavid du Colombier #include "ums.h"
170641ea09SDavid du Colombier 
18*906943f9SDavid du Colombier enum
19*906943f9SDavid du Colombier {
200641ea09SDavid du Colombier 	Qdir = 0,
210641ea09SDavid du Colombier 	Qctl,
220641ea09SDavid du Colombier 	Qraw,
230641ea09SDavid du Colombier 	Qdata,
24*906943f9SDavid du Colombier 	Qmax,
250641ea09SDavid du Colombier };
260641ea09SDavid du Colombier 
270641ea09SDavid du Colombier typedef struct Dirtab Dirtab;
28*906943f9SDavid du Colombier struct Dirtab
29*906943f9SDavid du Colombier {
300641ea09SDavid du Colombier 	char	*name;
310641ea09SDavid du Colombier 	int	mode;
320641ea09SDavid du Colombier };
33*906943f9SDavid du Colombier 
34*906943f9SDavid du Colombier static Dirtab dirtab[] =
35*906943f9SDavid du Colombier {
36*906943f9SDavid du Colombier 	[Qdir]	"/",	DMDIR|0555,
37*906943f9SDavid du Colombier 	[Qctl]	"ctl",	0444,
38*906943f9SDavid du Colombier 	[Qraw]	"raw",	0640,
39*906943f9SDavid du Colombier 	[Qdata]	"data",	0640,
400641ea09SDavid du Colombier };
410641ea09SDavid du Colombier 
420641ea09SDavid du Colombier /*
43*906943f9SDavid du Colombier  * These are used by scuzz scsireq
440641ea09SDavid du Colombier  */
45*906943f9SDavid du Colombier int exabyte, force6bytecmds;
46*906943f9SDavid du Colombier long maxiosize = MaxIOsize;
470641ea09SDavid du Colombier 
48*906943f9SDavid du Colombier static int diskdebug;
490641ea09SDavid du Colombier 
50*906943f9SDavid du Colombier static int
51*906943f9SDavid du Colombier getmaxlun(Dev *dev)
520641ea09SDavid du Colombier {
530641ea09SDavid du Colombier 	uchar max;
54*906943f9SDavid du Colombier 	int r;
550641ea09SDavid du Colombier 
566a5dc222SDavid du Colombier 	max = 0;
57*906943f9SDavid du Colombier 	r = Rd2h|Rclass|Riface;
58*906943f9SDavid du Colombier 	if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
59*906943f9SDavid du Colombier 		dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
60*906943f9SDavid du Colombier 	}else
61*906943f9SDavid du Colombier 		dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
62*906943f9SDavid du Colombier 	return max;
630641ea09SDavid du Colombier }
640641ea09SDavid du Colombier 
65*906943f9SDavid du Colombier static int
66*906943f9SDavid du Colombier umsreset(Ums *ums)
670641ea09SDavid du Colombier {
68*906943f9SDavid du Colombier 	int r;
690641ea09SDavid du Colombier 
70*906943f9SDavid du Colombier 	r = Rh2d|Rclass|Riface;
71*906943f9SDavid du Colombier 	if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
72*906943f9SDavid du Colombier 		fprint(2, "disk: reset: %r\n");
730641ea09SDavid du Colombier 		return -1;
740641ea09SDavid du Colombier 	}
75*906943f9SDavid du Colombier 	return 0;
760641ea09SDavid du Colombier }
770641ea09SDavid du Colombier 
78*906943f9SDavid du Colombier static int
79*906943f9SDavid du Colombier umsrecover(Ums *ums)
80*906943f9SDavid du Colombier {
81*906943f9SDavid du Colombier 	if(umsreset(ums) < 0)
820641ea09SDavid du Colombier 		return -1;
83*906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epin, Ein) < 0)
84*906943f9SDavid du Colombier 		dprint(2, "disk: unstall epin: %r\n");
85*906943f9SDavid du Colombier 
86*906943f9SDavid du Colombier 	/* do we need this when epin == epout? */
87*906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epout, Eout) < 0)
88*906943f9SDavid du Colombier 		dprint(2, "disk: unstall epout: %r\n");
89*906943f9SDavid du Colombier 	return 0;
90*906943f9SDavid du Colombier }
91*906943f9SDavid du Colombier 
92*906943f9SDavid du Colombier static void
93*906943f9SDavid du Colombier umsfatal(Ums *ums)
94*906943f9SDavid du Colombier {
95*906943f9SDavid du Colombier 	int i;
96*906943f9SDavid du Colombier 
97*906943f9SDavid du Colombier 	devctl(ums->dev, "detach");
98*906943f9SDavid du Colombier 	for(i = 0; i < ums->maxlun; i++)
99*906943f9SDavid du Colombier 		usbfsdel(&ums->lun[i].fs);
100*906943f9SDavid du Colombier }
101*906943f9SDavid du Colombier 
102*906943f9SDavid du Colombier static int
103*906943f9SDavid du Colombier umscapacity(Umsc *lun)
104*906943f9SDavid du Colombier {
105*906943f9SDavid du Colombier 	uchar data[32];
106*906943f9SDavid du Colombier 
1070641ea09SDavid du Colombier 	lun->blocks = 0;
1080641ea09SDavid du Colombier 	lun->capacity = 0;
1090641ea09SDavid du Colombier 	lun->lbsize = 0;
110*906943f9SDavid du Colombier 	if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data)  < 0)
111*906943f9SDavid du Colombier 		return -1;
112*906943f9SDavid du Colombier 	lun->blocks = GETBELONG(data);
113*906943f9SDavid du Colombier 	lun->lbsize = GETBELONG(data+4);
114*906943f9SDavid du Colombier 	if(lun->blocks == 0xFFFFFFFF){
115*906943f9SDavid du Colombier 		if(SRrcapacity16(lun, data) < 0){
116*906943f9SDavid du Colombier 			lun->lbsize = 0;
117*906943f9SDavid du Colombier 			lun->blocks = 0;
118*906943f9SDavid du Colombier 			return -1;
1190641ea09SDavid du Colombier 		}else{
120*906943f9SDavid du Colombier 			lun->lbsize = GETBELONG(data + 8);
121*906943f9SDavid du Colombier 			lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
122*906943f9SDavid du Colombier 		}
123*906943f9SDavid du Colombier 	}
1240641ea09SDavid du Colombier 	lun->blocks++; /* SRcapacity returns LBA of last block */
1250641ea09SDavid du Colombier 	lun->capacity = (vlong)lun->blocks * lun->lbsize;
126*906943f9SDavid du Colombier 	return 0;
1270641ea09SDavid du Colombier }
128*906943f9SDavid du Colombier 
129*906943f9SDavid du Colombier static int
130*906943f9SDavid du Colombier umsinit(Ums *ums)
131*906943f9SDavid du Colombier {
132*906943f9SDavid du Colombier 	uchar i;
133*906943f9SDavid du Colombier 	Umsc *lun;
134*906943f9SDavid du Colombier 	int some;
135*906943f9SDavid du Colombier 
136*906943f9SDavid du Colombier 	umsreset(ums);
137*906943f9SDavid du Colombier 	ums->maxlun = getmaxlun(ums->dev);
138*906943f9SDavid du Colombier 	ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
139*906943f9SDavid du Colombier 	some = 0;
140*906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
141*906943f9SDavid du Colombier 		lun = &ums->lun[i];
142*906943f9SDavid du Colombier 		lun->ums = ums;
143*906943f9SDavid du Colombier 		lun->umsc = lun;
144*906943f9SDavid du Colombier 		lun->lun = i;
145*906943f9SDavid du Colombier 		lun->flags = Fopen | Fusb | Frw10;
146*906943f9SDavid du Colombier 		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0)
147*906943f9SDavid du Colombier 			continue;
148*906943f9SDavid du Colombier 		if(lun->inquiry[0] != 0x00){
149*906943f9SDavid du Colombier 			/* not a disk */
150*906943f9SDavid du Colombier 			fprint(2, "%s: lun %d is not a disk (type %#02x)\n",
151*906943f9SDavid du Colombier 				argv0, i, lun->inquiry[0]);
152*906943f9SDavid du Colombier 			continue;
153*906943f9SDavid du Colombier 		}
154*906943f9SDavid du Colombier 		SRstart(lun, 1);
155*906943f9SDavid du Colombier 		/*
156*906943f9SDavid du Colombier 		 * we ignore the device type reported by inquiry.
157*906943f9SDavid du Colombier 		 * Some devices return a wrong value but would still work.
158*906943f9SDavid du Colombier 		 */
159*906943f9SDavid du Colombier 		some++;
160*906943f9SDavid du Colombier 		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
161*906943f9SDavid du Colombier 		umscapacity(lun);
162*906943f9SDavid du Colombier 	}
163*906943f9SDavid du Colombier 	if(some == 0){
164*906943f9SDavid du Colombier 		devctl(ums->dev, "detach");
165*906943f9SDavid du Colombier 		return -1;
1660641ea09SDavid du Colombier 	}
1670641ea09SDavid du Colombier 	return 0;
1680641ea09SDavid du Colombier }
1690641ea09SDavid du Colombier 
1700641ea09SDavid du Colombier 
171*906943f9SDavid du Colombier /*
172*906943f9SDavid du Colombier  * called by SR*() commands provided by scuzz's scsireq
173*906943f9SDavid du Colombier  */
1740641ea09SDavid du Colombier long
1750641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
1760641ea09SDavid du Colombier {
1770641ea09SDavid du Colombier 	Cbw cbw;
1780641ea09SDavid du Colombier 	Csw csw;
1790641ea09SDavid du Colombier 	int n;
180*906943f9SDavid du Colombier 	Ums *ums;
181*906943f9SDavid du Colombier 
182*906943f9SDavid du Colombier 	ums = umsc->ums;
1830641ea09SDavid du Colombier 
1840641ea09SDavid du Colombier 	memcpy(cbw.signature, "USBC", 4);
185*906943f9SDavid du Colombier 	cbw.tag = ++ums->seq;
1860641ea09SDavid du Colombier 	cbw.datalen = data->count;
1870641ea09SDavid du Colombier 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
1880641ea09SDavid du Colombier 	cbw.lun = umsc->lun;
189*906943f9SDavid du Colombier 	if(cmd->count < 1 || cmd->count > 16)
190*906943f9SDavid du Colombier 		print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count);
191*906943f9SDavid du Colombier 
1920641ea09SDavid du Colombier 	cbw.len = cmd->count;
193*906943f9SDavid du Colombier 	assert(cmd->count <= sizeof(cbw.command));
1940641ea09SDavid du Colombier 	memcpy(cbw.command, cmd->p, cmd->count);
1950641ea09SDavid du Colombier 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
1960641ea09SDavid du Colombier 
197*906943f9SDavid du Colombier 	werrstr("");		/* we use %r later even for n == 0 */
198*906943f9SDavid du Colombier 
199*906943f9SDavid du Colombier 	if(diskdebug){
200*906943f9SDavid du Colombier 		fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
2010641ea09SDavid du Colombier 		for(n = 0; n < cbw.len; n++)
2020641ea09SDavid du Colombier 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
2030641ea09SDavid du Colombier 		fprint(2, " datalen: %ld\n", cbw.datalen);
2040641ea09SDavid du Colombier 	}
205*906943f9SDavid du Colombier 	if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
206*906943f9SDavid du Colombier 		fprint(2, "disk: cmd: %r\n");
207*906943f9SDavid du Colombier 		goto Fail;
2080641ea09SDavid du Colombier 	}
2090641ea09SDavid du Colombier 	if(data->count != 0){
2100641ea09SDavid du Colombier 		if(data->write)
211*906943f9SDavid du Colombier 			n = write(ums->epout->dfd, data->p, data->count);
212*906943f9SDavid du Colombier 		else{
213*906943f9SDavid du Colombier 			memset(data->p, data->count, 0);
214*906943f9SDavid du Colombier 			n = read(ums->epin->dfd, data->p, data->count);
2150641ea09SDavid du Colombier 		}
216*906943f9SDavid du Colombier 		if(diskdebug)
217*906943f9SDavid du Colombier 			if(n < 0)
218*906943f9SDavid du Colombier 				fprint(2, "disk: data: %r\n");
219*906943f9SDavid du Colombier 			else
220*906943f9SDavid du Colombier 				fprint(2, "disk: data: %d bytes\n", n);
221*906943f9SDavid du Colombier 		if(n <= 0)
222*906943f9SDavid du Colombier 			if(data->write == 0)
223*906943f9SDavid du Colombier 				unstall(ums->dev, ums->epin, Ein);
2240641ea09SDavid du Colombier 	}
225*906943f9SDavid du Colombier 	n = read(ums->epin->dfd, &csw, CswLen);
226*906943f9SDavid du Colombier 	if(n <= 0){
227*906943f9SDavid du Colombier 		/* n == 0 means "stalled" */
228*906943f9SDavid du Colombier 		unstall(ums->dev, ums->epin, Ein);
229*906943f9SDavid du Colombier 		n = read(ums->epin->dfd, &csw, CswLen);
2300641ea09SDavid du Colombier 	}
2310641ea09SDavid du Colombier 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
232*906943f9SDavid du Colombier 		dprint(2, "disk: read n=%d: status: %r\n", n);
233*906943f9SDavid du Colombier 		goto Fail;
2340641ea09SDavid du Colombier 	}
2350641ea09SDavid du Colombier 	if(csw.tag != cbw.tag){
236*906943f9SDavid du Colombier 		dprint(2, "disk: status tag mismatch\n");
237*906943f9SDavid du Colombier 		goto Fail;
2380641ea09SDavid du Colombier 	}
2390641ea09SDavid du Colombier 	if(csw.status >= CswPhaseErr){
240*906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
241*906943f9SDavid du Colombier 		goto Fail;
2420641ea09SDavid du Colombier 	}
243*906943f9SDavid du Colombier 	if(diskdebug){
2440641ea09SDavid du Colombier 		fprint(2, "status: %2.2ux residue: %ld\n",
2450641ea09SDavid du Colombier 			csw.status, csw.dataresidue);
2460641ea09SDavid du Colombier 		if(cbw.command[0] == ScmdRsense){
2470641ea09SDavid du Colombier 			fprint(2, "sense data:");
2480641ea09SDavid du Colombier 			for(n = 0; n < data->count - csw.dataresidue; n++)
2490641ea09SDavid du Colombier 				fprint(2, " %2.2x", data->p[n]);
2500641ea09SDavid du Colombier 			fprint(2, "\n");
2510641ea09SDavid du Colombier 		}
2520641ea09SDavid du Colombier 	}
253*906943f9SDavid du Colombier 	switch(csw.status){
254*906943f9SDavid du Colombier 	case CswOk:
2550641ea09SDavid du Colombier 		*status = STok;
256*906943f9SDavid du Colombier 		break;
257*906943f9SDavid du Colombier 	case CswFailed:
2580641ea09SDavid du Colombier 		*status = STcheck;
259*906943f9SDavid du Colombier 		break;
260*906943f9SDavid du Colombier 	default:
261*906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
262*906943f9SDavid du Colombier 		goto Fail;
263*906943f9SDavid du Colombier 	}
264*906943f9SDavid du Colombier 	ums->nerrs = 0;
2650641ea09SDavid du Colombier 	return data->count - csw.dataresidue;
2660641ea09SDavid du Colombier 
267*906943f9SDavid du Colombier Fail:
2680641ea09SDavid du Colombier 	*status = STharderr;
269*906943f9SDavid du Colombier 	if(ums->nerrs++ > 15){
270*906943f9SDavid du Colombier 		fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
271*906943f9SDavid du Colombier 		umsfatal(ums);
2720641ea09SDavid du Colombier 	}else
273*906943f9SDavid du Colombier 		umsrecover(ums);
2740641ea09SDavid du Colombier 	return -1;
2750641ea09SDavid du Colombier }
2760641ea09SDavid du Colombier 
277*906943f9SDavid du Colombier static int
278*906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name)
2790641ea09SDavid du Colombier {
280*906943f9SDavid du Colombier 	int i;
281*906943f9SDavid du Colombier 	Qid qid;
282*906943f9SDavid du Colombier 
283*906943f9SDavid du Colombier 	qid = fid->qid;
284*906943f9SDavid du Colombier 	if((qid.type & QTDIR) == 0){
285*906943f9SDavid du Colombier 		werrstr("walk in non-directory");
286*906943f9SDavid du Colombier 		return -1;
2870641ea09SDavid du Colombier 	}
2880641ea09SDavid du Colombier 
2890641ea09SDavid du Colombier 	if(strcmp(name, "..") == 0)
290*906943f9SDavid du Colombier 		return 0;
2910641ea09SDavid du Colombier 
292*906943f9SDavid du Colombier 	for(i = 1; i < nelem(dirtab); i++)
2930641ea09SDavid du Colombier 		if(strcmp(name, dirtab[i].name) == 0){
294*906943f9SDavid du Colombier 			qid.path = i | fs->qid;
295*906943f9SDavid du Colombier 			qid.vers = 0;
296*906943f9SDavid du Colombier 			qid.type = dirtab[i].mode >> 24;
297*906943f9SDavid du Colombier 			fid->qid = qid;
298*906943f9SDavid du Colombier 			return 0;
2990641ea09SDavid du Colombier 		}
300*906943f9SDavid du Colombier 	werrstr(Enotfound);
301*906943f9SDavid du Colombier 	return -1;
3020641ea09SDavid du Colombier }
3030641ea09SDavid du Colombier 
304*906943f9SDavid du Colombier static void
305*906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d)
3060641ea09SDavid du Colombier {
3070641ea09SDavid du Colombier 	Dirtab *t;
3080641ea09SDavid du Colombier 	Umsc *lun;
3090641ea09SDavid du Colombier 
310*906943f9SDavid du Colombier 	t = &dirtab[path];
311*906943f9SDavid du Colombier 	d->qid.path = path;
312*906943f9SDavid du Colombier 	d->qid.type = t->mode >> 24;
313*906943f9SDavid du Colombier 	d->mode = t->mode;
314*906943f9SDavid du Colombier 	d->name = t->name;
315*906943f9SDavid du Colombier 	lun = fs->aux;
316*906943f9SDavid du Colombier 	if(path == Qdata)
317*906943f9SDavid du Colombier 		d->length = lun->capacity;
318*906943f9SDavid du Colombier 	else
319*906943f9SDavid du Colombier 		d->length = 0;
320*906943f9SDavid du Colombier }
321*906943f9SDavid du Colombier 
322*906943f9SDavid du Colombier static int
323*906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
324*906943f9SDavid du Colombier {
325*906943f9SDavid du Colombier 	i++;	/* skip dir */
326*906943f9SDavid du Colombier 	if(i >= Qmax)
327*906943f9SDavid du Colombier 		return -1;
328*906943f9SDavid du Colombier 	else{
329*906943f9SDavid du Colombier 		dostat(fs, i, d);
330*906943f9SDavid du Colombier 		d->qid.path |= fs->qid;
331*906943f9SDavid du Colombier 		return 0;
332*906943f9SDavid du Colombier 	}
333*906943f9SDavid du Colombier }
334*906943f9SDavid du Colombier 
335*906943f9SDavid du Colombier static int
336*906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d)
337*906943f9SDavid du Colombier {
338*906943f9SDavid du Colombier 	int path;
339*906943f9SDavid du Colombier 
340*906943f9SDavid du Colombier 	path = qid.path & ~fs->qid;
341*906943f9SDavid du Colombier 	dostat(fs, path, d);
342*906943f9SDavid du Colombier 	d->qid.path |= fs->qid;
343*906943f9SDavid du Colombier 	return 0;
344*906943f9SDavid du Colombier }
345*906943f9SDavid du Colombier 
346*906943f9SDavid du Colombier static int
347*906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int)
348*906943f9SDavid du Colombier {
349*906943f9SDavid du Colombier 	ulong path;
350*906943f9SDavid du Colombier 	Umsc *lun;
351*906943f9SDavid du Colombier 
352*906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
353*906943f9SDavid du Colombier 	lun = fs->aux;
354*906943f9SDavid du Colombier 	switch(path){
355*906943f9SDavid du Colombier 	case Qraw:
356*906943f9SDavid du Colombier 		lun->phase = Pcmd;
3570641ea09SDavid du Colombier 		break;
358*906943f9SDavid du Colombier 	}
359*906943f9SDavid du Colombier 	return 0;
360*906943f9SDavid du Colombier }
361*906943f9SDavid du Colombier 
362*906943f9SDavid du Colombier /*
363*906943f9SDavid du Colombier  * Upon SRread/SRwrite errors we assume the medium may have changed,
364*906943f9SDavid du Colombier  * and ask again for the capacity of the media.
365*906943f9SDavid du Colombier  * BUG: How to proceed to avoid confussing dossrv??
366*906943f9SDavid du Colombier  */
367*906943f9SDavid du Colombier static long
368*906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
369*906943f9SDavid du Colombier {
370*906943f9SDavid du Colombier 	long bno, nb, len, off, n;
371*906943f9SDavid du Colombier 	ulong path;
372*906943f9SDavid du Colombier 	char buf[1024], *p;
373*906943f9SDavid du Colombier 	char *s;
374*906943f9SDavid du Colombier 	char *e;
375*906943f9SDavid du Colombier 	Umsc *lun;
376*906943f9SDavid du Colombier 	Ums *ums;
377*906943f9SDavid du Colombier 	Qid q;
378*906943f9SDavid du Colombier 
379*906943f9SDavid du Colombier 	q = fid->qid;
380*906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
381*906943f9SDavid du Colombier 	ums = fs->dev->aux;
382*906943f9SDavid du Colombier 	lun = fs->aux;
383*906943f9SDavid du Colombier 	qlock(ums);
384*906943f9SDavid du Colombier 	switch(path){
385*906943f9SDavid du Colombier 	case Qdir:
386*906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
3870641ea09SDavid du Colombier 		break;
3880641ea09SDavid du Colombier 	case Qctl:
389*906943f9SDavid du Colombier 		e = buf + sizeof(buf);
390*906943f9SDavid du Colombier 		s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]);
3910641ea09SDavid du Colombier 		if(lun->flags & Finqok)
392*906943f9SDavid du Colombier 			s = seprint(s, e, "inquiry %s ", lun->inq);
3930641ea09SDavid du Colombier 		if(lun->blocks > 0)
394*906943f9SDavid du Colombier 			s = seprint(s, e, "geometry %llud %ld", lun->blocks,
3950641ea09SDavid du Colombier 				lun->lbsize);
396*906943f9SDavid du Colombier 		s = seprint(s, e, "\n");
397*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
3980641ea09SDavid du Colombier 		break;
3990641ea09SDavid du Colombier 	case Qraw:
400*906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
401*906943f9SDavid du Colombier 			qunlock(ums);
402*906943f9SDavid du Colombier 			return -1;
4030641ea09SDavid du Colombier 		}
4040641ea09SDavid du Colombier 		switch(lun->phase){
4050641ea09SDavid du Colombier 		case Pcmd:
406*906943f9SDavid du Colombier 			qunlock(ums);
407*906943f9SDavid du Colombier 			werrstr("phase error");
408*906943f9SDavid du Colombier 			return -1;
4090641ea09SDavid du Colombier 		case Pdata:
410*906943f9SDavid du Colombier 			lun->data.p = (uchar*)data;
411*906943f9SDavid du Colombier 			lun->data.count = count;
4120641ea09SDavid du Colombier 			lun->data.write = 0;
413*906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
4140641ea09SDavid du Colombier 			lun->phase = Pstatus;
415*906943f9SDavid du Colombier 			if(count < 0){
416*906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
417*906943f9SDavid du Colombier 				qunlock(ums);
418*906943f9SDavid du Colombier 				return -1;
4190641ea09SDavid du Colombier 			}
4200641ea09SDavid du Colombier 			break;
4210641ea09SDavid du Colombier 		case Pstatus:
4220641ea09SDavid du Colombier 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
423*906943f9SDavid du Colombier 			count = usbreadbuf(data, count, 0LL, buf, n);
4240641ea09SDavid du Colombier 			lun->phase = Pcmd;
4250641ea09SDavid du Colombier 			break;
4260641ea09SDavid du Colombier 		}
4270641ea09SDavid du Colombier 		break;
4280641ea09SDavid du Colombier 	case Qdata:
429*906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
430*906943f9SDavid du Colombier 			qunlock(ums);
431*906943f9SDavid du Colombier 			return -1;
4320641ea09SDavid du Colombier 		}
433*906943f9SDavid du Colombier 		bno = offset / lun->lbsize;
434*906943f9SDavid du Colombier 		nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno;
4350641ea09SDavid du Colombier 		if(bno + nb > lun->blocks)
4360641ea09SDavid du Colombier 			nb = lun->blocks - bno;
4370641ea09SDavid du Colombier 		if(bno >= lun->blocks || nb == 0){
438*906943f9SDavid du Colombier 			count = 0;
4390641ea09SDavid du Colombier 			break;
4400641ea09SDavid du Colombier 		}
4410641ea09SDavid du Colombier 		if(nb * lun->lbsize > maxiosize)
4420641ea09SDavid du Colombier 			nb = maxiosize / lun->lbsize;
443*906943f9SDavid du Colombier 		p = emallocz(nb * lun->lbsize, 0);	/* could use a static buffer */
444*906943f9SDavid du Colombier 		lun->offset = offset / lun->lbsize;
4450641ea09SDavid du Colombier 		n = SRread(lun, p, nb * lun->lbsize);
446*906943f9SDavid du Colombier 		if(n < 0){
4470641ea09SDavid du Colombier 			free(p);
448*906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
449*906943f9SDavid du Colombier 			qunlock(ums);
450*906943f9SDavid du Colombier 			return -1;
4510641ea09SDavid du Colombier 		}
452*906943f9SDavid du Colombier 		len = count;
453*906943f9SDavid du Colombier 		off = offset % lun->lbsize;
454*906943f9SDavid du Colombier 		if(off + len > n)
455*906943f9SDavid du Colombier 			len = n - off;
456*906943f9SDavid du Colombier 		count = len;
457*906943f9SDavid du Colombier 		memmove(data, p + off, len);
4580641ea09SDavid du Colombier 		free(p);
4590641ea09SDavid du Colombier 		break;
4600641ea09SDavid du Colombier 	}
461*906943f9SDavid du Colombier 	qunlock(ums);
462*906943f9SDavid du Colombier 	return count;
4630641ea09SDavid du Colombier }
4640641ea09SDavid du Colombier 
465*906943f9SDavid du Colombier static long
466*906943f9SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
4670641ea09SDavid du Colombier {
468*906943f9SDavid du Colombier 	int bno, nb, len, off;
4690641ea09SDavid du Colombier 	ulong path;
4700641ea09SDavid du Colombier 	char *p;
471*906943f9SDavid du Colombier 	Ums *ums;
4720641ea09SDavid du Colombier 	Umsc *lun;
473*906943f9SDavid du Colombier 	char *data;
4740641ea09SDavid du Colombier 
475*906943f9SDavid du Colombier 	ums = fs->dev->aux;
476*906943f9SDavid du Colombier 	lun = fs->aux;
477*906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
478*906943f9SDavid du Colombier 	data = buf;
479*906943f9SDavid du Colombier 	qlock(ums);
480*906943f9SDavid du Colombier 	switch(path){
481*906943f9SDavid du Colombier 	default:
482*906943f9SDavid du Colombier 		qunlock(ums);
483*906943f9SDavid du Colombier 		werrstr(Eperm);
484*906943f9SDavid du Colombier 		return -1;
4850641ea09SDavid du Colombier 	case Qraw:
486*906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
487*906943f9SDavid du Colombier 			qunlock(ums);
488*906943f9SDavid du Colombier 			return -1;
4890641ea09SDavid du Colombier 		}
4900641ea09SDavid du Colombier 		switch(lun->phase){
4910641ea09SDavid du Colombier 		case Pcmd:
492*906943f9SDavid du Colombier 			if(count != 6 && count != 10){
493*906943f9SDavid du Colombier 				qunlock(ums);
494*906943f9SDavid du Colombier 				werrstr("bad command length");
495*906943f9SDavid du Colombier 				return -1;
4960641ea09SDavid du Colombier 			}
497*906943f9SDavid du Colombier 			memmove(lun->rawcmd, data, count);
4980641ea09SDavid du Colombier 			lun->cmd.p = lun->rawcmd;
499*906943f9SDavid du Colombier 			lun->cmd.count = count;
5000641ea09SDavid du Colombier 			lun->cmd.write = 1;
5010641ea09SDavid du Colombier 			lun->phase = Pdata;
5020641ea09SDavid du Colombier 			break;
5030641ea09SDavid du Colombier 		case Pdata:
504*906943f9SDavid du Colombier 			lun->data.p = (uchar*)data;
505*906943f9SDavid du Colombier 			lun->data.count = count;
5060641ea09SDavid du Colombier 			lun->data.write = 1;
507*906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
5080641ea09SDavid du Colombier 			lun->phase = Pstatus;
509*906943f9SDavid du Colombier 			if(count < 0){
510*906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
511*906943f9SDavid du Colombier 				qunlock(ums);
512*906943f9SDavid du Colombier 				return -1;
5130641ea09SDavid du Colombier 			}
5140641ea09SDavid du Colombier 			break;
5150641ea09SDavid du Colombier 		case Pstatus:
5160641ea09SDavid du Colombier 			lun->phase = Pcmd;
517*906943f9SDavid du Colombier 			qunlock(ums);
518*906943f9SDavid du Colombier 			werrstr("phase error");
519*906943f9SDavid du Colombier 			return -1;
5200641ea09SDavid du Colombier 		}
5210641ea09SDavid du Colombier 		break;
5220641ea09SDavid du Colombier 	case Qdata:
523*906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
524*906943f9SDavid du Colombier 			qunlock(ums);
525*906943f9SDavid du Colombier 			return -1;
5260641ea09SDavid du Colombier 		}
527*906943f9SDavid du Colombier 		bno = offset / lun->lbsize;
528*906943f9SDavid du Colombier 		nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno;
5290641ea09SDavid du Colombier 		if(bno + nb > lun->blocks)
5300641ea09SDavid du Colombier 			nb = lun->blocks - bno;
5310641ea09SDavid du Colombier 		if(bno >= lun->blocks || nb == 0){
532*906943f9SDavid du Colombier 			count = 0;
5330641ea09SDavid du Colombier 			break;
5340641ea09SDavid du Colombier 		}
5350641ea09SDavid du Colombier 		if(nb * lun->lbsize > maxiosize)
5360641ea09SDavid du Colombier 			nb = maxiosize / lun->lbsize;
537*906943f9SDavid du Colombier 		p = emallocz(nb * lun->lbsize, 0);
538*906943f9SDavid du Colombier 		off = offset % lun->lbsize;
539*906943f9SDavid du Colombier 		len = count;
540*906943f9SDavid du Colombier 		if(off || (len % lun->lbsize) != 0){
541*906943f9SDavid du Colombier 			lun->offset = offset / lun->lbsize;
542*906943f9SDavid du Colombier 			count = SRread(lun, p, nb * lun->lbsize);
543*906943f9SDavid du Colombier 			if(count < 0){
5440641ea09SDavid du Colombier 				free(p);
545*906943f9SDavid du Colombier 				lun->lbsize = 0;	/* medium may have changed */
546*906943f9SDavid du Colombier 				qunlock(ums);
547*906943f9SDavid du Colombier 				return -1;
5480641ea09SDavid du Colombier 			}
549*906943f9SDavid du Colombier 			if(off + len > count)
550*906943f9SDavid du Colombier 				len = count - off;
5510641ea09SDavid du Colombier 		}
552*906943f9SDavid du Colombier 		memmove(p+off, data, len);
553*906943f9SDavid du Colombier 		lun->offset = offset / lun->lbsize;
554*906943f9SDavid du Colombier 		count = SRwrite(lun, p, nb * lun->lbsize);
555*906943f9SDavid du Colombier 		if(count < 0){
5560641ea09SDavid du Colombier 			free(p);
557*906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
558*906943f9SDavid du Colombier 			qunlock(ums);
559*906943f9SDavid du Colombier 			return -1;
5600641ea09SDavid du Colombier 		}
561*906943f9SDavid du Colombier 		if(off+len > count)
562*906943f9SDavid du Colombier 			len = count - off;
563*906943f9SDavid du Colombier 		count = len;
5640641ea09SDavid du Colombier 		free(p);
5650641ea09SDavid du Colombier 		break;
5660641ea09SDavid du Colombier 	}
567*906943f9SDavid du Colombier 	qunlock(ums);
568*906943f9SDavid du Colombier 	return count;
5690641ea09SDavid du Colombier }
5700641ea09SDavid du Colombier 
571*906943f9SDavid du Colombier int
572*906943f9SDavid du Colombier findendpoints(Ums *ums)
573*906943f9SDavid du Colombier {
574*906943f9SDavid du Colombier 	Ep *ep;
575*906943f9SDavid du Colombier 	Usbdev *ud;
576*906943f9SDavid du Colombier 	ulong csp;
577*906943f9SDavid du Colombier 	ulong sc;
578*906943f9SDavid du Colombier 	int i;
579*906943f9SDavid du Colombier 	int epin, epout;
5800641ea09SDavid du Colombier 
581*906943f9SDavid du Colombier 	epin = epout = -1;
582*906943f9SDavid du Colombier 	ud = ums->dev->usb;
583*906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->ep); i++){
584*906943f9SDavid du Colombier 		if((ep = ud->ep[i]) == nil)
585*906943f9SDavid du Colombier 			continue;
586*906943f9SDavid du Colombier 		csp = ep->iface->csp;
587*906943f9SDavid du Colombier 		sc = Subclass(csp);
588*906943f9SDavid du Colombier 		if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
589*906943f9SDavid du Colombier 			continue;
590*906943f9SDavid du Colombier 		if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
591*906943f9SDavid du Colombier 			fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
592*906943f9SDavid du Colombier 		if(ep->type == Ebulk){
593*906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Ein)
594*906943f9SDavid du Colombier 				if(epin == -1)
595*906943f9SDavid du Colombier 					epin =  ep->id;
596*906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Eout)
597*906943f9SDavid du Colombier 				if(epout == -1)
598*906943f9SDavid du Colombier 					epout = ep->id;
599*906943f9SDavid du Colombier 		}
600*906943f9SDavid du Colombier 	}
601*906943f9SDavid du Colombier 	dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
602*906943f9SDavid du Colombier 	if(epin == -1 || epout == -1)
603*906943f9SDavid du Colombier 		return -1;
604*906943f9SDavid du Colombier 	ums->epin = openep(ums->dev, epin);
605*906943f9SDavid du Colombier 	if(ums->epin == nil){
606*906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epin);
607*906943f9SDavid du Colombier 		return -1;
608*906943f9SDavid du Colombier 	}
609*906943f9SDavid du Colombier 	if(epout == epin){
610*906943f9SDavid du Colombier 		incref(ums->epin);
611*906943f9SDavid du Colombier 		ums->epout = ums->epin;
612*906943f9SDavid du Colombier 	}else
613*906943f9SDavid du Colombier 		ums->epout = openep(ums->dev, epout);
614*906943f9SDavid du Colombier 	if(ums->epout == nil){
615*906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epout);
616*906943f9SDavid du Colombier 		closedev(ums->epin);
617*906943f9SDavid du Colombier 		return -1;
618*906943f9SDavid du Colombier 	}
619*906943f9SDavid du Colombier 	if(ums->epin == ums->epout)
620*906943f9SDavid du Colombier 		opendevdata(ums->epin, ORDWR);
621*906943f9SDavid du Colombier 	else{
622*906943f9SDavid du Colombier 		opendevdata(ums->epin, OREAD);
623*906943f9SDavid du Colombier 		opendevdata(ums->epout, OWRITE);
624*906943f9SDavid du Colombier 	}
625*906943f9SDavid du Colombier 	if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
626*906943f9SDavid du Colombier 		fprint(2, "disk: open i/o ep data: %r\n");
627*906943f9SDavid du Colombier 		closedev(ums->epin);
628*906943f9SDavid du Colombier 		closedev(ums->epout);
629*906943f9SDavid du Colombier 		return -1;
630*906943f9SDavid du Colombier 	}
631*906943f9SDavid du Colombier 	dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
6320641ea09SDavid du Colombier 
633*906943f9SDavid du Colombier 	if(usbdebug > 1 || diskdebug > 2){
634*906943f9SDavid du Colombier 		devctl(ums->epin, "debug 1");
635*906943f9SDavid du Colombier 		devctl(ums->epout, "debug 1");
636*906943f9SDavid du Colombier 		devctl(ums->dev, "debug 1");
637*906943f9SDavid du Colombier 	}
638*906943f9SDavid du Colombier 	return 0;
639*906943f9SDavid du Colombier }
640*906943f9SDavid du Colombier 
641*906943f9SDavid du Colombier static int
6420641ea09SDavid du Colombier usage(void)
6430641ea09SDavid du Colombier {
644*906943f9SDavid du Colombier 	werrstr("usage: usb/disk [-d]");
645*906943f9SDavid du Colombier 	return -1;
6460641ea09SDavid du Colombier }
6470641ea09SDavid du Colombier 
648*906943f9SDavid du Colombier static void
649*906943f9SDavid du Colombier umsdevfree(void *a)
6500641ea09SDavid du Colombier {
651*906943f9SDavid du Colombier 	Ums *ums = a;
6520641ea09SDavid du Colombier 
653*906943f9SDavid du Colombier 	if(ums == nil)
654*906943f9SDavid du Colombier 		return;
655*906943f9SDavid du Colombier 	closedev(ums->epin);
656*906943f9SDavid du Colombier 	closedev(ums->epout);
657*906943f9SDavid du Colombier 	ums->epin = ums->epout = nil;
658*906943f9SDavid du Colombier 	free(ums->lun);
659*906943f9SDavid du Colombier 	free(ums);
660*906943f9SDavid du Colombier }
661*906943f9SDavid du Colombier 
662*906943f9SDavid du Colombier static Usbfs diskfs = {
663*906943f9SDavid du Colombier 	.walk = dwalk,
664*906943f9SDavid du Colombier 	.open =	 dopen,
665*906943f9SDavid du Colombier 	.read =	 dread,
666*906943f9SDavid du Colombier 	.write = dwrite,
667*906943f9SDavid du Colombier 	.stat =	 dstat,
668*906943f9SDavid du Colombier };
669*906943f9SDavid du Colombier 
670*906943f9SDavid du Colombier int
671*906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv)
672*906943f9SDavid du Colombier {
673*906943f9SDavid du Colombier 	Ums *ums;
674*906943f9SDavid du Colombier 	Umsc *lun;
675*906943f9SDavid du Colombier 	int i;
6760641ea09SDavid du Colombier 
6770641ea09SDavid du Colombier 	ARGBEGIN{
6780641ea09SDavid du Colombier 	case 'd':
679*906943f9SDavid du Colombier 		scsidebug(diskdebug);
680*906943f9SDavid du Colombier 		diskdebug++;
6810641ea09SDavid du Colombier 		break;
6820641ea09SDavid du Colombier 	default:
683*906943f9SDavid du Colombier 		return usage();
6840641ea09SDavid du Colombier 	}ARGEND
685*906943f9SDavid du Colombier 	if(argc != 0)
686*906943f9SDavid du Colombier 		return usage();
6870641ea09SDavid du Colombier 
688*906943f9SDavid du Colombier 	ums = dev->aux = emallocz(sizeof(Ums), 1);
689*906943f9SDavid du Colombier 	ums->maxlun = -1;
690*906943f9SDavid du Colombier 	ums->dev = dev;
691*906943f9SDavid du Colombier 	dev->free = umsdevfree;
692*906943f9SDavid du Colombier 	if(findendpoints(ums) < 0){
693*906943f9SDavid du Colombier 		werrstr("disk: endpoints not found");
694*906943f9SDavid du Colombier 		return -1;
6950641ea09SDavid du Colombier 	}
696*906943f9SDavid du Colombier 	if(umsinit(ums) < 0){
697*906943f9SDavid du Colombier 		dprint(2, "disk: umsinit: %r\n");
698*906943f9SDavid du Colombier 		return -1;
699*906943f9SDavid du Colombier 	}
7000641ea09SDavid du Colombier 
701*906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
702*906943f9SDavid du Colombier 		lun = &ums->lun[i];
703*906943f9SDavid du Colombier 		lun->fs = diskfs;
704*906943f9SDavid du Colombier 		snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i);
705*906943f9SDavid du Colombier 		lun->fs.dev = dev;
706*906943f9SDavid du Colombier 		incref(dev);
707*906943f9SDavid du Colombier 		lun->fs.aux = lun;
708*906943f9SDavid du Colombier 		usbfsadd(&lun->fs);
709*906943f9SDavid du Colombier 	}
710*906943f9SDavid du Colombier 	closedev(dev);
711*906943f9SDavid du Colombier 	return 0;
7120641ea09SDavid du Colombier }
713