xref: /openbsd/sys/dev/usb/if_uaq.c (revision 497492a7)
1*497492a7Sjmatthew /*	$OpenBSD: if_uaq.c,v 1.1 2021/09/04 12:11:45 jmatthew Exp $	*/
2*497492a7Sjmatthew /*-
3*497492a7Sjmatthew  * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org>
4*497492a7Sjmatthew  * All rights reserved.
5*497492a7Sjmatthew  *
6*497492a7Sjmatthew  * Redistribution and use in source and binary forms, with or without
7*497492a7Sjmatthew  * modification, are permitted provided that the following conditions
8*497492a7Sjmatthew  * are met:
9*497492a7Sjmatthew  * 1. Redistributions of source code must retain the above copyright
10*497492a7Sjmatthew  *    notice, this list of conditions and the following disclaimer.
11*497492a7Sjmatthew  * 2. Redistributions in binary form must reproduce the above copyright
12*497492a7Sjmatthew  *    notice, this list of conditions and the following disclaimer in the
13*497492a7Sjmatthew  *    documentation and/or other materials provided with the distribution.
14*497492a7Sjmatthew  *
15*497492a7Sjmatthew  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*497492a7Sjmatthew  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*497492a7Sjmatthew  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*497492a7Sjmatthew  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*497492a7Sjmatthew  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*497492a7Sjmatthew  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*497492a7Sjmatthew  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*497492a7Sjmatthew  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*497492a7Sjmatthew  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*497492a7Sjmatthew  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*497492a7Sjmatthew  * SUCH DAMAGE.
26*497492a7Sjmatthew  */
27*497492a7Sjmatthew 
28*497492a7Sjmatthew #include "bpfilter.h"
29*497492a7Sjmatthew #include "vlan.h"
30*497492a7Sjmatthew 
31*497492a7Sjmatthew #include <sys/param.h>
32*497492a7Sjmatthew #include <sys/systm.h>
33*497492a7Sjmatthew #include <sys/sockio.h>
34*497492a7Sjmatthew #include <sys/rwlock.h>
35*497492a7Sjmatthew #include <sys/mbuf.h>
36*497492a7Sjmatthew #include <sys/kernel.h>
37*497492a7Sjmatthew #include <sys/socket.h>
38*497492a7Sjmatthew #include <sys/device.h>
39*497492a7Sjmatthew 
40*497492a7Sjmatthew #include <machine/bus.h>
41*497492a7Sjmatthew 
42*497492a7Sjmatthew #include <net/if.h>
43*497492a7Sjmatthew #include <net/if_media.h>
44*497492a7Sjmatthew 
45*497492a7Sjmatthew #if NBPFILTER > 0
46*497492a7Sjmatthew #include <net/bpf.h>
47*497492a7Sjmatthew #endif
48*497492a7Sjmatthew 
49*497492a7Sjmatthew #include <netinet/in.h>
50*497492a7Sjmatthew #include <netinet/if_ether.h>
51*497492a7Sjmatthew 
52*497492a7Sjmatthew #include <dev/usb/usb.h>
53*497492a7Sjmatthew #include <dev/usb/usbdi.h>
54*497492a7Sjmatthew #include <dev/usb/usbdi_util.h>
55*497492a7Sjmatthew #include <dev/usb/usbdivar.h>
56*497492a7Sjmatthew #include <dev/usb/usbdevs.h>
57*497492a7Sjmatthew 
58*497492a7Sjmatthew #ifdef UAQ_DEBUG
59*497492a7Sjmatthew #define DPRINTF(x)	do { if (uaqdebug) printf x; } while (0)
60*497492a7Sjmatthew #define DPRINTFN(n,x)	do { if (uaqdebug >= (n)) printf x; } while (0)
61*497492a7Sjmatthew int	uaqdebug = 0;
62*497492a7Sjmatthew #else
63*497492a7Sjmatthew #define DPRINTF(x)
64*497492a7Sjmatthew #define DPRINTFN(n,x)
65*497492a7Sjmatthew #endif
66*497492a7Sjmatthew 
67*497492a7Sjmatthew #define UAQ_ENDPT_RX		0
68*497492a7Sjmatthew #define UAQ_ENDPT_TX		1
69*497492a7Sjmatthew #define UAQ_ENDPT_INTR		2
70*497492a7Sjmatthew #define UAQ_ENDPT_MAX		3
71*497492a7Sjmatthew 
72*497492a7Sjmatthew #define UAQ_TX_LIST_CNT		1
73*497492a7Sjmatthew #define UAQ_RX_LIST_CNT		1
74*497492a7Sjmatthew #define UAQ_TX_BUF_ALIGN	8
75*497492a7Sjmatthew #define UAQ_RX_BUF_ALIGN	8
76*497492a7Sjmatthew 
77*497492a7Sjmatthew #define UAQ_TX_BUFSZ		16384
78*497492a7Sjmatthew #define UAQ_RX_BUFSZ		32768
79*497492a7Sjmatthew 
80*497492a7Sjmatthew #define UAQ_CTL_READ		1
81*497492a7Sjmatthew #define UAQ_CTL_WRITE		2
82*497492a7Sjmatthew 
83*497492a7Sjmatthew #define UAQ_MCAST_FILTER_SIZE	8
84*497492a7Sjmatthew 
85*497492a7Sjmatthew /* control commands */
86*497492a7Sjmatthew #define UAQ_CMD_ACCESS_MAC	0x01
87*497492a7Sjmatthew #define UAQ_CMD_FLASH_PARAM	0x20
88*497492a7Sjmatthew #define UAQ_CMD_PHY_POWER	0x31
89*497492a7Sjmatthew #define UAQ_CMD_WOL_CFG		0x60
90*497492a7Sjmatthew #define UAQ_CMD_PHY_OPS		0x61
91*497492a7Sjmatthew 
92*497492a7Sjmatthew /* SFR registers */
93*497492a7Sjmatthew #define UAQ_SFR_GENERAL_STATUS	0x03
94*497492a7Sjmatthew #define UAQ_SFR_CHIP_STATUS	0x05
95*497492a7Sjmatthew #define UAQ_SFR_RX_CTL		0x0B
96*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_STOP	0x0000
97*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_PRO	0x0001
98*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AMALL	0x0002
99*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AB	0x0008
100*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AM	0x0010
101*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_START	0x0080
102*497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_IPE	0x0200
103*497492a7Sjmatthew #define UAQ_SFR_IPG_0		0x0D
104*497492a7Sjmatthew #define UAQ_SFR_NODE_ID		0x10
105*497492a7Sjmatthew #define UAQ_SFR_MCAST_FILTER	0x16
106*497492a7Sjmatthew #define UAQ_SFR_MEDIUM_STATUS_MODE 0x22
107*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_XGMIIMODE	0x0001
108*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_FULL_DUPLEX	0x0002
109*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_RXFLOW_CTRLEN	0x0010
110*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_TXFLOW_CTRLEN	0x0020
111*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_JUMBO_EN	0x0040
112*497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_RECEIVE_EN	0x0100
113*497492a7Sjmatthew #define UAQ_SFR_MONITOR_MODE	0x24
114*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_EPHYRW	0x01
115*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWLC	0x02
116*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWMP	0x04
117*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWWF	0x08
118*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RW_FLAG	0x10
119*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_PMEPOL	0x20
120*497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_PMETYPE	0x40
121*497492a7Sjmatthew #define UAQ_SFR_RX_BULKIN_QCTRL 0x2E
122*497492a7Sjmatthew #define UAQ_SFR_RXCOE_CTL	0x34
123*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_IP		0x01
124*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_TCP		0x02
125*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_UDP		0x04
126*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_ICMP		0x08
127*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_IGMP		0x10
128*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_TCPV6		0x20
129*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_UDPV6		0x40
130*497492a7Sjmatthew #define  UAQ_SFR_RXCOE_ICMV6		0x80
131*497492a7Sjmatthew #define UAQ_SFR_TXCOE_CTL	0x35
132*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_IP		0x01
133*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_TCP		0x02
134*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_UDP		0x04
135*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_ICMP		0x08
136*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_IGMP		0x10
137*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_TCPV6		0x20
138*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_UDPV6		0x40
139*497492a7Sjmatthew #define  UAQ_SFR_TXCOE_ICMV6		0x80
140*497492a7Sjmatthew #define UAQ_SFR_BM_INT_MASK	0x41
141*497492a7Sjmatthew #define UAQ_SFR_BMRX_DMA_CTRL	0x43
142*497492a7Sjmatthew #define  UAQ_SFR_BMRX_DMA_EN	0x80
143*497492a7Sjmatthew #define UAQ_SFR_BMTX_DMA_CTRL	0x46
144*497492a7Sjmatthew #define UAQ_SFR_PAUSE_WATERLVL_LOW 0x54
145*497492a7Sjmatthew #define UAQ_SFR_ARC_CTRL	0x9E
146*497492a7Sjmatthew #define UAQ_SFR_SWP_CTRL	0xB1
147*497492a7Sjmatthew #define UAQ_SFR_TX_PAUSE_RESEND_T 0xB2
148*497492a7Sjmatthew #define UAQ_SFR_ETH_MAC_PATH	0xB7
149*497492a7Sjmatthew #define  UAQ_SFR_RX_PATH_READY	0x01
150*497492a7Sjmatthew #define UAQ_SFR_BULK_OUT_CTRL	0xB9
151*497492a7Sjmatthew #define  UAQ_SFR_BULK_OUT_FLUSH_EN	0x01
152*497492a7Sjmatthew #define  UAQ_SFR_BULK_OUT_EFF_EN	0x02
153*497492a7Sjmatthew 
154*497492a7Sjmatthew #define UAQ_FW_VER_MAJOR	0xDA
155*497492a7Sjmatthew #define UAQ_FW_VER_MINOR	0xDB
156*497492a7Sjmatthew #define UAQ_FW_VER_REV		0xDC
157*497492a7Sjmatthew 
158*497492a7Sjmatthew /* phy ops */
159*497492a7Sjmatthew #define UAQ_PHY_ADV_100M	(1 << 0)
160*497492a7Sjmatthew #define UAQ_PHY_ADV_1G		(1 << 1)
161*497492a7Sjmatthew #define UAQ_PHY_ADV_2_5G	(1 << 2)
162*497492a7Sjmatthew #define UAQ_PHY_ADV_5G		(1 << 3)
163*497492a7Sjmatthew #define UAQ_PHY_ADV_MASK	0x0F
164*497492a7Sjmatthew 
165*497492a7Sjmatthew #define UAQ_PHY_PAUSE		(1 << 16)
166*497492a7Sjmatthew #define UAQ_PHY_ASYM_PAUSE	(1 << 17)
167*497492a7Sjmatthew #define UAQ_PHY_LOW_POWER	(1 << 18)
168*497492a7Sjmatthew #define UAQ_PHY_POWER_EN	(1 << 19)
169*497492a7Sjmatthew #define UAQ_PHY_WOL		(1 << 20)
170*497492a7Sjmatthew #define UAQ_PHY_DOWNSHIFT	(1 << 21)
171*497492a7Sjmatthew 
172*497492a7Sjmatthew #define UAQ_PHY_DSH_RETRY_SHIFT	0x18
173*497492a7Sjmatthew #define UAQ_PHY_DSH_RETRY_MASK	0xF000000
174*497492a7Sjmatthew 
175*497492a7Sjmatthew /* status */
176*497492a7Sjmatthew #define UAQ_STATUS_LINK		0x8000
177*497492a7Sjmatthew #define UAQ_STATUS_SPEED_MASK	0x7F00
178*497492a7Sjmatthew #define UAQ_STATUS_SPEED_SHIFT	8
179*497492a7Sjmatthew #define UAQ_STATUS_SPEED_5G	0x000F
180*497492a7Sjmatthew #define UAQ_STATUS_SPEED_2_5G	0x0010
181*497492a7Sjmatthew #define UAQ_STATUS_SPEED_1G	0x0011
182*497492a7Sjmatthew #define UAQ_STATUS_SPEED_100M	0x0013
183*497492a7Sjmatthew 
184*497492a7Sjmatthew /* rx descriptor */
185*497492a7Sjmatthew #define UAQ_RX_HDR_COUNT_MASK	0x1FFF
186*497492a7Sjmatthew #define UAQ_RX_HDR_OFFSET_MASK	0xFFFFE000
187*497492a7Sjmatthew #define UAQ_RX_HDR_OFFSET_SHIFT	13
188*497492a7Sjmatthew 
189*497492a7Sjmatthew /* rx packet descriptor */
190*497492a7Sjmatthew #define UAQ_RX_PKT_L4_ERR	0x01
191*497492a7Sjmatthew #define UAQ_RX_PKT_L3_ERR	0x02
192*497492a7Sjmatthew #define UAQ_RX_PKT_L4_MASK	0x1C
193*497492a7Sjmatthew #define UAQ_RX_PKT_L4_UDP	0x04
194*497492a7Sjmatthew #define UAQ_RX_PKT_L4_TCP	0x10
195*497492a7Sjmatthew #define UAQ_RX_PKT_L3_MASK	0x60
196*497492a7Sjmatthew #define UAQ_RX_PKT_L3_IP	0x20
197*497492a7Sjmatthew #define UAQ_RX_PKT_L3_IP6	0x40
198*497492a7Sjmatthew #define UAQ_RX_PKT_VLAN		0x400
199*497492a7Sjmatthew #define UAQ_RX_PKT_RX_OK	0x800
200*497492a7Sjmatthew #define UAQ_RX_PKT_DROP		0x80000000
201*497492a7Sjmatthew #define UAQ_RX_PKT_LEN_MASK	0x7FFF0000
202*497492a7Sjmatthew #define UAQ_RX_PKT_LEN_SHIFT	16
203*497492a7Sjmatthew #define UAQ_RX_PKT_VLAN_SHIFT	32
204*497492a7Sjmatthew 
205*497492a7Sjmatthew /* tx packet descriptor */
206*497492a7Sjmatthew #define UAQ_TX_PKT_LEN_MASK	0x1FFFFF
207*497492a7Sjmatthew #define UAQ_TX_PKT_DROP_PADD	(1 << 28)
208*497492a7Sjmatthew #define UAQ_TX_PKT_VLAN		(1 << 29)
209*497492a7Sjmatthew #define UAQ_TX_PKT_VLAN_MASK	0xFFFF
210*497492a7Sjmatthew #define UAQ_TX_PKT_VLAN_SHIFT	0x30
211*497492a7Sjmatthew 
212*497492a7Sjmatthew 
213*497492a7Sjmatthew struct uaq_chain {
214*497492a7Sjmatthew 	struct uaq_softc	*uc_sc;
215*497492a7Sjmatthew 	struct usbd_xfer	*uc_xfer;
216*497492a7Sjmatthew 	char			*uc_buf;
217*497492a7Sjmatthew 	uint32_t		 uc_cnt;
218*497492a7Sjmatthew 	uint32_t		 uc_buflen;
219*497492a7Sjmatthew 	uint32_t		 uc_bufmax;
220*497492a7Sjmatthew 	SLIST_ENTRY(uaq_chain)	 uc_list;
221*497492a7Sjmatthew 	uint8_t			 uc_idx;
222*497492a7Sjmatthew };
223*497492a7Sjmatthew 
224*497492a7Sjmatthew struct uaq_cdata {
225*497492a7Sjmatthew 	struct uaq_chain	 uaq_rx_chain[UAQ_RX_LIST_CNT];
226*497492a7Sjmatthew 	struct uaq_chain	 uaq_tx_chain[UAQ_TX_LIST_CNT];
227*497492a7Sjmatthew 	SLIST_HEAD(uaq_list_head, uaq_chain) uaq_tx_free;
228*497492a7Sjmatthew };
229*497492a7Sjmatthew 
230*497492a7Sjmatthew struct uaq_softc {
231*497492a7Sjmatthew 	struct device		 sc_dev;
232*497492a7Sjmatthew 	struct usbd_device	*sc_udev;
233*497492a7Sjmatthew 
234*497492a7Sjmatthew 	struct usbd_interface	*sc_iface;
235*497492a7Sjmatthew 	struct usb_task		 sc_link_task;
236*497492a7Sjmatthew 	struct timeval		 sc_rx_notice;
237*497492a7Sjmatthew 	int			 sc_ed[UAQ_ENDPT_MAX];
238*497492a7Sjmatthew 	struct usbd_pipe	*sc_ep[UAQ_ENDPT_MAX];
239*497492a7Sjmatthew 	int			 sc_out_frame_size;
240*497492a7Sjmatthew 
241*497492a7Sjmatthew 	struct arpcom		 sc_ac;
242*497492a7Sjmatthew 	struct ifmedia		 sc_ifmedia;
243*497492a7Sjmatthew 
244*497492a7Sjmatthew 	struct uaq_cdata	 sc_cdata;
245*497492a7Sjmatthew 	uint64_t		 sc_link_status;
246*497492a7Sjmatthew 	int			 sc_link_speed;
247*497492a7Sjmatthew 
248*497492a7Sjmatthew 	uint32_t		 sc_phy_cfg;
249*497492a7Sjmatthew 	uint16_t		 sc_rxctl;
250*497492a7Sjmatthew };
251*497492a7Sjmatthew 
252*497492a7Sjmatthew const struct usb_devno uaq_devs[] = {
253*497492a7Sjmatthew 	{ USB_VENDOR_AQUANTIA, USB_PRODUCT_AQUANTIA_AQC111 },
254*497492a7Sjmatthew 	{ USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX111 },
255*497492a7Sjmatthew 	{ USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX112 },
256*497492a7Sjmatthew 	{ USB_VENDOR_TRENDNET, USB_PRODUCT_TRENDNET_TUCET5G },
257*497492a7Sjmatthew 	{ USB_VENDOR_QNAP, USB_PRODUCT_QNAP_UC5G1T },
258*497492a7Sjmatthew };
259*497492a7Sjmatthew 
260*497492a7Sjmatthew int		uaq_match(struct device *, void *, void *);
261*497492a7Sjmatthew void		uaq_attach(struct device *, struct device *, void *);
262*497492a7Sjmatthew int		uaq_detach(struct device *, int);
263*497492a7Sjmatthew 
264*497492a7Sjmatthew int		uaq_ctl(struct uaq_softc *, uint8_t, uint8_t, uint16_t,
265*497492a7Sjmatthew 		    uint16_t, void *, int);
266*497492a7Sjmatthew int		uaq_read_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
267*497492a7Sjmatthew 		    void *, int);
268*497492a7Sjmatthew int		uaq_write_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
269*497492a7Sjmatthew 		    void *, int);
270*497492a7Sjmatthew uint8_t		uaq_read_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
271*497492a7Sjmatthew uint16_t	uaq_read_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
272*497492a7Sjmatthew uint32_t	uaq_read_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
273*497492a7Sjmatthew int		uaq_write_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
274*497492a7Sjmatthew 		    uint32_t);
275*497492a7Sjmatthew int		uaq_write_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
276*497492a7Sjmatthew 		    uint32_t);
277*497492a7Sjmatthew int		uaq_write_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
278*497492a7Sjmatthew 		    uint32_t);
279*497492a7Sjmatthew 
280*497492a7Sjmatthew int		uaq_ifmedia_upd(struct ifnet *);
281*497492a7Sjmatthew void		uaq_ifmedia_sts(struct ifnet *, struct ifmediareq *);
282*497492a7Sjmatthew void		uaq_add_media_types(struct uaq_softc *);
283*497492a7Sjmatthew void		uaq_iff(struct uaq_softc *);
284*497492a7Sjmatthew 
285*497492a7Sjmatthew void		uaq_init(void *);
286*497492a7Sjmatthew int		uaq_ioctl(struct ifnet *, u_long, caddr_t);
287*497492a7Sjmatthew int		uaq_xfer_list_init(struct uaq_softc *, struct uaq_chain *,
288*497492a7Sjmatthew 		    uint32_t, int);
289*497492a7Sjmatthew void		uaq_xfer_list_free(struct uaq_softc *, struct uaq_chain *, int);
290*497492a7Sjmatthew 
291*497492a7Sjmatthew void		uaq_stop(struct uaq_softc *);
292*497492a7Sjmatthew void		uaq_link(struct uaq_softc *);
293*497492a7Sjmatthew void		uaq_intr(struct usbd_xfer *, void *, usbd_status);
294*497492a7Sjmatthew void		uaq_start(struct ifnet *);
295*497492a7Sjmatthew void		uaq_rxeof(struct usbd_xfer *, void *, usbd_status);
296*497492a7Sjmatthew void		uaq_txeof(struct usbd_xfer *, void *, usbd_status);
297*497492a7Sjmatthew void		uaq_watchdog(struct ifnet *);
298*497492a7Sjmatthew void		uaq_reset(struct uaq_softc *);
299*497492a7Sjmatthew 
300*497492a7Sjmatthew int		uaq_encap_txpkt(struct uaq_softc *, struct mbuf *, char *,
301*497492a7Sjmatthew 		    uint32_t);
302*497492a7Sjmatthew int		uaq_encap_xfer(struct uaq_softc *, struct uaq_chain *);
303*497492a7Sjmatthew 
304*497492a7Sjmatthew struct cfdriver uaq_cd = {
305*497492a7Sjmatthew 	NULL, "uaq", DV_IFNET
306*497492a7Sjmatthew };
307*497492a7Sjmatthew 
308*497492a7Sjmatthew const struct cfattach uaq_ca = {
309*497492a7Sjmatthew 	sizeof(struct uaq_softc), uaq_match, uaq_attach, uaq_detach
310*497492a7Sjmatthew };
311*497492a7Sjmatthew 
312*497492a7Sjmatthew int
313*497492a7Sjmatthew uaq_ctl(struct uaq_softc *sc, uint8_t rw, uint8_t cmd, uint16_t val,
314*497492a7Sjmatthew     uint16_t index, void *buf, int len)
315*497492a7Sjmatthew {
316*497492a7Sjmatthew 	usb_device_request_t	req;
317*497492a7Sjmatthew 	usbd_status		err;
318*497492a7Sjmatthew 
319*497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
320*497492a7Sjmatthew 		return 0;
321*497492a7Sjmatthew 
322*497492a7Sjmatthew 	if (rw == UAQ_CTL_WRITE)
323*497492a7Sjmatthew 		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
324*497492a7Sjmatthew 	else
325*497492a7Sjmatthew 		req.bmRequestType = UT_READ_VENDOR_DEVICE;
326*497492a7Sjmatthew 	req.bRequest = cmd;
327*497492a7Sjmatthew 	USETW(req.wValue, val);
328*497492a7Sjmatthew 	USETW(req.wIndex, index);
329*497492a7Sjmatthew 	USETW(req.wLength, len);
330*497492a7Sjmatthew 
331*497492a7Sjmatthew 	DPRINTFN(5, ("uaq_ctl: rw %d, val 0x%04hx, index 0x%04hx, len %d\n",
332*497492a7Sjmatthew 	    rw, val, index, len));
333*497492a7Sjmatthew 	err = usbd_do_request(sc->sc_udev, &req, buf);
334*497492a7Sjmatthew 	if (err) {
335*497492a7Sjmatthew 		DPRINTF(("uaq_ctl: error %d\n", err));
336*497492a7Sjmatthew 		return -1;
337*497492a7Sjmatthew 	}
338*497492a7Sjmatthew 
339*497492a7Sjmatthew 	return 0;
340*497492a7Sjmatthew }
341*497492a7Sjmatthew 
342*497492a7Sjmatthew int
343*497492a7Sjmatthew uaq_read_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index,
344*497492a7Sjmatthew     void *buf, int len)
345*497492a7Sjmatthew {
346*497492a7Sjmatthew 	return (uaq_ctl(sc, UAQ_CTL_READ, cmd, addr, index, buf, len));
347*497492a7Sjmatthew }
348*497492a7Sjmatthew 
349*497492a7Sjmatthew int
350*497492a7Sjmatthew uaq_write_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index,
351*497492a7Sjmatthew     void *buf, int len)
352*497492a7Sjmatthew {
353*497492a7Sjmatthew 	return (uaq_ctl(sc, UAQ_CTL_WRITE, cmd, addr, index, buf, len));
354*497492a7Sjmatthew }
355*497492a7Sjmatthew 
356*497492a7Sjmatthew uint8_t
357*497492a7Sjmatthew uaq_read_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
358*497492a7Sjmatthew {
359*497492a7Sjmatthew 	uint8_t		val;
360*497492a7Sjmatthew 
361*497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 1);
362*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_1: cmd %x reg %x index %x = %x\n", cmd, reg,
363*497492a7Sjmatthew 	    index, val));
364*497492a7Sjmatthew 	return (val);
365*497492a7Sjmatthew }
366*497492a7Sjmatthew 
367*497492a7Sjmatthew uint16_t
368*497492a7Sjmatthew uaq_read_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
369*497492a7Sjmatthew {
370*497492a7Sjmatthew 	uint16_t	val;
371*497492a7Sjmatthew 
372*497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 2);
373*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_2: cmd %x reg %x index %x = %x\n", cmd, reg,
374*497492a7Sjmatthew 	    index, UGETW(&val)));
375*497492a7Sjmatthew 
376*497492a7Sjmatthew 	return (UGETW(&val));
377*497492a7Sjmatthew }
378*497492a7Sjmatthew 
379*497492a7Sjmatthew uint32_t
380*497492a7Sjmatthew uaq_read_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
381*497492a7Sjmatthew {
382*497492a7Sjmatthew 	uint32_t	val;
383*497492a7Sjmatthew 
384*497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 4);
385*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_4: cmd %x reg %x index %x = %x\n", cmd, reg,
386*497492a7Sjmatthew 	    index, UGETDW(&val)));
387*497492a7Sjmatthew 	return (UGETDW(&val));
388*497492a7Sjmatthew }
389*497492a7Sjmatthew 
390*497492a7Sjmatthew int
391*497492a7Sjmatthew uaq_write_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
392*497492a7Sjmatthew     uint32_t val)
393*497492a7Sjmatthew {
394*497492a7Sjmatthew 	uint8_t		temp;
395*497492a7Sjmatthew 
396*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_1: cmd %x reg %x index %x: %x\n", cmd, reg,
397*497492a7Sjmatthew 	    index, val));
398*497492a7Sjmatthew 	temp = val & 0xff;
399*497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 1));
400*497492a7Sjmatthew }
401*497492a7Sjmatthew 
402*497492a7Sjmatthew int
403*497492a7Sjmatthew uaq_write_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
404*497492a7Sjmatthew     uint32_t val)
405*497492a7Sjmatthew {
406*497492a7Sjmatthew 	uint16_t	temp;
407*497492a7Sjmatthew 
408*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_2: cmd %x reg %x index %x: %x\n", cmd, reg,
409*497492a7Sjmatthew 	    index, val));
410*497492a7Sjmatthew 	USETW(&temp, val & 0xffff);
411*497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 2));
412*497492a7Sjmatthew }
413*497492a7Sjmatthew 
414*497492a7Sjmatthew int
415*497492a7Sjmatthew uaq_write_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
416*497492a7Sjmatthew     uint32_t val)
417*497492a7Sjmatthew {
418*497492a7Sjmatthew 	uint8_t	temp[4];
419*497492a7Sjmatthew 
420*497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_4: cmd %x reg %x index %x: %x\n", cmd, reg,
421*497492a7Sjmatthew 	    index, val));
422*497492a7Sjmatthew 	USETDW(temp, val);
423*497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 4));
424*497492a7Sjmatthew }
425*497492a7Sjmatthew 
426*497492a7Sjmatthew int
427*497492a7Sjmatthew uaq_match(struct device *parent, void *match, void *aux)
428*497492a7Sjmatthew {
429*497492a7Sjmatthew 	struct usb_attach_arg	*uaa = aux;
430*497492a7Sjmatthew 
431*497492a7Sjmatthew 	if (uaa->iface == NULL || uaa->configno != 1)
432*497492a7Sjmatthew 		return (UMATCH_NONE);
433*497492a7Sjmatthew 
434*497492a7Sjmatthew 	return (usb_lookup(uaq_devs, uaa->vendor, uaa->product) != NULL ?
435*497492a7Sjmatthew 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
436*497492a7Sjmatthew }
437*497492a7Sjmatthew 
438*497492a7Sjmatthew void
439*497492a7Sjmatthew uaq_attach(struct device *parent, struct device *self, void *aux)
440*497492a7Sjmatthew {
441*497492a7Sjmatthew 	struct uaq_softc		*sc = (struct uaq_softc *)self;
442*497492a7Sjmatthew 	struct usb_attach_arg		*uaa = aux;
443*497492a7Sjmatthew 	usb_interface_descriptor_t	*id;
444*497492a7Sjmatthew 	usb_endpoint_descriptor_t	*ed;
445*497492a7Sjmatthew 	struct ifnet			*ifp;
446*497492a7Sjmatthew 	int				i, s;
447*497492a7Sjmatthew 
448*497492a7Sjmatthew 	sc->sc_udev = uaa->device;
449*497492a7Sjmatthew 	sc->sc_iface = uaa->iface;
450*497492a7Sjmatthew 
451*497492a7Sjmatthew 	usb_init_task(&sc->sc_link_task, (void (*)(void *))uaq_link, sc,
452*497492a7Sjmatthew 	    USB_TASK_TYPE_GENERIC);
453*497492a7Sjmatthew 
454*497492a7Sjmatthew 	id = usbd_get_interface_descriptor(sc->sc_iface);
455*497492a7Sjmatthew 
456*497492a7Sjmatthew 	for (i = 0; i < id->bNumEndpoints; i++) {
457*497492a7Sjmatthew 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
458*497492a7Sjmatthew 		if (!ed) {
459*497492a7Sjmatthew 			printf("%s: couldn't get ep %d\n",
460*497492a7Sjmatthew 			    sc->sc_dev.dv_xname, i);
461*497492a7Sjmatthew 			return;
462*497492a7Sjmatthew 		}
463*497492a7Sjmatthew 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
464*497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
465*497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_RX] = ed->bEndpointAddress;
466*497492a7Sjmatthew 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
467*497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
468*497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_TX] = ed->bEndpointAddress;
469*497492a7Sjmatthew 			sc->sc_out_frame_size = UGETW(ed->wMaxPacketSize);
470*497492a7Sjmatthew 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
471*497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
472*497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_INTR] = ed->bEndpointAddress;
473*497492a7Sjmatthew 		}
474*497492a7Sjmatthew 	}
475*497492a7Sjmatthew 
476*497492a7Sjmatthew 	if ((sc->sc_ed[UAQ_ENDPT_RX] == 0) ||
477*497492a7Sjmatthew 	    (sc->sc_ed[UAQ_ENDPT_TX] == 0) ||
478*497492a7Sjmatthew 	    (sc->sc_ed[UAQ_ENDPT_INTR] == 0)) {
479*497492a7Sjmatthew 		printf("%s: missing one or more endpoints (%d, %d, %d)\n",
480*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, sc->sc_ed[UAQ_ENDPT_RX],
481*497492a7Sjmatthew 		    sc->sc_ed[UAQ_ENDPT_TX], sc->sc_ed[UAQ_ENDPT_INTR]);
482*497492a7Sjmatthew 		return;
483*497492a7Sjmatthew 	}
484*497492a7Sjmatthew 
485*497492a7Sjmatthew 	s = splnet();
486*497492a7Sjmatthew 
487*497492a7Sjmatthew 	printf("%s: ver %u.%u.%u", sc->sc_dev.dv_xname,
488*497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MAJOR, 1) & 0x7f,
489*497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MINOR, 1),
490*497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_REV, 1));
491*497492a7Sjmatthew 
492*497492a7Sjmatthew 	uaq_read_mem(sc, UAQ_CMD_FLASH_PARAM, 0, 0, &sc->sc_ac.ac_enaddr,
493*497492a7Sjmatthew 	    ETHER_ADDR_LEN);
494*497492a7Sjmatthew 	printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));
495*497492a7Sjmatthew 
496*497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
497*497492a7Sjmatthew 	ifp->if_softc = sc;
498*497492a7Sjmatthew 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
499*497492a7Sjmatthew 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
500*497492a7Sjmatthew 	ifp->if_ioctl = uaq_ioctl;
501*497492a7Sjmatthew 	ifp->if_start = uaq_start;
502*497492a7Sjmatthew 	ifp->if_watchdog = uaq_watchdog;
503*497492a7Sjmatthew 
504*497492a7Sjmatthew 	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 |
505*497492a7Sjmatthew 	    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
506*497492a7Sjmatthew 
507*497492a7Sjmatthew #if NVLAN > 0
508*497492a7Sjmatthew 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
509*497492a7Sjmatthew #endif
510*497492a7Sjmatthew 
511*497492a7Sjmatthew 	ifmedia_init(&sc->sc_ifmedia, IFM_IMASK, uaq_ifmedia_upd,
512*497492a7Sjmatthew 	    uaq_ifmedia_sts);
513*497492a7Sjmatthew 	uaq_add_media_types(sc);
514*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
515*497492a7Sjmatthew 	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
516*497492a7Sjmatthew 	sc->sc_ifmedia.ifm_media = sc->sc_ifmedia.ifm_cur->ifm_media;
517*497492a7Sjmatthew 
518*497492a7Sjmatthew 	if_attach(ifp);
519*497492a7Sjmatthew 	ether_ifattach(ifp);
520*497492a7Sjmatthew 
521*497492a7Sjmatthew 	splx(s);
522*497492a7Sjmatthew }
523*497492a7Sjmatthew 
524*497492a7Sjmatthew int
525*497492a7Sjmatthew uaq_detach(struct device *self, int flags)
526*497492a7Sjmatthew {
527*497492a7Sjmatthew 	struct uaq_softc	*sc = (struct uaq_softc *)self;
528*497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
529*497492a7Sjmatthew 	int			s;
530*497492a7Sjmatthew 
531*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_TX] != NULL)
532*497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_TX]);
533*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_RX] != NULL)
534*497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_RX]);
535*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL)
536*497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_INTR]);
537*497492a7Sjmatthew 
538*497492a7Sjmatthew 	s = splusb();
539*497492a7Sjmatthew 
540*497492a7Sjmatthew 	usb_rem_task(sc->sc_udev, &sc->sc_link_task);
541*497492a7Sjmatthew 
542*497492a7Sjmatthew 	usb_detach_wait(&sc->sc_dev);
543*497492a7Sjmatthew 
544*497492a7Sjmatthew 	if (ifp->if_flags & IFF_RUNNING)
545*497492a7Sjmatthew 		uaq_stop(sc);
546*497492a7Sjmatthew 
547*497492a7Sjmatthew 	if (ifp->if_softc != NULL) {
548*497492a7Sjmatthew 		ether_ifdetach(ifp);
549*497492a7Sjmatthew 		if_detach(ifp);
550*497492a7Sjmatthew 	}
551*497492a7Sjmatthew 
552*497492a7Sjmatthew 	splx(s);
553*497492a7Sjmatthew 
554*497492a7Sjmatthew 	return 0;
555*497492a7Sjmatthew }
556*497492a7Sjmatthew 
557*497492a7Sjmatthew int
558*497492a7Sjmatthew uaq_ifmedia_upd(struct ifnet *ifp)
559*497492a7Sjmatthew {
560*497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
561*497492a7Sjmatthew 	struct ifmedia		*ifm = &sc->sc_ifmedia;
562*497492a7Sjmatthew 	int			 auto_adv;
563*497492a7Sjmatthew 
564*497492a7Sjmatthew 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
565*497492a7Sjmatthew 		return (EINVAL);
566*497492a7Sjmatthew 
567*497492a7Sjmatthew 	auto_adv = UAQ_PHY_ADV_100M | UAQ_PHY_ADV_1G;
568*497492a7Sjmatthew 	if (sc->sc_udev->speed == USB_SPEED_SUPER)
569*497492a7Sjmatthew 		auto_adv |= UAQ_PHY_ADV_2_5G | UAQ_PHY_ADV_5G;
570*497492a7Sjmatthew 
571*497492a7Sjmatthew 	sc->sc_phy_cfg &= ~(UAQ_PHY_ADV_MASK);
572*497492a7Sjmatthew 	sc->sc_phy_cfg |= UAQ_PHY_PAUSE | UAQ_PHY_ASYM_PAUSE |
573*497492a7Sjmatthew 	    UAQ_PHY_DOWNSHIFT | (3 << UAQ_PHY_DSH_RETRY_SHIFT);
574*497492a7Sjmatthew 
575*497492a7Sjmatthew 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
576*497492a7Sjmatthew 	case IFM_AUTO:
577*497492a7Sjmatthew 		sc->sc_phy_cfg |= auto_adv;
578*497492a7Sjmatthew 		break;
579*497492a7Sjmatthew 	case IFM_5000_T:
580*497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_5G;
581*497492a7Sjmatthew 		break;
582*497492a7Sjmatthew 	case IFM_2500_T:
583*497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_2_5G;
584*497492a7Sjmatthew 		break;
585*497492a7Sjmatthew 	case IFM_1000_T:
586*497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_1G;
587*497492a7Sjmatthew 		break;
588*497492a7Sjmatthew 	case IFM_100_TX:
589*497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_100M;
590*497492a7Sjmatthew 		break;
591*497492a7Sjmatthew 	default:
592*497492a7Sjmatthew 		printf("%s: unsupported media type\n", sc->sc_dev.dv_xname);
593*497492a7Sjmatthew 		return (EINVAL);
594*497492a7Sjmatthew 	}
595*497492a7Sjmatthew 
596*497492a7Sjmatthew 	DPRINTFN(1, ("%s: phy cfg %x\n", sc->sc_dev.dv_xname, sc->sc_phy_cfg));
597*497492a7Sjmatthew 	uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg);
598*497492a7Sjmatthew 	return (0);
599*497492a7Sjmatthew }
600*497492a7Sjmatthew 
601*497492a7Sjmatthew void
602*497492a7Sjmatthew uaq_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
603*497492a7Sjmatthew {
604*497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
605*497492a7Sjmatthew 
606*497492a7Sjmatthew 	ifmr->ifm_status = IFM_AVALID;
607*497492a7Sjmatthew 	if (sc->sc_link_speed > 0) {
608*497492a7Sjmatthew 		ifmr->ifm_status |= IFM_ACTIVE;
609*497492a7Sjmatthew 		ifmr->ifm_active = IFM_ETHER | IFM_FDX;
610*497492a7Sjmatthew 		switch (sc->sc_link_speed) {
611*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
612*497492a7Sjmatthew 			ifmr->ifm_active |= IFM_5000_T;
613*497492a7Sjmatthew 			break;
614*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_2_5G:
615*497492a7Sjmatthew 			ifmr->ifm_active |= IFM_2500_T;
616*497492a7Sjmatthew 			break;
617*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_1G:
618*497492a7Sjmatthew 			ifmr->ifm_active |= IFM_1000_T;
619*497492a7Sjmatthew 			break;
620*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
621*497492a7Sjmatthew 			ifmr->ifm_active |= IFM_100_TX;
622*497492a7Sjmatthew 			break;
623*497492a7Sjmatthew 		default:
624*497492a7Sjmatthew 			break;
625*497492a7Sjmatthew 		}
626*497492a7Sjmatthew 	}
627*497492a7Sjmatthew }
628*497492a7Sjmatthew 
629*497492a7Sjmatthew void
630*497492a7Sjmatthew uaq_add_media_types(struct uaq_softc *sc)
631*497492a7Sjmatthew {
632*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
633*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0,
634*497492a7Sjmatthew 	    NULL);
635*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T, 0, NULL);
636*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T | IFM_FDX, 0,
637*497492a7Sjmatthew 	    NULL);
638*497492a7Sjmatthew 	/* only add 2.5G and 5G if at super speed */
639*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T, 0, NULL);
640*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T | IFM_FDX, 0,
641*497492a7Sjmatthew 	    NULL);
642*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T, 0, NULL);
643*497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T | IFM_FDX, 0,
644*497492a7Sjmatthew 	    NULL);
645*497492a7Sjmatthew }
646*497492a7Sjmatthew 
647*497492a7Sjmatthew void
648*497492a7Sjmatthew uaq_iff(struct uaq_softc *sc)
649*497492a7Sjmatthew {
650*497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
651*497492a7Sjmatthew 	struct ether_multi	*enm;
652*497492a7Sjmatthew 	struct ether_multistep	step;
653*497492a7Sjmatthew 	uint8_t			filter[UAQ_MCAST_FILTER_SIZE];
654*497492a7Sjmatthew 	uint32_t		hash;
655*497492a7Sjmatthew 
656*497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
657*497492a7Sjmatthew 		return;
658*497492a7Sjmatthew 
659*497492a7Sjmatthew 	sc->sc_rxctl &= ~(UAQ_SFR_RX_CTL_PRO | UAQ_SFR_RX_CTL_AMALL |
660*497492a7Sjmatthew 	    UAQ_SFR_RX_CTL_AM);
661*497492a7Sjmatthew 	if (ifp->if_flags & IFF_PROMISC || sc->sc_ac.ac_multirangecnt > 0) {
662*497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_PRO;
663*497492a7Sjmatthew 	} else if (ifp->if_flags & IFF_ALLMULTI ||
664*497492a7Sjmatthew 	    sc->sc_ac.ac_multirangecnt > 0) {
665*497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_AMALL;
666*497492a7Sjmatthew 	} else if (sc->sc_ac.ac_multicnt > 0) {
667*497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_AM;
668*497492a7Sjmatthew 
669*497492a7Sjmatthew 		bzero(filter, sizeof(filter));
670*497492a7Sjmatthew 		ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
671*497492a7Sjmatthew 		while (enm != NULL) {
672*497492a7Sjmatthew 			hash = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN)
673*497492a7Sjmatthew 			    >> 26;
674*497492a7Sjmatthew 			filter[hash >> 3] |= (1 << (hash & 7));
675*497492a7Sjmatthew 			ETHER_NEXT_MULTI(step, enm);
676*497492a7Sjmatthew 		}
677*497492a7Sjmatthew 
678*497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MCAST_FILTER,
679*497492a7Sjmatthew 		    UAQ_MCAST_FILTER_SIZE, filter, UAQ_MCAST_FILTER_SIZE);
680*497492a7Sjmatthew 	}
681*497492a7Sjmatthew 
682*497492a7Sjmatthew 	DPRINTFN(1, ("%s: rxctl = %x\n", sc->sc_dev.dv_xname, sc->sc_rxctl));
683*497492a7Sjmatthew 	uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl);
684*497492a7Sjmatthew }
685*497492a7Sjmatthew 
686*497492a7Sjmatthew void
687*497492a7Sjmatthew uaq_reset(struct uaq_softc *sc)
688*497492a7Sjmatthew {
689*497492a7Sjmatthew 	uint8_t mode;
690*497492a7Sjmatthew 
691*497492a7Sjmatthew 	sc->sc_phy_cfg = UAQ_PHY_POWER_EN;
692*497492a7Sjmatthew 	uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg);
693*497492a7Sjmatthew 
694*497492a7Sjmatthew 	uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, 0,
695*497492a7Sjmatthew 	    sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
696*497492a7Sjmatthew 	uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, ETHER_ADDR_LEN,
697*497492a7Sjmatthew 	    sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
698*497492a7Sjmatthew 
699*497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BM_INT_MASK, 0, 0xff);
700*497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_SWP_CTRL, 0, 0);
701*497492a7Sjmatthew 
702*497492a7Sjmatthew 	mode = uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1);
703*497492a7Sjmatthew 	mode &= ~(UAQ_SFR_MONITOR_MODE_EPHYRW | UAQ_SFR_MONITOR_MODE_RWLC |
704*497492a7Sjmatthew 	    UAQ_SFR_MONITOR_MODE_RWMP | UAQ_SFR_MONITOR_MODE_RWWF |
705*497492a7Sjmatthew 	    UAQ_SFR_MONITOR_MODE_RW_FLAG);
706*497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1, mode);
707*497492a7Sjmatthew 
708*497492a7Sjmatthew 	sc->sc_link_status = 0;
709*497492a7Sjmatthew 	sc->sc_link_speed = 0;
710*497492a7Sjmatthew }
711*497492a7Sjmatthew 
712*497492a7Sjmatthew void
713*497492a7Sjmatthew uaq_init(void *xsc)
714*497492a7Sjmatthew {
715*497492a7Sjmatthew 	struct uaq_softc	*sc = xsc;
716*497492a7Sjmatthew 	struct uaq_chain	*c;
717*497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
718*497492a7Sjmatthew 	usbd_status		err;
719*497492a7Sjmatthew 	int			s, i;
720*497492a7Sjmatthew 
721*497492a7Sjmatthew 	s = splnet();
722*497492a7Sjmatthew 
723*497492a7Sjmatthew 	uaq_stop(sc);
724*497492a7Sjmatthew 
725*497492a7Sjmatthew 	uaq_reset(sc);
726*497492a7Sjmatthew 
727*497492a7Sjmatthew 	if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_rx_chain,
728*497492a7Sjmatthew 		UAQ_RX_BUFSZ, UAQ_RX_LIST_CNT) == ENOBUFS) {
729*497492a7Sjmatthew 		printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
730*497492a7Sjmatthew 		splx(s);
731*497492a7Sjmatthew 		return;
732*497492a7Sjmatthew 	}
733*497492a7Sjmatthew 
734*497492a7Sjmatthew 	if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_tx_chain,
735*497492a7Sjmatthew 		UAQ_TX_BUFSZ, UAQ_TX_LIST_CNT) == ENOBUFS) {
736*497492a7Sjmatthew 		printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
737*497492a7Sjmatthew 		splx(s);
738*497492a7Sjmatthew 		return;
739*497492a7Sjmatthew 	}
740*497492a7Sjmatthew 
741*497492a7Sjmatthew 	SLIST_INIT(&sc->sc_cdata.uaq_tx_free);
742*497492a7Sjmatthew 	for (i = 0; i < UAQ_TX_LIST_CNT; i++)
743*497492a7Sjmatthew 		SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free,
744*497492a7Sjmatthew 		    &sc->sc_cdata.uaq_tx_chain[i], uc_list);
745*497492a7Sjmatthew 
746*497492a7Sjmatthew 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_RX],
747*497492a7Sjmatthew 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_RX]);
748*497492a7Sjmatthew 	if (err) {
749*497492a7Sjmatthew 		printf("%s: open rx pipe failed: %s\n",
750*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, usbd_errstr(err));
751*497492a7Sjmatthew 		splx(s);
752*497492a7Sjmatthew 		return;
753*497492a7Sjmatthew 	}
754*497492a7Sjmatthew 
755*497492a7Sjmatthew 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_TX],
756*497492a7Sjmatthew 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_TX]);
757*497492a7Sjmatthew 	if (err) {
758*497492a7Sjmatthew 		printf("%s: open tx pipe failed: %s\n",
759*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, usbd_errstr(err));
760*497492a7Sjmatthew 		splx(s);
761*497492a7Sjmatthew 		return;
762*497492a7Sjmatthew 	}
763*497492a7Sjmatthew 
764*497492a7Sjmatthew 	for (i = 0; i < UAQ_RX_LIST_CNT; i++) {
765*497492a7Sjmatthew 		c = &sc->sc_cdata.uaq_rx_chain[i];
766*497492a7Sjmatthew 		usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_RX],
767*497492a7Sjmatthew 		    c, c->uc_buf, c->uc_bufmax,
768*497492a7Sjmatthew 		    USBD_SHORT_XFER_OK | USBD_NO_COPY,
769*497492a7Sjmatthew 		    USBD_NO_TIMEOUT, uaq_rxeof);
770*497492a7Sjmatthew 		usbd_transfer(c->uc_xfer);
771*497492a7Sjmatthew 	}
772*497492a7Sjmatthew 
773*497492a7Sjmatthew 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_INTR],
774*497492a7Sjmatthew 	    0, &sc->sc_ep[UAQ_ENDPT_INTR], sc,
775*497492a7Sjmatthew 	    &sc->sc_link_status, sizeof(sc->sc_link_status), uaq_intr,
776*497492a7Sjmatthew 	    USBD_DEFAULT_INTERVAL);
777*497492a7Sjmatthew 	if (err) {
778*497492a7Sjmatthew 		printf("%s: couldn't open interrupt pipe\n",
779*497492a7Sjmatthew 		    sc->sc_dev.dv_xname);
780*497492a7Sjmatthew 		return;
781*497492a7Sjmatthew 	}
782*497492a7Sjmatthew 
783*497492a7Sjmatthew 	uaq_ifmedia_upd(ifp);
784*497492a7Sjmatthew 
785*497492a7Sjmatthew 	ifp->if_flags |= IFF_RUNNING;
786*497492a7Sjmatthew 	ifq_clr_oactive(&ifp->if_snd);
787*497492a7Sjmatthew 
788*497492a7Sjmatthew 	splx(s);
789*497492a7Sjmatthew }
790*497492a7Sjmatthew 
791*497492a7Sjmatthew int
792*497492a7Sjmatthew uaq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
793*497492a7Sjmatthew {
794*497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
795*497492a7Sjmatthew 	struct ifreq		*ifr = (struct ifreq *)data;
796*497492a7Sjmatthew 	int			s, error = 0;
797*497492a7Sjmatthew 
798*497492a7Sjmatthew 	s = splnet();
799*497492a7Sjmatthew 
800*497492a7Sjmatthew 	switch (cmd) {
801*497492a7Sjmatthew 	case SIOCSIFADDR:
802*497492a7Sjmatthew 		ifp->if_flags |= IFF_UP;
803*497492a7Sjmatthew 		if (!(ifp->if_flags & IFF_RUNNING))
804*497492a7Sjmatthew 			uaq_init(sc);
805*497492a7Sjmatthew 		break;
806*497492a7Sjmatthew 
807*497492a7Sjmatthew 	case SIOCSIFFLAGS:
808*497492a7Sjmatthew 		if (ifp->if_flags & IFF_UP) {
809*497492a7Sjmatthew 			if (ifp->if_flags & IFF_RUNNING)
810*497492a7Sjmatthew 				error = ENETRESET;
811*497492a7Sjmatthew 			else
812*497492a7Sjmatthew 				uaq_init(sc);
813*497492a7Sjmatthew 		} else {
814*497492a7Sjmatthew 			if (ifp->if_flags & IFF_RUNNING)
815*497492a7Sjmatthew 				uaq_stop(sc);
816*497492a7Sjmatthew 		}
817*497492a7Sjmatthew 		break;
818*497492a7Sjmatthew 
819*497492a7Sjmatthew 	case SIOCGIFMEDIA:
820*497492a7Sjmatthew 	case SIOCSIFMEDIA:
821*497492a7Sjmatthew 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
822*497492a7Sjmatthew 		break;
823*497492a7Sjmatthew 
824*497492a7Sjmatthew 	default:
825*497492a7Sjmatthew 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
826*497492a7Sjmatthew 	}
827*497492a7Sjmatthew 
828*497492a7Sjmatthew 	if (error == ENETRESET) {
829*497492a7Sjmatthew 		if (ifp->if_flags & IFF_RUNNING)
830*497492a7Sjmatthew 			uaq_iff(sc);
831*497492a7Sjmatthew 		error = 0;
832*497492a7Sjmatthew 	}
833*497492a7Sjmatthew 
834*497492a7Sjmatthew 	splx(s);
835*497492a7Sjmatthew 
836*497492a7Sjmatthew 	return (error);
837*497492a7Sjmatthew }
838*497492a7Sjmatthew 
839*497492a7Sjmatthew int
840*497492a7Sjmatthew uaq_xfer_list_init(struct uaq_softc *sc, struct uaq_chain *ch,
841*497492a7Sjmatthew     uint32_t bufsize, int listlen)
842*497492a7Sjmatthew {
843*497492a7Sjmatthew 	struct uaq_chain	*c;
844*497492a7Sjmatthew 	int			i;
845*497492a7Sjmatthew 
846*497492a7Sjmatthew 	for (i = 0; i < listlen; i++) {
847*497492a7Sjmatthew 		c = &ch[i];
848*497492a7Sjmatthew 		c->uc_sc = sc;
849*497492a7Sjmatthew 		c->uc_idx = i;
850*497492a7Sjmatthew 		c->uc_buflen = 0;
851*497492a7Sjmatthew 		c->uc_bufmax = bufsize;
852*497492a7Sjmatthew 		c->uc_cnt = 0;
853*497492a7Sjmatthew 		if (c->uc_xfer == NULL) {
854*497492a7Sjmatthew 			c->uc_xfer = usbd_alloc_xfer(sc->sc_udev);
855*497492a7Sjmatthew 			if (c->uc_xfer == NULL)
856*497492a7Sjmatthew 				return (ENOBUFS);
857*497492a7Sjmatthew 
858*497492a7Sjmatthew 			c->uc_buf = usbd_alloc_buffer(c->uc_xfer, c->uc_bufmax);
859*497492a7Sjmatthew 			if (c->uc_buf == NULL) {
860*497492a7Sjmatthew 				usbd_free_xfer(c->uc_xfer);
861*497492a7Sjmatthew 				c->uc_xfer = NULL;
862*497492a7Sjmatthew 				return (ENOBUFS);
863*497492a7Sjmatthew 			}
864*497492a7Sjmatthew 		}
865*497492a7Sjmatthew 	}
866*497492a7Sjmatthew 
867*497492a7Sjmatthew 	return (0);
868*497492a7Sjmatthew }
869*497492a7Sjmatthew 
870*497492a7Sjmatthew void
871*497492a7Sjmatthew uaq_xfer_list_free(struct uaq_softc *sc, struct uaq_chain *ch, int listlen)
872*497492a7Sjmatthew {
873*497492a7Sjmatthew 	int	i;
874*497492a7Sjmatthew 
875*497492a7Sjmatthew 	for (i = 0; i < listlen; i++) {
876*497492a7Sjmatthew 		if (ch[i].uc_buf != NULL) {
877*497492a7Sjmatthew 			ch[i].uc_buf = NULL;
878*497492a7Sjmatthew 		}
879*497492a7Sjmatthew 		ch[i].uc_cnt = 0;
880*497492a7Sjmatthew 		if (ch[i].uc_xfer != NULL) {
881*497492a7Sjmatthew 			usbd_free_xfer(ch[i].uc_xfer);
882*497492a7Sjmatthew 			ch[i].uc_xfer = NULL;
883*497492a7Sjmatthew 		}
884*497492a7Sjmatthew 	}
885*497492a7Sjmatthew }
886*497492a7Sjmatthew 
887*497492a7Sjmatthew void
888*497492a7Sjmatthew uaq_stop(struct uaq_softc *sc)
889*497492a7Sjmatthew {
890*497492a7Sjmatthew 	struct uaq_cdata	*cd;
891*497492a7Sjmatthew 	struct ifnet		*ifp;
892*497492a7Sjmatthew 	usbd_status		err;
893*497492a7Sjmatthew 
894*497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
895*497492a7Sjmatthew 	ifp->if_timer = 0;
896*497492a7Sjmatthew 	ifp->if_flags &= ~IFF_RUNNING;
897*497492a7Sjmatthew 	ifq_clr_oactive(&ifp->if_snd);
898*497492a7Sjmatthew 
899*497492a7Sjmatthew 	sc->sc_link_status = 0;
900*497492a7Sjmatthew 	sc->sc_link_speed = 0;
901*497492a7Sjmatthew 
902*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_RX] != NULL) {
903*497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_RX]);
904*497492a7Sjmatthew 		if (err) {
905*497492a7Sjmatthew 			printf("%s: close rx pipe failed: %s\n",
906*497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
907*497492a7Sjmatthew 		}
908*497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_RX] = NULL;
909*497492a7Sjmatthew 	}
910*497492a7Sjmatthew 
911*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_TX] != NULL) {
912*497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_TX]);
913*497492a7Sjmatthew 		if (err) {
914*497492a7Sjmatthew 			printf("%s: close tx pipe failed: %s\n",
915*497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
916*497492a7Sjmatthew 		}
917*497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_TX] = NULL;
918*497492a7Sjmatthew 	}
919*497492a7Sjmatthew 
920*497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL) {
921*497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_INTR]);
922*497492a7Sjmatthew 		if (err) {
923*497492a7Sjmatthew 			printf("%s: close intr pipe failed: %s\n",
924*497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
925*497492a7Sjmatthew 		}
926*497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_INTR] = NULL;
927*497492a7Sjmatthew 	}
928*497492a7Sjmatthew 
929*497492a7Sjmatthew 	cd = &sc->sc_cdata;
930*497492a7Sjmatthew 	uaq_xfer_list_free(sc, cd->uaq_rx_chain, UAQ_RX_LIST_CNT);
931*497492a7Sjmatthew 	uaq_xfer_list_free(sc, cd->uaq_tx_chain, UAQ_TX_LIST_CNT);
932*497492a7Sjmatthew }
933*497492a7Sjmatthew 
934*497492a7Sjmatthew void
935*497492a7Sjmatthew uaq_link(struct uaq_softc *sc)
936*497492a7Sjmatthew {
937*497492a7Sjmatthew 	if (sc->sc_link_speed > 0) {
938*497492a7Sjmatthew 		uint8_t resend[3] = { 0, 0xf8, 7 };
939*497492a7Sjmatthew 		uint8_t qctrl[5] = { 7, 0x00, 0x01, 0x1e, 0xff };
940*497492a7Sjmatthew 		uint8_t ipg = 0;
941*497492a7Sjmatthew 
942*497492a7Sjmatthew 		switch (sc->sc_link_speed) {
943*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
944*497492a7Sjmatthew 			resend[1] = 0xfb;
945*497492a7Sjmatthew 			resend[2] = 0x4;
946*497492a7Sjmatthew 			break;
947*497492a7Sjmatthew 
948*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
949*497492a7Sjmatthew 			ipg = 5;
950*497492a7Sjmatthew 			break;
951*497492a7Sjmatthew 		}
952*497492a7Sjmatthew 
953*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_IPG_0, 1, ipg);
954*497492a7Sjmatthew 
955*497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TX_PAUSE_RESEND_T,
956*497492a7Sjmatthew 		    3, resend, 3);
957*497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_BULKIN_QCTRL,
958*497492a7Sjmatthew 		    5, qctrl, 5);
959*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_PAUSE_WATERLVL_LOW,
960*497492a7Sjmatthew 		    2, 0x0810);
961*497492a7Sjmatthew 
962*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMRX_DMA_CTRL, 1,
963*497492a7Sjmatthew 		    0);
964*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMTX_DMA_CTRL, 1,
965*497492a7Sjmatthew 		    0);
966*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ARC_CTRL, 1, 0);
967*497492a7Sjmatthew 
968*497492a7Sjmatthew 		sc->sc_rxctl = UAQ_SFR_RX_CTL_IPE | UAQ_SFR_RX_CTL_AB;
969*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
970*497492a7Sjmatthew 		    sc->sc_rxctl);
971*497492a7Sjmatthew 
972*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ETH_MAC_PATH, 1,
973*497492a7Sjmatthew 		    UAQ_SFR_RX_PATH_READY);
974*497492a7Sjmatthew 
975*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
976*497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_EFF_EN);
977*497492a7Sjmatthew 
978*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
979*497492a7Sjmatthew 		    2, 0);
980*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
981*497492a7Sjmatthew 		    2, UAQ_SFR_MEDIUM_XGMIIMODE | UAQ_SFR_MEDIUM_FULL_DUPLEX |
982*497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_RECEIVE_EN | UAQ_SFR_MEDIUM_RXFLOW_CTRLEN |
983*497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_TXFLOW_CTRLEN);	/* JUMBO_EN */
984*497492a7Sjmatthew 
985*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RXCOE_CTL, 1,
986*497492a7Sjmatthew 		    UAQ_SFR_RXCOE_IP | UAQ_SFR_RXCOE_TCP | UAQ_SFR_RXCOE_UDP |
987*497492a7Sjmatthew 		    UAQ_SFR_RXCOE_TCPV6 | UAQ_SFR_RXCOE_UDPV6);
988*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TXCOE_CTL, 1,
989*497492a7Sjmatthew 		    UAQ_SFR_TXCOE_IP | UAQ_SFR_TXCOE_TCP | UAQ_SFR_TXCOE_UDP |
990*497492a7Sjmatthew 		    UAQ_SFR_TXCOE_TCPV6 | UAQ_SFR_TXCOE_UDPV6);
991*497492a7Sjmatthew 
992*497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_START;
993*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
994*497492a7Sjmatthew 		    sc->sc_rxctl);
995*497492a7Sjmatthew 	} else {
996*497492a7Sjmatthew 		uint16_t mode;
997*497492a7Sjmatthew 
998*497492a7Sjmatthew 		mode = uaq_read_2(sc, UAQ_CMD_ACCESS_MAC,
999*497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_STATUS_MODE, 2);
1000*497492a7Sjmatthew 		mode &= ~UAQ_SFR_MEDIUM_RECEIVE_EN;
1001*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
1002*497492a7Sjmatthew 		    2, mode);
1003*497492a7Sjmatthew 
1004*497492a7Sjmatthew 		sc->sc_rxctl &= ~UAQ_SFR_RX_CTL_START;
1005*497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
1006*497492a7Sjmatthew 		    sc->sc_rxctl);
1007*497492a7Sjmatthew 
1008*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
1009*497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_FLUSH_EN | UAQ_SFR_BULK_OUT_EFF_EN);
1010*497492a7Sjmatthew 
1011*497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
1012*497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_EFF_EN);
1013*497492a7Sjmatthew 	}
1014*497492a7Sjmatthew }
1015*497492a7Sjmatthew 
1016*497492a7Sjmatthew void
1017*497492a7Sjmatthew uaq_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
1018*497492a7Sjmatthew {
1019*497492a7Sjmatthew 	struct uaq_softc	*sc = priv;
1020*497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
1021*497492a7Sjmatthew 	uint64_t		linkstatus;
1022*497492a7Sjmatthew 	uint64_t		baudrate;
1023*497492a7Sjmatthew 	int			link_state;
1024*497492a7Sjmatthew 
1025*497492a7Sjmatthew 	if (status == USBD_CANCELLED)
1026*497492a7Sjmatthew 		return;
1027*497492a7Sjmatthew 
1028*497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1029*497492a7Sjmatthew 		DPRINTFN(2, ("uaq_intr: status=%d\n", status));
1030*497492a7Sjmatthew 		if (status == USBD_STALLED)
1031*497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1032*497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_INTR]);
1033*497492a7Sjmatthew 		return;
1034*497492a7Sjmatthew 	}
1035*497492a7Sjmatthew 
1036*497492a7Sjmatthew 	linkstatus = letoh64(sc->sc_link_status);
1037*497492a7Sjmatthew 	DPRINTFN(1, ("uaq_intr: link status %llx\n", linkstatus));
1038*497492a7Sjmatthew 
1039*497492a7Sjmatthew 	if (linkstatus & UAQ_STATUS_LINK) {
1040*497492a7Sjmatthew 		link_state = LINK_STATE_FULL_DUPLEX;
1041*497492a7Sjmatthew 		sc->sc_link_speed = (linkstatus & UAQ_STATUS_SPEED_MASK)
1042*497492a7Sjmatthew 		    >> UAQ_STATUS_SPEED_SHIFT;
1043*497492a7Sjmatthew 		switch (sc->sc_link_speed) {
1044*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
1045*497492a7Sjmatthew 			baudrate = IF_Gbps(5);
1046*497492a7Sjmatthew 			break;
1047*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_2_5G:
1048*497492a7Sjmatthew 			baudrate = IF_Mbps(2500);
1049*497492a7Sjmatthew 			break;
1050*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_1G:
1051*497492a7Sjmatthew 			baudrate = IF_Gbps(1);
1052*497492a7Sjmatthew 			break;
1053*497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
1054*497492a7Sjmatthew 			baudrate = IF_Mbps(100);
1055*497492a7Sjmatthew 			break;
1056*497492a7Sjmatthew 		default:
1057*497492a7Sjmatthew 			baudrate = 0;
1058*497492a7Sjmatthew 			break;
1059*497492a7Sjmatthew 		}
1060*497492a7Sjmatthew 
1061*497492a7Sjmatthew 		ifp->if_baudrate = baudrate;
1062*497492a7Sjmatthew 	} else {
1063*497492a7Sjmatthew 		link_state = LINK_STATE_DOWN;
1064*497492a7Sjmatthew 		sc->sc_link_speed = 0;
1065*497492a7Sjmatthew 	}
1066*497492a7Sjmatthew 
1067*497492a7Sjmatthew 	if (link_state != ifp->if_link_state) {
1068*497492a7Sjmatthew 		ifp->if_link_state = link_state;
1069*497492a7Sjmatthew 		if_link_state_change(ifp);
1070*497492a7Sjmatthew 		usb_add_task(sc->sc_udev, &sc->sc_link_task);
1071*497492a7Sjmatthew 	}
1072*497492a7Sjmatthew }
1073*497492a7Sjmatthew 
1074*497492a7Sjmatthew void
1075*497492a7Sjmatthew uaq_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
1076*497492a7Sjmatthew {
1077*497492a7Sjmatthew 	struct uaq_chain	*c = (struct uaq_chain *)priv;
1078*497492a7Sjmatthew 	struct uaq_softc	*sc = c->uc_sc;
1079*497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
1080*497492a7Sjmatthew 	uint8_t			*buf;
1081*497492a7Sjmatthew 	uint64_t		*pdesc;
1082*497492a7Sjmatthew 	uint64_t		desc;
1083*497492a7Sjmatthew 	uint32_t		total_len;
1084*497492a7Sjmatthew 	struct mbuf_list	ml = MBUF_LIST_INITIALIZER();
1085*497492a7Sjmatthew 	struct mbuf		*m;
1086*497492a7Sjmatthew 	int			pktlen, s;
1087*497492a7Sjmatthew 	int			count, offset;
1088*497492a7Sjmatthew 
1089*497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1090*497492a7Sjmatthew 		return;
1091*497492a7Sjmatthew 
1092*497492a7Sjmatthew 	if (!(ifp->if_flags & IFF_RUNNING))
1093*497492a7Sjmatthew 		return;
1094*497492a7Sjmatthew 
1095*497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1096*497492a7Sjmatthew 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1097*497492a7Sjmatthew 			return;
1098*497492a7Sjmatthew 		if (usbd_ratecheck(&sc->sc_rx_notice)) {
1099*497492a7Sjmatthew 			printf("%s: usb errors on rx: %s\n",
1100*497492a7Sjmatthew 				sc->sc_dev.dv_xname, usbd_errstr(status));
1101*497492a7Sjmatthew 		}
1102*497492a7Sjmatthew 		if (status == USBD_STALLED)
1103*497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1104*497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_RX]);
1105*497492a7Sjmatthew 		goto done;
1106*497492a7Sjmatthew 	}
1107*497492a7Sjmatthew 
1108*497492a7Sjmatthew 	usbd_get_xfer_status(xfer, NULL, (void **)&buf, &total_len, NULL);
1109*497492a7Sjmatthew 	DPRINTFN(3, ("received %d bytes\n", total_len));
1110*497492a7Sjmatthew 	if ((total_len & 7) != 0) {
1111*497492a7Sjmatthew 		printf("%s: weird rx transfer length %d\n",
1112*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, total_len);
1113*497492a7Sjmatthew 		goto done;
1114*497492a7Sjmatthew 	}
1115*497492a7Sjmatthew 
1116*497492a7Sjmatthew 	pdesc = (uint64_t *)(buf + (total_len - sizeof(desc)));
1117*497492a7Sjmatthew 	desc = lemtoh64(pdesc);
1118*497492a7Sjmatthew 
1119*497492a7Sjmatthew 	count = desc & UAQ_RX_HDR_COUNT_MASK;
1120*497492a7Sjmatthew 	if (count == 0)
1121*497492a7Sjmatthew 		goto done;
1122*497492a7Sjmatthew 
1123*497492a7Sjmatthew 	/* get offset of packet headers */
1124*497492a7Sjmatthew 	offset = total_len - ((count + 1) * sizeof(desc));
1125*497492a7Sjmatthew 	if (offset != ((desc & UAQ_RX_HDR_OFFSET_MASK) >>
1126*497492a7Sjmatthew 	    UAQ_RX_HDR_OFFSET_SHIFT)) {
1127*497492a7Sjmatthew 		printf("%s: offset mismatch, got %d expected %lld\n",
1128*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, offset,
1129*497492a7Sjmatthew 		    desc >> UAQ_RX_HDR_OFFSET_SHIFT);
1130*497492a7Sjmatthew 		goto done;
1131*497492a7Sjmatthew 	}
1132*497492a7Sjmatthew 	if (offset < 0 || offset > total_len) {
1133*497492a7Sjmatthew 		printf("%s: offset %d outside buffer (%d)\n",
1134*497492a7Sjmatthew 		    sc->sc_dev.dv_xname, offset, total_len);
1135*497492a7Sjmatthew 		goto done;
1136*497492a7Sjmatthew 	}
1137*497492a7Sjmatthew 
1138*497492a7Sjmatthew 	pdesc = (uint64_t *)(buf + offset);
1139*497492a7Sjmatthew 	total_len = offset;
1140*497492a7Sjmatthew 
1141*497492a7Sjmatthew 	while (count-- > 0) {
1142*497492a7Sjmatthew 		desc = lemtoh64(pdesc);
1143*497492a7Sjmatthew 		pdesc++;
1144*497492a7Sjmatthew 
1145*497492a7Sjmatthew 		pktlen = (desc & UAQ_RX_PKT_LEN_MASK) >> UAQ_RX_PKT_LEN_SHIFT;
1146*497492a7Sjmatthew 		if (pktlen > total_len) {
1147*497492a7Sjmatthew 			DPRINTFN(2, ("not enough bytes for this packet\n"));
1148*497492a7Sjmatthew 			ifp->if_ierrors++;
1149*497492a7Sjmatthew 			goto done;
1150*497492a7Sjmatthew 		}
1151*497492a7Sjmatthew 
1152*497492a7Sjmatthew 		m = m_devget(buf + 2, pktlen - 2, ETHER_ALIGN);
1153*497492a7Sjmatthew 		if (m == NULL) {
1154*497492a7Sjmatthew 			DPRINTFN(2, ("m_devget failed for this packet\n"));
1155*497492a7Sjmatthew 			ifp->if_ierrors++;
1156*497492a7Sjmatthew 			goto done;
1157*497492a7Sjmatthew 		}
1158*497492a7Sjmatthew 
1159*497492a7Sjmatthew 		if ((desc & UAQ_RX_PKT_L3_ERR) == 0)
1160*497492a7Sjmatthew 			m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
1161*497492a7Sjmatthew 
1162*497492a7Sjmatthew 		if ((desc & UAQ_RX_PKT_L4_ERR) == 0)
1163*497492a7Sjmatthew 			m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
1164*497492a7Sjmatthew 			    M_UDP_CSUM_IN_OK;
1165*497492a7Sjmatthew 
1166*497492a7Sjmatthew #if NVLAN > 0
1167*497492a7Sjmatthew 		if (desc & UAQ_RX_PKT_VLAN) {
1168*497492a7Sjmatthew 			m->m_pkthdr.ether_vtag = (desc >> UAQ_RX_PKT_VLAN_SHIFT) &
1169*497492a7Sjmatthew 			    0xfff;
1170*497492a7Sjmatthew 			m->m_flags |= M_VLANTAG;
1171*497492a7Sjmatthew 		}
1172*497492a7Sjmatthew #endif
1173*497492a7Sjmatthew 		ml_enqueue(&ml, m);
1174*497492a7Sjmatthew 
1175*497492a7Sjmatthew 		total_len -= roundup(pktlen, UAQ_RX_BUF_ALIGN);
1176*497492a7Sjmatthew 		buf += roundup(pktlen, UAQ_RX_BUF_ALIGN);
1177*497492a7Sjmatthew 	}
1178*497492a7Sjmatthew 
1179*497492a7Sjmatthew done:
1180*497492a7Sjmatthew 	s = splnet();
1181*497492a7Sjmatthew 	if_input(ifp, &ml);
1182*497492a7Sjmatthew 	splx(s);
1183*497492a7Sjmatthew 	memset(c->uc_buf, 0, UAQ_RX_BUFSZ);
1184*497492a7Sjmatthew 
1185*497492a7Sjmatthew 	usbd_setup_xfer(xfer, sc->sc_ep[UAQ_ENDPT_RX], c, c->uc_buf,
1186*497492a7Sjmatthew 	    UAQ_RX_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
1187*497492a7Sjmatthew 	    USBD_NO_TIMEOUT, uaq_rxeof);
1188*497492a7Sjmatthew 	usbd_transfer(xfer);
1189*497492a7Sjmatthew }
1190*497492a7Sjmatthew 
1191*497492a7Sjmatthew 
1192*497492a7Sjmatthew void
1193*497492a7Sjmatthew uaq_watchdog(struct ifnet *ifp)
1194*497492a7Sjmatthew {
1195*497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
1196*497492a7Sjmatthew 	struct uaq_chain	*c;
1197*497492a7Sjmatthew 	usbd_status		err;
1198*497492a7Sjmatthew 	int			i, s;
1199*497492a7Sjmatthew 
1200*497492a7Sjmatthew 	ifp->if_timer = 0;
1201*497492a7Sjmatthew 
1202*497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1203*497492a7Sjmatthew 		return;
1204*497492a7Sjmatthew 
1205*497492a7Sjmatthew 	if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP))
1206*497492a7Sjmatthew 		return;
1207*497492a7Sjmatthew 
1208*497492a7Sjmatthew 	sc = ifp->if_softc;
1209*497492a7Sjmatthew 	s = splnet();
1210*497492a7Sjmatthew 
1211*497492a7Sjmatthew 	ifp->if_oerrors++;
1212*497492a7Sjmatthew 	DPRINTF(("%s: watchdog timeout\n", sc->sc_dev.dv_xname));
1213*497492a7Sjmatthew 
1214*497492a7Sjmatthew 	for (i = 0; i < UAQ_TX_LIST_CNT; i++) {
1215*497492a7Sjmatthew 		c = &sc->sc_cdata.uaq_tx_chain[i];
1216*497492a7Sjmatthew 		if (c->uc_cnt > 0) {
1217*497492a7Sjmatthew 			usbd_get_xfer_status(c->uc_xfer, NULL, NULL, NULL,
1218*497492a7Sjmatthew 			    &err);
1219*497492a7Sjmatthew 			uaq_txeof(c->uc_xfer, c, err);
1220*497492a7Sjmatthew 		}
1221*497492a7Sjmatthew 	}
1222*497492a7Sjmatthew 
1223*497492a7Sjmatthew 	if (ifq_is_oactive(&ifp->if_snd))
1224*497492a7Sjmatthew 		ifq_restart(&ifp->if_snd);
1225*497492a7Sjmatthew 	splx(s);
1226*497492a7Sjmatthew }
1227*497492a7Sjmatthew 
1228*497492a7Sjmatthew void
1229*497492a7Sjmatthew uaq_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
1230*497492a7Sjmatthew {
1231*497492a7Sjmatthew 	struct uaq_softc	*sc;
1232*497492a7Sjmatthew 	struct uaq_chain	*c;
1233*497492a7Sjmatthew 	struct ifnet		*ifp;
1234*497492a7Sjmatthew 	int			s;
1235*497492a7Sjmatthew 
1236*497492a7Sjmatthew 	c = priv;
1237*497492a7Sjmatthew 	sc = c->uc_sc;
1238*497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
1239*497492a7Sjmatthew 
1240*497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1241*497492a7Sjmatthew 		return;
1242*497492a7Sjmatthew 
1243*497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION)
1244*497492a7Sjmatthew 		DPRINTF(("%s: %s uc_idx=%u : %s\n", sc->sc_dev.dv_xname,
1245*497492a7Sjmatthew 			__func__, c->uc_idx, usbd_errstr(status)));
1246*497492a7Sjmatthew 	else
1247*497492a7Sjmatthew 		DPRINTF(("%s: txeof\n", sc->sc_dev.dv_xname));
1248*497492a7Sjmatthew 
1249*497492a7Sjmatthew 	s = splnet();
1250*497492a7Sjmatthew 
1251*497492a7Sjmatthew 	c->uc_cnt = 0;
1252*497492a7Sjmatthew 	c->uc_buflen = 0;
1253*497492a7Sjmatthew 
1254*497492a7Sjmatthew 	SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free, c, uc_list);
1255*497492a7Sjmatthew 
1256*497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1257*497492a7Sjmatthew 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
1258*497492a7Sjmatthew 			splx(s);
1259*497492a7Sjmatthew 			return;
1260*497492a7Sjmatthew 		}
1261*497492a7Sjmatthew 
1262*497492a7Sjmatthew 		ifp->if_oerrors++;
1263*497492a7Sjmatthew 		printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname,
1264*497492a7Sjmatthew 		    usbd_errstr(status));
1265*497492a7Sjmatthew 
1266*497492a7Sjmatthew 		if (status == USBD_STALLED)
1267*497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1268*497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_TX]);
1269*497492a7Sjmatthew 		splx(s);
1270*497492a7Sjmatthew 		return;
1271*497492a7Sjmatthew 	}
1272*497492a7Sjmatthew 
1273*497492a7Sjmatthew 	ifp->if_timer = 0;
1274*497492a7Sjmatthew 	if (ifq_is_oactive(&ifp->if_snd))
1275*497492a7Sjmatthew 		ifq_restart(&ifp->if_snd);
1276*497492a7Sjmatthew 	splx(s);
1277*497492a7Sjmatthew }
1278*497492a7Sjmatthew 
1279*497492a7Sjmatthew void
1280*497492a7Sjmatthew uaq_start(struct ifnet *ifp)
1281*497492a7Sjmatthew {
1282*497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
1283*497492a7Sjmatthew 	struct uaq_cdata	*cd = &sc->sc_cdata;
1284*497492a7Sjmatthew 	struct uaq_chain	*c;
1285*497492a7Sjmatthew 	struct mbuf		*m = NULL;
1286*497492a7Sjmatthew 	int			s, mlen;
1287*497492a7Sjmatthew 
1288*497492a7Sjmatthew 	if ((sc->sc_link_speed == 0) ||
1289*497492a7Sjmatthew 		(ifp->if_flags & (IFF_RUNNING|IFF_UP)) !=
1290*497492a7Sjmatthew 		    (IFF_RUNNING|IFF_UP)) {
1291*497492a7Sjmatthew 		return;
1292*497492a7Sjmatthew 	}
1293*497492a7Sjmatthew 
1294*497492a7Sjmatthew 	s = splnet();
1295*497492a7Sjmatthew 
1296*497492a7Sjmatthew 	c = SLIST_FIRST(&cd->uaq_tx_free);
1297*497492a7Sjmatthew 	while (c != NULL) {
1298*497492a7Sjmatthew 		m = ifq_deq_begin(&ifp->if_snd);
1299*497492a7Sjmatthew 		if (m == NULL)
1300*497492a7Sjmatthew 			break;
1301*497492a7Sjmatthew 
1302*497492a7Sjmatthew 		mlen = m->m_pkthdr.len;
1303*497492a7Sjmatthew 
1304*497492a7Sjmatthew 		/* Discard packet larger than buffer. */
1305*497492a7Sjmatthew 		if (mlen + sizeof(uint64_t) >= c->uc_bufmax) {
1306*497492a7Sjmatthew 			ifq_deq_commit(&ifp->if_snd, m);
1307*497492a7Sjmatthew 			m_freem(m);
1308*497492a7Sjmatthew 			ifp->if_oerrors++;
1309*497492a7Sjmatthew 			continue;
1310*497492a7Sjmatthew 		}
1311*497492a7Sjmatthew 
1312*497492a7Sjmatthew 		/* Append packet to current buffer. */
1313*497492a7Sjmatthew 		mlen = uaq_encap_txpkt(sc, m, c->uc_buf + c->uc_buflen,
1314*497492a7Sjmatthew 		    c->uc_bufmax - c->uc_buflen);
1315*497492a7Sjmatthew 		if (mlen <= 0) {
1316*497492a7Sjmatthew 			ifq_deq_rollback(&ifp->if_snd, m);
1317*497492a7Sjmatthew 			break;
1318*497492a7Sjmatthew 		}
1319*497492a7Sjmatthew 
1320*497492a7Sjmatthew 		ifq_deq_commit(&ifp->if_snd, m);
1321*497492a7Sjmatthew 		c->uc_cnt += 1;
1322*497492a7Sjmatthew 		c->uc_buflen += mlen;
1323*497492a7Sjmatthew 
1324*497492a7Sjmatthew #if NBPFILTER > 0
1325*497492a7Sjmatthew 		if (ifp->if_bpf)
1326*497492a7Sjmatthew 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
1327*497492a7Sjmatthew #endif
1328*497492a7Sjmatthew 
1329*497492a7Sjmatthew 		m_freem(m);
1330*497492a7Sjmatthew 	}
1331*497492a7Sjmatthew 
1332*497492a7Sjmatthew 	if (c != NULL) {
1333*497492a7Sjmatthew 		/* Send current buffer unless empty */
1334*497492a7Sjmatthew 		if (c->uc_buflen > 0 && c->uc_cnt > 0) {
1335*497492a7Sjmatthew 			SLIST_REMOVE_HEAD(&cd->uaq_tx_free, uc_list);
1336*497492a7Sjmatthew 			if (uaq_encap_xfer(sc, c)) {
1337*497492a7Sjmatthew 				SLIST_INSERT_HEAD(&cd->uaq_tx_free, c,
1338*497492a7Sjmatthew 				    uc_list);
1339*497492a7Sjmatthew 			}
1340*497492a7Sjmatthew 			c = SLIST_FIRST(&cd->uaq_tx_free);
1341*497492a7Sjmatthew 
1342*497492a7Sjmatthew 			ifp->if_timer = 5;
1343*497492a7Sjmatthew 			if (c == NULL)
1344*497492a7Sjmatthew 				ifq_set_oactive(&ifp->if_snd);
1345*497492a7Sjmatthew 		}
1346*497492a7Sjmatthew 	}
1347*497492a7Sjmatthew 
1348*497492a7Sjmatthew 	splx(s);
1349*497492a7Sjmatthew }
1350*497492a7Sjmatthew 
1351*497492a7Sjmatthew int
1352*497492a7Sjmatthew uaq_encap_txpkt(struct uaq_softc *sc, struct mbuf *m, char *buf,
1353*497492a7Sjmatthew     uint32_t maxlen)
1354*497492a7Sjmatthew {
1355*497492a7Sjmatthew 	uint64_t		desc;
1356*497492a7Sjmatthew 	int			padded;
1357*497492a7Sjmatthew 
1358*497492a7Sjmatthew 	desc = m->m_pkthdr.len;
1359*497492a7Sjmatthew 	padded = roundup(m->m_pkthdr.len, UAQ_TX_BUF_ALIGN);
1360*497492a7Sjmatthew 	if (((padded + sizeof(desc)) % sc->sc_out_frame_size) == 0) {
1361*497492a7Sjmatthew 		desc |= UAQ_TX_PKT_DROP_PADD;
1362*497492a7Sjmatthew 		padded += 8;
1363*497492a7Sjmatthew 	}
1364*497492a7Sjmatthew 
1365*497492a7Sjmatthew 	if (padded + sizeof(desc) > maxlen)
1366*497492a7Sjmatthew 		return (-1);
1367*497492a7Sjmatthew 
1368*497492a7Sjmatthew #if NVLAN > 0
1369*497492a7Sjmatthew 	if (m->m_flags & M_VLANTAG)
1370*497492a7Sjmatthew 		desc |= (((uint64_t)m->m_pkthdr.ether_vtag) <<
1371*497492a7Sjmatthew 		    UAQ_TX_PKT_VLAN_SHIFT) | UAQ_TX_PKT_VLAN;
1372*497492a7Sjmatthew #endif
1373*497492a7Sjmatthew 
1374*497492a7Sjmatthew 	htolem64((uint64_t *)buf, desc);
1375*497492a7Sjmatthew 	m_copydata(m, 0, m->m_pkthdr.len, buf + sizeof(desc));
1376*497492a7Sjmatthew 	return (padded + sizeof(desc));
1377*497492a7Sjmatthew }
1378*497492a7Sjmatthew 
1379*497492a7Sjmatthew int
1380*497492a7Sjmatthew uaq_encap_xfer(struct uaq_softc *sc, struct uaq_chain *c)
1381*497492a7Sjmatthew {
1382*497492a7Sjmatthew 	usbd_status	err;
1383*497492a7Sjmatthew 
1384*497492a7Sjmatthew 	usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_TX], c, c->uc_buf,
1385*497492a7Sjmatthew 	    c->uc_buflen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 10000,
1386*497492a7Sjmatthew 	    uaq_txeof);
1387*497492a7Sjmatthew 
1388*497492a7Sjmatthew 	err = usbd_transfer(c->uc_xfer);
1389*497492a7Sjmatthew 	if (err != USBD_IN_PROGRESS) {
1390*497492a7Sjmatthew 		c->uc_cnt = 0;
1391*497492a7Sjmatthew 		c->uc_buflen = 0;
1392*497492a7Sjmatthew 		uaq_stop(sc);
1393*497492a7Sjmatthew 		return (EIO);
1394*497492a7Sjmatthew 	}
1395*497492a7Sjmatthew 
1396*497492a7Sjmatthew 	return (0);
1397*497492a7Sjmatthew }
1398