]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/usbd/usbd.c
add nusb/cam
[plan9front.git] / sys / src / cmd / nusb / usbd / usbd.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include <9p.h>
6 #include "usb.h"
7 #include "dat.h"
8 #include "fns.h"
9
10 enum {
11         Qroot,
12         Qusbevent,
13         Qmax
14 };
15
16 char *names[] = {
17         "",
18         "usbevent",
19 };
20
21 static char Enonexist[] = "does not exist";
22
23 typedef struct Event Event;
24
25 struct Event {
26         Dev *dev;       /* the device producing the event,
27                            dev->aux points to Fid processing the event */
28         char *data;
29         int len;
30         Event *link;
31         int ref;        /* number of readers which will read this one
32                            the next time they'll read */
33         int prev;       /* number of events pointing to this one with
34                            their link pointers */
35 };
36
37 static Event *evlast;
38 static Req *reqfirst, *reqlast;
39 static QLock evlock;
40
41 static void
42 addreader(Req *req)
43 {
44         req->aux = nil;
45         if(reqfirst == nil)
46                 reqfirst = req;
47         else
48                 reqlast->aux = req;
49         reqlast = req;
50 }
51
52 static void
53 fulfill(Req *req, Event *e)
54 {
55         int n;
56         
57         n = e->len;
58         if(n > req->ifcall.count)
59                 n = req->ifcall.count;
60         memmove(req->ofcall.data, e->data, n);
61         req->ofcall.count = n;
62 }
63
64 static void
65 initevent(void)
66 {
67         evlast = emallocz(sizeof(Event), 1);
68 }
69
70 static Event*
71 putevent(Event *e)
72 {
73         Event *ee;
74
75         ee = e->link;
76         if(e->ref || e->prev)
77                 return ee;
78         ee->prev--;
79         closedev(e->dev);
80         free(e->data);
81         free(e);
82         return ee;
83 }
84
85 static void
86 procreqs(void)
87 {
88         Req *r, *p, *x;
89         Event *e;
90         Fid *f;
91
92 Loop:
93         for(p = nil, r = reqfirst; r != nil; p = r, r = x){
94                 x = r->aux;
95                 f = r->fid;
96                 e = f->aux;
97                 if(e == evlast)
98                         continue;
99                 if(e->dev->aux == f){
100                         e->dev->aux = nil;      /* release device */
101                         e->ref--;
102                         e = putevent(e);
103                         e->ref++;
104                         f->aux = e;
105                         goto Loop;
106                 }
107                 if(e->dev->aux == nil){
108                         e->dev->aux = f;        /* claim device */
109                         if(x == nil)
110                                 reqlast = p;
111                         if(p == nil)
112                                 reqfirst = x;
113                         else
114                                 p->aux = x;
115                         r->aux = nil;
116                         fulfill(r, e);
117                         respond(r, nil);
118                         goto Loop;
119                 }
120         }
121 }
122
123 static void
124 pushevent(Dev *d, char *data)
125 {
126         Event *e;
127         
128         qlock(&evlock);
129         e = evlast;
130         evlast = emallocz(sizeof(Event), 1);
131         incref(d);
132         e->dev = d;
133         e->data = data;
134         e->len = strlen(data);
135         e->link = evlast;
136         evlast->prev++;
137         procreqs();
138         putevent(e);
139         qunlock(&evlock);
140 }
141
142 static int
143 dirgen(int n, Dir *d, void *)
144 {
145         if(n >= Qmax - 1)
146                 return -1;
147         d->qid.path = n + 1;
148         d->qid.vers = 0;
149         if(n >= 0){
150                 d->qid.type = 0;
151                 d->mode = 0444;
152         }else{
153                 d->qid.type = QTDIR;
154                 d->mode = 0555 | DMDIR;
155         }
156         d->uid = estrdup9p(getuser());
157         d->gid = estrdup9p(d->uid);
158         d->muid = estrdup9p(d->uid);
159         d->name = estrdup9p(names[n+1]);
160         d->atime = d->mtime = time(0);
161         d->length = 0;
162         return 0;
163 }
164
165 static void
166 usbdattach(Req *req)
167 {
168         req->fid->qid = (Qid) {Qroot, 0, QTDIR};
169         req->ofcall.qid = req->fid->qid;
170         respond(req, nil);
171 }
172
173 static char *
174 usbdwalk(Fid *fid, char *name, Qid *qid)
175 {
176         int i;
177
178         if(fid->qid.path != Qroot)
179                 return "not a directory";
180         if(strcmp(name, "..") == 0){
181                 *qid = fid->qid;
182                 return nil;
183         }
184         for(i = Qroot+1; i < Qmax; i++)
185                 if(strcmp(name, names[i]) == 0){
186                         fid->qid = (Qid) {i, 0, 0};
187                         *qid = fid->qid;
188                         return nil;
189                 }
190         return Enonexist;
191 }
192
193 static void
194 usbdread(Req *req)
195 {
196         switch((long)req->fid->qid.path){
197         case Qroot:
198                 dirread9p(req, dirgen, nil);
199                 respond(req, nil);
200                 break;
201         case Qusbevent:
202                 qlock(&evlock);
203                 addreader(req);
204                 procreqs();
205                 qunlock(&evlock);
206                 break;
207         default:
208                 respond(req, Enonexist);
209                 break;
210         }
211 }
212
213 static void
214 usbdstat(Req *req)
215 {
216         if(dirgen(req->fid->qid.path - 1, &req->d, nil) < 0)
217                 respond(req, Enonexist);
218         else
219                 respond(req, nil);
220 }
221
222 static char *
223 formatdev(Dev *d, int type)
224 {
225         Usbdev *u = d->usb;
226         return smprint("%s %d %.4x %.4x %.6lx %s\n",
227                 type ? "detach" : "attach",
228                 d->id, u->vid, u->did, u->csp, 
229                 d->hname != nil ? d->hname : "");
230 }
231
232 static void
233 enumerate(Event **l)
234 {
235         extern Hub *hubs;
236
237         Event *e;
238         Hub *h;
239         Port *p;
240         Dev *d;
241         int i;
242         
243         for(h = hubs; h != nil; h = h->next){
244                 for(i = 1; i <= h->nport; i++){
245                         p = &h->port[i];
246                         d = p->dev;
247                         if(d == nil || d->usb == nil || p->hub != nil)
248                                 continue;
249                         e = emallocz(sizeof(Event), 1);
250                         incref(d);
251                         e->dev = d;
252                         e->data = formatdev(d, 0);
253                         e->len = strlen(e->data);
254                         e->prev = 1;
255                         *l = e;
256                         l = &e->link;
257
258                 }
259         }
260         *l = evlast;
261         evlast->prev++;
262 }
263
264 static void
265 usbdopen(Req *req)
266 {
267         extern QLock hublock;
268
269         if(req->fid->qid.path == Qusbevent){
270                 Event *e;
271
272                 qlock(&hublock);
273                 qlock(&evlock);
274
275                 enumerate(&e);
276                 e->prev--;
277                 e->ref++;
278                 req->fid->aux = e;
279
280                 qunlock(&evlock);
281                 qunlock(&hublock);
282         }
283         respond(req, nil);
284 }
285
286 static void
287 usbddestroyfid(Fid *fid)
288 {
289         if(fid->qid.path == Qusbevent){
290                 Event *e;
291
292                 qlock(&evlock);
293                 e = fid->aux;
294                 if(e != nil){
295                         fid->aux = nil;
296                         if(e->dev != nil && e->dev->aux == fid){
297                                 e->dev->aux = nil;      /* release device */
298                                 procreqs();
299                         }
300                         e->ref--;
301                         while(e->ref == 0 && e->prev == 0 && e != evlast)
302                                 e = putevent(e);
303                 }
304                 qunlock(&evlock);
305         }
306 }
307
308 static void
309 usbdflush(Req *req)
310 {
311         Req *r, *p, *x;
312
313         qlock(&evlock);
314         for(p = nil, r = reqfirst; r != nil; p = r, r = x){
315                 x = r->aux;
316                 if(r == req->oldreq){
317                         if(x == nil)
318                                 reqlast = p;
319                         if(p == nil)
320                                 reqfirst = x;
321                         else
322                                 p->aux = x;
323                         r->aux = nil;
324                         respond(r, "interrupted");
325                         break;
326                 }
327         }
328         qunlock(&evlock);
329         respond(req, nil);
330 }
331
332 Srv usbdsrv = {
333         .attach = usbdattach,
334         .walk1 = usbdwalk,
335         .read = usbdread,
336         .stat = usbdstat,
337         .open = usbdopen,
338         .flush = usbdflush,
339         .destroyfid = usbddestroyfid,
340 };
341
342 static void
343 assignhname(Dev *dev)
344 {
345         extern Hub *hubs;
346         char buf[64];
347         Usbdev *ud;
348         Hub *h;
349         int i;
350
351         ud = dev->usb;
352
353         /* build string of device unique stuff */
354         snprint(buf, sizeof(buf), "%.4x%.4x%.4x%.6lx%s",
355                 ud->vid, ud->did, ud->dno, ud->csp, ud->serial);
356
357         hname(buf);
358
359         /* check for collisions */
360         for(h = hubs; h != nil; h = h->next){
361                 for(i = 1; i <= h->nport; i++){
362                         if(h->port[i].dev == nil)
363                                 continue;
364                         if(h->port[i].dev->hname == nil || h->port[i].dev == dev)
365                                 continue;
366                         if(strcmp(h->port[i].dev->hname, buf) == 0){
367                                 dev->hname = smprint("%s%d", buf, dev->id);
368                                 return;
369                         }
370                 }
371         }
372         dev->hname = strdup(buf);
373 }
374
375 int
376 attachdev(Port *p)
377 {
378         Dev *d = p->dev;
379         int id;
380
381         if(d->usb->class == Clhub){
382                 /*
383                  * Hubs are handled directly by this process avoiding
384                  * concurrent operation so that at most one device
385                  * has the config address in use.
386                  * We cancel kernel debug for these eps. too chatty.
387                  */
388                 if((p->hub = newhub(d->dir, d)) == nil)
389                         return -1;
390                 return 0;
391         }
392
393         /* create all endpoint files for default conf #1 */
394         for(id=1; id<nelem(d->usb->ep); id++){
395                 Ep *ep = d->usb->ep[id];
396                 if(ep != nil && ep->conf != nil && ep->conf->cval == 1){
397                         Dev *epd = openep(d, id);
398                         if(epd != nil)
399                                 closedev(epd);
400                 }
401         }
402
403         /* close control endpoint so driver can open it */
404         close(d->dfd);
405         d->dfd = -1;
406
407         /* assign stable name based on device descriptor */
408         assignhname(d);
409
410         /* set device info for ctl file */
411         devctl(d, "info %s csp %#08lux vid %#.4ux did %#.4ux %q %q %s",
412                 classname(Class(d->usb->csp)), d->usb->csp, d->usb->vid, d->usb->did,
413                 d->usb->vendor, d->usb->product, d->hname);
414
415         pushevent(d, formatdev(d, 0));
416         return 0;
417 }
418
419 void
420 detachdev(Port *p)
421 {
422         Dev *d = p->dev;
423
424         if(d->usb->class == Clhub)
425                 return;
426         pushevent(d, formatdev(d, 1));
427 }
428
429 /*
430  * we create /env/usbbusy on startup and once all devices have been
431  * enumerated and readers have consumed all the events, we remove the
432  * file so nusbrc can continue.
433  */
434 static int busyfd = -1;
435
436 void
437 checkidle(void)
438 {
439         if(busyfd < 0 || reqlast == nil || evlast == nil || evlast->prev > 0)
440                 return;
441
442         close(busyfd);
443         busyfd = -1;
444 }
445
446 void
447 main(int argc, char **argv)
448 {
449         int fd, i, nd;
450         Dir *d;
451
452         ARGBEGIN {
453         case 'D':
454                 chatty9p++;
455                 break;
456         case 'd':
457                 usbdebug++;
458                 break;
459         } ARGEND;
460
461         busyfd = create("/env/usbbusy", ORCLOSE, 0600);
462         quotefmtinstall();
463         initevent();
464         rfork(RFNOTEG);
465         switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
466         case -1: sysfatal("rfork: %r");
467         case 0: work(); exits(nil);
468         }
469         if(argc == 0){
470                 if((fd = open("/dev/usb", OREAD)) < 0){
471                         rendezvous(work, nil);
472                         sysfatal("/dev/usb: %r");
473                 }
474                 nd = dirreadall(fd, &d);
475                 close(fd);
476                 if(nd < 2){
477                         rendezvous(work, nil);
478                         sysfatal("/dev/usb: no hubs");
479                 }
480                 for(i = 0; i < nd; i++)
481                         if(strcmp(d[i].name, "ctl") != 0)
482                                 rendezvous(work, smprint("/dev/usb/%s", d[i].name));
483                 free(d);
484         }else
485                 for(i = 0; i < argc; i++)
486                         rendezvous(work, estrdup9p(argv[i]));
487         rendezvous(work, nil);
488         postsharesrv(&usbdsrv, nil, "usb", "usbd");
489         exits(nil);
490 }