xref: /freebsd/sys/dev/usb/net/if_axge.c (revision 6962da914dd511349b219241e92b32329be76fc6)
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 #include <sys/cdefs.h>
30da089c14SMark Johnston /*
31*6962da91SDamien Broka  * ASIX Electronics AX88178A/AX88179/AX88179A USB 2.0/3.0 gigabit ethernet
32*6962da91SDamien Broka  * driver.
33da089c14SMark Johnston  */
34da089c14SMark Johnston 
35da089c14SMark Johnston #include <sys/param.h>
36da089c14SMark Johnston #include <sys/systm.h>
37da089c14SMark Johnston #include <sys/bus.h>
38da089c14SMark Johnston #include <sys/condvar.h>
397c10cf8cSPyun YongHyeon #include <sys/endian.h>
40da089c14SMark Johnston #include <sys/kernel.h>
41da089c14SMark Johnston #include <sys/lock.h>
42da089c14SMark Johnston #include <sys/module.h>
43da089c14SMark Johnston #include <sys/mutex.h>
44da089c14SMark Johnston #include <sys/socket.h>
45da089c14SMark Johnston #include <sys/sysctl.h>
46da089c14SMark Johnston #include <sys/unistd.h>
47da089c14SMark Johnston 
48da089c14SMark Johnston #include <net/if.h>
49da089c14SMark Johnston #include <net/if_var.h>
5031c484adSJustin Hibbits #include <net/if_media.h>
5131c484adSJustin Hibbits 
5231c484adSJustin Hibbits #include <dev/mii/mii.h>
5331c484adSJustin Hibbits #include <dev/mii/miivar.h>
54da089c14SMark Johnston 
55da089c14SMark Johnston #include <dev/usb/usb.h>
56da089c14SMark Johnston #include <dev/usb/usbdi.h>
57da089c14SMark Johnston #include <dev/usb/usbdi_util.h>
58da089c14SMark Johnston #include "usbdevs.h"
59da089c14SMark Johnston 
60da089c14SMark Johnston #define	USB_DEBUG_VAR 	axge_debug
61da089c14SMark Johnston #include <dev/usb/usb_debug.h>
62da089c14SMark Johnston #include <dev/usb/usb_process.h>
63da089c14SMark Johnston 
64da089c14SMark Johnston #include <dev/usb/net/usb_ethernet.h>
65da089c14SMark Johnston #include <dev/usb/net/if_axgereg.h>
66da089c14SMark Johnston 
6731c484adSJustin Hibbits #include "miibus_if.h"
6831c484adSJustin Hibbits 
69da089c14SMark Johnston /*
70da089c14SMark Johnston  * Various supported device vendors/products.
71da089c14SMark Johnston  */
72da089c14SMark Johnston 
73da089c14SMark Johnston static const STRUCT_USB_HOST_ID axge_devs[] = {
74*6962da91SDamien Broka #define	AXGE_DEV(v,p,i,...)	\
75*6962da91SDamien Broka 	{ USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), __VA_ARGS__ }
76*6962da91SDamien Broka 	AXGE_DEV(ASIX, AX88178A, AXGE_FLAG_178A),
77*6962da91SDamien Broka 	AXGE_DEV(ASIX, AX88179, AXGE_FLAG_179, USB_DEV_BCD_LTEQ(0x0100)),
78*6962da91SDamien Broka 	AXGE_DEV(ASIX, AX88179, AXGE_FLAG_179A, USB_DEV_BCD_GTEQ(0x0200)),
79*6962da91SDamien Broka 	AXGE_DEV(BELKIN, B2B128, AXGE_FLAG_179),
80*6962da91SDamien Broka 	AXGE_DEV(DLINK, DUB1312, AXGE_FLAG_179),
81*6962da91SDamien Broka 	AXGE_DEV(LENOVO, GIGALAN, AXGE_FLAG_179),
82*6962da91SDamien Broka 	AXGE_DEV(SITECOMEU, LN032, AXGE_FLAG_179),
83da089c14SMark Johnston #undef AXGE_DEV
84da089c14SMark Johnston };
85da089c14SMark Johnston 
86da089c14SMark Johnston static const struct {
8761c067adSKevin Lo 	uint8_t	ctrl;
8861c067adSKevin Lo 	uint8_t timer_l;
8961c067adSKevin Lo 	uint8_t	timer_h;
9061c067adSKevin Lo 	uint8_t	size;
9161c067adSKevin Lo 	uint8_t	ifg;
92948d799eSHans Petter Selasky } __packed axge_bulk_size[] = {
9361c067adSKevin Lo 	{ 7, 0x4f, 0x00, 0x12, 0xff },
9461c067adSKevin Lo 	{ 7, 0x20, 0x03, 0x16, 0xff },
9561c067adSKevin Lo 	{ 7, 0xae, 0x07, 0x18, 0xff },
9661c067adSKevin Lo 	{ 7, 0xcc, 0x4c, 0x18, 0x08 }
97da089c14SMark Johnston };
98da089c14SMark Johnston 
99da089c14SMark Johnston /* prototypes */
100da089c14SMark Johnston 
101da089c14SMark Johnston static device_probe_t axge_probe;
102da089c14SMark Johnston static device_attach_t axge_attach;
103da089c14SMark Johnston static device_detach_t axge_detach;
104da089c14SMark Johnston 
105da089c14SMark Johnston static usb_callback_t axge_bulk_read_callback;
106da089c14SMark Johnston static usb_callback_t axge_bulk_write_callback;
107da089c14SMark Johnston 
108da089c14SMark Johnston static miibus_readreg_t axge_miibus_readreg;
109da089c14SMark Johnston static miibus_writereg_t axge_miibus_writereg;
110da089c14SMark Johnston static miibus_statchg_t axge_miibus_statchg;
111da089c14SMark Johnston 
112da089c14SMark Johnston static uether_fn_t axge_attach_post;
113da089c14SMark Johnston static uether_fn_t axge_init;
114da089c14SMark Johnston static uether_fn_t axge_stop;
115da089c14SMark Johnston static uether_fn_t axge_start;
116da089c14SMark Johnston static uether_fn_t axge_tick;
117a5d82655SPyun YongHyeon static uether_fn_t axge_rxfilter;
118da089c14SMark Johnston 
119da089c14SMark Johnston static int	axge_read_mem(struct axge_softc *, uint8_t, uint16_t,
120da089c14SMark Johnston 		    uint16_t, void *, int);
121da089c14SMark Johnston static void	axge_write_mem(struct axge_softc *, uint8_t, uint16_t,
122da089c14SMark Johnston 		    uint16_t, void *, int);
123d32048bbSKevin Lo static uint8_t	axge_read_cmd_1(struct axge_softc *, uint8_t, uint16_t);
124da089c14SMark Johnston static uint16_t	axge_read_cmd_2(struct axge_softc *, uint8_t, uint16_t,
125da089c14SMark Johnston 		    uint16_t);
126da089c14SMark Johnston static void	axge_write_cmd_1(struct axge_softc *, uint8_t, uint16_t,
127d32048bbSKevin Lo 		    uint8_t);
128da089c14SMark Johnston static void	axge_write_cmd_2(struct axge_softc *, uint8_t, uint16_t,
129da089c14SMark Johnston 		    uint16_t, uint16_t);
130da089c14SMark Johnston static void	axge_chip_init(struct axge_softc *);
131da089c14SMark Johnston static void	axge_reset(struct axge_softc *);
132da089c14SMark Johnston 
133da089c14SMark Johnston static int	axge_attach_post_sub(struct usb_ether *);
134935b194dSJustin Hibbits static int	axge_ifmedia_upd(if_t);
135935b194dSJustin Hibbits static void	axge_ifmedia_sts(if_t, struct ifmediareq *);
136935b194dSJustin Hibbits static int	axge_ioctl(if_t, u_long, caddr_t);
1372110950bSHans Petter Selasky static void	axge_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
1382110950bSHans Petter Selasky static void	axge_rxeof(struct usb_ether *, struct usb_page_cache *,
13962d42655SHans Petter Selasky 		    unsigned, unsigned, uint32_t);
140da089c14SMark Johnston static void	axge_csum_cfg(struct usb_ether *);
141da089c14SMark Johnston 
142da089c14SMark Johnston #define	AXGE_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
143da089c14SMark Johnston 
144da089c14SMark Johnston #ifdef USB_DEBUG
145da089c14SMark Johnston static int axge_debug = 0;
146da089c14SMark Johnston 
147f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, axge, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
148f8d2b1f3SPawel Biernacki     "USB axge");
149ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RWTUN, &axge_debug, 0,
150da089c14SMark Johnston     "Debug level");
151da089c14SMark Johnston #endif
152da089c14SMark Johnston 
153da089c14SMark Johnston static const struct usb_config axge_config[AXGE_N_TRANSFER] = {
154da089c14SMark Johnston 	[AXGE_BULK_DT_WR] = {
155da089c14SMark Johnston 		.type = UE_BULK,
156da089c14SMark Johnston 		.endpoint = UE_ADDR_ANY,
157da089c14SMark Johnston 		.direction = UE_DIR_OUT,
1587c10cf8cSPyun YongHyeon 		.frames = AXGE_N_FRAMES,
1597c10cf8cSPyun YongHyeon 		.bufsize = AXGE_N_FRAMES * MCLBYTES,
160da089c14SMark Johnston 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
161da089c14SMark Johnston 		.callback = axge_bulk_write_callback,
162da089c14SMark Johnston 		.timeout = 10000,	/* 10 seconds */
163da089c14SMark Johnston 	},
164da089c14SMark Johnston 	[AXGE_BULK_DT_RD] = {
165da089c14SMark Johnston 		.type = UE_BULK,
166da089c14SMark Johnston 		.endpoint = UE_ADDR_ANY,
167da089c14SMark Johnston 		.direction = UE_DIR_IN,
168948d799eSHans Petter Selasky 		.bufsize = 65536,
169da089c14SMark Johnston 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
170da089c14SMark Johnston 		.callback = axge_bulk_read_callback,
171da089c14SMark Johnston 		.timeout = 0,		/* no timeout */
172da089c14SMark Johnston 	},
173da089c14SMark Johnston };
174da089c14SMark Johnston 
175da089c14SMark Johnston static device_method_t axge_methods[] = {
176da089c14SMark Johnston 	/* Device interface. */
177da089c14SMark Johnston 	DEVMETHOD(device_probe,		axge_probe),
178da089c14SMark Johnston 	DEVMETHOD(device_attach,	axge_attach),
179da089c14SMark Johnston 	DEVMETHOD(device_detach,	axge_detach),
180da089c14SMark Johnston 
181da089c14SMark Johnston 	/* MII interface. */
182da089c14SMark Johnston 	DEVMETHOD(miibus_readreg,	axge_miibus_readreg),
183da089c14SMark Johnston 	DEVMETHOD(miibus_writereg,	axge_miibus_writereg),
184da089c14SMark Johnston 	DEVMETHOD(miibus_statchg,	axge_miibus_statchg),
185da089c14SMark Johnston 
186da089c14SMark Johnston 	DEVMETHOD_END
187da089c14SMark Johnston };
188da089c14SMark Johnston 
189da089c14SMark Johnston static driver_t axge_driver = {
190da089c14SMark Johnston 	.name = "axge",
191da089c14SMark Johnston 	.methods = axge_methods,
192da089c14SMark Johnston 	.size = sizeof(struct axge_softc),
193da089c14SMark Johnston };
194da089c14SMark Johnston 
195bc9372d7SJohn Baldwin DRIVER_MODULE(axge, uhub, axge_driver, NULL, NULL);
1963e38757dSJohn Baldwin DRIVER_MODULE(miibus, axge, miibus_driver, NULL, NULL);
197da089c14SMark Johnston MODULE_DEPEND(axge, uether, 1, 1, 1);
198da089c14SMark Johnston MODULE_DEPEND(axge, usb, 1, 1, 1);
199da089c14SMark Johnston MODULE_DEPEND(axge, ether, 1, 1, 1);
200da089c14SMark Johnston MODULE_DEPEND(axge, miibus, 1, 1, 1);
201da089c14SMark Johnston MODULE_VERSION(axge, 1);
202f809f280SWarner Losh USB_PNP_HOST_INFO(axge_devs);
203da089c14SMark Johnston 
204da089c14SMark Johnston static const struct usb_ether_methods axge_ue_methods = {
205da089c14SMark Johnston 	.ue_attach_post = axge_attach_post,
206da089c14SMark Johnston 	.ue_attach_post_sub = axge_attach_post_sub,
207da089c14SMark Johnston 	.ue_start = axge_start,
208da089c14SMark Johnston 	.ue_init = axge_init,
209da089c14SMark Johnston 	.ue_stop = axge_stop,
210da089c14SMark Johnston 	.ue_tick = axge_tick,
211a5d82655SPyun YongHyeon 	.ue_setmulti = axge_rxfilter,
212a5d82655SPyun YongHyeon 	.ue_setpromisc = axge_rxfilter,
213da089c14SMark Johnston 	.ue_mii_upd = axge_ifmedia_upd,
214da089c14SMark Johnston 	.ue_mii_sts = axge_ifmedia_sts,
215da089c14SMark Johnston };
216da089c14SMark Johnston 
217da089c14SMark Johnston static int
axge_read_mem(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t val,void * buf,int len)218da089c14SMark Johnston axge_read_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
219da089c14SMark Johnston     uint16_t val, void *buf, int len)
220da089c14SMark Johnston {
221da089c14SMark Johnston 	struct usb_device_request req;
222da089c14SMark Johnston 
223da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
224da089c14SMark Johnston 
225da089c14SMark Johnston 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
226da089c14SMark Johnston 	req.bRequest = cmd;
227da089c14SMark Johnston 	USETW(req.wValue, val);
228da089c14SMark Johnston 	USETW(req.wIndex, index);
229da089c14SMark Johnston 	USETW(req.wLength, len);
230da089c14SMark Johnston 
231da089c14SMark Johnston 	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
232da089c14SMark Johnston }
233da089c14SMark Johnston 
234da089c14SMark Johnston static void
axge_write_mem(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t val,void * buf,int len)235da089c14SMark Johnston axge_write_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
236da089c14SMark Johnston     uint16_t val, void *buf, int len)
237da089c14SMark Johnston {
238da089c14SMark Johnston 	struct usb_device_request req;
239da089c14SMark Johnston 
240da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
241da089c14SMark Johnston 
242da089c14SMark Johnston 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
243da089c14SMark Johnston 	req.bRequest = cmd;
244da089c14SMark Johnston 	USETW(req.wValue, val);
245da089c14SMark Johnston 	USETW(req.wIndex, index);
246da089c14SMark Johnston 	USETW(req.wLength, len);
247da089c14SMark Johnston 
248da089c14SMark Johnston 	if (uether_do_request(&sc->sc_ue, &req, buf, 1000)) {
249da089c14SMark Johnston 		/* Error ignored. */
250da089c14SMark Johnston 	}
251da089c14SMark Johnston }
252da089c14SMark Johnston 
25361c067adSKevin Lo static uint8_t
axge_read_cmd_1(struct axge_softc * sc,uint8_t cmd,uint16_t reg)254d32048bbSKevin Lo axge_read_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg)
25561c067adSKevin Lo {
25661c067adSKevin Lo 	uint8_t val;
25761c067adSKevin Lo 
258d32048bbSKevin Lo 	axge_read_mem(sc, cmd, 1, reg, &val, 1);
25961c067adSKevin Lo 	return (val);
26061c067adSKevin Lo }
26161c067adSKevin Lo 
262da089c14SMark Johnston static uint16_t
axge_read_cmd_2(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t reg)263da089c14SMark Johnston axge_read_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
264da089c14SMark Johnston     uint16_t reg)
265da089c14SMark Johnston {
266da089c14SMark Johnston 	uint8_t val[2];
267da089c14SMark Johnston 
268da089c14SMark Johnston 	axge_read_mem(sc, cmd, index, reg, &val, 2);
269da089c14SMark Johnston 	return (UGETW(val));
270da089c14SMark Johnston }
271da089c14SMark Johnston 
272da089c14SMark Johnston static void
axge_write_cmd_1(struct axge_softc * sc,uint8_t cmd,uint16_t reg,uint8_t val)273d32048bbSKevin Lo axge_write_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg, uint8_t val)
274da089c14SMark Johnston {
275d32048bbSKevin Lo 	axge_write_mem(sc, cmd, 1, reg, &val, 1);
276da089c14SMark Johnston }
277da089c14SMark Johnston 
278da089c14SMark Johnston static void
axge_write_cmd_2(struct axge_softc * sc,uint8_t cmd,uint16_t index,uint16_t reg,uint16_t val)279da089c14SMark Johnston axge_write_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
280da089c14SMark Johnston     uint16_t reg, uint16_t val)
281da089c14SMark Johnston {
282da089c14SMark Johnston 	uint8_t temp[2];
283da089c14SMark Johnston 
284da089c14SMark Johnston 	USETW(temp, val);
285da089c14SMark Johnston 	axge_write_mem(sc, cmd, index, reg, &temp, 2);
286da089c14SMark Johnston }
287da089c14SMark Johnston 
288da089c14SMark Johnston static int
axge_miibus_readreg(device_t dev,int phy,int reg)289da089c14SMark Johnston axge_miibus_readreg(device_t dev, int phy, int reg)
290da089c14SMark Johnston {
291da089c14SMark Johnston 	struct axge_softc *sc;
292da089c14SMark Johnston 	uint16_t val;
293da089c14SMark Johnston 	int locked;
294da089c14SMark Johnston 
295da089c14SMark Johnston 	sc = device_get_softc(dev);
296da089c14SMark Johnston 	locked = mtx_owned(&sc->sc_mtx);
297da089c14SMark Johnston 	if (!locked)
298da089c14SMark Johnston 		AXGE_LOCK(sc);
299da089c14SMark Johnston 
300da089c14SMark Johnston 	val = axge_read_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy);
301da089c14SMark Johnston 
302da089c14SMark Johnston 	if (!locked)
303da089c14SMark Johnston 		AXGE_UNLOCK(sc);
304da089c14SMark Johnston 
305da089c14SMark Johnston 	return (val);
306da089c14SMark Johnston }
307da089c14SMark Johnston 
308da089c14SMark Johnston static int
axge_miibus_writereg(device_t dev,int phy,int reg,int val)309da089c14SMark Johnston axge_miibus_writereg(device_t dev, int phy, int reg, int val)
310da089c14SMark Johnston {
311da089c14SMark Johnston 	struct axge_softc *sc;
312da089c14SMark Johnston 	int locked;
313da089c14SMark Johnston 
314da089c14SMark Johnston 	sc = device_get_softc(dev);
315da089c14SMark Johnston 	locked = mtx_owned(&sc->sc_mtx);
316da089c14SMark Johnston 	if (!locked)
317da089c14SMark Johnston 		AXGE_LOCK(sc);
318da089c14SMark Johnston 
319da089c14SMark Johnston 	axge_write_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy, val);
320da089c14SMark Johnston 
321da089c14SMark Johnston 	if (!locked)
322da089c14SMark Johnston 		AXGE_UNLOCK(sc);
323da089c14SMark Johnston 
324da089c14SMark Johnston 	return (0);
325da089c14SMark Johnston }
326da089c14SMark Johnston 
327da089c14SMark Johnston static void
axge_miibus_statchg(device_t dev)328da089c14SMark Johnston axge_miibus_statchg(device_t dev)
329da089c14SMark Johnston {
330da089c14SMark Johnston 	struct axge_softc *sc;
331da089c14SMark Johnston 	struct mii_data *mii;
332935b194dSJustin Hibbits 	if_t ifp;
33361c067adSKevin Lo 	uint8_t link_status, tmp[5];
334da089c14SMark Johnston 	uint16_t val;
335da089c14SMark Johnston 	int locked;
336da089c14SMark Johnston 
337da089c14SMark Johnston 	sc = device_get_softc(dev);
338da089c14SMark Johnston 	mii = GET_MII(sc);
339da089c14SMark Johnston 	locked = mtx_owned(&sc->sc_mtx);
340da089c14SMark Johnston 	if (!locked)
341da089c14SMark Johnston 		AXGE_LOCK(sc);
342da089c14SMark Johnston 
343da089c14SMark Johnston 	ifp = uether_getifp(&sc->sc_ue);
344da089c14SMark Johnston 	if (mii == NULL || ifp == NULL ||
345935b194dSJustin Hibbits 	    (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
346da089c14SMark Johnston 		goto done;
347da089c14SMark Johnston 
348da089c14SMark Johnston 	sc->sc_flags &= ~AXGE_FLAG_LINK;
349da089c14SMark Johnston 	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
350da089c14SMark Johnston 	    (IFM_ACTIVE | IFM_AVALID)) {
351da089c14SMark Johnston 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
352da089c14SMark Johnston 		case IFM_10_T:
353da089c14SMark Johnston 		case IFM_100_TX:
354da089c14SMark Johnston 		case IFM_1000_T:
355da089c14SMark Johnston 			sc->sc_flags |= AXGE_FLAG_LINK;
356da089c14SMark Johnston 			break;
357da089c14SMark Johnston 		default:
358da089c14SMark Johnston 			break;
359da089c14SMark Johnston 		}
360da089c14SMark Johnston 	}
361da089c14SMark Johnston 
362da089c14SMark Johnston 	/* Lost link, do nothing. */
363da089c14SMark Johnston 	if ((sc->sc_flags & AXGE_FLAG_LINK) == 0)
364da089c14SMark Johnston 		goto done;
365da089c14SMark Johnston 
366d32048bbSKevin Lo 	link_status = axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PLSR);
36761c067adSKevin Lo 
368da089c14SMark Johnston 	val = 0;
369da089c14SMark Johnston 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
370d32048bbSKevin Lo 		val |= MSR_FD;
371da089c14SMark Johnston 		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
372d32048bbSKevin Lo 			val |= MSR_TFC;
373da089c14SMark Johnston 		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
374d32048bbSKevin Lo 			val |= MSR_RFC;
375da089c14SMark Johnston 	}
376d32048bbSKevin Lo 	val |=  MSR_RE;
377da089c14SMark Johnston 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
378da089c14SMark Johnston 	case IFM_1000_T:
379d32048bbSKevin Lo 		val |= MSR_GM | MSR_EN_125MHZ;
380d32048bbSKevin Lo 		if (link_status & PLSR_USB_SS)
38161c067adSKevin Lo 			memcpy(tmp, &axge_bulk_size[0], 5);
382d32048bbSKevin Lo 		else if (link_status & PLSR_USB_HS)
38361c067adSKevin Lo 			memcpy(tmp, &axge_bulk_size[1], 5);
38461c067adSKevin Lo 		else
38561c067adSKevin Lo 			memcpy(tmp, &axge_bulk_size[3], 5);
38661c067adSKevin Lo 		break;
387da089c14SMark Johnston 	case IFM_100_TX:
388d32048bbSKevin Lo 		val |= MSR_PS;
389d32048bbSKevin Lo 		if (link_status & (PLSR_USB_SS | PLSR_USB_HS))
39061c067adSKevin Lo 			memcpy(tmp, &axge_bulk_size[2], 5);
39161c067adSKevin Lo 		else
39261c067adSKevin Lo 			memcpy(tmp, &axge_bulk_size[3], 5);
39361c067adSKevin Lo 		break;
394da089c14SMark Johnston 	case IFM_10_T:
39561c067adSKevin Lo 		memcpy(tmp, &axge_bulk_size[3], 5);
396da089c14SMark Johnston 		break;
397da089c14SMark Johnston 	}
39861c067adSKevin Lo 	/* Rx bulk configuration. */
39961c067adSKevin Lo 	axge_write_mem(sc, AXGE_ACCESS_MAC, 5, AXGE_RX_BULKIN_QCTRL, tmp, 5);
400d32048bbSKevin Lo 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
401da089c14SMark Johnston done:
402da089c14SMark Johnston 	if (!locked)
403da089c14SMark Johnston 		AXGE_UNLOCK(sc);
404da089c14SMark Johnston }
405da089c14SMark Johnston 
406da089c14SMark Johnston static void
axge_chip_init(struct axge_softc * sc)407da089c14SMark Johnston axge_chip_init(struct axge_softc *sc)
408da089c14SMark Johnston {
409da089c14SMark Johnston 	/* Power up ethernet PHY. */
410d32048bbSKevin Lo 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, 0);
411d32048bbSKevin Lo 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, EPPRCR_IPRL);
412da089c14SMark Johnston 	uether_pause(&sc->sc_ue, hz / 4);
413d32048bbSKevin Lo 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT,
414da089c14SMark Johnston 	    AXGE_CLK_SELECT_ACS | AXGE_CLK_SELECT_BCS);
415da089c14SMark Johnston 	uether_pause(&sc->sc_ue, hz / 10);
416*6962da91SDamien Broka 
417*6962da91SDamien Broka 	if ((sc->sc_flags & AXGE_FLAG_179A) != 0) {
418*6962da91SDamien Broka 		/*
419*6962da91SDamien Broka 		 * 179A chip has two firmware modes that each use different
420*6962da91SDamien Broka 		 * transfer layouts for Ethernet over USB. The newer fw mode has
421*6962da91SDamien Broka 		 * larger rx packet headers which seem to
422*6962da91SDamien Broka 		 * accomodate for ethernet frames up to 9K length and a VLAN
423*6962da91SDamien Broka 		 * field for hardware tagging, but is not backward compatible
424*6962da91SDamien Broka 		 * with 178A/179 bulk transfer code due to the change in size
425*6962da91SDamien Broka 		 * and field alignments. The other fw mode uses the same packet
426*6962da91SDamien Broka 		 * headers as the older 178A/179 chips, which this driver uses.
427*6962da91SDamien Broka 		 *
428*6962da91SDamien Broka 		 * As we do not currently have VLAN hw tagging or jumbo support
429*6962da91SDamien Broka 		 * in this driver anyway, we're ok forcing 179A into its compat
430*6962da91SDamien Broka 		 * mode by default.
431*6962da91SDamien Broka 		 */
432*6962da91SDamien Broka 		axge_write_cmd_1(sc, AXGE_FW_MODE, AXGE_FW_MODE_178A179, 0);
433*6962da91SDamien Broka 	}
434da089c14SMark Johnston }
435da089c14SMark Johnston 
436da089c14SMark Johnston static void
axge_reset(struct axge_softc * sc)437da089c14SMark Johnston axge_reset(struct axge_softc *sc)
438da089c14SMark Johnston {
439da089c14SMark Johnston 	struct usb_config_descriptor *cd;
440da089c14SMark Johnston 	usb_error_t err;
441da089c14SMark Johnston 
442da089c14SMark Johnston 	cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
443da089c14SMark Johnston 
444da089c14SMark Johnston 	err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
445da089c14SMark Johnston 	    cd->bConfigurationValue);
446da089c14SMark Johnston 	if (err)
447da089c14SMark Johnston 		DPRINTF("reset failed (ignored)\n");
448da089c14SMark Johnston 
449da089c14SMark Johnston 	/* Wait a little while for the chip to get its brains in order. */
450da089c14SMark Johnston 	uether_pause(&sc->sc_ue, hz / 100);
451da089c14SMark Johnston 
452da089c14SMark Johnston 	/* Reinitialize controller to achieve full reset. */
453da089c14SMark Johnston 	axge_chip_init(sc);
454da089c14SMark Johnston }
455da089c14SMark Johnston 
456da089c14SMark Johnston static void
axge_attach_post(struct usb_ether * ue)457da089c14SMark Johnston axge_attach_post(struct usb_ether *ue)
458da089c14SMark Johnston {
459da089c14SMark Johnston 	struct axge_softc *sc;
460da089c14SMark Johnston 
461da089c14SMark Johnston 	sc = uether_getsc(ue);
462da089c14SMark Johnston 
463da089c14SMark Johnston 	/* Initialize controller and get station address. */
464da089c14SMark Johnston 	axge_chip_init(sc);
465d32048bbSKevin Lo 	axge_read_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
466da089c14SMark Johnston 	    ue->ue_eaddr, ETHER_ADDR_LEN);
467da089c14SMark Johnston }
468da089c14SMark Johnston 
469da089c14SMark Johnston static int
axge_attach_post_sub(struct usb_ether * ue)470da089c14SMark Johnston axge_attach_post_sub(struct usb_ether *ue)
471da089c14SMark Johnston {
472935b194dSJustin Hibbits 	if_t ifp;
473da089c14SMark Johnston 	int error;
474da089c14SMark Johnston 
475da089c14SMark Johnston 	ifp = ue->ue_ifp;
476935b194dSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
477935b194dSJustin Hibbits 	if_setstartfn(ifp, uether_start);
478935b194dSJustin Hibbits 	if_setioctlfn(ifp, axge_ioctl);
479935b194dSJustin Hibbits 	if_setinitfn(ifp, uether_init);
480935b194dSJustin Hibbits 	if_setsendqlen(ifp, ifqmaxlen);
481935b194dSJustin Hibbits 	if_setsendqready(ifp);
482da089c14SMark Johnston 
483935b194dSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM, 0);
484935b194dSJustin Hibbits 	if_sethwassist(ifp, AXGE_CSUM_FEATURES);
485935b194dSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
486da089c14SMark Johnston 
487c6df6f53SWarner Losh 	bus_topo_lock();
488da089c14SMark Johnston 	error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
489da089c14SMark Johnston 	    uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
490a42c5d9fSPyun YongHyeon 	    BMSR_DEFCAPMASK, AXGE_PHY_ADDR, MII_OFFSET_ANY, MIIF_DOPAUSE);
491c6df6f53SWarner Losh 	bus_topo_unlock();
492da089c14SMark Johnston 
493da089c14SMark Johnston 	return (error);
494da089c14SMark Johnston }
495da089c14SMark Johnston 
496da089c14SMark Johnston /*
497da089c14SMark Johnston  * Set media options.
498da089c14SMark Johnston  */
499da089c14SMark Johnston static int
axge_ifmedia_upd(if_t ifp)500935b194dSJustin Hibbits axge_ifmedia_upd(if_t ifp)
501da089c14SMark Johnston {
502da089c14SMark Johnston 	struct axge_softc *sc;
503da089c14SMark Johnston 	struct mii_data *mii;
504da089c14SMark Johnston 	struct mii_softc *miisc;
505da089c14SMark Johnston 	int error;
506da089c14SMark Johnston 
507935b194dSJustin Hibbits 	sc = if_getsoftc(ifp);
508da089c14SMark Johnston 	mii = GET_MII(sc);
509da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
510da089c14SMark Johnston 
511da089c14SMark Johnston 	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
512da089c14SMark Johnston 	    PHY_RESET(miisc);
513da089c14SMark Johnston 	error = mii_mediachg(mii);
514da089c14SMark Johnston 
515da089c14SMark Johnston 	return (error);
516da089c14SMark Johnston }
517da089c14SMark Johnston 
518da089c14SMark Johnston /*
519da089c14SMark Johnston  * Report current media status.
520da089c14SMark Johnston  */
521da089c14SMark Johnston static void
axge_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)522935b194dSJustin Hibbits axge_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
523da089c14SMark Johnston {
524da089c14SMark Johnston 	struct axge_softc *sc;
525da089c14SMark Johnston 	struct mii_data *mii;
526da089c14SMark Johnston 
527935b194dSJustin Hibbits 	sc = if_getsoftc(ifp);
528da089c14SMark Johnston 	mii = GET_MII(sc);
529da089c14SMark Johnston 	AXGE_LOCK(sc);
530da089c14SMark Johnston 	mii_pollstat(mii);
531da089c14SMark Johnston 	ifmr->ifm_active = mii->mii_media_active;
532da089c14SMark Johnston 	ifmr->ifm_status = mii->mii_media_status;
533da089c14SMark Johnston 	AXGE_UNLOCK(sc);
534da089c14SMark Johnston }
535da089c14SMark Johnston 
536da089c14SMark Johnston /*
537da089c14SMark Johnston  * Probe for a AX88179 chip.
538da089c14SMark Johnston  */
539da089c14SMark Johnston static int
axge_probe(device_t dev)540da089c14SMark Johnston axge_probe(device_t dev)
541da089c14SMark Johnston {
542da089c14SMark Johnston 	struct usb_attach_arg *uaa;
543da089c14SMark Johnston 
544da089c14SMark Johnston 	uaa = device_get_ivars(dev);
545da089c14SMark Johnston 	if (uaa->usb_mode != USB_MODE_HOST)
546da089c14SMark Johnston 		return (ENXIO);
547da089c14SMark Johnston 	if (uaa->info.bConfigIndex != AXGE_CONFIG_IDX)
548da089c14SMark Johnston 		return (ENXIO);
549da089c14SMark Johnston 	if (uaa->info.bIfaceIndex != AXGE_IFACE_IDX)
550da089c14SMark Johnston 		return (ENXIO);
551da089c14SMark Johnston 
552da089c14SMark Johnston 	return (usbd_lookup_id_by_uaa(axge_devs, sizeof(axge_devs), uaa));
553da089c14SMark Johnston }
554da089c14SMark Johnston 
555da089c14SMark Johnston /*
556da089c14SMark Johnston  * Attach the interface. Allocate softc structures, do ifmedia
557da089c14SMark Johnston  * setup and ethernet/BPF attach.
558da089c14SMark Johnston  */
559da089c14SMark Johnston static int
axge_attach(device_t dev)560da089c14SMark Johnston axge_attach(device_t dev)
561da089c14SMark Johnston {
562da089c14SMark Johnston 	struct usb_attach_arg *uaa;
563da089c14SMark Johnston 	struct axge_softc *sc;
564da089c14SMark Johnston 	struct usb_ether *ue;
565da089c14SMark Johnston 	uint8_t iface_index;
566da089c14SMark Johnston 	int error;
567da089c14SMark Johnston 
568da089c14SMark Johnston 	uaa = device_get_ivars(dev);
569da089c14SMark Johnston 	sc = device_get_softc(dev);
570da089c14SMark Johnston 	ue = &sc->sc_ue;
571da089c14SMark Johnston 
572da089c14SMark Johnston 	device_set_usb_desc(dev);
573da089c14SMark Johnston 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
574da089c14SMark Johnston 
575*6962da91SDamien Broka 	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
576*6962da91SDamien Broka 
577da089c14SMark Johnston 	iface_index = AXGE_IFACE_IDX;
578da089c14SMark Johnston 	error = usbd_transfer_setup(uaa->device, &iface_index,
579da089c14SMark Johnston 	    sc->sc_xfer, axge_config, AXGE_N_TRANSFER, sc, &sc->sc_mtx);
580da089c14SMark Johnston 	if (error) {
581da089c14SMark Johnston 		device_printf(dev, "allocating USB transfers failed\n");
5822948462dSPyun YongHyeon 		mtx_destroy(&sc->sc_mtx);
5832948462dSPyun YongHyeon 		return (ENXIO);
584da089c14SMark Johnston 	}
585da089c14SMark Johnston 
586da089c14SMark Johnston 	ue->ue_sc = sc;
587da089c14SMark Johnston 	ue->ue_dev = dev;
588da089c14SMark Johnston 	ue->ue_udev = uaa->device;
589da089c14SMark Johnston 	ue->ue_mtx = &sc->sc_mtx;
590da089c14SMark Johnston 	ue->ue_methods = &axge_ue_methods;
591da089c14SMark Johnston 
592da089c14SMark Johnston 	error = uether_ifattach(ue);
593da089c14SMark Johnston 	if (error) {
594da089c14SMark Johnston 		device_printf(dev, "could not attach interface\n");
595da089c14SMark Johnston 		goto detach;
596da089c14SMark Johnston 	}
597da089c14SMark Johnston 	return (0);			/* success */
598da089c14SMark Johnston 
599da089c14SMark Johnston detach:
600da089c14SMark Johnston 	axge_detach(dev);
601da089c14SMark Johnston 	return (ENXIO);			/* failure */
602da089c14SMark Johnston }
603da089c14SMark Johnston 
604da089c14SMark Johnston static int
axge_detach(device_t dev)605da089c14SMark Johnston axge_detach(device_t dev)
606da089c14SMark Johnston {
607da089c14SMark Johnston 	struct axge_softc *sc;
608da089c14SMark Johnston 	struct usb_ether *ue;
6099ab0e2a7SPyun YongHyeon 	uint16_t val;
610da089c14SMark Johnston 
611da089c14SMark Johnston 	sc = device_get_softc(dev);
612da089c14SMark Johnston 	ue = &sc->sc_ue;
6139ab0e2a7SPyun YongHyeon 	if (device_is_attached(dev)) {
6147ef4e76fSHans Petter Selasky 		/* wait for any post attach or other command to complete */
6157ef4e76fSHans Petter Selasky 		usb_proc_drain(&ue->ue_tq);
6167ef4e76fSHans Petter Selasky 
6179ab0e2a7SPyun YongHyeon 		AXGE_LOCK(sc);
6189ab0e2a7SPyun YongHyeon 		/*
6199ab0e2a7SPyun YongHyeon 		 * XXX
6209ab0e2a7SPyun YongHyeon 		 * ether_ifdetach(9) should be called first.
6219ab0e2a7SPyun YongHyeon 		 */
6229ab0e2a7SPyun YongHyeon 		axge_stop(ue);
6239ab0e2a7SPyun YongHyeon 		/* Force bulk-in to return a zero-length USB packet. */
6249ab0e2a7SPyun YongHyeon 		val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR);
6259ab0e2a7SPyun YongHyeon 		val |= EPPRCR_BZ | EPPRCR_IPRL;
6269ab0e2a7SPyun YongHyeon 		axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, val);
6279ab0e2a7SPyun YongHyeon 		/* Change clock. */
6289ab0e2a7SPyun YongHyeon 		axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT, 0);
6299ab0e2a7SPyun YongHyeon 		/* Disable MAC. */
6309ab0e2a7SPyun YongHyeon 		axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, 0);
6319ab0e2a7SPyun YongHyeon 		AXGE_UNLOCK(sc);
6329ab0e2a7SPyun YongHyeon 	}
633da089c14SMark Johnston 	usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER);
634da089c14SMark Johnston 	uether_ifdetach(ue);
635da089c14SMark Johnston 	mtx_destroy(&sc->sc_mtx);
636da089c14SMark Johnston 
637da089c14SMark Johnston 	return (0);
638da089c14SMark Johnston }
639da089c14SMark Johnston 
640da089c14SMark Johnston static void
axge_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)641da089c14SMark Johnston axge_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
642da089c14SMark Johnston {
643da089c14SMark Johnston 	struct axge_softc *sc;
644da089c14SMark Johnston 	struct usb_ether *ue;
645da089c14SMark Johnston 	struct usb_page_cache *pc;
646da089c14SMark Johnston 	int actlen;
647da089c14SMark Johnston 
648da089c14SMark Johnston 	sc = usbd_xfer_softc(xfer);
649da089c14SMark Johnston 	ue = &sc->sc_ue;
650da089c14SMark Johnston 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
651da089c14SMark Johnston 
652da089c14SMark Johnston 	switch (USB_GET_STATE(xfer)) {
653da089c14SMark Johnston 	case USB_ST_TRANSFERRED:
654da089c14SMark Johnston 		pc = usbd_xfer_get_frame(xfer, 0);
6552110950bSHans Petter Selasky 		axge_rx_frame(ue, pc, actlen);
656da089c14SMark Johnston 
657da089c14SMark Johnston 		/* FALLTHROUGH */
658da089c14SMark Johnston 	case USB_ST_SETUP:
659da089c14SMark Johnston tr_setup:
660da089c14SMark Johnston 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
661da089c14SMark Johnston 		usbd_transfer_submit(xfer);
662da089c14SMark Johnston 		uether_rxflush(ue);
663948d799eSHans Petter Selasky 		break;
664da089c14SMark Johnston 
665da089c14SMark Johnston 	default:
666da089c14SMark Johnston 		if (error != USB_ERR_CANCELLED) {
667da089c14SMark Johnston 			usbd_xfer_set_stall(xfer);
668da089c14SMark Johnston 			goto tr_setup;
669da089c14SMark Johnston 		}
670948d799eSHans Petter Selasky 		break;
671da089c14SMark Johnston 	}
672da089c14SMark Johnston }
673da089c14SMark Johnston 
674da089c14SMark Johnston static void
axge_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)675da089c14SMark Johnston axge_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
676da089c14SMark Johnston {
677da089c14SMark Johnston 	struct axge_softc *sc;
678935b194dSJustin Hibbits 	if_t ifp;
679da089c14SMark Johnston 	struct usb_page_cache *pc;
680da089c14SMark Johnston 	struct mbuf *m;
6817c10cf8cSPyun YongHyeon 	struct axge_frame_txhdr txhdr;
682d32048bbSKevin Lo 	int nframes, pos;
683da089c14SMark Johnston 
684da089c14SMark Johnston 	sc = usbd_xfer_softc(xfer);
685da089c14SMark Johnston 	ifp = uether_getifp(&sc->sc_ue);
686da089c14SMark Johnston 
687da089c14SMark Johnston 	switch (USB_GET_STATE(xfer)) {
688da089c14SMark Johnston 	case USB_ST_TRANSFERRED:
689935b194dSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
690da089c14SMark Johnston 		/* FALLTHROUGH */
691da089c14SMark Johnston 	case USB_ST_SETUP:
692da089c14SMark Johnston tr_setup:
693da089c14SMark Johnston 		if ((sc->sc_flags & AXGE_FLAG_LINK) == 0 ||
694935b194dSJustin Hibbits 		    (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) != 0) {
695da089c14SMark Johnston 			/*
696da089c14SMark Johnston 			 * Don't send anything if there is no link or
697da089c14SMark Johnston 			 * controller is busy.
698da089c14SMark Johnston 			 */
699da089c14SMark Johnston 			return;
700da089c14SMark Johnston 		}
701da089c14SMark Johnston 
7027c10cf8cSPyun YongHyeon 		for (nframes = 0; nframes < AXGE_N_FRAMES &&
703935b194dSJustin Hibbits 		    !if_sendq_empty(ifp); nframes++) {
704935b194dSJustin Hibbits 			m = if_dequeue(ifp);
705da089c14SMark Johnston 			if (m == NULL)
706da089c14SMark Johnston 				break;
707da089c14SMark Johnston 			usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
708da089c14SMark Johnston 			    nframes);
709da089c14SMark Johnston 			pc = usbd_xfer_get_frame(xfer, nframes);
7107c10cf8cSPyun YongHyeon 			txhdr.mss = 0;
7117c10cf8cSPyun YongHyeon 			txhdr.len = htole32(AXGE_TXBYTES(m->m_pkthdr.len));
712935b194dSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0 &&
7137c10cf8cSPyun YongHyeon 			    (m->m_pkthdr.csum_flags & AXGE_CSUM_FEATURES) == 0)
7147c10cf8cSPyun YongHyeon 				txhdr.len |= htole32(AXGE_CSUM_DISABLE);
7157c10cf8cSPyun YongHyeon 
7167c10cf8cSPyun YongHyeon 			pos = 0;
7177c10cf8cSPyun YongHyeon 			usbd_copy_in(pc, pos, &txhdr, sizeof(txhdr));
7187c10cf8cSPyun YongHyeon 			pos += sizeof(txhdr);
719d32048bbSKevin Lo 			usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
720d32048bbSKevin Lo 			pos += m->m_pkthdr.len;
721da089c14SMark Johnston 
722da089c14SMark Johnston 			/*
723da089c14SMark Johnston 			 * if there's a BPF listener, bounce a copy
724da089c14SMark Johnston 			 * of this frame to him:
725da089c14SMark Johnston 			 */
726da089c14SMark Johnston 			BPF_MTAP(ifp, m);
727da089c14SMark Johnston 
728da089c14SMark Johnston 			m_freem(m);
729da089c14SMark Johnston 
730da089c14SMark Johnston 			/* Set frame length. */
731d32048bbSKevin Lo 			usbd_xfer_set_frame_len(xfer, nframes, pos);
732da089c14SMark Johnston 		}
733da089c14SMark Johnston 		if (nframes != 0) {
7347c10cf8cSPyun YongHyeon 			/*
7357c10cf8cSPyun YongHyeon 			 * XXX
7367c10cf8cSPyun YongHyeon 			 * Update TX packet counter here. This is not
7377c10cf8cSPyun YongHyeon 			 * correct way but it seems that there is no way
7387c10cf8cSPyun YongHyeon 			 * to know how many packets are sent at the end
7397c10cf8cSPyun YongHyeon 			 * of transfer because controller combines
7407c10cf8cSPyun YongHyeon 			 * multiple writes into single one if there is
7417c10cf8cSPyun YongHyeon 			 * room in TX buffer of controller.
7427c10cf8cSPyun YongHyeon 			 */
7437c10cf8cSPyun YongHyeon 			if_inc_counter(ifp, IFCOUNTER_OPACKETS, nframes);
744da089c14SMark Johnston 			usbd_xfer_set_frames(xfer, nframes);
745da089c14SMark Johnston 			usbd_transfer_submit(xfer);
746935b194dSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
747da089c14SMark Johnston 		}
748da089c14SMark Johnston 		return;
749da089c14SMark Johnston 		/* NOTREACHED */
750da089c14SMark Johnston 	default:
751ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
752935b194dSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
753da089c14SMark Johnston 
754da089c14SMark Johnston 		if (error != USB_ERR_CANCELLED) {
755da089c14SMark Johnston 			usbd_xfer_set_stall(xfer);
756da089c14SMark Johnston 			goto tr_setup;
757da089c14SMark Johnston 		}
758da089c14SMark Johnston 		return;
759da089c14SMark Johnston 	}
760da089c14SMark Johnston }
761da089c14SMark Johnston 
762da089c14SMark Johnston static void
axge_tick(struct usb_ether * ue)763da089c14SMark Johnston axge_tick(struct usb_ether *ue)
764da089c14SMark Johnston {
765da089c14SMark Johnston 	struct axge_softc *sc;
766da089c14SMark Johnston 	struct mii_data *mii;
767da089c14SMark Johnston 
768da089c14SMark Johnston 	sc = uether_getsc(ue);
769da089c14SMark Johnston 	mii = GET_MII(sc);
770da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
771da089c14SMark Johnston 
772da089c14SMark Johnston 	mii_tick(mii);
773da089c14SMark Johnston }
774da089c14SMark Johnston 
77519a1980eSGleb Smirnoff static u_int
axge_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)77619a1980eSGleb Smirnoff axge_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
77719a1980eSGleb Smirnoff {
77819a1980eSGleb Smirnoff 	uint8_t *hashtbl = arg;
77919a1980eSGleb Smirnoff 	uint32_t h;
78019a1980eSGleb Smirnoff 
78119a1980eSGleb Smirnoff 	h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 26;
78219a1980eSGleb Smirnoff 	hashtbl[h / 8] |= 1 << (h % 8);
78319a1980eSGleb Smirnoff 
78419a1980eSGleb Smirnoff 	return (1);
78519a1980eSGleb Smirnoff }
78619a1980eSGleb Smirnoff 
787da089c14SMark Johnston static void
axge_rxfilter(struct usb_ether * ue)788a5d82655SPyun YongHyeon axge_rxfilter(struct usb_ether *ue)
789da089c14SMark Johnston {
790da089c14SMark Johnston 	struct axge_softc *sc;
791935b194dSJustin Hibbits 	if_t ifp;
792da089c14SMark Johnston 	uint16_t rxmode;
793da089c14SMark Johnston 	uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
794da089c14SMark Johnston 
795da089c14SMark Johnston 	sc = uether_getsc(ue);
796da089c14SMark Johnston 	ifp = uether_getifp(ue);
797da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
798da089c14SMark Johnston 
799a5d82655SPyun YongHyeon 	/*
800a5d82655SPyun YongHyeon 	 * Configure RX settings.
801a5d82655SPyun YongHyeon 	 * Don't set RCR_IPE(IP header alignment on 32bit boundary) to disable
802a5d82655SPyun YongHyeon 	 * inserting extra padding bytes.  This wastes ethernet to USB host
803a5d82655SPyun YongHyeon 	 * bandwidth as well as complicating RX handling logic.  Current USB
804a5d82655SPyun YongHyeon 	 * framework requires copying RX frames to mbufs so there is no need
805a5d82655SPyun YongHyeon 	 * to worry about alignment.
806a5d82655SPyun YongHyeon 	 */
807a5d82655SPyun YongHyeon 	rxmode = RCR_DROP_CRCERR | RCR_START;
808935b194dSJustin Hibbits 	if (if_getflags(ifp) & IFF_BROADCAST)
809a5d82655SPyun YongHyeon 		rxmode |= RCR_ACPT_BCAST;
810935b194dSJustin Hibbits 	if (if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) {
811935b194dSJustin Hibbits 		if (if_getflags(ifp) & IFF_PROMISC)
812a5d82655SPyun YongHyeon 			rxmode |= RCR_PROMISC;
813b2cdc7caSPyun YongHyeon 		rxmode |= RCR_ACPT_ALL_MCAST;
814d32048bbSKevin Lo 		axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
815da089c14SMark Johnston 		return;
816da089c14SMark Johnston 	}
817da089c14SMark Johnston 
818a5d82655SPyun YongHyeon 	rxmode |= RCR_ACPT_MCAST;
81919a1980eSGleb Smirnoff 	if_foreach_llmaddr(ifp, axge_hash_maddr, &hashtbl);
820da089c14SMark Johnston 
821d32048bbSKevin Lo 	axge_write_mem(sc, AXGE_ACCESS_MAC, 8, AXGE_MFA, (void *)&hashtbl, 8);
822d32048bbSKevin Lo 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
823da089c14SMark Johnston }
824da089c14SMark Johnston 
825da089c14SMark Johnston static void
axge_start(struct usb_ether * ue)826da089c14SMark Johnston axge_start(struct usb_ether *ue)
827da089c14SMark Johnston {
828da089c14SMark Johnston 	struct axge_softc *sc;
829da089c14SMark Johnston 
830da089c14SMark Johnston 	sc = uether_getsc(ue);
831da089c14SMark Johnston 	/*
832da089c14SMark Johnston 	 * Start the USB transfers, if not already started.
833da089c14SMark Johnston 	 */
834da089c14SMark Johnston 	usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_RD]);
835da089c14SMark Johnston 	usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_WR]);
836da089c14SMark Johnston }
837da089c14SMark Johnston 
838da089c14SMark Johnston static void
axge_init(struct usb_ether * ue)839da089c14SMark Johnston axge_init(struct usb_ether *ue)
840da089c14SMark Johnston {
841da089c14SMark Johnston 	struct axge_softc *sc;
842935b194dSJustin Hibbits 	if_t ifp;
843da089c14SMark Johnston 
844da089c14SMark Johnston 	sc = uether_getsc(ue);
845da089c14SMark Johnston 	ifp = uether_getifp(ue);
846da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
847da089c14SMark Johnston 
848935b194dSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
849da089c14SMark Johnston 		return;
850da089c14SMark Johnston 
851da089c14SMark Johnston 	/*
852da089c14SMark Johnston 	 * Cancel pending I/O and free all RX/TX buffers.
853da089c14SMark Johnston 	 */
854da089c14SMark Johnston 	axge_stop(ue);
855da089c14SMark Johnston 
856da089c14SMark Johnston 	axge_reset(sc);
857da089c14SMark Johnston 
858da089c14SMark Johnston 	/* Set MAC address. */
859d32048bbSKevin Lo 	axge_write_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
860935b194dSJustin Hibbits 	    if_getlladdr(ifp), ETHER_ADDR_LEN);
861da089c14SMark Johnston 
862d32048bbSKevin Lo 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLLR, 0x34);
863d32048bbSKevin Lo 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLHR, 0x52);
864da089c14SMark Johnston 
865da089c14SMark Johnston 	/* Configure TX/RX checksum offloading. */
866da089c14SMark Johnston 	axge_csum_cfg(ue);
867da089c14SMark Johnston 
868a5d82655SPyun YongHyeon 	/*  Configure RX filters. */
869a5d82655SPyun YongHyeon 	axge_rxfilter(ue);
870da089c14SMark Johnston 
871a5d82655SPyun YongHyeon 	/*
872a5d82655SPyun YongHyeon 	 * XXX
873a5d82655SPyun YongHyeon 	 * Controller supports wakeup on link change detection,
874a5d82655SPyun YongHyeon 	 * magic packet and wakeup frame recpetion.  But it seems
875a5d82655SPyun YongHyeon 	 * there is no framework for USB ethernet suspend/wakeup.
876a5d82655SPyun YongHyeon 	 * Disable all wakeup functions.
877a5d82655SPyun YongHyeon 	 */
878a5d82655SPyun YongHyeon 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR, 0);
879a5d82655SPyun YongHyeon 	(void)axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR);
880da089c14SMark Johnston 
881a5d82655SPyun YongHyeon 	/* Configure default medium type. */
882a5d82655SPyun YongHyeon 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, MSR_GM | MSR_FD |
883a5d82655SPyun YongHyeon 	    MSR_RFC | MSR_TFC | MSR_RE);
884da089c14SMark Johnston 
885da089c14SMark Johnston 	usbd_xfer_set_stall(sc->sc_xfer[AXGE_BULK_DT_WR]);
886da089c14SMark Johnston 
887935b194dSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
888da089c14SMark Johnston 	/* Switch to selected media. */
889da089c14SMark Johnston 	axge_ifmedia_upd(ifp);
890da089c14SMark Johnston }
891da089c14SMark Johnston 
892da089c14SMark Johnston static void
axge_stop(struct usb_ether * ue)893da089c14SMark Johnston axge_stop(struct usb_ether *ue)
894da089c14SMark Johnston {
895da089c14SMark Johnston 	struct axge_softc *sc;
896935b194dSJustin Hibbits 	if_t ifp;
89715b5fb58SPyun YongHyeon 	uint16_t val;
898da089c14SMark Johnston 
899da089c14SMark Johnston 	sc = uether_getsc(ue);
900da089c14SMark Johnston 	ifp = uether_getifp(ue);
901da089c14SMark Johnston 
902da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
903da089c14SMark Johnston 
90415b5fb58SPyun YongHyeon 	val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR);
90515b5fb58SPyun YongHyeon 	val &= ~MSR_RE;
90615b5fb58SPyun YongHyeon 	axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
90715b5fb58SPyun YongHyeon 
9087ef4e76fSHans Petter Selasky 	if (ifp != NULL)
909935b194dSJustin Hibbits 		if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
910da089c14SMark Johnston 	sc->sc_flags &= ~AXGE_FLAG_LINK;
911da089c14SMark Johnston 
912da089c14SMark Johnston 	/*
913da089c14SMark Johnston 	 * Stop all the transfers, if not already stopped:
914da089c14SMark Johnston 	 */
915da089c14SMark Johnston 	usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_WR]);
916da089c14SMark Johnston 	usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_RD]);
917da089c14SMark Johnston }
918da089c14SMark Johnston 
919da089c14SMark Johnston static int
axge_ioctl(if_t ifp,u_long cmd,caddr_t data)920935b194dSJustin Hibbits axge_ioctl(if_t ifp, u_long cmd, caddr_t data)
921da089c14SMark Johnston {
922da089c14SMark Johnston 	struct usb_ether *ue;
923da089c14SMark Johnston 	struct axge_softc *sc;
924da089c14SMark Johnston 	struct ifreq *ifr;
925da089c14SMark Johnston 	int error, mask, reinit;
926da089c14SMark Johnston 
927935b194dSJustin Hibbits 	ue = if_getsoftc(ifp);
928da089c14SMark Johnston 	sc = uether_getsc(ue);
929da089c14SMark Johnston 	ifr = (struct ifreq *)data;
930da089c14SMark Johnston 	error = 0;
931da089c14SMark Johnston 	reinit = 0;
932da089c14SMark Johnston 	if (cmd == SIOCSIFCAP) {
933da089c14SMark Johnston 		AXGE_LOCK(sc);
934935b194dSJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
935da089c14SMark Johnston 		if ((mask & IFCAP_TXCSUM) != 0 &&
936935b194dSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_TXCSUM) != 0) {
937935b194dSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM);
938935b194dSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
939935b194dSJustin Hibbits 				if_sethwassistbits(ifp, AXGE_CSUM_FEATURES, 0);
940da089c14SMark Johnston 			else
941935b194dSJustin Hibbits 				if_sethwassistbits(ifp, 0, AXGE_CSUM_FEATURES);
942da089c14SMark Johnston 			reinit++;
943da089c14SMark Johnston 		}
944da089c14SMark Johnston 		if ((mask & IFCAP_RXCSUM) != 0 &&
945935b194dSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
946935b194dSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM);
947da089c14SMark Johnston 			reinit++;
948da089c14SMark Johnston 		}
949935b194dSJustin Hibbits 		if (reinit > 0 && if_getdrvflags(ifp) & IFF_DRV_RUNNING)
950935b194dSJustin Hibbits 			if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
951da089c14SMark Johnston 		else
952da089c14SMark Johnston 			reinit = 0;
953da089c14SMark Johnston 		AXGE_UNLOCK(sc);
954da089c14SMark Johnston 		if (reinit > 0)
955da089c14SMark Johnston 			uether_init(ue);
956da089c14SMark Johnston 	} else
957da089c14SMark Johnston 		error = uether_ioctl(ifp, cmd, data);
958da089c14SMark Johnston 
959da089c14SMark Johnston 	return (error);
960da089c14SMark Johnston }
961da089c14SMark Johnston 
9622110950bSHans Petter Selasky static void
axge_rx_frame(struct usb_ether * ue,struct usb_page_cache * pc,int actlen)963da089c14SMark Johnston axge_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
964da089c14SMark Johnston {
965a5d82655SPyun YongHyeon 	struct axge_frame_rxhdr pkt_hdr;
966561227daSHans Petter Selasky 	uint32_t rxhdr;
967a5d82655SPyun YongHyeon 	uint32_t pos;
968a5d82655SPyun YongHyeon 	uint32_t pkt_cnt, pkt_end;
969561227daSHans Petter Selasky 	uint32_t hdr_off;
970561227daSHans Petter Selasky 	uint32_t pktlen;
971561227daSHans Petter Selasky 
972561227daSHans Petter Selasky 	/* verify we have enough data */
973561227daSHans Petter Selasky 	if (actlen < (int)sizeof(rxhdr))
9742110950bSHans Petter Selasky 		return;
975da089c14SMark Johnston 
976da089c14SMark Johnston 	pos = 0;
977da089c14SMark Johnston 
978da089c14SMark Johnston 	usbd_copy_out(pc, actlen - sizeof(rxhdr), &rxhdr, sizeof(rxhdr));
979da089c14SMark Johnston 	rxhdr = le32toh(rxhdr);
980da089c14SMark Johnston 
981a5d82655SPyun YongHyeon 	pkt_cnt = rxhdr & 0xFFFF;
982a5d82655SPyun YongHyeon 	hdr_off = pkt_end = (rxhdr >> 16) & 0xFFFF;
983da089c14SMark Johnston 
984a5d82655SPyun YongHyeon 	/*
98570fbcd45SDamien Broka 	 * On older firmware:
986a5d82655SPyun YongHyeon 	 * <----------------------- actlen ------------------------>
987a5d82655SPyun YongHyeon 	 * [frame #0]...[frame #N][pkt_hdr #0]...[pkt_hdr #N][rxhdr]
98870fbcd45SDamien Broka 	 *
98970fbcd45SDamien Broka 	 * On newer firmware:
99070fbcd45SDamien Broka 	 * <----------------------- actlen -----------------
99170fbcd45SDamien Broka 	 * [frame #0]...[frame #N][pkt_hdr #0][dummy_hdr]...
99270fbcd45SDamien Broka 	 *                         -------------------------------->
99370fbcd45SDamien Broka 	 *                         ...[pkt_hdr #N][dummy_hdr][rxhdr]
99470fbcd45SDamien Broka 	 *
995a5d82655SPyun YongHyeon 	 * Each RX frame would be aligned on 8 bytes boundary. If
996a5d82655SPyun YongHyeon 	 * RCR_IPE bit is set in AXGE_RCR register, there would be 2
997a5d82655SPyun YongHyeon 	 * padding bytes and 6 dummy bytes(as the padding also should
998a5d82655SPyun YongHyeon 	 * be aligned on 8 bytes boundary) for each RX frame to align
999a5d82655SPyun YongHyeon 	 * IP header on 32bits boundary.  Driver don't set RCR_IPE bit
1000a5d82655SPyun YongHyeon 	 * of AXGE_RCR register, so there should be no padding bytes
1001a5d82655SPyun YongHyeon 	 * which simplifies RX logic a lot.
100270fbcd45SDamien Broka 	 *
100370fbcd45SDamien Broka 	 * Further, newer firmware interweaves dummy headers that have
100470fbcd45SDamien Broka 	 * pktlen == 0 and should be skipped without being seen as
100570fbcd45SDamien Broka 	 * dropped frames.
1006a5d82655SPyun YongHyeon 	 */
10072110950bSHans Petter Selasky 	while (pkt_cnt--) {
1008561227daSHans Petter Selasky 		/* verify the header offset */
1009561227daSHans Petter Selasky 		if ((int)(hdr_off + sizeof(pkt_hdr)) > actlen) {
10102110950bSHans Petter Selasky 			DPRINTF("End of packet headers\n");
10112110950bSHans Petter Selasky 			break;
10122110950bSHans Petter Selasky 		}
1013a5d82655SPyun YongHyeon 		usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr));
1014a5d82655SPyun YongHyeon 		pkt_hdr.status = le32toh(pkt_hdr.status);
1015a5d82655SPyun YongHyeon 		pktlen = AXGE_RXBYTES(pkt_hdr.status);
101670fbcd45SDamien Broka 		hdr_off += sizeof(pkt_hdr);
101770fbcd45SDamien Broka 
101870fbcd45SDamien Broka 		/* Skip dummy packet header. */
101970fbcd45SDamien Broka 		if (pktlen == 0)
102070fbcd45SDamien Broka 			continue;
102170fbcd45SDamien Broka 
1022a5d82655SPyun YongHyeon 		if (pos + pktlen > pkt_end) {
10232110950bSHans Petter Selasky 			DPRINTF("Data position reached end\n");
1024da089c14SMark Johnston 			break;
1025da089c14SMark Johnston 		}
1026561227daSHans Petter Selasky 
1027a5d82655SPyun YongHyeon 		if (AXGE_RX_ERR(pkt_hdr.status) != 0) {
10282110950bSHans Petter Selasky 			DPRINTF("Dropped a packet\n");
1029ecc70d3fSGleb Smirnoff 			if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1);
1030a5d82655SPyun YongHyeon 		} else
1031a5d82655SPyun YongHyeon 			axge_rxeof(ue, pc, pos, pktlen, pkt_hdr.status);
1032561227daSHans Petter Selasky 		pos += (pktlen + 7) & ~7;
1033da089c14SMark Johnston 	}
1034da089c14SMark Johnston }
1035da089c14SMark Johnston 
10362110950bSHans Petter Selasky static void
axge_rxeof(struct usb_ether * ue,struct usb_page_cache * pc,unsigned offset,unsigned len,uint32_t status)103762d42655SHans Petter Selasky axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned offset,
103862d42655SHans Petter Selasky     unsigned len, uint32_t status)
1039da089c14SMark Johnston {
1040935b194dSJustin Hibbits 	if_t ifp;
1041da089c14SMark Johnston 	struct mbuf *m;
1042da089c14SMark Johnston 
1043da089c14SMark Johnston 	ifp = ue->ue_ifp;
1044da089c14SMark Johnston 	if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
1045ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
10462110950bSHans Petter Selasky 		return;
1047da089c14SMark Johnston 	}
1048da089c14SMark Johnston 
1049a5d82655SPyun YongHyeon 	if (len > MHLEN - ETHER_ALIGN)
1050da089c14SMark Johnston 		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1051a5d82655SPyun YongHyeon 	else
1052a5d82655SPyun YongHyeon 		m = m_gethdr(M_NOWAIT, MT_DATA);
1053da089c14SMark Johnston 	if (m == NULL) {
1054ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
10552110950bSHans Petter Selasky 		return;
1056da089c14SMark Johnston 	}
10572110950bSHans Petter Selasky 	m->m_pkthdr.rcvif = ifp;
1058a5d82655SPyun YongHyeon 	m->m_len = m->m_pkthdr.len = len;
1059a5d82655SPyun YongHyeon 	m->m_data += ETHER_ALIGN;
1060da089c14SMark Johnston 
1061da089c14SMark Johnston 	usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
1062da089c14SMark Johnston 
1063935b194dSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) {
1064a5d82655SPyun YongHyeon 		if ((status & AXGE_RX_L3_CSUM_ERR) == 0 &&
1065a5d82655SPyun YongHyeon 		    (status & AXGE_RX_L3_TYPE_MASK) == AXGE_RX_L3_TYPE_IPV4)
1066a5d82655SPyun YongHyeon 			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
1067a5d82655SPyun YongHyeon 			    CSUM_IP_VALID;
1068a5d82655SPyun YongHyeon 		if ((status & AXGE_RX_L4_CSUM_ERR) == 0 &&
1069a5d82655SPyun YongHyeon 		    ((status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_UDP ||
1070a5d82655SPyun YongHyeon 		    (status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_TCP)) {
1071d32048bbSKevin Lo 			m->m_pkthdr.csum_flags |= CSUM_DATA_VALID |
1072a5d82655SPyun YongHyeon 			    CSUM_PSEUDO_HDR;
1073da089c14SMark Johnston 			m->m_pkthdr.csum_data = 0xffff;
1074da089c14SMark Johnston 		}
1075da089c14SMark Johnston 	}
1076a5d82655SPyun YongHyeon 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
1077948d799eSHans Petter Selasky 
107835d3dd8bSGleb Smirnoff 	(void)mbufq_enqueue(&ue->ue_rxq, m);
1079da089c14SMark Johnston }
1080da089c14SMark Johnston 
1081da089c14SMark Johnston static void
axge_csum_cfg(struct usb_ether * ue)1082da089c14SMark Johnston axge_csum_cfg(struct usb_ether *ue)
1083da089c14SMark Johnston {
1084da089c14SMark Johnston 	struct axge_softc *sc;
1085935b194dSJustin Hibbits 	if_t ifp;
1086da089c14SMark Johnston 	uint8_t csum;
1087da089c14SMark Johnston 
1088da089c14SMark Johnston 	sc = uether_getsc(ue);
1089da089c14SMark Johnston 	AXGE_LOCK_ASSERT(sc, MA_OWNED);
1090da089c14SMark Johnston 	ifp = uether_getifp(ue);
1091da089c14SMark Johnston 
1092da089c14SMark Johnston 	csum = 0;
1093935b194dSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
1094d32048bbSKevin Lo 		csum |= CTCR_IP | CTCR_TCP | CTCR_UDP;
1095d32048bbSKevin Lo 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CTCR, csum);
1096da089c14SMark Johnston 
1097da089c14SMark Johnston 	csum = 0;
1098935b194dSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0)
1099d32048bbSKevin Lo 		csum |= CRCR_IP | CRCR_TCP | CRCR_UDP;
1100d32048bbSKevin Lo 	axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CRCR, csum);
1101da089c14SMark Johnston }
1102