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