xref: /plan9/sys/src/cmd/usb/disk/disk.c (revision fd362a73ff89ae80075dd82c9aad2a3468f0f3c9)
10641ea09SDavid du Colombier /*
20641ea09SDavid du Colombier  * usb/disk - usb mass storage file server
33a827ddcSDavid du Colombier  *
43a827ddcSDavid du Colombier  * supports only the scsi command interface, not ata.
50641ea09SDavid du Colombier  */
6906943f9SDavid du Colombier 
70641ea09SDavid du Colombier #include <u.h>
80641ea09SDavid du Colombier #include <libc.h>
90641ea09SDavid du Colombier #include <ctype.h>
100641ea09SDavid du Colombier #include <fcall.h>
110641ea09SDavid du Colombier #include <thread.h>
12*fd362a73SDavid du Colombier #include <disk.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,
37d5789509SDavid du Colombier 	[Qctl]	"ctl",	0664,		/* nothing secret here */
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;
460641ea09SDavid du Colombier 
477d7728c9SDavid du Colombier int diskdebug;
480641ea09SDavid du Colombier 
493a827ddcSDavid du Colombier static void
ding(void *,char * msg)503a827ddcSDavid du Colombier ding(void *, char *msg)
513a827ddcSDavid du Colombier {
523a827ddcSDavid du Colombier 	if(strstr(msg, "alarm") != nil)
533a827ddcSDavid du Colombier 		noted(NCONT);
543a827ddcSDavid du Colombier 	noted(NDFLT);
553a827ddcSDavid du Colombier }
563a827ddcSDavid du Colombier 
57906943f9SDavid du Colombier static int
getmaxlun(Dev * dev)58906943f9SDavid du Colombier getmaxlun(Dev *dev)
590641ea09SDavid du Colombier {
600641ea09SDavid du Colombier 	uchar max;
61906943f9SDavid du Colombier 	int r;
620641ea09SDavid du Colombier 
636a5dc222SDavid du Colombier 	max = 0;
64906943f9SDavid du Colombier 	r = Rd2h|Rclass|Riface;
65906943f9SDavid du Colombier 	if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
66906943f9SDavid du Colombier 		dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
6714dd39c1SDavid du Colombier 	}else{
6814dd39c1SDavid du Colombier 		max &= 017;			/* 15 is the max. allowed */
69906943f9SDavid du Colombier 		dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
7014dd39c1SDavid du Colombier 	}
71906943f9SDavid du Colombier 	return max;
720641ea09SDavid du Colombier }
730641ea09SDavid du Colombier 
74906943f9SDavid du Colombier static int
umsreset(Ums * ums)75906943f9SDavid du Colombier umsreset(Ums *ums)
760641ea09SDavid du Colombier {
77906943f9SDavid du Colombier 	int r;
780641ea09SDavid du Colombier 
79906943f9SDavid du Colombier 	r = Rh2d|Rclass|Riface;
80906943f9SDavid du Colombier 	if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
81906943f9SDavid du Colombier 		fprint(2, "disk: reset: %r\n");
820641ea09SDavid du Colombier 		return -1;
830641ea09SDavid du Colombier 	}
84906943f9SDavid du Colombier 	return 0;
850641ea09SDavid du Colombier }
860641ea09SDavid du Colombier 
87906943f9SDavid du Colombier static int
umsrecover(Ums * ums)88906943f9SDavid du Colombier umsrecover(Ums *ums)
89906943f9SDavid du Colombier {
90906943f9SDavid du Colombier 	if(umsreset(ums) < 0)
910641ea09SDavid du Colombier 		return -1;
92906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epin, Ein) < 0)
93906943f9SDavid du Colombier 		dprint(2, "disk: unstall epin: %r\n");
94906943f9SDavid du Colombier 
95906943f9SDavid du Colombier 	/* do we need this when epin == epout? */
96906943f9SDavid du Colombier 	if(unstall(ums->dev, ums->epout, Eout) < 0)
97906943f9SDavid du Colombier 		dprint(2, "disk: unstall epout: %r\n");
98906943f9SDavid du Colombier 	return 0;
99906943f9SDavid du Colombier }
100906943f9SDavid du Colombier 
101906943f9SDavid du Colombier static void
umsfatal(Ums * ums)102906943f9SDavid du Colombier umsfatal(Ums *ums)
103906943f9SDavid du Colombier {
104906943f9SDavid du Colombier 	int i;
105906943f9SDavid du Colombier 
106906943f9SDavid du Colombier 	devctl(ums->dev, "detach");
107906943f9SDavid du Colombier 	for(i = 0; i < ums->maxlun; i++)
108906943f9SDavid du Colombier 		usbfsdel(&ums->lun[i].fs);
109906943f9SDavid du Colombier }
110906943f9SDavid du Colombier 
111906943f9SDavid du Colombier static int
ispow2(uvlong ul)1123a827ddcSDavid du Colombier ispow2(uvlong ul)
1133a827ddcSDavid du Colombier {
1143a827ddcSDavid du Colombier 	return (ul & (ul - 1)) == 0;
1153a827ddcSDavid du Colombier }
1163a827ddcSDavid du Colombier 
1173a827ddcSDavid du Colombier /*
1183a827ddcSDavid du Colombier  * return smallest power of 2 >= n
1193a827ddcSDavid du Colombier  */
1203a827ddcSDavid du Colombier static int
log2(int n)1213a827ddcSDavid du Colombier log2(int n)
1223a827ddcSDavid du Colombier {
1233a827ddcSDavid du Colombier 	int i;
1243a827ddcSDavid du Colombier 
1253a827ddcSDavid du Colombier 	for(i = 0; (1 << i) < n; i++)
1263a827ddcSDavid du Colombier 		;
1273a827ddcSDavid du Colombier 	return i;
1283a827ddcSDavid du Colombier }
1293a827ddcSDavid du Colombier 
1303a827ddcSDavid du Colombier static int
umscapacity(Umsc * lun)131906943f9SDavid du Colombier umscapacity(Umsc *lun)
132906943f9SDavid du Colombier {
133906943f9SDavid du Colombier 	uchar data[32];
134906943f9SDavid du Colombier 
1350641ea09SDavid du Colombier 	lun->blocks = 0;
1360641ea09SDavid du Colombier 	lun->capacity = 0;
1370641ea09SDavid du Colombier 	lun->lbsize = 0;
1387d7728c9SDavid du Colombier 	memset(data, 0, sizeof data);
139906943f9SDavid du Colombier 	if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data)  < 0)
140906943f9SDavid du Colombier 		return -1;
141906943f9SDavid du Colombier 	lun->blocks = GETBELONG(data);
142906943f9SDavid du Colombier 	lun->lbsize = GETBELONG(data+4);
143906943f9SDavid du Colombier 	if(lun->blocks == 0xFFFFFFFF){
144906943f9SDavid du Colombier 		if(SRrcapacity16(lun, data) < 0){
145906943f9SDavid du Colombier 			lun->lbsize = 0;
146906943f9SDavid du Colombier 			lun->blocks = 0;
147906943f9SDavid du Colombier 			return -1;
1480641ea09SDavid du Colombier 		}else{
149906943f9SDavid du Colombier 			lun->lbsize = GETBELONG(data + 8);
1507d7728c9SDavid du Colombier 			lun->blocks = (uvlong)GETBELONG(data)<<32 |
1517d7728c9SDavid du Colombier 				GETBELONG(data + 4);
152906943f9SDavid du Colombier 		}
153906943f9SDavid du Colombier 	}
1540641ea09SDavid du Colombier 	lun->blocks++; /* SRcapacity returns LBA of last block */
1550641ea09SDavid du Colombier 	lun->capacity = (vlong)lun->blocks * lun->lbsize;
1567d7728c9SDavid du Colombier 	if(diskdebug)
1577d7728c9SDavid du Colombier 		fprint(2, "disk: logical block size %lud, # blocks %llud\n",
1587d7728c9SDavid du Colombier 			lun->lbsize, lun->blocks);
159906943f9SDavid du Colombier 	return 0;
1600641ea09SDavid du Colombier }
161906943f9SDavid du Colombier 
162906943f9SDavid du Colombier static int
umsinit(Ums * ums)163906943f9SDavid du Colombier umsinit(Ums *ums)
164906943f9SDavid du Colombier {
165906943f9SDavid du Colombier 	uchar i;
166906943f9SDavid du Colombier 	Umsc *lun;
167906943f9SDavid du Colombier 	int some;
168906943f9SDavid du Colombier 
169906943f9SDavid du Colombier 	umsreset(ums);
170906943f9SDavid du Colombier 	ums->maxlun = getmaxlun(ums->dev);
171906943f9SDavid du Colombier 	ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
172906943f9SDavid du Colombier 	some = 0;
173906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
174906943f9SDavid du Colombier 		lun = &ums->lun[i];
175906943f9SDavid du Colombier 		lun->ums = ums;
176906943f9SDavid du Colombier 		lun->umsc = lun;
177906943f9SDavid du Colombier 		lun->lun = i;
178906943f9SDavid du Colombier 		lun->flags = Fopen | Fusb | Frw10;
179d5789509SDavid du Colombier 		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
180d5789509SDavid du Colombier 			dprint(2, "disk: lun %d inquiry failed\n", i);
181906943f9SDavid du Colombier 			continue;
182d5789509SDavid du Colombier 		}
183d5789509SDavid du Colombier 		switch(lun->inquiry[0]){
184d5789509SDavid du Colombier 		case Devdir:
185d5789509SDavid du Colombier 		case Devworm:		/* a little different than the others */
186d5789509SDavid du Colombier 		case Devcd:
187d5789509SDavid du Colombier 		case Devmo:
188d5789509SDavid du Colombier 			break;
189d5789509SDavid du Colombier 		default:
190d5789509SDavid du Colombier 			fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
191d5789509SDavid du Colombier 				i, lun->inquiry[0]);
192906943f9SDavid du Colombier 			continue;
193906943f9SDavid du Colombier 		}
194906943f9SDavid du Colombier 		SRstart(lun, 1);
195906943f9SDavid du Colombier 		/*
196906943f9SDavid du Colombier 		 * we ignore the device type reported by inquiry.
197906943f9SDavid du Colombier 		 * Some devices return a wrong value but would still work.
198906943f9SDavid du Colombier 		 */
199906943f9SDavid du Colombier 		some++;
200906943f9SDavid du Colombier 		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
201906943f9SDavid du Colombier 		umscapacity(lun);
202906943f9SDavid du Colombier 	}
203906943f9SDavid du Colombier 	if(some == 0){
204d5789509SDavid du Colombier 		dprint(2, "disk: all luns failed\n");
205906943f9SDavid du Colombier 		devctl(ums->dev, "detach");
206906943f9SDavid du Colombier 		return -1;
2070641ea09SDavid du Colombier 	}
2080641ea09SDavid du Colombier 	return 0;
2090641ea09SDavid du Colombier }
2100641ea09SDavid du Colombier 
2110641ea09SDavid du Colombier 
212906943f9SDavid du Colombier /*
213906943f9SDavid du Colombier  * called by SR*() commands provided by scuzz's scsireq
214906943f9SDavid du Colombier  */
2150641ea09SDavid du Colombier long
umsrequest(Umsc * umsc,ScsiPtr * cmd,ScsiPtr * data,int * status)2160641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
2170641ea09SDavid du Colombier {
2180641ea09SDavid du Colombier 	Cbw cbw;
2190641ea09SDavid du Colombier 	Csw csw;
2203a827ddcSDavid du Colombier 	int n, nio, left;
221906943f9SDavid du Colombier 	Ums *ums;
222906943f9SDavid du Colombier 
223906943f9SDavid du Colombier 	ums = umsc->ums;
2240641ea09SDavid du Colombier 
2250641ea09SDavid du Colombier 	memcpy(cbw.signature, "USBC", 4);
226906943f9SDavid du Colombier 	cbw.tag = ++ums->seq;
2270641ea09SDavid du Colombier 	cbw.datalen = data->count;
2280641ea09SDavid du Colombier 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
2290641ea09SDavid du Colombier 	cbw.lun = umsc->lun;
230906943f9SDavid du Colombier 	if(cmd->count < 1 || cmd->count > 16)
2312806980eSDavid du Colombier 		fprint(2, "disk: umsrequest: bad cmd count: %ld\n", cmd->count);
232906943f9SDavid du Colombier 
2330641ea09SDavid du Colombier 	cbw.len = cmd->count;
234906943f9SDavid du Colombier 	assert(cmd->count <= sizeof(cbw.command));
2350641ea09SDavid du Colombier 	memcpy(cbw.command, cmd->p, cmd->count);
2360641ea09SDavid du Colombier 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
2370641ea09SDavid du Colombier 
238906943f9SDavid du Colombier 	werrstr("");		/* we use %r later even for n == 0 */
239906943f9SDavid du Colombier 	if(diskdebug){
240906943f9SDavid du Colombier 		fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
2410641ea09SDavid du Colombier 		for(n = 0; n < cbw.len; n++)
2420641ea09SDavid du Colombier 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
2430641ea09SDavid du Colombier 		fprint(2, " datalen: %ld\n", cbw.datalen);
2440641ea09SDavid du Colombier 	}
2453a827ddcSDavid du Colombier 
2463a827ddcSDavid du Colombier 	/* issue tunnelled scsi command */
247906943f9SDavid du Colombier 	if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
248906943f9SDavid du Colombier 		fprint(2, "disk: cmd: %r\n");
249906943f9SDavid du Colombier 		goto Fail;
2500641ea09SDavid du Colombier 	}
2513a827ddcSDavid du Colombier 
2523a827ddcSDavid du Colombier 	/* transfer the data */
253d5789509SDavid du Colombier 	nio = data->count;
2543a827ddcSDavid du Colombier 	if(nio != 0){
2550641ea09SDavid du Colombier 		if(data->write)
2563a827ddcSDavid du Colombier 			n = write(ums->epout->dfd, data->p, nio);
257906943f9SDavid du Colombier 		else{
2583a827ddcSDavid du Colombier 			n = read(ums->epin->dfd, data->p, nio);
2593a827ddcSDavid du Colombier 			left = nio - n;
2603a827ddcSDavid du Colombier 			if (n >= 0 && left > 0)	/* didn't fill data->p? */
2613a827ddcSDavid du Colombier 				memset(data->p + n, 0, left);
2620641ea09SDavid du Colombier 		}
2633a827ddcSDavid du Colombier 		nio = n;
264906943f9SDavid du Colombier 		if(diskdebug)
265906943f9SDavid du Colombier 			if(n < 0)
266906943f9SDavid du Colombier 				fprint(2, "disk: data: %r\n");
267906943f9SDavid du Colombier 			else
268906943f9SDavid du Colombier 				fprint(2, "disk: data: %d bytes\n", n);
269906943f9SDavid du Colombier 		if(n <= 0)
270906943f9SDavid du Colombier 			if(data->write == 0)
271906943f9SDavid du Colombier 				unstall(ums->dev, ums->epin, Ein);
2720641ea09SDavid du Colombier 	}
2733a827ddcSDavid du Colombier 
2743a827ddcSDavid du Colombier 	/* read the transfer's status */
275906943f9SDavid du Colombier 	n = read(ums->epin->dfd, &csw, CswLen);
276906943f9SDavid du Colombier 	if(n <= 0){
277906943f9SDavid du Colombier 		/* n == 0 means "stalled" */
278906943f9SDavid du Colombier 		unstall(ums->dev, ums->epin, Ein);
279906943f9SDavid du Colombier 		n = read(ums->epin->dfd, &csw, CswLen);
2800641ea09SDavid du Colombier 	}
2813a827ddcSDavid du Colombier 
2820641ea09SDavid du Colombier 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
283906943f9SDavid du Colombier 		dprint(2, "disk: read n=%d: status: %r\n", n);
284906943f9SDavid du Colombier 		goto Fail;
2850641ea09SDavid du Colombier 	}
2860641ea09SDavid du Colombier 	if(csw.tag != cbw.tag){
287906943f9SDavid du Colombier 		dprint(2, "disk: status tag mismatch\n");
288906943f9SDavid du Colombier 		goto Fail;
2890641ea09SDavid du Colombier 	}
2900641ea09SDavid du Colombier 	if(csw.status >= CswPhaseErr){
291906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
292906943f9SDavid du Colombier 		goto Fail;
2930641ea09SDavid du Colombier 	}
294d5789509SDavid du Colombier 	if(csw.dataresidue == 0 || ums->wrongresidues)
295d5789509SDavid du Colombier 		csw.dataresidue = data->count - nio;
296906943f9SDavid du Colombier 	if(diskdebug){
297d5789509SDavid du Colombier 		fprint(2, "disk: status: %2.2ux residue: %ld\n",
2980641ea09SDavid du Colombier 			csw.status, csw.dataresidue);
2990641ea09SDavid du Colombier 		if(cbw.command[0] == ScmdRsense){
3000641ea09SDavid du Colombier 			fprint(2, "sense data:");
3010641ea09SDavid du Colombier 			for(n = 0; n < data->count - csw.dataresidue; n++)
3020641ea09SDavid du Colombier 				fprint(2, " %2.2x", data->p[n]);
3030641ea09SDavid du Colombier 			fprint(2, "\n");
3040641ea09SDavid du Colombier 		}
3050641ea09SDavid du Colombier 	}
306906943f9SDavid du Colombier 	switch(csw.status){
307906943f9SDavid du Colombier 	case CswOk:
3080641ea09SDavid du Colombier 		*status = STok;
309906943f9SDavid du Colombier 		break;
310906943f9SDavid du Colombier 	case CswFailed:
3110641ea09SDavid du Colombier 		*status = STcheck;
312906943f9SDavid du Colombier 		break;
313906943f9SDavid du Colombier 	default:
314906943f9SDavid du Colombier 		dprint(2, "disk: phase error\n");
315906943f9SDavid du Colombier 		goto Fail;
316906943f9SDavid du Colombier 	}
317906943f9SDavid du Colombier 	ums->nerrs = 0;
3180641ea09SDavid du Colombier 	return data->count - csw.dataresidue;
3190641ea09SDavid du Colombier 
320906943f9SDavid du Colombier Fail:
3210641ea09SDavid du Colombier 	*status = STharderr;
322906943f9SDavid du Colombier 	if(ums->nerrs++ > 15){
323906943f9SDavid du Colombier 		fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
324906943f9SDavid du Colombier 		umsfatal(ums);
3250641ea09SDavid du Colombier 	}else
326906943f9SDavid du Colombier 		umsrecover(ums);
3270641ea09SDavid du Colombier 	return -1;
3280641ea09SDavid du Colombier }
3290641ea09SDavid du Colombier 
330906943f9SDavid du Colombier static int
dwalk(Usbfs * fs,Fid * fid,char * name)331906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name)
3320641ea09SDavid du Colombier {
333906943f9SDavid du Colombier 	int i;
334906943f9SDavid du Colombier 	Qid qid;
335906943f9SDavid du Colombier 
336906943f9SDavid du Colombier 	qid = fid->qid;
337906943f9SDavid du Colombier 	if((qid.type & QTDIR) == 0){
338906943f9SDavid du Colombier 		werrstr("walk in non-directory");
339906943f9SDavid du Colombier 		return -1;
3400641ea09SDavid du Colombier 	}
3410641ea09SDavid du Colombier 
3420641ea09SDavid du Colombier 	if(strcmp(name, "..") == 0)
343906943f9SDavid du Colombier 		return 0;
3440641ea09SDavid du Colombier 
345906943f9SDavid du Colombier 	for(i = 1; i < nelem(dirtab); i++)
3460641ea09SDavid du Colombier 		if(strcmp(name, dirtab[i].name) == 0){
347906943f9SDavid du Colombier 			qid.path = i | fs->qid;
348906943f9SDavid du Colombier 			qid.vers = 0;
349906943f9SDavid du Colombier 			qid.type = dirtab[i].mode >> 24;
350906943f9SDavid du Colombier 			fid->qid = qid;
351906943f9SDavid du Colombier 			return 0;
3520641ea09SDavid du Colombier 		}
353906943f9SDavid du Colombier 	werrstr(Enotfound);
354906943f9SDavid du Colombier 	return -1;
3550641ea09SDavid du Colombier }
3560641ea09SDavid du Colombier 
357906943f9SDavid du Colombier static void
dostat(Usbfs * fs,int path,Dir * d)358906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d)
3590641ea09SDavid du Colombier {
3600641ea09SDavid du Colombier 	Dirtab *t;
3610641ea09SDavid du Colombier 	Umsc *lun;
3620641ea09SDavid du Colombier 
363906943f9SDavid du Colombier 	t = &dirtab[path];
364906943f9SDavid du Colombier 	d->qid.path = path;
365906943f9SDavid du Colombier 	d->qid.type = t->mode >> 24;
366906943f9SDavid du Colombier 	d->mode = t->mode;
367906943f9SDavid du Colombier 	d->name = t->name;
368906943f9SDavid du Colombier 	lun = fs->aux;
369906943f9SDavid du Colombier 	if(path == Qdata)
370906943f9SDavid du Colombier 		d->length = lun->capacity;
371906943f9SDavid du Colombier 	else
372906943f9SDavid du Colombier 		d->length = 0;
373906943f9SDavid du Colombier }
374906943f9SDavid du Colombier 
375906943f9SDavid du Colombier static int
dirgen(Usbfs * fs,Qid,int i,Dir * d,void *)376906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
377906943f9SDavid du Colombier {
378906943f9SDavid du Colombier 	i++;	/* skip dir */
379906943f9SDavid du Colombier 	if(i >= Qmax)
380906943f9SDavid du Colombier 		return -1;
381906943f9SDavid du Colombier 	else{
382906943f9SDavid du Colombier 		dostat(fs, i, d);
383906943f9SDavid du Colombier 		d->qid.path |= fs->qid;
384906943f9SDavid du Colombier 		return 0;
385906943f9SDavid du Colombier 	}
386906943f9SDavid du Colombier }
387906943f9SDavid du Colombier 
388906943f9SDavid du Colombier static int
dstat(Usbfs * fs,Qid qid,Dir * d)389906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d)
390906943f9SDavid du Colombier {
391906943f9SDavid du Colombier 	int path;
392906943f9SDavid du Colombier 
393906943f9SDavid du Colombier 	path = qid.path & ~fs->qid;
394906943f9SDavid du Colombier 	dostat(fs, path, d);
395906943f9SDavid du Colombier 	d->qid.path |= fs->qid;
396906943f9SDavid du Colombier 	return 0;
397906943f9SDavid du Colombier }
398906943f9SDavid du Colombier 
399906943f9SDavid du Colombier static int
dopen(Usbfs * fs,Fid * fid,int)400906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int)
401906943f9SDavid du Colombier {
402906943f9SDavid du Colombier 	ulong path;
403906943f9SDavid du Colombier 	Umsc *lun;
404906943f9SDavid du Colombier 
405906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
406906943f9SDavid du Colombier 	lun = fs->aux;
407906943f9SDavid du Colombier 	switch(path){
408906943f9SDavid du Colombier 	case Qraw:
409906943f9SDavid du Colombier 		lun->phase = Pcmd;
4100641ea09SDavid du Colombier 		break;
411906943f9SDavid du Colombier 	}
412906943f9SDavid du Colombier 	return 0;
413906943f9SDavid du Colombier }
414906943f9SDavid du Colombier 
415906943f9SDavid du Colombier /*
4163a827ddcSDavid du Colombier  * check i/o parameters and compute values needed later.
4173a827ddcSDavid du Colombier  * we shift & mask manually to avoid run-time calls to _divv and _modv,
4183a827ddcSDavid du Colombier  * since we don't need general division nor its cost.
4193a827ddcSDavid du Colombier  */
4203a827ddcSDavid du Colombier static int
setup(Umsc * lun,char * data,int count,vlong offset)4213a827ddcSDavid du Colombier setup(Umsc *lun, char *data, int count, vlong offset)
4223a827ddcSDavid du Colombier {
4233a827ddcSDavid du Colombier 	long nb, lbsize, lbshift, lbmask;
4243a827ddcSDavid du Colombier 	uvlong bno;
4253a827ddcSDavid du Colombier 
4263a827ddcSDavid du Colombier 	if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
4273a827ddcSDavid du Colombier 	    lun->lbsize == 0)
4283a827ddcSDavid du Colombier 		return -1;
4293a827ddcSDavid du Colombier 	lbsize = lun->lbsize;
4303a827ddcSDavid du Colombier 	assert(ispow2(lbsize));
4313a827ddcSDavid du Colombier 	lbshift = log2(lbsize);
4323a827ddcSDavid du Colombier 	lbmask = lbsize - 1;
4333a827ddcSDavid du Colombier 
4343a827ddcSDavid du Colombier 	bno = offset >> lbshift;	/* offset / lbsize */
4353a827ddcSDavid du Colombier 	nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
4363a827ddcSDavid du Colombier 
4373a827ddcSDavid du Colombier 	if(bno + nb > lun->blocks)		/* past end of device? */
4383a827ddcSDavid du Colombier 		nb = lun->blocks - bno;
4393a827ddcSDavid du Colombier 	if(nb * lbsize > Maxiosize)
4403a827ddcSDavid du Colombier 		nb = Maxiosize / lbsize;
4413a827ddcSDavid du Colombier 	lun->nb = nb;
4423a827ddcSDavid du Colombier 	if(bno >= lun->blocks || nb == 0)
4433a827ddcSDavid du Colombier 		return 0;
4443a827ddcSDavid du Colombier 
4453a827ddcSDavid du Colombier 	lun->offset = bno;
4463a827ddcSDavid du Colombier 	lun->off = offset & lbmask;		/* offset % lbsize */
4473a827ddcSDavid du Colombier 	if(lun->off == 0 && (count & lbmask) == 0)
4483a827ddcSDavid du Colombier 		lun->bufp = data;
4493a827ddcSDavid du Colombier 	else
4503a827ddcSDavid du Colombier 		/* not transferring full, aligned blocks; need intermediary */
4513a827ddcSDavid du Colombier 		lun->bufp = lun->buf;
4523a827ddcSDavid du Colombier 	return count;
4533a827ddcSDavid du Colombier }
4543a827ddcSDavid du Colombier 
4553a827ddcSDavid du Colombier /*
456906943f9SDavid du Colombier  * Upon SRread/SRwrite errors we assume the medium may have changed,
457906943f9SDavid du Colombier  * and ask again for the capacity of the media.
458194f7e8cSDavid du Colombier  * BUG: How to proceed to avoid confusing dossrv??
459194f7e8cSDavid du Colombier  *
460194f7e8cSDavid du Colombier  * ctl reads must match the format documented in sd(3) exactly
461194f7e8cSDavid du Colombier  * to interoperate with the rest of the system.
462906943f9SDavid du Colombier  */
463906943f9SDavid du Colombier static long
dread(Usbfs * fs,Fid * fid,void * data,long count,vlong offset)464906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
465906943f9SDavid du Colombier {
4663a827ddcSDavid du Colombier 	long n;
467906943f9SDavid du Colombier 	ulong path;
4683a827ddcSDavid du Colombier 	char buf[1024];
469ed868a7cSDavid du Colombier 	char *s, *e;
470906943f9SDavid du Colombier 	Umsc *lun;
471906943f9SDavid du Colombier 	Ums *ums;
472906943f9SDavid du Colombier 	Qid q;
473906943f9SDavid du Colombier 
474906943f9SDavid du Colombier 	q = fid->qid;
475906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
476906943f9SDavid du Colombier 	ums = fs->dev->aux;
477906943f9SDavid du Colombier 	lun = fs->aux;
4783a827ddcSDavid du Colombier 
479906943f9SDavid du Colombier 	qlock(ums);
480906943f9SDavid du Colombier 	switch(path){
481906943f9SDavid du Colombier 	case Qdir:
482906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
4830641ea09SDavid du Colombier 		break;
4840641ea09SDavid du Colombier 	case Qctl:
4853e33a36fSDavid du Colombier 		/*
4863e33a36fSDavid du Colombier 		 * Some usb disks need an extra opportunity to divulge their
4873e33a36fSDavid du Colombier 		 * capacity (e.g. M-Systems/SanDisk 1GB flash drive).
4883e33a36fSDavid du Colombier 		 */
4893e33a36fSDavid du Colombier 		if(lun->lbsize <= 0)
4903e33a36fSDavid du Colombier 			umscapacity(lun);
4913e33a36fSDavid du Colombier 
492194f7e8cSDavid du Colombier 		s = buf;
493906943f9SDavid du Colombier 		e = buf + sizeof(buf);
4940641ea09SDavid du Colombier 		if(lun->flags & Finqok)
495194f7e8cSDavid du Colombier 			s = seprint(s, e, "inquiry %s lun %ld: %s\n",
496194f7e8cSDavid du Colombier 				fs->dev->dir, lun - &ums->lun[0], lun->inq);
4970641ea09SDavid du Colombier 		if(lun->blocks > 0)
498194f7e8cSDavid du Colombier 			s = seprint(s, e, "geometry %llud %ld\n",
4993a827ddcSDavid du Colombier 				lun->blocks, lun->lbsize);
500906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
5010641ea09SDavid du Colombier 		break;
5020641ea09SDavid du Colombier 	case Qraw:
503906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
5043a827ddcSDavid du Colombier 			count = -1;
5053a827ddcSDavid du Colombier 			break;
5060641ea09SDavid du Colombier 		}
5070641ea09SDavid du Colombier 		switch(lun->phase){
5080641ea09SDavid du Colombier 		case Pcmd:
509906943f9SDavid du Colombier 			qunlock(ums);
510906943f9SDavid du Colombier 			werrstr("phase error");
511906943f9SDavid du Colombier 			return -1;
5120641ea09SDavid du Colombier 		case Pdata:
5133a827ddcSDavid du Colombier 			lun->data.p = data;
514906943f9SDavid du Colombier 			lun->data.count = count;
5150641ea09SDavid du Colombier 			lun->data.write = 0;
516906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
5170641ea09SDavid du Colombier 			lun->phase = Pstatus;
5183a827ddcSDavid du Colombier 			if(count < 0)
519906943f9SDavid du Colombier 				lun->lbsize = 0;  /* medium may have changed */
5200641ea09SDavid du Colombier 			break;
5210641ea09SDavid du Colombier 		case Pstatus:
5220641ea09SDavid du Colombier 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
523906943f9SDavid du Colombier 			count = usbreadbuf(data, count, 0LL, buf, n);
5240641ea09SDavid du Colombier 			lun->phase = Pcmd;
5250641ea09SDavid du Colombier 			break;
5260641ea09SDavid du Colombier 		}
5270641ea09SDavid du Colombier 		break;
5280641ea09SDavid du Colombier 	case Qdata:
5293a827ddcSDavid du Colombier 		count = setup(lun, data, count, offset);
5303a827ddcSDavid du Colombier 		if (count <= 0)
5310641ea09SDavid du Colombier 			break;
5323a827ddcSDavid du Colombier 		n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
533906943f9SDavid du Colombier 		if(n < 0){
534906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
5353a827ddcSDavid du Colombier 			count = -1;
5363a827ddcSDavid du Colombier 		} else if (lun->bufp == data)
5373a827ddcSDavid du Colombier 			count = n;
5383a827ddcSDavid du Colombier 		else{
5393a827ddcSDavid du Colombier 			/*
5403a827ddcSDavid du Colombier 			 * if n == lun->nb*lun->lbsize (as expected),
5413a827ddcSDavid du Colombier 			 * just copy count bytes.
5423a827ddcSDavid du Colombier 			 */
5433a827ddcSDavid du Colombier 			if(lun->off + count > n)
5443a827ddcSDavid du Colombier 				count = n - lun->off; /* short read */
5453a827ddcSDavid du Colombier 			if(count > 0)
5463a827ddcSDavid du Colombier 				memmove(data, lun->bufp + lun->off, count);
5470641ea09SDavid du Colombier 		}
5480641ea09SDavid du Colombier 		break;
5490641ea09SDavid du Colombier 	}
550906943f9SDavid du Colombier 	qunlock(ums);
551906943f9SDavid du Colombier 	return count;
5520641ea09SDavid du Colombier }
5530641ea09SDavid du Colombier 
554906943f9SDavid du Colombier static long
dwrite(Usbfs * fs,Fid * fid,void * data,long count,vlong offset)5553a827ddcSDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
5560641ea09SDavid du Colombier {
5573a827ddcSDavid du Colombier 	long len, ocount;
5580641ea09SDavid du Colombier 	ulong path;
5593a827ddcSDavid du Colombier 	uvlong bno;
560906943f9SDavid du Colombier 	Ums *ums;
5610641ea09SDavid du Colombier 	Umsc *lun;
5620641ea09SDavid du Colombier 
563906943f9SDavid du Colombier 	ums = fs->dev->aux;
564906943f9SDavid du Colombier 	lun = fs->aux;
565906943f9SDavid du Colombier 	path = fid->qid.path & ~fs->qid;
5663a827ddcSDavid du Colombier 
567906943f9SDavid du Colombier 	qlock(ums);
568906943f9SDavid du Colombier 	switch(path){
569906943f9SDavid du Colombier 	default:
570906943f9SDavid du Colombier 		werrstr(Eperm);
5713a827ddcSDavid du Colombier 		count = -1;
5723a827ddcSDavid du Colombier 		break;
573d5789509SDavid du Colombier 	case Qctl:
574d5789509SDavid du Colombier 		dprint(2, "usb/disk: ctl ignored\n");
5753a827ddcSDavid du Colombier 		break;
5760641ea09SDavid du Colombier 	case Qraw:
577906943f9SDavid du Colombier 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
5783a827ddcSDavid du Colombier 			count = -1;
5793a827ddcSDavid du Colombier 			break;
5800641ea09SDavid du Colombier 		}
5810641ea09SDavid du Colombier 		switch(lun->phase){
5820641ea09SDavid du Colombier 		case Pcmd:
583906943f9SDavid du Colombier 			if(count != 6 && count != 10){
584906943f9SDavid du Colombier 				qunlock(ums);
585906943f9SDavid du Colombier 				werrstr("bad command length");
586906943f9SDavid du Colombier 				return -1;
5870641ea09SDavid du Colombier 			}
588906943f9SDavid du Colombier 			memmove(lun->rawcmd, data, count);
5890641ea09SDavid du Colombier 			lun->cmd.p = lun->rawcmd;
590906943f9SDavid du Colombier 			lun->cmd.count = count;
5910641ea09SDavid du Colombier 			lun->cmd.write = 1;
5920641ea09SDavid du Colombier 			lun->phase = Pdata;
5930641ea09SDavid du Colombier 			break;
5940641ea09SDavid du Colombier 		case Pdata:
5953a827ddcSDavid du Colombier 			lun->data.p = data;
596906943f9SDavid du Colombier 			lun->data.count = count;
5970641ea09SDavid du Colombier 			lun->data.write = 1;
598906943f9SDavid du Colombier 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
5990641ea09SDavid du Colombier 			lun->phase = Pstatus;
6003a827ddcSDavid du Colombier 			if(count < 0)
601906943f9SDavid du Colombier 				lun->lbsize = 0;  /* medium may have changed */
6020641ea09SDavid du Colombier 			break;
6030641ea09SDavid du Colombier 		case Pstatus:
6040641ea09SDavid du Colombier 			lun->phase = Pcmd;
605906943f9SDavid du Colombier 			werrstr("phase error");
6063a827ddcSDavid du Colombier 			count = -1;
6073a827ddcSDavid du Colombier 			break;
6080641ea09SDavid du Colombier 		}
6090641ea09SDavid du Colombier 		break;
6100641ea09SDavid du Colombier 	case Qdata:
6113a827ddcSDavid du Colombier 		len = ocount = count;
6123a827ddcSDavid du Colombier 		count = setup(lun, data, count, offset);
6133a827ddcSDavid du Colombier 		if (count <= 0)
6143a827ddcSDavid du Colombier 			break;
6153a827ddcSDavid du Colombier 		bno = lun->offset;
6163a827ddcSDavid du Colombier 		if (lun->bufp == lun->buf) {
6173a827ddcSDavid du Colombier 			count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
6183a827ddcSDavid du Colombier 			if(count < 0) {
6193a827ddcSDavid du Colombier 				lun->lbsize = 0;  /* medium may have changed */
6200641ea09SDavid du Colombier 				break;
6210641ea09SDavid du Colombier 			}
6223a827ddcSDavid du Colombier 			/*
6233a827ddcSDavid du Colombier 			 * if count == lun->nb*lun->lbsize, as expected, just
6243a827ddcSDavid du Colombier 			 * copy len (the original count) bytes of user data.
6253a827ddcSDavid du Colombier 			 */
6263a827ddcSDavid du Colombier 			if(lun->off + len > count)
6273a827ddcSDavid du Colombier 				len = count - lun->off; /* short read */
6283a827ddcSDavid du Colombier 			if(len > 0)
6293a827ddcSDavid du Colombier 				memmove(lun->bufp + lun->off, data, len);
6303a827ddcSDavid du Colombier 		}
6313a827ddcSDavid du Colombier 
6323a827ddcSDavid du Colombier 		lun->offset = bno;
6333a827ddcSDavid du Colombier 		count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
6343a827ddcSDavid du Colombier 		if(count < 0)
635906943f9SDavid du Colombier 			lun->lbsize = 0;	/* medium may have changed */
6363a827ddcSDavid du Colombier 		else{
6373a827ddcSDavid du Colombier 			if(lun->off + len > count)
6383a827ddcSDavid du Colombier 				count -= lun->off; /* short write */
6393a827ddcSDavid du Colombier 			/* never report more bytes written than requested */
6403a827ddcSDavid du Colombier 			if(count < 0)
6413a827ddcSDavid du Colombier 				count = 0;
6423a827ddcSDavid du Colombier 			else if(count > ocount)
6433a827ddcSDavid du Colombier 				count = ocount;
6440641ea09SDavid du Colombier 		}
6450641ea09SDavid du Colombier 		break;
6460641ea09SDavid du Colombier 	}
647906943f9SDavid du Colombier 	qunlock(ums);
648906943f9SDavid du Colombier 	return count;
6490641ea09SDavid du Colombier }
6500641ea09SDavid du Colombier 
651906943f9SDavid du Colombier int
findendpoints(Ums * ums)652906943f9SDavid du Colombier findendpoints(Ums *ums)
653906943f9SDavid du Colombier {
654906943f9SDavid du Colombier 	Ep *ep;
655906943f9SDavid du Colombier 	Usbdev *ud;
656ed868a7cSDavid du Colombier 	ulong csp, sc;
657ed868a7cSDavid du Colombier 	int i, epin, epout;
6580641ea09SDavid du Colombier 
659906943f9SDavid du Colombier 	epin = epout = -1;
660906943f9SDavid du Colombier 	ud = ums->dev->usb;
661906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->ep); i++){
662906943f9SDavid du Colombier 		if((ep = ud->ep[i]) == nil)
663906943f9SDavid du Colombier 			continue;
664906943f9SDavid du Colombier 		csp = ep->iface->csp;
665906943f9SDavid du Colombier 		sc = Subclass(csp);
666906943f9SDavid du Colombier 		if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
667906943f9SDavid du Colombier 			continue;
668906943f9SDavid du Colombier 		if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
669906943f9SDavid du Colombier 			fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
670906943f9SDavid du Colombier 		if(ep->type == Ebulk){
671906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Ein)
672906943f9SDavid du Colombier 				if(epin == -1)
673906943f9SDavid du Colombier 					epin =  ep->id;
674906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Eout)
675906943f9SDavid du Colombier 				if(epout == -1)
676906943f9SDavid du Colombier 					epout = ep->id;
677906943f9SDavid du Colombier 		}
678906943f9SDavid du Colombier 	}
679906943f9SDavid du Colombier 	dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
680906943f9SDavid du Colombier 	if(epin == -1 || epout == -1)
681906943f9SDavid du Colombier 		return -1;
682906943f9SDavid du Colombier 	ums->epin = openep(ums->dev, epin);
683906943f9SDavid du Colombier 	if(ums->epin == nil){
684906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epin);
685906943f9SDavid du Colombier 		return -1;
686906943f9SDavid du Colombier 	}
687906943f9SDavid du Colombier 	if(epout == epin){
688906943f9SDavid du Colombier 		incref(ums->epin);
689906943f9SDavid du Colombier 		ums->epout = ums->epin;
690906943f9SDavid du Colombier 	}else
691906943f9SDavid du Colombier 		ums->epout = openep(ums->dev, epout);
692906943f9SDavid du Colombier 	if(ums->epout == nil){
693906943f9SDavid du Colombier 		fprint(2, "disk: openep %d: %r\n", epout);
694906943f9SDavid du Colombier 		closedev(ums->epin);
695906943f9SDavid du Colombier 		return -1;
696906943f9SDavid du Colombier 	}
697906943f9SDavid du Colombier 	if(ums->epin == ums->epout)
698906943f9SDavid du Colombier 		opendevdata(ums->epin, ORDWR);
699906943f9SDavid du Colombier 	else{
700906943f9SDavid du Colombier 		opendevdata(ums->epin, OREAD);
701906943f9SDavid du Colombier 		opendevdata(ums->epout, OWRITE);
702906943f9SDavid du Colombier 	}
703906943f9SDavid du Colombier 	if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
704906943f9SDavid du Colombier 		fprint(2, "disk: open i/o ep data: %r\n");
705906943f9SDavid du Colombier 		closedev(ums->epin);
706906943f9SDavid du Colombier 		closedev(ums->epout);
707906943f9SDavid du Colombier 		return -1;
708906943f9SDavid du Colombier 	}
709906943f9SDavid du Colombier 	dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
710d37e33ffSDavid du Colombier 
711d37e33ffSDavid du Colombier 	devctl(ums->epin, "timeout 2000");
712d37e33ffSDavid du Colombier 	devctl(ums->epout, "timeout 2000");
7130641ea09SDavid du Colombier 
714906943f9SDavid du Colombier 	if(usbdebug > 1 || diskdebug > 2){
715906943f9SDavid du Colombier 		devctl(ums->epin, "debug 1");
716906943f9SDavid du Colombier 		devctl(ums->epout, "debug 1");
717906943f9SDavid du Colombier 		devctl(ums->dev, "debug 1");
718906943f9SDavid du Colombier 	}
719906943f9SDavid du Colombier 	return 0;
720906943f9SDavid du Colombier }
721906943f9SDavid du Colombier 
722906943f9SDavid du Colombier static int
usage(void)7230641ea09SDavid du Colombier usage(void)
7240641ea09SDavid du Colombier {
725ed868a7cSDavid du Colombier 	werrstr("usage: usb/disk [-d] [-N nb]");
726906943f9SDavid du Colombier 	return -1;
7270641ea09SDavid du Colombier }
7280641ea09SDavid du Colombier 
729906943f9SDavid du Colombier static void
umsdevfree(void * a)730906943f9SDavid du Colombier umsdevfree(void *a)
7310641ea09SDavid du Colombier {
732906943f9SDavid du Colombier 	Ums *ums = a;
7330641ea09SDavid du Colombier 
734906943f9SDavid du Colombier 	if(ums == nil)
735906943f9SDavid du Colombier 		return;
736906943f9SDavid du Colombier 	closedev(ums->epin);
737906943f9SDavid du Colombier 	closedev(ums->epout);
738906943f9SDavid du Colombier 	ums->epin = ums->epout = nil;
739906943f9SDavid du Colombier 	free(ums->lun);
740906943f9SDavid du Colombier 	free(ums);
741906943f9SDavid du Colombier }
742906943f9SDavid du Colombier 
743906943f9SDavid du Colombier static Usbfs diskfs = {
744906943f9SDavid du Colombier 	.walk = dwalk,
745906943f9SDavid du Colombier 	.open =	 dopen,
746906943f9SDavid du Colombier 	.read =	 dread,
747906943f9SDavid du Colombier 	.write = dwrite,
748906943f9SDavid du Colombier 	.stat =	 dstat,
749906943f9SDavid du Colombier };
750906943f9SDavid du Colombier 
751906943f9SDavid du Colombier int
diskmain(Dev * dev,int argc,char ** argv)752906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv)
753906943f9SDavid du Colombier {
754906943f9SDavid du Colombier 	Ums *ums;
755906943f9SDavid du Colombier 	Umsc *lun;
756ed868a7cSDavid du Colombier 	int i, devid;
7570641ea09SDavid du Colombier 
758ed868a7cSDavid du Colombier 	devid = dev->id;
7590641ea09SDavid du Colombier 	ARGBEGIN{
7600641ea09SDavid du Colombier 	case 'd':
761906943f9SDavid du Colombier 		scsidebug(diskdebug);
762906943f9SDavid du Colombier 		diskdebug++;
7630641ea09SDavid du Colombier 		break;
764ed868a7cSDavid du Colombier 	case 'N':
765ed868a7cSDavid du Colombier 		devid = atoi(EARGF(usage()));
766ed868a7cSDavid du Colombier 		break;
7670641ea09SDavid du Colombier 	default:
768906943f9SDavid du Colombier 		return usage();
7690641ea09SDavid du Colombier 	}ARGEND
770ed868a7cSDavid du Colombier 	if(argc != 0) {
771906943f9SDavid du Colombier 		return usage();
772ed868a7cSDavid du Colombier 	}
7730641ea09SDavid du Colombier 
7743a827ddcSDavid du Colombier //	notify(ding);
775906943f9SDavid du Colombier 	ums = dev->aux = emallocz(sizeof(Ums), 1);
776906943f9SDavid du Colombier 	ums->maxlun = -1;
777906943f9SDavid du Colombier 	ums->dev = dev;
778906943f9SDavid du Colombier 	dev->free = umsdevfree;
779906943f9SDavid du Colombier 	if(findendpoints(ums) < 0){
780906943f9SDavid du Colombier 		werrstr("disk: endpoints not found");
781906943f9SDavid du Colombier 		return -1;
7820641ea09SDavid du Colombier 	}
783d5789509SDavid du Colombier 
784d5789509SDavid du Colombier 	/*
785d5789509SDavid du Colombier 	 * SanDISK 512M gets residues wrong.
786d5789509SDavid du Colombier 	 */
787d5789509SDavid du Colombier 	if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
788d5789509SDavid du Colombier 		ums->wrongresidues = 1;
789d5789509SDavid du Colombier 
790906943f9SDavid du Colombier 	if(umsinit(ums) < 0){
791906943f9SDavid du Colombier 		dprint(2, "disk: umsinit: %r\n");
792906943f9SDavid du Colombier 		return -1;
793906943f9SDavid du Colombier 	}
7940641ea09SDavid du Colombier 
795906943f9SDavid du Colombier 	for(i = 0; i <= ums->maxlun; i++){
796906943f9SDavid du Colombier 		lun = &ums->lun[i];
797906943f9SDavid du Colombier 		lun->fs = diskfs;
798ed868a7cSDavid du Colombier 		snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i);
799906943f9SDavid du Colombier 		lun->fs.dev = dev;
800906943f9SDavid du Colombier 		incref(dev);
801906943f9SDavid du Colombier 		lun->fs.aux = lun;
802906943f9SDavid du Colombier 		usbfsadd(&lun->fs);
803906943f9SDavid du Colombier 	}
804906943f9SDavid du Colombier 	return 0;
8050641ea09SDavid du Colombier }
806