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