if_aue.c revision 192499
1184610Salfred/*-
2184610Salfred * Copyright (c) 1997, 1998, 1999, 2000
3184610Salfred *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
4184610Salfred *
5188412Sthompsa * Copyright (c) 2006
6189002Sed *      Alfred Perlstein <alfred@FreeBSD.org>. All rights reserved.
7188412Sthompsa *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred * 3. All advertising materials mentioning features or use of this software
17184610Salfred *    must display the following acknowledgement:
18184610Salfred *	This product includes software developed by Bill Paul.
19184610Salfred * 4. Neither the name of the author nor the names of any co-contributors
20184610Salfred *    may be used to endorse or promote products derived from this software
21184610Salfred *    without specific prior written permission.
22184610Salfred *
23184610Salfred * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
24184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
27184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33184610Salfred * THE POSSIBILITY OF SUCH DAMAGE.
34184610Salfred */
35184610Salfred
36184610Salfred#include <sys/cdefs.h>
37184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_aue.c 192499 2009-05-21 00:04:17Z thompsa $");
38184610Salfred
39184610Salfred/*
40184610Salfred * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver.
41184610Salfred * Datasheet is available from http://www.admtek.com.tw.
42184610Salfred *
43184610Salfred * Written by Bill Paul <wpaul@ee.columbia.edu>
44184610Salfred * Electrical Engineering Department
45184610Salfred * Columbia University, New York City
46188412Sthompsa *
47189002Sed * SMP locking by Alfred Perlstein <alfred@FreeBSD.org>.
48188412Sthompsa * RED Inc.
49184610Salfred */
50184610Salfred
51184610Salfred/*
52184610Salfred * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet
53184610Salfred * support: the control endpoint for reading/writing registers, burst
54184610Salfred * read endpoint for packet reception, burst write for packet transmission
55184610Salfred * and one for "interrupts." The chip uses the same RX filter scheme
56184610Salfred * as the other ADMtek ethernet parts: one perfect filter entry for the
57184610Salfred * the station address and a 64-bit multicast hash table. The chip supports
58184610Salfred * both MII and HomePNA attachments.
59184610Salfred *
60184610Salfred * Since the maximum data transfer speed of USB is supposed to be 12Mbps,
61184610Salfred * you're never really going to get 100Mbps speeds from this device. I
62184610Salfred * think the idea is to allow the device to connect to 10 or 100Mbps
63184610Salfred * networks, not necessarily to provide 100Mbps performance. Also, since
64184610Salfred * the controller uses an external PHY chip, it's possible that board
65184610Salfred * designers might simply choose a 10Mbps PHY.
66184610Salfred *
67188412Sthompsa * Registers are accessed using usb2_ether_do_request(). Packet
68188412Sthompsa * transfers are done using usb2_transfer() and friends.
69184610Salfred */
70184610Salfred
71188746Sthompsa#include "usbdevs.h"
72188942Sthompsa#include <dev/usb/usb.h>
73188942Sthompsa#include <dev/usb/usb_mfunc.h>
74188942Sthompsa#include <dev/usb/usb_error.h>
75184610Salfred
76184610Salfred#define	USB_DEBUG_VAR aue_debug
77184610Salfred
78188942Sthompsa#include <dev/usb/usb_core.h>
79188942Sthompsa#include <dev/usb/usb_lookup.h>
80188942Sthompsa#include <dev/usb/usb_process.h>
81188942Sthompsa#include <dev/usb/usb_debug.h>
82188942Sthompsa#include <dev/usb/usb_request.h>
83188942Sthompsa#include <dev/usb/usb_busdma.h>
84188942Sthompsa#include <dev/usb/usb_util.h>
85184610Salfred
86188942Sthompsa#include <dev/usb/net/usb_ethernet.h>
87188942Sthompsa#include <dev/usb/net/if_auereg.h>
88184610Salfred
89184610Salfred#if USB_DEBUG
90184610Salfredstatic int aue_debug = 0;
91184610Salfred
92184610SalfredSYSCTL_NODE(_hw_usb2, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue");
93184610SalfredSYSCTL_INT(_hw_usb2_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0,
94184610Salfred    "Debug level");
95184610Salfred#endif
96184610Salfred
97184610Salfred/*
98184610Salfred * Various supported device vendors/products.
99184610Salfred */
100184610Salfredstatic const struct usb2_device_id aue_devs[] = {
101188412Sthompsa    {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, AUE_FLAG_PII)},
102188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, 0)},
103188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, AUE_FLAG_LSYS)},
104188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, 0)},
105188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, AUE_FLAG_PNA | AUE_FLAG_PII)},
106188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, AUE_FLAG_PII)},
107188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, AUE_FLAG_PNA)},
108188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, AUE_FLAG_PNA)},
109188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, AUE_FLAG_PII)},
110188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, AUE_FLAG_PII)},
111188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, AUE_FLAG_PII)},
112188412Sthompsa    {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, AUE_FLAG_PNA)},
113188412Sthompsa    {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, AUE_FLAG_PII)},
114188412Sthompsa    {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, 0)},
115188412Sthompsa    {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, AUE_FLAG_PII)},
116188412Sthompsa    {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, AUE_FLAG_PII)},
117188412Sthompsa    {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, AUE_FLAG_PII)},
118188412Sthompsa    {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, AUE_FLAG_PII)},
119188412Sthompsa    {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY)},
120188412Sthompsa    {USB_VPI(USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, AUE_FLAG_PII)},
121188412Sthompsa    {USB_VPI(USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, AUE_FLAG_PII)},
122188412Sthompsa    {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, AUE_FLAG_PII)},
123188412Sthompsa    {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, AUE_FLAG_PII)},
124188412Sthompsa    {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, 0)},
125188412Sthompsa    {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, AUE_FLAG_PII)},
126188412Sthompsa    {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, 0)},
127188412Sthompsa    {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, AUE_FLAG_PNA)},
128188412Sthompsa    {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, AUE_FLAG_PII)},
129188412Sthompsa    {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, 0)},
130188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, AUE_FLAG_LSYS)},
131188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
132188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII)},
133188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII)},
134188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, AUE_FLAG_PNA)},
135188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, AUE_FLAG_LSYS)},
136188412Sthompsa    {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, AUE_FLAG_LSYS)},
137188412Sthompsa    {USB_VPI(USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, AUE_FLAG_PNA | AUE_FLAG_PII)},
138188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, AUE_FLAG_PII)},
139188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, AUE_FLAG_PII)},
140188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, 0)},
141188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, AUE_FLAG_LSYS)},
142188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, 0)},
143188412Sthompsa    {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, AUE_FLAG_LSYS)},
144188412Sthompsa    {USB_VPI(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, 0)},
145188412Sthompsa    {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, 0)},
146188412Sthompsa    {USB_VPI(USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, AUE_FLAG_PII)},
147188412Sthompsa    {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, AUE_FLAG_PII)},
148188412Sthompsa    {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, AUE_FLAG_PII)},
149188412Sthompsa    {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, 0)},
150188412Sthompsa    {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, 0)},
151188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA)},
152188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, AUE_FLAG_LSYS)},
153188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, AUE_FLAG_LSYS)},
154188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII)},
155188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
156188412Sthompsa    {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, AUE_FLAG_LSYS)},
157188412Sthompsa    {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, AUE_FLAG_PII)},
158188412Sthompsa    {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, 0)},
159188412Sthompsa    {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, 0)},
160188412Sthompsa    {USB_VPI(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, AUE_FLAG_PII)},
161188412Sthompsa    {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, AUE_FLAG_PII)},
162188412Sthompsa    {USB_VPI(USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, AUE_FLAG_PII)},
163188412Sthompsa    {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, AUE_FLAG_PII)},
164188412Sthompsa    {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, AUE_FLAG_PII)},
165188412Sthompsa    {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, 0)},
166188412Sthompsa    {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, AUE_FLAG_PII)},
167188412Sthompsa    {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, 0)},
168188412Sthompsa    {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, AUE_FLAG_PII)},
169184610Salfred};
170184610Salfred
171184610Salfred/* prototypes */
172184610Salfred
173184610Salfredstatic device_probe_t aue_probe;
174184610Salfredstatic device_attach_t aue_attach;
175184610Salfredstatic device_detach_t aue_detach;
176188412Sthompsastatic miibus_readreg_t aue_miibus_readreg;
177188412Sthompsastatic miibus_writereg_t aue_miibus_writereg;
178188412Sthompsastatic miibus_statchg_t aue_miibus_statchg;
179184610Salfred
180184610Salfredstatic usb2_callback_t aue_intr_callback;
181184610Salfredstatic usb2_callback_t aue_bulk_read_callback;
182184610Salfredstatic usb2_callback_t aue_bulk_write_callback;
183184610Salfred
184188412Sthompsastatic usb2_ether_fn_t aue_attach_post;
185188412Sthompsastatic usb2_ether_fn_t aue_init;
186188412Sthompsastatic usb2_ether_fn_t aue_stop;
187188412Sthompsastatic usb2_ether_fn_t aue_start;
188188412Sthompsastatic usb2_ether_fn_t aue_tick;
189188412Sthompsastatic usb2_ether_fn_t aue_setmulti;
190188412Sthompsastatic usb2_ether_fn_t aue_setpromisc;
191188412Sthompsa
192188412Sthompsastatic uint8_t	aue_csr_read_1(struct aue_softc *, uint16_t);
193188412Sthompsastatic uint16_t	aue_csr_read_2(struct aue_softc *, uint16_t);
194188412Sthompsastatic void	aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t);
195188412Sthompsastatic void	aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t);
196188412Sthompsastatic void	aue_eeprom_getword(struct aue_softc *, int, uint16_t *);
197188412Sthompsastatic void	aue_read_eeprom(struct aue_softc *, uint8_t *, uint16_t,
198185948Sthompsa		    uint16_t);
199188412Sthompsastatic void	aue_reset(struct aue_softc *);
200188412Sthompsastatic void	aue_reset_pegasus_II(struct aue_softc *);
201184610Salfred
202188412Sthompsastatic int	aue_ifmedia_upd(struct ifnet *);
203188412Sthompsastatic void	aue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
204184610Salfred
205187259Sthompsastatic const struct usb2_config aue_config[AUE_N_TRANSFER] = {
206184610Salfred
207187259Sthompsa	[AUE_BULK_DT_WR] = {
208184610Salfred		.type = UE_BULK,
209184610Salfred		.endpoint = UE_ADDR_ANY,
210184610Salfred		.direction = UE_DIR_OUT,
211190734Sthompsa		.bufsize = (MCLBYTES + 2),
212190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
213190734Sthompsa		.callback = aue_bulk_write_callback,
214190734Sthompsa		.timeout = 10000,	/* 10 seconds */
215184610Salfred	},
216184610Salfred
217187259Sthompsa	[AUE_BULK_DT_RD] = {
218184610Salfred		.type = UE_BULK,
219184610Salfred		.endpoint = UE_ADDR_ANY,
220184610Salfred		.direction = UE_DIR_IN,
221190734Sthompsa		.bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN),
222190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
223190734Sthompsa		.callback = aue_bulk_read_callback,
224184610Salfred	},
225184610Salfred
226187259Sthompsa	[AUE_INTR_DT_RD] = {
227184610Salfred		.type = UE_INTERRUPT,
228184610Salfred		.endpoint = UE_ADDR_ANY,
229184610Salfred		.direction = UE_DIR_IN,
230190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
231190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
232190734Sthompsa		.callback = aue_intr_callback,
233184610Salfred	},
234184610Salfred};
235184610Salfred
236184610Salfredstatic device_method_t aue_methods[] = {
237184610Salfred	/* Device interface */
238184610Salfred	DEVMETHOD(device_probe, aue_probe),
239184610Salfred	DEVMETHOD(device_attach, aue_attach),
240184610Salfred	DEVMETHOD(device_detach, aue_detach),
241184610Salfred
242184610Salfred	/* bus interface */
243184610Salfred	DEVMETHOD(bus_print_child, bus_generic_print_child),
244184610Salfred	DEVMETHOD(bus_driver_added, bus_generic_driver_added),
245184610Salfred
246184610Salfred	/* MII interface */
247188412Sthompsa	DEVMETHOD(miibus_readreg, aue_miibus_readreg),
248188412Sthompsa	DEVMETHOD(miibus_writereg, aue_miibus_writereg),
249188412Sthompsa	DEVMETHOD(miibus_statchg, aue_miibus_statchg),
250184610Salfred
251184610Salfred	{0, 0}
252184610Salfred};
253184610Salfred
254184610Salfredstatic driver_t aue_driver = {
255184610Salfred	.name = "aue",
256184610Salfred	.methods = aue_methods,
257184610Salfred	.size = sizeof(struct aue_softc)
258184610Salfred};
259184610Salfred
260184610Salfredstatic devclass_t aue_devclass;
261184610Salfred
262189275SthompsaDRIVER_MODULE(aue, uhub, aue_driver, aue_devclass, NULL, 0);
263184610SalfredDRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0);
264188942SthompsaMODULE_DEPEND(aue, uether, 1, 1, 1);
265188942SthompsaMODULE_DEPEND(aue, usb, 1, 1, 1);
266188412SthompsaMODULE_DEPEND(aue, ether, 1, 1, 1);
267188412SthompsaMODULE_DEPEND(aue, miibus, 1, 1, 1);
268184610Salfred
269188412Sthompsastatic const struct usb2_ether_methods aue_ue_methods = {
270188412Sthompsa	.ue_attach_post = aue_attach_post,
271188412Sthompsa	.ue_start = aue_start,
272188412Sthompsa	.ue_init = aue_init,
273188412Sthompsa	.ue_stop = aue_stop,
274188412Sthompsa	.ue_tick = aue_tick,
275188412Sthompsa	.ue_setmulti = aue_setmulti,
276188412Sthompsa	.ue_setpromisc = aue_setpromisc,
277188412Sthompsa	.ue_mii_upd = aue_ifmedia_upd,
278188412Sthompsa	.ue_mii_sts = aue_ifmedia_sts,
279188412Sthompsa};
280184610Salfred
281188412Sthompsa#define	AUE_SETBIT(sc, reg, x) \
282188412Sthompsa	aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
283184610Salfred
284188412Sthompsa#define	AUE_CLRBIT(sc, reg, x) \
285188412Sthompsa	aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
286184610Salfred
287184610Salfredstatic uint8_t
288188412Sthompsaaue_csr_read_1(struct aue_softc *sc, uint16_t reg)
289184610Salfred{
290184610Salfred	struct usb2_device_request req;
291188412Sthompsa	usb2_error_t err;
292184610Salfred	uint8_t val;
293184610Salfred
294184610Salfred	req.bmRequestType = UT_READ_VENDOR_DEVICE;
295184610Salfred	req.bRequest = AUE_UR_READREG;
296184610Salfred	USETW(req.wValue, 0);
297184610Salfred	USETW(req.wIndex, reg);
298184610Salfred	USETW(req.wLength, 1);
299184610Salfred
300188412Sthompsa	err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
301188412Sthompsa	if (err)
302188412Sthompsa		return (0);
303184610Salfred	return (val);
304184610Salfred}
305184610Salfred
306184610Salfredstatic uint16_t
307188412Sthompsaaue_csr_read_2(struct aue_softc *sc, uint16_t reg)
308184610Salfred{
309184610Salfred	struct usb2_device_request req;
310188412Sthompsa	usb2_error_t err;
311184610Salfred	uint16_t val;
312184610Salfred
313184610Salfred	req.bmRequestType = UT_READ_VENDOR_DEVICE;
314184610Salfred	req.bRequest = AUE_UR_READREG;
315184610Salfred	USETW(req.wValue, 0);
316184610Salfred	USETW(req.wIndex, reg);
317184610Salfred	USETW(req.wLength, 2);
318184610Salfred
319188412Sthompsa	err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
320188412Sthompsa	if (err)
321188412Sthompsa		return (0);
322184610Salfred	return (le16toh(val));
323184610Salfred}
324184610Salfred
325184610Salfredstatic void
326188412Sthompsaaue_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val)
327184610Salfred{
328184610Salfred	struct usb2_device_request req;
329184610Salfred
330184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
331184610Salfred	req.bRequest = AUE_UR_WRITEREG;
332184610Salfred	req.wValue[0] = val;
333184610Salfred	req.wValue[1] = 0;
334184610Salfred	USETW(req.wIndex, reg);
335184610Salfred	USETW(req.wLength, 1);
336184610Salfred
337188412Sthompsa	if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
338188412Sthompsa		/* error ignored */
339188412Sthompsa	}
340184610Salfred}
341184610Salfred
342184610Salfredstatic void
343188412Sthompsaaue_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val)
344184610Salfred{
345184610Salfred	struct usb2_device_request req;
346184610Salfred
347184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
348184610Salfred	req.bRequest = AUE_UR_WRITEREG;
349184610Salfred	USETW(req.wValue, val);
350184610Salfred	USETW(req.wIndex, reg);
351184610Salfred	USETW(req.wLength, 2);
352184610Salfred
353184610Salfred	val = htole16(val);
354184610Salfred
355188412Sthompsa	if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
356188412Sthompsa		/* error ignored */
357188412Sthompsa	}
358184610Salfred}
359184610Salfred
360184610Salfred/*
361184610Salfred * Read a word of data stored in the EEPROM at address 'addr.'
362184610Salfred */
363184610Salfredstatic void
364188412Sthompsaaue_eeprom_getword(struct aue_softc *sc, int addr, uint16_t *dest)
365184610Salfred{
366188412Sthompsa	int i;
367188412Sthompsa	uint16_t word = 0;
368184610Salfred
369188412Sthompsa	aue_csr_write_1(sc, AUE_EE_REG, addr);
370188412Sthompsa	aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
371184610Salfred
372188412Sthompsa	for (i = 0; i != AUE_TIMEOUT; i++) {
373188412Sthompsa		if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
374184610Salfred			break;
375188412Sthompsa		if (usb2_ether_pause(&sc->sc_ue, hz / 100))
376188412Sthompsa			break;
377184610Salfred	}
378184610Salfred
379188412Sthompsa	if (i == AUE_TIMEOUT)
380188412Sthompsa		device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n");
381184610Salfred
382188412Sthompsa	word = aue_csr_read_2(sc, AUE_EE_DATA);
383188412Sthompsa	*dest = word;
384184610Salfred}
385184610Salfred
386184610Salfred/*
387184610Salfred * Read a sequence of words from the EEPROM.
388184610Salfred */
389184610Salfredstatic void
390188412Sthompsaaue_read_eeprom(struct aue_softc *sc, uint8_t *dest,
391184610Salfred    uint16_t off, uint16_t len)
392184610Salfred{
393188412Sthompsa	uint16_t *ptr = (uint16_t *)dest;
394188412Sthompsa	int i;
395184610Salfred
396188412Sthompsa	for (i = 0; i != len; i++, ptr++)
397188412Sthompsa		aue_eeprom_getword(sc, off + i, ptr);
398184610Salfred}
399184610Salfred
400184610Salfredstatic int
401188412Sthompsaaue_miibus_readreg(device_t dev, int phy, int reg)
402184610Salfred{
403184610Salfred	struct aue_softc *sc = device_get_softc(dev);
404188412Sthompsa	int i, locked;
405188412Sthompsa	uint16_t val = 0;
406184610Salfred
407188412Sthompsa	locked = mtx_owned(&sc->sc_mtx);
408188412Sthompsa	if (!locked)
409188412Sthompsa		AUE_LOCK(sc);
410184610Salfred
411184610Salfred	/*
412188412Sthompsa	 * The Am79C901 HomePNA PHY actually contains two transceivers: a 1Mbps
413188412Sthompsa	 * HomePNA PHY and a 10Mbps full/half duplex ethernet PHY with NWAY
414188412Sthompsa	 * autoneg. However in the ADMtek adapter, only the 1Mbps PHY is
415188412Sthompsa	 * actually connected to anything, so we ignore the 10Mbps one. It
416188412Sthompsa	 * happens to be configured for MII address 3, so we filter that out.
417184610Salfred	 */
418184610Salfred	if (sc->sc_flags & AUE_FLAG_DUAL_PHY) {
419188412Sthompsa		if (phy == 3)
420184610Salfred			goto done;
421184610Salfred#if 0
422188412Sthompsa		if (phy != 1)
423184610Salfred			goto done;
424184610Salfred#endif
425184610Salfred	}
426188412Sthompsa	aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
427188412Sthompsa	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
428184610Salfred
429188412Sthompsa	for (i = 0; i != AUE_TIMEOUT; i++) {
430188412Sthompsa		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
431184610Salfred			break;
432188412Sthompsa		if (usb2_ether_pause(&sc->sc_ue, hz / 100))
433188412Sthompsa			break;
434184610Salfred	}
435184610Salfred
436188412Sthompsa	if (i == AUE_TIMEOUT)
437188412Sthompsa		device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
438184610Salfred
439188412Sthompsa	val = aue_csr_read_2(sc, AUE_PHY_DATA);
440188412Sthompsa
441184610Salfreddone:
442188412Sthompsa	if (!locked)
443188412Sthompsa		AUE_UNLOCK(sc);
444188412Sthompsa	return (val);
445184610Salfred}
446184610Salfred
447184610Salfredstatic int
448188412Sthompsaaue_miibus_writereg(device_t dev, int phy, int reg, int data)
449184610Salfred{
450184610Salfred	struct aue_softc *sc = device_get_softc(dev);
451188412Sthompsa	int i;
452188412Sthompsa	int locked;
453184610Salfred
454188412Sthompsa	if (phy == 3)
455184610Salfred		return (0);
456184610Salfred
457188412Sthompsa	locked = mtx_owned(&sc->sc_mtx);
458188412Sthompsa	if (!locked)
459188412Sthompsa		AUE_LOCK(sc);
460184610Salfred
461188412Sthompsa	aue_csr_write_2(sc, AUE_PHY_DATA, data);
462188412Sthompsa	aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
463188412Sthompsa	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
464184610Salfred
465188412Sthompsa	for (i = 0; i != AUE_TIMEOUT; i++) {
466188412Sthompsa		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
467184610Salfred			break;
468188412Sthompsa		if (usb2_ether_pause(&sc->sc_ue, hz / 100))
469188412Sthompsa			break;
470184610Salfred	}
471184610Salfred
472188412Sthompsa	if (i == AUE_TIMEOUT)
473188412Sthompsa		device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
474188412Sthompsa
475188412Sthompsa	if (!locked)
476188412Sthompsa		AUE_UNLOCK(sc);
477184610Salfred	return (0);
478184610Salfred}
479184610Salfred
480184610Salfredstatic void
481188412Sthompsaaue_miibus_statchg(device_t dev)
482184610Salfred{
483184610Salfred	struct aue_softc *sc = device_get_softc(dev);
484184610Salfred	struct mii_data *mii = GET_MII(sc);
485188412Sthompsa	int locked;
486184610Salfred
487188412Sthompsa	locked = mtx_owned(&sc->sc_mtx);
488188412Sthompsa	if (!locked)
489188412Sthompsa		AUE_LOCK(sc);
490184610Salfred
491188412Sthompsa	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
492188412Sthompsa	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
493188412Sthompsa		AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
494188412Sthompsa	else
495188412Sthompsa		AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
496184610Salfred
497188412Sthompsa	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
498188412Sthompsa		AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
499188412Sthompsa	else
500188412Sthompsa		AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
501184610Salfred
502188412Sthompsa	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
503184610Salfred
504184610Salfred	/*
505184610Salfred	 * Set the LED modes on the LinkSys adapter.
506184610Salfred	 * This turns on the 'dual link LED' bin in the auxmode
507184610Salfred	 * register of the Broadcom PHY.
508184610Salfred	 */
509184610Salfred	if (sc->sc_flags & AUE_FLAG_LSYS) {
510184610Salfred		uint16_t auxmode;
511184610Salfred
512188412Sthompsa		auxmode = aue_miibus_readreg(dev, 0, 0x1b);
513188412Sthompsa		aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04);
514184610Salfred	}
515188412Sthompsa	if (!locked)
516188412Sthompsa		AUE_UNLOCK(sc);
517184610Salfred}
518184610Salfred
519188412Sthompsa#define	AUE_BITS	6
520184610Salfredstatic void
521188412Sthompsaaue_setmulti(struct usb2_ether *ue)
522184610Salfred{
523188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
524188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
525188412Sthompsa	struct ifmultiaddr *ifma;
526188412Sthompsa	uint32_t h = 0;
527188412Sthompsa	uint32_t i;
528188412Sthompsa	uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
529184610Salfred
530188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
531188412Sthompsa
532188412Sthompsa	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
533188412Sthompsa		AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
534184610Salfred		return;
535184610Salfred	}
536184610Salfred
537188412Sthompsa	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
538184610Salfred
539184610Salfred	/* now program new ones */
540188412Sthompsa	IF_ADDR_LOCK(ifp);
541188412Sthompsa	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
542188412Sthompsa		if (ifma->ifma_addr->sa_family != AF_LINK)
543188412Sthompsa			continue;
544188412Sthompsa		h = ether_crc32_le(LLADDR((struct sockaddr_dl *)
545188412Sthompsa		    ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1);
546188412Sthompsa		hashtbl[(h >> 3)] |=  1 << (h & 0x7);
547184610Salfred	}
548188412Sthompsa	IF_ADDR_UNLOCK(ifp);
549188412Sthompsa
550188412Sthompsa	/* write the hashtable */
551188412Sthompsa	for (i = 0; i != 8; i++)
552188412Sthompsa		aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
553184610Salfred}
554184610Salfred
555184610Salfredstatic void
556188412Sthompsaaue_reset_pegasus_II(struct aue_softc *sc)
557184610Salfred{
558184610Salfred	/* Magic constants taken from Linux driver. */
559188412Sthompsa	aue_csr_write_1(sc, AUE_REG_1D, 0);
560188412Sthompsa	aue_csr_write_1(sc, AUE_REG_7B, 2);
561184610Salfred#if 0
562184610Salfred	if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode)
563188412Sthompsa		aue_csr_write_1(sc, AUE_REG_81, 6);
564184610Salfred	else
565184610Salfred#endif
566188412Sthompsa		aue_csr_write_1(sc, AUE_REG_81, 2);
567184610Salfred}
568184610Salfred
569184610Salfredstatic void
570188412Sthompsaaue_reset(struct aue_softc *sc)
571184610Salfred{
572188412Sthompsa	int i;
573184610Salfred
574188412Sthompsa	AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
575184610Salfred
576188412Sthompsa	for (i = 0; i != AUE_TIMEOUT; i++) {
577188412Sthompsa		if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
578184610Salfred			break;
579188412Sthompsa		if (usb2_ether_pause(&sc->sc_ue, hz / 100))
580188412Sthompsa			break;
581184610Salfred	}
582184610Salfred
583188412Sthompsa	if (i == AUE_TIMEOUT)
584188412Sthompsa		device_printf(sc->sc_ue.ue_dev, "reset failed\n");
585188412Sthompsa
586184610Salfred	/*
587184610Salfred	 * The PHY(s) attached to the Pegasus chip may be held
588184610Salfred	 * in reset until we flip on the GPIO outputs. Make sure
589184610Salfred	 * to set the GPIO pins high so that the PHY(s) will
590184610Salfred	 * be enabled.
591184610Salfred	 *
592184610Salfred	 * Note: We force all of the GPIO pins low first, *then*
593184610Salfred	 * enable the ones we want.
594184610Salfred	 */
595188412Sthompsa	aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
596188412Sthompsa	aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
597184610Salfred
598184610Salfred	if (sc->sc_flags & AUE_FLAG_LSYS) {
599184610Salfred		/* Grrr. LinkSys has to be different from everyone else. */
600188412Sthompsa		aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1);
601188412Sthompsa		aue_csr_write_1(sc, AUE_GPIO0,
602188412Sthompsa		    AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0);
603184610Salfred	}
604188412Sthompsa	if (sc->sc_flags & AUE_FLAG_PII)
605188412Sthompsa		aue_reset_pegasus_II(sc);
606188412Sthompsa
607188412Sthompsa	/* Wait a little while for the chip to get its brains in order: */
608188412Sthompsa	usb2_ether_pause(&sc->sc_ue, hz / 100);
609184610Salfred}
610184610Salfred
611188412Sthompsastatic void
612188412Sthompsaaue_attach_post(struct usb2_ether *ue)
613188412Sthompsa{
614188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
615188412Sthompsa
616188412Sthompsa	/* reset the adapter */
617188412Sthompsa	aue_reset(sc);
618188412Sthompsa
619188412Sthompsa	/* get station address from the EEPROM */
620188412Sthompsa	aue_read_eeprom(sc, ue->ue_eaddr, 0, 3);
621188412Sthompsa}
622188412Sthompsa
623184610Salfred/*
624184610Salfred * Probe for a Pegasus chip.
625184610Salfred */
626184610Salfredstatic int
627184610Salfredaue_probe(device_t dev)
628184610Salfred{
629184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
630184610Salfred
631192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
632184610Salfred		return (ENXIO);
633188412Sthompsa	if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX)
634184610Salfred		return (ENXIO);
635188412Sthompsa	if (uaa->info.bIfaceIndex != AUE_IFACE_IDX)
636184610Salfred		return (ENXIO);
637185290Salfred	/*
638188412Sthompsa	 * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict
639188412Sthompsa	 * with older Belkin USB2LAN adapters.  Skip if_aue if we detect one of
640188412Sthompsa	 * the devices that look like Bluetooth adapters.
641185290Salfred	 */
642188412Sthompsa	if (uaa->info.idVendor == USB_VENDOR_BELKIN &&
643188412Sthompsa	    uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 &&
644188412Sthompsa	    uaa->info.bcdDevice == 0x0413)
645185290Salfred		return (ENXIO);
646188412Sthompsa
647184610Salfred	return (usb2_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa));
648184610Salfred}
649184610Salfred
650184610Salfred/*
651184610Salfred * Attach the interface. Allocate softc structures, do ifmedia
652184610Salfred * setup and ethernet/BPF attach.
653184610Salfred */
654184610Salfredstatic int
655184610Salfredaue_attach(device_t dev)
656184610Salfred{
657184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
658184610Salfred	struct aue_softc *sc = device_get_softc(dev);
659188412Sthompsa	struct usb2_ether *ue = &sc->sc_ue;
660184610Salfred	uint8_t iface_index;
661188412Sthompsa	int error;
662184610Salfred
663184610Salfred	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
664184610Salfred
665184610Salfred	if (uaa->info.bcdDevice >= 0x0201) {
666188412Sthompsa		/* XXX currently undocumented */
667188412Sthompsa		sc->sc_flags |= AUE_FLAG_VER_2;
668184610Salfred	}
669188412Sthompsa
670184610Salfred	device_set_usb2_desc(dev);
671188412Sthompsa	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
672184610Salfred
673184610Salfred	iface_index = AUE_IFACE_IDX;
674184610Salfred	error = usb2_transfer_setup(uaa->device, &iface_index,
675187259Sthompsa	    sc->sc_xfer, aue_config, AUE_N_TRANSFER,
676184610Salfred	    sc, &sc->sc_mtx);
677184610Salfred	if (error) {
678188412Sthompsa		device_printf(dev, "allocating USB transfers failed!\n");
679184610Salfred		goto detach;
680184610Salfred	}
681188412Sthompsa
682188412Sthompsa	ue->ue_sc = sc;
683188412Sthompsa	ue->ue_dev = dev;
684188412Sthompsa	ue->ue_udev = uaa->device;
685188412Sthompsa	ue->ue_mtx = &sc->sc_mtx;
686188412Sthompsa	ue->ue_methods = &aue_ue_methods;
687188412Sthompsa
688188412Sthompsa	error = usb2_ether_ifattach(ue);
689184610Salfred	if (error) {
690188412Sthompsa		device_printf(dev, "could not attach interface\n");
691184610Salfred		goto detach;
692184610Salfred	}
693184610Salfred	return (0);			/* success */
694184610Salfred
695184610Salfreddetach:
696184610Salfred	aue_detach(dev);
697184610Salfred	return (ENXIO);			/* failure */
698184610Salfred}
699184610Salfred
700184610Salfredstatic int
701184610Salfredaue_detach(device_t dev)
702184610Salfred{
703184610Salfred	struct aue_softc *sc = device_get_softc(dev);
704188412Sthompsa	struct usb2_ether *ue = &sc->sc_ue;
705184610Salfred
706187259Sthompsa	usb2_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER);
707188412Sthompsa	usb2_ether_ifdetach(ue);
708184610Salfred	mtx_destroy(&sc->sc_mtx);
709184610Salfred
710184610Salfred	return (0);
711184610Salfred}
712184610Salfred
713184610Salfredstatic void
714184610Salfredaue_intr_callback(struct usb2_xfer *xfer)
715184610Salfred{
716184610Salfred	struct aue_softc *sc = xfer->priv_sc;
717188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
718184610Salfred	struct aue_intrpkt pkt;
719184610Salfred
720184610Salfred	switch (USB_GET_STATE(xfer)) {
721184610Salfred	case USB_ST_TRANSFERRED:
722184610Salfred
723188412Sthompsa		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
724188412Sthompsa		    xfer->actlen >= sizeof(pkt)) {
725184610Salfred
726184610Salfred			usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt));
727184610Salfred
728188412Sthompsa			if (pkt.aue_txstat0)
729184610Salfred				ifp->if_oerrors++;
730184610Salfred			if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL &
731188412Sthompsa			    AUE_TXSTAT0_EXCESSCOLL))
732184610Salfred				ifp->if_collisions++;
733184610Salfred		}
734188412Sthompsa		/* FALLTHROUGH */
735184610Salfred	case USB_ST_SETUP:
736188412Sthompsatr_setup:
737188412Sthompsa		xfer->frlengths[0] = xfer->max_data_length;
738188412Sthompsa		usb2_start_hardware(xfer);
739184610Salfred		return;
740184610Salfred
741184610Salfred	default:			/* Error */
742184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
743188412Sthompsa			/* try to clear stall first */
744188412Sthompsa			xfer->flags.stall_pipe = 1;
745188412Sthompsa			goto tr_setup;
746184610Salfred		}
747184610Salfred		return;
748184610Salfred	}
749184610Salfred}
750184610Salfred
751184610Salfredstatic void
752184610Salfredaue_bulk_read_callback(struct usb2_xfer *xfer)
753184610Salfred{
754184610Salfred	struct aue_softc *sc = xfer->priv_sc;
755188412Sthompsa	struct usb2_ether *ue = &sc->sc_ue;
756188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
757188412Sthompsa	struct aue_rxpkt stat;
758184610Salfred
759184610Salfred	switch (USB_GET_STATE(xfer)) {
760184610Salfred	case USB_ST_TRANSFERRED:
761184610Salfred		DPRINTFN(11, "received %d bytes\n", xfer->actlen);
762184610Salfred
763184610Salfred		if (sc->sc_flags & AUE_FLAG_VER_2) {
764184610Salfred
765184610Salfred			if (xfer->actlen == 0) {
766184610Salfred				ifp->if_ierrors++;
767184610Salfred				goto tr_setup;
768184610Salfred			}
769184610Salfred		} else {
770184610Salfred
771188412Sthompsa			if (xfer->actlen <= (sizeof(stat) + ETHER_CRC_LEN)) {
772184610Salfred				ifp->if_ierrors++;
773184610Salfred				goto tr_setup;
774184610Salfred			}
775188412Sthompsa			usb2_copy_out(xfer->frbuffers,
776188412Sthompsa			    xfer->actlen - sizeof(stat), &stat, sizeof(stat));
777184610Salfred
778184610Salfred			/*
779184610Salfred			 * turn off all the non-error bits in the rx status
780184610Salfred			 * word:
781184610Salfred			 */
782188412Sthompsa			stat.aue_rxstat &= AUE_RXSTAT_MASK;
783188412Sthompsa			if (stat.aue_rxstat) {
784184610Salfred				ifp->if_ierrors++;
785184610Salfred				goto tr_setup;
786184610Salfred			}
787184610Salfred			/* No errors; receive the packet. */
788188412Sthompsa			xfer->actlen -= (sizeof(stat) + ETHER_CRC_LEN);
789184610Salfred		}
790188412Sthompsa		usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen);
791184610Salfred
792188412Sthompsa		/* FALLTHROUGH */
793184610Salfred	case USB_ST_SETUP:
794184610Salfredtr_setup:
795188412Sthompsa		xfer->frlengths[0] = xfer->max_data_length;
796188412Sthompsa		usb2_start_hardware(xfer);
797188412Sthompsa		usb2_ether_rxflush(ue);
798184610Salfred		return;
799184610Salfred
800184610Salfred	default:			/* Error */
801188412Sthompsa		DPRINTF("bulk read error, %s\n",
802188412Sthompsa		    usb2_errstr(xfer->error));
803188412Sthompsa
804184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
805184610Salfred			/* try to clear stall first */
806188412Sthompsa			xfer->flags.stall_pipe = 1;
807188412Sthompsa			goto tr_setup;
808184610Salfred		}
809184610Salfred		return;
810184610Salfred	}
811184610Salfred}
812184610Salfred
813184610Salfredstatic void
814184610Salfredaue_bulk_write_callback(struct usb2_xfer *xfer)
815184610Salfred{
816184610Salfred	struct aue_softc *sc = xfer->priv_sc;
817188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
818184610Salfred	struct mbuf *m;
819184610Salfred	uint8_t buf[2];
820184610Salfred
821184610Salfred	switch (USB_GET_STATE(xfer)) {
822184610Salfred	case USB_ST_TRANSFERRED:
823184610Salfred		DPRINTFN(11, "transfer of %d bytes complete\n", xfer->actlen);
824184610Salfred		ifp->if_opackets++;
825184610Salfred
826188412Sthompsa		/* FALLTHROUGH */
827184610Salfred	case USB_ST_SETUP:
828188412Sthompsatr_setup:
829188412Sthompsa		if ((sc->sc_flags & AUE_FLAG_LINK) == 0) {
830184610Salfred			/*
831184610Salfred			 * don't send anything if there is no link !
832184610Salfred			 */
833188412Sthompsa			return;
834184610Salfred		}
835184610Salfred		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
836184610Salfred
837188412Sthompsa		if (m == NULL)
838188412Sthompsa			return;
839188412Sthompsa		if (m->m_pkthdr.len > MCLBYTES)
840184610Salfred			m->m_pkthdr.len = MCLBYTES;
841184610Salfred		if (sc->sc_flags & AUE_FLAG_VER_2) {
842184610Salfred
843184610Salfred			xfer->frlengths[0] = m->m_pkthdr.len;
844184610Salfred
845184610Salfred			usb2_m_copy_in(xfer->frbuffers, 0,
846184610Salfred			    m, 0, m->m_pkthdr.len);
847184610Salfred
848184610Salfred		} else {
849184610Salfred
850184610Salfred			xfer->frlengths[0] = (m->m_pkthdr.len + 2);
851184610Salfred
852184610Salfred			/*
853188412Sthompsa		         * The ADMtek documentation says that the
854188412Sthompsa		         * packet length is supposed to be specified
855188412Sthompsa		         * in the first two bytes of the transfer,
856188412Sthompsa		         * however it actually seems to ignore this
857188412Sthompsa		         * info and base the frame size on the bulk
858188412Sthompsa		         * transfer length.
859184610Salfred		         */
860184610Salfred			buf[0] = (uint8_t)(m->m_pkthdr.len);
861184610Salfred			buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
862184610Salfred
863184610Salfred			usb2_copy_in(xfer->frbuffers, 0, buf, 2);
864184610Salfred
865184610Salfred			usb2_m_copy_in(xfer->frbuffers, 2,
866184610Salfred			    m, 0, m->m_pkthdr.len);
867184610Salfred		}
868184610Salfred
869184610Salfred		/*
870184610Salfred		 * if there's a BPF listener, bounce a copy
871184610Salfred		 * of this frame to him:
872184610Salfred		 */
873184610Salfred		BPF_MTAP(ifp, m);
874184610Salfred
875184610Salfred		m_freem(m);
876184610Salfred
877184610Salfred		usb2_start_hardware(xfer);
878184610Salfred		return;
879184610Salfred
880184610Salfred	default:			/* Error */
881184610Salfred		DPRINTFN(11, "transfer error, %s\n",
882184610Salfred		    usb2_errstr(xfer->error));
883184610Salfred
884188412Sthompsa		ifp->if_oerrors++;
885188412Sthompsa
886184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
887184610Salfred			/* try to clear stall first */
888188412Sthompsa			xfer->flags.stall_pipe = 1;
889188412Sthompsa			goto tr_setup;
890184610Salfred		}
891184610Salfred		return;
892184610Salfred	}
893184610Salfred}
894184610Salfred
895184610Salfredstatic void
896188412Sthompsaaue_tick(struct usb2_ether *ue)
897184610Salfred{
898188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
899188412Sthompsa	struct mii_data *mii = GET_MII(sc);
900184610Salfred
901188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
902184610Salfred
903184610Salfred	mii_tick(mii);
904188412Sthompsa	if ((sc->sc_flags & AUE_FLAG_LINK) == 0
905188412Sthompsa	    && mii->mii_media_status & IFM_ACTIVE &&
906188412Sthompsa	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
907188412Sthompsa		sc->sc_flags |= AUE_FLAG_LINK;
908188412Sthompsa		aue_start(ue);
909184610Salfred	}
910184610Salfred}
911184610Salfred
912184610Salfredstatic void
913188412Sthompsaaue_start(struct usb2_ether *ue)
914184610Salfred{
915188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
916184610Salfred
917188412Sthompsa	/*
918188412Sthompsa	 * start the USB transfers, if not already started:
919188412Sthompsa	 */
920188412Sthompsa	usb2_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]);
921188412Sthompsa	usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]);
922188412Sthompsa	usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]);
923184610Salfred}
924184610Salfred
925184610Salfredstatic void
926188412Sthompsaaue_init(struct usb2_ether *ue)
927184610Salfred{
928188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
929188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
930188412Sthompsa	int i;
931184610Salfred
932188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
933184610Salfred
934184610Salfred	/*
935184610Salfred	 * Cancel pending I/O
936184610Salfred	 */
937188412Sthompsa	aue_reset(sc);
938184610Salfred
939184610Salfred	/* Set MAC address */
940188412Sthompsa	for (i = 0; i != ETHER_ADDR_LEN; i++)
941188412Sthompsa		aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]);
942184610Salfred
943184610Salfred	/* update promiscuous setting */
944188412Sthompsa	aue_setpromisc(ue);
945184610Salfred
946188412Sthompsa	/* Load the multicast filter. */
947188412Sthompsa	aue_setmulti(ue);
948184610Salfred
949188412Sthompsa	/* Enable RX and TX */
950188412Sthompsa	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
951188412Sthompsa	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
952188412Sthompsa	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
953184610Salfred
954188412Sthompsa	usb2_transfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]);
955184610Salfred
956188412Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
957188412Sthompsa	aue_start(ue);
958184610Salfred}
959184610Salfred
960184610Salfredstatic void
961188412Sthompsaaue_setpromisc(struct usb2_ether *ue)
962184610Salfred{
963188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
964188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
965188412Sthompsa
966188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
967188412Sthompsa
968184610Salfred	/* if we want promiscuous mode, set the allframes bit: */
969188412Sthompsa	if (ifp->if_flags & IFF_PROMISC)
970188412Sthompsa		AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
971188412Sthompsa	else
972188412Sthompsa		AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
973184610Salfred}
974184610Salfred
975184610Salfred/*
976184610Salfred * Set media options.
977184610Salfred */
978184610Salfredstatic int
979188412Sthompsaaue_ifmedia_upd(struct ifnet *ifp)
980184610Salfred{
981184610Salfred	struct aue_softc *sc = ifp->if_softc;
982184610Salfred	struct mii_data *mii = GET_MII(sc);
983184610Salfred
984188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
985184610Salfred
986188412Sthompsa        sc->sc_flags &= ~AUE_FLAG_LINK;
987184610Salfred	if (mii->mii_instance) {
988184610Salfred		struct mii_softc *miisc;
989184610Salfred
990188412Sthompsa		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
991184610Salfred			mii_phy_reset(miisc);
992184610Salfred	}
993184610Salfred	mii_mediachg(mii);
994188412Sthompsa	return (0);
995184610Salfred}
996184610Salfred
997184610Salfred/*
998184610Salfred * Report current media status.
999184610Salfred */
1000184610Salfredstatic void
1001188412Sthompsaaue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1002184610Salfred{
1003184610Salfred	struct aue_softc *sc = ifp->if_softc;
1004188412Sthompsa	struct mii_data *mii = GET_MII(sc);
1005184610Salfred
1006188412Sthompsa	AUE_LOCK(sc);
1007188412Sthompsa	mii_pollstat(mii);
1008188412Sthompsa	AUE_UNLOCK(sc);
1009188412Sthompsa	ifmr->ifm_active = mii->mii_media_active;
1010188412Sthompsa	ifmr->ifm_status = mii->mii_media_status;
1011184610Salfred}
1012184610Salfred
1013184610Salfred/*
1014184610Salfred * Stop the adapter and free any mbufs allocated to the
1015184610Salfred * RX and TX lists.
1016184610Salfred */
1017184610Salfredstatic void
1018188412Sthompsaaue_stop(struct usb2_ether *ue)
1019184610Salfred{
1020188412Sthompsa	struct aue_softc *sc = usb2_ether_getsc(ue);
1021188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
1022184610Salfred
1023188412Sthompsa	AUE_LOCK_ASSERT(sc, MA_OWNED);
1024184610Salfred
1025188412Sthompsa	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1026188412Sthompsa	sc->sc_flags &= ~AUE_FLAG_LINK;
1027184610Salfred
1028184610Salfred	/*
1029184610Salfred	 * stop all the transfers, if not already stopped:
1030184610Salfred	 */
1031187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]);
1032187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]);
1033187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]);
1034184610Salfred
1035188412Sthompsa	aue_csr_write_1(sc, AUE_CTL0, 0);
1036188412Sthompsa	aue_csr_write_1(sc, AUE_CTL1, 0);
1037188412Sthompsa	aue_reset(sc);
1038184610Salfred}
1039