xref: /freebsd/sys/dev/usb/net/if_axge.c (revision ca48e43ba9ee73a07cdbad8365117793b01273bb)
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