1 | /* $NetBSD: if_axen.c,v 1.9 2016/06/10 13:27:15 ozaki-r Exp $ */ |
2 | /* $OpenBSD: if_axen.c,v 1.3 2013/10/21 10:10:22 yuo Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 2013 Yojiro UO <yuo@openbsd.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | /* |
21 | * ASIX Electronics AX88178a USB 2.0 ethernet and AX88179 USB 3.0 Ethernet |
22 | * driver. |
23 | */ |
24 | |
25 | #include <sys/cdefs.h> |
26 | __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v 1.9 2016/06/10 13:27:15 ozaki-r Exp $" ); |
27 | |
28 | #ifdef _KERNEL_OPT |
29 | #include "opt_inet.h" |
30 | #endif |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/bus.h> |
34 | #include <sys/device.h> |
35 | #include <sys/kernel.h> |
36 | #include <sys/mbuf.h> |
37 | #include <sys/module.h> |
38 | #include <sys/rwlock.h> |
39 | #include <sys/socket.h> |
40 | #include <sys/sockio.h> |
41 | #include <sys/systm.h> |
42 | |
43 | #include <sys/rndsource.h> |
44 | |
45 | #include <net/if.h> |
46 | #include <net/if_dl.h> |
47 | #include <net/if_ether.h> |
48 | #include <net/if_media.h> |
49 | |
50 | #include <net/bpf.h> |
51 | |
52 | #include <dev/mii/mii.h> |
53 | #include <dev/mii/miivar.h> |
54 | |
55 | #include <dev/usb/usb.h> |
56 | #include <dev/usb/usbdi.h> |
57 | #include <dev/usb/usbdi_util.h> |
58 | #include <dev/usb/usbdivar.h> |
59 | #include <dev/usb/usbdevs.h> |
60 | |
61 | #include <dev/usb/if_axenreg.h> |
62 | |
63 | #ifdef AXEN_DEBUG |
64 | #define DPRINTF(x) do { if (axendebug) printf x; } while (/*CONSTCOND*/0) |
65 | #define DPRINTFN(n,x) do { if (axendebug >= (n)) printf x; } while (/*CONSTCOND*/0) |
66 | int axendebug = 0; |
67 | #else |
68 | #define DPRINTF(x) |
69 | #define DPRINTFN(n,x) |
70 | #endif |
71 | |
72 | #define AXEN_TOE /* enable checksum offload function */ |
73 | |
74 | /* |
75 | * Various supported device vendors/products. |
76 | */ |
77 | static const struct axen_type axen_devs[] = { |
78 | #if 0 /* not tested */ |
79 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178A}, AX178A }, |
80 | #endif |
81 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88179}, AX179 } |
82 | }; |
83 | |
84 | #define axen_lookup(v, p) ((const struct axen_type *)usb_lookup(axen_devs, v, p)) |
85 | |
86 | static int axen_match(device_t, cfdata_t, void *); |
87 | static void axen_attach(device_t, device_t, void *); |
88 | static int axen_detach(device_t, int); |
89 | static int axen_activate(device_t, devact_t); |
90 | |
91 | CFATTACH_DECL_NEW(axen, sizeof(struct axen_softc), |
92 | axen_match, axen_attach, axen_detach, axen_activate); |
93 | |
94 | static int axen_tx_list_init(struct axen_softc *); |
95 | static int axen_rx_list_init(struct axen_softc *); |
96 | static struct mbuf *axen_newbuf(void); |
97 | static int axen_encap(struct axen_softc *, struct mbuf *, int); |
98 | static void axen_rxeof(struct usbd_xfer *, void *, usbd_status); |
99 | static void axen_txeof(struct usbd_xfer *, void *, usbd_status); |
100 | static void axen_tick(void *); |
101 | static void axen_tick_task(void *); |
102 | static void axen_start(struct ifnet *); |
103 | static int axen_ioctl(struct ifnet *, u_long, void *); |
104 | static int axen_init(struct ifnet *); |
105 | static void axen_stop(struct ifnet *, int); |
106 | static void axen_watchdog(struct ifnet *); |
107 | static int axen_miibus_readreg(device_t, int, int); |
108 | static void axen_miibus_writereg(device_t, int, int, int); |
109 | static void axen_miibus_statchg(struct ifnet *); |
110 | static int axen_cmd(struct axen_softc *, int, int, int, void *); |
111 | static int axen_ifmedia_upd(struct ifnet *); |
112 | static void axen_ifmedia_sts(struct ifnet *, struct ifmediareq *); |
113 | static void axen_reset(struct axen_softc *); |
114 | #if 0 |
115 | static int axen_ax88179_eeprom(struct axen_softc *, void *); |
116 | #endif |
117 | |
118 | static void axen_iff(struct axen_softc *); |
119 | static void axen_lock_mii(struct axen_softc *); |
120 | static void axen_unlock_mii(struct axen_softc *); |
121 | |
122 | static void axen_ax88179_init(struct axen_softc *); |
123 | |
124 | /* Get exclusive access to the MII registers */ |
125 | static void |
126 | axen_lock_mii(struct axen_softc *sc) |
127 | { |
128 | |
129 | sc->axen_refcnt++; |
130 | rw_enter(&sc->axen_mii_lock, RW_WRITER); |
131 | } |
132 | |
133 | static void |
134 | axen_unlock_mii(struct axen_softc *sc) |
135 | { |
136 | |
137 | rw_exit(&sc->axen_mii_lock); |
138 | if (--sc->axen_refcnt < 0) |
139 | usb_detach_wakeupold(sc->axen_dev); |
140 | } |
141 | |
142 | static int |
143 | axen_cmd(struct axen_softc *sc, int cmd, int index, int val, void *buf) |
144 | { |
145 | usb_device_request_t req; |
146 | usbd_status err; |
147 | |
148 | KASSERT(rw_lock_held(&sc->axen_mii_lock)); |
149 | |
150 | if (sc->axen_dying) |
151 | return 0; |
152 | |
153 | if (AXEN_CMD_DIR(cmd)) |
154 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
155 | else |
156 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
157 | req.bRequest = AXEN_CMD_CMD(cmd); |
158 | USETW(req.wValue, val); |
159 | USETW(req.wIndex, index); |
160 | USETW(req.wLength, AXEN_CMD_LEN(cmd)); |
161 | |
162 | err = usbd_do_request(sc->axen_udev, &req, buf); |
163 | DPRINTFN(5, ("axen_cmd: cmd 0x%04x val 0x%04x len %d\n" , |
164 | cmd, val, AXEN_CMD_LEN(cmd))); |
165 | |
166 | if (err) { |
167 | DPRINTF(("axen_cmd err: cmd: %d, error: %d\n" , cmd, err)); |
168 | return -1; |
169 | } |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int |
175 | axen_miibus_readreg(device_t dev, int phy, int reg) |
176 | { |
177 | struct axen_softc *sc = device_private(dev); |
178 | usbd_status err; |
179 | uint16_t val; |
180 | int ival; |
181 | |
182 | if (sc->axen_dying) { |
183 | DPRINTF(("axen: dying\n" )); |
184 | return 0; |
185 | } |
186 | |
187 | if (sc->axen_phyno != phy) |
188 | return 0; |
189 | |
190 | axen_lock_mii(sc); |
191 | err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &val); |
192 | axen_unlock_mii(sc); |
193 | |
194 | if (err) { |
195 | aprint_error_dev(sc->axen_dev, "read PHY failed\n" ); |
196 | return -1; |
197 | } |
198 | |
199 | ival = le16toh(val); |
200 | DPRINTFN(2,("axen_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n" , |
201 | phy, reg, ival)); |
202 | |
203 | if (reg == MII_BMSR) { |
204 | ival &= ~BMSR_EXTCAP; |
205 | } |
206 | |
207 | return ival; |
208 | } |
209 | |
210 | static void |
211 | axen_miibus_writereg(device_t dev, int phy, int reg, int val) |
212 | { |
213 | struct axen_softc *sc = device_private(dev); |
214 | usbd_status err; |
215 | uint16_t uval; |
216 | |
217 | if (sc->axen_dying) |
218 | return; |
219 | |
220 | if (sc->axen_phyno != phy) |
221 | return; |
222 | |
223 | uval = htole16(val); |
224 | axen_lock_mii(sc); |
225 | err = axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); |
226 | axen_unlock_mii(sc); |
227 | DPRINTFN(2, ("axen_miibus_writereg: phy 0x%x reg 0x%x val 0x%0x\n" , |
228 | phy, reg, val)); |
229 | |
230 | if (err) { |
231 | aprint_error_dev(sc->axen_dev, "write PHY failed\n" ); |
232 | return; |
233 | } |
234 | } |
235 | |
236 | static void |
237 | axen_miibus_statchg(struct ifnet *ifp) |
238 | { |
239 | struct axen_softc *sc = ifp->if_softc; |
240 | struct mii_data *mii = GET_MII(sc); |
241 | int err; |
242 | uint16_t val; |
243 | uint16_t wval; |
244 | |
245 | sc->axen_link = 0; |
246 | if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == |
247 | (IFM_ACTIVE | IFM_AVALID)) { |
248 | switch (IFM_SUBTYPE(mii->mii_media_active)) { |
249 | case IFM_10_T: |
250 | case IFM_100_TX: |
251 | sc->axen_link++; |
252 | break; |
253 | case IFM_1000_T: |
254 | sc->axen_link++; |
255 | break; |
256 | default: |
257 | break; |
258 | } |
259 | } |
260 | |
261 | /* Lost link, do nothing. */ |
262 | if (sc->axen_link == 0) |
263 | return; |
264 | |
265 | val = 0; |
266 | if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) |
267 | val |= AXEN_MEDIUM_FDX; |
268 | |
269 | val |= (AXEN_MEDIUM_RECV_EN | AXEN_MEDIUM_ALWAYS_ONE); |
270 | val |= (AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN); |
271 | |
272 | switch (IFM_SUBTYPE(mii->mii_media_active)) { |
273 | case IFM_1000_T: |
274 | val |= AXEN_MEDIUM_GIGA | AXEN_MEDIUM_EN_125MHZ; |
275 | break; |
276 | case IFM_100_TX: |
277 | val |= AXEN_MEDIUM_PS; |
278 | break; |
279 | case IFM_10_T: |
280 | /* doesn't need to be handled */ |
281 | break; |
282 | } |
283 | |
284 | DPRINTF(("axen_miibus_statchg: val=0x%x\n" , val)); |
285 | wval = htole16(val); |
286 | axen_lock_mii(sc); |
287 | err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); |
288 | axen_unlock_mii(sc); |
289 | if (err) { |
290 | aprint_error_dev(sc->axen_dev, "media change failed\n" ); |
291 | return; |
292 | } |
293 | } |
294 | |
295 | /* |
296 | * Set media options. |
297 | */ |
298 | static int |
299 | axen_ifmedia_upd(struct ifnet *ifp) |
300 | { |
301 | struct axen_softc *sc = ifp->if_softc; |
302 | struct mii_data *mii = GET_MII(sc); |
303 | int rc; |
304 | |
305 | sc->axen_link = 0; |
306 | |
307 | if (mii->mii_instance) { |
308 | struct mii_softc *miisc; |
309 | |
310 | LIST_FOREACH(miisc, &mii->mii_phys, mii_list) |
311 | mii_phy_reset(miisc); |
312 | } |
313 | |
314 | if ((rc = mii_mediachg(mii)) == ENXIO) |
315 | return 0; |
316 | return rc; |
317 | } |
318 | |
319 | /* |
320 | * Report current media status. |
321 | */ |
322 | static void |
323 | axen_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) |
324 | { |
325 | struct axen_softc *sc = ifp->if_softc; |
326 | struct mii_data *mii = GET_MII(sc); |
327 | |
328 | mii_pollstat(mii); |
329 | ifmr->ifm_active = mii->mii_media_active; |
330 | ifmr->ifm_status = mii->mii_media_status; |
331 | } |
332 | |
333 | static void |
334 | axen_iff(struct axen_softc *sc) |
335 | { |
336 | struct ifnet *ifp = GET_IFP(sc); |
337 | struct ethercom *ec = &sc->axen_ec; |
338 | struct ether_multi *enm; |
339 | struct ether_multistep step; |
340 | uint32_t h = 0; |
341 | uint16_t rxmode; |
342 | uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
343 | uint16_t wval; |
344 | |
345 | if (sc->axen_dying) |
346 | return; |
347 | |
348 | rxmode = 0; |
349 | |
350 | /* Enable receiver, set RX mode */ |
351 | axen_lock_mii(sc); |
352 | axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); |
353 | rxmode = le16toh(wval); |
354 | rxmode &= ~(AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST | |
355 | AXEN_RXCTL_PROMISC); |
356 | ifp->if_flags &= ~IFF_ALLMULTI; |
357 | |
358 | /* |
359 | * Always accept broadcast frames. |
360 | * Always accept frames destined to our station address. |
361 | */ |
362 | rxmode |= AXEN_RXCTL_ACPT_BCAST; |
363 | |
364 | if (ifp->if_flags & IFF_PROMISC || ec->ec_multicnt > 0 /* XXX */) { |
365 | ifp->if_flags |= IFF_ALLMULTI; |
366 | rxmode |= AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST; |
367 | if (ifp->if_flags & IFF_PROMISC) |
368 | rxmode |= AXEN_RXCTL_PROMISC; |
369 | } else { |
370 | rxmode |= AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_ACPT_PHY_MCAST; |
371 | |
372 | /* now program new ones */ |
373 | ETHER_FIRST_MULTI(step, ec, enm); |
374 | while (enm != NULL) { |
375 | h = ether_crc32_be(enm->enm_addrlo, |
376 | ETHER_ADDR_LEN) >> 26; |
377 | hashtbl[h / 8] |= 1 << (h % 8); |
378 | ETHER_NEXT_MULTI(step, enm); |
379 | } |
380 | } |
381 | |
382 | axen_cmd(sc, AXEN_CMD_MAC_WRITE_FILTER, 8, AXEN_FILTER_MULTI, hashtbl); |
383 | wval = htole16(rxmode); |
384 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); |
385 | axen_unlock_mii(sc); |
386 | } |
387 | |
388 | static void |
389 | axen_reset(struct axen_softc *sc) |
390 | { |
391 | |
392 | if (sc->axen_dying) |
393 | return; |
394 | /* XXX What to reset? */ |
395 | |
396 | /* Wait a little while for the chip to get its brains in order. */ |
397 | DELAY(1000); |
398 | } |
399 | |
400 | #if 0 /* not used */ |
401 | #define AXEN_GPIO_WRITE(x,y) do { \ |
402 | axen_cmd(sc, AXEN_CMD_WRITE_GPIO, 0, (x), NULL); \ |
403 | usbd_delay_ms(sc->axen_udev, (y)); \ |
404 | } while (/*CONSTCOND*/0) |
405 | |
406 | static int |
407 | axen_ax88179_eeprom(struct axen_softc *sc, void *addr) |
408 | { |
409 | int i, retry; |
410 | uint8_t eeprom[20]; |
411 | uint16_t csum; |
412 | uint16_t buf; |
413 | |
414 | for (i = 0; i < 6; i++) { |
415 | /* set eeprom address */ |
416 | buf = htole16(i); |
417 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MAC_EEPROM_ADDR, &buf); |
418 | |
419 | /* set eeprom command */ |
420 | buf = htole16(AXEN_EEPROM_READ); |
421 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MAC_EEPROM_CMD, &buf); |
422 | |
423 | /* check the value is ready */ |
424 | retry = 3; |
425 | do { |
426 | buf = htole16(AXEN_EEPROM_READ); |
427 | usbd_delay_ms(sc->axen_udev, 10); |
428 | axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_MAC_EEPROM_CMD, |
429 | &buf); |
430 | retry--; |
431 | if (retry < 0) |
432 | return EINVAL; |
433 | } while ((le16toh(buf) & 0xff) & AXEN_EEPROM_BUSY); |
434 | |
435 | /* read data */ |
436 | axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_EEPROM_READ, |
437 | &eeprom[i * 2]); |
438 | |
439 | /* sanity check */ |
440 | if ((i == 0) && (eeprom[0] == 0xff)) |
441 | return EINVAL; |
442 | } |
443 | |
444 | /* check checksum */ |
445 | csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9]; |
446 | csum = (csum >> 8) + (csum & 0xff) + eeprom[10]; |
447 | if (csum != 0xff) { |
448 | printf("eeprom checksum mismatchi(0x%02x)\n" , csum); |
449 | return EINVAL; |
450 | } |
451 | |
452 | memcpy(addr, eeprom, ETHER_ADDR_LEN); |
453 | return 0; |
454 | } |
455 | #endif |
456 | |
457 | static void |
458 | axen_ax88179_init(struct axen_softc *sc) |
459 | { |
460 | struct axen_qctrl qctrl; |
461 | uint16_t ctl, temp; |
462 | uint16_t wval; |
463 | uint8_t val; |
464 | |
465 | axen_lock_mii(sc); |
466 | |
467 | /* XXX: ? */ |
468 | axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val); |
469 | DPRINTFN(5, ("AXEN_CMD_MAC_READ(0x05): 0x%02x\n" , val)); |
470 | |
471 | /* check AX88179 version, UA1 / UA2 */ |
472 | axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_GENERAL_STATUS, &val); |
473 | /* UA1 */ |
474 | if (!(val & AXEN_GENERAL_STATUS_MASK)) { |
475 | sc->axen_rev = AXEN_REV_UA1; |
476 | DPRINTF(("AX88179 ver. UA1\n" )); |
477 | } else { |
478 | sc->axen_rev = AXEN_REV_UA2; |
479 | DPRINTF(("AX88179 ver. UA2\n" )); |
480 | } |
481 | |
482 | /* power up ethernet PHY */ |
483 | wval = htole16(0); |
484 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); |
485 | |
486 | wval = htole16(AXEN_PHYPWR_RSTCTL_IPRL); |
487 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); |
488 | usbd_delay_ms(sc->axen_udev, 200); |
489 | |
490 | /* set clock mode */ |
491 | val = AXEN_PHYCLK_ACS | AXEN_PHYCLK_BCS; |
492 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); |
493 | usbd_delay_ms(sc->axen_udev, 100); |
494 | |
495 | /* set monitor mode (disable) */ |
496 | val = AXEN_MONITOR_NONE; |
497 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MONITOR_MODE, &val); |
498 | |
499 | /* enable auto detach */ |
500 | axen_cmd(sc, AXEN_CMD_EEPROM_READ, 2, AXEN_EEPROM_STAT, &wval); |
501 | temp = le16toh(wval); |
502 | DPRINTFN(2,("EEPROM0x43 = 0x%04x\n" , temp)); |
503 | if (!(temp == 0xffff) && !(temp & 0x0100)) { |
504 | /* Enable auto detach bit */ |
505 | val = 0; |
506 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); |
507 | val = AXEN_PHYCLK_ULR; |
508 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PHYCLK, &val); |
509 | usbd_delay_ms(sc->axen_udev, 100); |
510 | |
511 | axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_PHYPWR_RSTCTL, &wval); |
512 | ctl = le16toh(wval); |
513 | ctl |= AXEN_PHYPWR_RSTCTL_AUTODETACH; |
514 | wval = htole16(ctl); |
515 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_PHYPWR_RSTCTL, &wval); |
516 | usbd_delay_ms(sc->axen_udev, 200); |
517 | aprint_error_dev(sc->axen_dev, "enable auto detach (0x%04x)\n" , |
518 | ctl); |
519 | } |
520 | |
521 | /* bulkin queue setting */ |
522 | axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_USB_UPLINK, &val); |
523 | switch (val) { |
524 | case AXEN_USB_FS: |
525 | DPRINTF(("uplink: USB1.1\n" )); |
526 | qctrl.ctrl = 0x07; |
527 | qctrl.timer_low = 0xcc; |
528 | qctrl.timer_high = 0x4c; |
529 | qctrl.bufsize = AXEN_BUFSZ_LS - 1; |
530 | qctrl.ifg = 0x08; |
531 | break; |
532 | case AXEN_USB_HS: |
533 | DPRINTF(("uplink: USB2.0\n" )); |
534 | qctrl.ctrl = 0x07; |
535 | qctrl.timer_low = 0x02; |
536 | qctrl.timer_high = 0xa0; |
537 | qctrl.bufsize = AXEN_BUFSZ_HS - 1; |
538 | qctrl.ifg = 0xff; |
539 | break; |
540 | case AXEN_USB_SS: |
541 | DPRINTF(("uplink: USB3.0\n" )); |
542 | qctrl.ctrl = 0x07; |
543 | qctrl.timer_low = 0x4f; |
544 | qctrl.timer_high = 0x00; |
545 | qctrl.bufsize = AXEN_BUFSZ_SS - 1; |
546 | qctrl.ifg = 0xff; |
547 | break; |
548 | default: |
549 | aprint_error_dev(sc->axen_dev, "unknown uplink bus:0x%02x\n" , |
550 | val); |
551 | axen_unlock_mii(sc); |
552 | return; |
553 | } |
554 | axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl); |
555 | |
556 | /* |
557 | * set buffer high/low watermark to pause/resume. |
558 | * write 2byte will set high/log simultaneous with AXEN_PAUSE_HIGH. |
559 | * XXX: what is the best value? OSX driver uses 0x3c-0x4c as LOW-HIGH |
560 | * watermark parameters. |
561 | */ |
562 | val = 0x34; |
563 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PAUSE_LOW_WATERMARK, &val); |
564 | val = 0x52; |
565 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_PAUSE_HIGH_WATERMARK, &val); |
566 | |
567 | /* Set RX/TX configuration. */ |
568 | /* Offloadng enable */ |
569 | #ifdef AXEN_TOE |
570 | val = AXEN_RXCOE_IPv4 | AXEN_RXCOE_TCPv4 | AXEN_RXCOE_UDPv4 | |
571 | AXEN_RXCOE_TCPv6 | AXEN_RXCOE_UDPv6; |
572 | #else |
573 | val = AXEN_RXCOE_OFF; |
574 | #endif |
575 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_RX_COE, &val); |
576 | |
577 | #ifdef AXEN_TOE |
578 | val = AXEN_TXCOE_IPv4 | AXEN_TXCOE_TCPv4 | AXEN_TXCOE_UDPv4 | |
579 | AXEN_TXCOE_TCPv6 | AXEN_TXCOE_UDPv6; |
580 | #else |
581 | val = AXEN_TXCOE_OFF; |
582 | #endif |
583 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val); |
584 | |
585 | /* Set RX control register */ |
586 | ctl = AXEN_RXCTL_IPE | AXEN_RXCTL_DROPCRCERR | AXEN_RXCTL_AUTOB; |
587 | ctl |= AXEN_RXCTL_ACPT_PHY_MCAST | AXEN_RXCTL_ACPT_ALL_MCAST; |
588 | ctl |= AXEN_RXCTL_START; |
589 | wval = htole16(ctl); |
590 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); |
591 | |
592 | /* set monitor mode (enable) */ |
593 | val = AXEN_MONITOR_PMETYPE | AXEN_MONITOR_PMEPOL | AXEN_MONITOR_RWMP; |
594 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_MONITOR_MODE, &val); |
595 | axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_MONITOR_MODE, &val); |
596 | DPRINTF(("axen: Monitor mode = 0x%02x\n" , val)); |
597 | |
598 | /* set medium type */ |
599 | ctl = AXEN_MEDIUM_GIGA | AXEN_MEDIUM_FDX | AXEN_MEDIUM_ALWAYS_ONE | |
600 | AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN; |
601 | ctl |= AXEN_MEDIUM_RECV_EN; |
602 | wval = htole16(ctl); |
603 | DPRINTF(("axen: set to medium mode: 0x%04x\n" , ctl)); |
604 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); |
605 | usbd_delay_ms(sc->axen_udev, 100); |
606 | |
607 | axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MEDIUM_STATUS, &wval); |
608 | DPRINTF(("axen: current medium mode: 0x%04x\n" , le16toh(wval))); |
609 | |
610 | axen_unlock_mii(sc); |
611 | |
612 | #if 0 /* XXX: TBD.... */ |
613 | #define GMII_LED_ACTIVE 0x1a |
614 | #define GMII_PHY_PAGE_SEL 0x1e |
615 | #define GMII_PHY_PAGE_SEL 0x1f |
616 | #define GMII_PAGE_EXT 0x0007 |
617 | axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE_SEL, |
618 | GMII_PAGE_EXT); |
619 | axen_miibus_writereg(&sc->axen_dev, sc->axen_phyno, GMII_PHY_PAGE, |
620 | 0x002c); |
621 | #endif |
622 | |
623 | #if 1 /* XXX: phy hack ? */ |
624 | axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0005); |
625 | axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x0C, 0x0000); |
626 | val = axen_miibus_readreg(sc->axen_dev, sc->axen_phyno, 0x0001); |
627 | axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x01, |
628 | val | 0x0080); |
629 | axen_miibus_writereg(sc->axen_dev, sc->axen_phyno, 0x1F, 0x0000); |
630 | #endif |
631 | } |
632 | |
633 | static int |
634 | axen_match(device_t parent, cfdata_t match, void *aux) |
635 | { |
636 | struct usb_attach_arg *uaa = aux; |
637 | |
638 | return axen_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
639 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
640 | } |
641 | |
642 | static void |
643 | axen_attach(device_t parent, device_t self, void *aux) |
644 | { |
645 | struct axen_softc *sc = device_private(self); |
646 | struct usb_attach_arg *uaa = aux; |
647 | struct usbd_device *dev = uaa->uaa_device; |
648 | usbd_status err; |
649 | usb_interface_descriptor_t *id; |
650 | usb_endpoint_descriptor_t *ed; |
651 | struct mii_data *mii; |
652 | uint8_t eaddr[ETHER_ADDR_LEN]; |
653 | char *devinfop; |
654 | const char *devname = device_xname(self); |
655 | struct ifnet *ifp; |
656 | int i, s; |
657 | |
658 | aprint_naive("\n" ); |
659 | aprint_normal("\n" ); |
660 | |
661 | sc->axen_dev = self; |
662 | sc->axen_udev = dev; |
663 | |
664 | devinfop = usbd_devinfo_alloc(dev, 0); |
665 | aprint_normal_dev(self, "%s\n" , devinfop); |
666 | usbd_devinfo_free(devinfop); |
667 | |
668 | err = usbd_set_config_no(dev, AXEN_CONFIG_NO, 1); |
669 | if (err) { |
670 | aprint_error_dev(self, "failed to set configuration" |
671 | ", err=%s\n" , usbd_errstr(err)); |
672 | return; |
673 | } |
674 | |
675 | sc->axen_flags = axen_lookup(uaa->uaa_vendor, uaa->uaa_product)->axen_flags; |
676 | |
677 | rw_init(&sc->axen_mii_lock); |
678 | usb_init_task(&sc->axen_tick_task, axen_tick_task, sc, 0); |
679 | |
680 | err = usbd_device2interface_handle(dev, AXEN_IFACE_IDX,&sc->axen_iface); |
681 | if (err) { |
682 | aprint_error_dev(self, "getting interface handle failed\n" ); |
683 | return; |
684 | } |
685 | |
686 | sc->axen_product = uaa->uaa_product; |
687 | sc->axen_vendor = uaa->uaa_vendor; |
688 | |
689 | id = usbd_get_interface_descriptor(sc->axen_iface); |
690 | |
691 | /* decide on what our bufsize will be */ |
692 | switch (sc->axen_udev->ud_speed) { |
693 | case USB_SPEED_SUPER: |
694 | sc->axen_bufsz = AXEN_BUFSZ_SS * 1024; |
695 | break; |
696 | case USB_SPEED_HIGH: |
697 | sc->axen_bufsz = AXEN_BUFSZ_HS * 1024; |
698 | break; |
699 | default: |
700 | sc->axen_bufsz = AXEN_BUFSZ_LS * 1024; |
701 | break; |
702 | } |
703 | |
704 | /* Find endpoints. */ |
705 | for (i = 0; i < id->bNumEndpoints; i++) { |
706 | ed = usbd_interface2endpoint_descriptor(sc->axen_iface, i); |
707 | if (!ed) { |
708 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
709 | return; |
710 | } |
711 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
712 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
713 | sc->axen_ed[AXEN_ENDPT_RX] = ed->bEndpointAddress; |
714 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
715 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
716 | sc->axen_ed[AXEN_ENDPT_TX] = ed->bEndpointAddress; |
717 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
718 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { |
719 | sc->axen_ed[AXEN_ENDPT_INTR] = ed->bEndpointAddress; |
720 | } |
721 | } |
722 | |
723 | s = splnet(); |
724 | |
725 | sc->axen_phyno = AXEN_PHY_ID; |
726 | DPRINTF(("%s: phyno %d\n" , device_xname(self), sc->axen_phyno)); |
727 | |
728 | /* |
729 | * Get station address. |
730 | */ |
731 | #if 0 /* read from eeprom */ |
732 | if (axen_ax88179_eeprom(sc, &eaddr)) { |
733 | printf("EEPROM checksum error\n" ); |
734 | return; |
735 | } |
736 | #else /* use MAC command */ |
737 | axen_lock_mii(sc); |
738 | axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, 6, AXEN_CMD_MAC_NODE_ID, &eaddr); |
739 | axen_unlock_mii(sc); |
740 | #endif |
741 | axen_ax88179_init(sc); |
742 | |
743 | /* |
744 | * An ASIX chip was detected. Inform the world. |
745 | */ |
746 | if (sc->axen_flags & AX178A) |
747 | aprint_normal_dev(self, "AX88178a\n" ); |
748 | else if (sc->axen_flags & AX179) |
749 | aprint_normal_dev(self, "AX88179\n" ); |
750 | aprint_normal_dev(self, "Ethernet address %s\n" , ether_sprintf(eaddr)); |
751 | |
752 | /* Initialize interface info.*/ |
753 | |
754 | ifp = &sc->sc_if; |
755 | ifp->if_softc = sc; |
756 | strlcpy(ifp->if_xname, devname, IFNAMSIZ); |
757 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
758 | ifp->if_ioctl = axen_ioctl; |
759 | ifp->if_start = axen_start; |
760 | ifp->if_init = axen_init; |
761 | ifp->if_stop = axen_stop; |
762 | ifp->if_watchdog = axen_watchdog; |
763 | |
764 | IFQ_SET_READY(&ifp->if_snd); |
765 | |
766 | sc->axen_ec.ec_capabilities = ETHERCAP_VLAN_MTU; |
767 | #ifdef AXEN_TOE |
768 | ifp->if_capabilities |= IFCAP_CSUM_IPv4_Rx | IFCAP_CSUM_IPv4_Tx | |
769 | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_TCPv4_Tx | |
770 | IFCAP_CSUM_UDPv4_Rx | IFCAP_CSUM_UDPv4_Tx | |
771 | IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_TCPv6_Tx | |
772 | IFCAP_CSUM_UDPv6_Rx | IFCAP_CSUM_UDPv6_Tx; |
773 | #endif |
774 | |
775 | /* Initialize MII/media info. */ |
776 | mii = &sc->axen_mii; |
777 | mii->mii_ifp = ifp; |
778 | mii->mii_readreg = axen_miibus_readreg; |
779 | mii->mii_writereg = axen_miibus_writereg; |
780 | mii->mii_statchg = axen_miibus_statchg; |
781 | mii->mii_flags = MIIF_AUTOTSLEEP; |
782 | |
783 | sc->axen_ec.ec_mii = mii; |
784 | ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts); |
785 | mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); |
786 | |
787 | if (LIST_FIRST(&mii->mii_phys) == NULL) { |
788 | ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); |
789 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); |
790 | } else |
791 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); |
792 | |
793 | /* Attach the interface. */ |
794 | if_attach(ifp); |
795 | ether_ifattach(ifp, eaddr); |
796 | rnd_attach_source(&sc->rnd_source, device_xname(sc->axen_dev), |
797 | RND_TYPE_NET, RND_FLAG_DEFAULT); |
798 | |
799 | callout_init(&sc->axen_stat_ch, 0); |
800 | callout_setfunc(&sc->axen_stat_ch, axen_tick, sc); |
801 | |
802 | sc->axen_attached = true; |
803 | splx(s); |
804 | |
805 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->axen_udev,sc->axen_dev); |
806 | |
807 | if (!pmf_device_register(self, NULL, NULL)) |
808 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
809 | } |
810 | |
811 | static int |
812 | axen_detach(device_t self, int flags) |
813 | { |
814 | struct axen_softc *sc = device_private(self); |
815 | struct ifnet *ifp = GET_IFP(sc); |
816 | int s; |
817 | |
818 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->axen_dev), __func__)); |
819 | |
820 | /* Detached before attached finished, so just bail out. */ |
821 | if (!sc->axen_attached) |
822 | return 0; |
823 | |
824 | pmf_device_deregister(self); |
825 | |
826 | sc->axen_dying = true; |
827 | |
828 | /* |
829 | * Remove any pending tasks. They cannot be executing because they run |
830 | * in the same thread as detach. |
831 | */ |
832 | usb_rem_task(sc->axen_udev, &sc->axen_tick_task); |
833 | |
834 | s = splusb(); |
835 | |
836 | if (ifp->if_flags & IFF_RUNNING) |
837 | axen_stop(ifp, 1); |
838 | |
839 | callout_destroy(&sc->axen_stat_ch); |
840 | rnd_detach_source(&sc->rnd_source); |
841 | mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); |
842 | ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); |
843 | ether_ifdetach(ifp); |
844 | if_detach(ifp); |
845 | |
846 | #ifdef DIAGNOSTIC |
847 | if (sc->axen_ep[AXEN_ENDPT_TX] != NULL || |
848 | sc->axen_ep[AXEN_ENDPT_RX] != NULL || |
849 | sc->axen_ep[AXEN_ENDPT_INTR] != NULL) |
850 | aprint_debug_dev(self, "detach has active endpoints\n" ); |
851 | #endif |
852 | |
853 | sc->axen_attached = false; |
854 | |
855 | if (--sc->axen_refcnt >= 0) { |
856 | /* Wait for processes to go away. */ |
857 | usb_detach_waitold(sc->axen_dev); |
858 | } |
859 | splx(s); |
860 | |
861 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axen_udev,sc->axen_dev); |
862 | |
863 | rw_destroy(&sc->axen_mii_lock); |
864 | |
865 | return 0; |
866 | } |
867 | |
868 | static int |
869 | axen_activate(device_t self, devact_t act) |
870 | { |
871 | struct axen_softc *sc = device_private(self); |
872 | struct ifnet *ifp = GET_IFP(sc); |
873 | |
874 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->axen_dev), __func__)); |
875 | |
876 | switch (act) { |
877 | case DVACT_DEACTIVATE: |
878 | if_deactivate(ifp); |
879 | sc->axen_dying = true; |
880 | return 0; |
881 | default: |
882 | return EOPNOTSUPP; |
883 | } |
884 | } |
885 | |
886 | static struct mbuf * |
887 | axen_newbuf(void) |
888 | { |
889 | struct mbuf *m; |
890 | |
891 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
892 | if (m == NULL) |
893 | return NULL; |
894 | |
895 | MCLGET(m, M_DONTWAIT); |
896 | if (!(m->m_flags & M_EXT)) { |
897 | m_freem(m); |
898 | return NULL; |
899 | } |
900 | |
901 | m->m_len = m->m_pkthdr.len = MCLBYTES; |
902 | m_adj(m, ETHER_ALIGN); |
903 | |
904 | return m; |
905 | } |
906 | |
907 | static int |
908 | axen_rx_list_init(struct axen_softc *sc) |
909 | { |
910 | struct axen_cdata *cd; |
911 | struct axen_chain *c; |
912 | int i; |
913 | |
914 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->axen_dev), __func__)); |
915 | |
916 | cd = &sc->axen_cdata; |
917 | for (i = 0; i < AXEN_RX_LIST_CNT; i++) { |
918 | c = &cd->axen_rx_chain[i]; |
919 | c->axen_sc = sc; |
920 | c->axen_idx = i; |
921 | if (c->axen_xfer == NULL) { |
922 | int err = usbd_create_xfer(sc->axen_ep[AXEN_ENDPT_RX], |
923 | sc->axen_bufsz, USBD_SHORT_XFER_OK, 0, |
924 | &c->axen_xfer); |
925 | if (err) |
926 | return err; |
927 | c->axen_buf = usbd_get_buffer(c->axen_xfer); |
928 | } |
929 | } |
930 | |
931 | return 0; |
932 | } |
933 | |
934 | static int |
935 | axen_tx_list_init(struct axen_softc *sc) |
936 | { |
937 | struct axen_cdata *cd; |
938 | struct axen_chain *c; |
939 | int i; |
940 | |
941 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->axen_dev), __func__)); |
942 | |
943 | cd = &sc->axen_cdata; |
944 | for (i = 0; i < AXEN_TX_LIST_CNT; i++) { |
945 | c = &cd->axen_tx_chain[i]; |
946 | c->axen_sc = sc; |
947 | c->axen_idx = i; |
948 | if (c->axen_xfer == NULL) { |
949 | int err = usbd_create_xfer(sc->axen_ep[AXEN_ENDPT_TX], |
950 | sc->axen_bufsz, USBD_FORCE_SHORT_XFER, 0, |
951 | &c->axen_xfer); |
952 | if (err) |
953 | return err; |
954 | c->axen_buf = usbd_get_buffer(c->axen_xfer); |
955 | } |
956 | } |
957 | |
958 | return 0; |
959 | } |
960 | |
961 | /* |
962 | * A frame has been uploaded: pass the resulting mbuf chain up to |
963 | * the higher level protocols. |
964 | */ |
965 | static void |
966 | axen_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) |
967 | { |
968 | struct axen_chain *c = (struct axen_chain *)priv; |
969 | struct axen_softc *sc = c->axen_sc; |
970 | struct ifnet *ifp = GET_IFP(sc); |
971 | uint8_t *buf = c->axen_buf; |
972 | struct mbuf *m; |
973 | uint32_t total_len; |
974 | uint32_t rx_hdr, pkt_hdr; |
975 | uint32_t *hdr_p; |
976 | uint16_t hdr_offset, pkt_count; |
977 | size_t pkt_len; |
978 | size_t temp; |
979 | int s; |
980 | |
981 | DPRINTFN(10,("%s: %s: enter\n" , device_xname(sc->axen_dev), __func__)); |
982 | |
983 | if (sc->axen_dying) |
984 | return; |
985 | |
986 | if (!(ifp->if_flags & IFF_RUNNING)) |
987 | return; |
988 | |
989 | if (status != USBD_NORMAL_COMPLETION) { |
990 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
991 | return; |
992 | if (usbd_ratecheck(&sc->axen_rx_notice)) { |
993 | aprint_error_dev(sc->axen_dev, "usb errors on rx: %s\n" , |
994 | usbd_errstr(status)); |
995 | } |
996 | if (status == USBD_STALLED) |
997 | usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_RX]); |
998 | goto done; |
999 | } |
1000 | |
1001 | usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); |
1002 | |
1003 | if (total_len < sizeof(pkt_hdr)) { |
1004 | ifp->if_ierrors++; |
1005 | goto done; |
1006 | } |
1007 | |
1008 | /* |
1009 | * buffer map |
1010 | * [packet #0]...[packet #n][pkt hdr#0]..[pkt hdr#n][recv_hdr] |
1011 | * each packet has 0xeeee as psuedo header.. |
1012 | */ |
1013 | hdr_p = (uint32_t *)(buf + total_len - sizeof(uint32_t)); |
1014 | rx_hdr = le32toh(*hdr_p); |
1015 | hdr_offset = (uint16_t)(rx_hdr >> 16); |
1016 | pkt_count = (uint16_t)(rx_hdr & 0xffff); |
1017 | |
1018 | if (total_len > sc->axen_bufsz) { |
1019 | aprint_error_dev(sc->axen_dev, "rxeof: too large transfer\n" ); |
1020 | goto done; |
1021 | } |
1022 | |
1023 | /* sanity check */ |
1024 | if (hdr_offset > total_len) { |
1025 | ifp->if_ierrors++; |
1026 | usbd_delay_ms(sc->axen_udev, 100); |
1027 | goto done; |
1028 | } |
1029 | |
1030 | /* point first packet header */ |
1031 | hdr_p = (uint32_t *)(buf + hdr_offset); |
1032 | |
1033 | /* |
1034 | * ax88179 will pack multiple ip packet to a USB transaction. |
1035 | * process all of packets in the buffer |
1036 | */ |
1037 | |
1038 | #if 1 /* XXX: paranoiac check. need to remove later */ |
1039 | #define AXEN_MAX_PACKED_PACKET 200 |
1040 | if (pkt_count > AXEN_MAX_PACKED_PACKET) { |
1041 | DPRINTF(("%s: Too many packets (%d) in a transaction, discard.\n" , |
1042 | device_xname(sc->axen_dev), pkt_count)); |
1043 | goto done; |
1044 | } |
1045 | #endif |
1046 | |
1047 | do { |
1048 | if ((buf[0] != 0xee) || (buf[1] != 0xee)){ |
1049 | aprint_error_dev(sc->axen_dev, |
1050 | "invalid buffer(pkt#%d), continue\n" , pkt_count); |
1051 | ifp->if_ierrors += pkt_count; |
1052 | goto done; |
1053 | } |
1054 | |
1055 | pkt_hdr = le32toh(*hdr_p); |
1056 | pkt_len = (pkt_hdr >> 16) & 0x1fff; |
1057 | DPRINTFN(10, |
1058 | ("%s: rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n" , |
1059 | device_xname(sc->axen_dev), pkt_count, pkt_hdr, pkt_len)); |
1060 | |
1061 | if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) || |
1062 | (pkt_hdr & AXEN_RXHDR_DROP_ERR)) { |
1063 | ifp->if_ierrors++; |
1064 | /* move to next pkt header */ |
1065 | DPRINTF(("%s: crc err (pkt#%d)\n" , |
1066 | device_xname(sc->axen_dev), pkt_count)); |
1067 | goto nextpkt; |
1068 | } |
1069 | |
1070 | /* process each packet */ |
1071 | /* allocate mbuf */ |
1072 | m = axen_newbuf(); |
1073 | if (m == NULL) { |
1074 | ifp->if_ierrors++; |
1075 | goto nextpkt; |
1076 | } |
1077 | |
1078 | /* skip pseudo header (2byte) */ |
1079 | ifp->if_ipackets++; |
1080 | m_set_rcvif(m, ifp); |
1081 | m->m_pkthdr.len = m->m_len = pkt_len - 6; |
1082 | |
1083 | #ifdef AXEN_TOE |
1084 | /* cheksum err */ |
1085 | if ((pkt_hdr & AXEN_RXHDR_L3CSUM_ERR) || |
1086 | (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) { |
1087 | aprint_error_dev(sc->axen_dev, |
1088 | "checksum err (pkt#%d)\n" , pkt_count); |
1089 | goto nextpkt; |
1090 | } else { |
1091 | m->m_pkthdr.csum_flags |= M_CSUM_IPv4; |
1092 | } |
1093 | |
1094 | int l4_type = (pkt_hdr & AXEN_RXHDR_L4_TYPE_MASK) >> |
1095 | AXEN_RXHDR_L4_TYPE_OFFSET; |
1096 | |
1097 | if ((l4_type == AXEN_RXHDR_L4_TYPE_TCP) || |
1098 | (l4_type == AXEN_RXHDR_L4_TYPE_UDP)) { |
1099 | m->m_pkthdr.csum_flags |= M_CSUM_TCPv4 | |
1100 | M_CSUM_UDPv4; /* XXX v6? */ |
1101 | } |
1102 | #endif |
1103 | |
1104 | memcpy(mtod(m, char *), buf + 2, pkt_len - 6); |
1105 | |
1106 | /* push the packet up */ |
1107 | s = splnet(); |
1108 | bpf_mtap(ifp, m); |
1109 | if_percpuq_enqueue((ifp)->if_percpuq, (m)); |
1110 | splx(s); |
1111 | |
1112 | nextpkt: |
1113 | /* |
1114 | * prepare next packet |
1115 | * as each packet will be aligned 8byte boundary, |
1116 | * need to fix up the start point of the buffer. |
1117 | */ |
1118 | temp = ((pkt_len + 7) & 0xfff8); |
1119 | buf = buf + temp; |
1120 | hdr_p++; |
1121 | pkt_count--; |
1122 | } while( pkt_count > 0); |
1123 | |
1124 | done: |
1125 | /* clear buffer for next transaction */ |
1126 | memset(c->axen_buf, 0, sc->axen_bufsz); |
1127 | |
1128 | /* Setup new transfer. */ |
1129 | usbd_setup_xfer(xfer, c, c->axen_buf, sc->axen_bufsz, |
1130 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axen_rxeof); |
1131 | usbd_transfer(xfer); |
1132 | |
1133 | DPRINTFN(10,("%s: %s: start rx\n" ,device_xname(sc->axen_dev),__func__)); |
1134 | } |
1135 | |
1136 | /* |
1137 | * A frame was downloaded to the chip. It's safe for us to clean up |
1138 | * the list buffers. |
1139 | */ |
1140 | static void |
1141 | axen_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) |
1142 | { |
1143 | struct axen_chain *c = (struct axen_chain *)priv; |
1144 | struct axen_softc *sc = c->axen_sc; |
1145 | struct ifnet *ifp = GET_IFP(sc); |
1146 | int s; |
1147 | |
1148 | if (sc->axen_dying) |
1149 | return; |
1150 | |
1151 | s = splnet(); |
1152 | |
1153 | if (status != USBD_NORMAL_COMPLETION) { |
1154 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { |
1155 | splx(s); |
1156 | return; |
1157 | } |
1158 | ifp->if_oerrors++; |
1159 | aprint_error_dev(sc->axen_dev, "usb error on tx: %s\n" , |
1160 | usbd_errstr(status)); |
1161 | if (status == USBD_STALLED) |
1162 | usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_TX]); |
1163 | splx(s); |
1164 | return; |
1165 | } |
1166 | |
1167 | ifp->if_timer = 0; |
1168 | ifp->if_flags &= ~IFF_OACTIVE; |
1169 | |
1170 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1171 | axen_start(ifp); |
1172 | |
1173 | ifp->if_opackets++; |
1174 | splx(s); |
1175 | } |
1176 | |
1177 | static void |
1178 | axen_tick(void *xsc) |
1179 | { |
1180 | struct axen_softc *sc = xsc; |
1181 | |
1182 | if (sc == NULL) |
1183 | return; |
1184 | |
1185 | DPRINTFN(0xff,("%s: %s: enter\n" , device_xname(sc->axen_dev),__func__)); |
1186 | |
1187 | if (sc->axen_dying) |
1188 | return; |
1189 | |
1190 | /* Perform periodic stuff in process context */ |
1191 | usb_add_task(sc->axen_udev, &sc->axen_tick_task, USB_TASKQ_DRIVER); |
1192 | } |
1193 | |
1194 | static void |
1195 | axen_tick_task(void *xsc) |
1196 | { |
1197 | int s; |
1198 | struct axen_softc *sc; |
1199 | struct ifnet *ifp; |
1200 | struct mii_data *mii; |
1201 | |
1202 | sc = xsc; |
1203 | |
1204 | if (sc == NULL) |
1205 | return; |
1206 | |
1207 | if (sc->axen_dying) |
1208 | return; |
1209 | |
1210 | ifp = GET_IFP(sc); |
1211 | mii = GET_MII(sc); |
1212 | if (mii == NULL) |
1213 | return; |
1214 | |
1215 | s = splnet(); |
1216 | |
1217 | mii_tick(mii); |
1218 | if (sc->axen_link == 0 && |
1219 | (mii->mii_media_status & IFM_ACTIVE) != 0 && |
1220 | IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { |
1221 | DPRINTF(("%s: %s: got link\n" , device_xname(sc->axen_dev), |
1222 | __func__)); |
1223 | sc->axen_link++; |
1224 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1225 | axen_start(ifp); |
1226 | } |
1227 | |
1228 | callout_schedule(&sc->axen_stat_ch, hz); |
1229 | |
1230 | splx(s); |
1231 | } |
1232 | |
1233 | static int |
1234 | axen_encap(struct axen_softc *sc, struct mbuf *m, int idx) |
1235 | { |
1236 | struct ifnet *ifp = GET_IFP(sc); |
1237 | struct axen_chain *c; |
1238 | usbd_status err; |
1239 | struct axen_sframe_hdr hdr; |
1240 | int length, boundary; |
1241 | |
1242 | c = &sc->axen_cdata.axen_tx_chain[idx]; |
1243 | |
1244 | boundary = (sc->axen_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64; |
1245 | |
1246 | hdr.plen = htole32(m->m_pkthdr.len); |
1247 | hdr.gso = 0; /* disable segmentation offloading */ |
1248 | |
1249 | memcpy(c->axen_buf, &hdr, sizeof(hdr)); |
1250 | length = sizeof(hdr); |
1251 | |
1252 | m_copydata(m, 0, m->m_pkthdr.len, c->axen_buf + length); |
1253 | length += m->m_pkthdr.len; |
1254 | |
1255 | if ((length % boundary) == 0) { |
1256 | hdr.plen = 0x0; |
1257 | hdr.gso |= 0x80008000; /* enable padding */ |
1258 | memcpy(c->axen_buf + length, &hdr, sizeof(hdr)); |
1259 | length += sizeof(hdr); |
1260 | } |
1261 | |
1262 | usbd_setup_xfer(c->axen_xfer, c, c->axen_buf, length, |
1263 | USBD_FORCE_SHORT_XFER, 10000, axen_txeof); |
1264 | |
1265 | /* Transmit */ |
1266 | err = usbd_transfer(c->axen_xfer); |
1267 | if (err != USBD_IN_PROGRESS) { |
1268 | axen_stop(ifp, 0); |
1269 | return EIO; |
1270 | } |
1271 | |
1272 | sc->axen_cdata.axen_tx_cnt++; |
1273 | |
1274 | return 0; |
1275 | } |
1276 | |
1277 | static void |
1278 | axen_start(struct ifnet *ifp) |
1279 | { |
1280 | struct axen_softc *sc; |
1281 | struct mbuf *m; |
1282 | |
1283 | sc = ifp->if_softc; |
1284 | |
1285 | if (sc->axen_link == 0) |
1286 | return; |
1287 | |
1288 | if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) |
1289 | return; |
1290 | |
1291 | IFQ_POLL(&ifp->if_snd, m); |
1292 | if (m == NULL) |
1293 | return; |
1294 | |
1295 | if (axen_encap(sc, m, 0)) { |
1296 | ifp->if_flags |= IFF_OACTIVE; |
1297 | return; |
1298 | } |
1299 | IFQ_DEQUEUE(&ifp->if_snd, m); |
1300 | |
1301 | /* |
1302 | * If there's a BPF listener, bounce a copy of this frame |
1303 | * to him. |
1304 | */ |
1305 | bpf_mtap(ifp, m); |
1306 | m_freem(m); |
1307 | |
1308 | ifp->if_flags |= IFF_OACTIVE; |
1309 | |
1310 | /* |
1311 | * Set a timeout in case the chip goes out to lunch. |
1312 | */ |
1313 | ifp->if_timer = 5; |
1314 | } |
1315 | |
1316 | static int |
1317 | axen_init(struct ifnet *ifp) |
1318 | { |
1319 | struct axen_softc *sc = ifp->if_softc; |
1320 | struct axen_chain *c; |
1321 | usbd_status err; |
1322 | int i, s; |
1323 | uint16_t rxmode; |
1324 | uint16_t wval; |
1325 | uint8_t bval; |
1326 | |
1327 | s = splnet(); |
1328 | |
1329 | if (ifp->if_flags & IFF_RUNNING) |
1330 | axen_stop(ifp, 0); |
1331 | |
1332 | /* |
1333 | * Cancel pending I/O and free all RX/TX buffers. |
1334 | */ |
1335 | axen_reset(sc); |
1336 | |
1337 | /* XXX: ? */ |
1338 | axen_lock_mii(sc); |
1339 | bval = 0x01; |
1340 | axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_UNK_28, &bval); |
1341 | axen_unlock_mii(sc); |
1342 | |
1343 | /* Program promiscuous mode and multicast filters. */ |
1344 | axen_iff(sc); |
1345 | |
1346 | /* Enable receiver, set RX mode */ |
1347 | axen_lock_mii(sc); |
1348 | axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); |
1349 | rxmode = le16toh(wval); |
1350 | rxmode |= AXEN_RXCTL_START; |
1351 | wval = htole16(rxmode); |
1352 | axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); |
1353 | axen_unlock_mii(sc); |
1354 | |
1355 | /* Open RX and TX pipes. */ |
1356 | err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_RX], |
1357 | USBD_EXCLUSIVE_USE, &sc->axen_ep[AXEN_ENDPT_RX]); |
1358 | if (err) { |
1359 | aprint_error_dev(sc->axen_dev, "open rx pipe failed: %s\n" , |
1360 | usbd_errstr(err)); |
1361 | splx(s); |
1362 | return EIO; |
1363 | } |
1364 | |
1365 | err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_TX], |
1366 | USBD_EXCLUSIVE_USE, &sc->axen_ep[AXEN_ENDPT_TX]); |
1367 | if (err) { |
1368 | aprint_error_dev(sc->axen_dev, "open tx pipe failed: %s\n" , |
1369 | usbd_errstr(err)); |
1370 | splx(s); |
1371 | return EIO; |
1372 | } |
1373 | |
1374 | /* Init RX ring. */ |
1375 | if (axen_rx_list_init(sc)) { |
1376 | aprint_error_dev(sc->axen_dev, "rx list init failed\n" ); |
1377 | splx(s); |
1378 | return ENOBUFS; |
1379 | } |
1380 | |
1381 | /* Init TX ring. */ |
1382 | if (axen_tx_list_init(sc)) { |
1383 | aprint_error_dev(sc->axen_dev, "tx list init failed\n" ); |
1384 | splx(s); |
1385 | return ENOBUFS; |
1386 | } |
1387 | |
1388 | /* Start up the receive pipe. */ |
1389 | for (i = 0; i < AXEN_RX_LIST_CNT; i++) { |
1390 | c = &sc->axen_cdata.axen_rx_chain[i]; |
1391 | |
1392 | usbd_setup_xfer(c->axen_xfer, c, c->axen_buf, sc->axen_bufsz, |
1393 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axen_rxeof); |
1394 | usbd_transfer(c->axen_xfer); |
1395 | } |
1396 | |
1397 | ifp->if_flags |= IFF_RUNNING; |
1398 | ifp->if_flags &= ~IFF_OACTIVE; |
1399 | |
1400 | splx(s); |
1401 | |
1402 | callout_schedule(&sc->axen_stat_ch, hz); |
1403 | return 0; |
1404 | } |
1405 | |
1406 | static int |
1407 | axen_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
1408 | { |
1409 | struct axen_softc *sc = ifp->if_softc; |
1410 | int s; |
1411 | int error = 0; |
1412 | |
1413 | s = splnet(); |
1414 | |
1415 | switch (cmd) { |
1416 | case SIOCSIFFLAGS: |
1417 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) |
1418 | break; |
1419 | |
1420 | switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { |
1421 | case IFF_RUNNING: |
1422 | axen_stop(ifp, 1); |
1423 | break; |
1424 | case IFF_UP: |
1425 | axen_init(ifp); |
1426 | break; |
1427 | case IFF_UP | IFF_RUNNING: |
1428 | if ((ifp->if_flags ^ sc->axen_if_flags) == IFF_PROMISC) |
1429 | axen_iff(sc); |
1430 | else |
1431 | axen_init(ifp); |
1432 | break; |
1433 | } |
1434 | sc->axen_if_flags = ifp->if_flags; |
1435 | break; |
1436 | |
1437 | default: |
1438 | if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) |
1439 | break; |
1440 | |
1441 | error = 0; |
1442 | |
1443 | if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI) |
1444 | axen_iff(sc); |
1445 | break; |
1446 | } |
1447 | splx(s); |
1448 | |
1449 | return error; |
1450 | } |
1451 | |
1452 | static void |
1453 | axen_watchdog(struct ifnet *ifp) |
1454 | { |
1455 | struct axen_softc *sc; |
1456 | struct axen_chain *c; |
1457 | usbd_status stat; |
1458 | int s; |
1459 | |
1460 | sc = ifp->if_softc; |
1461 | |
1462 | ifp->if_oerrors++; |
1463 | aprint_error_dev(sc->axen_dev, "watchdog timeout\n" ); |
1464 | |
1465 | s = splusb(); |
1466 | c = &sc->axen_cdata.axen_tx_chain[0]; |
1467 | usbd_get_xfer_status(c->axen_xfer, NULL, NULL, NULL, &stat); |
1468 | axen_txeof(c->axen_xfer, c, stat); |
1469 | |
1470 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1471 | axen_start(ifp); |
1472 | splx(s); |
1473 | } |
1474 | |
1475 | /* |
1476 | * Stop the adapter and free any mbufs allocated to the |
1477 | * RX and TX lists. |
1478 | */ |
1479 | static void |
1480 | axen_stop(struct ifnet *ifp, int disable) |
1481 | { |
1482 | struct axen_softc *sc = ifp->if_softc; |
1483 | usbd_status err; |
1484 | int i; |
1485 | |
1486 | axen_reset(sc); |
1487 | |
1488 | ifp->if_timer = 0; |
1489 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
1490 | |
1491 | callout_stop(&sc->axen_stat_ch); |
1492 | |
1493 | /* Stop transfers. */ |
1494 | if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { |
1495 | err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_RX]); |
1496 | if (err) { |
1497 | aprint_error_dev(sc->axen_dev, |
1498 | "abort rx pipe failed: %s\n" , usbd_errstr(err)); |
1499 | |
1500 | } |
1501 | } |
1502 | |
1503 | if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { |
1504 | err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_TX]); |
1505 | if (err) { |
1506 | aprint_error_dev(sc->axen_dev, |
1507 | "abort tx pipe failed: %s\n" , usbd_errstr(err)); |
1508 | } |
1509 | } |
1510 | |
1511 | if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { |
1512 | err = usbd_abort_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); |
1513 | if (err) { |
1514 | aprint_error_dev(sc->axen_dev, |
1515 | "abort intr pipe failed: %s\n" , usbd_errstr(err)); |
1516 | } |
1517 | } |
1518 | |
1519 | /* Free RX resources. */ |
1520 | for (i = 0; i < AXEN_RX_LIST_CNT; i++) { |
1521 | if (sc->axen_cdata.axen_rx_chain[i].axen_xfer != NULL) { |
1522 | usbd_destroy_xfer(sc->axen_cdata.axen_rx_chain[i].axen_xfer); |
1523 | sc->axen_cdata.axen_rx_chain[i].axen_xfer = NULL; |
1524 | } |
1525 | } |
1526 | |
1527 | /* Free TX resources. */ |
1528 | for (i = 0; i < AXEN_TX_LIST_CNT; i++) { |
1529 | if (sc->axen_cdata.axen_tx_chain[i].axen_xfer != NULL) { |
1530 | usbd_destroy_xfer(sc->axen_cdata.axen_tx_chain[i].axen_xfer); |
1531 | sc->axen_cdata.axen_tx_chain[i].axen_xfer = NULL; |
1532 | } |
1533 | } |
1534 | |
1535 | /* Close pipes. */ |
1536 | if (sc->axen_ep[AXEN_ENDPT_RX] != NULL) { |
1537 | err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_RX]); |
1538 | if (err) { |
1539 | aprint_error_dev(sc->axen_dev, |
1540 | "close rx pipe failed: %s\n" , usbd_errstr(err)); |
1541 | } |
1542 | sc->axen_ep[AXEN_ENDPT_RX] = NULL; |
1543 | } |
1544 | |
1545 | if (sc->axen_ep[AXEN_ENDPT_TX] != NULL) { |
1546 | err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_TX]); |
1547 | if (err) { |
1548 | aprint_error_dev(sc->axen_dev, |
1549 | "close tx pipe failed: %s\n" , usbd_errstr(err)); |
1550 | } |
1551 | sc->axen_ep[AXEN_ENDPT_TX] = NULL; |
1552 | } |
1553 | |
1554 | if (sc->axen_ep[AXEN_ENDPT_INTR] != NULL) { |
1555 | err = usbd_close_pipe(sc->axen_ep[AXEN_ENDPT_INTR]); |
1556 | if (err) { |
1557 | aprint_error_dev(sc->axen_dev, |
1558 | "close intr pipe failed: %s\n" , usbd_errstr(err)); |
1559 | } |
1560 | sc->axen_ep[AXEN_ENDPT_INTR] = NULL; |
1561 | } |
1562 | |
1563 | sc->axen_link = 0; |
1564 | } |
1565 | |
1566 | MODULE(MODULE_CLASS_DRIVER, if_axen, "bpf" ); |
1567 | |
1568 | #ifdef _MODULE |
1569 | #include "ioconf.c" |
1570 | #endif |
1571 | |
1572 | static int |
1573 | if_axen_modcmd(modcmd_t cmd, void *aux) |
1574 | { |
1575 | int error = 0; |
1576 | |
1577 | switch (cmd) { |
1578 | case MODULE_CMD_INIT: |
1579 | #ifdef _MODULE |
1580 | error = config_init_component(cfdriver_ioconf_axen, |
1581 | cfattach_ioconf_axen, cfdata_ioconf_axen); |
1582 | #endif |
1583 | return error; |
1584 | case MODULE_CMD_FINI: |
1585 | #ifdef _MODULE |
1586 | error = config_fini_component(cfdriver_ioconf_axen, |
1587 | cfattach_ioconf_axen, cfdata_ioconf_axen); |
1588 | #endif |
1589 | return error; |
1590 | default: |
1591 | return ENOTTY; |
1592 | } |
1593 | } |
1594 | |