Start bringing networking back in
[akaros.git] / kern / drivers / dev / ether.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 #include <vfs.h>
17 #include <kfs.h>
18 #include <slab.h>
19 #include <kmalloc.h>
20 #include <kref.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <assert.h>
24 #include <error.h>
25 #include <cpio.h>
26 #include <pmap.h>
27 #include <smp.h>
28 #include <ip.h>
29
30 enum {
31         Type8021Q=      0x8100, /* value of type field for 802.1[pQ] tags */
32 };
33
34 static struct ether *etherxx[MaxEther]; /* real controllers */
35 static struct ether*    vlanalloc(struct ether*, int);
36 static void     vlanoq(struct ether*, struct block*);
37
38 struct chan*
39 etherattach(char* spec)
40 {
41         ERRSTACK(2);
42         uint32_t ctlrno;
43         char *p;
44         struct chan *chan;
45         struct ether *ether, *vlan;
46         int vlanid;
47
48         ctlrno = 0;
49         vlanid = 0;
50         if(spec && *spec){
51                 ctlrno = strtoul(spec, &p, 0);
52                 /* somebody interpret this for me. */
53                 if((ctlrno == 0) && (p == spec) || (ctlrno >= MaxEther) || (*p) && (*p != '.'))
54                         error(Ebadarg);
55                 if(*p == '.'){  /* vlan */
56                         vlanid = strtoul(p+1, &p, 0);
57                         if(vlanid <= 0 || vlanid > 0xFFF || *p)
58                                 error(Ebadarg);
59                 }
60         }
61         if((ether = etherxx[ctlrno]) == 0)
62                 error(Enodev);
63         rlock(&ether->rwlock);
64         if(waserror()){
65                 runlock(&ether->rwlock);
66                 nexterror();
67         }
68         if(vlanid){
69                 if(ether->maxmtu < ETHERMAXTU+4)
70                         error("interface cannot support 802.1 tags");
71                 vlan = vlanalloc(ether, vlanid);
72                 chan = devattach('l', spec);
73                 chan->dev = ctlrno  + (vlanid<<8);
74                 chan->aux = vlan;
75                 poperror();
76                 runlock(&ether->rwlock);
77                 return chan;
78         }
79         chan = devattach('l', spec);
80         chan->dev = ctlrno;
81         chan->aux = ether;
82         if(ether->attach)
83                 ether->attach(ether);
84         poperror();
85         runlock(&ether->rwlock);
86         return chan;
87 }
88
89 static void
90 ethershutdown(void)
91 {
92         struct ether *ether;
93         int i;
94
95         for(i=0; i<MaxEther; i++){
96                 ether = etherxx[i];
97                 if(ether != NULL && ether->detach != NULL)
98                         ether->detach(ether);
99         }
100 }
101
102 static struct walkqid*
103 etherwalk(struct chan* chan, struct chan *nchan, char **name, int nname)
104 {
105         ERRSTACK(2);
106         struct walkqid *wq;
107         struct ether *ether;
108
109         ether = chan->aux;
110         rlock(&ether->rwlock);
111         if(waserror()) {
112                 runlock(&ether->rwlock);
113                 nexterror();
114         }
115         wq = netifwalk(&ether->netif, chan, nchan, name, nname);
116         if(wq && wq->clone != NULL && wq->clone != chan)
117                 wq->clone->aux = ether;
118         poperror();
119         runlock(&ether->rwlock);
120         return wq;
121 }
122
123 static int
124 etherstat(struct chan* chan, uint8_t* dp, int n)
125 {
126         ERRSTACK(2);
127         int s;
128         struct ether *ether;
129
130         ether = chan->aux;
131         rlock(&ether->rwlock);
132         if(waserror()) {
133                 runlock(&ether->rwlock);
134                 nexterror();
135         }
136         s = netifstat(&ether->netif, chan, dp, n);
137         poperror();
138         runlock(&ether->rwlock);
139         return s;
140 }
141
142 static struct chan*
143 etheropen(struct chan* chan, int omode)
144 {
145         ERRSTACK(2);
146         struct chan *c;
147         struct ether *ether;
148
149         ether = chan->aux;
150         rlock(&ether->rwlock);
151         if(waserror()) {
152                 runlock(&ether->rwlock);
153                 nexterror();
154         }
155         c = netifopen(&ether->netif, chan, omode);
156         poperror();
157         runlock(&ether->rwlock);
158         return c;
159 }
160
161 static void
162 etherclose(struct chan* chan)
163 {
164         ERRSTACK(2);
165         struct ether *ether;
166
167         ether = chan->aux;
168         rlock(&ether->rwlock);
169         if(waserror()) {
170                 runlock(&ether->rwlock);
171                 nexterror();
172         }
173         netifclose(&ether->netif, chan);
174         poperror();
175         runlock(&ether->rwlock);
176 }
177
178 static long
179 etherread(struct chan* chan, void* buf, long n, int64_t off)
180 {
181         ERRSTACK(2);
182         struct ether *ether;
183         uint32_t offset = off;
184         long r;
185
186         ether = chan->aux;
187         rlock(&ether->rwlock);
188         if(waserror()) {
189                 runlock(&ether->rwlock);
190                 nexterror();
191         }
192         if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
193                 /*
194                  * With some controllers it is necessary to reach
195                  * into the chip to extract statistics.
196                  */
197                 if(NETTYPE(chan->qid.path) == Nifstatqid){
198                         r = ether->ifstat(ether, buf, n, offset);
199                         goto out;
200                 }
201                 if(NETTYPE(chan->qid.path) == Nstatqid)
202                         ether->ifstat(ether, buf, 0, offset);
203         }
204         r = netifread(&ether->netif, chan, buf, n, offset);
205 out:
206         poperror();
207         runlock(&ether->rwlock);
208         return r;
209 }
210
211 static struct block*
212 etherbread(struct chan* chan, long n, uint32_t offset)
213 {
214         ERRSTACK(2);
215         struct block *b;
216         struct ether *ether;
217
218         ether = chan->aux;
219         rlock(&ether->rwlock);
220         if(waserror()) {
221                 runlock(&ether->rwlock);
222                 nexterror();
223         }
224         b = netifbread(&ether->netif, chan, n, offset);
225         poperror();
226         runlock(&ether->rwlock);
227         return b;
228 }
229
230 static int
231 etherwstat(struct chan* chan, uint8_t* dp, int n)
232 {
233         ERRSTACK(2);
234         struct ether *ether;
235         int r;
236
237         ether = chan->aux;
238         rlock(&ether->rwlock);
239         if(waserror()) {
240                 runlock(&ether->rwlock);
241                 nexterror();
242         }
243         r = netifwstat(&ether->netif, chan, dp, n);
244         poperror();
245         runlock(&ether->rwlock);
246         return r;
247 }
248
249 static void
250 etherrtrace(struct netfile* f, struct etherpkt* pkt, int len)
251 {
252         int i, n;
253         struct block *bp;
254
255         if(qwindow(f->in) <= 0)
256                 return;
257         if(len > 58)
258                 n = 58;
259         else
260                 n = len;
261         bp = iallocb(64);
262         if(bp == NULL)
263                 return;
264         memmove(bp->wp, pkt->d, n);
265         i = milliseconds();
266         bp->wp[58] = len>>8;
267         bp->wp[59] = len;
268         bp->wp[60] = i>>24;
269         bp->wp[61] = i>>16;
270         bp->wp[62] = i>>8;
271         bp->wp[63] = i;
272         bp->wp += 64;
273         qpass(f->in, bp);
274 }
275
276 struct block*
277 etheriq(struct ether* ether, struct block* bp, int fromwire)
278 {
279         struct etherpkt *pkt;
280         uint16_t type;
281         int len, multi, tome, fromme, vlanid, i;
282         struct netfile **ep, *f, **fp, *fx;
283         struct block *xbp;
284         struct ether *vlan;
285
286         ether->netif.inpackets++;
287
288         pkt = (struct etherpkt*)bp->rp;
289         len = BLEN(bp);
290         type = (pkt->type[0]<<8)|pkt->type[1];
291         if(type == Type8021Q && ether->nvlan){
292                 vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF;
293                 if(vlanid){
294                         for(i = 0; i < ARRAY_SIZE(ether->vlans); i++){
295                                 vlan = ether->vlans[i];
296                                 if(vlan != NULL && vlan->vlanid == vlanid){
297                                         memmove(bp->rp+4, bp->rp, 2*Eaddrlen);
298                                         bp->rp += 4;
299                                         return etheriq(vlan, bp, fromwire);
300                                 }
301                         }
302                         /* allow normal type handling to accept or discard it */
303                 }
304         }
305
306         fx = 0;
307         ep = &ether->netif.f[Ntypes];
308
309         multi = pkt->d[0] & 1;
310         /* check for valid multcast addresses */
311         if(multi && memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) != 0 && ether->netif.prom == 0){
312                 if(!activemulti(&ether->netif, pkt->d, sizeof(pkt->d))){
313                         if(fromwire){
314                                 freeb(bp);
315                                 bp = 0;
316                         }
317                         return bp;
318                 }
319         }
320
321         /* is it for me? */
322         tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
323         fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
324
325         /*
326          * Multiplex the packet to all the connections which want it.
327          * If the packet is not to be used subsequently (fromwire != 0),
328          * attempt to simply pass it into one of the connections, thereby
329          * saving a copy of the data (usual case hopefully).
330          */
331         for(fp = ether->netif.f; fp < ep; fp++){
332                 if((f = *fp) && (f->type == type || f->type < 0))
333                 if(tome || multi || f->prom){
334                         /* Don't want to hear bridged packets */
335                         if(f->bridge && !fromwire && !fromme)
336                                 continue;
337                         if(!f->headersonly){
338                                 if(fromwire && fx == 0)
339                                         fx = f;
340                                 else if((xbp = iallocb(len))){
341                                         memmove(xbp->wp, pkt, len);
342                                         xbp->wp += len;
343                                         if(qpass(f->in, xbp) < 0)
344                                                 ether->netif.soverflows++;
345                                 }
346                                 else
347                                         ether->netif.soverflows++;
348                         }
349                         else
350                                 etherrtrace(f, pkt, len);
351                 }
352         }
353
354         if(fx){
355                 if(qpass(fx->in, bp) < 0)
356                         ether->netif.soverflows++;
357                 return 0;
358         }
359         if(fromwire){
360                 freeb(bp);
361                 return 0;
362         }
363
364         return bp;
365 }
366
367 static int
368 etheroq(struct ether* ether, struct block* bp)
369 {
370         int len, loopback, s;
371         struct etherpkt *pkt;
372
373         ether->netif.outpackets++;
374
375         /*
376          * Check if the packet has to be placed back onto the input queue,
377          * i.e. if it's a loopback or broadcast packet or the interface is
378          * in promiscuous mode.
379          * If it's a loopback packet indicate to etheriq that the data isn't
380          * needed and return, etheriq will pass-on or free the block.
381          * To enable bridging to work, only packets that were originated
382          * by this interface are fed back.
383          */
384         pkt = (struct etherpkt*)bp->rp;
385         len = BLEN(bp);
386         loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
387         if(loopback || memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) == 0 || ether->netif.prom){
388                 s = splhi();
389                 etheriq(ether, bp, 0);
390                 splx(s);
391         }
392
393         if(!loopback){
394                 if(ether->vlanid){
395                         /* add tag */
396                         bp = padblock(bp, 2+2);
397                         memmove(bp->rp, bp->rp+4, 2*Eaddrlen);
398                         hnputs(bp->rp+2*Eaddrlen, Type8021Q);
399                         hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF);     /* prio:3 0:1 vid:12 */
400                         ether = ether->ctlr;
401                 }
402                 qbwrite(ether->oq, bp);
403                 if(ether->transmit != NULL)
404                         ether->transmit(ether);
405         }else
406                 freeb(bp);
407
408         return len;
409 }
410
411 static long
412 etherwrite(struct chan* chan, void* buf, long n, int64_t unused)
413 {
414         ERRSTACK(2);
415         struct ether *ether;
416         struct block *bp;
417         int onoff;
418         struct cmdbuf *cb;
419         long l;
420
421         ether = chan->aux;
422         rlock(&ether->rwlock);
423         if(waserror()) {
424                 runlock(&ether->rwlock);
425                 nexterror();
426         }
427         if(NETTYPE(chan->qid.path) != Ndataqid) {
428                 l = netifwrite(&ether->netif, chan, buf, n);
429                 if(l >= 0)
430                         goto out;
431                 cb = parsecmd(buf, n);
432                 if(strcmp(cb->f[0], "nonblocking") == 0){
433                         if(cb->nf <= 1)
434                                 onoff = 1;
435                         else
436                                 onoff = atoi(cb->f[1]);
437                         if(ether->oq != NULL)
438                                 qnoblock(ether->oq, onoff);
439                         kfree(cb);
440                         goto out;
441                 }
442                 kfree(cb);
443                 if(ether->ctl!=NULL){
444                         l = ether->ctl(ether,buf,n);
445                         goto out;
446                 }
447                 error(Ebadctl);
448         }
449
450         if(n > ether->maxmtu)
451                 error(Etoobig);
452         if(n < ether->minmtu)
453                 error(Etoosmall);
454         bp = allocb(n);
455         if(waserror()){
456                 freeb(bp);
457                 nexterror();
458         }
459         memmove(bp->rp, buf, n);
460         memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
461         bp->wp += n;
462         poperror();
463
464         l = etheroq(ether, bp);
465 out:
466         poperror();
467         runlock(&ether->rwlock);
468         return l;
469 }
470
471 static long
472 etherbwrite(struct chan* chan, struct block* bp, uint32_t unused)
473 {
474         ERRSTACK(2);
475         struct ether *ether;
476         long n;
477
478         n = BLEN(bp);
479         if(NETTYPE(chan->qid.path) != Ndataqid){
480                 if(waserror()) {
481                         freeb(bp);
482                         nexterror();
483                 }
484                 n = etherwrite(chan, bp->rp, n, 0);
485                 poperror();
486                 freeb(bp);
487                 return n;
488         }
489         ether = chan->aux;
490         rlock(&ether->rwlock);
491         if(waserror()) {
492                 runlock(&ether->rwlock);
493                 nexterror();
494         }
495         if(n > ether->maxmtu){
496                 freeb(bp);
497                 error(Etoobig);
498         }
499         if(n < ether->minmtu){
500                 freeb(bp);
501                 error(Etoosmall);
502         }
503         n = etheroq(ether, bp);
504         poperror();
505         runlock(&ether->rwlock);
506         return n;
507 }
508
509 static void
510 nop(struct ether*unused)
511 {
512 }
513
514 static long
515 vlanctl(struct ether *ether, void *buf, long n)
516 {
517         uint8_t ea[Eaddrlen];
518         struct ether *master;
519         struct cmdbuf *cb;
520         int i;
521
522         cb = parsecmd(buf, n);
523         if(cb->nf >= 2
524         && strcmp(cb->f[0], "ea")==0
525         && parseether(ea, cb->f[1]) == 0){
526                 kfree(cb);
527                 memmove(ether->ea, ea, Eaddrlen);
528                 memmove(ether->netif.addr, ether->ea, Eaddrlen);
529                 return 0;
530         }
531         if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){
532                 master = ether->ctlr;
533                 qlock(&master->vlq);
534                 for(i = 0; i < ARRAY_SIZE(master->vlans); i++)
535                         if(master->vlans[i] == ether){
536                                 ether->vlanid = 0;
537                                 master->nvlan--;
538                                 break;
539                         }
540                 qunlock(&master->vlq);
541                 kfree(cb);
542                 return 0;
543         }
544         kfree(cb);
545         error(Ebadctl);
546         return -1;      /* not reached */
547 }
548
549 static struct ether*
550 vlanalloc(struct ether *ether, int id)
551 {
552         ERRSTACK(2);
553         struct ether *vlan;
554         int i, fid;
555         char name[KNAMELEN];
556
557         qlock(&ether->vlq);
558         if(waserror()){
559                 qunlock(&ether->vlq);
560                 nexterror();
561         }
562         fid = -1;
563         for(i = 0; i < ARRAY_SIZE(ether->vlans); i++){
564                 vlan = ether->vlans[i];
565                 if(vlan != NULL && vlan->vlanid == id){
566                         poperror();
567                         qunlock(&ether->vlq);
568                         return vlan;
569                 }
570                 if(fid < 0 && (vlan == NULL || vlan->vlanid == 0))
571                         fid = i;
572         }
573         if(fid < 0)
574                 error(Enoifc);
575         snprintf(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
576         vlan = ether->vlans[fid];
577         if(vlan == NULL){
578                 vlan = kzmalloc(sizeof(struct ether), 1);
579                 if(vlan == NULL)
580                         error(Enovmem);
581                 netifinit(&vlan->netif, name, Ntypes, ether->netif.limit);
582                 ether->vlans[fid] = vlan;       /* id is still zero, can't be matched */
583                 ether->nvlan++;
584         }else
585                 memmove(vlan->netif.name, name, KNAMELEN-1);
586 #warning "fix me setting up vlans"
587 #if 0
588         vlan->attach = nop;
589         vlan->transmit = NULL;
590         vlan->ctl = vlanctl;
591         vlan->irq = -1;
592 //      vlan->promiscuous = ether->promiscuous;
593 //      vlan->multicast = ether->multicast;
594         vlan->arg = vlan;
595         vlan->mbps = ether->mbps;
596         vlan->fullduplex = ether->fullduplex;
597         vlan->encry = ether->encry;
598         vlan->minmtu = ether->minmtu;
599         vlan->maxmtu = ether->maxmtu;
600         vlan->ctlrno = ether->ctlrno;
601         vlan->vlanid = id;
602         vlan->alen = Eaddrlen;
603         memmove(vlan->addr, ether->addr, sizeof(vlan->addr));
604         memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast));
605         vlan->oq = NULL;
606         vlan->ctlr = ether;
607         vlan->vlanid = id;
608 #endif
609         poperror();
610         qunlock(&ether->vlq);
611         return vlan;
612 }
613
614 static struct {
615         char*   type;
616         int     (*reset)(struct ether*);
617 } cards[MaxEther+1];
618
619 void
620 addethercard(char* t, int (*r)(struct ether*))
621 {
622         static int ncard;
623
624         if(ncard == MaxEther)
625                 panic("too many ether cards");
626         cards[ncard].type = t;
627         cards[ncard].reset = r;
628         ncard++;
629 }
630
631 int
632 parseether(uint8_t *to, char *from)
633 {
634         char nip[4];
635         char *p;
636         int i;
637
638         p = from;
639         for(i = 0; i < Eaddrlen; i++){
640                 if(*p == 0)
641                         return -1;
642                 nip[0] = *p++;
643                 if(*p == 0)
644                         return -1;
645                 nip[1] = *p++;
646                 nip[2] = 0;
647                 to[i] = strtoul(nip, 0, 16);
648                 if(*p == ':')
649                         p++;
650         }
651         return 0;
652 }
653
654 static void
655 etherreset(void)
656 {
657 #warning "fix me etherreset"
658 #if 0
659         struct ether *ether;
660         int i, n, ctlrno;
661         char name[KNAMELEN], buf[128];
662
663         for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
664                 if(ether == 0)
665                         ether = kzmalloc(sizeof(struct ether), 0);
666                 memset(ether, 0, sizeof(struct ether));
667                 ether->ctlrno = ctlrno;
668                 ether->netif.mbps = 10;
669                 ether->minmtu = ETHERMINTU;
670                 ether->maxmtu = ETHERMAXTU;
671                 ether->netif.itype = -1;
672
673                 if(archether(ctlrno, ether) <= 0)
674                         continue;
675
676                 for(n = 0; cards[n].type; n++){
677                         if(cistrcmp(cards[n].type, ether->type))
678                                 continue;
679                         for(i = 0; i < ether->nopt; i++){
680                                 if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
681                                         if(parseether(ether->ea, &ether->opt[i][3]) == -1)
682                                                 memset(ether->ea, 0, Eaddrlen);
683                                 }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
684                                         cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
685                                         ether->fullduplex = 1;
686                                 else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
687                                         ether->mbps = 100;
688                         }
689                         if(cards[n].reset(ether))
690                                 break;
691                         snprintf(name, sizeof(name), "ether%d", ctlrno);
692
693                         if(ether->interrupt != NULL)
694                                 intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
695
696                         i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
697                                 ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
698                         if(ether->mem)
699                                 i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
700                         if(ether->size)
701                                 i += sprint(buf+i, " size 0x%luX", ether->size);
702                         i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
703                                 ether->ea[0], ether->ea[1], ether->ea[2],
704                                 ether->ea[3], ether->ea[4], ether->ea[5]);
705                         sprint(buf+i, "\n");
706                         iprint(buf);
707
708                         if(ether->mbps == 100){
709                                 netifinit(ether, name, Ntypes, 256*1024);
710                                 if(ether->oq == 0)
711                                         ether->oq = qopen(256*1024, Qmsg, 0, 0);
712                         }
713                         else{
714                                 netifinit(ether, name, Ntypes, 64*1024);
715                                 if(ether->oq == 0)
716                                         ether->oq = qopen(64*1024, Qmsg, 0, 0);
717                         }
718                         if(ether->oq == 0)
719                                 panic("etherreset %s", name);
720                         ether->alen = Eaddrlen;
721                         memmove(ether->addr, ether->ea, Eaddrlen);
722                         memset(ether->bcast, 0xFF, Eaddrlen);
723
724                         etherxx[ctlrno] = ether;
725                         ether = 0;
726                         break;
727                 }
728         }
729         if(ether)
730                 kfree(ether);
731 #endif
732 }
733
734 static void
735 etherpower(int on)
736 {
737         int i;
738         struct ether *ether;
739
740         for(i = 0; i < MaxEther; i++){
741                 if((ether = etherxx[i]) == NULL || ether->power == NULL)
742                         continue;
743                 if(on){
744                         if(canrlock(&ether->rwlock))
745                                 continue;
746                         if(ether->power != NULL)
747                                 ether->power(ether, on);
748                         wunlock(&ether->rwlock);
749                 }else{
750 #warning "what is this eraders stuff? (rwlock FIX ME )"
751 /*
752                         if(ether->readers)
753                                 continue;
754 */
755                         wlock(&ether->rwlock);
756                         if(ether->power != NULL)
757                                 ether->power(ether, on);
758                         /* Keep locked until power goes back on */
759                 }
760         }
761 }
762
763 #define ETHERPOLY 0xedb88320
764
765 /* really slow 32 bit crc for ethers */
766 uint32_t
767 ethercrc(uint8_t *p, int len)
768 {
769         int i, j;
770         uint32_t crc, b;
771
772         crc = 0xffffffff;
773         for(i = 0; i < len; i++){
774                 b = *p++;
775                 for(j = 0; j < 8; j++){
776                         crc = (crc>>1) ^ (((crc^b) & 1) ? ETHERPOLY : 0);
777                         b >>= 1;
778                 }
779         }
780         return crc;
781 }
782
783 struct dev etherdevtab = {
784         'l',
785         "ether",
786
787         etherreset,
788         devinit,
789         ethershutdown,
790         etherattach,
791         etherwalk,
792         etherstat,
793         etheropen,
794         devcreate,
795         etherclose,
796         etherread,
797         etherbread,
798         etherwrite,
799         etherbwrite,
800         devremove,
801         etherwstat,
802         etherpower,
803 };