]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/ptp/ptp.c
merge
[plan9front.git] / sys / src / cmd / nusb / ptp / ptp.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include <auth.h>
6 #include <fcall.h>
7 #include <9p.h>
8
9 #include "usb.h"
10
11 enum
12 {
13         Qroot,
14         Qstore,
15         Qobj,
16         Qthumb,
17 };
18
19 enum {
20         /* flags */
21         DataSend                        =       0x00010000,
22         DataRecv                        =       0x00020000,
23         OutParam                        =       0x00040000,
24
25         /* rpc codes */
26         OpenSession             =       0x1002,
27         CloseSession            =       0x1003,
28         GetStorageIds           =       0x1004,
29         GetStorageInfo          =       0x1005,
30         GetObjectHandles        =       0x1007,
31         GetObjectInfo           =       0x1008,
32         GetObject                       =       0x1009,
33         GetThumb                =       0x100A,
34         DeleteObject            =       0x100B,
35         GetPartialObject        =       0x101B,
36
37         Maxio                   =       0x2000,
38 };
39
40 typedef struct Ptprpc Ptprpc;
41 typedef struct Node Node;
42
43 struct Ptprpc
44 {
45         uchar   length[4];
46         uchar   type[2];
47         uchar   code[2];
48         uchar   transid[4];
49         union {
50                 uchar   p[5][4];
51                 uchar   d[52];
52         };
53 };
54
55 struct Node
56 {
57         Dir             d;
58
59         Node    *parent;
60         Node    *next;
61         Node    *child;
62
63         int             store;
64         int             handle;
65         int             format;
66
67         void            *data;
68         int             ndata;
69 };
70
71 enum {
72         In,
73         Out,
74         Int,
75         Setup,
76 };
77
78 static Dev *usbep[Setup+1];
79
80 static int debug = 0;
81 static ulong time0;
82 static int maxpacket = 64;
83 static int sessionId = 1;
84 static int transId = 1;
85
86 static Node **nodes;
87 static int nnodes;
88
89 static Channel *iochan;
90
91 char Eperm[] = "permission denied";
92 char Einterrupt[] = "interrupted";
93
94 #define PATH(type, n)           ((uvlong)(type)|((uvlong)(n)<<4))
95 #define TYPE(path)                      ((int)((path)&0xF))
96 #define NUM(path)                       ((int)((path)>>4))
97
98 static void
99 hexdump(char *prefix, uchar *p, int n)
100 {
101         char *s;
102         int i;
103         int m;
104
105         m = 12;
106         s = emalloc9p(1+((n+1)*((m*6)+7)));
107         s[0] = '\0';
108         for(i=0; i<n; i++){
109                 int printable;
110                 char x[8];
111                 if((i % m)==0){
112                         sprint(x, "\n%.4x: ", i);
113                         strcat(s, x);
114                 }
115                 printable = (p[i] >= 32 && p[i]<=127);
116                 sprint(x, "%.2x %c  ", (int)p[i],  printable ? p[i] : '.');
117                 strcat(s, x);
118         }
119         fprint(2, "%20-s: %6d bytes %s\n", prefix, n, s);
120         free(s);
121 }
122
123 static int
124 wasinterrupt(void)
125 {
126         char err[ERRMAX];
127
128         rerrstr(err, sizeof(err));
129         if(strstr(err, Einterrupt) || strstr(err, "timed out")){
130                 werrstr(Einterrupt);
131                 return 1;
132         }
133         return 0;
134 }
135
136 static char *
137 ptperrstr(int code)
138 {
139         static char *a[] = {
140                 "undefined",
141                 nil ,
142                 "general error" ,
143                 "session not open" ,
144                  "invalid transaction id" ,
145                  "operation not supported" ,
146                  "parameter not supported" ,
147                  "incomplete transfer" ,
148                  "invalid storage id" ,
149                  "invalid object handle" ,
150                  "device prop not supported" ,
151                  "invalid object format code" ,
152                  "storage full" ,
153                  "object write protected" ,
154                  "store read only" ,
155                  "access denied" ,
156                  "no thumbnail present" ,
157                  "self test failed" ,
158                  "partial deletion" ,
159                  "store not available" ,
160                  "specification by format unsupported" ,
161                  "no valid object info" ,
162                  "invalid code format" ,
163                  "unknown vendor code",
164                 "capture already terminated",
165                 "device busy",
166                 "invalid parent object",
167                 "invalid device prop format",
168                 "invalid device prop value",
169                 "invalid parameter",
170                 "session already opend",
171                 "transaction canceld",
172                 "specification of destination unsupported"
173         };
174
175         code -= 0x2000;
176         if(code < 0)
177                 return nil;
178         if(code >= nelem(a))
179                 return "invalid error number";
180         return a[code];
181 }
182
183 static int
184 ptpcheckerr(Ptprpc *rpc, int type, int transid, int length)
185 {
186         char *s;
187
188         if(length < 4+2+2+4){
189                 werrstr("short response: %d < %d", length, 4+2+2+4);
190                 return 1;
191         }
192         if(GET4(rpc->length) < length){
193                 werrstr("unexpected response length 0x%x < 0x%x", GET4(rpc->length), length);
194                 return 1;
195         }
196         if(GET4(rpc->transid) != transid){
197                 werrstr("unexpected transaction id 0x%x != 0x%x", GET4(rpc->transid), transid);
198                 return 1;
199         }
200         if(GET2(rpc->type) != type){
201                 werrstr("unexpected response type 0x%x != 0x%x", GET2(rpc->type), type);
202                 return 1;
203         }
204         if(s = ptperrstr(GET2(rpc->code))){
205                 werrstr("%s", s);
206                 return -GET2(rpc->code);
207         }
208         return 0;
209 }
210
211 static int
212 vptprpc(Ioproc *io, int code, int flags, va_list a)
213 {
214         Ptprpc rpc;
215         int np, n, t, i, l;
216         uchar *b, *p, *e;
217
218         np = flags & 0xF;
219         n = 4+2+2+4+4*np;
220         t = transId++;
221
222         PUT4(rpc.length, n);
223         PUT2(rpc.type, 1);
224         PUT2(rpc.code, code);
225         PUT4(rpc.transid, t);
226
227         for(i=0; i<np; i++){
228                 int x = va_arg(a, int);
229                 PUT4(rpc.p[i], x);
230         }
231         if(debug)
232                 hexdump("req>", (uchar*)&rpc, n);
233         werrstr("");
234         if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
235                 wasinterrupt();
236                 return -1;
237         }
238
239         if(flags & DataSend){
240                 void *sdata;
241                 int sdatalen;
242
243                 sdata = va_arg(a, void*);
244                 sdatalen = va_arg(a, int);
245
246                 b = (uchar*)sdata;
247                 p = b;
248                 e = b + sdatalen;
249
250                 l = 4+2+2+4+sdatalen;
251                 PUT4(rpc.length, l);
252                 PUT2(rpc.type, 2);
253
254                 if((n = sdatalen) > sizeof(rpc.d))
255                         n = sizeof(rpc.d);
256                 memmove(rpc.d, p, n);
257                 p += n;
258                 n += (4+2+2+4);
259
260                 if(debug)
261                         hexdump("data>", (uchar*)&rpc, n);
262                 if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
263                         wasinterrupt();
264                         return -1;
265                 }
266                 while(p < e){
267                         n = e - p;
268                         if(n > Maxio)
269                                 n = Maxio;
270                         if((n = iowrite(io, usbep[Out]->dfd, p, n)) < 0){
271                                 wasinterrupt();
272                                 return -1;
273                         }
274                         p += n;
275                 }
276         }
277
278         if(flags & DataRecv){
279                 void **prdata;
280                 int *prdatalen;
281
282                 prdata = va_arg(a, void**);
283                 prdatalen = va_arg(a, int*);
284
285                 *prdata = nil;
286                 *prdatalen = 0;
287
288                 while((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) <= 0){
289                         if(n < 0){
290                                 wasinterrupt();
291                                 return -1;
292                         }
293                 }
294                 if((l = ptpcheckerr(&rpc, 2, t, n)) < 0)
295                         return -1;
296                 if(l && GET2(rpc.type) == 3)
297                         goto Resp1;
298
299                 l = GET4(rpc.length);
300                 l -= (4+2+2+4);
301                 n -= (4+2+2+4);
302         
303                 b = emalloc9p(l);
304                 p = b;
305                 e = b+l;
306                 if(n <= l){
307                         if(debug)
308                                 hexdump("data<", rpc.d, n);
309                         memmove(p, rpc.d, n);
310                         p += n;
311                         while(p < e){
312                                 n = e-p;
313                                 if(n > Maxio)
314                                         n = Maxio;
315                                 while((n = ioread(io, usbep[In]->dfd, p, n)) <= 0){
316                                         if(n < 0){
317                                                 wasinterrupt();
318                                                 free(b);
319                                                 return -1;
320                                         }
321                                 }
322                                 if(debug)
323                                         hexdump("data<", p, n);
324                                 p += n;
325                         }
326                         *prdata = b;
327                         *prdatalen = e-b;
328                 } else {
329                         if(debug)
330                                 hexdump("data<", rpc.d, l);
331                         memmove(p, rpc.d, l);
332                         *prdata = b;
333                         *prdatalen = e-b;
334
335                         n -= l;
336                         memmove(&rpc, rpc.d+l, n);
337                         goto Resp1;
338                 }
339         }
340
341         while((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) <= 0){
342                 if(n < 0){
343                         wasinterrupt();
344                         return -1;
345                 }
346         }
347 Resp1:
348         if(debug)
349                 hexdump("resp<", (uchar*)&rpc, n);
350         if(ptpcheckerr(&rpc, 3, t, n))
351                 return -1;
352         if(flags & OutParam){
353                 int *pp;
354
355                 for(i=0; i<nelem(rpc.p); i++){
356                         if((pp = va_arg(a, int*)) == nil)
357                                 break;
358                         *pp = GET4(rpc.p[i]);
359                 }
360         }
361         return 0;
362 }
363
364 static int
365 ptprpc(Req *r, int code, int flags, ...)
366 {
367         va_list va;
368         Channel *c;
369         Ioproc *io;
370         Alt a[3];
371         char *m;
372         int i;
373
374         i = -1;
375         c = nil;
376         io = nil;
377         m = Einterrupt;
378         a[0].op = CHANRCV;
379         a[0].c = iochan;
380         a[0].v = &io;
381         if(r){
382                 c = chancreate(sizeof(char*), 0);
383                 a[1].op = CHANRCV;
384                 a[1].c = c;
385                 a[1].v = &m;
386                 a[2].op = CHANEND;
387                 r->aux = c;
388                 srvrelease(r->srv);
389         } else
390                 a[1].op = CHANEND;
391         if(alt(a) == 0){
392                 va_start(va, flags);
393                 i = vptprpc(io, code, flags, va);
394                 va_end(va);
395                 if(i < 0 && debug)
396                         fprint(2, "rpc: %r\n");
397         } else
398                 werrstr("%s", m);
399         if(r){
400                 srvacquire(r->srv);
401                 r->aux = nil;
402         }
403         if(io)
404                 sendp(iochan, io);
405         if(c)
406                 chanfree(c);
407         return i;
408 }
409
410 static int*
411 ptparray4(uchar *d, uchar *e)
412 {
413         int *a, i, n;
414
415         if(d + 4 > e)
416                 return nil;
417         n = GET4(d);
418         d += 4;
419         if(d + n*4 > e)
420                 return nil;
421         a = emalloc9p((1+n) * sizeof(int));
422         a[0] = n;
423         for(i=0; i<n; i++){
424                 a[i+1] = GET4(d);
425                 d += 4;
426         }
427         return a;
428 }
429
430 static char*
431 ptpstring2(uchar *d, uchar *e)
432 {
433         int n, i;
434         char *s, *p;
435
436         if(d+1 > e)
437                 return nil;
438         n = *d;
439         d++;
440         if(d + n*2 > e)
441                 return nil;
442         p = s = emalloc9p((n+1)*UTFmax);
443         for(i=0; i<n; i++){
444                 Rune r;
445
446                 r = GET2(d);
447                 d += 2;
448                 if(r == 0)
449                         break;
450                 p += runetochar(p, &r);
451         }
452         *p = 0;
453         return s;
454 }
455
456 static void
457 cleardir(Dir *d)
458 {
459         free(d->name);
460         free(d->uid);
461         free(d->gid);
462         free(d->muid);
463         memset(d, 0, sizeof(*d));
464 }
465
466 static void
467 copydir(Dir *d, Dir *s)
468 {
469         memmove(d, s, sizeof(*d));
470         if(d->name)
471                 d->name = estrdup9p(d->name);
472         if(d->uid)
473                 d->uid = estrdup9p(d->uid);
474         if(d->gid)
475                 d->gid = estrdup9p(d->gid);
476         if(d->muid)
477                 d->muid = estrdup9p(d->muid);
478 }
479
480 static Node*
481 cachednode(uvlong path, Node ***pf)
482 {
483         Node *x;
484         int i;
485
486         if(pf)
487                 *pf = nil;
488         for(i=0; i<nnodes; i++){
489                 if((x = nodes[i]) == nil){
490                         if(pf)
491                                 *pf = &nodes[i];
492                         continue;
493                 }
494                 if(x->d.qid.path == path)
495                         return x;
496         }
497         return nil;
498 }
499
500 static Node*
501 getnode(Req *r, uvlong path)
502 {
503         Node *x, *y, **f;
504         uchar *p;
505         int np;
506         char *s;
507
508         if(x = cachednode(path, &f))
509                 return x;
510
511         y = nil;
512         x = emalloc9p(sizeof(*x));
513         memset(x, 0, sizeof(*x));
514         x->d.qid.path = path;
515         x->d.uid = estrdup9p("ptp");
516         x->d.gid = estrdup9p("usb");
517         x->d.atime = x->d.mtime = time0;
518
519         p = nil;
520         np = 0;
521         switch(TYPE(path)){
522         case Qroot:
523                 x->d.qid.type = QTDIR;
524                 x->d.mode = DMDIR|0777;
525                 x->d.name = estrdup9p("/");
526                 goto Addnode;
527
528         case Qstore:
529                 x->store = NUM(path);
530                 x->handle = 0xffffffff;
531                 x->d.qid.type = QTDIR;
532                 x->d.mode = DMDIR|0777;
533                 x->d.name = emalloc9p(10);
534                 sprint(x->d.name, "%x", x->store);
535                 goto Addnode;
536
537         case Qobj:
538         case Qthumb:
539                 if(ptprpc(r, GetObjectInfo, 1|DataRecv, NUM(path), &p, &np) < 0)
540                         break;
541                 if(debug)
542                         hexdump("objectinfo", p, np);
543                 if(np < 52){
544                         werrstr("bad objectinfo");
545                         break;
546                 }
547
548                 /*
549                  * another proc migh'v come in and done it for us,
550                  * so check the cache again.
551                  */
552                 if(y = cachednode(path, &f))
553                         break;
554
555                 if((x->d.name = ptpstring2(p+52, p+np)) == nil){
556                         werrstr("bad objectinfo");
557                         break;
558                 }
559                 x->handle = NUM(path);
560                 x->store = GET4(p);
561                 x->format = GET2(p+4);
562                 if(x->format == 0x3001 && GET2(p+42) == 1){
563                         x->d.qid.type = QTDIR;
564                         x->d.mode = DMDIR|0777;
565                 } else {
566                         x->d.mode = 0666;
567                         if(TYPE(path) == Qthumb){
568                                 char *t;
569
570                                 t = emalloc9p(8 + strlen(x->d.name));
571                                 sprint(t, "thumb_%s", x->d.name);
572                                 free(x->d.name);
573                                 x->d.name = t;
574
575                                 x->d.length = GET4(p+14);
576                         } else {
577                                 x->d.length = GET4(p+8);
578                         }
579                 }
580                 if(s = ptpstring2(p+(53+p[52]*2), p+np)){
581                         if(strlen(s) >= 15){
582                                 Tm t;
583
584                                 // 0123 45 67 8 9A BC DF
585                                 // 2008 12 26 T 00 21 18
586                                 memset(&t, 0, sizeof(t));
587
588                                 s[0x10] = 0;
589                                 t.sec = atoi(s+0xD);
590                                 s[0xD] = 0;
591                                 t.min = atoi(s+0xB);
592                                 s[0xB] = 0;
593                                 t.hour = atoi(s+0x9);
594                                 s[0x8] = 0;
595                                 t.mday = atoi(s+0x6);
596                                 s[0x6] = 0;
597                                 t.mon = atoi(s+0x4) - 1;
598                                 s[0x4] = 0;
599                                 t.year = atoi(s) - 1900;
600
601                                 x->d.atime = x->d.mtime = tm2sec(&t);
602                         }
603                         free(s);
604                 }
605                 free(p);
606         Addnode:
607                 if(f == nil){
608                         if(nnodes % 64 == 0)
609                                 nodes = erealloc9p(nodes, sizeof(nodes[0]) * (nnodes + 64));
610                         f = &nodes[nnodes++];
611                 }
612                 return *f = x;
613         }
614
615         cleardir(&x->d);
616         free(x);
617         free(p);
618         return y;
619 }
620
621 static void
622 freenode(Node *nod)
623 {
624         int i;
625
626         /* remove the node from the tree */
627         for(i=0; i<nnodes; i++){
628                 if(nod == nodes[i]){
629                         nodes[i] = nil;
630                         break;
631                 }
632         }
633         cleardir(&nod->d);
634         free(nod->data);
635         free(nod);
636 }
637
638 static int
639 readchilds(Req *r, Node *nod)
640 {
641         int e, i;
642         int *a;
643         uchar *p;
644         int np;
645         Node *x, **xx;
646
647         e = 0;
648         switch(TYPE(nod->d.qid.path)){
649         case Qroot:
650                 if(ptprpc(r, GetStorageIds, 0|DataRecv, &p, &np) < 0)
651                         return -1;
652                 a = ptparray4(p, p+np);
653                 free(p);
654                 xx = &nod->child;
655                 *xx = nil;
656                 for(i=0; a && i<a[0]; i++){
657                         if((x = getnode(r, PATH(Qstore, a[i+1]))) == nil){
658                                 e = -1;
659                                 break;
660                         }
661                         x->parent = nod;
662                         *xx = x;
663                         xx = &x->next;
664                         *xx = nil;
665                 }               
666                 free(a);
667                 break;
668
669         case Qstore:
670         case Qobj:
671                 if(ptprpc(r, GetObjectHandles, 3|DataRecv, nod->store, 0, nod->handle, &p, &np) < 0)
672                         return -1;
673                 a = ptparray4(p, p+np);
674                 free(p);
675                 xx = &nod->child;
676                 *xx = nil;
677                 for(i=0; a && i<a[0]; i++){
678                         if((x = getnode(r, PATH(Qobj, a[i+1]))) == nil){
679                                 e = -1;
680                                 break;
681                         }
682                         x->parent = nod;
683                         *xx = x;
684                         xx = &x->next;
685                         *xx = nil;
686
687                         /* skip thumb when not image format */
688                         if((x->format & 0xFF00) != 0x3800)
689                                 continue;
690                         if((x = getnode(r, PATH(Qthumb, a[i+1]))) == nil){
691                                 e = -1;
692                                 break;
693                         }
694                         x->parent = nod;
695                         *xx = x;
696                         xx = &x->next;
697                         *xx = nil;
698                 }
699                 free(a);
700                 break;
701         }
702
703         return e;
704 }
705
706 static void
707 fsattach(Req *r)
708 {
709         if(r->ifcall.aname && r->ifcall.aname[0]){
710                 respond(r, "invalid attach specifier");
711                 return;
712         }
713         r->fid->qid.path = PATH(Qroot, 0);
714         r->fid->qid.type = QTDIR;
715         r->fid->qid.vers = 0;
716         r->ofcall.qid = r->fid->qid;
717         respond(r, nil);
718 }
719
720 static void
721 fsstat(Req *r)
722 {
723         Node *nod;
724
725         if((nod = getnode(r, r->fid->qid.path)) == nil){
726                 responderror(r);
727                 return;
728         }
729         copydir(&r->d, &nod->d);
730         respond(r, nil);
731 }
732
733 static int
734 nodegen(int i, Dir *d, void *aux)
735 {
736         Node *nod = aux;
737
738         for(nod=nod->child; nod && i; nod=nod->next, i--)
739                 ;
740         if(i==0 && nod){
741                 copydir(d, &nod->d);
742                 return 0;
743         }
744         return -1;
745 }
746
747 static char*
748 fswalk1(Req *r, char *name, Qid *qid)
749 {
750         static char buf[ERRMAX];
751         uvlong path;
752         Node *nod;
753         Fid *fid;
754
755         fid = r->newfid;
756         path = fid->qid.path;
757         if(!(fid->qid.type&QTDIR))
758                 return "walk in non-directory";
759         if(nod = getnode(r, path)){
760                 if(strcmp(name, "..") == 0){
761                         if(nod = nod->parent)
762                                 *qid = nod->d.qid;
763                         return nil;
764                 }
765                 if(readchilds(r, nod) == 0){
766                         for(nod=nod->child; nod; nod=nod->next){
767                                 if(strcmp(nod->d.name, name) == 0){
768                                         *qid = nod->d.qid;
769                                         return nil;
770                                 }
771                         }
772                         return "directory entry not found";
773                 }
774         }
775         rerrstr(buf, sizeof(buf));
776         return buf;
777 }
778
779 static char*
780 oldwalk1(Fid *fid, char *name, void *arg)
781 {
782         Qid qid;
783         char *e;
784         Req *r;
785
786         r = arg;
787         assert(fid == r->newfid);
788         if(e = fswalk1(r, name, &qid))
789                 return e;
790         fid->qid = qid;
791         return nil;
792 }
793
794 static char*
795 oldclone(Fid *, Fid *, void *)
796 {
797         return nil;
798 }
799
800 static void
801 fswalk(Req *r)
802 {
803         walkandclone(r, oldwalk1, oldclone, r);
804 }
805
806 static void
807 fsread(Req *r)
808 {
809         uvlong path;
810         Node *nod;
811         uchar *p;
812         int np;
813
814         np = 0;
815         p = nil;
816         path = r->fid->qid.path;
817         if(nod = getnode(r, path)){
818                 switch(TYPE(path)){
819                 case Qroot:
820                 case Qstore:
821                 case Qobj:
822                         if(nod->d.qid.type & QTDIR){
823                                 if(readchilds(r, nod) < 0)
824                                         break;
825                                 dirread9p(r, nodegen, nod);
826                                 respond(r, nil);
827                                 return;
828                         }
829                         if(nod->data == nil){
830                                 int offset, count, pos;
831
832                                 offset = r->ifcall.offset;
833                                 if(offset >= nod->d.length){
834                                         r->ofcall.count = 0;
835                                         respond(r, nil);
836                                         return;
837                                 }
838                                 /* are these people stupid? */
839                                 pos = (offset == 0) ? 1 : 2;
840                                 count = r->ifcall.count;
841                                 if((count + offset) > nod->d.length){
842                                         count = nod->d.length - offset;
843                                         pos = 3;
844                                 }
845                                 if(!ptprpc(r, GetPartialObject, 4|DataRecv, 
846                                         nod->handle, offset, count, pos, &p, &np)){
847                                         if(np <= count){
848                                                 memmove(r->ofcall.data, p, np);
849                                                 r->ofcall.count = np;
850                                                 respond(r, nil);
851                                                 free(p);
852                                                 return;
853                                         }
854                                 }
855                                 free(p);
856                         }
857                         /* no break */
858                 case Qthumb:
859                         if(nod->data == nil){
860                                 np = 0;
861                                 p = nil;
862                                 if(ptprpc(r, TYPE(path)==Qthumb ? GetThumb : GetObject,
863                                         1|DataRecv, nod->handle, &p, &np) < 0){
864                                         free(p);
865                                         break;
866                                 }
867                                 nod->data = p;
868                                 nod->ndata = np;
869                         }
870                         readbuf(r, nod->data, nod->ndata);
871                         respond(r, nil);
872                         return;
873                 }
874         }
875         responderror(r);
876 }
877
878 static void
879 fsremove(Req *r)
880 {
881         Node *nod;
882         uvlong path;
883
884         path = r->fid->qid.path;
885         if(nod = getnode(r, path)){
886                 switch(TYPE(path)){
887                 default:
888                         werrstr(Eperm);
889                         break;
890                 case Qobj:
891                         if(ptprpc(r, DeleteObject, 2, nod->handle, 0) < 0)
892                                 break;
893                         /* no break */
894                 case Qthumb:
895                         if(nod = cachednode(path, nil))
896                                 freenode(nod);
897                         respond(r, nil);
898                         return;
899                 }
900         }
901         responderror(r);
902 }
903
904 static void
905 fsopen(Req *r)
906 {
907         if(r->ifcall.mode != OREAD){
908                 respond(r, Eperm);
909                 return;
910         }
911         respond(r, nil);
912 }
913
914 static void
915 fsflush(Req *r)
916 {
917         Channel *c;
918
919         if(c = r->oldreq->aux)
920                 nbsendp(c, Einterrupt);
921         respond(r, nil);
922 }
923
924 static void
925 fsdestroyfid(Fid *fid)
926 {
927         Node *nod;
928         uvlong path;
929
930         path = fid->qid.path;
931         switch(TYPE(path)){
932         case Qobj:
933         case Qthumb:
934                 if(nod = cachednode(path, nil)){
935                         free(nod->data);
936                         nod->data = nil;
937                         nod->ndata = 0;
938                 }
939                 break;
940         }
941 }
942
943 static void
944 fsend(Srv *)
945 {
946         ptprpc(nil, CloseSession, 0);
947         closeioproc(recvp(iochan));
948 }
949
950 static int
951 findendpoints(Dev *d, int *epin, int *epout, int *epint)
952 {
953         int i;
954         Ep *ep;
955         Usbdev *ud;
956
957         ud = d->usb;
958         *epin = *epout = *epint = -1;
959         for(i=0; i<nelem(ud->ep); i++){
960                 if((ep = ud->ep[i]) == nil)
961                         continue;
962                 if(ep->type == Eintr && *epint == -1)
963                         *epint = ep->id;
964                 if(ep->type != Ebulk)
965                         continue;
966                 if(ep->dir == Eboth || ep->dir == Ein)
967                         if(*epin == -1)
968                                 *epin =  ep->id;
969                 if(ep->dir == Eboth || ep->dir == Eout)
970                         if(*epout == -1)
971                                 *epout = ep->id;
972         }
973         if(*epin >= 0 && *epout >= 0)
974                 return 0;
975         return -1;
976 }
977
978 Srv fs = 
979 {
980         .attach = fsattach,
981         .destroyfid = fsdestroyfid,
982         .walk = fswalk,
983         .open = fsopen,
984         .read = fsread,
985         .remove = fsremove,
986         .stat = fsstat,
987         .flush = fsflush,
988         .end = fsend,
989 };
990
991 static void
992 usage(void)
993 {
994         fprint(2, "usage: %s [-dD] devid\n", argv0);
995         threadexits("usage");
996 }
997
998 void
999 threadmain(int argc, char **argv)
1000 {
1001         char name[64], desc[64];
1002         int epin, epout, epint;
1003         Dev *d;
1004
1005         ARGBEGIN {
1006         case 'd':
1007                 debug++;
1008                 break;
1009         case 'D':
1010                 chatty9p++;
1011                 break;
1012         default:
1013                 usage();
1014         } ARGEND;
1015
1016         if(argc == 0)
1017                 usage();
1018         if((d = getdev(atoi(*argv))) == nil)
1019                 sysfatal("opendev: %r");
1020         if(findendpoints(d, &epin, &epout, &epint)  < 0)
1021                 sysfatal("findendpoints: %r");
1022
1023         usbep[In] = openep(usbep[Setup] = d, epin);
1024         if(epin == epout){
1025                 incref(usbep[In]);
1026                 usbep[Out] = usbep[In];
1027                 opendevdata(usbep[In], ORDWR);
1028         } else {
1029                 usbep[Out] = openep(d, epout);
1030                 opendevdata(usbep[In], OREAD);
1031                 opendevdata(usbep[Out], OWRITE);
1032         }
1033         if(usbep[In]->dfd < 0 || usbep[Out]->dfd < 0)
1034                 sysfatal("open endpoints: %r");
1035         iochan = chancreate(sizeof(Ioproc*), 1);
1036         sendp(iochan, ioproc());
1037
1038         sessionId = getpid();
1039         if(ptprpc(nil, OpenSession, 1, sessionId) < 0)
1040                 sysfatal("open session: %r");
1041
1042         time0 = time(0);
1043
1044         snprint(name, sizeof name, "sdU%d.0", d->id);
1045         snprint(desc, sizeof desc, "%d.ptp", d->id);
1046         threadpostsharesrv(&fs, nil, name, desc);
1047
1048         threadexits(0);
1049 }