xref: /openbsd/sys/dev/usb/if_uaq.c (revision 81508fe3)
1*81508fe3Sjsg /*	$OpenBSD: if_uaq.c,v 1.6 2024/05/23 03:21:08 jsg Exp $	*/
2497492a7Sjmatthew /*-
3497492a7Sjmatthew  * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org>
4497492a7Sjmatthew  * All rights reserved.
5497492a7Sjmatthew  *
6497492a7Sjmatthew  * Redistribution and use in source and binary forms, with or without
7497492a7Sjmatthew  * modification, are permitted provided that the following conditions
8497492a7Sjmatthew  * are met:
9497492a7Sjmatthew  * 1. Redistributions of source code must retain the above copyright
10497492a7Sjmatthew  *    notice, this list of conditions and the following disclaimer.
11497492a7Sjmatthew  * 2. Redistributions in binary form must reproduce the above copyright
12497492a7Sjmatthew  *    notice, this list of conditions and the following disclaimer in the
13497492a7Sjmatthew  *    documentation and/or other materials provided with the distribution.
14497492a7Sjmatthew  *
15497492a7Sjmatthew  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16497492a7Sjmatthew  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17497492a7Sjmatthew  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18497492a7Sjmatthew  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19497492a7Sjmatthew  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20497492a7Sjmatthew  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21497492a7Sjmatthew  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22497492a7Sjmatthew  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23497492a7Sjmatthew  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24497492a7Sjmatthew  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25497492a7Sjmatthew  * SUCH DAMAGE.
26497492a7Sjmatthew  */
27497492a7Sjmatthew 
28497492a7Sjmatthew #include "bpfilter.h"
29497492a7Sjmatthew #include "vlan.h"
30497492a7Sjmatthew 
31497492a7Sjmatthew #include <sys/param.h>
32497492a7Sjmatthew #include <sys/systm.h>
33497492a7Sjmatthew #include <sys/sockio.h>
34497492a7Sjmatthew #include <sys/mbuf.h>
35497492a7Sjmatthew #include <sys/device.h>
36497492a7Sjmatthew 
37497492a7Sjmatthew #include <machine/bus.h>
38497492a7Sjmatthew 
39497492a7Sjmatthew #include <net/if.h>
40497492a7Sjmatthew #include <net/if_media.h>
41497492a7Sjmatthew 
42497492a7Sjmatthew #if NBPFILTER > 0
43497492a7Sjmatthew #include <net/bpf.h>
44497492a7Sjmatthew #endif
45497492a7Sjmatthew 
46497492a7Sjmatthew #include <netinet/in.h>
47497492a7Sjmatthew #include <netinet/if_ether.h>
48497492a7Sjmatthew 
49497492a7Sjmatthew #include <dev/usb/usb.h>
50497492a7Sjmatthew #include <dev/usb/usbdi.h>
51497492a7Sjmatthew #include <dev/usb/usbdi_util.h>
52497492a7Sjmatthew #include <dev/usb/usbdivar.h>
53497492a7Sjmatthew #include <dev/usb/usbdevs.h>
54497492a7Sjmatthew 
55497492a7Sjmatthew #ifdef UAQ_DEBUG
56497492a7Sjmatthew #define DPRINTF(x)	do { if (uaqdebug) printf x; } while (0)
57497492a7Sjmatthew #define DPRINTFN(n,x)	do { if (uaqdebug >= (n)) printf x; } while (0)
58497492a7Sjmatthew int	uaqdebug = 0;
59497492a7Sjmatthew #else
60497492a7Sjmatthew #define DPRINTF(x)
61497492a7Sjmatthew #define DPRINTFN(n,x)
62497492a7Sjmatthew #endif
63497492a7Sjmatthew 
64497492a7Sjmatthew #define UAQ_ENDPT_RX		0
65497492a7Sjmatthew #define UAQ_ENDPT_TX		1
66497492a7Sjmatthew #define UAQ_ENDPT_INTR		2
67497492a7Sjmatthew #define UAQ_ENDPT_MAX		3
68497492a7Sjmatthew 
69497492a7Sjmatthew #define UAQ_TX_LIST_CNT		1
70497492a7Sjmatthew #define UAQ_RX_LIST_CNT		1
71497492a7Sjmatthew #define UAQ_TX_BUF_ALIGN	8
72497492a7Sjmatthew #define UAQ_RX_BUF_ALIGN	8
73497492a7Sjmatthew 
74497492a7Sjmatthew #define UAQ_TX_BUFSZ		16384
7521bc0131Sjmatthew #define UAQ_RX_BUFSZ		(62 * 1024)
76497492a7Sjmatthew 
77497492a7Sjmatthew #define UAQ_CTL_READ		1
78497492a7Sjmatthew #define UAQ_CTL_WRITE		2
79497492a7Sjmatthew 
80497492a7Sjmatthew #define UAQ_MCAST_FILTER_SIZE	8
81497492a7Sjmatthew 
82497492a7Sjmatthew /* control commands */
83497492a7Sjmatthew #define UAQ_CMD_ACCESS_MAC	0x01
84497492a7Sjmatthew #define UAQ_CMD_FLASH_PARAM	0x20
85497492a7Sjmatthew #define UAQ_CMD_PHY_POWER	0x31
86497492a7Sjmatthew #define UAQ_CMD_WOL_CFG		0x60
87497492a7Sjmatthew #define UAQ_CMD_PHY_OPS		0x61
88497492a7Sjmatthew 
89497492a7Sjmatthew /* SFR registers */
90497492a7Sjmatthew #define UAQ_SFR_GENERAL_STATUS	0x03
91497492a7Sjmatthew #define UAQ_SFR_CHIP_STATUS	0x05
92497492a7Sjmatthew #define UAQ_SFR_RX_CTL		0x0B
93497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_STOP	0x0000
94497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_PRO	0x0001
95497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AMALL	0x0002
96497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AB	0x0008
97497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_AM	0x0010
98497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_START	0x0080
99497492a7Sjmatthew #define  UAQ_SFR_RX_CTL_IPE	0x0200
100497492a7Sjmatthew #define UAQ_SFR_IPG_0		0x0D
101497492a7Sjmatthew #define UAQ_SFR_NODE_ID		0x10
102497492a7Sjmatthew #define UAQ_SFR_MCAST_FILTER	0x16
103497492a7Sjmatthew #define UAQ_SFR_MEDIUM_STATUS_MODE 0x22
104497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_XGMIIMODE	0x0001
105497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_FULL_DUPLEX	0x0002
106497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_RXFLOW_CTRLEN	0x0010
107497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_TXFLOW_CTRLEN	0x0020
108497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_JUMBO_EN	0x0040
109497492a7Sjmatthew #define  UAQ_SFR_MEDIUM_RECEIVE_EN	0x0100
110497492a7Sjmatthew #define UAQ_SFR_MONITOR_MODE	0x24
111497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_EPHYRW	0x01
112497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWLC	0x02
113497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWMP	0x04
114497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RWWF	0x08
115497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_RW_FLAG	0x10
116497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_PMEPOL	0x20
117497492a7Sjmatthew #define  UAQ_SFR_MONITOR_MODE_PMETYPE	0x40
118497492a7Sjmatthew #define UAQ_SFR_RX_BULKIN_QCTRL 0x2E
119497492a7Sjmatthew #define UAQ_SFR_RXCOE_CTL	0x34
120497492a7Sjmatthew #define  UAQ_SFR_RXCOE_IP		0x01
121497492a7Sjmatthew #define  UAQ_SFR_RXCOE_TCP		0x02
122497492a7Sjmatthew #define  UAQ_SFR_RXCOE_UDP		0x04
123497492a7Sjmatthew #define  UAQ_SFR_RXCOE_ICMP		0x08
124497492a7Sjmatthew #define  UAQ_SFR_RXCOE_IGMP		0x10
125497492a7Sjmatthew #define  UAQ_SFR_RXCOE_TCPV6		0x20
126497492a7Sjmatthew #define  UAQ_SFR_RXCOE_UDPV6		0x40
127497492a7Sjmatthew #define  UAQ_SFR_RXCOE_ICMV6		0x80
128497492a7Sjmatthew #define UAQ_SFR_TXCOE_CTL	0x35
129497492a7Sjmatthew #define  UAQ_SFR_TXCOE_IP		0x01
130497492a7Sjmatthew #define  UAQ_SFR_TXCOE_TCP		0x02
131497492a7Sjmatthew #define  UAQ_SFR_TXCOE_UDP		0x04
132497492a7Sjmatthew #define  UAQ_SFR_TXCOE_ICMP		0x08
133497492a7Sjmatthew #define  UAQ_SFR_TXCOE_IGMP		0x10
134497492a7Sjmatthew #define  UAQ_SFR_TXCOE_TCPV6		0x20
135497492a7Sjmatthew #define  UAQ_SFR_TXCOE_UDPV6		0x40
136497492a7Sjmatthew #define  UAQ_SFR_TXCOE_ICMV6		0x80
137497492a7Sjmatthew #define UAQ_SFR_BM_INT_MASK	0x41
138497492a7Sjmatthew #define UAQ_SFR_BMRX_DMA_CTRL	0x43
139497492a7Sjmatthew #define  UAQ_SFR_BMRX_DMA_EN	0x80
140497492a7Sjmatthew #define UAQ_SFR_BMTX_DMA_CTRL	0x46
141497492a7Sjmatthew #define UAQ_SFR_PAUSE_WATERLVL_LOW 0x54
142497492a7Sjmatthew #define UAQ_SFR_ARC_CTRL	0x9E
143497492a7Sjmatthew #define UAQ_SFR_SWP_CTRL	0xB1
144497492a7Sjmatthew #define UAQ_SFR_TX_PAUSE_RESEND_T 0xB2
145497492a7Sjmatthew #define UAQ_SFR_ETH_MAC_PATH	0xB7
146497492a7Sjmatthew #define  UAQ_SFR_RX_PATH_READY	0x01
147497492a7Sjmatthew #define UAQ_SFR_BULK_OUT_CTRL	0xB9
148497492a7Sjmatthew #define  UAQ_SFR_BULK_OUT_FLUSH_EN	0x01
149497492a7Sjmatthew #define  UAQ_SFR_BULK_OUT_EFF_EN	0x02
150497492a7Sjmatthew 
151497492a7Sjmatthew #define UAQ_FW_VER_MAJOR	0xDA
152497492a7Sjmatthew #define UAQ_FW_VER_MINOR	0xDB
153497492a7Sjmatthew #define UAQ_FW_VER_REV		0xDC
154497492a7Sjmatthew 
155497492a7Sjmatthew /* phy ops */
156497492a7Sjmatthew #define UAQ_PHY_ADV_100M	(1 << 0)
157497492a7Sjmatthew #define UAQ_PHY_ADV_1G		(1 << 1)
158497492a7Sjmatthew #define UAQ_PHY_ADV_2_5G	(1 << 2)
159497492a7Sjmatthew #define UAQ_PHY_ADV_5G		(1 << 3)
160497492a7Sjmatthew #define UAQ_PHY_ADV_MASK	0x0F
161497492a7Sjmatthew 
162497492a7Sjmatthew #define UAQ_PHY_PAUSE		(1 << 16)
163497492a7Sjmatthew #define UAQ_PHY_ASYM_PAUSE	(1 << 17)
164497492a7Sjmatthew #define UAQ_PHY_LOW_POWER	(1 << 18)
165497492a7Sjmatthew #define UAQ_PHY_POWER_EN	(1 << 19)
166497492a7Sjmatthew #define UAQ_PHY_WOL		(1 << 20)
167497492a7Sjmatthew #define UAQ_PHY_DOWNSHIFT	(1 << 21)
168497492a7Sjmatthew 
169497492a7Sjmatthew #define UAQ_PHY_DSH_RETRY_SHIFT	0x18
170497492a7Sjmatthew #define UAQ_PHY_DSH_RETRY_MASK	0xF000000
171497492a7Sjmatthew 
172497492a7Sjmatthew /* status */
173497492a7Sjmatthew #define UAQ_STATUS_LINK		0x8000
174497492a7Sjmatthew #define UAQ_STATUS_SPEED_MASK	0x7F00
175497492a7Sjmatthew #define UAQ_STATUS_SPEED_SHIFT	8
176497492a7Sjmatthew #define UAQ_STATUS_SPEED_5G	0x000F
177497492a7Sjmatthew #define UAQ_STATUS_SPEED_2_5G	0x0010
178497492a7Sjmatthew #define UAQ_STATUS_SPEED_1G	0x0011
179497492a7Sjmatthew #define UAQ_STATUS_SPEED_100M	0x0013
180497492a7Sjmatthew 
181497492a7Sjmatthew /* rx descriptor */
182497492a7Sjmatthew #define UAQ_RX_HDR_COUNT_MASK	0x1FFF
183497492a7Sjmatthew #define UAQ_RX_HDR_OFFSET_MASK	0xFFFFE000
184497492a7Sjmatthew #define UAQ_RX_HDR_OFFSET_SHIFT	13
185497492a7Sjmatthew 
186497492a7Sjmatthew /* rx packet descriptor */
187497492a7Sjmatthew #define UAQ_RX_PKT_L4_ERR	0x01
188497492a7Sjmatthew #define UAQ_RX_PKT_L3_ERR	0x02
189497492a7Sjmatthew #define UAQ_RX_PKT_L4_MASK	0x1C
190497492a7Sjmatthew #define UAQ_RX_PKT_L4_UDP	0x04
191497492a7Sjmatthew #define UAQ_RX_PKT_L4_TCP	0x10
192497492a7Sjmatthew #define UAQ_RX_PKT_L3_MASK	0x60
193497492a7Sjmatthew #define UAQ_RX_PKT_L3_IP	0x20
194497492a7Sjmatthew #define UAQ_RX_PKT_L3_IP6	0x40
195497492a7Sjmatthew #define UAQ_RX_PKT_VLAN		0x400
196497492a7Sjmatthew #define UAQ_RX_PKT_RX_OK	0x800
197497492a7Sjmatthew #define UAQ_RX_PKT_DROP		0x80000000
198497492a7Sjmatthew #define UAQ_RX_PKT_LEN_MASK	0x7FFF0000
199497492a7Sjmatthew #define UAQ_RX_PKT_LEN_SHIFT	16
200497492a7Sjmatthew #define UAQ_RX_PKT_VLAN_SHIFT	32
201497492a7Sjmatthew 
202497492a7Sjmatthew /* tx packet descriptor */
203497492a7Sjmatthew #define UAQ_TX_PKT_LEN_MASK	0x1FFFFF
204497492a7Sjmatthew #define UAQ_TX_PKT_DROP_PADD	(1 << 28)
205497492a7Sjmatthew #define UAQ_TX_PKT_VLAN		(1 << 29)
206497492a7Sjmatthew #define UAQ_TX_PKT_VLAN_MASK	0xFFFF
207497492a7Sjmatthew #define UAQ_TX_PKT_VLAN_SHIFT	0x30
208497492a7Sjmatthew 
209497492a7Sjmatthew 
210497492a7Sjmatthew struct uaq_chain {
211497492a7Sjmatthew 	struct uaq_softc	*uc_sc;
212497492a7Sjmatthew 	struct usbd_xfer	*uc_xfer;
213497492a7Sjmatthew 	char			*uc_buf;
214497492a7Sjmatthew 	uint32_t		 uc_cnt;
215497492a7Sjmatthew 	uint32_t		 uc_buflen;
216497492a7Sjmatthew 	uint32_t		 uc_bufmax;
217497492a7Sjmatthew 	SLIST_ENTRY(uaq_chain)	 uc_list;
218497492a7Sjmatthew 	uint8_t			 uc_idx;
219497492a7Sjmatthew };
220497492a7Sjmatthew 
221497492a7Sjmatthew struct uaq_cdata {
222497492a7Sjmatthew 	struct uaq_chain	 uaq_rx_chain[UAQ_RX_LIST_CNT];
223497492a7Sjmatthew 	struct uaq_chain	 uaq_tx_chain[UAQ_TX_LIST_CNT];
224497492a7Sjmatthew 	SLIST_HEAD(uaq_list_head, uaq_chain) uaq_tx_free;
225497492a7Sjmatthew };
226497492a7Sjmatthew 
227497492a7Sjmatthew struct uaq_softc {
228497492a7Sjmatthew 	struct device		 sc_dev;
229497492a7Sjmatthew 	struct usbd_device	*sc_udev;
230497492a7Sjmatthew 
231497492a7Sjmatthew 	struct usbd_interface	*sc_iface;
232497492a7Sjmatthew 	struct usb_task		 sc_link_task;
233497492a7Sjmatthew 	struct timeval		 sc_rx_notice;
234497492a7Sjmatthew 	int			 sc_ed[UAQ_ENDPT_MAX];
235497492a7Sjmatthew 	struct usbd_pipe	*sc_ep[UAQ_ENDPT_MAX];
236497492a7Sjmatthew 	int			 sc_out_frame_size;
237497492a7Sjmatthew 
238497492a7Sjmatthew 	struct arpcom		 sc_ac;
239497492a7Sjmatthew 	struct ifmedia		 sc_ifmedia;
240497492a7Sjmatthew 
241497492a7Sjmatthew 	struct uaq_cdata	 sc_cdata;
242497492a7Sjmatthew 	uint64_t		 sc_link_status;
243497492a7Sjmatthew 	int			 sc_link_speed;
244497492a7Sjmatthew 
245497492a7Sjmatthew 	uint32_t		 sc_phy_cfg;
246497492a7Sjmatthew 	uint16_t		 sc_rxctl;
247497492a7Sjmatthew };
248497492a7Sjmatthew 
249497492a7Sjmatthew const struct usb_devno uaq_devs[] = {
250497492a7Sjmatthew 	{ USB_VENDOR_AQUANTIA, USB_PRODUCT_AQUANTIA_AQC111 },
251497492a7Sjmatthew 	{ USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX111 },
252497492a7Sjmatthew 	{ USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX112 },
253497492a7Sjmatthew 	{ USB_VENDOR_TRENDNET, USB_PRODUCT_TRENDNET_TUCET5G },
254497492a7Sjmatthew 	{ USB_VENDOR_QNAP, USB_PRODUCT_QNAP_UC5G1T },
255497492a7Sjmatthew };
256497492a7Sjmatthew 
257497492a7Sjmatthew int		uaq_match(struct device *, void *, void *);
258497492a7Sjmatthew void		uaq_attach(struct device *, struct device *, void *);
259497492a7Sjmatthew int		uaq_detach(struct device *, int);
260497492a7Sjmatthew 
261497492a7Sjmatthew int		uaq_ctl(struct uaq_softc *, uint8_t, uint8_t, uint16_t,
262497492a7Sjmatthew 		    uint16_t, void *, int);
263497492a7Sjmatthew int		uaq_read_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
264497492a7Sjmatthew 		    void *, int);
265497492a7Sjmatthew int		uaq_write_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
266497492a7Sjmatthew 		    void *, int);
267497492a7Sjmatthew uint8_t		uaq_read_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
268497492a7Sjmatthew uint16_t	uaq_read_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
269497492a7Sjmatthew uint32_t	uaq_read_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t);
270497492a7Sjmatthew int		uaq_write_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
271497492a7Sjmatthew 		    uint32_t);
272497492a7Sjmatthew int		uaq_write_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
273497492a7Sjmatthew 		    uint32_t);
274497492a7Sjmatthew int		uaq_write_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t,
275497492a7Sjmatthew 		    uint32_t);
276497492a7Sjmatthew 
277497492a7Sjmatthew int		uaq_ifmedia_upd(struct ifnet *);
278497492a7Sjmatthew void		uaq_ifmedia_sts(struct ifnet *, struct ifmediareq *);
279497492a7Sjmatthew void		uaq_add_media_types(struct uaq_softc *);
280497492a7Sjmatthew void		uaq_iff(struct uaq_softc *);
281497492a7Sjmatthew 
282497492a7Sjmatthew void		uaq_init(void *);
283497492a7Sjmatthew int		uaq_ioctl(struct ifnet *, u_long, caddr_t);
284497492a7Sjmatthew int		uaq_xfer_list_init(struct uaq_softc *, struct uaq_chain *,
285497492a7Sjmatthew 		    uint32_t, int);
286497492a7Sjmatthew void		uaq_xfer_list_free(struct uaq_softc *, struct uaq_chain *, int);
287497492a7Sjmatthew 
288497492a7Sjmatthew void		uaq_stop(struct uaq_softc *);
289497492a7Sjmatthew void		uaq_link(struct uaq_softc *);
290497492a7Sjmatthew void		uaq_intr(struct usbd_xfer *, void *, usbd_status);
291497492a7Sjmatthew void		uaq_start(struct ifnet *);
292497492a7Sjmatthew void		uaq_rxeof(struct usbd_xfer *, void *, usbd_status);
293497492a7Sjmatthew void		uaq_txeof(struct usbd_xfer *, void *, usbd_status);
294497492a7Sjmatthew void		uaq_watchdog(struct ifnet *);
295497492a7Sjmatthew void		uaq_reset(struct uaq_softc *);
296497492a7Sjmatthew 
297497492a7Sjmatthew int		uaq_encap_txpkt(struct uaq_softc *, struct mbuf *, char *,
298497492a7Sjmatthew 		    uint32_t);
299497492a7Sjmatthew int		uaq_encap_xfer(struct uaq_softc *, struct uaq_chain *);
300497492a7Sjmatthew 
301497492a7Sjmatthew struct cfdriver uaq_cd = {
302497492a7Sjmatthew 	NULL, "uaq", DV_IFNET
303497492a7Sjmatthew };
304497492a7Sjmatthew 
305497492a7Sjmatthew const struct cfattach uaq_ca = {
306497492a7Sjmatthew 	sizeof(struct uaq_softc), uaq_match, uaq_attach, uaq_detach
307497492a7Sjmatthew };
308497492a7Sjmatthew 
309497492a7Sjmatthew int
uaq_ctl(struct uaq_softc * sc,uint8_t rw,uint8_t cmd,uint16_t val,uint16_t index,void * buf,int len)310497492a7Sjmatthew uaq_ctl(struct uaq_softc *sc, uint8_t rw, uint8_t cmd, uint16_t val,
311497492a7Sjmatthew     uint16_t index, void *buf, int len)
312497492a7Sjmatthew {
313497492a7Sjmatthew 	usb_device_request_t	req;
314497492a7Sjmatthew 	usbd_status		err;
315497492a7Sjmatthew 
316497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
317497492a7Sjmatthew 		return 0;
318497492a7Sjmatthew 
319497492a7Sjmatthew 	if (rw == UAQ_CTL_WRITE)
320497492a7Sjmatthew 		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
321497492a7Sjmatthew 	else
322497492a7Sjmatthew 		req.bmRequestType = UT_READ_VENDOR_DEVICE;
323497492a7Sjmatthew 	req.bRequest = cmd;
324497492a7Sjmatthew 	USETW(req.wValue, val);
325497492a7Sjmatthew 	USETW(req.wIndex, index);
326497492a7Sjmatthew 	USETW(req.wLength, len);
327497492a7Sjmatthew 
328497492a7Sjmatthew 	DPRINTFN(5, ("uaq_ctl: rw %d, val 0x%04hx, index 0x%04hx, len %d\n",
329497492a7Sjmatthew 	    rw, val, index, len));
330497492a7Sjmatthew 	err = usbd_do_request(sc->sc_udev, &req, buf);
331497492a7Sjmatthew 	if (err) {
332497492a7Sjmatthew 		DPRINTF(("uaq_ctl: error %d\n", err));
333497492a7Sjmatthew 		return -1;
334497492a7Sjmatthew 	}
335497492a7Sjmatthew 
336497492a7Sjmatthew 	return 0;
337497492a7Sjmatthew }
338497492a7Sjmatthew 
339497492a7Sjmatthew int
uaq_read_mem(struct uaq_softc * sc,uint8_t cmd,uint16_t addr,uint16_t index,void * buf,int len)340497492a7Sjmatthew uaq_read_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index,
341497492a7Sjmatthew     void *buf, int len)
342497492a7Sjmatthew {
343497492a7Sjmatthew 	return (uaq_ctl(sc, UAQ_CTL_READ, cmd, addr, index, buf, len));
344497492a7Sjmatthew }
345497492a7Sjmatthew 
346497492a7Sjmatthew int
uaq_write_mem(struct uaq_softc * sc,uint8_t cmd,uint16_t addr,uint16_t index,void * buf,int len)347497492a7Sjmatthew uaq_write_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index,
348497492a7Sjmatthew     void *buf, int len)
349497492a7Sjmatthew {
350497492a7Sjmatthew 	return (uaq_ctl(sc, UAQ_CTL_WRITE, cmd, addr, index, buf, len));
351497492a7Sjmatthew }
352497492a7Sjmatthew 
353497492a7Sjmatthew uint8_t
uaq_read_1(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index)354497492a7Sjmatthew uaq_read_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
355497492a7Sjmatthew {
356497492a7Sjmatthew 	uint8_t		val;
357497492a7Sjmatthew 
358497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 1);
359497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_1: cmd %x reg %x index %x = %x\n", cmd, reg,
360497492a7Sjmatthew 	    index, val));
361497492a7Sjmatthew 	return (val);
362497492a7Sjmatthew }
363497492a7Sjmatthew 
364497492a7Sjmatthew uint16_t
uaq_read_2(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index)365497492a7Sjmatthew uaq_read_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
366497492a7Sjmatthew {
367497492a7Sjmatthew 	uint16_t	val;
368497492a7Sjmatthew 
369497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 2);
370497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_2: cmd %x reg %x index %x = %x\n", cmd, reg,
371497492a7Sjmatthew 	    index, UGETW(&val)));
372497492a7Sjmatthew 
373497492a7Sjmatthew 	return (UGETW(&val));
374497492a7Sjmatthew }
375497492a7Sjmatthew 
376497492a7Sjmatthew uint32_t
uaq_read_4(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index)377497492a7Sjmatthew uaq_read_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index)
378497492a7Sjmatthew {
379497492a7Sjmatthew 	uint32_t	val;
380497492a7Sjmatthew 
381497492a7Sjmatthew 	uaq_read_mem(sc, cmd, reg, index, &val, 4);
382497492a7Sjmatthew 	DPRINTFN(4, ("uaq_read_4: cmd %x reg %x index %x = %x\n", cmd, reg,
383497492a7Sjmatthew 	    index, UGETDW(&val)));
384497492a7Sjmatthew 	return (UGETDW(&val));
385497492a7Sjmatthew }
386497492a7Sjmatthew 
387497492a7Sjmatthew int
uaq_write_1(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index,uint32_t val)388497492a7Sjmatthew uaq_write_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
389497492a7Sjmatthew     uint32_t val)
390497492a7Sjmatthew {
391497492a7Sjmatthew 	uint8_t		temp;
392497492a7Sjmatthew 
393497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_1: cmd %x reg %x index %x: %x\n", cmd, reg,
394497492a7Sjmatthew 	    index, val));
395497492a7Sjmatthew 	temp = val & 0xff;
396497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 1));
397497492a7Sjmatthew }
398497492a7Sjmatthew 
399497492a7Sjmatthew int
uaq_write_2(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index,uint32_t val)400497492a7Sjmatthew uaq_write_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
401497492a7Sjmatthew     uint32_t val)
402497492a7Sjmatthew {
403497492a7Sjmatthew 	uint16_t	temp;
404497492a7Sjmatthew 
405497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_2: cmd %x reg %x index %x: %x\n", cmd, reg,
406497492a7Sjmatthew 	    index, val));
407497492a7Sjmatthew 	USETW(&temp, val & 0xffff);
408497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 2));
409497492a7Sjmatthew }
410497492a7Sjmatthew 
411497492a7Sjmatthew int
uaq_write_4(struct uaq_softc * sc,uint8_t cmd,uint16_t reg,uint16_t index,uint32_t val)412497492a7Sjmatthew uaq_write_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index,
413497492a7Sjmatthew     uint32_t val)
414497492a7Sjmatthew {
415497492a7Sjmatthew 	uint8_t	temp[4];
416497492a7Sjmatthew 
417497492a7Sjmatthew 	DPRINTFN(4, ("uaq_write_4: cmd %x reg %x index %x: %x\n", cmd, reg,
418497492a7Sjmatthew 	    index, val));
419497492a7Sjmatthew 	USETDW(temp, val);
420497492a7Sjmatthew 	return (uaq_write_mem(sc, cmd, reg, index, &temp, 4));
421497492a7Sjmatthew }
422497492a7Sjmatthew 
423497492a7Sjmatthew int
uaq_match(struct device * parent,void * match,void * aux)424497492a7Sjmatthew uaq_match(struct device *parent, void *match, void *aux)
425497492a7Sjmatthew {
426497492a7Sjmatthew 	struct usb_attach_arg	*uaa = aux;
427497492a7Sjmatthew 
428497492a7Sjmatthew 	if (uaa->iface == NULL || uaa->configno != 1)
429497492a7Sjmatthew 		return (UMATCH_NONE);
430497492a7Sjmatthew 
431497492a7Sjmatthew 	return (usb_lookup(uaq_devs, uaa->vendor, uaa->product) != NULL ?
432497492a7Sjmatthew 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
433497492a7Sjmatthew }
434497492a7Sjmatthew 
435497492a7Sjmatthew void
uaq_attach(struct device * parent,struct device * self,void * aux)436497492a7Sjmatthew uaq_attach(struct device *parent, struct device *self, void *aux)
437497492a7Sjmatthew {
438497492a7Sjmatthew 	struct uaq_softc		*sc = (struct uaq_softc *)self;
439497492a7Sjmatthew 	struct usb_attach_arg		*uaa = aux;
440497492a7Sjmatthew 	usb_interface_descriptor_t	*id;
441497492a7Sjmatthew 	usb_endpoint_descriptor_t	*ed;
442497492a7Sjmatthew 	struct ifnet			*ifp;
443497492a7Sjmatthew 	int				i, s;
444497492a7Sjmatthew 
445497492a7Sjmatthew 	sc->sc_udev = uaa->device;
446497492a7Sjmatthew 	sc->sc_iface = uaa->iface;
447497492a7Sjmatthew 
448497492a7Sjmatthew 	usb_init_task(&sc->sc_link_task, (void (*)(void *))uaq_link, sc,
449497492a7Sjmatthew 	    USB_TASK_TYPE_GENERIC);
450497492a7Sjmatthew 
451497492a7Sjmatthew 	id = usbd_get_interface_descriptor(sc->sc_iface);
452497492a7Sjmatthew 
453497492a7Sjmatthew 	for (i = 0; i < id->bNumEndpoints; i++) {
454497492a7Sjmatthew 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
455497492a7Sjmatthew 		if (!ed) {
456497492a7Sjmatthew 			printf("%s: couldn't get ep %d\n",
457497492a7Sjmatthew 			    sc->sc_dev.dv_xname, i);
458497492a7Sjmatthew 			return;
459497492a7Sjmatthew 		}
460497492a7Sjmatthew 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
461497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
462497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_RX] = ed->bEndpointAddress;
463497492a7Sjmatthew 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
464497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
465497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_TX] = ed->bEndpointAddress;
466497492a7Sjmatthew 			sc->sc_out_frame_size = UGETW(ed->wMaxPacketSize);
467497492a7Sjmatthew 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
468497492a7Sjmatthew 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
469497492a7Sjmatthew 			sc->sc_ed[UAQ_ENDPT_INTR] = ed->bEndpointAddress;
470497492a7Sjmatthew 		}
471497492a7Sjmatthew 	}
472497492a7Sjmatthew 
473497492a7Sjmatthew 	if ((sc->sc_ed[UAQ_ENDPT_RX] == 0) ||
474497492a7Sjmatthew 	    (sc->sc_ed[UAQ_ENDPT_TX] == 0) ||
475497492a7Sjmatthew 	    (sc->sc_ed[UAQ_ENDPT_INTR] == 0)) {
476497492a7Sjmatthew 		printf("%s: missing one or more endpoints (%d, %d, %d)\n",
477497492a7Sjmatthew 		    sc->sc_dev.dv_xname, sc->sc_ed[UAQ_ENDPT_RX],
478497492a7Sjmatthew 		    sc->sc_ed[UAQ_ENDPT_TX], sc->sc_ed[UAQ_ENDPT_INTR]);
479497492a7Sjmatthew 		return;
480497492a7Sjmatthew 	}
481497492a7Sjmatthew 
482497492a7Sjmatthew 	s = splnet();
483497492a7Sjmatthew 
484497492a7Sjmatthew 	printf("%s: ver %u.%u.%u", sc->sc_dev.dv_xname,
485497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MAJOR, 1) & 0x7f,
486497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MINOR, 1),
487497492a7Sjmatthew 	    uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_REV, 1));
488497492a7Sjmatthew 
489497492a7Sjmatthew 	uaq_read_mem(sc, UAQ_CMD_FLASH_PARAM, 0, 0, &sc->sc_ac.ac_enaddr,
490497492a7Sjmatthew 	    ETHER_ADDR_LEN);
491497492a7Sjmatthew 	printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));
492497492a7Sjmatthew 
493497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
494497492a7Sjmatthew 	ifp->if_softc = sc;
495497492a7Sjmatthew 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
496497492a7Sjmatthew 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
497497492a7Sjmatthew 	ifp->if_ioctl = uaq_ioctl;
498497492a7Sjmatthew 	ifp->if_start = uaq_start;
499497492a7Sjmatthew 	ifp->if_watchdog = uaq_watchdog;
500497492a7Sjmatthew 
501497492a7Sjmatthew 	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 |
502497492a7Sjmatthew 	    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
503497492a7Sjmatthew 
504497492a7Sjmatthew #if NVLAN > 0
505497492a7Sjmatthew 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
506497492a7Sjmatthew #endif
507497492a7Sjmatthew 
508497492a7Sjmatthew 	ifmedia_init(&sc->sc_ifmedia, IFM_IMASK, uaq_ifmedia_upd,
509497492a7Sjmatthew 	    uaq_ifmedia_sts);
510497492a7Sjmatthew 	uaq_add_media_types(sc);
511497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
512497492a7Sjmatthew 	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
513497492a7Sjmatthew 	sc->sc_ifmedia.ifm_media = sc->sc_ifmedia.ifm_cur->ifm_media;
514497492a7Sjmatthew 
515497492a7Sjmatthew 	if_attach(ifp);
516497492a7Sjmatthew 	ether_ifattach(ifp);
517497492a7Sjmatthew 
518497492a7Sjmatthew 	splx(s);
519497492a7Sjmatthew }
520497492a7Sjmatthew 
521497492a7Sjmatthew int
uaq_detach(struct device * self,int flags)522497492a7Sjmatthew uaq_detach(struct device *self, int flags)
523497492a7Sjmatthew {
524497492a7Sjmatthew 	struct uaq_softc	*sc = (struct uaq_softc *)self;
525497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
526497492a7Sjmatthew 	int			s;
527497492a7Sjmatthew 
528497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_TX] != NULL)
529497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_TX]);
530497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_RX] != NULL)
531497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_RX]);
532497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL)
533497492a7Sjmatthew 		usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_INTR]);
534497492a7Sjmatthew 
535497492a7Sjmatthew 	s = splusb();
536497492a7Sjmatthew 
537497492a7Sjmatthew 	usb_rem_task(sc->sc_udev, &sc->sc_link_task);
538497492a7Sjmatthew 
539497492a7Sjmatthew 	usb_detach_wait(&sc->sc_dev);
540497492a7Sjmatthew 
541497492a7Sjmatthew 	if (ifp->if_flags & IFF_RUNNING)
542497492a7Sjmatthew 		uaq_stop(sc);
543497492a7Sjmatthew 
544497492a7Sjmatthew 	if (ifp->if_softc != NULL) {
545497492a7Sjmatthew 		ether_ifdetach(ifp);
546497492a7Sjmatthew 		if_detach(ifp);
547497492a7Sjmatthew 	}
548497492a7Sjmatthew 
549497492a7Sjmatthew 	splx(s);
550497492a7Sjmatthew 
551497492a7Sjmatthew 	return 0;
552497492a7Sjmatthew }
553497492a7Sjmatthew 
554497492a7Sjmatthew int
uaq_ifmedia_upd(struct ifnet * ifp)555497492a7Sjmatthew uaq_ifmedia_upd(struct ifnet *ifp)
556497492a7Sjmatthew {
557497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
558497492a7Sjmatthew 	struct ifmedia		*ifm = &sc->sc_ifmedia;
559497492a7Sjmatthew 	int			 auto_adv;
560497492a7Sjmatthew 
561497492a7Sjmatthew 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
562497492a7Sjmatthew 		return (EINVAL);
563497492a7Sjmatthew 
564497492a7Sjmatthew 	auto_adv = UAQ_PHY_ADV_100M | UAQ_PHY_ADV_1G;
565497492a7Sjmatthew 	if (sc->sc_udev->speed == USB_SPEED_SUPER)
566497492a7Sjmatthew 		auto_adv |= UAQ_PHY_ADV_2_5G | UAQ_PHY_ADV_5G;
567497492a7Sjmatthew 
568497492a7Sjmatthew 	sc->sc_phy_cfg &= ~(UAQ_PHY_ADV_MASK);
569497492a7Sjmatthew 	sc->sc_phy_cfg |= UAQ_PHY_PAUSE | UAQ_PHY_ASYM_PAUSE |
570497492a7Sjmatthew 	    UAQ_PHY_DOWNSHIFT | (3 << UAQ_PHY_DSH_RETRY_SHIFT);
571497492a7Sjmatthew 
572497492a7Sjmatthew 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
573497492a7Sjmatthew 	case IFM_AUTO:
574497492a7Sjmatthew 		sc->sc_phy_cfg |= auto_adv;
575497492a7Sjmatthew 		break;
576497492a7Sjmatthew 	case IFM_5000_T:
577497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_5G;
578497492a7Sjmatthew 		break;
579497492a7Sjmatthew 	case IFM_2500_T:
580497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_2_5G;
581497492a7Sjmatthew 		break;
582497492a7Sjmatthew 	case IFM_1000_T:
583497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_1G;
584497492a7Sjmatthew 		break;
585497492a7Sjmatthew 	case IFM_100_TX:
586497492a7Sjmatthew 		sc->sc_phy_cfg |= UAQ_PHY_ADV_100M;
587497492a7Sjmatthew 		break;
588497492a7Sjmatthew 	default:
589497492a7Sjmatthew 		printf("%s: unsupported media type\n", sc->sc_dev.dv_xname);
590497492a7Sjmatthew 		return (EINVAL);
591497492a7Sjmatthew 	}
592497492a7Sjmatthew 
593497492a7Sjmatthew 	DPRINTFN(1, ("%s: phy cfg %x\n", sc->sc_dev.dv_xname, sc->sc_phy_cfg));
594497492a7Sjmatthew 	uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg);
595497492a7Sjmatthew 	return (0);
596497492a7Sjmatthew }
597497492a7Sjmatthew 
598497492a7Sjmatthew void
uaq_ifmedia_sts(struct ifnet * ifp,struct ifmediareq * ifmr)599497492a7Sjmatthew uaq_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
600497492a7Sjmatthew {
601497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
602497492a7Sjmatthew 
603497492a7Sjmatthew 	ifmr->ifm_status = IFM_AVALID;
604497492a7Sjmatthew 	if (sc->sc_link_speed > 0) {
605497492a7Sjmatthew 		ifmr->ifm_status |= IFM_ACTIVE;
606497492a7Sjmatthew 		ifmr->ifm_active = IFM_ETHER | IFM_FDX;
607497492a7Sjmatthew 		switch (sc->sc_link_speed) {
608497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
609497492a7Sjmatthew 			ifmr->ifm_active |= IFM_5000_T;
610497492a7Sjmatthew 			break;
611497492a7Sjmatthew 		case UAQ_STATUS_SPEED_2_5G:
612497492a7Sjmatthew 			ifmr->ifm_active |= IFM_2500_T;
613497492a7Sjmatthew 			break;
614497492a7Sjmatthew 		case UAQ_STATUS_SPEED_1G:
615497492a7Sjmatthew 			ifmr->ifm_active |= IFM_1000_T;
616497492a7Sjmatthew 			break;
617497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
618497492a7Sjmatthew 			ifmr->ifm_active |= IFM_100_TX;
619497492a7Sjmatthew 			break;
620497492a7Sjmatthew 		default:
621497492a7Sjmatthew 			break;
622497492a7Sjmatthew 		}
623497492a7Sjmatthew 	}
624497492a7Sjmatthew }
625497492a7Sjmatthew 
626497492a7Sjmatthew void
uaq_add_media_types(struct uaq_softc * sc)627497492a7Sjmatthew uaq_add_media_types(struct uaq_softc *sc)
628497492a7Sjmatthew {
629497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
630497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0,
631497492a7Sjmatthew 	    NULL);
632497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T, 0, NULL);
633497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T | IFM_FDX, 0,
634497492a7Sjmatthew 	    NULL);
635497492a7Sjmatthew 	/* only add 2.5G and 5G if at super speed */
636497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T, 0, NULL);
637497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T | IFM_FDX, 0,
638497492a7Sjmatthew 	    NULL);
639497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T, 0, NULL);
640497492a7Sjmatthew 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T | IFM_FDX, 0,
641497492a7Sjmatthew 	    NULL);
642497492a7Sjmatthew }
643497492a7Sjmatthew 
644497492a7Sjmatthew void
uaq_iff(struct uaq_softc * sc)645497492a7Sjmatthew uaq_iff(struct uaq_softc *sc)
646497492a7Sjmatthew {
647497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
648497492a7Sjmatthew 	struct ether_multi	*enm;
649497492a7Sjmatthew 	struct ether_multistep	step;
650497492a7Sjmatthew 	uint8_t			filter[UAQ_MCAST_FILTER_SIZE];
651497492a7Sjmatthew 	uint32_t		hash;
652497492a7Sjmatthew 
653497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
654497492a7Sjmatthew 		return;
655497492a7Sjmatthew 
656497492a7Sjmatthew 	sc->sc_rxctl &= ~(UAQ_SFR_RX_CTL_PRO | UAQ_SFR_RX_CTL_AMALL |
657497492a7Sjmatthew 	    UAQ_SFR_RX_CTL_AM);
658093575ddSjmatthew 	ifp->if_flags &= ~IFF_ALLMULTI;
659093575ddSjmatthew 
660093575ddSjmatthew 	if (ifp->if_flags & IFF_PROMISC) {
661093575ddSjmatthew 		ifp->if_flags |= IFF_ALLMULTI;
662497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_PRO;
663093575ddSjmatthew 	} else if (sc->sc_ac.ac_multirangecnt > 0) {
664093575ddSjmatthew 		ifp->if_flags |= IFF_ALLMULTI;
665497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_AMALL;
666093575ddSjmatthew 	} else {
667497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_AM;
668497492a7Sjmatthew 
669497492a7Sjmatthew 		bzero(filter, sizeof(filter));
670497492a7Sjmatthew 		ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
671497492a7Sjmatthew 		while (enm != NULL) {
672497492a7Sjmatthew 			hash = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN)
673497492a7Sjmatthew 			    >> 26;
674497492a7Sjmatthew 			filter[hash >> 3] |= (1 << (hash & 7));
675497492a7Sjmatthew 			ETHER_NEXT_MULTI(step, enm);
676497492a7Sjmatthew 		}
677497492a7Sjmatthew 
678497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MCAST_FILTER,
679497492a7Sjmatthew 		    UAQ_MCAST_FILTER_SIZE, filter, UAQ_MCAST_FILTER_SIZE);
680497492a7Sjmatthew 	}
681497492a7Sjmatthew 
682497492a7Sjmatthew 	DPRINTFN(1, ("%s: rxctl = %x\n", sc->sc_dev.dv_xname, sc->sc_rxctl));
683497492a7Sjmatthew 	uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl);
684497492a7Sjmatthew }
685497492a7Sjmatthew 
686497492a7Sjmatthew void
uaq_reset(struct uaq_softc * sc)687497492a7Sjmatthew uaq_reset(struct uaq_softc *sc)
688497492a7Sjmatthew {
689497492a7Sjmatthew 	uint8_t mode;
690497492a7Sjmatthew 
691497492a7Sjmatthew 	sc->sc_phy_cfg = UAQ_PHY_POWER_EN;
692497492a7Sjmatthew 	uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg);
693497492a7Sjmatthew 
694497492a7Sjmatthew 	uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, 0,
695497492a7Sjmatthew 	    sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
696497492a7Sjmatthew 	uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, ETHER_ADDR_LEN,
697497492a7Sjmatthew 	    sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
698497492a7Sjmatthew 
699497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BM_INT_MASK, 0, 0xff);
700497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_SWP_CTRL, 0, 0);
701497492a7Sjmatthew 
702497492a7Sjmatthew 	mode = uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1);
703497492a7Sjmatthew 	mode &= ~(UAQ_SFR_MONITOR_MODE_EPHYRW | UAQ_SFR_MONITOR_MODE_RWLC |
704497492a7Sjmatthew 	    UAQ_SFR_MONITOR_MODE_RWMP | UAQ_SFR_MONITOR_MODE_RWWF |
705497492a7Sjmatthew 	    UAQ_SFR_MONITOR_MODE_RW_FLAG);
706497492a7Sjmatthew 	uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1, mode);
707497492a7Sjmatthew 
708497492a7Sjmatthew 	sc->sc_link_status = 0;
709497492a7Sjmatthew 	sc->sc_link_speed = 0;
710497492a7Sjmatthew }
711497492a7Sjmatthew 
712497492a7Sjmatthew void
uaq_init(void * xsc)713497492a7Sjmatthew uaq_init(void *xsc)
714497492a7Sjmatthew {
715497492a7Sjmatthew 	struct uaq_softc	*sc = xsc;
716497492a7Sjmatthew 	struct uaq_chain	*c;
717497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
718497492a7Sjmatthew 	usbd_status		err;
719497492a7Sjmatthew 	int			s, i;
720497492a7Sjmatthew 
721497492a7Sjmatthew 	s = splnet();
722497492a7Sjmatthew 
723497492a7Sjmatthew 	uaq_stop(sc);
724497492a7Sjmatthew 
725497492a7Sjmatthew 	uaq_reset(sc);
726497492a7Sjmatthew 
727497492a7Sjmatthew 	if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_rx_chain,
728497492a7Sjmatthew 		UAQ_RX_BUFSZ, UAQ_RX_LIST_CNT) == ENOBUFS) {
729497492a7Sjmatthew 		printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
730497492a7Sjmatthew 		splx(s);
731497492a7Sjmatthew 		return;
732497492a7Sjmatthew 	}
733497492a7Sjmatthew 
734497492a7Sjmatthew 	if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_tx_chain,
735497492a7Sjmatthew 		UAQ_TX_BUFSZ, UAQ_TX_LIST_CNT) == ENOBUFS) {
736497492a7Sjmatthew 		printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
737497492a7Sjmatthew 		splx(s);
738497492a7Sjmatthew 		return;
739497492a7Sjmatthew 	}
740497492a7Sjmatthew 
741497492a7Sjmatthew 	SLIST_INIT(&sc->sc_cdata.uaq_tx_free);
742497492a7Sjmatthew 	for (i = 0; i < UAQ_TX_LIST_CNT; i++)
743497492a7Sjmatthew 		SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free,
744497492a7Sjmatthew 		    &sc->sc_cdata.uaq_tx_chain[i], uc_list);
745497492a7Sjmatthew 
746497492a7Sjmatthew 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_RX],
747497492a7Sjmatthew 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_RX]);
748497492a7Sjmatthew 	if (err) {
749497492a7Sjmatthew 		printf("%s: open rx pipe failed: %s\n",
750497492a7Sjmatthew 		    sc->sc_dev.dv_xname, usbd_errstr(err));
751497492a7Sjmatthew 		splx(s);
752497492a7Sjmatthew 		return;
753497492a7Sjmatthew 	}
754497492a7Sjmatthew 
755497492a7Sjmatthew 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_TX],
756497492a7Sjmatthew 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_TX]);
757497492a7Sjmatthew 	if (err) {
758497492a7Sjmatthew 		printf("%s: open tx pipe failed: %s\n",
759497492a7Sjmatthew 		    sc->sc_dev.dv_xname, usbd_errstr(err));
760497492a7Sjmatthew 		splx(s);
761497492a7Sjmatthew 		return;
762497492a7Sjmatthew 	}
763497492a7Sjmatthew 
764497492a7Sjmatthew 	for (i = 0; i < UAQ_RX_LIST_CNT; i++) {
765497492a7Sjmatthew 		c = &sc->sc_cdata.uaq_rx_chain[i];
766497492a7Sjmatthew 		usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_RX],
767497492a7Sjmatthew 		    c, c->uc_buf, c->uc_bufmax,
768497492a7Sjmatthew 		    USBD_SHORT_XFER_OK | USBD_NO_COPY,
769497492a7Sjmatthew 		    USBD_NO_TIMEOUT, uaq_rxeof);
770497492a7Sjmatthew 		usbd_transfer(c->uc_xfer);
771497492a7Sjmatthew 	}
772497492a7Sjmatthew 
773497492a7Sjmatthew 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_INTR],
774497492a7Sjmatthew 	    0, &sc->sc_ep[UAQ_ENDPT_INTR], sc,
775497492a7Sjmatthew 	    &sc->sc_link_status, sizeof(sc->sc_link_status), uaq_intr,
776497492a7Sjmatthew 	    USBD_DEFAULT_INTERVAL);
777497492a7Sjmatthew 	if (err) {
778497492a7Sjmatthew 		printf("%s: couldn't open interrupt pipe\n",
779497492a7Sjmatthew 		    sc->sc_dev.dv_xname);
780f1599906Sjmatthew 		splx(s);
781497492a7Sjmatthew 		return;
782497492a7Sjmatthew 	}
783497492a7Sjmatthew 
784093575ddSjmatthew 	uaq_iff(sc);
785093575ddSjmatthew 
786497492a7Sjmatthew 	uaq_ifmedia_upd(ifp);
787497492a7Sjmatthew 
788497492a7Sjmatthew 	ifp->if_flags |= IFF_RUNNING;
789497492a7Sjmatthew 	ifq_clr_oactive(&ifp->if_snd);
790497492a7Sjmatthew 
791497492a7Sjmatthew 	splx(s);
792497492a7Sjmatthew }
793497492a7Sjmatthew 
794497492a7Sjmatthew int
uaq_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)795497492a7Sjmatthew uaq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
796497492a7Sjmatthew {
797497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
798497492a7Sjmatthew 	struct ifreq		*ifr = (struct ifreq *)data;
799497492a7Sjmatthew 	int			s, error = 0;
800497492a7Sjmatthew 
801497492a7Sjmatthew 	s = splnet();
802497492a7Sjmatthew 
803497492a7Sjmatthew 	switch (cmd) {
804497492a7Sjmatthew 	case SIOCSIFADDR:
805497492a7Sjmatthew 		ifp->if_flags |= IFF_UP;
806497492a7Sjmatthew 		if (!(ifp->if_flags & IFF_RUNNING))
807497492a7Sjmatthew 			uaq_init(sc);
808497492a7Sjmatthew 		break;
809497492a7Sjmatthew 
810497492a7Sjmatthew 	case SIOCSIFFLAGS:
811497492a7Sjmatthew 		if (ifp->if_flags & IFF_UP) {
812497492a7Sjmatthew 			if (ifp->if_flags & IFF_RUNNING)
813497492a7Sjmatthew 				error = ENETRESET;
814497492a7Sjmatthew 			else
815497492a7Sjmatthew 				uaq_init(sc);
816497492a7Sjmatthew 		} else {
817497492a7Sjmatthew 			if (ifp->if_flags & IFF_RUNNING)
818497492a7Sjmatthew 				uaq_stop(sc);
819497492a7Sjmatthew 		}
820497492a7Sjmatthew 		break;
821497492a7Sjmatthew 
822497492a7Sjmatthew 	case SIOCGIFMEDIA:
823497492a7Sjmatthew 	case SIOCSIFMEDIA:
824497492a7Sjmatthew 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
825497492a7Sjmatthew 		break;
826497492a7Sjmatthew 
827497492a7Sjmatthew 	default:
828497492a7Sjmatthew 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
829497492a7Sjmatthew 	}
830497492a7Sjmatthew 
831497492a7Sjmatthew 	if (error == ENETRESET) {
832497492a7Sjmatthew 		if (ifp->if_flags & IFF_RUNNING)
833497492a7Sjmatthew 			uaq_iff(sc);
834497492a7Sjmatthew 		error = 0;
835497492a7Sjmatthew 	}
836497492a7Sjmatthew 
837497492a7Sjmatthew 	splx(s);
838497492a7Sjmatthew 
839497492a7Sjmatthew 	return (error);
840497492a7Sjmatthew }
841497492a7Sjmatthew 
842497492a7Sjmatthew int
uaq_xfer_list_init(struct uaq_softc * sc,struct uaq_chain * ch,uint32_t bufsize,int listlen)843497492a7Sjmatthew uaq_xfer_list_init(struct uaq_softc *sc, struct uaq_chain *ch,
844497492a7Sjmatthew     uint32_t bufsize, int listlen)
845497492a7Sjmatthew {
846497492a7Sjmatthew 	struct uaq_chain	*c;
847497492a7Sjmatthew 	int			i;
848497492a7Sjmatthew 
849497492a7Sjmatthew 	for (i = 0; i < listlen; i++) {
850497492a7Sjmatthew 		c = &ch[i];
851497492a7Sjmatthew 		c->uc_sc = sc;
852497492a7Sjmatthew 		c->uc_idx = i;
853497492a7Sjmatthew 		c->uc_buflen = 0;
854497492a7Sjmatthew 		c->uc_bufmax = bufsize;
855497492a7Sjmatthew 		c->uc_cnt = 0;
856497492a7Sjmatthew 		if (c->uc_xfer == NULL) {
857497492a7Sjmatthew 			c->uc_xfer = usbd_alloc_xfer(sc->sc_udev);
858497492a7Sjmatthew 			if (c->uc_xfer == NULL)
859497492a7Sjmatthew 				return (ENOBUFS);
860497492a7Sjmatthew 
861497492a7Sjmatthew 			c->uc_buf = usbd_alloc_buffer(c->uc_xfer, c->uc_bufmax);
862497492a7Sjmatthew 			if (c->uc_buf == NULL) {
863497492a7Sjmatthew 				usbd_free_xfer(c->uc_xfer);
864497492a7Sjmatthew 				c->uc_xfer = NULL;
865497492a7Sjmatthew 				return (ENOBUFS);
866497492a7Sjmatthew 			}
867497492a7Sjmatthew 		}
868497492a7Sjmatthew 	}
869497492a7Sjmatthew 
870497492a7Sjmatthew 	return (0);
871497492a7Sjmatthew }
872497492a7Sjmatthew 
873497492a7Sjmatthew void
uaq_xfer_list_free(struct uaq_softc * sc,struct uaq_chain * ch,int listlen)874497492a7Sjmatthew uaq_xfer_list_free(struct uaq_softc *sc, struct uaq_chain *ch, int listlen)
875497492a7Sjmatthew {
876497492a7Sjmatthew 	int	i;
877497492a7Sjmatthew 
878497492a7Sjmatthew 	for (i = 0; i < listlen; i++) {
879497492a7Sjmatthew 		if (ch[i].uc_buf != NULL) {
880497492a7Sjmatthew 			ch[i].uc_buf = NULL;
881497492a7Sjmatthew 		}
882497492a7Sjmatthew 		ch[i].uc_cnt = 0;
883497492a7Sjmatthew 		if (ch[i].uc_xfer != NULL) {
884497492a7Sjmatthew 			usbd_free_xfer(ch[i].uc_xfer);
885497492a7Sjmatthew 			ch[i].uc_xfer = NULL;
886497492a7Sjmatthew 		}
887497492a7Sjmatthew 	}
888497492a7Sjmatthew }
889497492a7Sjmatthew 
890497492a7Sjmatthew void
uaq_stop(struct uaq_softc * sc)891497492a7Sjmatthew uaq_stop(struct uaq_softc *sc)
892497492a7Sjmatthew {
893497492a7Sjmatthew 	struct uaq_cdata	*cd;
894497492a7Sjmatthew 	struct ifnet		*ifp;
895497492a7Sjmatthew 	usbd_status		err;
896497492a7Sjmatthew 
897497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
898497492a7Sjmatthew 	ifp->if_timer = 0;
899497492a7Sjmatthew 	ifp->if_flags &= ~IFF_RUNNING;
900497492a7Sjmatthew 	ifq_clr_oactive(&ifp->if_snd);
901497492a7Sjmatthew 
902497492a7Sjmatthew 	sc->sc_link_status = 0;
903497492a7Sjmatthew 	sc->sc_link_speed = 0;
904497492a7Sjmatthew 
905497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_RX] != NULL) {
906497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_RX]);
907497492a7Sjmatthew 		if (err) {
908497492a7Sjmatthew 			printf("%s: close rx pipe failed: %s\n",
909497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
910497492a7Sjmatthew 		}
911497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_RX] = NULL;
912497492a7Sjmatthew 	}
913497492a7Sjmatthew 
914497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_TX] != NULL) {
915497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_TX]);
916497492a7Sjmatthew 		if (err) {
917497492a7Sjmatthew 			printf("%s: close tx pipe failed: %s\n",
918497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
919497492a7Sjmatthew 		}
920497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_TX] = NULL;
921497492a7Sjmatthew 	}
922497492a7Sjmatthew 
923497492a7Sjmatthew 	if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL) {
924497492a7Sjmatthew 		err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_INTR]);
925497492a7Sjmatthew 		if (err) {
926497492a7Sjmatthew 			printf("%s: close intr pipe failed: %s\n",
927497492a7Sjmatthew 			    sc->sc_dev.dv_xname, usbd_errstr(err));
928497492a7Sjmatthew 		}
929497492a7Sjmatthew 		sc->sc_ep[UAQ_ENDPT_INTR] = NULL;
930497492a7Sjmatthew 	}
931497492a7Sjmatthew 
932497492a7Sjmatthew 	cd = &sc->sc_cdata;
933497492a7Sjmatthew 	uaq_xfer_list_free(sc, cd->uaq_rx_chain, UAQ_RX_LIST_CNT);
934497492a7Sjmatthew 	uaq_xfer_list_free(sc, cd->uaq_tx_chain, UAQ_TX_LIST_CNT);
935497492a7Sjmatthew }
936497492a7Sjmatthew 
937497492a7Sjmatthew void
uaq_link(struct uaq_softc * sc)938497492a7Sjmatthew uaq_link(struct uaq_softc *sc)
939497492a7Sjmatthew {
940497492a7Sjmatthew 	if (sc->sc_link_speed > 0) {
941497492a7Sjmatthew 		uint8_t resend[3] = { 0, 0xf8, 7 };
942497492a7Sjmatthew 		uint8_t qctrl[5] = { 7, 0x00, 0x01, 0x1e, 0xff };
943497492a7Sjmatthew 		uint8_t ipg = 0;
944497492a7Sjmatthew 
945497492a7Sjmatthew 		switch (sc->sc_link_speed) {
946497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
947497492a7Sjmatthew 			resend[1] = 0xfb;
948497492a7Sjmatthew 			resend[2] = 0x4;
949497492a7Sjmatthew 			break;
950497492a7Sjmatthew 
951497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
952497492a7Sjmatthew 			ipg = 5;
953497492a7Sjmatthew 			break;
954497492a7Sjmatthew 		}
955497492a7Sjmatthew 
956497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_IPG_0, 1, ipg);
957497492a7Sjmatthew 
958497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TX_PAUSE_RESEND_T,
959497492a7Sjmatthew 		    3, resend, 3);
960497492a7Sjmatthew 		uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_BULKIN_QCTRL,
961497492a7Sjmatthew 		    5, qctrl, 5);
962497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_PAUSE_WATERLVL_LOW,
963497492a7Sjmatthew 		    2, 0x0810);
964497492a7Sjmatthew 
965497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMRX_DMA_CTRL, 1,
966497492a7Sjmatthew 		    0);
967497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMTX_DMA_CTRL, 1,
968497492a7Sjmatthew 		    0);
969497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ARC_CTRL, 1, 0);
970497492a7Sjmatthew 
971497492a7Sjmatthew 		sc->sc_rxctl = UAQ_SFR_RX_CTL_IPE | UAQ_SFR_RX_CTL_AB;
972497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
973497492a7Sjmatthew 		    sc->sc_rxctl);
974497492a7Sjmatthew 
975497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ETH_MAC_PATH, 1,
976497492a7Sjmatthew 		    UAQ_SFR_RX_PATH_READY);
977497492a7Sjmatthew 
978497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
979497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_EFF_EN);
980497492a7Sjmatthew 
981497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
982497492a7Sjmatthew 		    2, 0);
983497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
984497492a7Sjmatthew 		    2, UAQ_SFR_MEDIUM_XGMIIMODE | UAQ_SFR_MEDIUM_FULL_DUPLEX |
985497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_RECEIVE_EN | UAQ_SFR_MEDIUM_RXFLOW_CTRLEN |
986497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_TXFLOW_CTRLEN);	/* JUMBO_EN */
987497492a7Sjmatthew 
988497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RXCOE_CTL, 1,
989497492a7Sjmatthew 		    UAQ_SFR_RXCOE_IP | UAQ_SFR_RXCOE_TCP | UAQ_SFR_RXCOE_UDP |
990497492a7Sjmatthew 		    UAQ_SFR_RXCOE_TCPV6 | UAQ_SFR_RXCOE_UDPV6);
991497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TXCOE_CTL, 1,
992497492a7Sjmatthew 		    UAQ_SFR_TXCOE_IP | UAQ_SFR_TXCOE_TCP | UAQ_SFR_TXCOE_UDP |
993497492a7Sjmatthew 		    UAQ_SFR_TXCOE_TCPV6 | UAQ_SFR_TXCOE_UDPV6);
994497492a7Sjmatthew 
995497492a7Sjmatthew 		sc->sc_rxctl |= UAQ_SFR_RX_CTL_START;
996497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
997497492a7Sjmatthew 		    sc->sc_rxctl);
998497492a7Sjmatthew 	} else {
999497492a7Sjmatthew 		uint16_t mode;
1000497492a7Sjmatthew 
1001497492a7Sjmatthew 		mode = uaq_read_2(sc, UAQ_CMD_ACCESS_MAC,
1002497492a7Sjmatthew 		    UAQ_SFR_MEDIUM_STATUS_MODE, 2);
1003497492a7Sjmatthew 		mode &= ~UAQ_SFR_MEDIUM_RECEIVE_EN;
1004497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE,
1005497492a7Sjmatthew 		    2, mode);
1006497492a7Sjmatthew 
1007497492a7Sjmatthew 		sc->sc_rxctl &= ~UAQ_SFR_RX_CTL_START;
1008497492a7Sjmatthew 		uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2,
1009497492a7Sjmatthew 		    sc->sc_rxctl);
1010497492a7Sjmatthew 
1011497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
1012497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_FLUSH_EN | UAQ_SFR_BULK_OUT_EFF_EN);
1013497492a7Sjmatthew 
1014497492a7Sjmatthew 		uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1,
1015497492a7Sjmatthew 		    UAQ_SFR_BULK_OUT_EFF_EN);
1016497492a7Sjmatthew 	}
1017497492a7Sjmatthew }
1018497492a7Sjmatthew 
1019497492a7Sjmatthew void
uaq_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)1020497492a7Sjmatthew uaq_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
1021497492a7Sjmatthew {
1022497492a7Sjmatthew 	struct uaq_softc	*sc = priv;
1023497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
1024497492a7Sjmatthew 	uint64_t		linkstatus;
1025497492a7Sjmatthew 	uint64_t		baudrate;
1026497492a7Sjmatthew 	int			link_state;
1027497492a7Sjmatthew 
1028497492a7Sjmatthew 	if (status == USBD_CANCELLED)
1029497492a7Sjmatthew 		return;
1030497492a7Sjmatthew 
1031497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1032497492a7Sjmatthew 		DPRINTFN(2, ("uaq_intr: status=%d\n", status));
1033497492a7Sjmatthew 		if (status == USBD_STALLED)
1034497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1035497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_INTR]);
1036497492a7Sjmatthew 		return;
1037497492a7Sjmatthew 	}
1038497492a7Sjmatthew 
1039497492a7Sjmatthew 	linkstatus = letoh64(sc->sc_link_status);
1040497492a7Sjmatthew 	DPRINTFN(1, ("uaq_intr: link status %llx\n", linkstatus));
1041497492a7Sjmatthew 
1042497492a7Sjmatthew 	if (linkstatus & UAQ_STATUS_LINK) {
1043497492a7Sjmatthew 		link_state = LINK_STATE_FULL_DUPLEX;
1044497492a7Sjmatthew 		sc->sc_link_speed = (linkstatus & UAQ_STATUS_SPEED_MASK)
1045497492a7Sjmatthew 		    >> UAQ_STATUS_SPEED_SHIFT;
1046497492a7Sjmatthew 		switch (sc->sc_link_speed) {
1047497492a7Sjmatthew 		case UAQ_STATUS_SPEED_5G:
1048497492a7Sjmatthew 			baudrate = IF_Gbps(5);
1049497492a7Sjmatthew 			break;
1050497492a7Sjmatthew 		case UAQ_STATUS_SPEED_2_5G:
1051497492a7Sjmatthew 			baudrate = IF_Mbps(2500);
1052497492a7Sjmatthew 			break;
1053497492a7Sjmatthew 		case UAQ_STATUS_SPEED_1G:
1054497492a7Sjmatthew 			baudrate = IF_Gbps(1);
1055497492a7Sjmatthew 			break;
1056497492a7Sjmatthew 		case UAQ_STATUS_SPEED_100M:
1057497492a7Sjmatthew 			baudrate = IF_Mbps(100);
1058497492a7Sjmatthew 			break;
1059497492a7Sjmatthew 		default:
1060497492a7Sjmatthew 			baudrate = 0;
1061497492a7Sjmatthew 			break;
1062497492a7Sjmatthew 		}
1063497492a7Sjmatthew 
1064497492a7Sjmatthew 		ifp->if_baudrate = baudrate;
1065497492a7Sjmatthew 	} else {
1066497492a7Sjmatthew 		link_state = LINK_STATE_DOWN;
1067497492a7Sjmatthew 		sc->sc_link_speed = 0;
1068497492a7Sjmatthew 	}
1069497492a7Sjmatthew 
1070497492a7Sjmatthew 	if (link_state != ifp->if_link_state) {
1071497492a7Sjmatthew 		ifp->if_link_state = link_state;
1072497492a7Sjmatthew 		if_link_state_change(ifp);
1073497492a7Sjmatthew 		usb_add_task(sc->sc_udev, &sc->sc_link_task);
1074497492a7Sjmatthew 	}
1075497492a7Sjmatthew }
1076497492a7Sjmatthew 
1077497492a7Sjmatthew void
uaq_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1078497492a7Sjmatthew uaq_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
1079497492a7Sjmatthew {
1080497492a7Sjmatthew 	struct uaq_chain	*c = (struct uaq_chain *)priv;
1081497492a7Sjmatthew 	struct uaq_softc	*sc = c->uc_sc;
1082497492a7Sjmatthew 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
1083497492a7Sjmatthew 	uint8_t			*buf;
1084497492a7Sjmatthew 	uint64_t		*pdesc;
1085497492a7Sjmatthew 	uint64_t		desc;
1086497492a7Sjmatthew 	uint32_t		total_len;
1087497492a7Sjmatthew 	struct mbuf_list	ml = MBUF_LIST_INITIALIZER();
1088497492a7Sjmatthew 	struct mbuf		*m;
1089497492a7Sjmatthew 	int			pktlen, s;
1090497492a7Sjmatthew 	int			count, offset;
1091497492a7Sjmatthew 
1092497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1093497492a7Sjmatthew 		return;
1094497492a7Sjmatthew 
1095497492a7Sjmatthew 	if (!(ifp->if_flags & IFF_RUNNING))
1096497492a7Sjmatthew 		return;
1097497492a7Sjmatthew 
1098497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1099497492a7Sjmatthew 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1100497492a7Sjmatthew 			return;
1101497492a7Sjmatthew 		if (usbd_ratecheck(&sc->sc_rx_notice)) {
1102497492a7Sjmatthew 			printf("%s: usb errors on rx: %s\n",
1103497492a7Sjmatthew 				sc->sc_dev.dv_xname, usbd_errstr(status));
1104497492a7Sjmatthew 		}
1105497492a7Sjmatthew 		if (status == USBD_STALLED)
1106497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1107497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_RX]);
1108497492a7Sjmatthew 		goto done;
1109497492a7Sjmatthew 	}
1110497492a7Sjmatthew 
1111497492a7Sjmatthew 	usbd_get_xfer_status(xfer, NULL, (void **)&buf, &total_len, NULL);
1112497492a7Sjmatthew 	DPRINTFN(3, ("received %d bytes\n", total_len));
1113497492a7Sjmatthew 	if ((total_len & 7) != 0) {
1114497492a7Sjmatthew 		printf("%s: weird rx transfer length %d\n",
1115497492a7Sjmatthew 		    sc->sc_dev.dv_xname, total_len);
1116497492a7Sjmatthew 		goto done;
1117497492a7Sjmatthew 	}
1118497492a7Sjmatthew 
1119497492a7Sjmatthew 	pdesc = (uint64_t *)(buf + (total_len - sizeof(desc)));
1120497492a7Sjmatthew 	desc = lemtoh64(pdesc);
1121497492a7Sjmatthew 
1122497492a7Sjmatthew 	count = desc & UAQ_RX_HDR_COUNT_MASK;
1123497492a7Sjmatthew 	if (count == 0)
1124497492a7Sjmatthew 		goto done;
1125497492a7Sjmatthew 
1126497492a7Sjmatthew 	/* get offset of packet headers */
1127497492a7Sjmatthew 	offset = total_len - ((count + 1) * sizeof(desc));
1128497492a7Sjmatthew 	if (offset != ((desc & UAQ_RX_HDR_OFFSET_MASK) >>
1129497492a7Sjmatthew 	    UAQ_RX_HDR_OFFSET_SHIFT)) {
1130497492a7Sjmatthew 		printf("%s: offset mismatch, got %d expected %lld\n",
1131497492a7Sjmatthew 		    sc->sc_dev.dv_xname, offset,
1132497492a7Sjmatthew 		    desc >> UAQ_RX_HDR_OFFSET_SHIFT);
1133497492a7Sjmatthew 		goto done;
1134497492a7Sjmatthew 	}
1135497492a7Sjmatthew 	if (offset < 0 || offset > total_len) {
1136497492a7Sjmatthew 		printf("%s: offset %d outside buffer (%d)\n",
1137497492a7Sjmatthew 		    sc->sc_dev.dv_xname, offset, total_len);
1138497492a7Sjmatthew 		goto done;
1139497492a7Sjmatthew 	}
1140497492a7Sjmatthew 
1141497492a7Sjmatthew 	pdesc = (uint64_t *)(buf + offset);
1142497492a7Sjmatthew 	total_len = offset;
1143497492a7Sjmatthew 
1144497492a7Sjmatthew 	while (count-- > 0) {
1145497492a7Sjmatthew 		desc = lemtoh64(pdesc);
1146497492a7Sjmatthew 		pdesc++;
1147497492a7Sjmatthew 
1148497492a7Sjmatthew 		pktlen = (desc & UAQ_RX_PKT_LEN_MASK) >> UAQ_RX_PKT_LEN_SHIFT;
1149497492a7Sjmatthew 		if (pktlen > total_len) {
1150497492a7Sjmatthew 			DPRINTFN(2, ("not enough bytes for this packet\n"));
1151497492a7Sjmatthew 			ifp->if_ierrors++;
1152497492a7Sjmatthew 			goto done;
1153497492a7Sjmatthew 		}
1154497492a7Sjmatthew 
1155497492a7Sjmatthew 		m = m_devget(buf + 2, pktlen - 2, ETHER_ALIGN);
1156497492a7Sjmatthew 		if (m == NULL) {
1157497492a7Sjmatthew 			DPRINTFN(2, ("m_devget failed for this packet\n"));
1158497492a7Sjmatthew 			ifp->if_ierrors++;
1159497492a7Sjmatthew 			goto done;
1160497492a7Sjmatthew 		}
1161497492a7Sjmatthew 
1162497492a7Sjmatthew 		if ((desc & UAQ_RX_PKT_L3_ERR) == 0)
1163497492a7Sjmatthew 			m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
1164497492a7Sjmatthew 
1165497492a7Sjmatthew 		if ((desc & UAQ_RX_PKT_L4_ERR) == 0)
1166497492a7Sjmatthew 			m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
1167497492a7Sjmatthew 			    M_UDP_CSUM_IN_OK;
1168497492a7Sjmatthew 
1169497492a7Sjmatthew #if NVLAN > 0
1170497492a7Sjmatthew 		if (desc & UAQ_RX_PKT_VLAN) {
1171497492a7Sjmatthew 			m->m_pkthdr.ether_vtag = (desc >> UAQ_RX_PKT_VLAN_SHIFT) &
1172497492a7Sjmatthew 			    0xfff;
1173497492a7Sjmatthew 			m->m_flags |= M_VLANTAG;
1174497492a7Sjmatthew 		}
1175497492a7Sjmatthew #endif
1176497492a7Sjmatthew 		ml_enqueue(&ml, m);
1177497492a7Sjmatthew 
1178497492a7Sjmatthew 		total_len -= roundup(pktlen, UAQ_RX_BUF_ALIGN);
1179497492a7Sjmatthew 		buf += roundup(pktlen, UAQ_RX_BUF_ALIGN);
1180497492a7Sjmatthew 	}
1181497492a7Sjmatthew 
1182497492a7Sjmatthew done:
1183497492a7Sjmatthew 	s = splnet();
1184497492a7Sjmatthew 	if_input(ifp, &ml);
1185497492a7Sjmatthew 	splx(s);
1186497492a7Sjmatthew 	memset(c->uc_buf, 0, UAQ_RX_BUFSZ);
1187497492a7Sjmatthew 
1188497492a7Sjmatthew 	usbd_setup_xfer(xfer, sc->sc_ep[UAQ_ENDPT_RX], c, c->uc_buf,
1189497492a7Sjmatthew 	    UAQ_RX_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
1190497492a7Sjmatthew 	    USBD_NO_TIMEOUT, uaq_rxeof);
1191497492a7Sjmatthew 	usbd_transfer(xfer);
1192497492a7Sjmatthew }
1193497492a7Sjmatthew 
1194497492a7Sjmatthew 
1195497492a7Sjmatthew void
uaq_watchdog(struct ifnet * ifp)1196497492a7Sjmatthew uaq_watchdog(struct ifnet *ifp)
1197497492a7Sjmatthew {
1198497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
1199497492a7Sjmatthew 	struct uaq_chain	*c;
1200497492a7Sjmatthew 	usbd_status		err;
1201497492a7Sjmatthew 	int			i, s;
1202497492a7Sjmatthew 
1203497492a7Sjmatthew 	ifp->if_timer = 0;
1204497492a7Sjmatthew 
1205497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1206497492a7Sjmatthew 		return;
1207497492a7Sjmatthew 
1208497492a7Sjmatthew 	if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP))
1209497492a7Sjmatthew 		return;
1210497492a7Sjmatthew 
1211497492a7Sjmatthew 	sc = ifp->if_softc;
1212497492a7Sjmatthew 	s = splnet();
1213497492a7Sjmatthew 
1214497492a7Sjmatthew 	ifp->if_oerrors++;
1215497492a7Sjmatthew 	DPRINTF(("%s: watchdog timeout\n", sc->sc_dev.dv_xname));
1216497492a7Sjmatthew 
1217497492a7Sjmatthew 	for (i = 0; i < UAQ_TX_LIST_CNT; i++) {
1218497492a7Sjmatthew 		c = &sc->sc_cdata.uaq_tx_chain[i];
1219497492a7Sjmatthew 		if (c->uc_cnt > 0) {
1220497492a7Sjmatthew 			usbd_get_xfer_status(c->uc_xfer, NULL, NULL, NULL,
1221497492a7Sjmatthew 			    &err);
1222497492a7Sjmatthew 			uaq_txeof(c->uc_xfer, c, err);
1223497492a7Sjmatthew 		}
1224497492a7Sjmatthew 	}
1225497492a7Sjmatthew 
1226497492a7Sjmatthew 	if (ifq_is_oactive(&ifp->if_snd))
1227497492a7Sjmatthew 		ifq_restart(&ifp->if_snd);
1228497492a7Sjmatthew 	splx(s);
1229497492a7Sjmatthew }
1230497492a7Sjmatthew 
1231497492a7Sjmatthew void
uaq_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1232497492a7Sjmatthew uaq_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
1233497492a7Sjmatthew {
1234497492a7Sjmatthew 	struct uaq_softc	*sc;
1235497492a7Sjmatthew 	struct uaq_chain	*c;
1236497492a7Sjmatthew 	struct ifnet		*ifp;
1237497492a7Sjmatthew 	int			s;
1238497492a7Sjmatthew 
1239497492a7Sjmatthew 	c = priv;
1240497492a7Sjmatthew 	sc = c->uc_sc;
1241497492a7Sjmatthew 	ifp = &sc->sc_ac.ac_if;
1242497492a7Sjmatthew 
1243497492a7Sjmatthew 	if (usbd_is_dying(sc->sc_udev))
1244497492a7Sjmatthew 		return;
1245497492a7Sjmatthew 
1246497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION)
1247497492a7Sjmatthew 		DPRINTF(("%s: %s uc_idx=%u : %s\n", sc->sc_dev.dv_xname,
1248497492a7Sjmatthew 			__func__, c->uc_idx, usbd_errstr(status)));
1249497492a7Sjmatthew 	else
1250497492a7Sjmatthew 		DPRINTF(("%s: txeof\n", sc->sc_dev.dv_xname));
1251497492a7Sjmatthew 
1252497492a7Sjmatthew 	s = splnet();
1253497492a7Sjmatthew 
1254497492a7Sjmatthew 	c->uc_cnt = 0;
1255497492a7Sjmatthew 	c->uc_buflen = 0;
1256497492a7Sjmatthew 
1257497492a7Sjmatthew 	SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free, c, uc_list);
1258497492a7Sjmatthew 
1259497492a7Sjmatthew 	if (status != USBD_NORMAL_COMPLETION) {
1260497492a7Sjmatthew 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
1261497492a7Sjmatthew 			splx(s);
1262497492a7Sjmatthew 			return;
1263497492a7Sjmatthew 		}
1264497492a7Sjmatthew 
1265497492a7Sjmatthew 		ifp->if_oerrors++;
1266497492a7Sjmatthew 		printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname,
1267497492a7Sjmatthew 		    usbd_errstr(status));
1268497492a7Sjmatthew 
1269497492a7Sjmatthew 		if (status == USBD_STALLED)
1270497492a7Sjmatthew 			usbd_clear_endpoint_stall_async(
1271497492a7Sjmatthew 			    sc->sc_ep[UAQ_ENDPT_TX]);
1272497492a7Sjmatthew 		splx(s);
1273497492a7Sjmatthew 		return;
1274497492a7Sjmatthew 	}
1275497492a7Sjmatthew 
1276497492a7Sjmatthew 	ifp->if_timer = 0;
1277497492a7Sjmatthew 	if (ifq_is_oactive(&ifp->if_snd))
1278497492a7Sjmatthew 		ifq_restart(&ifp->if_snd);
1279497492a7Sjmatthew 	splx(s);
1280497492a7Sjmatthew }
1281497492a7Sjmatthew 
1282497492a7Sjmatthew void
uaq_start(struct ifnet * ifp)1283497492a7Sjmatthew uaq_start(struct ifnet *ifp)
1284497492a7Sjmatthew {
1285497492a7Sjmatthew 	struct uaq_softc	*sc = ifp->if_softc;
1286497492a7Sjmatthew 	struct uaq_cdata	*cd = &sc->sc_cdata;
1287497492a7Sjmatthew 	struct uaq_chain	*c;
1288497492a7Sjmatthew 	struct mbuf		*m = NULL;
1289497492a7Sjmatthew 	int			s, mlen;
1290497492a7Sjmatthew 
1291497492a7Sjmatthew 	if ((sc->sc_link_speed == 0) ||
1292497492a7Sjmatthew 		(ifp->if_flags & (IFF_RUNNING|IFF_UP)) !=
1293497492a7Sjmatthew 		    (IFF_RUNNING|IFF_UP)) {
1294497492a7Sjmatthew 		return;
1295497492a7Sjmatthew 	}
1296497492a7Sjmatthew 
1297497492a7Sjmatthew 	s = splnet();
1298497492a7Sjmatthew 
1299497492a7Sjmatthew 	c = SLIST_FIRST(&cd->uaq_tx_free);
1300497492a7Sjmatthew 	while (c != NULL) {
1301497492a7Sjmatthew 		m = ifq_deq_begin(&ifp->if_snd);
1302497492a7Sjmatthew 		if (m == NULL)
1303497492a7Sjmatthew 			break;
1304497492a7Sjmatthew 
1305497492a7Sjmatthew 		mlen = m->m_pkthdr.len;
1306497492a7Sjmatthew 
1307497492a7Sjmatthew 		/* Discard packet larger than buffer. */
1308497492a7Sjmatthew 		if (mlen + sizeof(uint64_t) >= c->uc_bufmax) {
1309497492a7Sjmatthew 			ifq_deq_commit(&ifp->if_snd, m);
1310497492a7Sjmatthew 			m_freem(m);
1311497492a7Sjmatthew 			ifp->if_oerrors++;
1312497492a7Sjmatthew 			continue;
1313497492a7Sjmatthew 		}
1314497492a7Sjmatthew 
1315497492a7Sjmatthew 		/* Append packet to current buffer. */
1316497492a7Sjmatthew 		mlen = uaq_encap_txpkt(sc, m, c->uc_buf + c->uc_buflen,
1317497492a7Sjmatthew 		    c->uc_bufmax - c->uc_buflen);
1318497492a7Sjmatthew 		if (mlen <= 0) {
1319497492a7Sjmatthew 			ifq_deq_rollback(&ifp->if_snd, m);
1320497492a7Sjmatthew 			break;
1321497492a7Sjmatthew 		}
1322497492a7Sjmatthew 
1323497492a7Sjmatthew 		ifq_deq_commit(&ifp->if_snd, m);
1324497492a7Sjmatthew 		c->uc_cnt += 1;
1325497492a7Sjmatthew 		c->uc_buflen += mlen;
1326497492a7Sjmatthew 
1327497492a7Sjmatthew #if NBPFILTER > 0
1328497492a7Sjmatthew 		if (ifp->if_bpf)
1329497492a7Sjmatthew 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
1330497492a7Sjmatthew #endif
1331497492a7Sjmatthew 
1332497492a7Sjmatthew 		m_freem(m);
1333497492a7Sjmatthew 	}
1334497492a7Sjmatthew 
1335497492a7Sjmatthew 	if (c != NULL) {
1336497492a7Sjmatthew 		/* Send current buffer unless empty */
1337497492a7Sjmatthew 		if (c->uc_buflen > 0 && c->uc_cnt > 0) {
1338497492a7Sjmatthew 			SLIST_REMOVE_HEAD(&cd->uaq_tx_free, uc_list);
1339497492a7Sjmatthew 			if (uaq_encap_xfer(sc, c)) {
1340497492a7Sjmatthew 				SLIST_INSERT_HEAD(&cd->uaq_tx_free, c,
1341497492a7Sjmatthew 				    uc_list);
1342497492a7Sjmatthew 			}
1343497492a7Sjmatthew 			c = SLIST_FIRST(&cd->uaq_tx_free);
1344497492a7Sjmatthew 
1345497492a7Sjmatthew 			ifp->if_timer = 5;
1346497492a7Sjmatthew 			if (c == NULL)
1347497492a7Sjmatthew 				ifq_set_oactive(&ifp->if_snd);
1348497492a7Sjmatthew 		}
1349497492a7Sjmatthew 	}
1350497492a7Sjmatthew 
1351497492a7Sjmatthew 	splx(s);
1352497492a7Sjmatthew }
1353497492a7Sjmatthew 
1354497492a7Sjmatthew int
uaq_encap_txpkt(struct uaq_softc * sc,struct mbuf * m,char * buf,uint32_t maxlen)1355497492a7Sjmatthew uaq_encap_txpkt(struct uaq_softc *sc, struct mbuf *m, char *buf,
1356497492a7Sjmatthew     uint32_t maxlen)
1357497492a7Sjmatthew {
1358497492a7Sjmatthew 	uint64_t		desc;
1359497492a7Sjmatthew 	int			padded;
1360497492a7Sjmatthew 
1361497492a7Sjmatthew 	desc = m->m_pkthdr.len;
1362497492a7Sjmatthew 	padded = roundup(m->m_pkthdr.len, UAQ_TX_BUF_ALIGN);
1363497492a7Sjmatthew 	if (((padded + sizeof(desc)) % sc->sc_out_frame_size) == 0) {
1364497492a7Sjmatthew 		desc |= UAQ_TX_PKT_DROP_PADD;
1365497492a7Sjmatthew 		padded += 8;
1366497492a7Sjmatthew 	}
1367497492a7Sjmatthew 
1368497492a7Sjmatthew 	if (padded + sizeof(desc) > maxlen)
1369497492a7Sjmatthew 		return (-1);
1370497492a7Sjmatthew 
1371497492a7Sjmatthew #if NVLAN > 0
1372497492a7Sjmatthew 	if (m->m_flags & M_VLANTAG)
1373497492a7Sjmatthew 		desc |= (((uint64_t)m->m_pkthdr.ether_vtag) <<
1374497492a7Sjmatthew 		    UAQ_TX_PKT_VLAN_SHIFT) | UAQ_TX_PKT_VLAN;
1375497492a7Sjmatthew #endif
1376497492a7Sjmatthew 
1377497492a7Sjmatthew 	htolem64((uint64_t *)buf, desc);
1378497492a7Sjmatthew 	m_copydata(m, 0, m->m_pkthdr.len, buf + sizeof(desc));
1379497492a7Sjmatthew 	return (padded + sizeof(desc));
1380497492a7Sjmatthew }
1381497492a7Sjmatthew 
1382497492a7Sjmatthew int
uaq_encap_xfer(struct uaq_softc * sc,struct uaq_chain * c)1383497492a7Sjmatthew uaq_encap_xfer(struct uaq_softc *sc, struct uaq_chain *c)
1384497492a7Sjmatthew {
1385497492a7Sjmatthew 	usbd_status	err;
1386497492a7Sjmatthew 
1387497492a7Sjmatthew 	usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_TX], c, c->uc_buf,
1388497492a7Sjmatthew 	    c->uc_buflen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 10000,
1389497492a7Sjmatthew 	    uaq_txeof);
1390497492a7Sjmatthew 
1391497492a7Sjmatthew 	err = usbd_transfer(c->uc_xfer);
1392497492a7Sjmatthew 	if (err != USBD_IN_PROGRESS) {
1393497492a7Sjmatthew 		c->uc_cnt = 0;
1394497492a7Sjmatthew 		c->uc_buflen = 0;
1395497492a7Sjmatthew 		uaq_stop(sc);
1396497492a7Sjmatthew 		return (EIO);
1397497492a7Sjmatthew 	}
1398497492a7Sjmatthew 
1399497492a7Sjmatthew 	return (0);
1400497492a7Sjmatthew }
1401