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