Improved device tags and sample usage
[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 #warning "splhi"
389                 //s = splhi();
390                 etheriq(ether, bp, 0);
391                 //splx(s);
392         }
393
394         if(!loopback){
395                 if(ether->vlanid){
396                         /* add tag */
397                         bp = padblock(bp, 2+2);
398                         memmove(bp->rp, bp->rp+4, 2*Eaddrlen);
399                         hnputs(bp->rp+2*Eaddrlen, Type8021Q);
400                         hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF);     /* prio:3 0:1 vid:12 */
401                         ether = ether->ctlr;
402                 }
403                 qbwrite(ether->oq, bp);
404                 if(ether->transmit != NULL)
405                         ether->transmit(ether);
406         }else
407                 freeb(bp);
408
409         return len;
410 }
411
412 static long
413 etherwrite(struct chan* chan, void* buf, long n, int64_t unused)
414 {
415         ERRSTACK(2);
416         struct ether *ether;
417         struct block *bp;
418         int onoff;
419         struct cmdbuf *cb;
420         long l;
421
422         ether = chan->aux;
423         rlock(&ether->rwlock);
424         if(waserror()) {
425                 runlock(&ether->rwlock);
426                 nexterror();
427         }
428         if(NETTYPE(chan->qid.path) != Ndataqid) {
429                 l = netifwrite(&ether->netif, chan, buf, n);
430                 if(l >= 0)
431                         goto out;
432                 cb = parsecmd(buf, n);
433                 if(strcmp(cb->f[0], "nonblocking") == 0){
434                         if(cb->nf <= 1)
435                                 onoff = 1;
436                         else
437                                 onoff = atoi(cb->f[1]);
438                         if(ether->oq != NULL)
439                                 qnoblock(ether->oq, onoff);
440                         kfree(cb);
441                         goto out;
442                 }
443                 kfree(cb);
444                 if(ether->ctl!=NULL){
445                         l = ether->ctl(ether,buf,n);
446                         goto out;
447                 }
448                 error(Ebadctl);
449         }
450
451         if(n > ether->maxmtu)
452                 error(Etoobig);
453         if(n < ether->minmtu)
454                 error(Etoosmall);
455         bp = allocb(n);
456         if(waserror()){
457                 freeb(bp);
458                 nexterror();
459         }
460         memmove(bp->rp, buf, n);
461         memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
462         bp->wp += n;
463         poperror();
464
465         l = etheroq(ether, bp);
466 out:
467         poperror();
468         runlock(&ether->rwlock);
469         return l;
470 }
471
472 static long
473 etherbwrite(struct chan* chan, struct block* bp, uint32_t unused)
474 {
475         ERRSTACK(2);
476         struct ether *ether;
477         long n;
478
479         n = BLEN(bp);
480         if(NETTYPE(chan->qid.path) != Ndataqid){
481                 if(waserror()) {
482                         freeb(bp);
483                         nexterror();
484                 }
485                 n = etherwrite(chan, bp->rp, n, 0);
486                 poperror();
487                 freeb(bp);
488                 return n;
489         }
490         ether = chan->aux;
491         rlock(&ether->rwlock);
492         if(waserror()) {
493                 runlock(&ether->rwlock);
494                 nexterror();
495         }
496         if(n > ether->maxmtu){
497                 freeb(bp);
498                 error(Etoobig);
499         }
500         if(n < ether->minmtu){
501                 freeb(bp);
502                 error(Etoosmall);
503         }
504         n = etheroq(ether, bp);
505         poperror();
506         runlock(&ether->rwlock);
507         return n;
508 }
509
510 static void
511 nop(struct ether*unused)
512 {
513 }
514
515 static long
516 vlanctl(struct ether *ether, void *buf, long n)
517 {
518         uint8_t ea[Eaddrlen];
519         struct ether *master;
520         struct cmdbuf *cb;
521         int i;
522
523         cb = parsecmd(buf, n);
524         if(cb->nf >= 2
525         && strcmp(cb->f[0], "ea")==0
526         && parseether(ea, cb->f[1]) == 0){
527                 kfree(cb);
528                 memmove(ether->ea, ea, Eaddrlen);
529                 memmove(ether->netif.addr, ether->ea, Eaddrlen);
530                 return 0;
531         }
532         if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){
533                 master = ether->ctlr;
534                 qlock(&master->vlq);
535                 for(i = 0; i < ARRAY_SIZE(master->vlans); i++)
536                         if(master->vlans[i] == ether){
537                                 ether->vlanid = 0;
538                                 master->nvlan--;
539                                 break;
540                         }
541                 qunlock(&master->vlq);
542                 kfree(cb);
543                 return 0;
544         }
545         kfree(cb);
546         error(Ebadctl);
547         return -1;      /* not reached */
548 }
549
550 static struct ether*
551 vlanalloc(struct ether *ether, int id)
552 {
553         ERRSTACK(2);
554         struct ether *vlan;
555         int i, fid;
556         char name[KNAMELEN];
557
558         qlock(&ether->vlq);
559         if(waserror()){
560                 qunlock(&ether->vlq);
561                 nexterror();
562         }
563         fid = -1;
564         for(i = 0; i < ARRAY_SIZE(ether->vlans); i++){
565                 vlan = ether->vlans[i];
566                 if(vlan != NULL && vlan->vlanid == id){
567                         poperror();
568                         qunlock(&ether->vlq);
569                         return vlan;
570                 }
571                 if(fid < 0 && (vlan == NULL || vlan->vlanid == 0))
572                         fid = i;
573         }
574         if(fid < 0)
575                 error(Enoifc);
576         snprintf(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
577         vlan = ether->vlans[fid];
578         if(vlan == NULL){
579                 vlan = kzmalloc(sizeof(struct ether), 1);
580                 if(vlan == NULL)
581                         error(Enovmem);
582                 netifinit(&vlan->netif, name, Ntypes, ether->netif.limit);
583                 ether->vlans[fid] = vlan;       /* id is still zero, can't be matched */
584                 ether->nvlan++;
585         }else
586                 memmove(vlan->netif.name, name, KNAMELEN-1);
587 #warning "fix me setting up vlans"
588 #if 0
589         vlan->attach = nop;
590         vlan->transmit = NULL;
591         vlan->ctl = vlanctl;
592         vlan->irq = -1;
593 //      vlan->promiscuous = ether->promiscuous;
594 //      vlan->multicast = ether->multicast;
595         vlan->arg = vlan;
596         vlan->mbps = ether->mbps;
597         vlan->fullduplex = ether->fullduplex;
598         vlan->encry = ether->encry;
599         vlan->minmtu = ether->minmtu;
600         vlan->maxmtu = ether->maxmtu;
601         vlan->ctlrno = ether->ctlrno;
602         vlan->vlanid = id;
603         vlan->alen = Eaddrlen;
604         memmove(vlan->addr, ether->addr, sizeof(vlan->addr));
605         memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast));
606         vlan->oq = NULL;
607         vlan->ctlr = ether;
608         vlan->vlanid = id;
609 #endif
610         poperror();
611         qunlock(&ether->vlq);
612         return vlan;
613 }
614
615 static struct {
616         char*   type;
617         int     (*reset)(struct ether*);
618 } cards[MaxEther+1];
619
620 void
621 addethercard(char* t, int (*r)(struct ether*))
622 {
623         static int ncard;
624
625         if(ncard == MaxEther)
626                 panic("too many ether cards");
627         cards[ncard].type = t;
628         cards[ncard].reset = r;
629         ncard++;
630 }
631
632 int
633 parseether(uint8_t *to, char *from)
634 {
635         char nip[4];
636         char *p;
637         int i;
638
639         p = from;
640         for(i = 0; i < Eaddrlen; i++){
641                 if(*p == 0)
642                         return -1;
643                 nip[0] = *p++;
644                 if(*p == 0)
645                         return -1;
646                 nip[1] = *p++;
647                 nip[2] = 0;
648                 to[i] = strtoul(nip, 0, 16);
649                 if(*p == ':')
650                         p++;
651         }
652         return 0;
653 }
654
655 static void
656 etherreset(void)
657 {
658 #warning "fix me etherreset"
659 #if 0
660         struct ether *ether;
661         int i, n, ctlrno;
662         char name[KNAMELEN], buf[128];
663
664         for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
665                 if(ether == 0)
666                         ether = kzmalloc(sizeof(struct ether), 0);
667                 memset(ether, 0, sizeof(struct ether));
668                 ether->ctlrno = ctlrno;
669                 ether->netif.mbps = 10;
670                 ether->minmtu = ETHERMINTU;
671                 ether->maxmtu = ETHERMAXTU;
672                 ether->netif.itype = -1;
673
674                 if(archether(ctlrno, ether) <= 0)
675                         continue;
676
677                 for(n = 0; cards[n].type; n++){
678                         if(cistrcmp(cards[n].type, ether->type))
679                                 continue;
680                         for(i = 0; i < ether->nopt; i++){
681                                 if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
682                                         if(parseether(ether->ea, &ether->opt[i][3]) == -1)
683                                                 memset(ether->ea, 0, Eaddrlen);
684                                 }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
685                                         cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
686                                         ether->fullduplex = 1;
687                                 else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
688                                         ether->mbps = 100;
689                         }
690                         if(cards[n].reset(ether))
691                                 break;
692                         snprintf(name, sizeof(name), "ether%d", ctlrno);
693
694                         if(ether->interrupt != NULL)
695                                 intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
696
697                         i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
698                                 ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
699                         if(ether->mem)
700                                 i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
701                         if(ether->size)
702                                 i += sprint(buf+i, " size 0x%luX", ether->size);
703                         i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
704                                 ether->ea[0], ether->ea[1], ether->ea[2],
705                                 ether->ea[3], ether->ea[4], ether->ea[5]);
706                         sprint(buf+i, "\n");
707                         iprint(buf);
708
709                         if(ether->mbps == 100){
710                                 netifinit(ether, name, Ntypes, 256*1024);
711                                 if(ether->oq == 0)
712                                         ether->oq = qopen(256*1024, Qmsg, 0, 0);
713                         }
714                         else{
715                                 netifinit(ether, name, Ntypes, 64*1024);
716                                 if(ether->oq == 0)
717                                         ether->oq = qopen(64*1024, Qmsg, 0, 0);
718                         }
719                         if(ether->oq == 0)
720                                 panic("etherreset %s", name);
721                         ether->alen = Eaddrlen;
722                         memmove(ether->addr, ether->ea, Eaddrlen);
723                         memset(ether->bcast, 0xFF, Eaddrlen);
724
725                         etherxx[ctlrno] = ether;
726                         ether = 0;
727                         break;
728                 }
729         }
730         if(ether)
731                 kfree(ether);
732 #endif
733 }
734
735 static void
736 etherpower(int on)
737 {
738         int i;
739         struct ether *ether;
740
741         for(i = 0; i < MaxEther; i++){
742                 if((ether = etherxx[i]) == NULL || ether->power == NULL)
743                         continue;
744                 if(on){
745                         if(canrlock(&ether->rwlock))
746                                 continue;
747                         if(ether->power != NULL)
748                                 ether->power(ether, on);
749                         wunlock(&ether->rwlock);
750                 }else{
751                         if(ether->rwlock.nr_readers)
752                                 continue;
753
754                         wlock(&ether->rwlock);
755                         if(ether->power != NULL)
756                                 ether->power(ether, on);
757                         /* Keep locked until power goes back on */
758                 }
759         }
760 }
761
762 #define ETHERPOLY 0xedb88320
763
764 /* really slow 32 bit crc for ethers */
765 uint32_t
766 ethercrc(uint8_t *p, int len)
767 {
768         int i, j;
769         uint32_t crc, b;
770
771         crc = 0xffffffff;
772         for(i = 0; i < len; i++){
773                 b = *p++;
774                 for(j = 0; j < 8; j++){
775                         crc = (crc>>1) ^ (((crc^b) & 1) ? ETHERPOLY : 0);
776                         b >>= 1;
777                 }
778         }
779         return crc;
780 }
781
782 struct dev etherdevtab __devtab = {
783         'l',
784         "ether",
785
786         etherreset,
787         devinit,
788         ethershutdown,
789         etherattach,
790         etherwalk,
791         etherstat,
792         etheropen,
793         devcreate,
794         etherclose,
795         etherread,
796         etherbread,
797         etherwrite,
798         etherbwrite,
799         devremove,
800         etherwstat,
801         etherpower,
802 };