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