xref: /plan9/sys/src/cmd/usb/disk/disk.c (revision fd362a73ff89ae80075dd82c9aad2a3468f0f3c9)
1 /*
2  * usb/disk - usb mass storage file server
3  *
4  * supports only the scsi command interface, not ata.
5  */
6 
7 #include <u.h>
8 #include <libc.h>
9 #include <ctype.h>
10 #include <fcall.h>
11 #include <thread.h>
12 #include <disk.h>
13 #include "scsireq.h"
14 #include "usb.h"
15 #include "usbfs.h"
16 #include "ums.h"
17 
18 enum
19 {
20 	Qdir = 0,
21 	Qctl,
22 	Qraw,
23 	Qdata,
24 	Qmax,
25 };
26 
27 typedef struct Dirtab Dirtab;
28 struct Dirtab
29 {
30 	char	*name;
31 	int	mode;
32 };
33 
34 static Dirtab dirtab[] =
35 {
36 	[Qdir]	"/",	DMDIR|0555,
37 	[Qctl]	"ctl",	0664,		/* nothing secret here */
38 	[Qraw]	"raw",	0640,
39 	[Qdata]	"data",	0640,
40 };
41 
42 /*
43  * These are used by scuzz scsireq
44  */
45 int exabyte, force6bytecmds;
46 
47 int diskdebug;
48 
49 static void
ding(void *,char * msg)50 ding(void *, char *msg)
51 {
52 	if(strstr(msg, "alarm") != nil)
53 		noted(NCONT);
54 	noted(NDFLT);
55 }
56 
57 static int
getmaxlun(Dev * dev)58 getmaxlun(Dev *dev)
59 {
60 	uchar max;
61 	int r;
62 
63 	max = 0;
64 	r = Rd2h|Rclass|Riface;
65 	if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
66 		dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
67 	}else{
68 		max &= 017;			/* 15 is the max. allowed */
69 		dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
70 	}
71 	return max;
72 }
73 
74 static int
umsreset(Ums * ums)75 umsreset(Ums *ums)
76 {
77 	int r;
78 
79 	r = Rh2d|Rclass|Riface;
80 	if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
81 		fprint(2, "disk: reset: %r\n");
82 		return -1;
83 	}
84 	return 0;
85 }
86 
87 static int
umsrecover(Ums * ums)88 umsrecover(Ums *ums)
89 {
90 	if(umsreset(ums) < 0)
91 		return -1;
92 	if(unstall(ums->dev, ums->epin, Ein) < 0)
93 		dprint(2, "disk: unstall epin: %r\n");
94 
95 	/* do we need this when epin == epout? */
96 	if(unstall(ums->dev, ums->epout, Eout) < 0)
97 		dprint(2, "disk: unstall epout: %r\n");
98 	return 0;
99 }
100 
101 static void
umsfatal(Ums * ums)102 umsfatal(Ums *ums)
103 {
104 	int i;
105 
106 	devctl(ums->dev, "detach");
107 	for(i = 0; i < ums->maxlun; i++)
108 		usbfsdel(&ums->lun[i].fs);
109 }
110 
111 static int
ispow2(uvlong ul)112 ispow2(uvlong ul)
113 {
114 	return (ul & (ul - 1)) == 0;
115 }
116 
117 /*
118  * return smallest power of 2 >= n
119  */
120 static int
log2(int n)121 log2(int n)
122 {
123 	int i;
124 
125 	for(i = 0; (1 << i) < n; i++)
126 		;
127 	return i;
128 }
129 
130 static int
umscapacity(Umsc * lun)131 umscapacity(Umsc *lun)
132 {
133 	uchar data[32];
134 
135 	lun->blocks = 0;
136 	lun->capacity = 0;
137 	lun->lbsize = 0;
138 	memset(data, 0, sizeof data);
139 	if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data)  < 0)
140 		return -1;
141 	lun->blocks = GETBELONG(data);
142 	lun->lbsize = GETBELONG(data+4);
143 	if(lun->blocks == 0xFFFFFFFF){
144 		if(SRrcapacity16(lun, data) < 0){
145 			lun->lbsize = 0;
146 			lun->blocks = 0;
147 			return -1;
148 		}else{
149 			lun->lbsize = GETBELONG(data + 8);
150 			lun->blocks = (uvlong)GETBELONG(data)<<32 |
151 				GETBELONG(data + 4);
152 		}
153 	}
154 	lun->blocks++; /* SRcapacity returns LBA of last block */
155 	lun->capacity = (vlong)lun->blocks * lun->lbsize;
156 	if(diskdebug)
157 		fprint(2, "disk: logical block size %lud, # blocks %llud\n",
158 			lun->lbsize, lun->blocks);
159 	return 0;
160 }
161 
162 static int
umsinit(Ums * ums)163 umsinit(Ums *ums)
164 {
165 	uchar i;
166 	Umsc *lun;
167 	int some;
168 
169 	umsreset(ums);
170 	ums->maxlun = getmaxlun(ums->dev);
171 	ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
172 	some = 0;
173 	for(i = 0; i <= ums->maxlun; i++){
174 		lun = &ums->lun[i];
175 		lun->ums = ums;
176 		lun->umsc = lun;
177 		lun->lun = i;
178 		lun->flags = Fopen | Fusb | Frw10;
179 		if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
180 			dprint(2, "disk: lun %d inquiry failed\n", i);
181 			continue;
182 		}
183 		switch(lun->inquiry[0]){
184 		case Devdir:
185 		case Devworm:		/* a little different than the others */
186 		case Devcd:
187 		case Devmo:
188 			break;
189 		default:
190 			fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
191 				i, lun->inquiry[0]);
192 			continue;
193 		}
194 		SRstart(lun, 1);
195 		/*
196 		 * we ignore the device type reported by inquiry.
197 		 * Some devices return a wrong value but would still work.
198 		 */
199 		some++;
200 		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
201 		umscapacity(lun);
202 	}
203 	if(some == 0){
204 		dprint(2, "disk: all luns failed\n");
205 		devctl(ums->dev, "detach");
206 		return -1;
207 	}
208 	return 0;
209 }
210 
211 
212 /*
213  * called by SR*() commands provided by scuzz's scsireq
214  */
215 long
umsrequest(Umsc * umsc,ScsiPtr * cmd,ScsiPtr * data,int * status)216 umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
217 {
218 	Cbw cbw;
219 	Csw csw;
220 	int n, nio, left;
221 	Ums *ums;
222 
223 	ums = umsc->ums;
224 
225 	memcpy(cbw.signature, "USBC", 4);
226 	cbw.tag = ++ums->seq;
227 	cbw.datalen = data->count;
228 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
229 	cbw.lun = umsc->lun;
230 	if(cmd->count < 1 || cmd->count > 16)
231 		fprint(2, "disk: umsrequest: bad cmd count: %ld\n", cmd->count);
232 
233 	cbw.len = cmd->count;
234 	assert(cmd->count <= sizeof(cbw.command));
235 	memcpy(cbw.command, cmd->p, cmd->count);
236 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
237 
238 	werrstr("");		/* we use %r later even for n == 0 */
239 	if(diskdebug){
240 		fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
241 		for(n = 0; n < cbw.len; n++)
242 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
243 		fprint(2, " datalen: %ld\n", cbw.datalen);
244 	}
245 
246 	/* issue tunnelled scsi command */
247 	if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
248 		fprint(2, "disk: cmd: %r\n");
249 		goto Fail;
250 	}
251 
252 	/* transfer the data */
253 	nio = data->count;
254 	if(nio != 0){
255 		if(data->write)
256 			n = write(ums->epout->dfd, data->p, nio);
257 		else{
258 			n = read(ums->epin->dfd, data->p, nio);
259 			left = nio - n;
260 			if (n >= 0 && left > 0)	/* didn't fill data->p? */
261 				memset(data->p + n, 0, left);
262 		}
263 		nio = n;
264 		if(diskdebug)
265 			if(n < 0)
266 				fprint(2, "disk: data: %r\n");
267 			else
268 				fprint(2, "disk: data: %d bytes\n", n);
269 		if(n <= 0)
270 			if(data->write == 0)
271 				unstall(ums->dev, ums->epin, Ein);
272 	}
273 
274 	/* read the transfer's status */
275 	n = read(ums->epin->dfd, &csw, CswLen);
276 	if(n <= 0){
277 		/* n == 0 means "stalled" */
278 		unstall(ums->dev, ums->epin, Ein);
279 		n = read(ums->epin->dfd, &csw, CswLen);
280 	}
281 
282 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
283 		dprint(2, "disk: read n=%d: status: %r\n", n);
284 		goto Fail;
285 	}
286 	if(csw.tag != cbw.tag){
287 		dprint(2, "disk: status tag mismatch\n");
288 		goto Fail;
289 	}
290 	if(csw.status >= CswPhaseErr){
291 		dprint(2, "disk: phase error\n");
292 		goto Fail;
293 	}
294 	if(csw.dataresidue == 0 || ums->wrongresidues)
295 		csw.dataresidue = data->count - nio;
296 	if(diskdebug){
297 		fprint(2, "disk: status: %2.2ux residue: %ld\n",
298 			csw.status, csw.dataresidue);
299 		if(cbw.command[0] == ScmdRsense){
300 			fprint(2, "sense data:");
301 			for(n = 0; n < data->count - csw.dataresidue; n++)
302 				fprint(2, " %2.2x", data->p[n]);
303 			fprint(2, "\n");
304 		}
305 	}
306 	switch(csw.status){
307 	case CswOk:
308 		*status = STok;
309 		break;
310 	case CswFailed:
311 		*status = STcheck;
312 		break;
313 	default:
314 		dprint(2, "disk: phase error\n");
315 		goto Fail;
316 	}
317 	ums->nerrs = 0;
318 	return data->count - csw.dataresidue;
319 
320 Fail:
321 	*status = STharderr;
322 	if(ums->nerrs++ > 15){
323 		fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
324 		umsfatal(ums);
325 	}else
326 		umsrecover(ums);
327 	return -1;
328 }
329 
330 static int
dwalk(Usbfs * fs,Fid * fid,char * name)331 dwalk(Usbfs *fs, Fid *fid, char *name)
332 {
333 	int i;
334 	Qid qid;
335 
336 	qid = fid->qid;
337 	if((qid.type & QTDIR) == 0){
338 		werrstr("walk in non-directory");
339 		return -1;
340 	}
341 
342 	if(strcmp(name, "..") == 0)
343 		return 0;
344 
345 	for(i = 1; i < nelem(dirtab); i++)
346 		if(strcmp(name, dirtab[i].name) == 0){
347 			qid.path = i | fs->qid;
348 			qid.vers = 0;
349 			qid.type = dirtab[i].mode >> 24;
350 			fid->qid = qid;
351 			return 0;
352 		}
353 	werrstr(Enotfound);
354 	return -1;
355 }
356 
357 static void
dostat(Usbfs * fs,int path,Dir * d)358 dostat(Usbfs *fs, int path, Dir *d)
359 {
360 	Dirtab *t;
361 	Umsc *lun;
362 
363 	t = &dirtab[path];
364 	d->qid.path = path;
365 	d->qid.type = t->mode >> 24;
366 	d->mode = t->mode;
367 	d->name = t->name;
368 	lun = fs->aux;
369 	if(path == Qdata)
370 		d->length = lun->capacity;
371 	else
372 		d->length = 0;
373 }
374 
375 static int
dirgen(Usbfs * fs,Qid,int i,Dir * d,void *)376 dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
377 {
378 	i++;	/* skip dir */
379 	if(i >= Qmax)
380 		return -1;
381 	else{
382 		dostat(fs, i, d);
383 		d->qid.path |= fs->qid;
384 		return 0;
385 	}
386 }
387 
388 static int
dstat(Usbfs * fs,Qid qid,Dir * d)389 dstat(Usbfs *fs, Qid qid, Dir *d)
390 {
391 	int path;
392 
393 	path = qid.path & ~fs->qid;
394 	dostat(fs, path, d);
395 	d->qid.path |= fs->qid;
396 	return 0;
397 }
398 
399 static int
dopen(Usbfs * fs,Fid * fid,int)400 dopen(Usbfs *fs, Fid *fid, int)
401 {
402 	ulong path;
403 	Umsc *lun;
404 
405 	path = fid->qid.path & ~fs->qid;
406 	lun = fs->aux;
407 	switch(path){
408 	case Qraw:
409 		lun->phase = Pcmd;
410 		break;
411 	}
412 	return 0;
413 }
414 
415 /*
416  * check i/o parameters and compute values needed later.
417  * we shift & mask manually to avoid run-time calls to _divv and _modv,
418  * since we don't need general division nor its cost.
419  */
420 static int
setup(Umsc * lun,char * data,int count,vlong offset)421 setup(Umsc *lun, char *data, int count, vlong offset)
422 {
423 	long nb, lbsize, lbshift, lbmask;
424 	uvlong bno;
425 
426 	if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
427 	    lun->lbsize == 0)
428 		return -1;
429 	lbsize = lun->lbsize;
430 	assert(ispow2(lbsize));
431 	lbshift = log2(lbsize);
432 	lbmask = lbsize - 1;
433 
434 	bno = offset >> lbshift;	/* offset / lbsize */
435 	nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
436 
437 	if(bno + nb > lun->blocks)		/* past end of device? */
438 		nb = lun->blocks - bno;
439 	if(nb * lbsize > Maxiosize)
440 		nb = Maxiosize / lbsize;
441 	lun->nb = nb;
442 	if(bno >= lun->blocks || nb == 0)
443 		return 0;
444 
445 	lun->offset = bno;
446 	lun->off = offset & lbmask;		/* offset % lbsize */
447 	if(lun->off == 0 && (count & lbmask) == 0)
448 		lun->bufp = data;
449 	else
450 		/* not transferring full, aligned blocks; need intermediary */
451 		lun->bufp = lun->buf;
452 	return count;
453 }
454 
455 /*
456  * Upon SRread/SRwrite errors we assume the medium may have changed,
457  * and ask again for the capacity of the media.
458  * BUG: How to proceed to avoid confusing dossrv??
459  *
460  * ctl reads must match the format documented in sd(3) exactly
461  * to interoperate with the rest of the system.
462  */
463 static long
dread(Usbfs * fs,Fid * fid,void * data,long count,vlong offset)464 dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
465 {
466 	long n;
467 	ulong path;
468 	char buf[1024];
469 	char *s, *e;
470 	Umsc *lun;
471 	Ums *ums;
472 	Qid q;
473 
474 	q = fid->qid;
475 	path = fid->qid.path & ~fs->qid;
476 	ums = fs->dev->aux;
477 	lun = fs->aux;
478 
479 	qlock(ums);
480 	switch(path){
481 	case Qdir:
482 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
483 		break;
484 	case Qctl:
485 		/*
486 		 * Some usb disks need an extra opportunity to divulge their
487 		 * capacity (e.g. M-Systems/SanDisk 1GB flash drive).
488 		 */
489 		if(lun->lbsize <= 0)
490 			umscapacity(lun);
491 
492 		s = buf;
493 		e = buf + sizeof(buf);
494 		if(lun->flags & Finqok)
495 			s = seprint(s, e, "inquiry %s lun %ld: %s\n",
496 				fs->dev->dir, lun - &ums->lun[0], lun->inq);
497 		if(lun->blocks > 0)
498 			s = seprint(s, e, "geometry %llud %ld\n",
499 				lun->blocks, lun->lbsize);
500 		count = usbreadbuf(data, count, offset, buf, s - buf);
501 		break;
502 	case Qraw:
503 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
504 			count = -1;
505 			break;
506 		}
507 		switch(lun->phase){
508 		case Pcmd:
509 			qunlock(ums);
510 			werrstr("phase error");
511 			return -1;
512 		case Pdata:
513 			lun->data.p = data;
514 			lun->data.count = count;
515 			lun->data.write = 0;
516 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
517 			lun->phase = Pstatus;
518 			if(count < 0)
519 				lun->lbsize = 0;  /* medium may have changed */
520 			break;
521 		case Pstatus:
522 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
523 			count = usbreadbuf(data, count, 0LL, buf, n);
524 			lun->phase = Pcmd;
525 			break;
526 		}
527 		break;
528 	case Qdata:
529 		count = setup(lun, data, count, offset);
530 		if (count <= 0)
531 			break;
532 		n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
533 		if(n < 0){
534 			lun->lbsize = 0;	/* medium may have changed */
535 			count = -1;
536 		} else if (lun->bufp == data)
537 			count = n;
538 		else{
539 			/*
540 			 * if n == lun->nb*lun->lbsize (as expected),
541 			 * just copy count bytes.
542 			 */
543 			if(lun->off + count > n)
544 				count = n - lun->off; /* short read */
545 			if(count > 0)
546 				memmove(data, lun->bufp + lun->off, count);
547 		}
548 		break;
549 	}
550 	qunlock(ums);
551 	return count;
552 }
553 
554 static long
dwrite(Usbfs * fs,Fid * fid,void * data,long count,vlong offset)555 dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
556 {
557 	long len, ocount;
558 	ulong path;
559 	uvlong bno;
560 	Ums *ums;
561 	Umsc *lun;
562 
563 	ums = fs->dev->aux;
564 	lun = fs->aux;
565 	path = fid->qid.path & ~fs->qid;
566 
567 	qlock(ums);
568 	switch(path){
569 	default:
570 		werrstr(Eperm);
571 		count = -1;
572 		break;
573 	case Qctl:
574 		dprint(2, "usb/disk: ctl ignored\n");
575 		break;
576 	case Qraw:
577 		if(lun->lbsize <= 0 && umscapacity(lun) < 0){
578 			count = -1;
579 			break;
580 		}
581 		switch(lun->phase){
582 		case Pcmd:
583 			if(count != 6 && count != 10){
584 				qunlock(ums);
585 				werrstr("bad command length");
586 				return -1;
587 			}
588 			memmove(lun->rawcmd, data, count);
589 			lun->cmd.p = lun->rawcmd;
590 			lun->cmd.count = count;
591 			lun->cmd.write = 1;
592 			lun->phase = Pdata;
593 			break;
594 		case Pdata:
595 			lun->data.p = data;
596 			lun->data.count = count;
597 			lun->data.write = 1;
598 			count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
599 			lun->phase = Pstatus;
600 			if(count < 0)
601 				lun->lbsize = 0;  /* medium may have changed */
602 			break;
603 		case Pstatus:
604 			lun->phase = Pcmd;
605 			werrstr("phase error");
606 			count = -1;
607 			break;
608 		}
609 		break;
610 	case Qdata:
611 		len = ocount = count;
612 		count = setup(lun, data, count, offset);
613 		if (count <= 0)
614 			break;
615 		bno = lun->offset;
616 		if (lun->bufp == lun->buf) {
617 			count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
618 			if(count < 0) {
619 				lun->lbsize = 0;  /* medium may have changed */
620 				break;
621 			}
622 			/*
623 			 * if count == lun->nb*lun->lbsize, as expected, just
624 			 * copy len (the original count) bytes of user data.
625 			 */
626 			if(lun->off + len > count)
627 				len = count - lun->off; /* short read */
628 			if(len > 0)
629 				memmove(lun->bufp + lun->off, data, len);
630 		}
631 
632 		lun->offset = bno;
633 		count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
634 		if(count < 0)
635 			lun->lbsize = 0;	/* medium may have changed */
636 		else{
637 			if(lun->off + len > count)
638 				count -= lun->off; /* short write */
639 			/* never report more bytes written than requested */
640 			if(count < 0)
641 				count = 0;
642 			else if(count > ocount)
643 				count = ocount;
644 		}
645 		break;
646 	}
647 	qunlock(ums);
648 	return count;
649 }
650 
651 int
findendpoints(Ums * ums)652 findendpoints(Ums *ums)
653 {
654 	Ep *ep;
655 	Usbdev *ud;
656 	ulong csp, sc;
657 	int i, epin, epout;
658 
659 	epin = epout = -1;
660 	ud = ums->dev->usb;
661 	for(i = 0; i < nelem(ud->ep); i++){
662 		if((ep = ud->ep[i]) == nil)
663 			continue;
664 		csp = ep->iface->csp;
665 		sc = Subclass(csp);
666 		if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
667 			continue;
668 		if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
669 			fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
670 		if(ep->type == Ebulk){
671 			if(ep->dir == Eboth || ep->dir == Ein)
672 				if(epin == -1)
673 					epin =  ep->id;
674 			if(ep->dir == Eboth || ep->dir == Eout)
675 				if(epout == -1)
676 					epout = ep->id;
677 		}
678 	}
679 	dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
680 	if(epin == -1 || epout == -1)
681 		return -1;
682 	ums->epin = openep(ums->dev, epin);
683 	if(ums->epin == nil){
684 		fprint(2, "disk: openep %d: %r\n", epin);
685 		return -1;
686 	}
687 	if(epout == epin){
688 		incref(ums->epin);
689 		ums->epout = ums->epin;
690 	}else
691 		ums->epout = openep(ums->dev, epout);
692 	if(ums->epout == nil){
693 		fprint(2, "disk: openep %d: %r\n", epout);
694 		closedev(ums->epin);
695 		return -1;
696 	}
697 	if(ums->epin == ums->epout)
698 		opendevdata(ums->epin, ORDWR);
699 	else{
700 		opendevdata(ums->epin, OREAD);
701 		opendevdata(ums->epout, OWRITE);
702 	}
703 	if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
704 		fprint(2, "disk: open i/o ep data: %r\n");
705 		closedev(ums->epin);
706 		closedev(ums->epout);
707 		return -1;
708 	}
709 	dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
710 
711 	devctl(ums->epin, "timeout 2000");
712 	devctl(ums->epout, "timeout 2000");
713 
714 	if(usbdebug > 1 || diskdebug > 2){
715 		devctl(ums->epin, "debug 1");
716 		devctl(ums->epout, "debug 1");
717 		devctl(ums->dev, "debug 1");
718 	}
719 	return 0;
720 }
721 
722 static int
usage(void)723 usage(void)
724 {
725 	werrstr("usage: usb/disk [-d] [-N nb]");
726 	return -1;
727 }
728 
729 static void
umsdevfree(void * a)730 umsdevfree(void *a)
731 {
732 	Ums *ums = a;
733 
734 	if(ums == nil)
735 		return;
736 	closedev(ums->epin);
737 	closedev(ums->epout);
738 	ums->epin = ums->epout = nil;
739 	free(ums->lun);
740 	free(ums);
741 }
742 
743 static Usbfs diskfs = {
744 	.walk = dwalk,
745 	.open =	 dopen,
746 	.read =	 dread,
747 	.write = dwrite,
748 	.stat =	 dstat,
749 };
750 
751 int
diskmain(Dev * dev,int argc,char ** argv)752 diskmain(Dev *dev, int argc, char **argv)
753 {
754 	Ums *ums;
755 	Umsc *lun;
756 	int i, devid;
757 
758 	devid = dev->id;
759 	ARGBEGIN{
760 	case 'd':
761 		scsidebug(diskdebug);
762 		diskdebug++;
763 		break;
764 	case 'N':
765 		devid = atoi(EARGF(usage()));
766 		break;
767 	default:
768 		return usage();
769 	}ARGEND
770 	if(argc != 0) {
771 		return usage();
772 	}
773 
774 //	notify(ding);
775 	ums = dev->aux = emallocz(sizeof(Ums), 1);
776 	ums->maxlun = -1;
777 	ums->dev = dev;
778 	dev->free = umsdevfree;
779 	if(findendpoints(ums) < 0){
780 		werrstr("disk: endpoints not found");
781 		return -1;
782 	}
783 
784 	/*
785 	 * SanDISK 512M gets residues wrong.
786 	 */
787 	if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
788 		ums->wrongresidues = 1;
789 
790 	if(umsinit(ums) < 0){
791 		dprint(2, "disk: umsinit: %r\n");
792 		return -1;
793 	}
794 
795 	for(i = 0; i <= ums->maxlun; i++){
796 		lun = &ums->lun[i];
797 		lun->fs = diskfs;
798 		snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i);
799 		lun->fs.dev = dev;
800 		incref(dev);
801 		lun->fs.aux = lun;
802 		usbfsadd(&lun->fs);
803 	}
804 	return 0;
805 }
806