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