21 DataSend = 0x00010000,
22 DataRecv = 0x00020000,
23 OutParam = 0x00040000,
27 CloseSession = 0x1003,
28 GetStorageIds = 0x1004,
29 GetStorageInfo = 0x1005,
30 GetObjectHandles = 0x1007,
31 GetObjectInfo = 0x1008,
34 DeleteObject = 0x100B,
35 GetPartialObject = 0x101B,
40 typedef struct Ptprpc Ptprpc;
41 typedef struct Node Node;
78 static Dev *usbep[Setup+1];
82 static int maxpacket = 64;
83 static int sessionId = 1;
84 static int transId = 1;
89 static Channel *iochan;
91 char Eperm[] = "permission denied";
92 char Einterrupt[] = "interrupted";
94 #define PATH(type, n) ((uvlong)(type)|((uvlong)(n)<<4))
95 #define TYPE(path) ((int)((path)&0xF))
96 #define NUM(path) ((int)((path)>>4))
99 hexdump(char *prefix, uchar *p, int n)
106 s = emalloc9p(1+((n+1)*((m*6)+7)));
112 sprint(x, "\n%.4x: ", i);
115 printable = (p[i] >= 32 && p[i]<=127);
116 sprint(x, "%.2x %c ", (int)p[i], printable ? p[i] : '.');
119 fprint(2, "%20-s: %6d bytes %s\n", prefix, n, s);
128 rerrstr(err, sizeof(err));
129 if(strstr(err, Einterrupt) || strstr(err, "timed out")){
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" ,
153 "object write protected" ,
156 "no thumbnail present" ,
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",
166 "invalid parent object",
167 "invalid device prop format",
168 "invalid device prop value",
170 "session already opend",
171 "transaction canceld",
172 "specification of destination unsupported"
179 return "invalid error number";
184 ptpcheckerr(Ptprpc *rpc, int type, int transid, int length)
188 if(length < 4+2+2+4){
189 werrstr("short response: %d < %d", length, 4+2+2+4);
192 if(GET4(rpc->length) < length){
193 werrstr("unexpected response length 0x%x < 0x%x", GET4(rpc->length), length);
196 if(GET4(rpc->transid) != transid){
197 werrstr("unexpected transaction id 0x%x != 0x%x", GET4(rpc->transid), transid);
200 if(GET2(rpc->type) != type){
201 werrstr("unexpected response type 0x%x != 0x%x", GET2(rpc->type), type);
204 if(s = ptperrstr(GET2(rpc->code))){
206 return -GET2(rpc->code);
212 vptprpc(Ioproc *io, int code, int flags, va_list a)
224 PUT2(rpc.code, code);
225 PUT4(rpc.transid, t);
228 int x = va_arg(a, int);
232 hexdump("req>", (uchar*)&rpc, n);
234 if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
239 if(flags & DataSend){
243 sdata = va_arg(a, void*);
244 sdatalen = va_arg(a, int);
250 l = 4+2+2+4+sdatalen;
254 if((n = sdatalen) > sizeof(rpc.d))
256 memmove(rpc.d, p, n);
261 hexdump("data>", (uchar*)&rpc, n);
262 if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
270 if((n = iowrite(io, usbep[Out]->dfd, p, n)) < 0){
278 if(flags & DataRecv){
282 prdata = va_arg(a, void**);
283 prdatalen = va_arg(a, int*);
288 while((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) <= 0){
294 if((l = ptpcheckerr(&rpc, 2, t, n)) < 0)
296 if(l && GET2(rpc.type) == 3)
299 l = GET4(rpc.length);
308 hexdump("data<", rpc.d, n);
309 memmove(p, rpc.d, n);
315 while((n = ioread(io, usbep[In]->dfd, p, n)) <= 0){
323 hexdump("data<", p, n);
330 hexdump("data<", rpc.d, l);
331 memmove(p, rpc.d, l);
336 memmove(&rpc, rpc.d+l, n);
341 while((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) <= 0){
349 hexdump("resp<", (uchar*)&rpc, n);
350 if(ptpcheckerr(&rpc, 3, t, n))
352 if(flags & OutParam){
355 for(i=0; i<nelem(rpc.p); i++){
356 if((pp = va_arg(a, int*)) == nil)
358 *pp = GET4(rpc.p[i]);
365 ptprpc(Req *r, int code, int flags, ...)
382 c = chancreate(sizeof(char*), 0);
393 i = vptprpc(io, code, flags, va);
396 fprint(2, "rpc: %r\n");
411 ptparray4(uchar *d, uchar *e)
421 a = emalloc9p((1+n) * sizeof(int));
431 ptpstring2(uchar *d, uchar *e)
442 p = s = emalloc9p((n+1)*UTFmax);
450 p += runetochar(p, &r);
463 memset(d, 0, sizeof(*d));
467 copydir(Dir *d, Dir *s)
469 memmove(d, s, sizeof(*d));
471 d->name = estrdup9p(d->name);
473 d->uid = estrdup9p(d->uid);
475 d->gid = estrdup9p(d->gid);
477 d->muid = estrdup9p(d->muid);
481 cachednode(uvlong path, Node ***pf)
488 for(i=0; i<nnodes; i++){
489 if((x = nodes[i]) == nil){
494 if(x->d.qid.path == path)
501 getnode(Req *r, uvlong path)
508 if(x = cachednode(path, &f))
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;
523 x->d.qid.type = QTDIR;
524 x->d.mode = DMDIR|0777;
525 x->d.name = estrdup9p("/");
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);
539 if(ptprpc(r, GetObjectInfo, 1|DataRecv, NUM(path), &p, &np) < 0)
542 hexdump("objectinfo", p, np);
544 werrstr("bad objectinfo");
549 * another proc migh'v come in and done it for us,
550 * so check the cache again.
552 if(y = cachednode(path, &f))
555 if((x->d.name = ptpstring2(p+52, p+np)) == nil){
556 werrstr("bad objectinfo");
559 x->handle = NUM(path);
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;
567 if(TYPE(path) == Qthumb){
570 t = emalloc9p(8 + strlen(x->d.name));
571 sprint(t, "thumb_%s", x->d.name);
575 x->d.length = GET4(p+14);
577 x->d.length = GET4(p+8);
580 if(s = ptpstring2(p+(53+p[52]*2), p+np)){
584 // 0123 45 67 8 9A BC DF
585 // 2008 12 26 T 00 21 18
586 memset(&t, 0, sizeof(t));
593 t.hour = atoi(s+0x9);
595 t.mday = atoi(s+0x6);
597 t.mon = atoi(s+0x4) - 1;
599 t.year = atoi(s) - 1900;
601 x->d.atime = x->d.mtime = tm2sec(&t);
609 nodes = erealloc9p(nodes, sizeof(nodes[0]) * (nnodes + 64));
610 f = &nodes[nnodes++];
626 /* remove the node from the tree */
627 for(i=0; i<nnodes; i++){
639 readchilds(Req *r, Node *nod)
648 switch(TYPE(nod->d.qid.path)){
650 if(ptprpc(r, GetStorageIds, 0|DataRecv, &p, &np) < 0)
652 a = ptparray4(p, p+np);
656 for(i=0; a && i<a[0]; i++){
657 if((x = getnode(r, PATH(Qstore, a[i+1]))) == nil){
671 if(ptprpc(r, GetObjectHandles, 3|DataRecv, nod->store, 0, nod->handle, &p, &np) < 0)
673 a = ptparray4(p, p+np);
677 for(i=0; a && i<a[0]; i++){
678 if((x = getnode(r, PATH(Qobj, a[i+1]))) == nil){
687 /* skip thumb when not image format */
688 if((x->format & 0xFF00) != 0x3800)
690 if((x = getnode(r, PATH(Qthumb, a[i+1]))) == nil){
709 if(r->ifcall.aname && r->ifcall.aname[0]){
710 respond(r, "invalid attach specifier");
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;
725 if((nod = getnode(r, r->fid->qid.path)) == nil){
729 copydir(&r->d, &nod->d);
734 nodegen(int i, Dir *d, void *aux)
738 for(nod=nod->child; nod && i; nod=nod->next, i--)
748 fswalk1(Req *r, char *name, Qid *qid)
750 static char buf[ERRMAX];
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)
765 if(readchilds(r, nod) == 0){
766 for(nod=nod->child; nod; nod=nod->next){
767 if(strcmp(nod->d.name, name) == 0){
772 return "directory entry not found";
775 rerrstr(buf, sizeof(buf));
780 oldwalk1(Fid *fid, char *name, void *arg)
787 assert(fid == r->newfid);
788 if(e = fswalk1(r, name, &qid))
795 oldclone(Fid *, Fid *, void *)
803 walkandclone(r, oldwalk1, oldclone, r);
816 path = r->fid->qid.path;
817 if(nod = getnode(r, path)){
822 if(nod->d.qid.type & QTDIR){
823 if(readchilds(r, nod) < 0)
825 dirread9p(r, nodegen, nod);
829 if(nod->data == nil){
830 int offset, count, pos;
832 offset = r->ifcall.offset;
833 if(offset >= nod->d.length){
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;
845 if(!ptprpc(r, GetPartialObject, 4|DataRecv,
846 nod->handle, offset, count, pos, &p, &np)){
848 memmove(r->ofcall.data, p, np);
849 r->ofcall.count = np;
859 if(nod->data == nil){
862 if(ptprpc(r, TYPE(path)==Qthumb ? GetThumb : GetObject,
863 1|DataRecv, nod->handle, &p, &np) < 0){
870 readbuf(r, nod->data, nod->ndata);
884 path = r->fid->qid.path;
885 if(nod = getnode(r, path)){
891 if(ptprpc(r, DeleteObject, 2, nod->handle, 0) < 0)
895 if(nod = cachednode(path, nil))
907 if(r->ifcall.mode != OREAD){
919 if(c = r->oldreq->aux)
920 nbsendp(c, Einterrupt);
925 fsdestroyfid(Fid *fid)
930 path = fid->qid.path;
934 if(nod = cachednode(path, nil)){
946 ptprpc(nil, CloseSession, 0);
947 closeioproc(recvp(iochan));
951 findendpoints(Dev *d, int *epin, int *epout, int *epint)
958 *epin = *epout = *epint = -1;
959 for(i=0; i<nelem(ud->ep); i++){
960 if((ep = ud->ep[i]) == nil)
962 if(ep->type == Eintr && *epint == -1)
964 if(ep->type != Ebulk)
966 if(ep->dir == Eboth || ep->dir == Ein)
969 if(ep->dir == Eboth || ep->dir == Eout)
973 if(*epin >= 0 && *epout >= 0)
981 .destroyfid = fsdestroyfid,
994 fprint(2, "usage: %s [-dD] devid\n", argv0);
995 threadexits("usage");
999 threadmain(int argc, char **argv)
1001 char name[64], desc[64];
1002 int epin, epout, epint;
1018 if((d = getdev(atoi(*argv))) == nil)
1019 sysfatal("opendev: %r");
1020 if(findendpoints(d, &epin, &epout, &epint) < 0)
1021 sysfatal("findendpoints: %r");
1023 usbep[In] = openep(usbep[Setup] = d, epin);
1026 usbep[Out] = usbep[In];
1027 opendevdata(usbep[In], ORDWR);
1029 usbep[Out] = openep(d, epout);
1030 opendevdata(usbep[In], OREAD);
1031 opendevdata(usbep[Out], OWRITE);
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());
1038 sessionId = getpid();
1039 if(ptprpc(nil, OpenSession, 1, sessionId) < 0)
1040 sysfatal("open session: %r");
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);