ref: 6f4f7004681fb3156bdccfe9619423541801e5d1
parent: f65f8427a0544a7f1d04d4a4d33cecc118eb47a5
author: cinap_lenrek <cinap_lenrek@localhost>
date: Sun Aug 21 23:05:01 EDT 2011
nusb/ptp: fix memory leaks, use libthread/ioprocs, cleanup
--- a/sys/src/cmd/nusb/ptp/mkfile
+++ b/sys/src/cmd/nusb/ptp/mkfile
@@ -5,7 +5,7 @@
TARG=ptp
HFILES=
-OFILES=usbptp.$O
+OFILES=ptp.$O
</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/nusb/ptp/ptp.c
@@ -1,0 +1,1014 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <auth.h>
+#include <fcall.h>
+#include <9p.h>
+
+#include "usb.h"
+
+enum
+{+ Qroot,
+ Qstore,
+ Qobj,
+ Qthumb,
+};
+
+enum {+ /* flags */
+ DataSend = 0x00010000,
+ DataRecv = 0x00020000,
+ OutParam = 0x00040000,
+
+ /* rpc codes */
+ OpenSession = 0x1002,
+ CloseSession = 0x1003,
+ GetStorageIds = 0x1004,
+ GetStorageInfo = 0x1005,
+ GetObjectHandles = 0x1007,
+ GetObjectInfo = 0x1008,
+ GetObject = 0x1009,
+ GetThumb = 0x100A,
+ DeleteObject = 0x100B,
+};
+
+typedef struct Ptprpc Ptprpc;
+typedef struct Node Node;
+typedef struct Ioflush Ioflush;
+
+struct Ptprpc
+{+ uchar length[4];
+ uchar type[2];
+ uchar code[2];
+ uchar transid[4];
+ union {+ uchar p[5][4];
+ uchar d[500];
+ };
+};
+
+struct Node
+{+ Dir d;
+
+ Node *parent;
+ Node *next;
+ Node *child;
+
+ int store;
+ int handle;
+ int format;
+
+ void *data;
+ int ndata;
+};
+
+struct Ioflush
+{+ Channel *c;
+ Ioproc *io;
+};
+
+
+enum {+ In,
+ Out,
+};
+
+static Dev *usbep[2];
+
+static int debug;
+static ulong time0;
+static int maxpacket = 64;
+static int sessionId = 0;
+static int transId = 0;
+
+static Node **nodes;
+static int nnodes;
+
+static Channel *iochan;
+
+char Eperm[] = "permission denied";
+char Einterrupt[] = "interrupted";
+
+#define PATH(type, n) ((uvlong)(type)|((uvlong)(n)<<4))
+#define TYPE(path) ((int)((path)&0xF))
+#define NUM(path) ((int)((path)>>4))
+
+static void
+hexdump(char *prefix, uchar *p, int n)
+{+ char *s;
+ int i;
+ int m;
+
+ m = 12;
+ s = emalloc9p(1+((n+1)*((m*6)+7)));
+ s[0] = '\0';
+ for(i=0; i<n; i++){+ int printable;
+ char x[8];
+ if((i % m)==0){+ sprint(x, "\n%.4x: ", i);
+ strcat(s, x);
+ }
+ printable = (p[i] >= 32 && p[i]<=127);
+ sprint(x, "%.2x %c ", (int)p[i], printable ? p[i] : '.');
+ strcat(s, x);
+ }
+ fprint(2, "%20-s: %6d bytes %s\n", prefix, n, s);
+ free(s);
+}
+
+static int
+wasinterrupt(void)
+{+ char err[ERRMAX];
+
+ rerrstr(err, sizeof(err));
+ if(strstr(err, Einterrupt) || strstr(err, "timed out")){+ werrstr(Einterrupt);
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+ptperrstr(int code)
+{+ static char *a[] = {+ "undefined",
+ nil ,
+ "general error" ,
+ "session not open" ,
+ "invalid transaction id" ,
+ "operation not supported" ,
+ "parameter not supported" ,
+ "incomplete transfer" ,
+ "invalid storage id" ,
+ "invalid object handle" ,
+ "device prop not supported" ,
+ "invalid object format code" ,
+ "storage full" ,
+ "object write protected" ,
+ "store read only" ,
+ "access denied" ,
+ "no thumbnail present" ,
+ "self test failed" ,
+ "partial deletion" ,
+ "store not available" ,
+ "specification by format unsupported" ,
+ "no valid object info" ,
+ "invalid code format" ,
+ "unknown vendor code",
+ "capture already terminated",
+ "device busy",
+ "invalid parent object",
+ "invalid device prop format",
+ "invalid device prop value",
+ "invalid parameter",
+ "session already opend",
+ "transaction canceld",
+ "specification of destination unsupported"
+ };
+
+ code -= 0x2000;
+ if(code < 0)
+ return nil;
+ if(code >= nelem(a))
+ return "invalid error number";
+ return a[code];
+}
+
+static int
+ptpcheckerr(Ptprpc *rpc, int type, int transid, int length)
+{+ char *s;
+
+ if(length < 4+2+2+4){+ werrstr("short response: %d < %d", length, 4+2+2+4);+ return 1;
+ }
+ if(GET4(rpc->length) < length){+ werrstr("unexpected response length 0x%x < 0x%x", GET4(rpc->length), length);+ return 1;
+ }
+ if(GET4(rpc->transid) != transid){+ werrstr("unexpected transaction id 0x%x != 0x%x", GET4(rpc->transid), transid);+ return 1;
+ }
+ if(GET2(rpc->type) != type){+ werrstr("unexpected response type 0x%x != 0x%x", GET2(rpc->type), type);+ return 1;
+ }
+ if(s = ptperrstr(GET2(rpc->code))){+ werrstr("%s", s);+ return -GET2(rpc->code);
+ }
+ return 0;
+}
+
+static int
+vptprpc(Ioproc *io, int code, int flags, va_list a)
+{+ Ptprpc rpc;
+ int np, n, t, i, l;
+ uchar *b, *p, *e;
+
+ np = flags & 0xF;
+ n = 4+2+2+4+4*np;
+ t = transId++;
+
+ PUT4(rpc.length, n);
+ PUT2(rpc.type, 1);
+ PUT2(rpc.code, code);
+ PUT4(rpc.transid, t);
+
+ for(i=0; i<np; i++){+ int x = va_arg(a, int);
+ PUT4(rpc.p[i], x);
+ }
+ if(debug)
+ hexdump("req>", (uchar*)&rpc, n);+
+ werrstr("");+ if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){+ wasinterrupt();
+ return -1;
+ }
+
+ if(flags & DataSend){+ void *sdata;
+ int sdatalen;
+
+ sdata = va_arg(a, void*);
+ sdatalen = va_arg(a, int);
+
+ b = (uchar*)sdata;
+ p = b;
+ e = b + sdatalen;
+
+ l = 4+2+2+4+sdatalen;
+ PUT4(rpc.length, l);
+ PUT2(rpc.type, 2);
+
+ if((n = sdatalen) > sizeof(rpc.d))
+ n = sizeof(rpc.d);
+ memmove(rpc.d, p, n);
+ p += n;
+ n += (4+2+2+4);
+
+ if(debug)
+ hexdump("data>", (uchar*)&rpc, n);+ if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){+ wasinterrupt();
+ return -1;
+ }
+ while(p < e){ + if((n = iowrite(io, usbep[Out]->dfd, p, e-p)) < 0){+ if(!wasinterrupt())
+ return -1;
+ }
+ p += n;
+ }
+ }
+
+ if(flags & DataRecv){+ void **prdata;
+ int *prdatalen;
+
+ prdata = va_arg(a, void**);
+ prdatalen = va_arg(a, int*);
+
+ *prdata = nil;
+ *prdatalen = 0;
+
+ do{+ if((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) < 0){+ wasinterrupt();
+ return -1;
+ }
+ if(debug)
+ hexdump("data<", (uchar*)&rpc, n);+ if((l = ptpcheckerr(&rpc, 2, t, n)) < 0)
+ return -1;
+ } while(l);
+
+ l = GET4(rpc.length);
+ if((l < 4+2+2+4) || (l < n)){+ werrstr("invalid recvdata length");+ return -1;
+ }
+
+ l -= (4+2+2+4);
+ n -= (4+2+2+4);
+
+ b = emalloc9p(l);
+ p = b;
+ e = b+l;
+ memmove(p, rpc.d, n);
+ p += n;
+
+ while(p < e){+ if((n = ioread(io, usbep[In]->dfd, p, e-p)) < 0){+ wasinterrupt();
+ free(b);
+ return -1;
+ }
+ p += n;
+ }
+ *prdata = b;
+ *prdatalen = e-b;
+ }
+
+ do {+ if((n = ioread(io, usbep[In]->dfd, &rpc, sizeof(rpc))) < 0){+ wasinterrupt();
+ return -1;
+ }
+ if(debug)
+ hexdump("resp<", (uchar*)&rpc, n);+ if((l = ptpcheckerr(&rpc, 3, t, n)) < 0)
+ return -1;
+ } while(l);
+
+ if(flags & OutParam){+ int *pp;
+
+ for(i=0; i<nelem(rpc.p); i++){+ if((pp = va_arg(a, int*)) == nil)
+ break;
+ *pp = GET4(rpc.p[i]);
+ }
+ }
+ return 0;
+}
+
+static void
+ioflusher(void *aux)
+{+ Ioflush f = *((Ioflush*)aux);
+ while(recvp(f.c))
+ iointerrupt(f.io);
+ chanfree(f.c);
+}
+
+static int
+ptprpc(Req *r, int code, int flags, ...)
+{+ va_list va;
+ Ioflush f;
+ Alt a[3];
+ char *m;
+ int i;
+
+ i = -1;
+ f.c = nil;
+ f.io = nil;
+ m = Einterrupt;
+ a[0].op = CHANRCV;
+ a[0].c = iochan;
+ a[0].v = &f.io;
+ if(r){+ f.c = chancreate(sizeof(char*), 0);
+ a[1].op = CHANRCV;
+ a[1].c = f.c;
+ a[1].v = &m;
+ a[2].op = CHANEND;
+ r->aux = f.c;
+ srvrelease(r->srv);
+ } else
+ a[1].op = CHANEND;
+ if(alt(a) == 0){+ ioflush(f.io);
+ if(f.c)
+ threadcreate(ioflusher, &f, 4*1024);
+ va_start(va, flags);
+ i = vptprpc(f.io, code, flags, va);
+ va_end(va);
+ } else
+ werrstr("%s", m);+ if(r){+ srvacquire(r->srv);
+ r->aux = nil;
+ }
+ if(f.io){+ if(f.c)
+ sendp(f.c, nil);
+ sendp(iochan, f.io);
+ } else if(f.c)
+ chanfree(f.c);
+ return i;
+}
+
+static int*
+ptparray4(uchar *d, uchar *e)
+{+ int *a, i, n;
+
+ if(d + 4 > e)
+ return nil;
+ n = GET4(d);
+ d += 4;
+ if(d + n*4 > e)
+ return nil;
+ a = emalloc9p((1+n) * sizeof(int));
+ a[0] = n;
+ for(i=0; i<n; i++){+ a[i+1] = GET4(d);
+ d += 4;
+ }
+ return a;
+}
+
+static char*
+ptpstring2(uchar *d, uchar *e)
+{+ int n, i;
+ char *s, *p;
+
+ if(d+1 > e)
+ return nil;
+ n = *d;
+ d++;
+ if(d + n*2 > e)
+ return nil;
+ p = s = emalloc9p((n+1)*UTFmax);
+ for(i=0; i<n; i++){+ Rune r;
+
+ r = GET2(d);
+ d += 2;
+ if(r == 0)
+ break;
+ p += runetochar(p, &r);
+ }
+ *p = 0;
+ return s;
+}
+
+static void
+cleardir(Dir *d)
+{+ free(d->name);
+ free(d->uid);
+ free(d->gid);
+ free(d->muid);
+ memset(d, 0, sizeof(*d));
+}
+
+static void
+copydir(Dir *d, Dir *s)
+{+ memmove(d, s, sizeof(*d));
+ if(d->name)
+ d->name = estrdup9p(d->name);
+ if(d->uid)
+ d->uid = estrdup9p(d->uid);
+ if(d->gid)
+ d->gid = estrdup9p(d->gid);
+ if(d->muid)
+ d->muid = estrdup9p(d->muid);
+}
+
+static Node*
+cachednode(uvlong path, Node ***pf)
+{+ Node *x;
+ int i;
+
+ if(pf)
+ *pf = nil;
+ for(i=0; i<nnodes; i++){+ if((x = nodes[i]) == nil){+ if(pf)
+ *pf = &nodes[i];
+ continue;
+ }
+ if(x->d.qid.path == path)
+ return x;
+ }
+ return nil;
+}
+
+static Node*
+getnode(Req *r, uvlong path)
+{+ Node *x, *y, **f;
+ uchar *p;
+ int np;
+ char *s;
+
+ if(x = cachednode(path, &f))
+ return x;
+
+ y = nil;
+ x = emalloc9p(sizeof(*x));
+ memset(x, 0, sizeof(*x));
+ x->d.qid.path = path;
+ x->d.uid = estrdup9p("ptp");+ x->d.gid = estrdup9p("usb");+ x->d.atime = x->d.mtime = time0;
+
+ p = nil;
+ np = 0;
+ switch(TYPE(path)){+ case Qroot:
+ x->d.qid.type = QTDIR;
+ x->d.mode = DMDIR|0777;
+ x->d.name = estrdup9p("/");+ goto Addnode;
+
+ case Qstore:
+ x->store = NUM(path);
+ x->handle = 0xffffffff;
+ x->d.qid.type = QTDIR;
+ x->d.mode = DMDIR|0777;
+ x->d.name = emalloc9p(10);
+ sprint(x->d.name, "%x", x->store);
+ goto Addnode;
+
+ case Qobj:
+ case Qthumb:
+ if(ptprpc(r, GetObjectInfo, 1|DataRecv, NUM(path), &p, &np) < 0)
+ break;
+ if(debug)
+ hexdump("objectinfo", p, np);+ if(np < 52){+ werrstr("bad objectinfo");+ break;
+ }
+
+ /*
+ * another proc migh'v come in and done it for us,
+ * so check the cache again.
+ */
+ if(y = cachednode(path, &f))
+ break;
+
+ if((x->d.name = ptpstring2(p+52, p+np)) == nil){+ werrstr("bad objectinfo");+ break;
+ }
+ x->handle = NUM(path);
+ x->store = GET4(p);
+ x->format = GET2(p+4);
+ if(x->format == 0x3001 && GET2(p+42) == 1){+ x->d.qid.type = QTDIR;
+ x->d.mode = DMDIR|0777;
+ } else {+ x->d.mode = 0666;
+ if(TYPE(path) == Qthumb){+ char *t;
+
+ t = emalloc9p(8 + strlen(x->d.name));
+ sprint(t, "thumb_%s", x->d.name);
+ free(x->d.name);
+ x->d.name = t;
+
+ x->d.length = GET4(p+14);
+ } else {+ x->d.length = GET4(p+8);
+ }
+ }
+ if(s = ptpstring2(p+(53+p[52]*2), p+np)){+ if(strlen(s) >= 15){+ Tm t;
+
+ // 0123 45 67 8 9A BC DF
+ // 2008 12 26 T 00 21 18
+ memset(&t, 0, sizeof(t));
+
+ s[0x10] = 0;
+ t.sec = atoi(s+0xD);
+ s[0xD] = 0;
+ t.min = atoi(s+0xB);
+ s[0xB] = 0;
+ t.hour = atoi(s+0x9);
+ s[0x8] = 0;
+ t.mday = atoi(s+0x6);
+ s[0x6] = 0;
+ t.mon = atoi(s+0x4) - 1;
+ s[0x4] = 0;
+ t.year = atoi(s) - 1900;
+
+ x->d.atime = x->d.mtime = tm2sec(&t);
+ }
+ free(s);
+ }
+ free(p);
+ Addnode:
+ if(f == nil){+ if(nnodes % 64 == 0)
+ nodes = erealloc9p(nodes, sizeof(nodes[0]) * (nnodes + 64));
+ f = &nodes[nnodes++];
+ }
+ return *f = x;
+ }
+
+ cleardir(&x->d);
+ free(x);
+ free(p);
+ return y;
+}
+
+static void
+freenode(Node *nod)
+{+ int i;
+
+ /* remove the node from the tree */
+ for(i=0; i<nnodes; i++){+ if(nod == nodes[i]){+ nodes[i] = nil;
+ break;
+ }
+ }
+ cleardir(&nod->d);
+ free(nod->data);
+ free(nod);
+}
+
+static int
+readchilds(Req *r, Node *nod)
+{+ int e, i;
+ int *a;
+ uchar *p;
+ int np;
+ Node *x, **xx;
+
+ e = 0;
+ switch(TYPE(nod->d.qid.path)){+ case Qroot:
+ if(ptprpc(r, GetStorageIds, 0|DataRecv, &p, &np) < 0)
+ return -1;
+ a = ptparray4(p, p+np);
+ free(p);
+ xx = &nod->child;
+ *xx = nil;
+ for(i=0; a && i<a[0]; i++){+ if((x = getnode(r, PATH(Qstore, a[i+1]))) == nil){+ e = -1;
+ break;
+ }
+ x->parent = nod;
+ *xx = x;
+ xx = &x->next;
+ *xx = nil;
+ }
+ free(a);
+ break;
+
+ case Qstore:
+ case Qobj:
+ if(ptprpc(r, GetObjectHandles, 3|DataRecv, nod->store, 0, nod->handle, &p, &np) < 0)
+ return -1;
+ a = ptparray4(p, p+np);
+ free(p);
+ xx = &nod->child;
+ *xx = nil;
+ for(i=0; a && i<a[0]; i++){+ if((x = getnode(r, PATH(Qobj, a[i+1]))) == nil){+ e = -1;
+ break;
+ }
+ x->parent = nod;
+ *xx = x;
+ xx = &x->next;
+ *xx = nil;
+
+ /* skip thumb when not image format */
+ if((x->format & 0xFF00) != 0x3800)
+ continue;
+ if((x = getnode(r, PATH(Qthumb, a[i+1]))) == nil){+ e = -1;
+ break;
+ }
+ x->parent = nod;
+ *xx = x;
+ xx = &x->next;
+ *xx = nil;
+ }
+ free(a);
+ break;
+ }
+
+ return e;
+}
+
+static void
+fsattach(Req *r)
+{+ if(r->ifcall.aname && r->ifcall.aname[0]){+ respond(r, "invalid attach specifier");
+ return;
+ }
+ r->fid->qid.path = PATH(Qroot, 0);
+ r->fid->qid.type = QTDIR;
+ r->fid->qid.vers = 0;
+ r->ofcall.qid = r->fid->qid;
+ respond(r, nil);
+}
+
+static void
+fsstat(Req *r)
+{+ Node *nod;
+
+ if((nod = getnode(r, r->fid->qid.path)) == nil){+ responderror(r);
+ return;
+ }
+ copydir(&r->d, &nod->d);
+ respond(r, nil);
+}
+
+static int
+nodegen(int i, Dir *d, void *aux)
+{+ Node *nod = aux;
+
+ for(nod=nod->child; nod && i; nod=nod->next, i--)
+ ;
+ if(i==0 && nod){+ copydir(d, &nod->d);
+ return 0;
+ }
+ return -1;
+}
+
+static char*
+fswalk1(Req *r, char *name, Qid *qid)
+{+ static char buf[ERRMAX];
+ uvlong path;
+ Node *nod;
+ Fid *fid;
+
+ fid = r->newfid;
+ path = fid->qid.path;
+ if(!(fid->qid.type&QTDIR))
+ return "walk in non-directory";
+ if(nod = getnode(r, path)){+ if(strcmp(name, "..") == 0){+ if(nod = nod->parent)
+ *qid = nod->d.qid;
+ return nil;
+ }
+ if(readchilds(r, nod) == 0){+ for(nod=nod->child; nod; nod=nod->next){+ if(strcmp(nod->d.name, name) == 0){+ *qid = nod->d.qid;
+ return nil;
+ }
+ }
+ return "directory entry not found";
+ }
+ }
+ rerrstr(buf, sizeof(buf));
+ return buf;
+}
+
+static char*
+oldwalk1(Fid *fid, char *name, void *arg)
+{+ Qid qid;
+ char *e;
+ Req *r;
+
+ r = arg;
+ assert(fid == r->newfid);
+ if(e = fswalk1(r, name, &qid))
+ return e;
+ fid->qid = qid;
+ return nil;
+}
+
+static char*
+oldclone(Fid *, Fid *, void *)
+{+ return nil;
+}
+
+static void
+fswalk(Req *r)
+{+ walkandclone(r, oldwalk1, oldclone, r);
+}
+
+static void
+fsread(Req *r)
+{+ uvlong path;
+ Node *nod;
+ uchar *p;
+ int np;
+
+ np = 0;
+ p = nil;
+ path = r->fid->qid.path;
+ if(nod = getnode(r, path)){+ switch(TYPE(path)){+ case Qroot:
+ case Qstore:
+ case Qobj:
+ if(nod->d.qid.type & QTDIR){+ if(readchilds(r, nod) < 0)
+ break;
+ dirread9p(r, nodegen, nod);
+ respond(r, nil);
+ return;
+ }
+ /* no break */
+ case Qthumb:
+ if(nod->data == nil){+ if(ptprpc(r, TYPE(path)==Qthumb ? GetThumb : GetObject,
+ 1|DataRecv, nod->handle, &p, &np) < 0)
+ break;
+ nod->data = p;
+ nod->ndata = np;
+ }
+ readbuf(r, nod->data, nod->ndata);
+ respond(r, nil);
+ return;
+ }
+ }
+ free(p);
+ responderror(r);
+}
+
+static void
+fsremove(Req *r)
+{+ Node *nod;
+ uvlong path;
+
+ path = r->fid->qid.path;
+ if(nod = getnode(r, path)){+ switch(TYPE(path)){+ default:
+ werrstr(Eperm);
+ break;
+ case Qobj:
+ if(ptprpc(r, DeleteObject, 2, nod->handle, 0) < 0)
+ break;
+ /* no break */
+ case Qthumb:
+ if(nod = cachednode(path, nil))
+ freenode(nod);
+ respond(r, nil);
+ return;
+ }
+ }
+ responderror(r);
+}
+
+static void
+fsopen(Req *r)
+{+ if(r->ifcall.mode != OREAD){+ respond(r, Eperm);
+ return;
+ }
+ respond(r, nil);
+}
+
+static void
+fsflush(Req *r)
+{+ Channel *c;
+
+ if(c = r->oldreq->aux)
+ sendp(c, Einterrupt);
+ respond(r, nil);
+}
+
+static void
+fsdestroyfid(Fid *fid)
+{+ Node *nod;
+ uvlong path;
+
+ path = fid->qid.path;
+ switch(TYPE(path)){+ case Qobj:
+ case Qthumb:
+ if(nod = cachednode(path, nil)){+ free(nod->data);
+ nod->data = nil;
+ nod->ndata = 0;
+ }
+ break;
+ }
+}
+
+static void
+fsend(Srv *)
+{+ ptprpc(nil, CloseSession, 0);
+ closeioproc(recvp(iochan));
+}
+
+static int
+findendpoints(Dev *d, int *epin, int *epout)
+{+ int i;
+ Ep *ep;
+ Usbdev *ud;
+
+ ud = d->usb;
+ *epin = *epout = -1;
+ for(i=0; i<nelem(ud->ep); i++){+ if((ep = ud->ep[i]) == nil)
+ continue;
+ if(ep->type != Ebulk)
+ continue;
+ if(ep->dir == Eboth || ep->dir == Ein)
+ if(*epin == -1)
+ *epin = ep->id;
+ if(ep->dir == Eboth || ep->dir == Eout)
+ if(*epout == -1)
+ *epout = ep->id;
+ if(*epin >= 0 && *epout >= 0)
+ return 0;
+ }
+ return -1;
+}
+
+Srv fs =
+{+ .attach = fsattach,
+ .destroyfid = fsdestroyfid,
+ .walk = fswalk,
+ .open = fsopen,
+ .read = fsread,
+ .remove = fsremove,
+ .stat = fsstat,
+ .flush = fsflush,
+ .end = fsend,
+};
+
+static void
+usage(void)
+{+ fprint(2, "usage: %s [-dD] devid\n", argv0);
+ threadexits("usage");+}
+
+void
+threadmain(int argc, char **argv)
+{+ char name[64], desc[64];
+ int epin, epout;
+ Dev *d;
+
+ ARGBEGIN {+ case 'd':
+ debug++;
+ break;
+ case 'D':
+ chatty9p++;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if(argc == 0)
+ usage();
+ if((d = getdev(atoi(*argv))) == nil)
+ sysfatal("opendev: %r");+ if(findendpoints(d, &epin, &epout) < 0)
+ sysfatal("findendpoints: %r");+
+ usbep[In] = openep(d, epin);
+ if(epin == epout){+ incref(usbep[In]);
+ usbep[Out] = usbep[In];
+ opendevdata(usbep[In], ORDWR);
+ } else {+ usbep[Out] = openep(d, epout);
+ opendevdata(usbep[In], OREAD);
+ opendevdata(usbep[Out], OWRITE);
+ }
+ if(usbep[In]->dfd < 0 || usbep[Out]->dfd < 0)
+ sysfatal("open endpoints: %r");+
+ iochan = chancreate(sizeof(Ioproc*), 1);
+ sendp(iochan, ioproc());
+
+ sessionId = getpid();
+ if(ptprpc(nil, OpenSession, 1, sessionId) < 0)
+ sysfatal("open session: %r");+
+ time0 = time(0);
+
+ snprint(name, sizeof name, "sdU%d.0", d->id);
+ snprint(desc, sizeof desc, "%d.ptp", d->id);
+ threadpostsharesrv(&fs, nil, name, desc);
+
+ threadexits(0);
+}
--- a/sys/src/cmd/nusb/ptp/usbptp.c
+++ /dev/null
@@ -1,987 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <bio.h>
-#include <auth.h>
-#include <fcall.h>
-#include <9p.h>
-
-#include "usb.h"
-
-enum
-{- Qroot,
- Qstore,
- Qobj,
- Qthumb,
-};
-
-enum {- /* flags */
- DataSend = 0x00010000,
- DataRecv = 0x00020000,
- OutParam = 0x00040000,
-
- /* rpc codes */
- OpenSession = 0x1002,
- CloseSession = 0x1003,
- GetStorageIds = 0x1004,
- GetStorageInfo = 0x1005,
- GetObjectHandles = 0x1007,
- GetObjectInfo = 0x1008,
- GetObject = 0x1009,
- GetThumb = 0x100A,
- DeleteObject = 0x100B,
-};
-
-typedef struct Ptprpc Ptprpc;
-struct Ptprpc
-{- uchar length[4];
- uchar type[2];
- uchar code[2];
- uchar transid[4];
- union {- uchar p[5][4];
- uchar d[500];
- };
-};
-
-typedef struct Node Node;
-struct Node
-{- Dir d;
-
- Node *parent;
- Node *next;
- Node *child;
-
- int store;
- int handle;
- int format;
-
- void *data;
- int ndata;
-};
-
-int debug;
-
-enum {- In,
- Out,
-};
-static Dev *usbep[2];
-
-static ulong time0;
-static int maxpacket = 64;
-static int sessionId = 0;
-static int transId = 0;
-
-static Node **nodes;
-static int nnodes;
-
-static char *uname;
-
-#define PATH(type, n) ((uvlong)(type)|((uvlong)(n)<<4))
-#define TYPE(path) ((int)((path)&0xF))
-#define NUM(path) ((int)((path)>>4))
-
-static void
-hexdump(char *prefix, uchar *p, int n)
-{- char *s;
- int i;
- int m;
-
- m = 12;
- s = emalloc9p(1+((n+1)*((m*6)+7)));
- s[0] = '\0';
- for(i=0; i<n; i++){- int printable;
- char x[8];
- if((i % m)==0){- sprint(x, "\n%.4x: ", i);
- strcat(s, x);
- }
- printable = (p[i] >= 32 && p[i]<=127);
- sprint(x, "%.2x %c ", (int)p[i], printable ? p[i] : '.');
- strcat(s, x);
- }
- fprint(2, "%20-s: %6d bytes %s\n", prefix, n, s);
- free(s);
-}
-
-static int
-wasinterrupt(void)
-{- char err[ERRMAX];
-
- rerrstr(err, sizeof(err));
- if(strstr(err, "interrupted") || strstr(err, "request timed out")){- werrstr("interrupted");- return 1;
- }
- return 0;
-}
-
-static char *
-ptperrstr(int code)
-{- static char *a[] = {- "undefined",
- nil ,
- "general error" ,
- "session not open" ,
- "invalid transaction id" ,
- "operation not supported" ,
- "parameter not supported" ,
- "incomplete transfer" ,
- "invalid storage id" ,
- "invalid object handle" ,
- "device prop not supported" ,
- "invalid object format code" ,
- "storage full" ,
- "object write protected" ,
- "store read only" ,
- "access denied" ,
- "no thumbnail present" ,
- "self test failed" ,
- "partial deletion" ,
- "store not available" ,
- "specification by format unsupported" ,
- "no valid object info" ,
- "invalid code format" ,
- "unknown vendor code",
- "capture already terminated",
- "device busy",
- "invalid parent object",
- "invalid device prop format",
- "invalid device prop value",
- "invalid parameter",
- "session already opend",
- "transaction canceld",
- "specification of destination unsupported"
- };
-
- code -= 0x2000;
- if(code < 0)
- return nil;
- if(code >= nelem(a))
- return "invalid error number";
- return a[code];
-}
-
-static int
-ptpcheckerr(Ptprpc *rpc, int type, int transid, int length)
-{- char *s;
-
- if(length < 4+2+2+4){- werrstr("short response: %d < %d", length, 4+2+2+4);- return 1;
- }
- if(GET4(rpc->length) < length){- werrstr("unexpected response length 0x%x < 0x%x", GET4(rpc->length), length);- return 1;
- }
- if(GET4(rpc->transid) != transid){- werrstr("unexpected transaction id 0x%x != 0x%x", GET4(rpc->transid), transid);- return 1;
- }
- if(GET2(rpc->type) != type){- werrstr("unexpected response type 0x%x != 0x%x", GET2(rpc->type), type);- return 1;
- }
- if(s = ptperrstr(GET2(rpc->code))){- werrstr("%s", s);- return -GET2(rpc->code);
- }
- return 0;
-}
-
-static int
-vptprpc(int code, int flags, va_list a)
-{- Ptprpc rpc;
- int np, n, t, i, l;
- uchar *b, *p, *e;
-
- np = flags & 0xF;
- n = 4+2+2+4+4*np;
- t = transId++;
-
- PUT4(rpc.length, n);
- PUT2(rpc.type, 1);
- PUT2(rpc.code, code);
- PUT4(rpc.transid, t);
-
- for(i=0; i<np; i++){- int x = va_arg(a, int);
- PUT4(rpc.p[i], x);
- }
- if(debug)
- hexdump("req>", (uchar*)&rpc, n);-
- werrstr("");- if(write(usbep[Out]->dfd, &rpc, n) != n){- wasinterrupt();
- return -1;
- }
-
- if(flags & DataSend){- void *sdata;
- int sdatalen;
-
- sdata = va_arg(a, void*);
- sdatalen = va_arg(a, int);
-
- b = (uchar*)sdata;
- p = b;
- e = b + sdatalen;
-
- l = 4+2+2+4+sdatalen;
- PUT4(rpc.length, l);
- PUT2(rpc.type, 2);
-
- if((n = sdatalen) > sizeof(rpc.d))
- n = sizeof(rpc.d);
- memmove(rpc.d, p, n);
- p += n;
- n += (4+2+2+4);
-
- if(debug)
- hexdump("data>", (uchar*)&rpc, n);- if(write(usbep[Out]->dfd, &rpc, n) != n){- wasinterrupt();
- return -1;
- }
- while(p < e){ - if((n = write(usbep[Out]->dfd, p, e-p)) < 0){- wasinterrupt();
- break;
- }
- p += n;
- }
- }
-
- if(flags & DataRecv){- void **prdata;
- int *prdatalen;
-
- prdata = va_arg(a, void**);
- prdatalen = va_arg(a, int*);
-
- *prdata = nil;
- *prdatalen = 0;
-
- do{- if((n = read(usbep[In]->dfd, &rpc, sizeof(rpc))) < 0){- wasinterrupt();
- return -1;
- }
- if(debug > 1)
- hexdump("data<", (uchar*)&rpc, n);- if((l = ptpcheckerr(&rpc, 2, t, n)) < 0)
- return -1;
- } while(l);
-
- l = GET4(rpc.length);
- if((l < 4+2+2+4) || (l < n)){- werrstr("invalid recvdata length");- return -1;
- }
-
- l -= (4+2+2+4);
- n -= (4+2+2+4);
-
- b = emalloc9p(l);
- p = b;
- e = b+l;
- memmove(p, rpc.d, n);
- p += n;
-
- while(p < e){- if((n = read(usbep[In]->dfd, p, e-p)) < 0){- wasinterrupt();
- free(b);
- return -1;
- }
- p += n;
- }
- *prdata = b;
- *prdatalen = e-b;
- }
-
- do {- if((n = read(usbep[In]->dfd, &rpc, sizeof(rpc))) < 0){- wasinterrupt();
- return -1;
- }
- if(debug > 1)
- hexdump("resp<", (uchar*)&rpc, n);- if((l = ptpcheckerr(&rpc, 3, t, n)) < 0)
- return -1;
- } while(l);
-
- if(flags & OutParam){- int *pp;
-
- for(i=0; i<nelem(rpc.p); i++){- if((pp = va_arg(a, int*)) == nil)
- break;
- *pp = GET4(rpc.p[i]);
- }
- }
- return 0;
-}
-
-static int
-ptprpc(Req *r, int code, int flags, ...)
-{- static long ptpsem = 1;
- va_list a;
- int i;
-
- if(r != nil){- r->aux = (void*)getpid();
- srvrelease(r->srv);
- }
- i = semacquire(&ptpsem, 1);
- if(i == 1){- va_start(a, flags);
- i = vptprpc(code, flags, a);
- va_end(a);
- semrelease(&ptpsem, 1);
- }
- if(i < 0 && debug)
- fprint(2, "ptprpc: req=%p %r\n", r);
- if(r != nil){- srvacquire(r->srv);
- r->aux = (void*)-1;
- }
- return i;
-}
-
-static int*
-ptparray4(uchar *d, uchar *e)
-{- int *a, i, n;
-
- if(d + 4 > e)
- return nil;
- n = GET4(d);
- d += 4;
- if(d + n*4 > e)
- return nil;
- a = emalloc9p((1+n) * sizeof(int));
- a[0] = n;
- for(i=0; i<n; i++){- a[i+1] = GET4(d);
- d += 4;
- }
- return a;
-}
-
-static char*
-ptpstring2(uchar *d, uchar *e)
-{- int n, i;
- char *s, *p;
-
- if(d+1 > e)
- return nil;
- n = *d;
- d++;
- if(d + n*2 > e)
- return nil;
- p = s = emalloc9p((n+1)*UTFmax);
- for(i=0; i<n; i++){- Rune r;
-
- r = GET2(d);
- d += 2;
- if(r == 0)
- break;
- p += runetochar(p, &r);
- }
- *p = 0;
- return s;
-}
-
-static void
-cleardir(Dir *d)
-{- free(d->name);
- free(d->uid);
- free(d->gid);
- free(d->muid);
- memset(d, 0, sizeof(*d));
-}
-
-static void
-copydir(Dir *d, Dir *s)
-{- memmove(d, s, sizeof(*d));
- if(d->name)
- d->name = estrdup9p(d->name);
- if(d->uid)
- d->uid = estrdup9p(d->uid);
- if(d->gid)
- d->gid = estrdup9p(d->gid);
- if(d->muid)
- d->muid = estrdup9p(d->muid);
-}
-
-static Node*
-getnode(uvlong path, Req *r)
-{- int i, j;
- Node *x;
- uchar *p;
- int np;
- char *s;
-
- j = -1;
- for(i=0; i<nnodes; i++){- if((x = nodes[i]) == nil){- j = i;
- continue;
- }
- if(x->d.qid.path == path)
- return x;
- }
-
- x = emalloc9p(sizeof(*x));
-
- memset(x, 0, sizeof(*x));
-
- x->d.qid.path = path;
- x->d.uid = estrdup9p(uname);
- x->d.gid = estrdup9p(uname);
- x->d.atime = x->d.mtime = time0;
-
- p = nil;
- np = 0;
- switch(TYPE(path)){- case Qroot:
- x->d.qid.type = QTDIR;
- x->d.mode = DMDIR|0777;
- x->d.name = estrdup9p("/");- break;
-
- case Qstore:
- x->store = NUM(path);
- x->handle = 0xffffffff;
- x->d.qid.type = QTDIR;
- x->d.mode = DMDIR|0777;
- x->d.name = emalloc9p(10);
- sprint(x->d.name, "%x", x->store);
- break;
-
- case Qobj:
- case Qthumb:
- if(ptprpc(r, GetObjectInfo, 1|DataRecv, NUM(path), &p, &np) < 0)
- goto err;
- if(debug > 1)
- hexdump("objectinfo", p, np);- if(np < 52){- werrstr("bad objectinfo");- goto err;
- }
-
- j = -1;
- for(i=0; i<nnodes; i++){- if(nodes[i] == nil){- j = i;
- continue;
- }
- if(nodes[i]->d.qid.path == path){- /* never mind */
- cleardir(&x->d);
- free(x);
- return nodes[i];
- }
- }
-
- if((x->d.name = ptpstring2(p+52, p+np)) == nil){- werrstr("bad objectinfo");- goto err;
- }
- x->handle = NUM(path);
- x->store = GET4(p);
- x->format = GET2(p+4);
- if(x->format == 0x3001 && GET2(p+42) == 1){- x->d.qid.type = QTDIR;
- x->d.mode = DMDIR|0777;
- } else {- x->d.mode = 0666;
- if(TYPE(path) == Qthumb){- char *t;
-
- t = emalloc9p(8 + strlen(x->d.name));
- sprint(t, "thumb_%s", x->d.name);
- free(x->d.name);
- x->d.name = t;
-
- x->d.length = GET4(p+14);
- } else {- x->d.length = GET4(p+8);
- }
- }
- if(s = ptpstring2(p+(53+p[52]*2), p+np)){- if(strlen(s) >= 15){- Tm t;
-
- // 0123 45 67 8 9A BC DF
- // 2008 12 26 T 00 21 18
- memset(&t, 0, sizeof(t));
-
- s[0x10] = 0;
- t.sec = atoi(s+0xD);
- s[0xD] = 0;
- t.min = atoi(s+0xB);
- s[0xB] = 0;
- t.hour = atoi(s+0x9);
- s[0x8] = 0;
- t.mday = atoi(s+0x6);
- s[0x6] = 0;
- t.mon = atoi(s+0x4) - 1;
- s[0x4] = 0;
- t.year = atoi(s) - 1900;
-
- x->d.atime = x->d.mtime = tm2sec(&t);
- }
- free(s);
- }
- free(p);
- break;
- }
-
- if(j < 0){- if(nnodes % 64 == 0)
- nodes = erealloc9p(nodes, sizeof(nodes[0]) * (nnodes + 64));
- j = nnodes++;
- }
- return nodes[j] = x;
-
-err:
- cleardir(&x->d);
- free(x);
- free(p);
-
- return nil;
-}
-
-static void
-freenode(Node *nod)
-{- int i;
-
- /* remove the node from the tree */
- for(i=0; i<nnodes; i++){- if(nod == nodes[i]){- nodes[i] = nil;
- break;
- }
- }
- cleardir(&nod->d);
- free(nod->data);
- free(nod);
-}
-
-static int
-readchilds(Node *nod, Req *r)
-{- int i;
- int *a;
- uchar *p;
- int np;
- Node *x, **xx;
-
- switch(TYPE(nod->d.qid.path)){- case Qroot:
- if(ptprpc(r, GetStorageIds, 0|DataRecv, &p, &np) < 0)
- return -1;
- a = ptparray4(p, p+np);
- free(p);
- xx = &nod->child;
- for(i=0; a && i<a[0]; i++){- if(x = getnode(PATH(Qstore, a[i+1]), r)){- x->parent = nod;
- *xx = x;
- xx = &x->next;
- }
- }
- *xx = nil;
- free(a);
- break;
-
- case Qstore:
- case Qobj:
- if(ptprpc(r, GetObjectHandles, 3|DataRecv, nod->store, 0, nod->handle, &p, &np) < 0)
- return -1;
- a = ptparray4(p, p+np);
- free(p);
- xx = &nod->child;
- for(i=0; a && i<a[0]; i++){- if(x = getnode(PATH(Qobj, a[i+1]), r)){- x->parent = nod;
- *xx = x;
- xx = &x->next;
-
- /* skip thumb when not image format */
- if((x->format & 0xFF00) != 0x3800)
- continue;
- }
- if(x = getnode(PATH(Qthumb, a[i+1]), r)){- x->parent = nod;
- *xx = x;
- xx = &x->next;
- }
- }
- *xx = nil;
- free(a);
- break;
- }
-
- return 0;
-}
-
-static void
-fsattach(Req *r)
-{- if(r->ifcall.aname && r->ifcall.aname[0]){- respond(r, "invalid attach specifier");
- return;
- }
- if(uname == nil)
- uname = estrdup9p(r->ifcall.uname);
- r->fid->qid.path = PATH(Qroot, 0);
- r->fid->qid.type = QTDIR;
- r->fid->qid.vers = 0;
- r->ofcall.qid = r->fid->qid;
- respond(r, nil);
-}
-
-static void
-fsstat(Req *r)
-{- Node *nod;
-
- if((nod = getnode(r->fid->qid.path, r)) == nil){- responderror(r);
- return;
- }
- copydir(&r->d, &nod->d);
- respond(r, nil);
-}
-
-static int
-nodegen(int i, Dir *d, void *aux)
-{- Node *nod = aux;
-
- for(nod=nod->child; nod && i; nod=nod->next, i--)
- ;
- if(i==0 && nod){- copydir(d, &nod->d);
- return 0;
- }
- return -1;
-}
-
-static char*
-fswalk1(Req *r, char *name, Qid *qid)
-{- static char buf[ERRMAX];
- uvlong path;
- Node *nod;
- Fid *fid;
-
- fid = r->newfid;
- path = fid->qid.path;
- if(!(fid->qid.type&QTDIR))
- return "walk in non-directory";
-
- if((nod = getnode(path, r)) == nil)
- goto err;
-
- if(strcmp(name, "..") == 0){- if(nod = nod->parent)
- *qid = nod->d.qid;
- return nil;
- }
- if(readchilds(nod, r) < 0)
- goto err;
- for(nod=nod->child; nod; nod=nod->next){- if(strcmp(nod->d.name, name) == 0){- *qid = nod->d.qid;
- return nil;
- }
- }
- return "directory entry not found";
-
-err:
- rerrstr(buf, sizeof(buf));
- return buf;
-}
-
-static char*
-oldwalk1(Fid *fid, char *name, void *arg)
-{- Qid qid;
- char *e;
- Req *r;
-
- r = arg;
- assert(fid == r->newfid);
- if(e = fswalk1(r, name, &qid))
- return e;
- fid->qid = qid;
- return nil;
-}
-
-static char*
-oldclone(Fid *, Fid *, void *)
-{- return nil;
-}
-
-static void
-fswalk(Req *r)
-{- walkandclone(r, oldwalk1, oldclone, r);
-}
-
-static void
-fsread(Req *r)
-{- uvlong path;
- Node *nod;
- uchar *p;
- int np;
-
- np = 0;
- p = nil;
- path = r->fid->qid.path;
- if((nod = getnode(path, r)) == nil)
- goto err;
-
- if(nod->d.qid.type & QTDIR){- if(readchilds(nod, r) < 0)
- goto err;
- dirread9p(r, nodegen, nod);
- respond(r, nil);
- return;
- }
-
- switch(TYPE(path)){- default:
- werrstr("bug in fsread path=%llux", path);- break;
-
- case Qobj:
- case Qthumb:
- if(nod->data == nil){- if(TYPE(path)==Qthumb){- if(ptprpc(r, GetThumb, 1|DataRecv, nod->handle, &p, &np) < 0)
- goto err;
- } else {- if(ptprpc(r, GetObject, 1|DataRecv, nod->handle, &p, &np) < 0)
- goto err;
- }
- nod->data = p;
- nod->ndata = np;
- }
- readbuf(r, nod->data, nod->ndata);
- respond(r, nil);
- return;
- }
-err:
- free(p);
- responderror(r);
-}
-
-static void
-fsremove(Req *r)
-{- Node *nod;
- uvlong path;
-
- path = r->fid->qid.path;
- if((nod = getnode(path, r)) == nil)
- goto err;
-
- switch(TYPE(path)){- default:
- werrstr("bug in fsremove path=%llux", path);- break;
- case Qobj:
- if(ptprpc(r, DeleteObject, 2, nod->handle, 0) < 0)
- goto err;
- case Qthumb:
- freenode(nod);
- respond(r, nil);
- return;
- }
-err:
- responderror(r);
-}
-
-static void
-fsopen(Req *r)
-{- if(r->ifcall.mode != OREAD){- respond(r, "permission denied");
- return;
- }
- respond(r, nil);
-}
-
-static void
-fsflush(Req *r)
-{- Req *o;
-
- if(o = r->oldreq){- if(debug)
- fprint(2, "fsflush: req=%p\n", o);
- int pid = (int)o->aux;
- if(pid != -1 && pid != 0){- o->aux = (void*)-1;
- postnote(PNPROC, pid, "interrupt");
- }
- }
- respond(r, nil);
-}
-
-static void
-fsdestroyfid(Fid *fid)
-{- Node *nod;
- uvlong path;
-
- path = fid->qid.path;
- switch(TYPE(path)){- case Qobj:
- case Qthumb:
- if(nod = getnode(path, nil)){- free(nod->data);
- nod->data = nil;
- nod->ndata = 0;
- }
- break;
- }
-}
-
-static void
-fsend(Srv *)
-{- ptprpc(nil, CloseSession, 0);
-}
-
-static int
-findendpoints(Dev *d, int *epin, int *epout)
-{- int i;
- Ep *ep;
- Usbdev *ud;
-
- ud = d->usb;
- *epin = *epout = -1;
- for(i=0; i<nelem(ud->ep); i++){- if((ep = ud->ep[i]) == nil)
- continue;
- if(ep->type != Ebulk)
- continue;
- if(ep->dir == Eboth || ep->dir == Ein)
- if(*epin == -1)
- *epin = ep->id;
- if(ep->dir == Eboth || ep->dir == Eout)
- if(*epout == -1)
- *epout = ep->id;
- if(*epin >= 0 && *epout >= 0)
- return 0;
- }
- return -1;
-}
-
-static int
-inote(void *, char *msg)
-{- if(strstr(msg, "interrupt"))
- return 1;
- return 0;
-}
-
-Srv fs =
-{-.attach= fsattach,
-.destroyfid= fsdestroyfid,
-.walk= fswalk,
-.open= fsopen,
-.read= fsread,
-.remove= fsremove,
-.stat= fsstat,
-.flush= fsflush,
-.end= fsend,
-};
-
-static void
-usage(void)
-{- fprint(2, "usage: %s [-dD] devid\n", argv0);
- exits("usage");-}
-
-void
-main(int argc, char **argv)
-{- int epin, epout;
- char name[64], desc[64];
- Dev *d;
-
- ARGBEGIN {- case 'd':
- debug++;
- break;
- case 'D':
- chatty9p++;
- break;
- default:
- usage();
- } ARGEND;
-
- if(argc == 0)
- usage();
- if((d = getdev(atoi(*argv))) == nil)
- sysfatal("opendev: %r");- if(findendpoints(d, &epin, &epout) < 0)
- sysfatal("findendpoints: %r");-
- usbep[In] = openep(d, epin);
- if(epin == epout){- incref(usbep[In]);
- usbep[Out] = usbep[In];
- opendevdata(usbep[In], ORDWR);
- } else {- usbep[Out] = openep(d, epout);
- opendevdata(usbep[In], OREAD);
- opendevdata(usbep[Out], OWRITE);
- }
- if(usbep[In]->dfd < 0 || usbep[Out]->dfd < 0)
- sysfatal("open endpoints: %r");-
- sessionId = getpid();
- if(ptprpc(nil, OpenSession, 1, sessionId) < 0)
- return;
-
- atnotify(inote, 1);
- time0 = time(0);
-
- snprint(name, sizeof name, "sdU%d.0", d->id);
- snprint(desc, sizeof desc, "%d.ptp", d->id);
- postsharesrv(&fs, nil, name, desc);
-
- exits(0);
-}
--
⑨