if_axe.c revision 188412
1202375Srdivacky/*-
2202375Srdivacky * Copyright (c) 1997, 1998, 1999, 2000-2003
3202375Srdivacky *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4202375Srdivacky *
5202375Srdivacky * Redistribution and use in source and binary forms, with or without
6202375Srdivacky * modification, are permitted provided that the following conditions
7202375Srdivacky * are met:
8202375Srdivacky * 1. Redistributions of source code must retain the above copyright
9202375Srdivacky *    notice, this list of conditions and the following disclaimer.
10202375Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
11202375Srdivacky *    notice, this list of conditions and the following disclaimer in the
12202375Srdivacky *    documentation and/or other materials provided with the distribution.
13202375Srdivacky * 3. All advertising materials mentioning features or use of this software
14202375Srdivacky *    must display the following acknowledgement:
15249423Sdim *	This product includes software developed by Bill Paul.
16249423Sdim * 4. Neither the name of the author nor the names of any co-contributors
17218893Sdim *    may be used to endorse or promote products derived from this software
18249423Sdim *    without specific prior written permission.
19202375Srdivacky *
20202375Srdivacky * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21202375Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22202375Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23202375Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24202375Srdivacky * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25202375Srdivacky * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26202375Srdivacky * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27202375Srdivacky * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28202375Srdivacky * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29202375Srdivacky * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30251662Sdim * THE POSSIBILITY OF SUCH DAMAGE.
31226633Sdim */
32226633Sdim
33251662Sdim#include <sys/cdefs.h>
34218893Sdim__FBSDID("$FreeBSD: head/sys/dev/usb2/ethernet/if_axe2.c 188412 2009-02-09 22:02:38Z thompsa $");
35218893Sdim
36218893Sdim/*
37218893Sdim * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver.
38218893Sdim * Used in the LinkSys USB200M and various other adapters.
39218893Sdim *
40218893Sdim * Manuals available from:
41218893Sdim * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
42251662Sdim * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
43202375Srdivacky * controller) to find the definitions for the RX control register.
44202375Srdivacky * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
45202375Srdivacky *
46202375Srdivacky * Written by Bill Paul <wpaul@windriver.com>
47202375Srdivacky * Senior Engineer
48218893Sdim * Wind River Systems
49202375Srdivacky */
50202375Srdivacky
51202375Srdivacky/*
52202375Srdivacky * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
53202375Srdivacky * It uses an external PHY (reference designs use a RealTek chip),
54218893Sdim * and has a 64-bit multicast hash filter. There is some information
55218893Sdim * missing from the manual which one needs to know in order to make
56202375Srdivacky * the chip function:
57251662Sdim *
58218893Sdim * - You must set bit 7 in the RX control register, otherwise the
59218893Sdim *   chip won't receive any packets.
60218893Sdim * - You must initialize all 3 IPG registers, or you won't be able
61218893Sdim *   to send any packets.
62218893Sdim *
63218893Sdim * Note that this device appears to only support loading the station
64251662Sdim * address via autload from the EEPROM (i.e. there's no way to manaully
65202375Srdivacky * set it).
66202375Srdivacky *
67202375Srdivacky * (Adam Weinberger wanted me to name this driver if_gir.c.)
68202375Srdivacky */
69202375Srdivacky
70202375Srdivacky/*
71202375Srdivacky * Ax88178 and Ax88772 support backported from the OpenBSD driver.
72202375Srdivacky * 2007/02/12, J.R. Oldroyd, fbsd@opal.com
73202375Srdivacky *
74202375Srdivacky * Manual here:
75202375Srdivacky * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf
76251662Sdim * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
77202375Srdivacky */
78251662Sdim
79202375Srdivacky#include <dev/usb2/include/usb2_devid.h>
80202375Srdivacky#include <dev/usb2/include/usb2_standard.h>
81202375Srdivacky#include <dev/usb2/include/usb2_mfunc.h>
82202375Srdivacky#include <dev/usb2/include/usb2_error.h>
83221345Sdim
84202375Srdivacky#define	USB_DEBUG_VAR axe_debug
85202375Srdivacky
86202375Srdivacky#include <dev/usb2/core/usb2_core.h>
87202375Srdivacky#include <dev/usb2/core/usb2_lookup.h>
88202375Srdivacky#include <dev/usb2/core/usb2_process.h>
89251662Sdim#include <dev/usb2/core/usb2_debug.h>
90202375Srdivacky#include <dev/usb2/core/usb2_request.h>
91221345Sdim#include <dev/usb2/core/usb2_busdma.h>
92202375Srdivacky#include <dev/usb2/core/usb2_util.h>
93202375Srdivacky
94202375Srdivacky#include <dev/usb2/ethernet/usb2_ethernet.h>
95202375Srdivacky#include <dev/usb2/ethernet/if_axereg.h>
96202375Srdivacky
97251662Sdim/*
98202375Srdivacky * AXE_178_MAX_FRAME_BURST
99202375Srdivacky * max frame burst size for Ax88178 and Ax88772
100202375Srdivacky *	0	2048 bytes
101202375Srdivacky *	1	4096 bytes
102202375Srdivacky *	2	8192 bytes
103202375Srdivacky *	3	16384 bytes
104202375Srdivacky * use the largest your system can handle without USB stalling.
105202375Srdivacky *
106202375Srdivacky * NB: 88772 parts appear to generate lots of input errors with
107202375Srdivacky * a 2K rx buffer and 8K is only slightly faster than 4K on an
108202375Srdivacky * EHCI port on a T42 so change at your own risk.
109202375Srdivacky */
110202375Srdivacky#define AXE_178_MAX_FRAME_BURST	1
111202375Srdivacky
112251662Sdim#if USB_DEBUG
113223017Sdimstatic int axe_debug = 0;
114223017Sdim
115223017SdimSYSCTL_NODE(_hw_usb2, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe");
116223017SdimSYSCTL_INT(_hw_usb2_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0,
117223017Sdim    "Debug level");
118223017Sdim#endif
119223017Sdim
120218893Sdim/*
121218893Sdim * Various supported device vendors/products.
122218893Sdim */
123218893Sdimstatic const struct usb2_device_id axe_devs[] = {
124218893Sdim	{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, 0)},
125218893Sdim	{USB_VPI(USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, 0)},
126223017Sdim	{USB_VPI(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, AXE_FLAG_772)},
127218893Sdim	{USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, 0)},
128202375Srdivacky	{USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, AXE_FLAG_178)},
129202375Srdivacky	{USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, AXE_FLAG_772)},
130202375Srdivacky	{USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, 0)},
131202375Srdivacky	{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, AXE_FLAG_178)},
132251662Sdim	{USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, 0)},
133251662Sdim	{USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, AXE_FLAG_772)},
134202375Srdivacky	{USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, 0)},
135202375Srdivacky	{USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, 0)},
136202375Srdivacky	{USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, AXE_FLAG_772)},
137202375Srdivacky	{USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, 0)},
138202375Srdivacky	{USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, AXE_FLAG_178)},
139202375Srdivacky	{USB_VPI(USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, 0)},
140202375Srdivacky	{USB_VPI(USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, 0)},
141202375Srdivacky	{USB_VPI(USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, AXE_FLAG_178)},
142202375Srdivacky	{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, 0)},
143251662Sdim	{USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, 0)},
144218893Sdim	{USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, AXE_FLAG_772)},
145251662Sdim	{USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, AXE_FLAG_178)},
146202375Srdivacky	{USB_VPI(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, 0)},
147202375Srdivacky	{USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, AXE_FLAG_178)},
148202375Srdivacky	{USB_VPI(USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, 0)},
149202375Srdivacky};
150202375Srdivacky
151202375Srdivackystatic device_probe_t axe_probe;
152202375Srdivackystatic device_attach_t axe_attach;
153218893Sdimstatic device_detach_t axe_detach;
154251662Sdimstatic device_shutdown_t axe_shutdown;
155202375Srdivacky
156202375Srdivackystatic usb2_callback_t axe_intr_callback;
157202375Srdivackystatic usb2_callback_t axe_bulk_read_callback;
158202375Srdivackystatic usb2_callback_t axe_bulk_write_callback;
159202375Srdivacky
160251662Sdimstatic miibus_readreg_t axe_miibus_readreg;
161202375Srdivackystatic miibus_writereg_t axe_miibus_writereg;
162202375Srdivackystatic miibus_statchg_t axe_miibus_statchg;
163202375Srdivacky
164202375Srdivackystatic usb2_ether_fn_t axe_attach_post;
165251662Sdimstatic usb2_ether_fn_t axe_init;
166202375Srdivackystatic usb2_ether_fn_t axe_stop;
167202375Srdivackystatic usb2_ether_fn_t axe_start;
168202375Srdivackystatic usb2_ether_fn_t axe_tick;
169202375Srdivackystatic usb2_ether_fn_t axe_setmulti;
170202375Srdivackystatic usb2_ether_fn_t axe_setpromisc;
171202375Srdivacky
172202375Srdivackystatic int	axe_ifmedia_upd(struct ifnet *);
173202375Srdivackystatic void	axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
174251662Sdimstatic int	axe_cmd(struct axe_softc *, int, int, int, void *);
175202375Srdivackystatic void	axe_ax88178_init(struct axe_softc *);
176202375Srdivackystatic void	axe_ax88772_init(struct axe_softc *);
177202375Srdivackystatic int	axe_get_phyno(struct axe_softc *, int);
178202375Srdivacky
179202375Srdivackystatic const struct usb2_config axe_config[AXE_N_TRANSFER] = {
180202375Srdivacky
181202375Srdivacky	[AXE_BULK_DT_WR] = {
182202375Srdivacky		.type = UE_BULK,
183202375Srdivacky		.endpoint = UE_ADDR_ANY,
184202375Srdivacky		.direction = UE_DIR_OUT,
185202375Srdivacky		.mh.bufsize = AXE_BULK_BUF_SIZE,
186202375Srdivacky		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
187202375Srdivacky		.mh.callback = axe_bulk_write_callback,
188202375Srdivacky		.mh.timeout = 10000,	/* 10 seconds */
189251662Sdim	},
190202375Srdivacky
191202375Srdivacky	[AXE_BULK_DT_RD] = {
192202375Srdivacky		.type = UE_BULK,
193202375Srdivacky		.endpoint = UE_ADDR_ANY,
194202375Srdivacky		.direction = UE_DIR_IN,
195202375Srdivacky#if (MCLBYTES < 2048)
196202375Srdivacky#error "(MCLBYTES < 2048)"
197202375Srdivacky#endif
198251662Sdim		.mh.bufsize = MCLBYTES,
199202375Srdivacky		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
200202375Srdivacky		.mh.callback = axe_bulk_read_callback,
201202375Srdivacky		.mh.timeout = 0,	/* no timeout */
202251662Sdim	},
203202375Srdivacky
204202375Srdivacky	[AXE_INTR_DT_RD] = {
205202375Srdivacky		.type = UE_INTERRUPT,
206202375Srdivacky		.endpoint = UE_ADDR_ANY,
207221345Sdim		.direction = UE_DIR_IN,
208202375Srdivacky		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
209202375Srdivacky		.mh.bufsize = 0,	/* use wMaxPacketSize */
210251662Sdim		.mh.callback = axe_intr_callback,
211202375Srdivacky	},
212202375Srdivacky};
213202375Srdivacky
214202375Srdivackystatic device_method_t axe_methods[] = {
215202375Srdivacky	/* Device interface */
216202375Srdivacky	DEVMETHOD(device_probe, axe_probe),
217251662Sdim	DEVMETHOD(device_attach, axe_attach),
218202375Srdivacky	DEVMETHOD(device_detach, axe_detach),
219202375Srdivacky	DEVMETHOD(device_shutdown, axe_shutdown),
220202375Srdivacky
221202375Srdivacky	/* bus interface */
222202375Srdivacky	DEVMETHOD(bus_print_child, bus_generic_print_child),
223251662Sdim	DEVMETHOD(bus_driver_added, bus_generic_driver_added),
224202375Srdivacky
225202375Srdivacky	/* MII interface */
226202375Srdivacky	DEVMETHOD(miibus_readreg, axe_miibus_readreg),
227202375Srdivacky	DEVMETHOD(miibus_writereg, axe_miibus_writereg),
228202375Srdivacky	DEVMETHOD(miibus_statchg, axe_miibus_statchg),
229251662Sdim
230202375Srdivacky	{0, 0}
231251662Sdim};
232226633Sdim
233218893Sdimstatic driver_t axe_driver = {
234223017Sdim	.name = "axe",
235218893Sdim	.methods = axe_methods,
236202375Srdivacky	.size = sizeof(struct axe_softc),
237202375Srdivacky};
238202375Srdivacky
239202375Srdivackystatic devclass_t axe_devclass;
240202375Srdivacky
241202375SrdivackyDRIVER_MODULE(axe, ushub, axe_driver, axe_devclass, NULL, 0);
242202375SrdivackyDRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0);
243202375SrdivackyMODULE_DEPEND(axe, usb2_ethernet, 1, 1, 1);
244221345SdimMODULE_DEPEND(axe, usb2_core, 1, 1, 1);
245202375SrdivackyMODULE_DEPEND(axe, ether, 1, 1, 1);
246202375SrdivackyMODULE_DEPEND(axe, miibus, 1, 1, 1);
247202375Srdivacky
248202375Srdivackystatic const struct usb2_ether_methods axe_ue_methods = {
249251662Sdim	.ue_attach_post = axe_attach_post,
250202375Srdivacky	.ue_start = axe_start,
251202375Srdivacky	.ue_init = axe_init,
252202375Srdivacky	.ue_stop = axe_stop,
253251662Sdim	.ue_tick = axe_tick,
254202375Srdivacky	.ue_setmulti = axe_setmulti,
255202375Srdivacky	.ue_setpromisc = axe_setpromisc,
256202375Srdivacky	.ue_mii_upd = axe_ifmedia_upd,
257202375Srdivacky	.ue_mii_sts = axe_ifmedia_sts,
258202375Srdivacky};
259202375Srdivacky
260210299Sedstatic int
261210299Sedaxe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
262210299Sed{
263202375Srdivacky	struct usb2_device_request req;
264202375Srdivacky	usb2_error_t err;
265202375Srdivacky
266202375Srdivacky	AXE_LOCK_ASSERT(sc, MA_OWNED);
267202375Srdivacky
268202375Srdivacky	req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ?
269251662Sdim	    UT_WRITE_VENDOR_DEVICE :
270202375Srdivacky	    UT_READ_VENDOR_DEVICE);
271202375Srdivacky	req.bRequest = AXE_CMD_CMD(cmd);
272202375Srdivacky	USETW(req.wValue, val);
273251662Sdim	USETW(req.wIndex, index);
274202375Srdivacky	USETW(req.wLength, AXE_CMD_LEN(cmd));
275202375Srdivacky
276202375Srdivacky	err = usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000);
277202375Srdivacky
278202375Srdivacky	return (err);
279202375Srdivacky}
280202375Srdivacky
281202375Srdivackystatic int
282202375Srdivackyaxe_miibus_readreg(device_t dev, int phy, int reg)
283251662Sdim{
284202375Srdivacky	struct axe_softc *sc = device_get_softc(dev);
285202375Srdivacky	uint16_t val;
286202375Srdivacky	int locked;
287202375Srdivacky
288202375Srdivacky	if (sc->sc_phyno != phy)
289226633Sdim		return (0);
290226633Sdim
291226633Sdim	locked = mtx_owned(&sc->sc_mtx);
292226633Sdim	if (!locked)
293226633Sdim		AXE_LOCK(sc);
294226633Sdim
295202375Srdivacky	axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
296202375Srdivacky	axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val);
297202375Srdivacky	axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
298202375Srdivacky
299202375Srdivacky	val = le16toh(val);
300202375Srdivacky	if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) {
301202375Srdivacky		/*
302204792Srdivacky		 * BMSR of AX88772 indicates that it supports extended
303251662Sdim		 * capability but the extended status register is
304202375Srdivacky		 * revered for embedded ethernet PHY. So clear the
305202375Srdivacky		 * extended capability bit of BMSR.
306202375Srdivacky		 */
307202375Srdivacky		val &= ~BMSR_EXTCAP;
308202375Srdivacky	}
309251662Sdim
310202375Srdivacky	if (!locked)
311202375Srdivacky		AXE_UNLOCK(sc);
312202375Srdivacky	return (val);
313251662Sdim}
314202375Srdivacky
315202375Srdivackystatic int
316251662Sdimaxe_miibus_writereg(device_t dev, int phy, int reg, int val)
317202375Srdivacky{
318202375Srdivacky	struct axe_softc *sc = device_get_softc(dev);
319202375Srdivacky	int locked;
320202375Srdivacky
321202375Srdivacky	val = htole16(val);
322251662Sdim
323251662Sdim	if (sc->sc_phyno != phy)
324202375Srdivacky		return (0);
325202375Srdivacky
326202375Srdivacky	locked = mtx_owned(&sc->sc_mtx);
327204792Srdivacky	if (!locked)
328202375Srdivacky		AXE_LOCK(sc);
329202375Srdivacky
330251662Sdim	axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
331202375Srdivacky	axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val);
332202375Srdivacky	axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
333202375Srdivacky
334202375Srdivacky	if (!locked)
335251662Sdim		AXE_UNLOCK(sc);
336202375Srdivacky	return (0);
337251662Sdim}
338202375Srdivacky
339202375Srdivackystatic void
340202375Srdivackyaxe_miibus_statchg(device_t dev)
341202375Srdivacky{
342202375Srdivacky	struct axe_softc *sc = device_get_softc(dev);
343202375Srdivacky	struct mii_data *mii = GET_MII(sc);
344202375Srdivacky	uint16_t val;
345251662Sdim	int err, locked;
346202375Srdivacky
347202375Srdivacky	locked = mtx_owned(&sc->sc_mtx);
348202375Srdivacky	if (!locked)
349221345Sdim		AXE_LOCK(sc);
350202375Srdivacky
351251662Sdim	val = (mii->mii_media_active & IFM_GMASK) == IFM_FDX ?
352202375Srdivacky	    AXE_MEDIA_FULL_DUPLEX : 0;
353202375Srdivacky	if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
354251662Sdim		val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
355202375Srdivacky
356202375Srdivacky		switch (IFM_SUBTYPE(mii->mii_media_active)) {
357202375Srdivacky		case IFM_1000_T:
358202375Srdivacky			val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK;
359202375Srdivacky			break;
360202375Srdivacky		case IFM_100_TX:
361202375Srdivacky			val |= AXE_178_MEDIA_100TX;
362251662Sdim			break;
363202375Srdivacky		case IFM_10_T:
364202375Srdivacky			/* doesn't need to be handled */
365202375Srdivacky			break;
366202375Srdivacky		}
367202375Srdivacky	}
368202375Srdivacky	err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
369202375Srdivacky	if (err)
370202375Srdivacky		device_printf(dev, "media change failed, error %d\n", err);
371202375Srdivacky
372202375Srdivacky	if (!locked)
373251662Sdim		AXE_UNLOCK(sc);
374202375Srdivacky}
375202375Srdivacky
376202375Srdivacky/*
377202375Srdivacky * Set media options.
378202375Srdivacky */
379202375Srdivackystatic int
380251662Sdimaxe_ifmedia_upd(struct ifnet *ifp)
381223017Sdim{
382223017Sdim	struct axe_softc *sc = ifp->if_softc;
383223017Sdim	struct mii_data *mii = GET_MII(sc);
384202375Srdivacky
385202375Srdivacky	AXE_LOCK_ASSERT(sc, MA_OWNED);
386202375Srdivacky
387202375Srdivacky        sc->sc_flags &= ~AXE_FLAG_LINK;
388202375Srdivacky	if (mii->mii_instance) {
389202375Srdivacky		struct mii_softc *miisc;
390202375Srdivacky
391202375Srdivacky		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
392202375Srdivacky			mii_phy_reset(miisc);
393202375Srdivacky	}
394202375Srdivacky	mii_mediachg(mii);
395202375Srdivacky	return (0);
396202375Srdivacky}
397202375Srdivacky
398251662Sdim/*
399202375Srdivacky * Report current media status.
400202375Srdivacky */
401202375Srdivackystatic void
402202375Srdivackyaxe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
403202375Srdivacky{
404226633Sdim	struct axe_softc *sc = ifp->if_softc;
405218893Sdim	struct mii_data *mii = GET_MII(sc);
406251662Sdim
407202375Srdivacky	AXE_LOCK(sc);
408202375Srdivacky	mii_pollstat(mii);
409202375Srdivacky	AXE_UNLOCK(sc);
410202375Srdivacky	ifmr->ifm_active = mii->mii_media_active;
411202375Srdivacky	ifmr->ifm_status = mii->mii_media_status;
412204642Srdivacky}
413202375Srdivacky
414202375Srdivackystatic void
415202375Srdivackyaxe_setmulti(struct usb2_ether *ue)
416202375Srdivacky{
417251662Sdim	struct axe_softc *sc = usb2_ether_getsc(ue);
418202375Srdivacky	struct ifnet *ifp = usb2_ether_getifp(ue);
419202375Srdivacky	struct ifmultiaddr *ifma;
420202375Srdivacky	uint32_t h = 0;
421202375Srdivacky	uint16_t rxmode;
422251662Sdim	uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
423218893Sdim
424218893Sdim	AXE_LOCK_ASSERT(sc, MA_OWNED);
425218893Sdim
426218893Sdim	axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
427218893Sdim	rxmode = le16toh(rxmode);
428218893Sdim
429218893Sdim	if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
430202375Srdivacky		rxmode |= AXE_RXCMD_ALLMULTI;
431202375Srdivacky		axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
432202375Srdivacky		return;
433202375Srdivacky	}
434202375Srdivacky	rxmode &= ~AXE_RXCMD_ALLMULTI;
435202375Srdivacky
436202375Srdivacky	IF_ADDR_LOCK(ifp);
437202375Srdivacky	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
438202375Srdivacky	{
439202375Srdivacky		if (ifma->ifma_addr->sa_family != AF_LINK)
440202375Srdivacky			continue;
441202375Srdivacky		h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
442202375Srdivacky		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
443202375Srdivacky		hashtbl[h / 8] |= 1 << (h % 8);
444202375Srdivacky	}
445251662Sdim	IF_ADDR_UNLOCK(ifp);
446218893Sdim
447218893Sdim	axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
448218893Sdim	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
449218893Sdim}
450218893Sdim
451218893Sdimstatic int
452202375Srdivackyaxe_get_phyno(struct axe_softc *sc, int sel)
453202375Srdivacky{
454202375Srdivacky	int phyno;
455202375Srdivacky
456202375Srdivacky	switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) {
457221345Sdim	case PHY_TYPE_100_HOME:
458202375Srdivacky	case PHY_TYPE_GIG:
459202375Srdivacky		phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]);
460202375Srdivacky		break;
461202375Srdivacky	case PHY_TYPE_SPECIAL:
462202375Srdivacky		/* FALLTHROUGH */
463202375Srdivacky	case PHY_TYPE_RSVD:
464202375Srdivacky		/* FALLTHROUGH */
465202375Srdivacky	case PHY_TYPE_NON_SUP:
466202375Srdivacky		/* FALLTHROUGH */
467202375Srdivacky	default:
468202375Srdivacky		phyno = -1;
469202375Srdivacky		break;
470202375Srdivacky	}
471202375Srdivacky
472202375Srdivacky	return (phyno);
473202375Srdivacky}
474202375Srdivacky
475202375Srdivackystatic void
476202375Srdivackyaxe_ax88178_init(struct axe_softc *sc)
477202375Srdivacky{
478202375Srdivacky	int gpio0 = 0, phymode = 0;
479202375Srdivacky	uint16_t eeprom;
480202375Srdivacky
481202375Srdivacky	axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL);
482202375Srdivacky	/* XXX magic */
483223017Sdim	axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom);
484223017Sdim	eeprom = le16toh(eeprom);
485223017Sdim	axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL);
486223017Sdim
487223017Sdim	/* if EEPROM is invalid we have to use to GPIO0 */
488223017Sdim	if (eeprom == 0xffff) {
489251662Sdim		phymode = 0;
490218893Sdim		gpio0 = 1;
491218893Sdim	} else {
492218893Sdim		phymode = eeprom & 7;
493218893Sdim		gpio0 = (eeprom & 0x80) ? 0 : 1;
494218893Sdim	}
495223017Sdim
496218893Sdim	axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL);
497218893Sdim	usb2_ether_pause(&sc->sc_ue, hz / 16);
498251662Sdim
499202375Srdivacky	if ((eeprom >> 8) != 0x01) {
500223017Sdim		axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
501223017Sdim		usb2_ether_pause(&sc->sc_ue, hz / 32);
502223017Sdim
503223017Sdim		axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL);
504202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 3);
505202375Srdivacky
506202375Srdivacky		axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
507202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 32);
508202375Srdivacky	} else {
509202375Srdivacky		axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL);
510202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 32);
511202375Srdivacky
512202375Srdivacky		axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL);
513202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 32);
514202375Srdivacky	}
515202375Srdivacky
516251662Sdim	/* soft reset */
517202375Srdivacky	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
518202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 4);
519202375Srdivacky
520202375Srdivacky	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
521202375Srdivacky	    AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
522202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 4);
523202375Srdivacky	/* Enable MII/GMII/RGMII interface to work with external PHY. */
524202375Srdivacky	axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL);
525202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 4);
526202375Srdivacky
527202375Srdivacky	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
528202375Srdivacky}
529202375Srdivacky
530251662Sdimstatic void
531202375Srdivackyaxe_ax88772_init(struct axe_softc *sc)
532202375Srdivacky{
533202375Srdivacky	axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
534202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 16);
535251662Sdim
536202375Srdivacky	if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) {
537202375Srdivacky		/* ask for the embedded PHY */
538202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
539251662Sdim		usb2_ether_pause(&sc->sc_ue, hz / 64);
540202375Srdivacky
541202375Srdivacky		/* power down and reset state, pin reset state */
542202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
543202375Srdivacky		    AXE_SW_RESET_CLEAR, NULL);
544202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 16);
545202375Srdivacky
546202375Srdivacky		/* power down/reset state, pin operating state */
547202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
548202375Srdivacky		    AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
549202375Srdivacky		usb2_ether_pause(&sc->sc_ue, hz / 4);
550251662Sdim
551202375Srdivacky		/* power up, reset */
552202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL);
553202375Srdivacky
554202375Srdivacky		/* power up, operating */
555202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
556202375Srdivacky		    AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
557202375Srdivacky	} else {
558202375Srdivacky		/* ask for external PHY */
559202375Srdivacky		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
560251662Sdim		usb2_ether_pause(&sc->sc_ue, hz / 64);
561202375Srdivacky
562202375Srdivacky		/* power down internal PHY */
563251662Sdim		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
564202375Srdivacky		    AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
565202375Srdivacky	}
566202375Srdivacky
567202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 4);
568202375Srdivacky	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
569202375Srdivacky}
570202375Srdivacky
571202375Srdivackystatic void
572202375Srdivackyaxe_reset(struct axe_softc *sc)
573251662Sdim{
574202375Srdivacky	struct usb2_config_descriptor *cd;
575202375Srdivacky	usb2_error_t err;
576202375Srdivacky
577202375Srdivacky	cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev);
578251662Sdim
579226633Sdim	err = usb2_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
580202375Srdivacky	    cd->bConfigurationValue);
581251662Sdim	if (err)
582202375Srdivacky		DPRINTF("reset failed (ignored)\n");
583202375Srdivacky
584202375Srdivacky	/* Wait a little while for the chip to get its brains in order. */
585202375Srdivacky	usb2_ether_pause(&sc->sc_ue, hz / 100);
586202375Srdivacky}
587202375Srdivacky
588202375Srdivackystatic void
589202375Srdivackyaxe_attach_post(struct usb2_ether *ue)
590202375Srdivacky{
591202375Srdivacky	struct axe_softc *sc = usb2_ether_getsc(ue);
592202375Srdivacky
593202375Srdivacky	/*
594202375Srdivacky	 * Load PHY indexes first. Needed by axe_xxx_init().
595202375Srdivacky	 */
596202375Srdivacky	axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs);
597202375Srdivacky#if 1
598202375Srdivacky	device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n",
599202375Srdivacky	    sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]);
600202375Srdivacky#endif
601202375Srdivacky	sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI);
602202375Srdivacky	if (sc->sc_phyno == -1)
603202375Srdivacky		sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC);
604202375Srdivacky	if (sc->sc_phyno == -1) {
605202375Srdivacky		device_printf(sc->sc_ue.ue_dev,
606202375Srdivacky		    "no valid PHY address found, assuming PHY address 0\n");
607202375Srdivacky		sc->sc_phyno = 0;
608202375Srdivacky	}
609202375Srdivacky
610202375Srdivacky	if (sc->sc_flags & AXE_FLAG_178)
611202375Srdivacky		axe_ax88178_init(sc);
612202375Srdivacky	else if (sc->sc_flags & AXE_FLAG_772)
613202375Srdivacky		axe_ax88772_init(sc);
614202375Srdivacky
615202375Srdivacky	/*
616202375Srdivacky	 * Get station address.
617202375Srdivacky	 */
618202375Srdivacky	if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772))
619202375Srdivacky		axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
620202375Srdivacky	else
621202375Srdivacky		axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
622251662Sdim
623202375Srdivacky	/*
624202375Srdivacky	 * Fetch IPG values.
625202375Srdivacky	 */
626202375Srdivacky	axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
627202375Srdivacky}
628202375Srdivacky
629251662Sdim/*
630202375Srdivacky * Probe for a AX88172 chip.
631202375Srdivacky */
632251662Sdimstatic int
633202375Srdivackyaxe_probe(device_t dev)
634202375Srdivacky{
635251662Sdim	struct usb2_attach_arg *uaa = device_get_ivars(dev);
636202375Srdivacky
637202375Srdivacky	if (uaa->usb2_mode != USB_MODE_HOST)
638202375Srdivacky		return (ENXIO);
639202375Srdivacky	if (uaa->info.bConfigIndex != AXE_CONFIG_IDX)
640202375Srdivacky		return (ENXIO);
641202375Srdivacky	if (uaa->info.bIfaceIndex != AXE_IFACE_IDX)
642202375Srdivacky		return (ENXIO);
643202375Srdivacky
644202375Srdivacky	return (usb2_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa));
645251662Sdim}
646202375Srdivacky
647202375Srdivacky/*
648202375Srdivacky * Attach the interface. Allocate softc structures, do ifmedia
649202375Srdivacky * setup and ethernet/BPF attach.
650202375Srdivacky */
651251662Sdimstatic int
652251662Sdimaxe_attach(device_t dev)
653202375Srdivacky{
654202375Srdivacky	struct usb2_attach_arg *uaa = device_get_ivars(dev);
655202375Srdivacky	struct axe_softc *sc = device_get_softc(dev);
656251662Sdim	struct usb2_ether *ue = &sc->sc_ue;
657202375Srdivacky	uint8_t iface_index;
658202375Srdivacky	int error;
659202375Srdivacky
660202375Srdivacky	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
661202375Srdivacky
662202375Srdivacky	device_set_usb2_desc(dev);
663251662Sdim
664202375Srdivacky	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
665202375Srdivacky
666202375Srdivacky	iface_index = AXE_IFACE_IDX;
667202375Srdivacky	error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
668202375Srdivacky	    axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx);
669251662Sdim	if (error) {
670202375Srdivacky		device_printf(dev, "allocating USB transfers failed!\n");
671202375Srdivacky		goto detach;
672202375Srdivacky	}
673202375Srdivacky
674202375Srdivacky	ue->ue_sc = sc;
675251662Sdim	ue->ue_dev = dev;
676202375Srdivacky	ue->ue_udev = uaa->device;
677202375Srdivacky	ue->ue_mtx = &sc->sc_mtx;
678202375Srdivacky	ue->ue_methods = &axe_ue_methods;
679202375Srdivacky
680251662Sdim	error = usb2_ether_ifattach(ue);
681202375Srdivacky	if (error) {
682202375Srdivacky		device_printf(dev, "could not attach interface\n");
683202375Srdivacky		goto detach;
684251662Sdim	}
685202375Srdivacky	return (0);			/* success */
686202375Srdivacky
687202375Srdivackydetach:
688251662Sdim	axe_detach(dev);
689263508Sdim	return (ENXIO);			/* failure */
690263508Sdim}
691263508Sdim
692263508Sdimstatic int
693251662Sdimaxe_detach(device_t dev)
694202375Srdivacky{
695202375Srdivacky	struct axe_softc *sc = device_get_softc(dev);
696202375Srdivacky	struct usb2_ether *ue = &sc->sc_ue;
697251662Sdim
698202375Srdivacky	usb2_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER);
699202375Srdivacky	usb2_ether_ifdetach(ue);
700202375Srdivacky	mtx_destroy(&sc->sc_mtx);
701251662Sdim
702202375Srdivacky	return (0);
703202375Srdivacky}
704202375Srdivacky
705202375Srdivackystatic void
706226633Sdimaxe_intr_callback(struct usb2_xfer *xfer)
707251662Sdim{
708202375Srdivacky	switch (USB_GET_STATE(xfer)) {
709251662Sdim	case USB_ST_TRANSFERRED:
710202375Srdivacky	case USB_ST_SETUP:
711202375Srdivackytr_setup:
712202375Srdivacky		xfer->frlengths[0] = xfer->max_data_length;
713251662Sdim		usb2_start_hardware(xfer);
714202375Srdivacky		return;
715221345Sdim
716221345Sdim	default:			/* Error */
717202375Srdivacky		if (xfer->error != USB_ERR_CANCELLED) {
718202375Srdivacky			/* try to clear stall first */
719251662Sdim			xfer->flags.stall_pipe = 1;
720202375Srdivacky			goto tr_setup;
721202375Srdivacky		}
722202375Srdivacky		return;
723251662Sdim	}
724202375Srdivacky}
725202375Srdivacky
726202375Srdivacky#if (AXE_BULK_BUF_SIZE >= 0x10000)
727202375Srdivacky#error "Please update axe_bulk_read_callback()!"
728202375Srdivacky#endif
729202375Srdivacky
730202375Srdivackystatic void
731202375Srdivackyaxe_bulk_read_callback(struct usb2_xfer *xfer)
732202375Srdivacky{
733202375Srdivacky	struct axe_softc *sc = xfer->priv_sc;
734202375Srdivacky	struct usb2_ether *ue = &sc->sc_ue;
735202375Srdivacky	struct ifnet *ifp = usb2_ether_getifp(ue);
736202375Srdivacky	struct axe_sframe_hdr hdr;
737251662Sdim	int error, pos, len, adjust;
738202375Srdivacky
739202375Srdivacky	switch (USB_GET_STATE(xfer)) {
740202375Srdivacky	case USB_ST_TRANSFERRED:
741202375Srdivacky		pos = 0;
742202375Srdivacky		while (1) {
743202375Srdivacky			if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
744202375Srdivacky				if (xfer->actlen < sizeof(hdr)) {
745202375Srdivacky					/* too little data */
746202375Srdivacky					break;
747251662Sdim				}
748202375Srdivacky				usb2_copy_out(xfer->frbuffers, pos, &hdr, sizeof(hdr));
749202375Srdivacky
750202375Srdivacky				if ((hdr.len ^ hdr.ilen) != 0xFFFF) {
751202375Srdivacky					/* we lost sync */
752202375Srdivacky					break;
753202375Srdivacky				}
754202375Srdivacky				xfer->actlen -= sizeof(hdr);
755202375Srdivacky				pos += sizeof(hdr);
756202375Srdivacky
757251662Sdim				len = le16toh(hdr.len);
758202375Srdivacky				if (len > xfer->actlen) {
759202375Srdivacky					/* invalid length */
760202375Srdivacky					break;
761202375Srdivacky				}
762202375Srdivacky				adjust = (len & 1);
763202375Srdivacky
764202375Srdivacky			} else {
765202375Srdivacky				len = xfer->actlen;
766251662Sdim				adjust = 0;
767202375Srdivacky			}
768202375Srdivacky			error = usb2_ether_rxbuf(ue, xfer->frbuffers, pos, len);
769202375Srdivacky			if (error)
770202375Srdivacky				break;
771202375Srdivacky
772251662Sdim			pos += len;
773263508Sdim			xfer->actlen -= len;
774202375Srdivacky
775202375Srdivacky			if (xfer->actlen <= adjust) {
776202375Srdivacky				/* we are finished */
777251662Sdim				goto tr_setup;
778202375Srdivacky			}
779202375Srdivacky			pos += adjust;
780202375Srdivacky			xfer->actlen -= adjust;
781251662Sdim		}
782202375Srdivacky
783202375Srdivacky		/* count an error */
784202375Srdivacky		ifp->if_ierrors++;
785202375Srdivacky
786202375Srdivacky		/* FALLTHROUGH */
787202375Srdivacky	case USB_ST_SETUP:
788202375Srdivackytr_setup:
789202375Srdivacky		xfer->frlengths[0] = xfer->max_data_length;
790202375Srdivacky		usb2_start_hardware(xfer);
791202375Srdivacky		usb2_ether_rxflush(ue);
792202375Srdivacky		return;
793263508Sdim
794202375Srdivacky	default:			/* Error */
795202375Srdivacky		DPRINTF("bulk read error, %s\n",
796202375Srdivacky		    usb2_errstr(xfer->error));
797202375Srdivacky
798202375Srdivacky		if (xfer->error != USB_ERR_CANCELLED) {
799202375Srdivacky			/* try to clear stall first */
800202375Srdivacky			xfer->flags.stall_pipe = 1;
801202375Srdivacky			goto tr_setup;
802202375Srdivacky		}
803202375Srdivacky		return;
804202375Srdivacky
805202375Srdivacky	}
806202375Srdivacky}
807202375Srdivacky
808202375Srdivacky#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4)))
809202375Srdivacky#error "Please update axe_bulk_write_callback()!"
810202375Srdivacky#endif
811202375Srdivacky
812202375Srdivackystatic void
813202375Srdivackyaxe_bulk_write_callback(struct usb2_xfer *xfer)
814202375Srdivacky{
815202375Srdivacky	struct axe_softc *sc = xfer->priv_sc;
816202375Srdivacky	struct axe_sframe_hdr hdr;
817202375Srdivacky	struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
818202375Srdivacky	struct mbuf *m;
819251662Sdim	int pos;
820202375Srdivacky
821202375Srdivacky	switch (USB_GET_STATE(xfer)) {
822202375Srdivacky	case USB_ST_TRANSFERRED:
823202375Srdivacky		DPRINTFN(11, "transfer complete\n");
824202375Srdivacky		ifp->if_opackets++;
825202375Srdivacky		/* FALLTHROUGH */
826202375Srdivacky	case USB_ST_SETUP:
827202375Srdivackytr_setup:
828202375Srdivacky		if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
829202375Srdivacky			/*
830202375Srdivacky			 * don't send anything if there is no link !
831202375Srdivacky			 */
832202375Srdivacky			return;
833202375Srdivacky		}
834202375Srdivacky		pos = 0;
835202375Srdivacky
836202375Srdivacky		while (1) {
837202375Srdivacky
838202375Srdivacky			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
839202375Srdivacky
840221345Sdim			if (m == NULL) {
841202375Srdivacky				if (pos > 0)
842221345Sdim					break;	/* send out data */
843202375Srdivacky				return;
844202375Srdivacky			}
845202375Srdivacky			if (m->m_pkthdr.len > MCLBYTES) {
846221345Sdim				m->m_pkthdr.len = MCLBYTES;
847221345Sdim			}
848251662Sdim			if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
849202375Srdivacky
850202375Srdivacky				hdr.len = htole16(m->m_pkthdr.len);
851221345Sdim				hdr.ilen = ~hdr.len;
852202375Srdivacky
853202375Srdivacky				usb2_copy_in(xfer->frbuffers, pos, &hdr, sizeof(hdr));
854202375Srdivacky
855202375Srdivacky				pos += sizeof(hdr);
856251662Sdim
857202375Srdivacky				/*
858202375Srdivacky				 * NOTE: Some drivers force a short packet
859202375Srdivacky				 * by appending a dummy header with zero
860221345Sdim				 * length at then end of the USB transfer.
861202375Srdivacky				 * This driver uses the
862202375Srdivacky				 * USB_FORCE_SHORT_XFER flag instead.
863202375Srdivacky				 */
864202375Srdivacky			}
865202375Srdivacky			usb2_m_copy_in(xfer->frbuffers, pos,
866202375Srdivacky			    m, 0, m->m_pkthdr.len);
867202375Srdivacky
868202375Srdivacky			pos += m->m_pkthdr.len;
869202375Srdivacky
870202375Srdivacky			/*
871202375Srdivacky			 * if there's a BPF listener, bounce a copy
872202375Srdivacky			 * of this frame to him:
873202375Srdivacky			 */
874202375Srdivacky			BPF_MTAP(ifp, m);
875202375Srdivacky
876202375Srdivacky			m_freem(m);
877202375Srdivacky
878202375Srdivacky			if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
879202375Srdivacky				if (pos > (AXE_BULK_BUF_SIZE - MCLBYTES - sizeof(hdr))) {
880202375Srdivacky					/* send out frame(s) */
881202375Srdivacky					break;
882202375Srdivacky				}
883202375Srdivacky			} else {
884202375Srdivacky				/* send out frame */
885202375Srdivacky				break;
886202375Srdivacky			}
887202375Srdivacky		}
888202375Srdivacky
889202375Srdivacky		xfer->frlengths[0] = pos;
890202375Srdivacky		usb2_start_hardware(xfer);
891202375Srdivacky		return;
892202375Srdivacky
893202375Srdivacky	default:			/* Error */
894202375Srdivacky		DPRINTFN(11, "transfer error, %s\n",
895202375Srdivacky		    usb2_errstr(xfer->error));
896204642Srdivacky
897202375Srdivacky		ifp->if_oerrors++;
898202375Srdivacky
899202375Srdivacky		if (xfer->error != USB_ERR_CANCELLED) {
900251662Sdim			/* try to clear stall first */
901202375Srdivacky			xfer->flags.stall_pipe = 1;
902202375Srdivacky			goto tr_setup;
903		}
904		return;
905
906	}
907}
908
909static void
910axe_tick(struct usb2_ether *ue)
911{
912	struct axe_softc *sc = usb2_ether_getsc(ue);
913	struct mii_data *mii = GET_MII(sc);
914
915	AXE_LOCK_ASSERT(sc, MA_OWNED);
916
917	mii_tick(mii);
918	if ((sc->sc_flags & AXE_FLAG_LINK) == 0
919	    && mii->mii_media_status & IFM_ACTIVE &&
920	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
921		sc->sc_flags |= AXE_FLAG_LINK;
922		axe_start(ue);
923	}
924}
925
926static void
927axe_start(struct usb2_ether *ue)
928{
929	struct axe_softc *sc = usb2_ether_getsc(ue);
930
931	/*
932	 * start the USB transfers, if not already started:
933	 */
934	usb2_transfer_start(sc->sc_xfer[AXE_INTR_DT_RD]);
935	usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]);
936	usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]);
937}
938
939static void
940axe_init(struct usb2_ether *ue)
941{
942	struct axe_softc *sc = usb2_ether_getsc(ue);
943	struct ifnet *ifp = usb2_ether_getifp(ue);
944	uint16_t rxmode;
945
946	AXE_LOCK_ASSERT(sc, MA_OWNED);
947
948	/* Cancel pending I/O */
949	axe_stop(ue);
950
951#ifdef notdef
952	/* Set MAC address */
953	axe_mac(sc, IF_LLADDR(ifp), 1);
954#endif
955
956	/* Set transmitter IPG values */
957	if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
958		axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2],
959		    (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL);
960	} else {
961		axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL);
962		axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL);
963		axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL);
964	}
965
966	/* Enable receiver, set RX mode */
967	rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
968	if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
969		rxmode |= AXE_178_RXCMD_MFB_2048;	/* chip default */
970	} else {
971		rxmode |= AXE_172_RXCMD_UNICAST;
972	}
973
974	/* If we want promiscuous mode, set the allframes bit. */
975	if (ifp->if_flags & IFF_PROMISC)
976		rxmode |= AXE_RXCMD_PROMISC;
977
978	if (ifp->if_flags & IFF_BROADCAST)
979		rxmode |= AXE_RXCMD_BROADCAST;
980
981	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
982
983	/* Load the multicast filter. */
984	axe_setmulti(ue);
985
986	usb2_transfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]);
987
988	ifp->if_drv_flags |= IFF_DRV_RUNNING;
989	axe_start(ue);
990}
991
992static void
993axe_setpromisc(struct usb2_ether *ue)
994{
995	struct axe_softc *sc = usb2_ether_getsc(ue);
996	struct ifnet *ifp = usb2_ether_getifp(ue);
997	uint16_t rxmode;
998
999	axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
1000
1001	rxmode = le16toh(rxmode);
1002
1003	if (ifp->if_flags & IFF_PROMISC) {
1004		rxmode |= AXE_RXCMD_PROMISC;
1005	} else {
1006		rxmode &= ~AXE_RXCMD_PROMISC;
1007	}
1008
1009	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
1010
1011	axe_setmulti(ue);
1012}
1013
1014static void
1015axe_stop(struct usb2_ether *ue)
1016{
1017	struct axe_softc *sc = usb2_ether_getsc(ue);
1018	struct ifnet *ifp = usb2_ether_getifp(ue);
1019
1020	AXE_LOCK_ASSERT(sc, MA_OWNED);
1021
1022	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1023	sc->sc_flags &= ~AXE_FLAG_LINK;
1024
1025	/*
1026	 * stop all the transfers, if not already stopped:
1027	 */
1028	usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
1029	usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
1030	usb2_transfer_stop(sc->sc_xfer[AXE_INTR_DT_RD]);
1031
1032	axe_reset(sc);
1033}
1034
1035/*
1036 * Stop all chip I/O so that the kernel's probe routines don't
1037 * get confused by errant DMAs when rebooting.
1038 */
1039static int
1040axe_shutdown(device_t dev)
1041{
1042	struct axe_softc *sc = device_get_softc(dev);
1043
1044	usb2_ether_ifshutdown(&sc->sc_ue);
1045
1046	return (0);
1047}
1048