1da089c14SMark Johnston /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
461c067adSKevin Lo * Copyright (c) 2013-2014 Kevin Lo
5da089c14SMark Johnston * All rights reserved.
6da089c14SMark Johnston *
7da089c14SMark Johnston * Redistribution and use in source and binary forms, with or without
8da089c14SMark Johnston * modification, are permitted provided that the following conditions
9da089c14SMark Johnston * are met:
10da089c14SMark Johnston * 1. Redistributions of source code must retain the above copyright
11da089c14SMark Johnston * notice, this list of conditions and the following disclaimer.
12da089c14SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright
13da089c14SMark Johnston * notice, this list of conditions and the following disclaimer in the
14da089c14SMark Johnston * documentation and/or other materials provided with the distribution.
15da089c14SMark Johnston *
16da089c14SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17da089c14SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18da089c14SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19da089c14SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20da089c14SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21da089c14SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22da089c14SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23da089c14SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24da089c14SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25da089c14SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26da089c14SMark Johnston * SUCH DAMAGE.
27da089c14SMark Johnston */
28da089c14SMark Johnston
29da089c14SMark Johnston /*
30*6962da91SDamien Broka * ASIX Electronics AX88178A/AX88179/AX88179A USB 2.0/3.0 gigabit ethernet
31*6962da91SDamien Broka * driver.
32da089c14SMark Johnston */
33da089c14SMark Johnston
34da089c14SMark Johnston #include <sys/param.h>
35da089c14SMark Johnston #include <sys/systm.h>
36da089c14SMark Johnston #include <sys/bus.h>
37da089c14SMark Johnston #include <sys/condvar.h>
387c10cf8cSPyun YongHyeon #include <sys/endian.h>
39da089c14SMark Johnston #include <sys/kernel.h>
40da089c14SMark Johnston #include <sys/lock.h>
41da089c14SMark Johnston #include <sys/module.h>
42da089c14SMark Johnston #include <sys/mutex.h>
43da089c14SMark Johnston #include <sys/socket.h>
44da089c14SMark Johnston #include <sys/sysctl.h>
45da089c14SMark Johnston #include <sys/unistd.h>
46da089c14SMark Johnston
47da089c14SMark Johnston #include <net/if.h>
48da089c14SMark Johnston #include <net/if_var.h>
4931c484adSJustin Hibbits #include <net/if_media.h>
5031c484adSJustin Hibbits
5131c484adSJustin Hibbits #include <dev/mii/mii.h>
5231c484adSJustin Hibbits #include <dev/mii/miivar.h>
53da089c14SMark Johnston
54da089c14SMark Johnston #include <dev/usb/usb.h>
55da089c14SMark Johnston #include <dev/usb/usbdi.h>
56da089c14SMark Johnston #include <dev/usb/usbdi_util.h>
57da089c14SMark Johnston #include "usbdevs.h"
58da089c14SMark Johnston
59da089c14SMark Johnston #define USB_DEBUG_VAR axge_debug
60da089c14SMark Johnston #include <dev/usb/usb_debug.h>
61da089c14SMark Johnston #include <dev/usb/usb_process.h>
62da089c14SMark Johnston
63da089c14SMark Johnston #include <dev/usb/net/usb_ethernet.h>
64da089c14SMark Johnston #include <dev/usb/net/if_axgereg.h>
65da089c14SMark Johnston
6631c484adSJustin Hibbits #include "miibus_if.h"
6731c484adSJustin Hibbits
68da089c14SMark Johnston /*
69da089c14SMark Johnston * Various supported device vendors/products.
70da089c14SMark Johnston */
71da089c14SMark Johnston
72da089c14SMark Johnston static const STRUCT_USB_HOST_ID axge_devs[] = {
73*6962da91SDamien Broka #define AXGE_DEV(v,p,i,...) \
74*6962da91SDamien Broka { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), __VA_ARGS__ }
75*6962da91SDamien Broka AXGE_DEV(ASIX, AX88178A, AXGE_FLAG_178A),
76*6962da91SDamien Broka AXGE_DEV(ASIX, AX88179, AXGE_FLAG_179, USB_DEV_BCD_LTEQ(0x0100)),
77*6962da91SDamien Broka AXGE_DEV(ASIX, AX88179, AXGE_FLAG_179A, USB_DEV_BCD_GTEQ(0x0200)),
78*6962da91SDamien Broka AXGE_DEV(BELKIN, B2B128, AXGE_FLAG_179),
79*6962da91SDamien Broka AXGE_DEV(DLINK, DUB1312, AXGE_FLAG_179),
80*6962da91SDamien Broka AXGE_DEV(LENOVO, GIGALAN, AXGE_FLAG_179),
81*6962da91SDamien Broka AXGE_DEV(SITECOMEU, LN032, AXGE_FLAG_179),
82da089c14SMark Johnston #undef AXGE_DEV
83da089c14SMark Johnston };
84da089c14SMark Johnston
85da089c14SMark Johnston static const struct {
8661c067adSKevin Lo uint8_t ctrl;
8761c067adSKevin Lo uint8_t timer_l;
8861c067adSKevin Lo uint8_t timer_h;
8961c067adSKevin Lo uint8_t size;
9061c067adSKevin Lo uint8_t ifg;
91948d799eSHans Petter Selasky } __packed axge_bulk_size[] = {
9261c067adSKevin Lo { 7, 0x4f, 0x00, 0x12, 0xff },
9361c067adSKevin Lo { 7, 0x20, 0x03, 0x16, 0xff },
9461c067adSKevin Lo { 7, 0xae, 0x07, 0x18, 0xff },
9561c067adSKevin Lo { 7, 0xcc, 0x4c, 0x18, 0x08 }
96da089c14SMark Johnston };
97da089c14SMark Johnston
98da089c14SMark Johnston /* prototypes */
99da089c14SMark Johnston
100da089c14SMark Johnston static device_probe_t axge_probe;
101da089c14SMark Johnston static device_attach_t axge_attach;
102da089c14SMark Johnston static device_detach_t axge_detach;
103da089c14SMark Johnston
104da089c14SMark Johnston static usb_callback_t axge_bulk_read_callback;
105da089c14SMark Johnston static usb_callback_t axge_bulk_write_callback;
106da089c14SMark Johnston
107da089c14SMark Johnston static miibus_readreg_t axge_miibus_readreg;
108da089c14SMark Johnston static miibus_writereg_t axge_miibus_writereg;
109da089c14SMark Johnston static miibus_statchg_t axge_miibus_statchg;
110da089c14SMark Johnston
111da089c14SMark Johnston static uether_fn_t axge_attach_post;
112da089c14SMark Johnston static uether_fn_t axge_init;
113da089c14SMark Johnston static uether_fn_t axge_stop;
114da089c14SMark Johnston static uether_fn_t axge_start;
115da089c14SMark Johnston static uether_fn_t axge_tick;
116a5d82655SPyun YongHyeon static uether_fn_t axge_rxfilter;
117da089c14SMark Johnston
118da089c14SMark Johnston static int axge_read_mem(struct axge_softc *, uint8_t, uint16_t,
119da089c14SMark Johnston uint16_t, void *, int);
120da089c14SMark Johnston static void axge_write_mem(struct axge_softc *, uint8_t, uint16_t,
121da089c14SMark Johnston uint16_t, void *, int);
122d32048bbSKevin Lo static uint8_t axge_read_cmd_1(struct axge_softc *, uint8_t, uint16_t);
123da089c14SMark Johnston static uint16_t axge_read_cmd_2(struct axge_softc *, uint8_t, uint16_t,
124da089c14SMark Johnston uint16_t);
125da089c14SMark Johnston static void axge_write_cmd_1(struct axge_softc *, uint8_t, uint16_t,
126d32048bbSKevin Lo uint8_t);
127da089c14SMark Johnston static void axge_write_cmd_2(struct axge_softc *, uint8_t, uint16_t,
128da089c14SMark Johnston uint16_t, uint16_t);
129da089c14SMark Johnston static void axge_chip_init(struct axge_softc *);
130da089c14SMark Johnston static void axge_reset(struct axge_softc *);
131da089c14SMark Johnston
132da089c14SMark Johnston static int axge_attach_post_sub(struct usb_ether *);
133935b194dSJustin Hibbits static int axge_ifmedia_upd(if_t);
134935b194dSJustin Hibbits static void axge_ifmedia_sts(if_t, struct ifmediareq *);
135935b194dSJustin Hibbits static int axge_ioctl(if_t, u_long, caddr_t);
1362110950bSHans Petter Selasky static void axge_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
1372110950bSHans Petter Selasky static void axge_rxeof(struct usb_ether *, struct usb_page_cache *,
13862d42655SHans Petter Selasky unsigned, unsigned, uint32_t);
139da089c14SMark Johnston static void axge_csum_cfg(struct usb_ether *);
140da089c14SMark Johnston
141da089c14SMark Johnston #define AXGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
142da089c14SMark Johnston
143da089c14SMark Johnston #ifdef USB_DEBUG
144da089c14SMark Johnston static int axge_debug = 0;
145da089c14SMark Johnston
146f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, axge, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
147f8d2b1f3SPawel Biernacki "USB axge");
148ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RWTUN, &axge_debug, 0,
149da089c14SMark Johnston "Debug level");
150da089c14SMark Johnston #endif
151da089c14SMark Johnston
152da089c14SMark Johnston static const struct usb_config axge_config[AXGE_N_TRANSFER] = {
153da089c14SMark Johnston [AXGE_BULK_DT_WR] = {
154da089c14SMark Johnston .type = UE_BULK,
155da089c14SMark Johnston .endpoint = UE_ADDR_ANY,
156da089c14SMark Johnston .direction = UE_DIR_OUT,
1577c10cf8cSPyun YongHyeon .frames = AXGE_N_FRAMES,
1587c10cf8cSPyun YongHyeon .bufsize = AXGE_N_FRAMES * MCLBYTES,
159da089c14SMark Johnston .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
160da089c14SMark Johnston .callback = axge_bulk_write_callback,
161da089c14SMark Johnston .timeout = 10000, /* 10 seconds */
162da089c14SMark Johnston },
163da089c14SMark Johnston [AXGE_BULK_DT_RD] = {
164da089c14SMark Johnston .type = UE_BULK,
165da089c14SMark Johnston .endpoint = UE_ADDR_ANY,
166da089c14SMark Johnston .direction = UE_DIR_IN,
167948d799eSHans Petter Selasky .bufsize = 65536,
168da089c14SMark Johnston .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
169da089c14SMark Johnston .callback = axge_bulk_read_callback,
170da089c14SMark Johnston .timeout = 0, /* no timeout */
171da089c14SMark Johnston },
172da089c14SMark Johnston };
173da089c14SMark Johnston
174da089c14SMark Johnston static device_method_t axge_methods[] = {
175da089c14SMark Johnston /* Device interface. */
176da089c14SMark Johnston DEVMETHOD(device_probe, axge_probe),
177da089c14SMark Johnston DEVMETHOD(device_attach, axge_attach),
178da089c14SMark Johnston DEVMETHOD(device_detach, axge_detach),
179da089c14SMark Johnston
180da089c14SMark Johnston /* MII interface. */
181da089c14SMark Johnston DEVMETHOD(miibus_readreg, axge_miibus_readreg),
182da089c14SMark Johnston DEVMETHOD(miibus_writereg, axge_miibus_writereg),
183da089c14SMark Johnston DEVMETHOD(miibus_statchg, axge_miibus_statchg),
184da089c14SMark Johnston
185da089c14SMark Johnston DEVMETHOD_END
186da089c14SMark Johnston };
187da089c14SMark Johnston
188da089c14SMark Johnston static driver_t axge_driver = {
189da089c14SMark Johnston .name = "axge",
190da089c14SMark Johnston .methods = axge_methods,
191da089c14SMark Johnston .size = sizeof(struct axge_softc),
192da089c14SMark Johnston };
193da089c14SMark Johnston
194bc9372d7SJohn Baldwin DRIVER_MODULE(axge, uhub, axge_driver, NULL, NULL);
1953e38757dSJohn Baldwin DRIVER_MODULE(miibus, axge, miibus_driver, NULL, NULL);
196da089c14SMark Johnston MODULE_DEPEND(axge, uether, 1, 1, 1);
197da089c14SMark Johnston MODULE_DEPEND(axge, usb, 1, 1, 1);
198da089c14SMark Johnston MODULE_DEPEND(axge, ether, 1, 1, 1);
199da089c14SMark Johnston MODULE_DEPEND(axge, miibus, 1, 1, 1);
200da089c14SMark Johnston MODULE_VERSION(axge, 1);
201f809f280SWarner Losh USB_PNP_HOST_INFO(axge_devs);
202da089c14SMark Johnston
203da089c14SMark Johnston static const struct usb_ether_methods axge_ue_methods = {
204da089c14SMark Johnston .ue_attach_post = axge_attach_post,
205da089c14SMark Johnston .ue_attach_post_sub = axge_attach_post_sub,
206da089c14SMark Johnston .ue_start = axge_start,
207da089c14SMark Johnston .ue_init = axge_init,
208da089c14SMark Johnston .ue_stop = axge_stop,
209da089c14SMark Johnston .ue_tick = axge_tick,
210a5d82655SPyun YongHyeon .ue_setmulti = axge_rxfilter,
211a5d82655SPyun YongHyeon .ue_setpromisc = axge_rxfilter,
212da089c14SMark Johnston .ue_mii_upd = axge_ifmedia_upd,
213da089c14SMark Johnston .ue_mii_sts = axge_ifmedia_sts,
214da089c14SMark Johnston };
215da089c14SMark Johnston
216da089c14SMark Johnston static int
axge_read_mem(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t val,void * buf,int len)217da089c14SMark Johnston axge_read_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
218da089c14SMark Johnston uint16_t val, void *buf, int len)
219da089c14SMark Johnston {
220da089c14SMark Johnston struct usb_device_request req;
221da089c14SMark Johnston
222da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
223da089c14SMark Johnston
224da089c14SMark Johnston req.bmRequestType = UT_READ_VENDOR_DEVICE;
225da089c14SMark Johnston req.bRequest = cmd;
226da089c14SMark Johnston USETW(req.wValue, val);
227da089c14SMark Johnston USETW(req.wIndex, index);
228da089c14SMark Johnston USETW(req.wLength, len);
229da089c14SMark Johnston
230da089c14SMark Johnston return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
231da089c14SMark Johnston }
232da089c14SMark Johnston
233da089c14SMark Johnston static void
axge_write_mem(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t val,void * buf,int len)234da089c14SMark Johnston axge_write_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
235da089c14SMark Johnston uint16_t val, void *buf, int len)
236da089c14SMark Johnston {
237da089c14SMark Johnston struct usb_device_request req;
238da089c14SMark Johnston
239da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
240da089c14SMark Johnston
241da089c14SMark Johnston req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
242da089c14SMark Johnston req.bRequest = cmd;
243da089c14SMark Johnston USETW(req.wValue, val);
244da089c14SMark Johnston USETW(req.wIndex, index);
245da089c14SMark Johnston USETW(req.wLength, len);
246da089c14SMark Johnston
247da089c14SMark Johnston if (uether_do_request(&sc->sc_ue, &req, buf, 1000)) {
248da089c14SMark Johnston /* Error ignored. */
249da089c14SMark Johnston }
250da089c14SMark Johnston }
251da089c14SMark Johnston
25261c067adSKevin Lo static uint8_t
axge_read_cmd_1(struct axge_softc * sc,uint8_t cmd,uint16_t reg)253d32048bbSKevin Lo axge_read_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg)
25461c067adSKevin Lo {
25561c067adSKevin Lo uint8_t val;
25661c067adSKevin Lo
257d32048bbSKevin Lo axge_read_mem(sc, cmd, 1, reg, &val, 1);
25861c067adSKevin Lo return (val);
25961c067adSKevin Lo }
26061c067adSKevin Lo
261da089c14SMark Johnston static uint16_t
axge_read_cmd_2(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t reg)262da089c14SMark Johnston axge_read_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
263da089c14SMark Johnston uint16_t reg)
264da089c14SMark Johnston {
265da089c14SMark Johnston uint8_t val[2];
266da089c14SMark Johnston
267da089c14SMark Johnston axge_read_mem(sc, cmd, index, reg, &val, 2);
268da089c14SMark Johnston return (UGETW(val));
269da089c14SMark Johnston }
270da089c14SMark Johnston
271da089c14SMark Johnston static void
axge_write_cmd_1(struct axge_softc * sc,uint8_t cmd,uint16_t reg,uint8_t val)272d32048bbSKevin Lo axge_write_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg, uint8_t val)
273da089c14SMark Johnston {
274d32048bbSKevin Lo axge_write_mem(sc, cmd, 1, reg, &val, 1);
275da089c14SMark Johnston }
276da089c14SMark Johnston
277da089c14SMark Johnston static void
axge_write_cmd_2(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t reg,uint16_t val)278da089c14SMark Johnston axge_write_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
279da089c14SMark Johnston uint16_t reg, uint16_t val)
280da089c14SMark Johnston {
281da089c14SMark Johnston uint8_t temp[2];
282da089c14SMark Johnston
283da089c14SMark Johnston USETW(temp, val);
284da089c14SMark Johnston axge_write_mem(sc, cmd, index, reg, &temp, 2);
285da089c14SMark Johnston }
286da089c14SMark Johnston
287da089c14SMark Johnston static int
axge_miibus_readreg(device_t dev,int phy,int reg)288da089c14SMark Johnston axge_miibus_readreg(device_t dev, int phy, int reg)
289da089c14SMark Johnston {
290da089c14SMark Johnston struct axge_softc *sc;
291da089c14SMark Johnston uint16_t val;
292da089c14SMark Johnston int locked;
293da089c14SMark Johnston
294da089c14SMark Johnston sc = device_get_softc(dev);
295da089c14SMark Johnston locked = mtx_owned(&sc->sc_mtx);
296da089c14SMark Johnston if (!locked)
297da089c14SMark Johnston AXGE_LOCK(sc);
298da089c14SMark Johnston
299da089c14SMark Johnston val = axge_read_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy);
300da089c14SMark Johnston
301da089c14SMark Johnston if (!locked)
302da089c14SMark Johnston AXGE_UNLOCK(sc);
303da089c14SMark Johnston
304da089c14SMark Johnston return (val);
305da089c14SMark Johnston }
306da089c14SMark Johnston
307da089c14SMark Johnston static int
axge_miibus_writereg(device_t dev,int phy,int reg,int val)308da089c14SMark Johnston axge_miibus_writereg(device_t dev, int phy, int reg, int val)
309da089c14SMark Johnston {
310da089c14SMark Johnston struct axge_softc *sc;
311da089c14SMark Johnston int locked;
312da089c14SMark Johnston
313da089c14SMark Johnston sc = device_get_softc(dev);
314da089c14SMark Johnston locked = mtx_owned(&sc->sc_mtx);
315da089c14SMark Johnston if (!locked)
316da089c14SMark Johnston AXGE_LOCK(sc);
317da089c14SMark Johnston
318da089c14SMark Johnston axge_write_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy, val);
319da089c14SMark Johnston
320da089c14SMark Johnston if (!locked)
321da089c14SMark Johnston AXGE_UNLOCK(sc);
322da089c14SMark Johnston
323da089c14SMark Johnston return (0);
324da089c14SMark Johnston }
325da089c14SMark Johnston
326da089c14SMark Johnston static void
axge_miibus_statchg(device_t dev)327da089c14SMark Johnston axge_miibus_statchg(device_t dev)
328da089c14SMark Johnston {
329da089c14SMark Johnston struct axge_softc *sc;
330da089c14SMark Johnston struct mii_data *mii;
331935b194dSJustin Hibbits if_t ifp;
33261c067adSKevin Lo uint8_t link_status, tmp[5];
333da089c14SMark Johnston uint16_t val;
334da089c14SMark Johnston int locked;
335da089c14SMark Johnston
336da089c14SMark Johnston sc = device_get_softc(dev);
337da089c14SMark Johnston mii = GET_MII(sc);
338da089c14SMark Johnston locked = mtx_owned(&sc->sc_mtx);
339da089c14SMark Johnston if (!locked)
340da089c14SMark Johnston AXGE_LOCK(sc);
341da089c14SMark Johnston
342da089c14SMark Johnston ifp = uether_getifp(&sc->sc_ue);
343da089c14SMark Johnston if (mii == NULL || ifp == NULL ||
344935b194dSJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
345da089c14SMark Johnston goto done;
346da089c14SMark Johnston
347da089c14SMark Johnston sc->sc_flags &= ~AXGE_FLAG_LINK;
348da089c14SMark Johnston if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
349da089c14SMark Johnston (IFM_ACTIVE | IFM_AVALID)) {
350da089c14SMark Johnston switch (IFM_SUBTYPE(mii->mii_media_active)) {
351da089c14SMark Johnston case IFM_10_T:
352da089c14SMark Johnston case IFM_100_TX:
353da089c14SMark Johnston case IFM_1000_T:
354da089c14SMark Johnston sc->sc_flags |= AXGE_FLAG_LINK;
355da089c14SMark Johnston break;
356da089c14SMark Johnston default:
357da089c14SMark Johnston break;
358da089c14SMark Johnston }
359da089c14SMark Johnston }
360da089c14SMark Johnston
361da089c14SMark Johnston /* Lost link, do nothing. */
362da089c14SMark Johnston if ((sc->sc_flags & AXGE_FLAG_LINK) == 0)
363da089c14SMark Johnston goto done;
364da089c14SMark Johnston
365d32048bbSKevin Lo link_status = axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PLSR);
36661c067adSKevin Lo
367da089c14SMark Johnston val = 0;
368da089c14SMark Johnston if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
369d32048bbSKevin Lo val |= MSR_FD;
370da089c14SMark Johnston if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
371d32048bbSKevin Lo val |= MSR_TFC;
372da089c14SMark Johnston if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
373d32048bbSKevin Lo val |= MSR_RFC;
374da089c14SMark Johnston }
375d32048bbSKevin Lo val |= MSR_RE;
376da089c14SMark Johnston switch (IFM_SUBTYPE(mii->mii_media_active)) {
377da089c14SMark Johnston case IFM_1000_T:
378d32048bbSKevin Lo val |= MSR_GM | MSR_EN_125MHZ;
379d32048bbSKevin Lo if (link_status & PLSR_USB_SS)
38061c067adSKevin Lo memcpy(tmp, &axge_bulk_size[0], 5);
381d32048bbSKevin Lo else if (link_status & PLSR_USB_HS)
38261c067adSKevin Lo memcpy(tmp, &axge_bulk_size[1], 5);
38361c067adSKevin Lo else
38461c067adSKevin Lo memcpy(tmp, &axge_bulk_size[3], 5);
38561c067adSKevin Lo break;
386da089c14SMark Johnston case IFM_100_TX:
387d32048bbSKevin Lo val |= MSR_PS;
388d32048bbSKevin Lo if (link_status & (PLSR_USB_SS | PLSR_USB_HS))
38961c067adSKevin Lo memcpy(tmp, &axge_bulk_size[2], 5);
39061c067adSKevin Lo else
39161c067adSKevin Lo memcpy(tmp, &axge_bulk_size[3], 5);
39261c067adSKevin Lo break;
393da089c14SMark Johnston case IFM_10_T:
39461c067adSKevin Lo memcpy(tmp, &axge_bulk_size[3], 5);
395da089c14SMark Johnston break;
396da089c14SMark Johnston }
39761c067adSKevin Lo /* Rx bulk configuration. */
39861c067adSKevin Lo axge_write_mem(sc, AXGE_ACCESS_MAC, 5, AXGE_RX_BULKIN_QCTRL, tmp, 5);
399d32048bbSKevin Lo axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
400da089c14SMark Johnston done:
401da089c14SMark Johnston if (!locked)
402da089c14SMark Johnston AXGE_UNLOCK(sc);
403da089c14SMark Johnston }
404da089c14SMark Johnston
405da089c14SMark Johnston static void
axge_chip_init(struct axge_softc * sc)406da089c14SMark Johnston axge_chip_init(struct axge_softc *sc)
407da089c14SMark Johnston {
408da089c14SMark Johnston /* Power up ethernet PHY. */
409d32048bbSKevin Lo axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, 0);
410d32048bbSKevin Lo axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, EPPRCR_IPRL);
411da089c14SMark Johnston uether_pause(&sc->sc_ue, hz / 4);
412d32048bbSKevin Lo axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT,
413da089c14SMark Johnston AXGE_CLK_SELECT_ACS | AXGE_CLK_SELECT_BCS);
414da089c14SMark Johnston uether_pause(&sc->sc_ue, hz / 10);
415*6962da91SDamien Broka
416*6962da91SDamien Broka if ((sc->sc_flags & AXGE_FLAG_179A) != 0) {
417*6962da91SDamien Broka /*
418*6962da91SDamien Broka * 179A chip has two firmware modes that each use different
419*6962da91SDamien Broka * transfer layouts for Ethernet over USB. The newer fw mode has
420*6962da91SDamien Broka * larger rx packet headers which seem to
421*6962da91SDamien Broka * accomodate for ethernet frames up to 9K length and a VLAN
422*6962da91SDamien Broka * field for hardware tagging, but is not backward compatible
423*6962da91SDamien Broka * with 178A/179 bulk transfer code due to the change in size
424*6962da91SDamien Broka * and field alignments. The other fw mode uses the same packet
425*6962da91SDamien Broka * headers as the older 178A/179 chips, which this driver uses.
426*6962da91SDamien Broka *
427*6962da91SDamien Broka * As we do not currently have VLAN hw tagging or jumbo support
428*6962da91SDamien Broka * in this driver anyway, we're ok forcing 179A into its compat
429*6962da91SDamien Broka * mode by default.
430*6962da91SDamien Broka */
431*6962da91SDamien Broka axge_write_cmd_1(sc, AXGE_FW_MODE, AXGE_FW_MODE_178A179, 0);
432*6962da91SDamien Broka }
433da089c14SMark Johnston }
434da089c14SMark Johnston
435da089c14SMark Johnston static void
axge_reset(struct axge_softc * sc)436da089c14SMark Johnston axge_reset(struct axge_softc *sc)
437da089c14SMark Johnston {
438da089c14SMark Johnston struct usb_config_descriptor *cd;
439da089c14SMark Johnston usb_error_t err;
440da089c14SMark Johnston
441da089c14SMark Johnston cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
442da089c14SMark Johnston
443da089c14SMark Johnston err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
444da089c14SMark Johnston cd->bConfigurationValue);
445da089c14SMark Johnston if (err)
446da089c14SMark Johnston DPRINTF("reset failed (ignored)\n");
447da089c14SMark Johnston
448da089c14SMark Johnston /* Wait a little while for the chip to get its brains in order. */
449da089c14SMark Johnston uether_pause(&sc->sc_ue, hz / 100);
450da089c14SMark Johnston
451da089c14SMark Johnston /* Reinitialize controller to achieve full reset. */
452da089c14SMark Johnston axge_chip_init(sc);
453da089c14SMark Johnston }
454da089c14SMark Johnston
455da089c14SMark Johnston static void
axge_attach_post(struct usb_ether * ue)456da089c14SMark Johnston axge_attach_post(struct usb_ether *ue)
457da089c14SMark Johnston {
458da089c14SMark Johnston struct axge_softc *sc;
459da089c14SMark Johnston
460da089c14SMark Johnston sc = uether_getsc(ue);
461da089c14SMark Johnston
462da089c14SMark Johnston /* Initialize controller and get station address. */
463da089c14SMark Johnston axge_chip_init(sc);
464d32048bbSKevin Lo axge_read_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
465da089c14SMark Johnston ue->ue_eaddr, ETHER_ADDR_LEN);
466da089c14SMark Johnston }
467da089c14SMark Johnston
468da089c14SMark Johnston static int
axge_attach_post_sub(struct usb_ether * ue)469da089c14SMark Johnston axge_attach_post_sub(struct usb_ether *ue)
470da089c14SMark Johnston {
471935b194dSJustin Hibbits if_t ifp;
472da089c14SMark Johnston int error;
473da089c14SMark Johnston
474da089c14SMark Johnston ifp = ue->ue_ifp;
475935b194dSJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
476935b194dSJustin Hibbits if_setstartfn(ifp, uether_start);
477935b194dSJustin Hibbits if_setioctlfn(ifp, axge_ioctl);
478935b194dSJustin Hibbits if_setinitfn(ifp, uether_init);
479935b194dSJustin Hibbits if_setsendqlen(ifp, ifqmaxlen);
480935b194dSJustin Hibbits if_setsendqready(ifp);
481da089c14SMark Johnston
482935b194dSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM, 0);
483935b194dSJustin Hibbits if_sethwassist(ifp, AXGE_CSUM_FEATURES);
484935b194dSJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
485da089c14SMark Johnston
486c6df6f53SWarner Losh bus_topo_lock();
487da089c14SMark Johnston error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
488da089c14SMark Johnston uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
489a42c5d9fSPyun YongHyeon BMSR_DEFCAPMASK, AXGE_PHY_ADDR, MII_OFFSET_ANY, MIIF_DOPAUSE);
490c6df6f53SWarner Losh bus_topo_unlock();
491da089c14SMark Johnston
492da089c14SMark Johnston return (error);
493da089c14SMark Johnston }
494da089c14SMark Johnston
495da089c14SMark Johnston /*
496da089c14SMark Johnston * Set media options.
497da089c14SMark Johnston */
498da089c14SMark Johnston static int
axge_ifmedia_upd(if_t ifp)499935b194dSJustin Hibbits axge_ifmedia_upd(if_t ifp)
500da089c14SMark Johnston {
501da089c14SMark Johnston struct axge_softc *sc;
502da089c14SMark Johnston struct mii_data *mii;
503da089c14SMark Johnston struct mii_softc *miisc;
504da089c14SMark Johnston int error;
505da089c14SMark Johnston
506935b194dSJustin Hibbits sc = if_getsoftc(ifp);
507da089c14SMark Johnston mii = GET_MII(sc);
508da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
509da089c14SMark Johnston
510da089c14SMark Johnston LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
511da089c14SMark Johnston PHY_RESET(miisc);
512da089c14SMark Johnston error = mii_mediachg(mii);
513da089c14SMark Johnston
514da089c14SMark Johnston return (error);
515da089c14SMark Johnston }
516da089c14SMark Johnston
517da089c14SMark Johnston /*
518da089c14SMark Johnston * Report current media status.
519da089c14SMark Johnston */
520da089c14SMark Johnston static void
axge_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)521935b194dSJustin Hibbits axge_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
522da089c14SMark Johnston {
523da089c14SMark Johnston struct axge_softc *sc;
524da089c14SMark Johnston struct mii_data *mii;
525da089c14SMark Johnston
526935b194dSJustin Hibbits sc = if_getsoftc(ifp);
527da089c14SMark Johnston mii = GET_MII(sc);
528da089c14SMark Johnston AXGE_LOCK(sc);
529da089c14SMark Johnston mii_pollstat(mii);
530da089c14SMark Johnston ifmr->ifm_active = mii->mii_media_active;
531da089c14SMark Johnston ifmr->ifm_status = mii->mii_media_status;
532da089c14SMark Johnston AXGE_UNLOCK(sc);
533da089c14SMark Johnston }
534da089c14SMark Johnston
535da089c14SMark Johnston /*
536da089c14SMark Johnston * Probe for a AX88179 chip.
537da089c14SMark Johnston */
538da089c14SMark Johnston static int
axge_probe(device_t dev)539da089c14SMark Johnston axge_probe(device_t dev)
540da089c14SMark Johnston {
541da089c14SMark Johnston struct usb_attach_arg *uaa;
542da089c14SMark Johnston
543da089c14SMark Johnston uaa = device_get_ivars(dev);
544da089c14SMark Johnston if (uaa->usb_mode != USB_MODE_HOST)
545da089c14SMark Johnston return (ENXIO);
546da089c14SMark Johnston if (uaa->info.bConfigIndex != AXGE_CONFIG_IDX)
547da089c14SMark Johnston return (ENXIO);
548da089c14SMark Johnston if (uaa->info.bIfaceIndex != AXGE_IFACE_IDX)
549da089c14SMark Johnston return (ENXIO);
550da089c14SMark Johnston
551da089c14SMark Johnston return (usbd_lookup_id_by_uaa(axge_devs, sizeof(axge_devs), uaa));
552da089c14SMark Johnston }
553da089c14SMark Johnston
554da089c14SMark Johnston /*
555da089c14SMark Johnston * Attach the interface. Allocate softc structures, do ifmedia
556da089c14SMark Johnston * setup and ethernet/BPF attach.
557da089c14SMark Johnston */
558da089c14SMark Johnston static int
axge_attach(device_t dev)559da089c14SMark Johnston axge_attach(device_t dev)
560da089c14SMark Johnston {
561da089c14SMark Johnston struct usb_attach_arg *uaa;
562da089c14SMark Johnston struct axge_softc *sc;
563da089c14SMark Johnston struct usb_ether *ue;
564da089c14SMark Johnston uint8_t iface_index;
565da089c14SMark Johnston int error;
566da089c14SMark Johnston
567da089c14SMark Johnston uaa = device_get_ivars(dev);
568da089c14SMark Johnston sc = device_get_softc(dev);
569da089c14SMark Johnston ue = &sc->sc_ue;
570da089c14SMark Johnston
571da089c14SMark Johnston device_set_usb_desc(dev);
572da089c14SMark Johnston mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
573da089c14SMark Johnston
574*6962da91SDamien Broka sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
575*6962da91SDamien Broka
576da089c14SMark Johnston iface_index = AXGE_IFACE_IDX;
577da089c14SMark Johnston error = usbd_transfer_setup(uaa->device, &iface_index,
578da089c14SMark Johnston sc->sc_xfer, axge_config, AXGE_N_TRANSFER, sc, &sc->sc_mtx);
579da089c14SMark Johnston if (error) {
580da089c14SMark Johnston device_printf(dev, "allocating USB transfers failed\n");
5812948462dSPyun YongHyeon mtx_destroy(&sc->sc_mtx);
5822948462dSPyun YongHyeon return (ENXIO);
583da089c14SMark Johnston }
584da089c14SMark Johnston
585da089c14SMark Johnston ue->ue_sc = sc;
586da089c14SMark Johnston ue->ue_dev = dev;
587da089c14SMark Johnston ue->ue_udev = uaa->device;
588da089c14SMark Johnston ue->ue_mtx = &sc->sc_mtx;
589da089c14SMark Johnston ue->ue_methods = &axge_ue_methods;
590da089c14SMark Johnston
591da089c14SMark Johnston error = uether_ifattach(ue);
592da089c14SMark Johnston if (error) {
593da089c14SMark Johnston device_printf(dev, "could not attach interface\n");
594da089c14SMark Johnston goto detach;
595da089c14SMark Johnston }
596da089c14SMark Johnston return (0); /* success */
597da089c14SMark Johnston
598da089c14SMark Johnston detach:
599da089c14SMark Johnston axge_detach(dev);
600da089c14SMark Johnston return (ENXIO); /* failure */
601da089c14SMark Johnston }
602da089c14SMark Johnston
603da089c14SMark Johnston static int
axge_detach(device_t dev)604da089c14SMark Johnston axge_detach(device_t dev)
605da089c14SMark Johnston {
606da089c14SMark Johnston struct axge_softc *sc;
607da089c14SMark Johnston struct usb_ether *ue;
6089ab0e2a7SPyun YongHyeon uint16_t val;
609da089c14SMark Johnston
610da089c14SMark Johnston sc = device_get_softc(dev);
611da089c14SMark Johnston ue = &sc->sc_ue;
6129ab0e2a7SPyun YongHyeon if (device_is_attached(dev)) {
6137ef4e76fSHans Petter Selasky /* wait for any post attach or other command to complete */
6147ef4e76fSHans Petter Selasky usb_proc_drain(&ue->ue_tq);
6157ef4e76fSHans Petter Selasky
6169ab0e2a7SPyun YongHyeon AXGE_LOCK(sc);
6179ab0e2a7SPyun YongHyeon /*
6189ab0e2a7SPyun YongHyeon * XXX
6199ab0e2a7SPyun YongHyeon * ether_ifdetach(9) should be called first.
6209ab0e2a7SPyun YongHyeon */
6219ab0e2a7SPyun YongHyeon axge_stop(ue);
6229ab0e2a7SPyun YongHyeon /* Force bulk-in to return a zero-length USB packet. */
6239ab0e2a7SPyun YongHyeon val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR);
6249ab0e2a7SPyun YongHyeon val |= EPPRCR_BZ | EPPRCR_IPRL;
6259ab0e2a7SPyun YongHyeon axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, val);
6269ab0e2a7SPyun YongHyeon /* Change clock. */
6279ab0e2a7SPyun YongHyeon axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT, 0);
6289ab0e2a7SPyun YongHyeon /* Disable MAC. */
6299ab0e2a7SPyun YongHyeon axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, 0);
6309ab0e2a7SPyun YongHyeon AXGE_UNLOCK(sc);
6319ab0e2a7SPyun YongHyeon }
632da089c14SMark Johnston usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER);
633da089c14SMark Johnston uether_ifdetach(ue);
634da089c14SMark Johnston mtx_destroy(&sc->sc_mtx);
635da089c14SMark Johnston
636da089c14SMark Johnston return (0);
637da089c14SMark Johnston }
638da089c14SMark Johnston
639da089c14SMark Johnston static void
axge_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)640da089c14SMark Johnston axge_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
641da089c14SMark Johnston {
642da089c14SMark Johnston struct axge_softc *sc;
643da089c14SMark Johnston struct usb_ether *ue;
644da089c14SMark Johnston struct usb_page_cache *pc;
645da089c14SMark Johnston int actlen;
646da089c14SMark Johnston
647da089c14SMark Johnston sc = usbd_xfer_softc(xfer);
648da089c14SMark Johnston ue = &sc->sc_ue;
649da089c14SMark Johnston usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
650da089c14SMark Johnston
651da089c14SMark Johnston switch (USB_GET_STATE(xfer)) {
652da089c14SMark Johnston case USB_ST_TRANSFERRED:
653da089c14SMark Johnston pc = usbd_xfer_get_frame(xfer, 0);
6542110950bSHans Petter Selasky axge_rx_frame(ue, pc, actlen);
655da089c14SMark Johnston
656da089c14SMark Johnston /* FALLTHROUGH */
657da089c14SMark Johnston case USB_ST_SETUP:
658da089c14SMark Johnston tr_setup:
659da089c14SMark Johnston usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
660da089c14SMark Johnston usbd_transfer_submit(xfer);
661da089c14SMark Johnston uether_rxflush(ue);
662948d799eSHans Petter Selasky break;
663da089c14SMark Johnston
664da089c14SMark Johnston default:
665da089c14SMark Johnston if (error != USB_ERR_CANCELLED) {
666da089c14SMark Johnston usbd_xfer_set_stall(xfer);
667da089c14SMark Johnston goto tr_setup;
668da089c14SMark Johnston }
669948d799eSHans Petter Selasky break;
670da089c14SMark Johnston }
671da089c14SMark Johnston }
672da089c14SMark Johnston
673da089c14SMark Johnston static void
axge_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)674da089c14SMark Johnston axge_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
675da089c14SMark Johnston {
676da089c14SMark Johnston struct axge_softc *sc;
677935b194dSJustin Hibbits if_t ifp;
678da089c14SMark Johnston struct usb_page_cache *pc;
679da089c14SMark Johnston struct mbuf *m;
6807c10cf8cSPyun YongHyeon struct axge_frame_txhdr txhdr;
681d32048bbSKevin Lo int nframes, pos;
682da089c14SMark Johnston
683da089c14SMark Johnston sc = usbd_xfer_softc(xfer);
684da089c14SMark Johnston ifp = uether_getifp(&sc->sc_ue);
685da089c14SMark Johnston
686da089c14SMark Johnston switch (USB_GET_STATE(xfer)) {
687da089c14SMark Johnston case USB_ST_TRANSFERRED:
688935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
689da089c14SMark Johnston /* FALLTHROUGH */
690da089c14SMark Johnston case USB_ST_SETUP:
691da089c14SMark Johnston tr_setup:
692da089c14SMark Johnston if ((sc->sc_flags & AXGE_FLAG_LINK) == 0 ||
693935b194dSJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) != 0) {
694da089c14SMark Johnston /*
695da089c14SMark Johnston * Don't send anything if there is no link or
696da089c14SMark Johnston * controller is busy.
697da089c14SMark Johnston */
698da089c14SMark Johnston return;
699da089c14SMark Johnston }
700da089c14SMark Johnston
7017c10cf8cSPyun YongHyeon for (nframes = 0; nframes < AXGE_N_FRAMES &&
702935b194dSJustin Hibbits !if_sendq_empty(ifp); nframes++) {
703935b194dSJustin Hibbits m = if_dequeue(ifp);
704da089c14SMark Johnston if (m == NULL)
705da089c14SMark Johnston break;
706da089c14SMark Johnston usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
707da089c14SMark Johnston nframes);
708da089c14SMark Johnston pc = usbd_xfer_get_frame(xfer, nframes);
7097c10cf8cSPyun YongHyeon txhdr.mss = 0;
7107c10cf8cSPyun YongHyeon txhdr.len = htole32(AXGE_TXBYTES(m->m_pkthdr.len));
711935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0 &&
7127c10cf8cSPyun YongHyeon (m->m_pkthdr.csum_flags & AXGE_CSUM_FEATURES) == 0)
7137c10cf8cSPyun YongHyeon txhdr.len |= htole32(AXGE_CSUM_DISABLE);
7147c10cf8cSPyun YongHyeon
7157c10cf8cSPyun YongHyeon pos = 0;
7167c10cf8cSPyun YongHyeon usbd_copy_in(pc, pos, &txhdr, sizeof(txhdr));
7177c10cf8cSPyun YongHyeon pos += sizeof(txhdr);
718d32048bbSKevin Lo usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
719d32048bbSKevin Lo pos += m->m_pkthdr.len;
720da089c14SMark Johnston
721da089c14SMark Johnston /*
722da089c14SMark Johnston * if there's a BPF listener, bounce a copy
723da089c14SMark Johnston * of this frame to him:
724da089c14SMark Johnston */
725da089c14SMark Johnston BPF_MTAP(ifp, m);
726da089c14SMark Johnston
727da089c14SMark Johnston m_freem(m);
728da089c14SMark Johnston
729da089c14SMark Johnston /* Set frame length. */
730d32048bbSKevin Lo usbd_xfer_set_frame_len(xfer, nframes, pos);
731da089c14SMark Johnston }
732da089c14SMark Johnston if (nframes != 0) {
7337c10cf8cSPyun YongHyeon /*
7347c10cf8cSPyun YongHyeon * XXX
7357c10cf8cSPyun YongHyeon * Update TX packet counter here. This is not
7367c10cf8cSPyun YongHyeon * correct way but it seems that there is no way
7377c10cf8cSPyun YongHyeon * to know how many packets are sent at the end
7387c10cf8cSPyun YongHyeon * of transfer because controller combines
7397c10cf8cSPyun YongHyeon * multiple writes into single one if there is
7407c10cf8cSPyun YongHyeon * room in TX buffer of controller.
7417c10cf8cSPyun YongHyeon */
7427c10cf8cSPyun YongHyeon if_inc_counter(ifp, IFCOUNTER_OPACKETS, nframes);
743da089c14SMark Johnston usbd_xfer_set_frames(xfer, nframes);
744da089c14SMark Johnston usbd_transfer_submit(xfer);
745935b194dSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
746da089c14SMark Johnston }
747da089c14SMark Johnston return;
748da089c14SMark Johnston /* NOTREACHED */
749da089c14SMark Johnston default:
750ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
751935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
752da089c14SMark Johnston
753da089c14SMark Johnston if (error != USB_ERR_CANCELLED) {
754da089c14SMark Johnston usbd_xfer_set_stall(xfer);
755da089c14SMark Johnston goto tr_setup;
756da089c14SMark Johnston }
757da089c14SMark Johnston return;
758da089c14SMark Johnston }
759da089c14SMark Johnston }
760da089c14SMark Johnston
761da089c14SMark Johnston static void
axge_tick(struct usb_ether * ue)762da089c14SMark Johnston axge_tick(struct usb_ether *ue)
763da089c14SMark Johnston {
764da089c14SMark Johnston struct axge_softc *sc;
765da089c14SMark Johnston struct mii_data *mii;
766da089c14SMark Johnston
767da089c14SMark Johnston sc = uether_getsc(ue);
768da089c14SMark Johnston mii = GET_MII(sc);
769da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
770da089c14SMark Johnston
771da089c14SMark Johnston mii_tick(mii);
772da089c14SMark Johnston }
773da089c14SMark Johnston
77419a1980eSGleb Smirnoff static u_int
axge_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)77519a1980eSGleb Smirnoff axge_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
77619a1980eSGleb Smirnoff {
77719a1980eSGleb Smirnoff uint8_t *hashtbl = arg;
77819a1980eSGleb Smirnoff uint32_t h;
77919a1980eSGleb Smirnoff
78019a1980eSGleb Smirnoff h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 26;
78119a1980eSGleb Smirnoff hashtbl[h / 8] |= 1 << (h % 8);
78219a1980eSGleb Smirnoff
78319a1980eSGleb Smirnoff return (1);
78419a1980eSGleb Smirnoff }
78519a1980eSGleb Smirnoff
786da089c14SMark Johnston static void
axge_rxfilter(struct usb_ether * ue)787a5d82655SPyun YongHyeon axge_rxfilter(struct usb_ether *ue)
788da089c14SMark Johnston {
789da089c14SMark Johnston struct axge_softc *sc;
790935b194dSJustin Hibbits if_t ifp;
791da089c14SMark Johnston uint16_t rxmode;
792da089c14SMark Johnston uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
793da089c14SMark Johnston
794da089c14SMark Johnston sc = uether_getsc(ue);
795da089c14SMark Johnston ifp = uether_getifp(ue);
796da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
797da089c14SMark Johnston
798a5d82655SPyun YongHyeon /*
799a5d82655SPyun YongHyeon * Configure RX settings.
800a5d82655SPyun YongHyeon * Don't set RCR_IPE(IP header alignment on 32bit boundary) to disable
801a5d82655SPyun YongHyeon * inserting extra padding bytes. This wastes ethernet to USB host
802a5d82655SPyun YongHyeon * bandwidth as well as complicating RX handling logic. Current USB
803a5d82655SPyun YongHyeon * framework requires copying RX frames to mbufs so there is no need
804a5d82655SPyun YongHyeon * to worry about alignment.
805a5d82655SPyun YongHyeon */
806a5d82655SPyun YongHyeon rxmode = RCR_DROP_CRCERR | RCR_START;
807935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_BROADCAST)
808a5d82655SPyun YongHyeon rxmode |= RCR_ACPT_BCAST;
809935b194dSJustin Hibbits if (if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) {
810935b194dSJustin Hibbits if (if_getflags(ifp) & IFF_PROMISC)
811a5d82655SPyun YongHyeon rxmode |= RCR_PROMISC;
812b2cdc7caSPyun YongHyeon rxmode |= RCR_ACPT_ALL_MCAST;
813d32048bbSKevin Lo axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
814da089c14SMark Johnston return;
815da089c14SMark Johnston }
816da089c14SMark Johnston
817a5d82655SPyun YongHyeon rxmode |= RCR_ACPT_MCAST;
81819a1980eSGleb Smirnoff if_foreach_llmaddr(ifp, axge_hash_maddr, &hashtbl);
819da089c14SMark Johnston
820d32048bbSKevin Lo axge_write_mem(sc, AXGE_ACCESS_MAC, 8, AXGE_MFA, (void *)&hashtbl, 8);
821d32048bbSKevin Lo axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
822da089c14SMark Johnston }
823da089c14SMark Johnston
824da089c14SMark Johnston static void
axge_start(struct usb_ether * ue)825da089c14SMark Johnston axge_start(struct usb_ether *ue)
826da089c14SMark Johnston {
827da089c14SMark Johnston struct axge_softc *sc;
828da089c14SMark Johnston
829da089c14SMark Johnston sc = uether_getsc(ue);
830da089c14SMark Johnston /*
831da089c14SMark Johnston * Start the USB transfers, if not already started.
832da089c14SMark Johnston */
833da089c14SMark Johnston usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_RD]);
834da089c14SMark Johnston usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_WR]);
835da089c14SMark Johnston }
836da089c14SMark Johnston
837da089c14SMark Johnston static void
axge_init(struct usb_ether * ue)838da089c14SMark Johnston axge_init(struct usb_ether *ue)
839da089c14SMark Johnston {
840da089c14SMark Johnston struct axge_softc *sc;
841935b194dSJustin Hibbits if_t ifp;
842da089c14SMark Johnston
843da089c14SMark Johnston sc = uether_getsc(ue);
844da089c14SMark Johnston ifp = uether_getifp(ue);
845da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
846da089c14SMark Johnston
847935b194dSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
848da089c14SMark Johnston return;
849da089c14SMark Johnston
850da089c14SMark Johnston /*
851da089c14SMark Johnston * Cancel pending I/O and free all RX/TX buffers.
852da089c14SMark Johnston */
853da089c14SMark Johnston axge_stop(ue);
854da089c14SMark Johnston
855da089c14SMark Johnston axge_reset(sc);
856da089c14SMark Johnston
857da089c14SMark Johnston /* Set MAC address. */
858d32048bbSKevin Lo axge_write_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
859935b194dSJustin Hibbits if_getlladdr(ifp), ETHER_ADDR_LEN);
860da089c14SMark Johnston
861d32048bbSKevin Lo axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLLR, 0x34);
862d32048bbSKevin Lo axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLHR, 0x52);
863da089c14SMark Johnston
864da089c14SMark Johnston /* Configure TX/RX checksum offloading. */
865da089c14SMark Johnston axge_csum_cfg(ue);
866da089c14SMark Johnston
867a5d82655SPyun YongHyeon /* Configure RX filters. */
868a5d82655SPyun YongHyeon axge_rxfilter(ue);
869da089c14SMark Johnston
870a5d82655SPyun YongHyeon /*
871a5d82655SPyun YongHyeon * XXX
872a5d82655SPyun YongHyeon * Controller supports wakeup on link change detection,
873a5d82655SPyun YongHyeon * magic packet and wakeup frame recpetion. But it seems
874a5d82655SPyun YongHyeon * there is no framework for USB ethernet suspend/wakeup.
875a5d82655SPyun YongHyeon * Disable all wakeup functions.
876a5d82655SPyun YongHyeon */
877a5d82655SPyun YongHyeon axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR, 0);
878a5d82655SPyun YongHyeon (void)axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR);
879da089c14SMark Johnston
880a5d82655SPyun YongHyeon /* Configure default medium type. */
881a5d82655SPyun YongHyeon axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, MSR_GM | MSR_FD |
882a5d82655SPyun YongHyeon MSR_RFC | MSR_TFC | MSR_RE);
883da089c14SMark Johnston
884da089c14SMark Johnston usbd_xfer_set_stall(sc->sc_xfer[AXGE_BULK_DT_WR]);
885da089c14SMark Johnston
886935b194dSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
887da089c14SMark Johnston /* Switch to selected media. */
888da089c14SMark Johnston axge_ifmedia_upd(ifp);
889da089c14SMark Johnston }
890da089c14SMark Johnston
891da089c14SMark Johnston static void
axge_stop(struct usb_ether * ue)892da089c14SMark Johnston axge_stop(struct usb_ether *ue)
893da089c14SMark Johnston {
894da089c14SMark Johnston struct axge_softc *sc;
895935b194dSJustin Hibbits if_t ifp;
89615b5fb58SPyun YongHyeon uint16_t val;
897da089c14SMark Johnston
898da089c14SMark Johnston sc = uether_getsc(ue);
899da089c14SMark Johnston ifp = uether_getifp(ue);
900da089c14SMark Johnston
901da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
902da089c14SMark Johnston
90315b5fb58SPyun YongHyeon val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR);
90415b5fb58SPyun YongHyeon val &= ~MSR_RE;
90515b5fb58SPyun YongHyeon axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
90615b5fb58SPyun YongHyeon
9077ef4e76fSHans Petter Selasky if (ifp != NULL)
908935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
909da089c14SMark Johnston sc->sc_flags &= ~AXGE_FLAG_LINK;
910da089c14SMark Johnston
911da089c14SMark Johnston /*
912da089c14SMark Johnston * Stop all the transfers, if not already stopped:
913da089c14SMark Johnston */
914da089c14SMark Johnston usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_WR]);
915da089c14SMark Johnston usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_RD]);
916da089c14SMark Johnston }
917da089c14SMark Johnston
918da089c14SMark Johnston static int
axge_ioctl(if_t ifp,u_long cmd,caddr_t data)919935b194dSJustin Hibbits axge_ioctl(if_t ifp, u_long cmd, caddr_t data)
920da089c14SMark Johnston {
921da089c14SMark Johnston struct usb_ether *ue;
922da089c14SMark Johnston struct axge_softc *sc;
923da089c14SMark Johnston struct ifreq *ifr;
924da089c14SMark Johnston int error, mask, reinit;
925da089c14SMark Johnston
926935b194dSJustin Hibbits ue = if_getsoftc(ifp);
927da089c14SMark Johnston sc = uether_getsc(ue);
928da089c14SMark Johnston ifr = (struct ifreq *)data;
929da089c14SMark Johnston error = 0;
930da089c14SMark Johnston reinit = 0;
931da089c14SMark Johnston if (cmd == SIOCSIFCAP) {
932da089c14SMark Johnston AXGE_LOCK(sc);
933935b194dSJustin Hibbits mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
934da089c14SMark Johnston if ((mask & IFCAP_TXCSUM) != 0 &&
935935b194dSJustin Hibbits (if_getcapabilities(ifp) & IFCAP_TXCSUM) != 0) {
936935b194dSJustin Hibbits if_togglecapenable(ifp, IFCAP_TXCSUM);
937935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
938935b194dSJustin Hibbits if_sethwassistbits(ifp, AXGE_CSUM_FEATURES, 0);
939da089c14SMark Johnston else
940935b194dSJustin Hibbits if_sethwassistbits(ifp, 0, AXGE_CSUM_FEATURES);
941da089c14SMark Johnston reinit++;
942da089c14SMark Johnston }
943da089c14SMark Johnston if ((mask & IFCAP_RXCSUM) != 0 &&
944935b194dSJustin Hibbits (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
945935b194dSJustin Hibbits if_togglecapenable(ifp, IFCAP_RXCSUM);
946da089c14SMark Johnston reinit++;
947da089c14SMark Johnston }
948935b194dSJustin Hibbits if (reinit > 0 && if_getdrvflags(ifp) & IFF_DRV_RUNNING)
949935b194dSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
950da089c14SMark Johnston else
951da089c14SMark Johnston reinit = 0;
952da089c14SMark Johnston AXGE_UNLOCK(sc);
953da089c14SMark Johnston if (reinit > 0)
954da089c14SMark Johnston uether_init(ue);
955da089c14SMark Johnston } else
956da089c14SMark Johnston error = uether_ioctl(ifp, cmd, data);
957da089c14SMark Johnston
958da089c14SMark Johnston return (error);
959da089c14SMark Johnston }
960da089c14SMark Johnston
9612110950bSHans Petter Selasky static void
axge_rx_frame(struct usb_ether * ue,struct usb_page_cache * pc,int actlen)962da089c14SMark Johnston axge_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
963da089c14SMark Johnston {
964a5d82655SPyun YongHyeon struct axge_frame_rxhdr pkt_hdr;
965561227daSHans Petter Selasky uint32_t rxhdr;
966a5d82655SPyun YongHyeon uint32_t pos;
967a5d82655SPyun YongHyeon uint32_t pkt_cnt, pkt_end;
968561227daSHans Petter Selasky uint32_t hdr_off;
969561227daSHans Petter Selasky uint32_t pktlen;
970561227daSHans Petter Selasky
971561227daSHans Petter Selasky /* verify we have enough data */
972561227daSHans Petter Selasky if (actlen < (int)sizeof(rxhdr))
9732110950bSHans Petter Selasky return;
974da089c14SMark Johnston
975da089c14SMark Johnston pos = 0;
976da089c14SMark Johnston
977da089c14SMark Johnston usbd_copy_out(pc, actlen - sizeof(rxhdr), &rxhdr, sizeof(rxhdr));
978da089c14SMark Johnston rxhdr = le32toh(rxhdr);
979da089c14SMark Johnston
980a5d82655SPyun YongHyeon pkt_cnt = rxhdr & 0xFFFF;
981a5d82655SPyun YongHyeon hdr_off = pkt_end = (rxhdr >> 16) & 0xFFFF;
982da089c14SMark Johnston
983a5d82655SPyun YongHyeon /*
98470fbcd45SDamien Broka * On older firmware:
985a5d82655SPyun YongHyeon * <----------------------- actlen ------------------------>
986a5d82655SPyun YongHyeon * [frame #0]...[frame #N][pkt_hdr #0]...[pkt_hdr #N][rxhdr]
98770fbcd45SDamien Broka *
98870fbcd45SDamien Broka * On newer firmware:
98970fbcd45SDamien Broka * <----------------------- actlen -----------------
99070fbcd45SDamien Broka * [frame #0]...[frame #N][pkt_hdr #0][dummy_hdr]...
99170fbcd45SDamien Broka * -------------------------------->
99270fbcd45SDamien Broka * ...[pkt_hdr #N][dummy_hdr][rxhdr]
99370fbcd45SDamien Broka *
994a5d82655SPyun YongHyeon * Each RX frame would be aligned on 8 bytes boundary. If
995a5d82655SPyun YongHyeon * RCR_IPE bit is set in AXGE_RCR register, there would be 2
996a5d82655SPyun YongHyeon * padding bytes and 6 dummy bytes(as the padding also should
997a5d82655SPyun YongHyeon * be aligned on 8 bytes boundary) for each RX frame to align
998a5d82655SPyun YongHyeon * IP header on 32bits boundary. Driver don't set RCR_IPE bit
999a5d82655SPyun YongHyeon * of AXGE_RCR register, so there should be no padding bytes
1000a5d82655SPyun YongHyeon * which simplifies RX logic a lot.
100170fbcd45SDamien Broka *
100270fbcd45SDamien Broka * Further, newer firmware interweaves dummy headers that have
100370fbcd45SDamien Broka * pktlen == 0 and should be skipped without being seen as
100470fbcd45SDamien Broka * dropped frames.
1005a5d82655SPyun YongHyeon */
10062110950bSHans Petter Selasky while (pkt_cnt--) {
1007561227daSHans Petter Selasky /* verify the header offset */
1008561227daSHans Petter Selasky if ((int)(hdr_off + sizeof(pkt_hdr)) > actlen) {
10092110950bSHans Petter Selasky DPRINTF("End of packet headers\n");
10102110950bSHans Petter Selasky break;
10112110950bSHans Petter Selasky }
1012a5d82655SPyun YongHyeon usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr));
1013a5d82655SPyun YongHyeon pkt_hdr.status = le32toh(pkt_hdr.status);
1014a5d82655SPyun YongHyeon pktlen = AXGE_RXBYTES(pkt_hdr.status);
101570fbcd45SDamien Broka hdr_off += sizeof(pkt_hdr);
101670fbcd45SDamien Broka
101770fbcd45SDamien Broka /* Skip dummy packet header. */
101870fbcd45SDamien Broka if (pktlen == 0)
101970fbcd45SDamien Broka continue;
102070fbcd45SDamien Broka
1021a5d82655SPyun YongHyeon if (pos + pktlen > pkt_end) {
10222110950bSHans Petter Selasky DPRINTF("Data position reached end\n");
1023da089c14SMark Johnston break;
1024da089c14SMark Johnston }
1025561227daSHans Petter Selasky
1026a5d82655SPyun YongHyeon if (AXGE_RX_ERR(pkt_hdr.status) != 0) {
10272110950bSHans Petter Selasky DPRINTF("Dropped a packet\n");
1028ecc70d3fSGleb Smirnoff if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1);
1029a5d82655SPyun YongHyeon } else
1030a5d82655SPyun YongHyeon axge_rxeof(ue, pc, pos, pktlen, pkt_hdr.status);
1031561227daSHans Petter Selasky pos += (pktlen + 7) & ~7;
1032da089c14SMark Johnston }
1033da089c14SMark Johnston }
1034da089c14SMark Johnston
10352110950bSHans Petter Selasky static void
axge_rxeof(struct usb_ether * ue,struct usb_page_cache * pc,unsigned offset,unsigned len,uint32_t status)103662d42655SHans Petter Selasky axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned offset,
103762d42655SHans Petter Selasky unsigned len, uint32_t status)
1038da089c14SMark Johnston {
1039935b194dSJustin Hibbits if_t ifp;
1040da089c14SMark Johnston struct mbuf *m;
1041da089c14SMark Johnston
1042da089c14SMark Johnston ifp = ue->ue_ifp;
1043da089c14SMark Johnston if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
1044ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
10452110950bSHans Petter Selasky return;
1046da089c14SMark Johnston }
1047da089c14SMark Johnston
1048a5d82655SPyun YongHyeon if (len > MHLEN - ETHER_ALIGN)
1049da089c14SMark Johnston m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1050a5d82655SPyun YongHyeon else
1051a5d82655SPyun YongHyeon m = m_gethdr(M_NOWAIT, MT_DATA);
1052da089c14SMark Johnston if (m == NULL) {
1053ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
10542110950bSHans Petter Selasky return;
1055da089c14SMark Johnston }
10562110950bSHans Petter Selasky m->m_pkthdr.rcvif = ifp;
1057a5d82655SPyun YongHyeon m->m_len = m->m_pkthdr.len = len;
1058a5d82655SPyun YongHyeon m->m_data += ETHER_ALIGN;
1059da089c14SMark Johnston
1060da089c14SMark Johnston usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
1061da089c14SMark Johnston
1062935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) {
1063a5d82655SPyun YongHyeon if ((status & AXGE_RX_L3_CSUM_ERR) == 0 &&
1064a5d82655SPyun YongHyeon (status & AXGE_RX_L3_TYPE_MASK) == AXGE_RX_L3_TYPE_IPV4)
1065a5d82655SPyun YongHyeon m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
1066a5d82655SPyun YongHyeon CSUM_IP_VALID;
1067a5d82655SPyun YongHyeon if ((status & AXGE_RX_L4_CSUM_ERR) == 0 &&
1068a5d82655SPyun YongHyeon ((status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_UDP ||
1069a5d82655SPyun YongHyeon (status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_TCP)) {
1070d32048bbSKevin Lo m->m_pkthdr.csum_flags |= CSUM_DATA_VALID |
1071a5d82655SPyun YongHyeon CSUM_PSEUDO_HDR;
1072da089c14SMark Johnston m->m_pkthdr.csum_data = 0xffff;
1073da089c14SMark Johnston }
1074da089c14SMark Johnston }
1075a5d82655SPyun YongHyeon if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
1076948d799eSHans Petter Selasky
107735d3dd8bSGleb Smirnoff (void)mbufq_enqueue(&ue->ue_rxq, m);
1078da089c14SMark Johnston }
1079da089c14SMark Johnston
1080da089c14SMark Johnston static void
axge_csum_cfg(struct usb_ether * ue)1081da089c14SMark Johnston axge_csum_cfg(struct usb_ether *ue)
1082da089c14SMark Johnston {
1083da089c14SMark Johnston struct axge_softc *sc;
1084935b194dSJustin Hibbits if_t ifp;
1085da089c14SMark Johnston uint8_t csum;
1086da089c14SMark Johnston
1087da089c14SMark Johnston sc = uether_getsc(ue);
1088da089c14SMark Johnston AXGE_LOCK_ASSERT(sc, MA_OWNED);
1089da089c14SMark Johnston ifp = uether_getifp(ue);
1090da089c14SMark Johnston
1091da089c14SMark Johnston csum = 0;
1092935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
1093d32048bbSKevin Lo csum |= CTCR_IP | CTCR_TCP | CTCR_UDP;
1094d32048bbSKevin Lo axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CTCR, csum);
1095da089c14SMark Johnston
1096da089c14SMark Johnston csum = 0;
1097935b194dSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0)
1098d32048bbSKevin Lo csum |= CRCR_IP | CRCR_TCP | CRCR_UDP;
1099d32048bbSKevin Lo axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CRCR, csum);
1100da089c14SMark Johnston }
1101