1 | /* $NetBSD: if_axe.c,v 1.74 2016/08/27 08:57:21 skrll Exp $ */ |
2 | /* $OpenBSD: if_axe.c,v 1.96 2010/01/09 05:33:08 jsg Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@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 | * Copyright (c) 1997, 1998, 1999, 2000-2003 |
22 | * Bill Paul <wpaul@windriver.com>. All rights reserved. |
23 | * |
24 | * Redistribution and use in source and binary forms, with or without |
25 | * modification, are permitted provided that the following conditions |
26 | * are met: |
27 | * 1. Redistributions of source code must retain the above copyright |
28 | * notice, this list of conditions and the following disclaimer. |
29 | * 2. Redistributions in binary form must reproduce the above copyright |
30 | * notice, this list of conditions and the following disclaimer in the |
31 | * documentation and/or other materials provided with the distribution. |
32 | * 3. All advertising materials mentioning features or use of this software |
33 | * must display the following acknowledgement: |
34 | * This product includes software developed by Bill Paul. |
35 | * 4. Neither the name of the author nor the names of any co-contributors |
36 | * may be used to endorse or promote products derived from this software |
37 | * without specific prior written permission. |
38 | * |
39 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND |
40 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
42 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD |
43 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
44 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
45 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
46 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
48 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
49 | * THE POSSIBILITY OF SUCH DAMAGE. |
50 | */ |
51 | |
52 | /* |
53 | * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the |
54 | * LinkSys USB200M and various other adapters. |
55 | * |
56 | * Manuals available from: |
57 | * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF |
58 | * Note: you need the manual for the AX88170 chip (USB 1.x ethernet |
59 | * controller) to find the definitions for the RX control register. |
60 | * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF |
61 | * |
62 | * Written by Bill Paul <wpaul@windriver.com> |
63 | * Senior Engineer |
64 | * Wind River Systems |
65 | */ |
66 | |
67 | /* |
68 | * The AX88172 provides USB ethernet supports at 10 and 100Mbps. |
69 | * It uses an external PHY (reference designs use a RealTek chip), |
70 | * and has a 64-bit multicast hash filter. There is some information |
71 | * missing from the manual which one needs to know in order to make |
72 | * the chip function: |
73 | * |
74 | * - You must set bit 7 in the RX control register, otherwise the |
75 | * chip won't receive any packets. |
76 | * - You must initialize all 3 IPG registers, or you won't be able |
77 | * to send any packets. |
78 | * |
79 | * Note that this device appears to only support loading the station |
80 | * address via autoload from the EEPROM (i.e. there's no way to manaully |
81 | * set it). |
82 | * |
83 | * (Adam Weinberger wanted me to name this driver if_gir.c.) |
84 | */ |
85 | |
86 | /* |
87 | * Ported to OpenBSD 3/28/2004 by Greg Taleck <taleck@oz.net> |
88 | * with bits and pieces from the aue and url drivers. |
89 | */ |
90 | |
91 | #include <sys/cdefs.h> |
92 | __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.74 2016/08/27 08:57:21 skrll Exp $" ); |
93 | |
94 | #ifdef _KERNEL_OPT |
95 | #include "opt_inet.h" |
96 | #endif |
97 | |
98 | #include <sys/param.h> |
99 | #include <sys/bus.h> |
100 | #include <sys/device.h> |
101 | #include <sys/kernel.h> |
102 | #include <sys/mbuf.h> |
103 | #include <sys/module.h> |
104 | #include <sys/mutex.h> |
105 | #include <sys/socket.h> |
106 | #include <sys/sockio.h> |
107 | #include <sys/systm.h> |
108 | |
109 | #include <sys/rndsource.h> |
110 | |
111 | #include <net/if.h> |
112 | #include <net/if_dl.h> |
113 | #include <net/if_ether.h> |
114 | #include <net/if_media.h> |
115 | |
116 | #include <net/bpf.h> |
117 | |
118 | #include <dev/mii/mii.h> |
119 | #include <dev/mii/miivar.h> |
120 | |
121 | #include <dev/usb/usb.h> |
122 | #include <dev/usb/usbdi.h> |
123 | #include <dev/usb/usbdi_util.h> |
124 | #include <dev/usb/usbdivar.h> |
125 | #include <dev/usb/usbdevs.h> |
126 | |
127 | #include <dev/usb/if_axereg.h> |
128 | |
129 | #ifdef AXE_DEBUG |
130 | #define DPRINTF(x) do { if (axedebug) printf x; } while (0) |
131 | #define DPRINTFN(n,x) do { if (axedebug >= (n)) printf x; } while (0) |
132 | int axedebug = 0; |
133 | #else |
134 | #define DPRINTF(x) |
135 | #define DPRINTFN(n,x) |
136 | #endif |
137 | |
138 | /* |
139 | * Various supported device vendors/products. |
140 | */ |
141 | static const struct axe_type axe_devs[] = { |
142 | { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE2000}, 0 }, |
143 | { { USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2}, 0 }, |
144 | { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET }, AX772 }, |
145 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 }, |
146 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 }, |
147 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772A}, AX772 }, |
148 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B}, AX772 | AX772B }, |
149 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B_1}, AX772 | AX772B }, |
150 | { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 }, |
151 | { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 }, |
152 | { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 }, |
153 | { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0}, |
154 | { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772 }, |
155 | { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0}, |
156 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 }, |
157 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 }, |
158 | { { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DUBE100B1 }, AX772 }, |
159 | { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100C1 }, AX772 | AX772B }, |
160 | { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 }, |
161 | { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 }, |
162 | { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 }, |
163 | { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_ETHERNET }, AX772 | AX772B }, |
164 | { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 }, |
165 | { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 }, |
166 | { { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LAN_GTJU2}, AX178 }, |
167 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2GT}, AX178 }, |
168 | { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX}, 0 }, |
169 | { { USB_VENDOR_MSI, USB_PRODUCT_MSI_AX88772A}, AX772 }, |
170 | { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 }, |
171 | { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 }, |
172 | { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 }, |
173 | { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 }, |
174 | { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 }, |
175 | { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 } |
176 | }; |
177 | #define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p)) |
178 | |
179 | int axe_match(device_t, cfdata_t, void *); |
180 | void axe_attach(device_t, device_t, void *); |
181 | int axe_detach(device_t, int); |
182 | int axe_activate(device_t, devact_t); |
183 | |
184 | CFATTACH_DECL_NEW(axe, sizeof(struct axe_softc), |
185 | axe_match, axe_attach, axe_detach, axe_activate); |
186 | |
187 | static int axe_tx_list_init(struct axe_softc *); |
188 | static int axe_rx_list_init(struct axe_softc *); |
189 | static int axe_encap(struct axe_softc *, struct mbuf *, int); |
190 | static void axe_rxeof(struct usbd_xfer *, void *, usbd_status); |
191 | static void axe_txeof(struct usbd_xfer *, void *, usbd_status); |
192 | static void axe_tick(void *); |
193 | static void axe_tick_task(void *); |
194 | static void axe_start(struct ifnet *); |
195 | static int axe_ioctl(struct ifnet *, u_long, void *); |
196 | static int axe_init(struct ifnet *); |
197 | static void axe_stop(struct ifnet *, int); |
198 | static void axe_watchdog(struct ifnet *); |
199 | static int axe_miibus_readreg_locked(device_t, int, int); |
200 | static int axe_miibus_readreg(device_t, int, int); |
201 | static void axe_miibus_writereg_locked(device_t, int, int, int); |
202 | static void axe_miibus_writereg(device_t, int, int, int); |
203 | static void axe_miibus_statchg(struct ifnet *); |
204 | static int axe_cmd(struct axe_softc *, int, int, int, void *); |
205 | static void axe_reset(struct axe_softc *); |
206 | static int axe_ifmedia_upd(struct ifnet *); |
207 | static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); |
208 | |
209 | static void axe_setmulti(struct axe_softc *); |
210 | static void axe_lock_mii(struct axe_softc *); |
211 | static void axe_unlock_mii(struct axe_softc *); |
212 | |
213 | static void axe_ax88178_init(struct axe_softc *); |
214 | static void axe_ax88772_init(struct axe_softc *); |
215 | |
216 | /* Get exclusive access to the MII registers */ |
217 | static void |
218 | axe_lock_mii(struct axe_softc *sc) |
219 | { |
220 | |
221 | sc->axe_refcnt++; |
222 | mutex_enter(&sc->axe_mii_lock); |
223 | } |
224 | |
225 | static void |
226 | axe_unlock_mii(struct axe_softc *sc) |
227 | { |
228 | |
229 | mutex_exit(&sc->axe_mii_lock); |
230 | if (--sc->axe_refcnt < 0) |
231 | usb_detach_wakeupold((sc->axe_dev)); |
232 | } |
233 | |
234 | static int |
235 | axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) |
236 | { |
237 | usb_device_request_t req; |
238 | usbd_status err; |
239 | |
240 | KASSERT(mutex_owned(&sc->axe_mii_lock)); |
241 | |
242 | if (sc->axe_dying) |
243 | return 0; |
244 | |
245 | if (AXE_CMD_DIR(cmd)) |
246 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
247 | else |
248 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
249 | req.bRequest = AXE_CMD_CMD(cmd); |
250 | USETW(req.wValue, val); |
251 | USETW(req.wIndex, index); |
252 | USETW(req.wLength, AXE_CMD_LEN(cmd)); |
253 | |
254 | err = usbd_do_request(sc->axe_udev, &req, buf); |
255 | |
256 | if (err) { |
257 | DPRINTF(("axe_cmd err: cmd %d err %d\n" , cmd, err)); |
258 | return -1; |
259 | } |
260 | return 0; |
261 | } |
262 | |
263 | static int |
264 | axe_miibus_readreg_locked(device_t dev, int phy, int reg) |
265 | { |
266 | struct axe_softc *sc = device_private(dev); |
267 | usbd_status err; |
268 | uint16_t val; |
269 | |
270 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); |
271 | err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, (void *)&val); |
272 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); |
273 | if (err) { |
274 | aprint_error_dev(sc->axe_dev, "read PHY failed\n" ); |
275 | return -1; |
276 | } |
277 | |
278 | val = le16toh(val); |
279 | if (sc->axe_flags & AX772 && reg == MII_BMSR) { |
280 | /* |
281 | * BMSR of AX88772 indicates it supports extended |
282 | * capability but the extended status register is |
283 | * reserverd for embedded ethernet PHY. So clear the |
284 | * extended capability bit of BMSR. |
285 | */ |
286 | val &= ~BMSR_EXTCAP; |
287 | } |
288 | |
289 | DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n" , |
290 | phy, reg, val)); |
291 | |
292 | return val; |
293 | } |
294 | |
295 | static int |
296 | axe_miibus_readreg(device_t dev, int phy, int reg) |
297 | { |
298 | struct axe_softc *sc = device_private(dev); |
299 | int val; |
300 | |
301 | if (sc->axe_dying) |
302 | return 0; |
303 | |
304 | if (sc->axe_phyno != phy) |
305 | return 0; |
306 | |
307 | axe_lock_mii(sc); |
308 | val = axe_miibus_readreg_locked(dev, phy, reg); |
309 | axe_unlock_mii(sc); |
310 | |
311 | return val; |
312 | } |
313 | |
314 | static void |
315 | axe_miibus_writereg_locked(device_t dev, int phy, int reg, int aval) |
316 | { |
317 | struct axe_softc *sc = device_private(dev); |
318 | usbd_status err; |
319 | uint16_t val; |
320 | |
321 | val = htole16(aval); |
322 | |
323 | axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); |
324 | err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, (void *)&val); |
325 | axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); |
326 | |
327 | if (err) { |
328 | aprint_error_dev(sc->axe_dev, "write PHY failed\n" ); |
329 | return; |
330 | } |
331 | } |
332 | |
333 | static void |
334 | axe_miibus_writereg(device_t dev, int phy, int reg, int aval) |
335 | { |
336 | struct axe_softc *sc = device_private(dev); |
337 | |
338 | if (sc->axe_dying) |
339 | return; |
340 | |
341 | if (sc->axe_phyno != phy) |
342 | return; |
343 | |
344 | axe_lock_mii(sc); |
345 | axe_miibus_writereg_locked(dev, phy, reg, aval); |
346 | axe_unlock_mii(sc); |
347 | } |
348 | |
349 | static void |
350 | axe_miibus_statchg(struct ifnet *ifp) |
351 | { |
352 | struct axe_softc *sc = ifp->if_softc; |
353 | struct mii_data *mii = &sc->axe_mii; |
354 | int val, err; |
355 | |
356 | if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) |
357 | val = AXE_MEDIA_FULL_DUPLEX; |
358 | else |
359 | val = 0; |
360 | |
361 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { |
362 | val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC); |
363 | if (sc->axe_flags & AX178) |
364 | val |= AXE_178_MEDIA_ENCK; |
365 | switch (IFM_SUBTYPE(mii->mii_media_active)) { |
366 | case IFM_1000_T: |
367 | val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; |
368 | break; |
369 | case IFM_100_TX: |
370 | val |= AXE_178_MEDIA_100TX; |
371 | break; |
372 | case IFM_10_T: |
373 | /* doesn't need to be handled */ |
374 | break; |
375 | } |
376 | } |
377 | |
378 | DPRINTF(("axe_miibus_statchg: val=0x%x\n" , val)); |
379 | axe_lock_mii(sc); |
380 | err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); |
381 | axe_unlock_mii(sc); |
382 | if (err) { |
383 | aprint_error_dev(sc->axe_dev, "media change failed\n" ); |
384 | return; |
385 | } |
386 | } |
387 | |
388 | /* |
389 | * Set media options |
390 | */ |
391 | static int |
392 | axe_ifmedia_upd(struct ifnet *ifp) |
393 | { |
394 | struct axe_softc *sc = ifp->if_softc; |
395 | struct mii_data *mii = &sc->axe_mii; |
396 | int rc; |
397 | |
398 | sc->axe_link = 0; |
399 | |
400 | if (mii->mii_instance) { |
401 | struct mii_softc *miisc; |
402 | |
403 | LIST_FOREACH(miisc, &mii->mii_phys, mii_list) |
404 | mii_phy_reset(miisc); |
405 | } |
406 | |
407 | if ((rc = mii_mediachg(mii)) == ENXIO) |
408 | return 0; |
409 | return rc; |
410 | } |
411 | |
412 | /* |
413 | * Report current media status |
414 | */ |
415 | static void |
416 | axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) |
417 | { |
418 | struct axe_softc *sc = ifp->if_softc; |
419 | struct mii_data *mii = &sc->axe_mii; |
420 | |
421 | mii_pollstat(mii); |
422 | ifmr->ifm_active = mii->mii_media_active; |
423 | ifmr->ifm_status = mii->mii_media_status; |
424 | } |
425 | |
426 | static void |
427 | axe_setmulti(struct axe_softc *sc) |
428 | { |
429 | struct ifnet *ifp = &sc->sc_if; |
430 | struct ether_multi *enm; |
431 | struct ether_multistep step; |
432 | uint32_t h = 0; |
433 | uint16_t rxmode; |
434 | uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
435 | |
436 | if (sc->axe_dying) |
437 | return; |
438 | |
439 | axe_lock_mii(sc); |
440 | axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, (void *)&rxmode); |
441 | rxmode = le16toh(rxmode); |
442 | |
443 | rxmode &= ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC); |
444 | |
445 | /* If we want promiscuous mode, set the allframes bit */ |
446 | if (ifp->if_flags & IFF_PROMISC) { |
447 | rxmode |= AXE_RXCMD_PROMISC; |
448 | goto allmulti; |
449 | } |
450 | |
451 | /* Now program new ones */ |
452 | ETHER_FIRST_MULTI(step, &sc->axe_ec, enm); |
453 | while (enm != NULL) { |
454 | if (memcmp(enm->enm_addrlo, enm->enm_addrhi, |
455 | ETHER_ADDR_LEN) != 0) |
456 | goto allmulti; |
457 | |
458 | h = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; |
459 | hashtbl[h >> 3] |= 1U << (h & 7); |
460 | ETHER_NEXT_MULTI(step, enm); |
461 | } |
462 | ifp->if_flags &= ~IFF_ALLMULTI; |
463 | axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); |
464 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
465 | axe_unlock_mii(sc); |
466 | return; |
467 | |
468 | allmulti: |
469 | ifp->if_flags |= IFF_ALLMULTI; |
470 | rxmode |= AXE_RXCMD_ALLMULTI; |
471 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
472 | axe_unlock_mii(sc); |
473 | } |
474 | |
475 | static void |
476 | axe_reset(struct axe_softc *sc) |
477 | { |
478 | |
479 | if (sc->axe_dying) |
480 | return; |
481 | /* XXX What to reset? */ |
482 | |
483 | /* Wait a little while for the chip to get its brains in order. */ |
484 | DELAY(1000); |
485 | } |
486 | |
487 | static int |
488 | axe_get_phyno(struct axe_softc *sc, int sel) |
489 | { |
490 | int phyno; |
491 | |
492 | switch (AXE_PHY_TYPE(sc->axe_phyaddrs[sel])) { |
493 | case PHY_TYPE_100_HOME: |
494 | /* FALLTHROUGH */ |
495 | case PHY_TYPE_GIG: |
496 | phyno = AXE_PHY_NO(sc->axe_phyaddrs[sel]); |
497 | break; |
498 | case PHY_TYPE_SPECIAL: |
499 | /* FALLTHROUGH */ |
500 | case PHY_TYPE_RSVD: |
501 | /* FALLTHROUGH */ |
502 | case PHY_TYPE_NON_SUP: |
503 | /* FALLTHROUGH */ |
504 | default: |
505 | phyno = -1; |
506 | break; |
507 | } |
508 | |
509 | return phyno; |
510 | } |
511 | |
512 | #define AXE_GPIO_WRITE(x, y) do { \ |
513 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ |
514 | usbd_delay_ms(sc->axe_udev, hztoms(y)); \ |
515 | } while (0) |
516 | |
517 | static void |
518 | axe_ax88178_init(struct axe_softc *sc) |
519 | { |
520 | int gpio0, ledmode, phymode; |
521 | uint16_t eeprom, val; |
522 | |
523 | axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); |
524 | /* XXX magic */ |
525 | axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); |
526 | axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); |
527 | |
528 | eeprom = le16toh(eeprom); |
529 | |
530 | DPRINTF((" EEPROM is 0x%x\n" , eeprom)); |
531 | |
532 | /* if EEPROM is invalid we have to use to GPIO0 */ |
533 | if (eeprom == 0xffff) { |
534 | phymode = AXE_PHY_MODE_MARVELL; |
535 | gpio0 = 1; |
536 | ledmode = 0; |
537 | } else { |
538 | phymode = eeprom & 0x7f; |
539 | gpio0 = (eeprom & 0x80) ? 0 : 1; |
540 | ledmode = eeprom >> 8; |
541 | } |
542 | |
543 | DPRINTF(("use gpio0: %d, phymode %d\n" , gpio0, phymode)); |
544 | |
545 | /* Program GPIOs depending on PHY hardware. */ |
546 | switch (phymode) { |
547 | case AXE_PHY_MODE_MARVELL: |
548 | if (gpio0 == 1) { |
549 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, |
550 | hz / 32); |
551 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, |
552 | hz / 32); |
553 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); |
554 | AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, |
555 | hz / 32); |
556 | } else { |
557 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
558 | AXE_GPIO1_EN, hz / 3); |
559 | if (ledmode == 1) { |
560 | AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); |
561 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, |
562 | hz / 3); |
563 | } else { |
564 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
565 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
566 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
567 | AXE_GPIO2_EN, hz / 4); |
568 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | |
569 | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
570 | } |
571 | } |
572 | break; |
573 | case AXE_PHY_MODE_CICADA: |
574 | case AXE_PHY_MODE_CICADA_V2: |
575 | case AXE_PHY_MODE_CICADA_V2_ASIX: |
576 | if (gpio0 == 1) |
577 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | |
578 | AXE_GPIO0_EN, hz / 32); |
579 | else |
580 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
581 | AXE_GPIO1_EN, hz / 32); |
582 | break; |
583 | case AXE_PHY_MODE_AGERE: |
584 | AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | |
585 | AXE_GPIO1_EN, hz / 32); |
586 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | |
587 | AXE_GPIO2_EN, hz / 32); |
588 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); |
589 | AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | |
590 | AXE_GPIO2_EN, hz / 32); |
591 | break; |
592 | case AXE_PHY_MODE_REALTEK_8211CL: |
593 | case AXE_PHY_MODE_REALTEK_8211BN: |
594 | case AXE_PHY_MODE_REALTEK_8251CL: |
595 | val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : |
596 | AXE_GPIO1 | AXE_GPIO1_EN; |
597 | AXE_GPIO_WRITE(val, hz / 32); |
598 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
599 | AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); |
600 | AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); |
601 | if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { |
602 | axe_miibus_writereg_locked(sc->axe_dev, |
603 | sc->axe_phyno, 0x1F, 0x0005); |
604 | axe_miibus_writereg_locked(sc->axe_dev, |
605 | sc->axe_phyno, 0x0C, 0x0000); |
606 | val = axe_miibus_readreg_locked(sc->axe_dev, |
607 | sc->axe_phyno, 0x0001); |
608 | axe_miibus_writereg_locked(sc->axe_dev, |
609 | sc->axe_phyno, 0x01, val | 0x0080); |
610 | axe_miibus_writereg_locked(sc->axe_dev, |
611 | sc->axe_phyno, 0x1F, 0x0000); |
612 | } |
613 | break; |
614 | default: |
615 | /* Unknown PHY model or no need to program GPIOs. */ |
616 | break; |
617 | } |
618 | |
619 | /* soft reset */ |
620 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); |
621 | usbd_delay_ms(sc->axe_udev, 150); |
622 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
623 | AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); |
624 | usbd_delay_ms(sc->axe_udev, 150); |
625 | /* Enable MII/GMII/RGMII for external PHY */ |
626 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); |
627 | usbd_delay_ms(sc->axe_udev, 10); |
628 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
629 | } |
630 | |
631 | static void |
632 | axe_ax88772_init(struct axe_softc *sc) |
633 | { |
634 | |
635 | axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); |
636 | usbd_delay_ms(sc->axe_udev, 40); |
637 | |
638 | if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) { |
639 | /* ask for the embedded PHY */ |
640 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); |
641 | usbd_delay_ms(sc->axe_udev, 10); |
642 | |
643 | /* power down and reset state, pin reset state */ |
644 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); |
645 | usbd_delay_ms(sc->axe_udev, 60); |
646 | |
647 | /* power down/reset state, pin operating state */ |
648 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
649 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); |
650 | usbd_delay_ms(sc->axe_udev, 150); |
651 | |
652 | /* power up, reset */ |
653 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); |
654 | |
655 | /* power up, operating */ |
656 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
657 | AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); |
658 | } else { |
659 | /* ask for external PHY */ |
660 | axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); |
661 | usbd_delay_ms(sc->axe_udev, 10); |
662 | |
663 | /* power down internal PHY */ |
664 | axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, |
665 | AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); |
666 | } |
667 | |
668 | usbd_delay_ms(sc->axe_udev, 150); |
669 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); |
670 | } |
671 | |
672 | /* |
673 | * Probe for a AX88172 chip. |
674 | */ |
675 | int |
676 | axe_match(device_t parent, cfdata_t match, void *aux) |
677 | { |
678 | struct usb_attach_arg *uaa = aux; |
679 | |
680 | return axe_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
681 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
682 | } |
683 | |
684 | /* |
685 | * Attach the interface. Allocate softc structures, do ifmedia |
686 | * setup and ethernet/BPF attach. |
687 | */ |
688 | void |
689 | axe_attach(device_t parent, device_t self, void *aux) |
690 | { |
691 | struct axe_softc *sc = device_private(self); |
692 | struct usb_attach_arg *uaa = aux; |
693 | struct usbd_device *dev = uaa->uaa_device; |
694 | usbd_status err; |
695 | usb_interface_descriptor_t *id; |
696 | usb_endpoint_descriptor_t *ed; |
697 | struct mii_data *mii; |
698 | uint8_t eaddr[ETHER_ADDR_LEN]; |
699 | char *devinfop; |
700 | const char *devname = device_xname(self); |
701 | struct ifnet *ifp; |
702 | int i, s; |
703 | |
704 | aprint_naive("\n" ); |
705 | aprint_normal("\n" ); |
706 | |
707 | sc->axe_dev = self; |
708 | sc->axe_udev = dev; |
709 | |
710 | devinfop = usbd_devinfo_alloc(dev, 0); |
711 | aprint_normal_dev(self, "%s\n" , devinfop); |
712 | usbd_devinfo_free(devinfop); |
713 | |
714 | err = usbd_set_config_no(dev, AXE_CONFIG_NO, 1); |
715 | if (err) { |
716 | aprint_error_dev(self, "failed to set configuration" |
717 | ", err=%s\n" , usbd_errstr(err)); |
718 | return; |
719 | } |
720 | |
721 | sc->axe_flags = axe_lookup(uaa->uaa_vendor, uaa->uaa_product)->axe_flags; |
722 | |
723 | mutex_init(&sc->axe_mii_lock, MUTEX_DEFAULT, IPL_NONE); |
724 | usb_init_task(&sc->axe_tick_task, axe_tick_task, sc, 0); |
725 | |
726 | err = usbd_device2interface_handle(dev, AXE_IFACE_IDX, &sc->axe_iface); |
727 | if (err) { |
728 | aprint_error_dev(self, "getting interface handle failed\n" ); |
729 | return; |
730 | } |
731 | |
732 | sc->axe_product = uaa->uaa_product; |
733 | sc->axe_vendor = uaa->uaa_vendor; |
734 | |
735 | id = usbd_get_interface_descriptor(sc->axe_iface); |
736 | |
737 | /* decide on what our bufsize will be */ |
738 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) |
739 | sc->axe_bufsz = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ? |
740 | AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ; |
741 | else |
742 | sc->axe_bufsz = AXE_172_BUFSZ; |
743 | |
744 | /* Find endpoints. */ |
745 | for (i = 0; i < id->bNumEndpoints; i++) { |
746 | ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i); |
747 | if (ed == NULL) { |
748 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
749 | return; |
750 | } |
751 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
752 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
753 | sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress; |
754 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
755 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
756 | sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress; |
757 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
758 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { |
759 | sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress; |
760 | } |
761 | } |
762 | |
763 | s = splnet(); |
764 | |
765 | /* We need the PHYID for init dance in some cases */ |
766 | axe_lock_mii(sc); |
767 | axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, (void *)&sc->axe_phyaddrs); |
768 | |
769 | DPRINTF((" phyaddrs[0]: %x phyaddrs[1]: %x\n" , |
770 | sc->axe_phyaddrs[0], sc->axe_phyaddrs[1])); |
771 | sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); |
772 | if (sc->axe_phyno == -1) |
773 | sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); |
774 | if (sc->axe_phyno == -1) { |
775 | DPRINTF((" no valid PHY address found, assuming PHY address 0\n" )); |
776 | sc->axe_phyno = 0; |
777 | } |
778 | |
779 | if (sc->axe_flags & AX178) |
780 | axe_ax88178_init(sc); |
781 | else if (sc->axe_flags & AX772) |
782 | axe_ax88772_init(sc); |
783 | |
784 | /* |
785 | * Get station address. |
786 | */ |
787 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) |
788 | axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, &eaddr); |
789 | else |
790 | axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, &eaddr); |
791 | |
792 | /* |
793 | * Load IPG values |
794 | */ |
795 | axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs); |
796 | axe_unlock_mii(sc); |
797 | |
798 | /* |
799 | * An ASIX chip was detected. Inform the world. |
800 | */ |
801 | aprint_normal_dev(self, "Ethernet address %s\n" , ether_sprintf(eaddr)); |
802 | |
803 | /* Initialize interface info.*/ |
804 | ifp = &sc->sc_if; |
805 | ifp->if_softc = sc; |
806 | strncpy(ifp->if_xname, devname, IFNAMSIZ); |
807 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
808 | ifp->if_ioctl = axe_ioctl; |
809 | ifp->if_start = axe_start; |
810 | ifp->if_init = axe_init; |
811 | ifp->if_stop = axe_stop; |
812 | ifp->if_watchdog = axe_watchdog; |
813 | |
814 | IFQ_SET_READY(&ifp->if_snd); |
815 | |
816 | sc->axe_ec.ec_capabilities = ETHERCAP_VLAN_MTU; |
817 | |
818 | /* Initialize MII/media info. */ |
819 | mii = &sc->axe_mii; |
820 | mii->mii_ifp = ifp; |
821 | mii->mii_readreg = axe_miibus_readreg; |
822 | mii->mii_writereg = axe_miibus_writereg; |
823 | mii->mii_statchg = axe_miibus_statchg; |
824 | mii->mii_flags = MIIF_AUTOTSLEEP; |
825 | |
826 | sc->axe_ec.ec_mii = mii; |
827 | if (sc->axe_flags & AXE_MII) |
828 | ifmedia_init(&mii->mii_media, 0, axe_ifmedia_upd, |
829 | axe_ifmedia_sts); |
830 | else |
831 | ifmedia_init(&mii->mii_media, 0, ether_mediachange, |
832 | ether_mediastatus); |
833 | |
834 | mii_attach(sc->axe_dev, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, |
835 | 0); |
836 | |
837 | if (LIST_EMPTY(&mii->mii_phys)) { |
838 | ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); |
839 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); |
840 | } else |
841 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); |
842 | |
843 | /* Attach the interface. */ |
844 | if_attach(ifp); |
845 | ether_ifattach(ifp, eaddr); |
846 | rnd_attach_source(&sc->rnd_source, device_xname(sc->axe_dev), |
847 | RND_TYPE_NET, RND_FLAG_DEFAULT); |
848 | |
849 | callout_init(&sc->axe_stat_ch, 0); |
850 | callout_setfunc(&sc->axe_stat_ch, axe_tick, sc); |
851 | |
852 | sc->axe_attached = true; |
853 | splx(s); |
854 | |
855 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->axe_udev, sc->axe_dev); |
856 | |
857 | if (!pmf_device_register(self, NULL, NULL)) |
858 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
859 | } |
860 | |
861 | int |
862 | axe_detach(device_t self, int flags) |
863 | { |
864 | struct axe_softc *sc = device_private(self); |
865 | int s; |
866 | struct ifnet *ifp = &sc->sc_if; |
867 | |
868 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->axe_dev), __func__)); |
869 | |
870 | /* Detached before attached finished, so just bail out. */ |
871 | if (!sc->axe_attached) |
872 | return 0; |
873 | |
874 | pmf_device_deregister(self); |
875 | |
876 | sc->axe_dying = true; |
877 | |
878 | /* |
879 | * Remove any pending tasks. They cannot be executing because they run |
880 | * in the same thread as detach. |
881 | */ |
882 | usb_rem_task(sc->axe_udev, &sc->axe_tick_task); |
883 | |
884 | s = splusb(); |
885 | |
886 | if (ifp->if_flags & IFF_RUNNING) |
887 | axe_stop(ifp, 1); |
888 | |
889 | callout_destroy(&sc->axe_stat_ch); |
890 | mutex_destroy(&sc->axe_mii_lock); |
891 | rnd_detach_source(&sc->rnd_source); |
892 | mii_detach(&sc->axe_mii, MII_PHY_ANY, MII_OFFSET_ANY); |
893 | ifmedia_delete_instance(&sc->axe_mii.mii_media, IFM_INST_ANY); |
894 | ether_ifdetach(ifp); |
895 | if_detach(ifp); |
896 | |
897 | #ifdef DIAGNOSTIC |
898 | if (sc->axe_ep[AXE_ENDPT_TX] != NULL || |
899 | sc->axe_ep[AXE_ENDPT_RX] != NULL || |
900 | sc->axe_ep[AXE_ENDPT_INTR] != NULL) |
901 | aprint_debug_dev(self, "detach has active endpoints\n" ); |
902 | #endif |
903 | |
904 | sc->axe_attached = false; |
905 | |
906 | if (--sc->axe_refcnt >= 0) { |
907 | /* Wait for processes to go away. */ |
908 | usb_detach_waitold(sc->axe_dev); |
909 | } |
910 | splx(s); |
911 | |
912 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axe_udev, sc->axe_dev); |
913 | |
914 | return 0; |
915 | } |
916 | |
917 | int |
918 | axe_activate(device_t self, devact_t act) |
919 | { |
920 | struct axe_softc *sc = device_private(self); |
921 | |
922 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->axe_dev), __func__)); |
923 | |
924 | switch (act) { |
925 | case DVACT_DEACTIVATE: |
926 | if_deactivate(&sc->axe_ec.ec_if); |
927 | sc->axe_dying = true; |
928 | return 0; |
929 | default: |
930 | return EOPNOTSUPP; |
931 | } |
932 | } |
933 | |
934 | static int |
935 | axe_rx_list_init(struct axe_softc *sc) |
936 | { |
937 | struct axe_cdata *cd; |
938 | struct axe_chain *c; |
939 | int i; |
940 | |
941 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->axe_dev), __func__)); |
942 | |
943 | cd = &sc->axe_cdata; |
944 | for (i = 0; i < AXE_RX_LIST_CNT; i++) { |
945 | c = &cd->axe_rx_chain[i]; |
946 | c->axe_sc = sc; |
947 | c->axe_idx = i; |
948 | if (c->axe_xfer == NULL) { |
949 | int err = usbd_create_xfer(sc->axe_ep[AXE_ENDPT_RX], |
950 | sc->axe_bufsz, USBD_SHORT_XFER_OK, 0, &c->axe_xfer); |
951 | if (err) |
952 | return err; |
953 | c->axe_buf = usbd_get_buffer(c->axe_xfer); |
954 | } |
955 | } |
956 | |
957 | return 0; |
958 | } |
959 | |
960 | static int |
961 | axe_tx_list_init(struct axe_softc *sc) |
962 | { |
963 | struct axe_cdata *cd; |
964 | struct axe_chain *c; |
965 | int i; |
966 | |
967 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->axe_dev), __func__)); |
968 | |
969 | cd = &sc->axe_cdata; |
970 | for (i = 0; i < AXE_TX_LIST_CNT; i++) { |
971 | c = &cd->axe_tx_chain[i]; |
972 | c->axe_sc = sc; |
973 | c->axe_idx = i; |
974 | if (c->axe_xfer == NULL) { |
975 | int err = usbd_create_xfer(sc->axe_ep[AXE_ENDPT_TX], |
976 | sc->axe_bufsz, USBD_FORCE_SHORT_XFER, 0, |
977 | &c->axe_xfer); |
978 | if (err) |
979 | return err; |
980 | c->axe_buf = usbd_get_buffer(c->axe_xfer); |
981 | } |
982 | } |
983 | |
984 | return 0; |
985 | } |
986 | |
987 | /* |
988 | * A frame has been uploaded: pass the resulting mbuf chain up to |
989 | * the higher level protocols. |
990 | */ |
991 | static void |
992 | axe_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) |
993 | { |
994 | struct axe_softc *sc; |
995 | struct axe_chain *c; |
996 | struct ifnet *ifp; |
997 | uint8_t *buf; |
998 | uint32_t total_len; |
999 | u_int rxlen, pktlen; |
1000 | struct mbuf *m; |
1001 | struct axe_sframe_hdr hdr; |
1002 | int s; |
1003 | |
1004 | c = (struct axe_chain *)priv; |
1005 | sc = c->axe_sc; |
1006 | buf = c->axe_buf; |
1007 | ifp = &sc->sc_if; |
1008 | |
1009 | DPRINTFN(10,("%s: %s: enter\n" , device_xname(sc->axe_dev),__func__)); |
1010 | |
1011 | if (sc->axe_dying) |
1012 | return; |
1013 | |
1014 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
1015 | return; |
1016 | |
1017 | if (status != USBD_NORMAL_COMPLETION) { |
1018 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
1019 | return; |
1020 | if (usbd_ratecheck(&sc->axe_rx_notice)) |
1021 | aprint_error_dev(sc->axe_dev, "usb errors on rx: %s\n" , |
1022 | usbd_errstr(status)); |
1023 | if (status == USBD_STALLED) |
1024 | usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_RX]); |
1025 | goto done; |
1026 | } |
1027 | |
1028 | usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); |
1029 | |
1030 | do { |
1031 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { |
1032 | if (total_len < sizeof(hdr)) { |
1033 | ifp->if_ierrors++; |
1034 | goto done; |
1035 | } |
1036 | |
1037 | memcpy(&hdr, buf, sizeof(hdr)); |
1038 | total_len -= sizeof(hdr); |
1039 | buf += sizeof(hdr); |
1040 | |
1041 | if (((le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK) ^ |
1042 | (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK)) != |
1043 | AXE_RH1M_RXLEN_MASK) { |
1044 | ifp->if_ierrors++; |
1045 | goto done; |
1046 | } |
1047 | |
1048 | rxlen = le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK; |
1049 | if (total_len < rxlen) { |
1050 | pktlen = total_len; |
1051 | total_len = 0; |
1052 | } else { |
1053 | pktlen = rxlen; |
1054 | rxlen = roundup2(rxlen, 2); |
1055 | total_len -= rxlen; |
1056 | } |
1057 | |
1058 | } else { /* AX172 */ |
1059 | pktlen = rxlen = total_len; |
1060 | total_len = 0; |
1061 | } |
1062 | |
1063 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
1064 | if (m == NULL) { |
1065 | ifp->if_ierrors++; |
1066 | goto done; |
1067 | } |
1068 | |
1069 | if (pktlen > MHLEN - ETHER_ALIGN) { |
1070 | MCLGET(m, M_DONTWAIT); |
1071 | if ((m->m_flags & M_EXT) == 0) { |
1072 | m_freem(m); |
1073 | ifp->if_ierrors++; |
1074 | goto done; |
1075 | } |
1076 | } |
1077 | m->m_data += ETHER_ALIGN; |
1078 | |
1079 | ifp->if_ipackets++; |
1080 | m_set_rcvif(m, ifp); |
1081 | m->m_pkthdr.len = m->m_len = pktlen; |
1082 | |
1083 | memcpy(mtod(m, uint8_t *), buf, pktlen); |
1084 | buf += rxlen; |
1085 | |
1086 | s = splnet(); |
1087 | |
1088 | bpf_mtap(ifp, m); |
1089 | |
1090 | DPRINTFN(10,("%s: %s: deliver %d\n" , device_xname(sc->axe_dev), |
1091 | __func__, m->m_len)); |
1092 | if_percpuq_enqueue((ifp)->if_percpuq, (m)); |
1093 | |
1094 | splx(s); |
1095 | |
1096 | } while (total_len > 0); |
1097 | |
1098 | done: |
1099 | |
1100 | /* Setup new transfer. */ |
1101 | usbd_setup_xfer(xfer, c, c->axe_buf, sc->axe_bufsz, |
1102 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axe_rxeof); |
1103 | usbd_transfer(xfer); |
1104 | |
1105 | DPRINTFN(10,("%s: %s: start rx\n" , device_xname(sc->axe_dev), __func__)); |
1106 | } |
1107 | |
1108 | /* |
1109 | * A frame was downloaded to the chip. It's safe for us to clean up |
1110 | * the list buffers. |
1111 | */ |
1112 | |
1113 | static void |
1114 | axe_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) |
1115 | { |
1116 | struct axe_softc *sc; |
1117 | struct axe_chain *c; |
1118 | struct ifnet *ifp; |
1119 | int s; |
1120 | |
1121 | c = priv; |
1122 | sc = c->axe_sc; |
1123 | ifp = &sc->sc_if; |
1124 | |
1125 | if (sc->axe_dying) |
1126 | return; |
1127 | |
1128 | s = splnet(); |
1129 | |
1130 | ifp->if_timer = 0; |
1131 | ifp->if_flags &= ~IFF_OACTIVE; |
1132 | |
1133 | if (status != USBD_NORMAL_COMPLETION) { |
1134 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { |
1135 | splx(s); |
1136 | return; |
1137 | } |
1138 | ifp->if_oerrors++; |
1139 | aprint_error_dev(sc->axe_dev, "usb error on tx: %s\n" , |
1140 | usbd_errstr(status)); |
1141 | if (status == USBD_STALLED) |
1142 | usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_TX]); |
1143 | splx(s); |
1144 | return; |
1145 | } |
1146 | ifp->if_opackets++; |
1147 | |
1148 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1149 | axe_start(ifp); |
1150 | |
1151 | splx(s); |
1152 | } |
1153 | |
1154 | static void |
1155 | axe_tick(void *xsc) |
1156 | { |
1157 | struct axe_softc *sc = xsc; |
1158 | |
1159 | if (sc == NULL) |
1160 | return; |
1161 | |
1162 | DPRINTFN(0xff, ("%s: %s: enter\n" , device_xname(sc->axe_dev), __func__)); |
1163 | |
1164 | if (sc->axe_dying) |
1165 | return; |
1166 | |
1167 | /* Perform periodic stuff in process context */ |
1168 | usb_add_task(sc->axe_udev, &sc->axe_tick_task, USB_TASKQ_DRIVER); |
1169 | } |
1170 | |
1171 | static void |
1172 | axe_tick_task(void *xsc) |
1173 | { |
1174 | int s; |
1175 | struct axe_softc *sc; |
1176 | struct ifnet *ifp; |
1177 | struct mii_data *mii; |
1178 | |
1179 | sc = xsc; |
1180 | |
1181 | if (sc == NULL) |
1182 | return; |
1183 | |
1184 | if (sc->axe_dying) |
1185 | return; |
1186 | |
1187 | ifp = &sc->sc_if; |
1188 | mii = &sc->axe_mii; |
1189 | |
1190 | if (mii == NULL) |
1191 | return; |
1192 | |
1193 | s = splnet(); |
1194 | |
1195 | mii_tick(mii); |
1196 | if (sc->axe_link == 0 && |
1197 | (mii->mii_media_status & IFM_ACTIVE) != 0 && |
1198 | IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { |
1199 | DPRINTF(("%s: %s: got link\n" , device_xname(sc->axe_dev), |
1200 | __func__)); |
1201 | sc->axe_link++; |
1202 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1203 | axe_start(ifp); |
1204 | } |
1205 | |
1206 | callout_schedule(&sc->axe_stat_ch, hz); |
1207 | |
1208 | splx(s); |
1209 | } |
1210 | |
1211 | static int |
1212 | axe_encap(struct axe_softc *sc, struct mbuf *m, int idx) |
1213 | { |
1214 | struct ifnet *ifp = &sc->sc_if; |
1215 | struct axe_chain *c; |
1216 | usbd_status err; |
1217 | struct axe_sframe_hdr hdr; |
1218 | int length, boundary; |
1219 | |
1220 | c = &sc->axe_cdata.axe_tx_chain[idx]; |
1221 | |
1222 | /* |
1223 | * Copy the mbuf data into a contiguous buffer, leaving two |
1224 | * bytes at the beginning to hold the frame length. |
1225 | */ |
1226 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { |
1227 | boundary = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64; |
1228 | |
1229 | hdr.len = htole16(m->m_pkthdr.len); |
1230 | hdr.ilen = ~hdr.len; |
1231 | |
1232 | memcpy(c->axe_buf, &hdr, sizeof(hdr)); |
1233 | length = sizeof(hdr); |
1234 | |
1235 | m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf + length); |
1236 | length += m->m_pkthdr.len; |
1237 | |
1238 | if ((length % boundary) == 0) { |
1239 | hdr.len = 0x0000; |
1240 | hdr.ilen = 0xffff; |
1241 | memcpy(c->axe_buf + length, &hdr, sizeof(hdr)); |
1242 | length += sizeof(hdr); |
1243 | } |
1244 | } else { |
1245 | m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf); |
1246 | length = m->m_pkthdr.len; |
1247 | } |
1248 | |
1249 | usbd_setup_xfer(c->axe_xfer, c, c->axe_buf, length, |
1250 | USBD_FORCE_SHORT_XFER, 10000, axe_txeof); |
1251 | |
1252 | /* Transmit */ |
1253 | err = usbd_transfer(c->axe_xfer); |
1254 | if (err != USBD_IN_PROGRESS) { |
1255 | axe_stop(ifp, 0); |
1256 | return EIO; |
1257 | } |
1258 | |
1259 | sc->axe_cdata.axe_tx_cnt++; |
1260 | |
1261 | return 0; |
1262 | } |
1263 | |
1264 | static void |
1265 | axe_start(struct ifnet *ifp) |
1266 | { |
1267 | struct axe_softc *sc; |
1268 | struct mbuf *m; |
1269 | |
1270 | sc = ifp->if_softc; |
1271 | |
1272 | if ((sc->axe_flags & AXE_MII) != 0 && sc->axe_link == 0) |
1273 | return; |
1274 | |
1275 | if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) |
1276 | return; |
1277 | |
1278 | IFQ_POLL(&ifp->if_snd, m); |
1279 | if (m == NULL) { |
1280 | return; |
1281 | } |
1282 | |
1283 | if (axe_encap(sc, m, 0)) { |
1284 | ifp->if_flags |= IFF_OACTIVE; |
1285 | return; |
1286 | } |
1287 | IFQ_DEQUEUE(&ifp->if_snd, m); |
1288 | |
1289 | /* |
1290 | * If there's a BPF listener, bounce a copy of this frame |
1291 | * to him. |
1292 | */ |
1293 | bpf_mtap(ifp, m); |
1294 | m_freem(m); |
1295 | |
1296 | ifp->if_flags |= IFF_OACTIVE; |
1297 | |
1298 | /* |
1299 | * Set a timeout in case the chip goes out to lunch. |
1300 | */ |
1301 | ifp->if_timer = 5; |
1302 | |
1303 | return; |
1304 | } |
1305 | |
1306 | static int |
1307 | axe_init(struct ifnet *ifp) |
1308 | { |
1309 | struct axe_softc *sc = ifp->if_softc; |
1310 | struct axe_chain *c; |
1311 | usbd_status err; |
1312 | int rxmode; |
1313 | int i, s; |
1314 | uint8_t eaddr[ETHER_ADDR_LEN]; |
1315 | |
1316 | s = splnet(); |
1317 | |
1318 | if (ifp->if_flags & IFF_RUNNING) |
1319 | axe_stop(ifp, 0); |
1320 | |
1321 | /* |
1322 | * Cancel pending I/O and free all RX/TX buffers. |
1323 | */ |
1324 | axe_reset(sc); |
1325 | |
1326 | /* Set MAC address */ |
1327 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { |
1328 | memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr)); |
1329 | axe_lock_mii(sc); |
1330 | axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, eaddr); |
1331 | axe_unlock_mii(sc); |
1332 | } |
1333 | |
1334 | /* Set transmitter IPG values */ |
1335 | axe_lock_mii(sc); |
1336 | if (sc->axe_flags & AX178 || sc->axe_flags & AX772) |
1337 | axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2], |
1338 | (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL); |
1339 | else { |
1340 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL); |
1341 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL); |
1342 | axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL); |
1343 | } |
1344 | |
1345 | /* Enable receiver, set RX mode */ |
1346 | rxmode = AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE; |
1347 | if (sc->axe_flags & AX772B) |
1348 | rxmode |= AXE_772B_RXCMD_RH1M; |
1349 | else if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { |
1350 | if (sc->axe_udev->ud_speed == USB_SPEED_HIGH) { |
1351 | /* Largest possible USB buffer size for AX88178 */ |
1352 | rxmode |= AXE_178_RXCMD_MFB; |
1353 | } |
1354 | } else |
1355 | rxmode |= AXE_172_RXCMD_UNICAST; |
1356 | |
1357 | /* If we want promiscuous mode, set the allframes bit. */ |
1358 | if (ifp->if_flags & IFF_PROMISC) |
1359 | rxmode |= AXE_RXCMD_PROMISC; |
1360 | |
1361 | if (ifp->if_flags & IFF_BROADCAST) |
1362 | rxmode |= AXE_RXCMD_BROADCAST; |
1363 | |
1364 | axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); |
1365 | axe_unlock_mii(sc); |
1366 | |
1367 | /* Load the multicast filter. */ |
1368 | axe_setmulti(sc); |
1369 | |
1370 | /* Open RX and TX pipes. */ |
1371 | err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_RX], |
1372 | USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_RX]); |
1373 | if (err) { |
1374 | aprint_error_dev(sc->axe_dev, "open rx pipe failed: %s\n" , |
1375 | usbd_errstr(err)); |
1376 | splx(s); |
1377 | return EIO; |
1378 | } |
1379 | |
1380 | err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_TX], |
1381 | USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_TX]); |
1382 | if (err) { |
1383 | aprint_error_dev(sc->axe_dev, "open tx pipe failed: %s\n" , |
1384 | usbd_errstr(err)); |
1385 | splx(s); |
1386 | return EIO; |
1387 | } |
1388 | |
1389 | /* Init RX ring. */ |
1390 | if (axe_rx_list_init(sc) != 0) { |
1391 | aprint_error_dev(sc->axe_dev, "rx list init failed\n" ); |
1392 | splx(s); |
1393 | return ENOBUFS; |
1394 | } |
1395 | |
1396 | /* Init TX ring. */ |
1397 | if (axe_tx_list_init(sc) != 0) { |
1398 | aprint_error_dev(sc->axe_dev, "tx list init failed\n" ); |
1399 | splx(s); |
1400 | return ENOBUFS; |
1401 | } |
1402 | |
1403 | /* Start up the receive pipe. */ |
1404 | for (i = 0; i < AXE_RX_LIST_CNT; i++) { |
1405 | c = &sc->axe_cdata.axe_rx_chain[i]; |
1406 | usbd_setup_xfer(c->axe_xfer, c, c->axe_buf, sc->axe_bufsz, |
1407 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axe_rxeof); |
1408 | usbd_transfer(c->axe_xfer); |
1409 | } |
1410 | |
1411 | ifp->if_flags |= IFF_RUNNING; |
1412 | ifp->if_flags &= ~IFF_OACTIVE; |
1413 | |
1414 | splx(s); |
1415 | |
1416 | callout_schedule(&sc->axe_stat_ch, hz); |
1417 | return 0; |
1418 | } |
1419 | |
1420 | static int |
1421 | axe_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
1422 | { |
1423 | struct axe_softc *sc = ifp->if_softc; |
1424 | int s; |
1425 | int error = 0; |
1426 | |
1427 | s = splnet(); |
1428 | |
1429 | switch(cmd) { |
1430 | case SIOCSIFFLAGS: |
1431 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) |
1432 | break; |
1433 | |
1434 | switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { |
1435 | case IFF_RUNNING: |
1436 | axe_stop(ifp, 1); |
1437 | break; |
1438 | case IFF_UP: |
1439 | axe_init(ifp); |
1440 | break; |
1441 | case IFF_UP | IFF_RUNNING: |
1442 | if ((ifp->if_flags ^ sc->axe_if_flags) == IFF_PROMISC) |
1443 | axe_setmulti(sc); |
1444 | else |
1445 | axe_init(ifp); |
1446 | break; |
1447 | } |
1448 | sc->axe_if_flags = ifp->if_flags; |
1449 | break; |
1450 | |
1451 | default: |
1452 | if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) |
1453 | break; |
1454 | |
1455 | error = 0; |
1456 | |
1457 | if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI) |
1458 | axe_setmulti(sc); |
1459 | |
1460 | } |
1461 | splx(s); |
1462 | |
1463 | return error; |
1464 | } |
1465 | |
1466 | static void |
1467 | axe_watchdog(struct ifnet *ifp) |
1468 | { |
1469 | struct axe_softc *sc; |
1470 | struct axe_chain *c; |
1471 | usbd_status stat; |
1472 | int s; |
1473 | |
1474 | sc = ifp->if_softc; |
1475 | |
1476 | ifp->if_oerrors++; |
1477 | aprint_error_dev(sc->axe_dev, "watchdog timeout\n" ); |
1478 | |
1479 | s = splusb(); |
1480 | c = &sc->axe_cdata.axe_tx_chain[0]; |
1481 | usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &stat); |
1482 | axe_txeof(c->axe_xfer, c, stat); |
1483 | |
1484 | if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
1485 | axe_start(ifp); |
1486 | splx(s); |
1487 | } |
1488 | |
1489 | /* |
1490 | * Stop the adapter and free any mbufs allocated to the |
1491 | * RX and TX lists. |
1492 | */ |
1493 | static void |
1494 | axe_stop(struct ifnet *ifp, int disable) |
1495 | { |
1496 | struct axe_softc *sc = ifp->if_softc; |
1497 | usbd_status err; |
1498 | int i; |
1499 | |
1500 | axe_reset(sc); |
1501 | |
1502 | ifp->if_timer = 0; |
1503 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
1504 | |
1505 | callout_stop(&sc->axe_stat_ch); |
1506 | |
1507 | /* Stop transfers. */ |
1508 | if (sc->axe_ep[AXE_ENDPT_RX] != NULL) { |
1509 | err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]); |
1510 | if (err) { |
1511 | aprint_error_dev(sc->axe_dev, |
1512 | "abort rx pipe failed: %s\n" , usbd_errstr(err)); |
1513 | } |
1514 | } |
1515 | |
1516 | if (sc->axe_ep[AXE_ENDPT_TX] != NULL) { |
1517 | err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]); |
1518 | if (err) { |
1519 | aprint_error_dev(sc->axe_dev, |
1520 | "abort tx pipe failed: %s\n" , usbd_errstr(err)); |
1521 | } |
1522 | } |
1523 | |
1524 | if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) { |
1525 | err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]); |
1526 | if (err) { |
1527 | aprint_error_dev(sc->axe_dev, |
1528 | "abort intr pipe failed: %s\n" , usbd_errstr(err)); |
1529 | } |
1530 | } |
1531 | |
1532 | /* Free RX resources. */ |
1533 | for (i = 0; i < AXE_RX_LIST_CNT; i++) { |
1534 | if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) { |
1535 | usbd_destroy_xfer(sc->axe_cdata.axe_rx_chain[i].axe_xfer); |
1536 | sc->axe_cdata.axe_rx_chain[i].axe_xfer = NULL; |
1537 | } |
1538 | } |
1539 | |
1540 | /* Free TX resources. */ |
1541 | for (i = 0; i < AXE_TX_LIST_CNT; i++) { |
1542 | if (sc->axe_cdata.axe_tx_chain[i].axe_xfer != NULL) { |
1543 | usbd_destroy_xfer(sc->axe_cdata.axe_tx_chain[i].axe_xfer); |
1544 | sc->axe_cdata.axe_tx_chain[i].axe_xfer = NULL; |
1545 | } |
1546 | } |
1547 | |
1548 | /* Close pipes. */ |
1549 | if (sc->axe_ep[AXE_ENDPT_RX] != NULL) { |
1550 | err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_RX]); |
1551 | if (err) { |
1552 | aprint_error_dev(sc->axe_dev, |
1553 | "close rx pipe failed: %s\n" , usbd_errstr(err)); |
1554 | } |
1555 | sc->axe_ep[AXE_ENDPT_RX] = NULL; |
1556 | } |
1557 | |
1558 | if (sc->axe_ep[AXE_ENDPT_TX] != NULL) { |
1559 | err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_TX]); |
1560 | if (err) { |
1561 | aprint_error_dev(sc->axe_dev, |
1562 | "close tx pipe failed: %s\n" , usbd_errstr(err)); |
1563 | } |
1564 | sc->axe_ep[AXE_ENDPT_TX] = NULL; |
1565 | } |
1566 | |
1567 | if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) { |
1568 | err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_INTR]); |
1569 | if (err) { |
1570 | aprint_error_dev(sc->axe_dev, |
1571 | "close intr pipe failed: %s\n" , usbd_errstr(err)); |
1572 | } |
1573 | sc->axe_ep[AXE_ENDPT_INTR] = NULL; |
1574 | } |
1575 | |
1576 | sc->axe_link = 0; |
1577 | } |
1578 | |
1579 | MODULE(MODULE_CLASS_DRIVER, if_axe, "bpf" ); |
1580 | |
1581 | #ifdef _MODULE |
1582 | #include "ioconf.c" |
1583 | #endif |
1584 | |
1585 | static int |
1586 | if_axe_modcmd(modcmd_t cmd, void *aux) |
1587 | { |
1588 | int error = 0; |
1589 | |
1590 | switch (cmd) { |
1591 | case MODULE_CMD_INIT: |
1592 | #ifdef _MODULE |
1593 | error = config_init_component(cfdriver_ioconf_axe, |
1594 | cfattach_ioconf_axe, cfdata_ioconf_axe); |
1595 | #endif |
1596 | return error; |
1597 | case MODULE_CMD_FINI: |
1598 | #ifdef _MODULE |
1599 | error = config_fini_component(cfdriver_ioconf_axe, |
1600 | cfattach_ioconf_axe, cfdata_ioconf_axe); |
1601 | #endif |
1602 | return error; |
1603 | default: |
1604 | return ENOTTY; |
1605 | } |
1606 | } |
1607 | |