]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/ether/smsc.c
nusb/ether: fix ethertype filtering
[plan9front.git] / sys / src / cmd / nusb / ether / smsc.c
1 /*
2  * SMSC LAN95XX
3  */
4
5 #include <u.h>
6 #include <libc.h>
7 #include <thread.h>
8
9 #include "usb.h"
10 #include "dat.h"
11
12 enum {
13         Doburst         = 1,
14         Resettime       = 1000,
15         E2pbusytime     = 1000,
16         Afcdefault      = 0xF830A1,
17 //      Hsburst         = 37,   /* from original linux driver */
18         Hsburst         = 8,
19         Fsburst         = 129,
20         Defbulkdly      = 0x2000,
21
22         Ethp8021q       = 0x8100,
23         MACoffset       = 1,
24         PHYinternal     = 1,
25         Rxerror         = 0x8000,
26         Txfirst         = 0x2000,
27         Txlast          = 0x1000,
28
29         /* USB vendor requests */
30         Writereg        = 0xA0,
31         Readreg         = 0xA1,
32
33         /* device registers */
34         Intsts          = 0x08,
35         Txcfg           = 0x10,
36                 Txon    = 1<<2,
37         Hwcfg           = 0x14,
38                 Bir     = 1<<12,
39                 Rxdoff  = 3<<9,
40                 Mef     = 1<<5,
41                 Lrst    = 1<<3,
42                 Bce     = 1<<1,
43         Pmctrl          = 0x20,
44                 Phyrst  = 1<<4,
45         Ledgpio         = 0x24,
46                 Ledspd  = 1<<24,
47                 Ledlnk  = 1<<20,
48                 Ledfdx  = 1<<16,
49         Afccfg          = 0x2C,
50         E2pcmd          = 0x30,
51                 Busy    = 1<<31,
52                 Timeout = 1<<10,
53                 Read    = 0,
54         E2pdata         = 0x34,
55         Burstcap        = 0x38,
56         Intepctl        = 0x68,
57                 Phyint  = 1<<15,
58         Bulkdelay       = 0x6C,
59         Maccr           = 0x100,
60                 Mcpas   = 1<<19,
61                 Prms    = 1<<18,
62                 Hpfilt  = 1<<13,
63                 Txen    = 1<<3,
64                 Rxen    = 1<<2,
65         Addrh           = 0x104,
66         Addrl           = 0x108,
67         Hashh           = 0x10C,
68         Hashl           = 0x110,
69         MIIaddr         = 0x114,
70                 MIIwrite= 1<<1,
71                 MIIread = 0<<1,
72                 MIIbusy = 1<<0,
73         MIIdata         = 0x118,
74         Flow            = 0x11C,
75         Vlan1           = 0x120,
76         Coecr           = 0x130,
77                 Txcoe   = 1<<16,
78                 Rxcoemd = 1<<1,
79                 Rxcoe   = 1<<0,
80
81         /* MII registers */
82         Bmcr            = 0,
83                 Bmcrreset= 1<<15,
84                 Speed100= 1<<13,
85                 Anenable= 1<<12,
86                 Anrestart= 1<<9,
87                 Fulldpx = 1<<8,
88         Bmsr            = 1,
89         Advertise       = 4,
90                 Adcsma  = 0x0001,
91                 Ad10h   = 0x0020,
92                 Ad10f   = 0x0040,
93                 Ad100h  = 0x0080,
94                 Ad100f  = 0x0100,
95                 Adpause = 0x0400,
96                 Adpauseasym= 0x0800,
97                 Adall   = Ad10h|Ad10f|Ad100h|Ad100f,
98         Phyintsrc       = 29,
99         Phyintmask      = 30,
100                 Anegcomp= 1<<6,
101                 Linkdown= 1<<4,
102 };
103
104 static int
105 wr(Dev *d, int reg, int val)
106 {
107         int ret;
108
109         ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
110                 (uchar*)&val, sizeof(val));
111         if(ret < 0)
112                 fprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
113         return ret;
114 }
115
116 static int
117 rr(Dev *d, int reg)
118 {
119         int ret, rval;
120
121         ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
122                 (uchar*)&rval, sizeof(rval));
123         if(ret < 0){
124                 fprint(2, "%s: rr(%x): %r", argv0, reg);
125                 return 0;
126         }
127         return rval;
128 }
129
130 static int
131 miird(Dev *d, int idx)
132 {
133         while(rr(d, MIIaddr) & MIIbusy)
134                 ;
135         wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread);
136         while(rr(d, MIIaddr) & MIIbusy)
137                 ;
138         return rr(d, MIIdata);
139 }
140
141 static void
142 miiwr(Dev *d, int idx, int val)
143 {
144         while(rr(d, MIIaddr) & MIIbusy)
145                 ;
146         wr(d, MIIdata, val);
147         wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite);
148         while(rr(d, MIIaddr) & MIIbusy)
149                 ;
150 }
151
152 static int
153 eepromr(Dev *d, int off, uchar *buf, int len)
154 {
155         int i, v;
156
157         for(i = 0; i < E2pbusytime; i++)
158                 if((rr(d, E2pcmd) & Busy) == 0)
159                         break;
160         if(i == E2pbusytime)
161                 return -1;
162         for(i = 0; i < len; i++){
163                 wr(d, E2pcmd, Busy|Read|(i+off));
164                 while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
165                         ;
166                 if(v & Timeout)
167                         return -1;
168                 buf[i] = rr(d, E2pdata);
169         }
170         return 0;
171 }
172
173 static void
174 phyinit(Dev *d)
175 {
176         int i;
177
178         miiwr(d, Bmcr, Bmcrreset|Anenable);
179         for(i = 0; i < Resettime/10; i++){
180                 if((miird(d, Bmcr) & Bmcrreset) == 0)
181                         break;
182                 sleep(10);
183         }
184         miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
185 //      miiwr(d, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym);
186         miird(d, Phyintsrc);
187         miiwr(d, Phyintmask, Anegcomp|Linkdown);
188         miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
189 }
190
191
192 static int
193 doreset(Dev *d, int reg, int bit)
194 {
195         int i;
196
197         if(wr(d, reg, bit) < 0)
198                 return -1;
199         for(i = 0; i < Resettime/10; i++){
200                  if((rr(d, reg) & bit) == 0)
201                         return 1;
202                 sleep(10);
203         }
204         return 0;
205 }
206
207 static int
208 smscreceive(Dev *ep)
209 {
210         Block *b;
211         uint hd;
212         int n;
213
214         if(Doburst)
215                 b = allocb(Hsburst*512);
216         else
217                 b = allocb(Maxpkt+4);
218         if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
219                 freeb(b);
220                 return -1;
221         }
222         b->wp += n;
223         while(BLEN(b) >= 4){
224                 hd = GET4(b->rp);
225                 b->rp += 4;
226                 n = hd >> 16;
227                 if(n > BLEN(b))
228                         break;
229                 if((hd & Rxerror) == 0){
230                         if(n == BLEN(b)){
231                                 etheriq(b);
232                                 return 0;
233                         }
234                         etheriq(copyblock(b, n));
235                 }
236                 b->rp += (n + 3) & ~3;
237         }
238         freeb(b);
239         return 0;
240 }
241
242 static void
243 smsctransmit(Dev *ep, Block *b)
244 {
245         int n;
246
247         n = BLEN(b);
248         b->rp -= 8;
249         PUT4(b->rp, n | Txfirst | Txlast);
250         PUT4(b->rp+4, n);
251         write(ep->dfd, b->rp, BLEN(b));
252         freeb(b);
253 }
254
255 static int
256 smscpromiscuous(Dev *d, int on)
257 {
258         int rxctl;
259
260         rxctl = rr(d, Maccr);
261         if(on)
262                 rxctl |= Prms;
263         else
264                 rxctl &= ~Prms;
265         return wr(d, Maccr, rxctl);
266 }
267
268 static int
269 smscmulticast(Dev *d, uchar *, int)
270 {
271         int rxctl;
272
273         rxctl = rr(d, Maccr);
274         if(nmulti != 0)
275                 rxctl |= Mcpas;
276         else
277                 rxctl &= ~Mcpas;
278         return wr(d, Maccr, rxctl);
279 }
280
281 int
282 smscinit(Dev *d)
283 {
284         if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
285                 return -1;
286         if(!setmac)
287                 if(eepromr(d, MACoffset, macaddr, Eaddrlen) < 0)
288                         return -1;
289         wr(d, Addrl, GET4(macaddr));
290         wr(d, Addrh, GET2(macaddr+4));
291         if(Doburst){
292                 wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce);
293                 wr(d, Burstcap, Hsburst);
294         }else{
295                 wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir);
296                 wr(d, Burstcap, 0);
297         }
298         wr(d, Bulkdelay, Defbulkdly);
299         wr(d, Intsts, ~0);
300         wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx);
301         wr(d, Flow, 0);
302         wr(d, Afccfg, Afcdefault);
303         wr(d, Vlan1, Ethp8021q);
304         wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */
305
306         wr(d, Hashh, 0);
307         wr(d, Hashl, 0);
308         wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt));
309
310         phyinit(d);
311
312         wr(d, Intepctl, rr(d, Intepctl)|Phyint);
313         wr(d, Maccr, rr(d, Maccr)|Txen|Rxen);
314         wr(d, Txcfg, Txon);
315
316         eptransmit = smsctransmit;
317         epreceive = smscreceive;
318         eppromiscuous = smscpromiscuous;
319         epmulticast = smscmulticast;
320
321         return 0;
322 }