]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/usbd/usbd.c
nusb: handle sub hubs
[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         char *data;
27         int len;
28         Event *link;
29         int ref;  /* number of readers which will read this one
30                      the next time they'll read */
31         int prev; /* number of events pointing to this one with
32                      their link pointers */
33 };
34
35 static Event *evlast;
36 static Req *reqfirst, *reqlast;
37 static QLock evlock;
38
39 static void
40 addreader(Req *req)
41 {
42         req->aux = nil;
43         if(reqfirst == nil)
44                 reqfirst = req;
45         else
46                 reqlast->aux = req;
47         reqlast = req;
48 }
49
50 static void
51 fulfill(Req *req, Event *e)
52 {
53         int n;
54         
55         n = e->len;
56         if(n > req->ifcall.count)
57                 n = req->ifcall.count;
58         memmove(req->ofcall.data, e->data, n);
59         req->ofcall.count = n;
60 }
61
62 static void
63 initevent(void)
64 {
65         evlast = emallocz(sizeof(Event), 1);
66 }
67
68 static void
69 readevent(Req *req)
70 {
71         Event *e;
72
73         qlock(&evlock);
74         e = req->fid->aux;
75         if(e == evlast){
76                 addreader(req);
77                 qunlock(&evlock);
78                 return;
79         }
80         fulfill(req, e);
81         req->fid->aux = e->link;
82         e->link->ref++;
83         if(--e->ref == 0 && e->prev == 0){
84                 e->link->prev--;
85                 free(e->data);
86                 free(e);
87         }
88         qunlock(&evlock);
89         respond(req, nil);
90 }
91
92 static void
93 pushevent(char *data)
94 {
95         Event *e, *ee;
96         Req *r, *rr;
97         
98         qlock(&evlock);
99         e = evlast;
100         ee = emallocz(sizeof(Event), 1);
101         evlast = ee;
102         e->data = data;
103         e->len = strlen(data);
104         e->link = ee;
105         ee->prev++;
106         for(r = reqfirst; r != nil; r = rr){
107                 rr = r->aux;
108                 r->aux = nil;
109                 r->fid->aux = ee;
110                 ee->ref++;
111                 e->ref--;
112                 fulfill(r, e);
113                 respond(r, nil);
114         }
115         if(e->ref == 0 && e->prev == 0){
116                 ee->prev--;
117                 free(e->data);
118                 free(e);
119         }
120         reqfirst = nil;
121         reqlast = nil;
122         qunlock(&evlock);
123 }
124
125 static int
126 dirgen(int n, Dir *d, void *)
127 {
128         if(n >= Qmax - 1)
129                 return -1;
130         d->qid.path = n + 1;
131         d->qid.vers = 0;
132         if(n >= 0)
133                 d->qid.type = 0;
134         else
135                 d->qid.type = QTDIR;
136         d->uid = strdup(getuser());
137         d->gid = strdup(d->uid);
138         d->muid = strdup(d->uid);
139         d->name = strdup(names[n+1]);
140         d->mode = 0555 | (d->qid.type << 24);
141         d->atime = d->mtime = time(0);
142         d->length = 0;
143         return 0;
144 }
145
146 static void
147 usbdattach(Req *req)
148 {
149         req->fid->qid = (Qid) {Qroot, 0, QTDIR};
150         req->ofcall.qid = req->fid->qid;
151         respond(req, nil);
152 }
153
154 static char *
155 usbdwalk(Fid *fid, char *name, Qid *qid)
156 {
157         int i;
158
159         if(strcmp(name, "..") == 0){
160                 fid->qid = (Qid) {Qroot, 0, QTDIR};
161                 *qid = fid->qid;
162                 return nil;
163         }
164         if(fid->qid.path != Qroot)
165                 return "not a directory";
166         for(i = 0; i < Qmax; i++)
167                 if(strcmp(name, names[i]) == 0){
168                         fid->qid = (Qid) {i, 0, 0};
169                         *qid = fid->qid;
170                         return nil;
171                 }
172         return "does not exist";
173 }
174
175 static void
176 usbdread(Req *req)
177 {
178         switch((long)req->fid->qid.path){
179         case Qroot:
180                 dirread9p(req, dirgen, nil);
181                 respond(req, nil);
182                 break;
183         case Qusbevent:
184                 if(req->fid->aux == nil){
185                         respond(req, "the front fell off");
186                         return;
187                 }
188                 readevent(req);
189                 break;
190         default:
191                 respond(req, Enonexist);
192                 break;
193         }
194 }
195
196 static void
197 usbdstat(Req *req)
198 {
199         if(dirgen(req->fid->qid.path - 1, &req->d, nil) < 0)
200                 respond(req, "the front fell off");
201         else
202                 respond(req, nil);
203 }
204
205 static char *
206 formatdev(Dev *d)
207 {
208         Usbdev *u;
209         
210         u = d->usb;
211         return smprint("in id %d vid 0x%.4x did 0x%.4x csp 0x%.8lx\n",
212                 d->id, u->vid, u->did, u->csp);
213 }
214
215 static void
216 enumerate(Event **l)
217 {
218         Event *e;
219         Hub *h;
220         Port *p;
221         extern Hub *hubs;
222         
223         for(h = hubs; h != nil; h = h->next){
224                 for(p = h->port; p < h->port + h->nport; p++){
225                         if(p->dev == nil || p->dev->usb == nil || p->hub != nil)
226                                 continue;
227                         e = emallocz(sizeof(Event), 1);
228                         e->data = formatdev(p->dev);
229                         e->len = strlen(e->data);
230                         e->prev = 1;
231                         *l = e;
232                         l = &e->link;
233                 }
234         }
235         *l = evlast;
236         evlast->prev++;
237 }
238
239 static void
240 usbdopen(Req *req)
241 {
242         extern QLock hublock;
243
244         if(req->fid->qid.path == Qusbevent){
245                 qlock(&hublock);
246                 qlock(&evlock);
247                 enumerate(&req->fid->aux);
248                 ((Event *)req->fid->aux)->ref++;
249                 ((Event *)req->fid->aux)->prev--;
250                 qunlock(&evlock);
251                 qunlock(&hublock);
252         }
253         respond(req, nil);
254 }
255
256 static void
257 usbddestroyfid(Fid *fid)
258 {
259         Event *e, *ee;
260
261         if(fid->qid.path == Qusbevent && fid->aux != nil){
262                 qlock(&evlock);
263                 e = fid->aux;
264                 if(--e->ref == 0 && e->prev == 0){
265                         while(e->ref == 0 && e->prev == 0 && e != evlast){
266                                 ee = e->link;
267                                 ee->prev--;
268                                 free(e->data);
269                                 free(e);
270                                 e = ee;
271                         }
272                 }
273                 qunlock(&evlock);
274         }
275 }
276
277 static void
278 usbdflush(Req *req)
279 {
280         Req **l, *r;
281         qlock(&evlock);
282         l = &reqfirst;
283         while(r = *l){
284                 if(r == req->oldreq){
285                         *l = r->aux;
286                         break;
287                 }
288                 l = &r->aux;
289         }
290         qunlock(&evlock);
291         respond(req->oldreq, "interrupted");
292         respond(req, nil);
293 }
294
295 Srv usbdsrv = {
296         .attach = usbdattach,
297         .walk1 = usbdwalk,
298         .read = usbdread,
299         .stat = usbdstat,
300         .open = usbdopen,
301         .flush = usbdflush,
302         .destroyfid = usbddestroyfid,
303 };
304
305 int
306 startdev(Port *p)
307 {
308         Dev *d;
309
310         if((d = p->dev) == nil || p->dev->usb == nil){
311                 fprint(2, "okay what?\n");
312                 return -1;
313         }
314         if(d->usb->class == Clhub){
315                 /*
316                  * Hubs are handled directly by this process avoiding
317                  * concurrent operation so that at most one device
318                  * has the config address in use.
319                  * We cancel kernel debug for these eps. too chatty.
320                  */
321                 if((p->hub = newhub(d->dir, d)) == nil)
322                         return -1;
323                 return 0;
324         }
325         close(d->dfd);
326         d->dfd = -1;
327         pushevent(formatdev(d));
328         return 0;
329 }
330
331 void
332 main(int argc, char **argv)
333 {
334         int fd, i, nd;
335         Dir *d;
336         
337         argc--; argv++;
338         initevent();
339         rfork(RFNOTEG);
340         switch(rfork(RFPROC|RFMEM)){
341         case -1: sysfatal("rfork: %r");
342         case 0: work(); exits(nil);
343         }
344         if(argc == 0){
345                 fd = open("/dev/usb", OREAD);
346                 if(fd < 0)
347                         sysfatal("/dev/usb: %r");
348                 nd = dirreadall(fd, &d);
349                 close(fd);
350                 if(nd < 2)
351                         sysfatal("/dev/usb: no hubs");
352                 for(i = 0; i < nd; i++)
353                         if(strcmp(d[i].name, "ctl") != 0)
354                                 rendezvous(work, smprint("/dev/usb/%s", d[i].name));
355                 free(d);
356         }else
357                 for(i = 0; i < argc; i++)
358                         rendezvous(work, strdup(argv[i]));
359         rendezvous(work, nil);
360         postsharesrv(&usbdsrv, nil, "usb", "usbd", "b");
361 }