]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/ether/ether.c
merge
[plan9front.git] / sys / src / cmd / nusb / ether / ether.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 #include <ip.h>
9
10 #include "usb.h"
11 #include "dat.h"
12
13 typedef struct Tab Tab;
14 typedef struct Qbuf Qbuf;
15 typedef struct Dq Dq;
16 typedef struct Conn Conn;
17 typedef struct Ehdr Ehdr;
18 typedef struct Stats Stats;
19
20 enum
21 {
22         Qroot,
23         Qiface,
24         Qclone,
25         Qstats,
26         Qaddr,
27         Qndir,
28         Qctl,
29         Qdata,
30         Qtype,
31         Qmax,
32 };
33
34 struct Tab
35 {
36         char *name;
37         ulong mode;
38 };
39
40 Tab tab[] =
41 {
42         "/",            DMDIR|0555,
43         "etherU",       DMDIR|0555,     /* calling it *ether* makes snoopy(8) happy */
44         "clone",        0666,
45         "stats",        0666,
46         "addr", 0444,
47         "%ud",  DMDIR|0555,
48         "ctl",          0666,
49         "data", 0666,
50         "type", 0444,
51 };
52
53 struct Qbuf
54 {
55         Qbuf            *next;
56         int             ndata;
57         uchar   data[];
58 };
59
60 struct Dq
61 {
62         QLock   l;
63         Dq              *next;
64         Req             *r;
65         Req             **rt;
66         Qbuf            *q;
67         Qbuf            **qt;
68
69         int             nb;
70 };
71
72 struct Conn
73 {
74         QLock   l;
75         int             used;
76         int             type;
77         int             prom;
78         Dq              *dq;
79 };
80
81 struct Ehdr
82 {
83         uchar   d[6];
84         uchar   s[6];
85         uchar   type[2];
86 };
87
88 struct Stats
89 {
90         int             in;
91         int             out;
92 };
93
94 Stats stats;
95 Conn conn[32];
96 int nconn = 0;
97
98 Dev *epin;
99 Dev *epout;
100
101 ulong time0;
102 static char *uname;
103
104 #define PATH(type, n)           ((type)|((n)<<8))
105 #define TYPE(path)                      (((uint)(path) & 0x000000FF)>>0)
106 #define NUM(path)                       (((uint)(path) & 0xFFFFFF00)>>8)
107 #define NUMCONN(c)              (((long)(c)-(long)&conn[0])/sizeof(conn[0]))
108
109 static int
110 receivepacket(void *buf, int len);
111
112 static void
113 fillstat(Dir *d, uvlong path)
114 {
115         Tab *t;
116
117         memset(d, 0, sizeof(*d));
118         d->uid = estrdup9p(uname);
119         d->gid = estrdup9p(uname);
120         d->qid.path = path;
121         d->atime = d->mtime = time0;
122         t = &tab[TYPE(path)];
123         d->name = smprint(t->name, NUM(path));
124         d->qid.type = t->mode>>24;
125         d->mode = t->mode;
126 }
127
128 static void
129 fsattach(Req *r)
130 {
131         if(r->ifcall.aname && r->ifcall.aname[0]){
132                 respond(r, "invalid attach specifier");
133                 return;
134         }
135         if(uname == nil)
136                 uname = estrdup9p(r->ifcall.uname);
137         r->fid->qid.path = PATH(Qroot, 0);
138         r->fid->qid.type = QTDIR;
139         r->fid->qid.vers = 0;
140         r->ofcall.qid = r->fid->qid;
141         respond(r, nil);
142 }
143
144 static void
145 fsstat(Req *r)
146 {
147         fillstat(&r->d, r->fid->qid.path);
148         respond(r, nil);
149 }
150
151 static int
152 rootgen(int i, Dir *d, void*)
153 {
154         i += Qroot+1;
155         if(i == Qiface){
156                 fillstat(d, i);
157                 return 0;
158         }
159         return -1;
160 }
161
162 static int
163 ifacegen(int i, Dir *d, void*)
164 {
165         i += Qiface+1;
166         if(i < Qndir){
167                 fillstat(d, i);
168                 return 0;
169         }
170         i -= Qndir;
171         if(i < nconn){
172                 fillstat(d, PATH(Qndir, i));
173                 return 0;
174         }
175         return -1;
176 }
177
178 static int
179 ndirgen(int i, Dir *d, void *aux)
180 {
181         i += Qndir+1;
182         if(i < Qmax){
183                 fillstat(d, PATH(i, NUMCONN(aux)));
184                 return 0;
185         }
186         return -1;
187 }
188
189 static char*
190 fswalk1(Fid *fid, char *name, Qid *qid)
191 {
192         int i, n;
193         char buf[32];
194         ulong path;
195
196         path = fid->qid.path;
197         if(!(fid->qid.type&QTDIR))
198                 return "walk in non-directory";
199
200         if(strcmp(name, "..") == 0){
201                 switch(TYPE(path)){
202                 case Qroot:
203                         return nil;
204                 case Qiface:
205                         qid->path = PATH(Qroot, 0);
206                         qid->type = tab[Qroot].mode>>24;
207                         return nil;
208                 case Qndir:
209                         qid->path = PATH(Qiface, 0);
210                         qid->type = tab[Qiface].mode>>24;
211                         return nil;
212                 default:
213                         return "bug in fswalk1";
214                 }
215         }
216
217         for(i = TYPE(path)+1; i<nelem(tab); i++){
218                 if(i==Qndir){
219                         n = atoi(name);
220                         snprint(buf, sizeof buf, "%d", n);
221                         if(n < nconn && strcmp(buf, name) == 0){
222                                 qid->path = PATH(i, n);
223                                 qid->type = tab[i].mode>>24;
224                                 return nil;
225                         }
226                         break;
227                 }
228                 if(strcmp(name, tab[i].name) == 0){
229                         qid->path = PATH(i, NUM(path));
230                         qid->type = tab[i].mode>>24;
231                         return nil;
232                 }
233                 if(tab[i].mode&DMDIR)
234                         break;
235         }
236         return "directory entry not found";
237 }
238
239 static void
240 matchrq(Dq *d)
241 {
242         Req *r;
243         Qbuf *b;
244
245         while(r = d->r){
246                 int n;
247
248                 if((b = d->q) == nil)
249                         break;
250                 if((d->q = b->next) == nil)
251                         d->qt = &d->q;
252                 if((d->r = (Req*)r->aux) == nil)
253                         d->rt = &d->r;
254
255                 n = r->ifcall.count;
256                 if(n > b->ndata)
257                         n = b->ndata;
258                 memmove(r->ofcall.data, b->data, n);
259                 free(b);
260                 r->ofcall.count = n;
261                 respond(r, nil);
262         }
263 }
264
265 static void
266 readconndata(Req *r)
267 {
268         Dq *d;
269
270         d = r->fid->aux;
271         qlock(&d->l);
272         if(d->q==nil && d->nb){
273                 qunlock(&d->l);
274                 r->ofcall.count = 0;
275                 respond(r, nil);
276                 return;
277         }
278         // enqueue request
279         r->aux = nil;
280         *d->rt = r;
281         d->rt = (Req**)&r->aux;
282         matchrq(d);
283         qunlock(&d->l);
284 }
285
286 static void
287 writeconndata(Req *r)
288 {
289         Dq *d;
290         void *p;
291         int n;
292
293         d = r->fid->aux;
294         p = r->ifcall.data;
295         n = r->ifcall.count;
296         if((n == 11) && memcmp(p, "nonblocking", n)==0){
297                 d->nb = 1;
298                 goto out;
299         }
300         epwrite(epout, p, n);
301         if(receivepacket(p, n) == 0)
302                 stats.out++;
303 out:
304         r->ofcall.count = n;
305         respond(r, nil);
306 }
307
308 static char*
309 mac2str(uchar *m)
310 {
311         int i;
312         char *t = "0123456789abcdef";
313         static char buf[13];
314         buf[13] = 0;
315         for(i=0; i<6; i++){
316                 buf[i*2] = t[m[i]>>4];
317                 buf[i*2+1] = t[m[i]&0xF];
318         }
319         return buf;
320 }
321
322 static void
323 fsread(Req *r)
324 {
325         char buf[200];
326         char e[ERRMAX];
327         ulong path;
328
329         path = r->fid->qid.path;
330
331         switch(TYPE(path)){
332         default:
333                 snprint(e, sizeof e, "bug in fsread path=%lux", path);
334                 respond(r, e);
335                 break;
336
337         case Qroot:
338                 dirread9p(r, rootgen, nil);
339                 respond(r, nil);
340                 break;
341
342         case Qiface:
343                 dirread9p(r, ifacegen, nil);
344                 respond(r, nil);
345                 break;
346
347         case Qstats:
348                 snprint(buf, sizeof(buf),
349                         "in: %d\n"
350                         "out: %d\n"
351                         "mbps: %d\n"
352                         "addr: %E\n",
353                         stats.in, stats.out, 10, macaddr);
354                 readstr(r, buf);
355                 respond(r, nil);
356                 break;
357
358         case Qaddr:
359                 snprint(buf, sizeof(buf), "%E", macaddr);
360                 readstr(r, buf);
361                 respond(r, nil);
362                 break;
363
364         case Qndir:
365                 dirread9p(r, ndirgen, &conn[NUM(path)]);
366                 respond(r, nil);
367                 break;
368
369         case Qctl:
370                 snprint(buf, sizeof(buf), "%11d ", NUM(path));
371                 readstr(r, buf);
372                 respond(r, nil);
373                 break;
374
375         case Qtype:
376                 snprint(buf, sizeof(buf), "%11d ", conn[NUM(path)].type);
377                 readstr(r, buf);
378                 respond(r, nil);
379                 break;
380
381         case Qdata:
382                 readconndata(r);
383                 break;
384         }
385 }
386
387 static void
388 fswrite(Req *r)
389 {
390         char e[ERRMAX];
391         ulong path;
392         char *p;
393         int n;
394
395         path = r->fid->qid.path;
396         switch(TYPE(path)){
397         case Qctl:
398                 n = r->ifcall.count;
399                 p = (char*)r->ifcall.data;
400                 if((n == 11) && memcmp(p, "promiscuous", 11)==0)
401                         conn[NUM(path)].prom = 1;
402                 if((n > 8) && memcmp(p, "connect ", 8)==0){
403                         char x[12];
404
405                         if(n - 8 >= sizeof(x)){
406                                 respond(r, "invalid control msg");
407                                 return;
408                         }
409
410                         p += 8;
411                         memcpy(x, p, n-8);
412                         x[n-8] = 0;
413
414                         conn[NUM(path)].type = atoi(p);
415                 }
416                 r->ofcall.count = n;
417                 respond(r, nil);
418                 break;
419         case Qdata:
420                 writeconndata(r);
421                 break;
422         default:
423                 snprint(e, sizeof e, "bug in fswrite path=%lux", path);
424                 respond(r, e);
425         }
426 }
427
428 static void
429 fsopen(Req *r)
430 {
431         static int need[4] = { 4, 2, 6, 1 };
432         ulong path;
433         int i, n;
434         Tab *t;
435         Dq *d;
436         Conn *c;
437
438
439         /*
440          * lib9p already handles the blatantly obvious.
441          * we just have to enforce the permissions we have set.
442          */
443         path = r->fid->qid.path;
444         t = &tab[TYPE(path)];
445         n = need[r->ifcall.mode&3];
446         if((n&t->mode) != n){
447                 respond(r, "permission denied");
448                 return;
449         }
450
451         d = nil;
452         r->fid->aux = nil;
453
454         switch(TYPE(path)){
455         case Qclone:
456                 for(i=0; i<nelem(conn); i++){
457                         if(conn[i].used)
458                                 continue;
459                         if(i >= nconn)
460                                 nconn = i+1;
461                         path = PATH(Qctl, i);
462                         goto CaseConn;
463                 }
464                 respond(r, "out of connections");
465                 return;
466         case Qdata:
467                 d = emalloc9p(sizeof(*d));
468                 memset(d, 0, sizeof(*d));
469                 d->qt = &d->q;
470                 d->rt = &d->r;
471                 r->fid->aux = d;
472         case Qndir:
473         case Qctl:
474         case Qtype:
475         CaseConn:
476                 c = &conn[NUM(path)];
477                 qlock(&c->l);
478                 if(c->used++ == 0){
479                         c->type = 0;
480                         c->prom = 0;
481                 }
482                 if(d != nil){
483                         d->next = c->dq;
484                         c->dq = d;
485                 }
486                 qunlock(&c->l);
487                 break;
488         }
489
490         r->fid->qid.path = path;
491         r->ofcall.qid.path = path;
492         respond(r, nil);
493 }
494
495 static void
496 fsflush(Req *r)
497 {
498         Req *o, **p;
499         Fid *f;
500         Dq *d;
501
502         o = r->oldreq;
503         f = o->fid;
504         if(TYPE(f->qid.path) == Qdata){
505                 d = f->aux;
506                 qlock(&d->l);
507                 for(p=&d->r; *p; p=(Req**)&((*p)->aux)){
508                         if(*p == o){
509                                 if((*p = (Req*)o->aux) == nil)
510                                         d->rt = p;
511                                 r->oldreq = nil;
512                                 respond(o, "interrupted");
513                                 break;
514                         }
515                 }
516                 qunlock(&d->l);
517         }
518         respond(r, nil);
519 }
520
521
522 static void
523 fsdestroyfid(Fid *fid)
524 {
525         Conn *c;
526         Qbuf *b;
527         Dq **x, *d;
528
529         if(TYPE(fid->qid.path) >= Qndir){
530                 c = &conn[NUM(fid->qid.path)];
531                 qlock(&c->l);
532                 if(d = fid->aux){
533                         fid->aux = nil;
534                         for(x=&c->dq; *x; x=&((*x)->next)){
535                                 if(*x == d){
536                                         *x = d->next;
537                                         break;
538                                 }
539                         }
540                         qlock(&d->l);
541                         while(b = d->q){
542                                 d->q = b->next;
543                                 free(b);
544                         }
545                         qunlock(&d->l);
546                 }
547                 if(TYPE(fid->qid.path) == Qctl)
548                         c->prom = 0;
549                 c->used--;
550                 qunlock(&c->l);
551         }
552 }
553
554 static void
555 setalt(Dev *d, int ifcid, int altid)
556 {
557         if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, altid, ifcid, nil, 0) < 0)
558                 dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, ifcid, altid);
559 }
560
561 static int
562 ifaceinit(Dev *d, Iface *ifc, int *ei, int *eo)
563 {
564         int i, epin, epout;
565         Ep *ep;
566
567         if(ifc == nil)
568                 return -1;
569
570         epin = epout = -1;
571         for(i = 0; (epin < 0 || epout < 0) && i < nelem(ifc->ep); i++)
572                 if((ep = ifc->ep[i]) != nil && ep->type == Ebulk){
573                         if(ep->dir == Eboth || ep->dir == Ein)
574                                 if(epin == -1)
575                                         epin =  ep->id;
576                         if(ep->dir == Eboth || ep->dir == Eout)
577                                 if(epout == -1)
578                                         epout = ep->id;
579                 }
580         if(epin == -1 || epout == -1)
581                 return -1;
582
583         for(i = 0; i < nelem(ifc->altc); i++)
584                 if(ifc->altc[i] != nil)
585                         setalt(d, ifc->id, i);
586
587         *ei = epin;
588         *eo = epout;
589         return 0;
590 }
591
592 int
593 findendpoints(Dev *d, int *ei, int *eo)
594 {
595         int i, j, ctlid, datid;
596         Iface *ctlif, *datif;
597         Usbdev *ud;
598         Desc *desc;
599         Conf *c;
600
601         ud = d->usb;
602         *ei = *eo = -1;
603
604         /* look for union descriptor with ethernet ctrl interface */
605         for(i = 0; i < nelem(ud->ddesc); i++){
606                 if((desc = ud->ddesc[i]) == nil)
607                         continue;
608                 if(desc->data.bLength < 5 || desc->data.bbytes[0] != Cdcunion)
609                         continue;
610
611                 ctlid = desc->data.bbytes[1];
612                 datid = desc->data.bbytes[2];
613
614                 if((c = desc->conf) == nil)
615                         continue;
616
617                 ctlif = datif = nil;
618                 for(j = 0; j < nelem(c->iface); j++){
619                         if(c->iface[j] == nil)
620                                 continue;
621                         if(c->iface[j]->id == ctlid)
622                                 ctlif = c->iface[j];
623                         if(c->iface[j]->id == datid)
624                                 datif = c->iface[j];
625
626                         if(datif != nil && ctlif != nil){
627                                 if(Subclass(ctlif->csp) == Scether)
628                                         if(ifaceinit(d, datif, ei, eo) != -1)
629                                                 return 0;
630                                 break;
631                         }
632                 }               
633         }
634
635         /* try any other one that seems to be ok */
636         for(i = 0; i < nelem(ud->conf); i++)
637                 if((c = ud->conf[i]) != nil)
638                         for(j = 0; j < nelem(c->iface); j++)
639                                 if(ifaceinit(d,c->iface[j],ei,eo) != -1)
640                                         return 0;
641
642         return -1;
643 }
644
645 static int
646 inote(void *, char *msg)
647 {
648         if(strstr(msg, "interrupt"))
649                 return 1;
650         return 0;
651 }
652
653 static int
654 receivepacket(void *buf, int len)
655 {
656         int i;
657         int t;
658         Ehdr *h;
659
660         if(len < sizeof(*h))
661                 return -1;
662
663         h = (Ehdr*)buf;
664         t = (h->type[0]<<8)|h->type[1];
665
666         for(i=0; i<nconn; i++){
667                 Qbuf *b;
668                 Conn *c;
669                 Dq *d;
670
671                 c = &conn[i];
672                 qlock(&c->l);
673                 if(!c->used)
674                         goto next;
675                 if(c->type > 0)
676                         if(c->type != t)
677                                 goto next;
678                 if(!c->prom && !(h->d[0]&1))
679                         if(memcmp(h->d, macaddr, sizeof(macaddr)))
680                                 goto next;
681                 for(d=c->dq; d; d=d->next){
682                         int n;
683
684                         n = len;
685                         if(c->type == -2 && n > 64)
686                                 n = 64;
687
688                         b = emalloc9p(sizeof(*b) + n);
689                         b->ndata = n;
690                         memcpy(b->data, buf, n);
691
692                         qlock(&d->l);
693                         // enqueue buffer
694                         b->next = nil;
695                         *d->qt = b;
696                         d->qt = &b->next;
697                         matchrq(d);
698                         qunlock(&d->l);
699                 }
700 next:
701                 qunlock(&c->l);
702         }
703         return 0;
704 }
705
706 static void
707 usbreadproc(void *)
708 {
709         char err[ERRMAX];
710         uchar buf[4*1024];
711         int n, nerr;
712
713         atnotify(inote, 1);
714
715         threadsetname("usbreadproc");
716
717         nerr = 0;
718         for(;;){
719                 n = epread(epin, buf, sizeof(buf));
720                 if(n < 0){
721                         rerrstr(err, sizeof(err));
722                         if(strstr(err, "interrupted") || strstr(err, "timed out"))
723                                 continue;
724                         fprint(2, "usbreadproc: %s\n", err);
725                         if(++nerr < 3)
726                                 continue;
727                         threadexitsall(err);
728                 }
729                 nerr = 0;
730                 if(n == 0)
731                         continue;
732                 if(receivepacket(buf, n) == 0)
733                         stats.in++;
734         }
735 }
736
737 Srv fs = 
738 {
739 .attach=                fsattach,
740 .destroyfid=    fsdestroyfid,
741 .walk1=         fswalk1,
742 .open=          fsopen,
743 .read=          fsread,
744 .write=         fswrite,
745 .stat=          fsstat,
746 .flush=         fsflush,
747 };
748
749 static void
750 usage(void)
751 {
752         fprint(2, "usage: %s [-dD] [-t type] [-a addr] devid\n", argv0);
753         exits("usage");
754 }
755
756
757 extern int a88178init(Dev *);
758 extern int a88772init(Dev *);
759 extern int smscinit(Dev *);
760 extern int cdcinit(Dev *);
761
762 static struct {
763         char *name;
764         int (*init)(Dev*);
765 } ethertype[] = {
766         "cdc",          cdcinit,
767         "smsc",         smscinit,
768         "a88178",       a88178init,
769         "a88772",       a88772init,
770 };
771
772 void
773 threadmain(int argc, char **argv)
774 {
775         char s[64], *t;
776         int et, ei, eo;
777         Dev *d;
778
779         fmtinstall('E', eipfmt);
780
781         et = 0; /* CDC */
782         ARGBEGIN {
783         case 'd':
784                 debug = 1;
785                 break;
786         case 'D':
787                 chatty9p++;
788                 break;
789         case 'a':
790                 setmac = 1;
791                 parseether(macaddr, EARGF(usage()));
792                 break;
793         case 't':
794                 t = EARGF(usage());
795                 for(et=0; et<nelem(ethertype); et++)
796                         if(strcmp(t, ethertype[et].name) == 0)
797                                 break;
798                 if(et == nelem(ethertype)){
799                         fprint(2, "unsupported ethertype -t %s, supported types are: ", t);
800                         for(et=0; et<nelem(ethertype); et++)
801                                 fprint(2, "%s ", ethertype[et].name);
802                         fprint(2, "\n");
803                         exits("usage");
804                 }
805                 break;
806         default:
807                 usage();
808         } ARGEND;
809
810         if(argc != 1)
811                 usage();
812
813         if((d = getdev(atoi(*argv))) == nil)
814                 sysfatal("getdev: %r");
815         if(findendpoints(d, &ei, &eo) < 0)
816                 sysfatal("no endpoints found");
817
818         werrstr("");
819         if((*ethertype[et].init)(d) < 0)
820                 sysfatal("%s init failed: %r", ethertype[et].name);
821         if(epread == nil || epwrite == nil)
822                 sysfatal("bug in init");
823
824         if((epin = openep(d, ei)) == nil)
825                 sysfatal("openep: %r");
826         if(ei == eo){
827                 incref(epin);
828                 epout = epin;
829                 opendevdata(epin, ORDWR);
830         } else {
831                 if((epout = openep(d, eo)) == nil)
832                         sysfatal("openep: %r");
833                 opendevdata(epin, OREAD);
834                 opendevdata(epout, OWRITE);
835         }
836
837         proccreate(usbreadproc, nil, 8*1024);
838
839         atnotify(inote, 1);
840         time0 = time(0);
841         tab[Qiface].name = smprint("etherU%d", d->id);
842         snprint(s, sizeof(s), "%d.ether", d->id);
843         closedev(d);
844         threadpostsharesrv(&fs, nil, "usbnet", s);
845
846         threadexits(0);
847 }