Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
50
ding(void *, char *msg)
51
{
52
	if(strstr(msg, "alarm") != nil)
53
		noted(NCONT);
54
	noted(NDFLT);
55
}
56
 
57
static int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
723
usage(void)
724
{
725
	werrstr("usage: usb/disk [-d] [-N nb]");
726
	return -1;
727
}
728
 
729
static void
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
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
}