if_udav.c revision 242777
1184610Salfred/*	$NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $	*/
2184610Salfred/*	$nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $	*/
3184610Salfred/*	$FreeBSD: head/sys/dev/usb/net/if_udav.c 242777 2012-11-08 16:31:13Z hselasky $	*/
4184610Salfred/*-
5184610Salfred * Copyright (c) 2003
6184610Salfred *     Shingo WATANABE <nabe@nabechan.org>.  All rights reserved.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred * 3. Neither the name of the author nor the names of any co-contributors
17184610Salfred *    may be used to endorse or promote products derived from this software
18184610Salfred *    without specific prior written permission.
19184610Salfred *
20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30184610Salfred * SUCH DAMAGE.
31184610Salfred *
32184610Salfred */
33184610Salfred
34184610Salfred/*
35184610Salfred * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
36184610Salfred * The spec can be found at the following url.
37184610Salfred *   http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
38184610Salfred */
39184610Salfred
40184610Salfred/*
41184610Salfred * TODO:
42184610Salfred *	Interrupt Endpoint support
43184610Salfred *	External PHYs
44184610Salfred */
45184610Salfred
46184610Salfred#include <sys/cdefs.h>
47184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_udav.c 242777 2012-11-08 16:31:13Z hselasky $");
48184610Salfred
49194677Sthompsa#include <sys/stdint.h>
50194677Sthompsa#include <sys/stddef.h>
51194677Sthompsa#include <sys/param.h>
52194677Sthompsa#include <sys/queue.h>
53194677Sthompsa#include <sys/types.h>
54194677Sthompsa#include <sys/systm.h>
55194677Sthompsa#include <sys/kernel.h>
56194677Sthompsa#include <sys/bus.h>
57194677Sthompsa#include <sys/module.h>
58194677Sthompsa#include <sys/lock.h>
59194677Sthompsa#include <sys/mutex.h>
60194677Sthompsa#include <sys/condvar.h>
61194677Sthompsa#include <sys/sysctl.h>
62194677Sthompsa#include <sys/sx.h>
63194677Sthompsa#include <sys/unistd.h>
64194677Sthompsa#include <sys/callout.h>
65194677Sthompsa#include <sys/malloc.h>
66194677Sthompsa#include <sys/priv.h>
67194677Sthompsa
68194677Sthompsa#include <dev/usb/usb.h>
69194677Sthompsa#include <dev/usb/usbdi.h>
70194677Sthompsa#include <dev/usb/usbdi_util.h>
71188746Sthompsa#include "usbdevs.h"
72184610Salfred
73184610Salfred#define	USB_DEBUG_VAR udav_debug
74194677Sthompsa#include <dev/usb/usb_debug.h>
75188942Sthompsa#include <dev/usb/usb_process.h>
76184610Salfred
77188942Sthompsa#include <dev/usb/net/usb_ethernet.h>
78188942Sthompsa#include <dev/usb/net/if_udavreg.h>
79184610Salfred
80184610Salfred/* prototypes */
81184610Salfred
82184610Salfredstatic device_probe_t udav_probe;
83184610Salfredstatic device_attach_t udav_attach;
84184610Salfredstatic device_detach_t udav_detach;
85184610Salfred
86193045Sthompsastatic usb_callback_t udav_bulk_write_callback;
87193045Sthompsastatic usb_callback_t udav_bulk_read_callback;
88193045Sthompsastatic usb_callback_t udav_intr_callback;
89184610Salfred
90193045Sthompsastatic uether_fn_t udav_attach_post;
91193045Sthompsastatic uether_fn_t udav_init;
92193045Sthompsastatic uether_fn_t udav_stop;
93193045Sthompsastatic uether_fn_t udav_start;
94193045Sthompsastatic uether_fn_t udav_tick;
95193045Sthompsastatic uether_fn_t udav_setmulti;
96193045Sthompsastatic uether_fn_t udav_setpromisc;
97184610Salfred
98188412Sthompsastatic int	udav_csr_read(struct udav_softc *, uint16_t, void *, int);
99188412Sthompsastatic int	udav_csr_write(struct udav_softc *, uint16_t, void *, int);
100188412Sthompsastatic uint8_t	udav_csr_read1(struct udav_softc *, uint16_t);
101188412Sthompsastatic int	udav_csr_write1(struct udav_softc *, uint16_t, uint8_t);
102188412Sthompsastatic void	udav_reset(struct udav_softc *);
103188412Sthompsastatic int	udav_ifmedia_upd(struct ifnet *);
104188412Sthompsastatic void	udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
105184610Salfred
106188412Sthompsastatic miibus_readreg_t udav_miibus_readreg;
107188412Sthompsastatic miibus_writereg_t udav_miibus_writereg;
108188412Sthompsastatic miibus_statchg_t udav_miibus_statchg;
109184610Salfred
110192984Sthompsastatic const struct usb_config udav_config[UDAV_N_TRANSFER] = {
111184610Salfred
112187259Sthompsa	[UDAV_BULK_DT_WR] = {
113184610Salfred		.type = UE_BULK,
114184610Salfred		.endpoint = UE_ADDR_ANY,
115184610Salfred		.direction = UE_DIR_OUT,
116190734Sthompsa		.bufsize = (MCLBYTES + 2),
117190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
118190734Sthompsa		.callback = udav_bulk_write_callback,
119190734Sthompsa		.timeout = 10000,	/* 10 seconds */
120184610Salfred	},
121184610Salfred
122187259Sthompsa	[UDAV_BULK_DT_RD] = {
123184610Salfred		.type = UE_BULK,
124184610Salfred		.endpoint = UE_ADDR_ANY,
125184610Salfred		.direction = UE_DIR_IN,
126190734Sthompsa		.bufsize = (MCLBYTES + 3),
127190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
128190734Sthompsa		.callback = udav_bulk_read_callback,
129190734Sthompsa		.timeout = 0,	/* no timeout */
130184610Salfred	},
131184610Salfred
132187259Sthompsa	[UDAV_INTR_DT_RD] = {
133184610Salfred		.type = UE_INTERRUPT,
134184610Salfred		.endpoint = UE_ADDR_ANY,
135184610Salfred		.direction = UE_DIR_IN,
136190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
137190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
138190734Sthompsa		.callback = udav_intr_callback,
139184610Salfred	},
140184610Salfred};
141184610Salfred
142184610Salfredstatic device_method_t udav_methods[] = {
143184610Salfred	/* Device interface */
144184610Salfred	DEVMETHOD(device_probe, udav_probe),
145184610Salfred	DEVMETHOD(device_attach, udav_attach),
146184610Salfred	DEVMETHOD(device_detach, udav_detach),
147184610Salfred
148184610Salfred	/* MII interface */
149188412Sthompsa	DEVMETHOD(miibus_readreg, udav_miibus_readreg),
150188412Sthompsa	DEVMETHOD(miibus_writereg, udav_miibus_writereg),
151188412Sthompsa	DEVMETHOD(miibus_statchg, udav_miibus_statchg),
152184610Salfred
153227843Smarius	DEVMETHOD_END
154184610Salfred};
155184610Salfred
156184610Salfredstatic driver_t udav_driver = {
157184610Salfred	.name = "udav",
158184610Salfred	.methods = udav_methods,
159184610Salfred	.size = sizeof(struct udav_softc),
160184610Salfred};
161184610Salfred
162184610Salfredstatic devclass_t udav_devclass;
163184610Salfred
164189275SthompsaDRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, NULL, 0);
165184610SalfredDRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
166188942SthompsaMODULE_DEPEND(udav, uether, 1, 1, 1);
167188942SthompsaMODULE_DEPEND(udav, usb, 1, 1, 1);
168184610SalfredMODULE_DEPEND(udav, ether, 1, 1, 1);
169184610SalfredMODULE_DEPEND(udav, miibus, 1, 1, 1);
170212122SthompsaMODULE_VERSION(udav, 1);
171184610Salfred
172242777Shselaskystatic const struct usb_ether_methods udav_ue_methods = {
173188412Sthompsa	.ue_attach_post = udav_attach_post,
174188412Sthompsa	.ue_start = udav_start,
175188412Sthompsa	.ue_init = udav_init,
176188412Sthompsa	.ue_stop = udav_stop,
177188412Sthompsa	.ue_tick = udav_tick,
178188412Sthompsa	.ue_setmulti = udav_setmulti,
179188412Sthompsa	.ue_setpromisc = udav_setpromisc,
180188412Sthompsa	.ue_mii_upd = udav_ifmedia_upd,
181188412Sthompsa	.ue_mii_sts = udav_ifmedia_status,
182188412Sthompsa};
183188412Sthompsa
184242777Shselaskystatic const struct usb_ether_methods udav_ue_methods_nophy = {
185242777Shselasky	.ue_attach_post = udav_attach_post,
186242777Shselasky	.ue_start = udav_start,
187242777Shselasky	.ue_init = udav_init,
188242777Shselasky	.ue_stop = udav_stop,
189242777Shselasky	.ue_setmulti = udav_setmulti,
190242777Shselasky	.ue_setpromisc = udav_setpromisc,
191242777Shselasky};
192242777Shselasky
193207077Sthompsa#ifdef USB_DEBUG
194184610Salfredstatic int udav_debug = 0;
195184610Salfred
196227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav");
197192502SthompsaSYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0,
198184610Salfred    "Debug level");
199184610Salfred#endif
200184610Salfred
201188412Sthompsa#define	UDAV_SETBIT(sc, reg, x)	\
202188412Sthompsa	udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
203184610Salfred
204188412Sthompsa#define	UDAV_CLRBIT(sc, reg, x)	\
205188412Sthompsa	udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
206184610Salfred
207223486Shselaskystatic const STRUCT_USB_HOST_ID udav_devs[] = {
208184610Salfred	/* ShanTou DM9601 USB NIC */
209184610Salfred	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)},
210184610Salfred	/* ShanTou ST268 USB NIC */
211184610Salfred	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)},
212184610Salfred	/* Corega USB-TXC */
213184610Salfred	{USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)},
214218864Shselasky	/* ShanTou AMD8515 USB NIC */
215218864Shselasky	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ADM8515, 0)},
216218864Shselasky	/* Kontron AG USB Ethernet */
217218864Shselasky	{USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_DM9601, 0)},
218238466Srpaulo	{USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_JP1082,
219238466Srpaulo	    UDAV_FLAG_NO_PHY)},
220184610Salfred};
221184610Salfred
222188412Sthompsastatic void
223192984Sthompsaudav_attach_post(struct usb_ether *ue)
224188412Sthompsa{
225194228Sthompsa	struct udav_softc *sc = uether_getsc(ue);
226188412Sthompsa
227188412Sthompsa	/* reset the adapter */
228188412Sthompsa	udav_reset(sc);
229188412Sthompsa
230188412Sthompsa	/* Get Ethernet Address */
231188412Sthompsa	udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN);
232188412Sthompsa}
233188412Sthompsa
234184610Salfredstatic int
235184610Salfredudav_probe(device_t dev)
236184610Salfred{
237192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
238184610Salfred
239192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
240184610Salfred		return (ENXIO);
241188412Sthompsa	if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX)
242184610Salfred		return (ENXIO);
243188412Sthompsa	if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX)
244184610Salfred		return (ENXIO);
245188412Sthompsa
246194228Sthompsa	return (usbd_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa));
247184610Salfred}
248184610Salfred
249184610Salfredstatic int
250184610Salfredudav_attach(device_t dev)
251184610Salfred{
252192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
253184610Salfred	struct udav_softc *sc = device_get_softc(dev);
254192984Sthompsa	struct usb_ether *ue = &sc->sc_ue;
255184610Salfred	uint8_t iface_index;
256188412Sthompsa	int error;
257184610Salfred
258184610Salfred	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
259184610Salfred
260194228Sthompsa	device_set_usb_desc(dev);
261184610Salfred
262188412Sthompsa	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
263184610Salfred
264184610Salfred	iface_index = UDAV_IFACE_INDEX;
265194228Sthompsa	error = usbd_transfer_setup(uaa->device, &iface_index,
266187259Sthompsa	    sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx);
267184610Salfred	if (error) {
268199816Sthompsa		device_printf(dev, "allocating USB transfers failed\n");
269184610Salfred		goto detach;
270184610Salfred	}
271188412Sthompsa
272238466Srpaulo	/*
273238466Srpaulo	 * The JP1082 has an unusable PHY and provides no link information.
274238466Srpaulo	 */
275238466Srpaulo	if (sc->sc_flags & UDAV_FLAG_NO_PHY) {
276242777Shselasky		ue->ue_methods = &udav_ue_methods_nophy;
277238466Srpaulo		sc->sc_flags |= UDAV_FLAG_LINK;
278242777Shselasky	} else {
279242777Shselasky		ue->ue_methods = &udav_ue_methods;
280238466Srpaulo	}
281238466Srpaulo
282188412Sthompsa	ue->ue_sc = sc;
283188412Sthompsa	ue->ue_dev = dev;
284188412Sthompsa	ue->ue_udev = uaa->device;
285188412Sthompsa	ue->ue_mtx = &sc->sc_mtx;
286188412Sthompsa
287194228Sthompsa	error = uether_ifattach(ue);
288184610Salfred	if (error) {
289188412Sthompsa		device_printf(dev, "could not attach interface\n");
290184610Salfred		goto detach;
291184610Salfred	}
292184610Salfred
293184610Salfred	return (0);			/* success */
294184610Salfred
295184610Salfreddetach:
296184610Salfred	udav_detach(dev);
297184610Salfred	return (ENXIO);			/* failure */
298184610Salfred}
299184610Salfred
300184610Salfredstatic int
301184610Salfredudav_detach(device_t dev)
302184610Salfred{
303184610Salfred	struct udav_softc *sc = device_get_softc(dev);
304192984Sthompsa	struct usb_ether *ue = &sc->sc_ue;
305184610Salfred
306194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
307194228Sthompsa	uether_ifdetach(ue);
308184610Salfred	mtx_destroy(&sc->sc_mtx);
309184610Salfred
310184610Salfred	return (0);
311184610Salfred}
312184610Salfred
313184610Salfred#if 0
314188412Sthompsastatic int
315188412Sthompsaudav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf,
316188412Sthompsa    int len)
317184610Salfred{
318192984Sthompsa	struct usb_device_request req;
319184610Salfred
320184610Salfred	len &= 0xff;
321184610Salfred
322184610Salfred	req.bmRequestType = UT_READ_VENDOR_DEVICE;
323184610Salfred	req.bRequest = UDAV_REQ_MEM_READ;
324184610Salfred	USETW(req.wValue, 0x0000);
325184610Salfred	USETW(req.wIndex, offset);
326184610Salfred	USETW(req.wLength, len);
327184610Salfred
328194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
329184610Salfred}
330184610Salfred
331188412Sthompsastatic int
332188412Sthompsaudav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf,
333188412Sthompsa    int len)
334184610Salfred{
335192984Sthompsa	struct usb_device_request req;
336184610Salfred
337184610Salfred	len &= 0xff;
338184610Salfred
339184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
340184610Salfred	req.bRequest = UDAV_REQ_MEM_WRITE;
341184610Salfred	USETW(req.wValue, 0x0000);
342184610Salfred	USETW(req.wIndex, offset);
343184610Salfred	USETW(req.wLength, len);
344184610Salfred
345194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
346184610Salfred}
347184610Salfred
348188412Sthompsastatic int
349188412Sthompsaudav_mem_write1(struct udav_softc *sc, uint16_t offset,
350184610Salfred    uint8_t ch)
351184610Salfred{
352192984Sthompsa	struct usb_device_request req;
353184610Salfred
354184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
355184610Salfred	req.bRequest = UDAV_REQ_MEM_WRITE1;
356184610Salfred	USETW(req.wValue, ch);
357184610Salfred	USETW(req.wIndex, offset);
358184610Salfred	USETW(req.wLength, 0x0000);
359184610Salfred
360194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
361184610Salfred}
362184610Salfred#endif
363184610Salfred
364188412Sthompsastatic int
365188412Sthompsaudav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int len)
366184610Salfred{
367192984Sthompsa	struct usb_device_request req;
368184610Salfred
369184610Salfred	len &= 0xff;
370184610Salfred
371184610Salfred	req.bmRequestType = UT_READ_VENDOR_DEVICE;
372184610Salfred	req.bRequest = UDAV_REQ_REG_READ;
373184610Salfred	USETW(req.wValue, 0x0000);
374184610Salfred	USETW(req.wIndex, offset);
375184610Salfred	USETW(req.wLength, len);
376184610Salfred
377194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
378184610Salfred}
379184610Salfred
380188412Sthompsastatic int
381188412Sthompsaudav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int len)
382184610Salfred{
383192984Sthompsa	struct usb_device_request req;
384184610Salfred
385184610Salfred	offset &= 0xff;
386184610Salfred	len &= 0xff;
387184610Salfred
388184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
389184610Salfred	req.bRequest = UDAV_REQ_REG_WRITE;
390184610Salfred	USETW(req.wValue, 0x0000);
391184610Salfred	USETW(req.wIndex, offset);
392184610Salfred	USETW(req.wLength, len);
393184610Salfred
394194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
395184610Salfred}
396184610Salfred
397184610Salfredstatic uint8_t
398188412Sthompsaudav_csr_read1(struct udav_softc *sc, uint16_t offset)
399184610Salfred{
400184610Salfred	uint8_t val;
401184610Salfred
402188412Sthompsa	udav_csr_read(sc, offset, &val, 1);
403184610Salfred	return (val);
404184610Salfred}
405184610Salfred
406188412Sthompsastatic int
407188412Sthompsaudav_csr_write1(struct udav_softc *sc, uint16_t offset,
408184610Salfred    uint8_t ch)
409184610Salfred{
410192984Sthompsa	struct usb_device_request req;
411184610Salfred
412184610Salfred	offset &= 0xff;
413184610Salfred
414184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
415184610Salfred	req.bRequest = UDAV_REQ_REG_WRITE1;
416184610Salfred	USETW(req.wValue, ch);
417184610Salfred	USETW(req.wIndex, offset);
418184610Salfred	USETW(req.wLength, 0x0000);
419184610Salfred
420194228Sthompsa	return (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
421184610Salfred}
422184610Salfred
423184610Salfredstatic void
424192984Sthompsaudav_init(struct usb_ether *ue)
425184610Salfred{
426188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
427194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
428184610Salfred
429188412Sthompsa	UDAV_LOCK_ASSERT(sc, MA_OWNED);
430184610Salfred
431184610Salfred	/*
432184610Salfred	 * Cancel pending I/O
433184610Salfred	 */
434188412Sthompsa	udav_stop(ue);
435184610Salfred
436184610Salfred	/* set MAC address */
437188412Sthompsa	udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN);
438184610Salfred
439184610Salfred	/* initialize network control register */
440184610Salfred
441184610Salfred	/* disable loopback  */
442188412Sthompsa	UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
443184610Salfred
444184610Salfred	/* Initialize RX control register */
445188412Sthompsa	UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
446184610Salfred
447184610Salfred	/* load multicast filter and update promiscious mode bit */
448188412Sthompsa	udav_setpromisc(ue);
449184610Salfred
450184610Salfred	/* enable RX */
451188412Sthompsa	UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
452184610Salfred
453184610Salfred	/* clear POWER_DOWN state of internal PHY */
454188412Sthompsa	UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
455188412Sthompsa	UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
456184610Salfred
457194677Sthompsa	usbd_xfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]);
458184610Salfred
459188412Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
460188412Sthompsa	udav_start(ue);
461184610Salfred}
462184610Salfred
463184610Salfredstatic void
464188412Sthompsaudav_reset(struct udav_softc *sc)
465184610Salfred{
466188412Sthompsa	int i;
467184610Salfred
468184610Salfred	/* Select PHY */
469184610Salfred#if 1
470184610Salfred	/*
471184610Salfred	 * XXX: force select internal phy.
472184610Salfred	 *	external phy routines are not tested.
473184610Salfred	 */
474188412Sthompsa	UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
475184610Salfred#else
476188412Sthompsa	if (sc->sc_flags & UDAV_EXT_PHY)
477188412Sthompsa		UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
478188412Sthompsa	else
479188412Sthompsa		UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
480184610Salfred#endif
481184610Salfred
482188412Sthompsa	UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
483184610Salfred
484188412Sthompsa	for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
485188412Sthompsa		if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
486184610Salfred			break;
487194228Sthompsa		if (uether_pause(&sc->sc_ue, hz / 100))
488188412Sthompsa			break;
489184610Salfred	}
490184610Salfred
491194228Sthompsa	uether_pause(&sc->sc_ue, hz / 100);
492184610Salfred}
493184610Salfred
494184610Salfred#define	UDAV_BITS	6
495184610Salfredstatic void
496192984Sthompsaudav_setmulti(struct usb_ether *ue)
497184610Salfred{
498188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
499194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
500188412Sthompsa	struct ifmultiaddr *ifma;
501188412Sthompsa	uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
502188412Sthompsa	int h = 0;
503184610Salfred
504188412Sthompsa	UDAV_LOCK_ASSERT(sc, MA_OWNED);
505184610Salfred
506188412Sthompsa	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
507188412Sthompsa		UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
508188412Sthompsa		return;
509188412Sthompsa	}
510188412Sthompsa
511188412Sthompsa	/* first, zot all the existing hash bits */
512188412Sthompsa	memset(hashtbl, 0x00, sizeof(hashtbl));
513188412Sthompsa	hashtbl[7] |= 0x80;	/* broadcast address */
514188412Sthompsa	udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
515188412Sthompsa
516188412Sthompsa	/* now program new ones */
517195049Srwatson	if_maddr_rlock(ifp);
518188412Sthompsa	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
519188412Sthompsa	{
520188412Sthompsa		if (ifma->ifma_addr->sa_family != AF_LINK)
521188412Sthompsa			continue;
522188412Sthompsa		h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
523188412Sthompsa		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
524188412Sthompsa		hashtbl[h / 8] |= 1 << (h % 8);
525188412Sthompsa	}
526195049Srwatson	if_maddr_runlock(ifp);
527188412Sthompsa
528188412Sthompsa	/* disable all multicast */
529188412Sthompsa	UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
530188412Sthompsa
531188412Sthompsa	/* write hash value to the register */
532188412Sthompsa	udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
533184610Salfred}
534184610Salfred
535184610Salfredstatic void
536192984Sthompsaudav_setpromisc(struct usb_ether *ue)
537184610Salfred{
538188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
539194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
540184610Salfred	uint8_t rxmode;
541184610Salfred
542188412Sthompsa	rxmode = udav_csr_read1(sc, UDAV_RCR);
543184610Salfred	rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC);
544184610Salfred
545188412Sthompsa	if (ifp->if_flags & IFF_PROMISC)
546184610Salfred		rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC;
547188412Sthompsa	else if (ifp->if_flags & IFF_ALLMULTI)
548184610Salfred		rxmode |= UDAV_RCR_ALL;
549184610Salfred
550184610Salfred	/* write new mode bits */
551188412Sthompsa	udav_csr_write1(sc, UDAV_RCR, rxmode);
552184610Salfred}
553184610Salfred
554184610Salfredstatic void
555192984Sthompsaudav_start(struct usb_ether *ue)
556184610Salfred{
557188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
558184610Salfred
559188412Sthompsa	/*
560188412Sthompsa	 * start the USB transfers, if not already started:
561188412Sthompsa	 */
562194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]);
563194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]);
564194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]);
565184610Salfred}
566184610Salfred
567184610Salfredstatic void
568194677Sthompsaudav_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
569184610Salfred{
570194677Sthompsa	struct udav_softc *sc = usbd_xfer_softc(xfer);
571194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
572194677Sthompsa	struct usb_page_cache *pc;
573184610Salfred	struct mbuf *m;
574188412Sthompsa	int extra_len;
575188412Sthompsa	int temp_len;
576184610Salfred	uint8_t buf[2];
577184610Salfred
578184610Salfred	switch (USB_GET_STATE(xfer)) {
579184610Salfred	case USB_ST_TRANSFERRED:
580184610Salfred		DPRINTFN(11, "transfer complete\n");
581184610Salfred		ifp->if_opackets++;
582184610Salfred
583188412Sthompsa		/* FALLTHROUGH */
584184610Salfred	case USB_ST_SETUP:
585188412Sthompsatr_setup:
586188412Sthompsa		if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) {
587184610Salfred			/*
588184610Salfred			 * don't send anything if there is no link !
589184610Salfred			 */
590188412Sthompsa			return;
591184610Salfred		}
592184610Salfred		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
593184610Salfred
594188412Sthompsa		if (m == NULL)
595188412Sthompsa			return;
596188412Sthompsa		if (m->m_pkthdr.len > MCLBYTES)
597184610Salfred			m->m_pkthdr.len = MCLBYTES;
598184610Salfred		if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) {
599184610Salfred			extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len;
600184610Salfred		} else {
601184610Salfred			extra_len = 0;
602184610Salfred		}
603184610Salfred
604184610Salfred		temp_len = (m->m_pkthdr.len + extra_len);
605184610Salfred
606184610Salfred		/*
607184610Salfred		 * the frame length is specified in the first 2 bytes of the
608184610Salfred		 * buffer
609184610Salfred		 */
610184610Salfred		buf[0] = (uint8_t)(temp_len);
611184610Salfred		buf[1] = (uint8_t)(temp_len >> 8);
612184610Salfred
613184610Salfred		temp_len += 2;
614184610Salfred
615194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
616194677Sthompsa		usbd_copy_in(pc, 0, buf, 2);
617194677Sthompsa		usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len);
618184610Salfred
619194677Sthompsa		if (extra_len)
620194677Sthompsa			usbd_frame_zero(pc, temp_len - extra_len, extra_len);
621184610Salfred		/*
622184610Salfred		 * if there's a BPF listener, bounce a copy
623184610Salfred		 * of this frame to him:
624184610Salfred		 */
625184610Salfred		BPF_MTAP(ifp, m);
626184610Salfred
627184610Salfred		m_freem(m);
628184610Salfred
629194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, temp_len);
630194228Sthompsa		usbd_transfer_submit(xfer);
631184610Salfred		return;
632184610Salfred
633184610Salfred	default:			/* Error */
634184610Salfred		DPRINTFN(11, "transfer error, %s\n",
635194677Sthompsa		    usbd_errstr(error));
636184610Salfred
637188412Sthompsa		ifp->if_oerrors++;
638188412Sthompsa
639194677Sthompsa		if (error != USB_ERR_CANCELLED) {
640184610Salfred			/* try to clear stall first */
641194677Sthompsa			usbd_xfer_set_stall(xfer);
642188412Sthompsa			goto tr_setup;
643184610Salfred		}
644184610Salfred		return;
645184610Salfred	}
646184610Salfred}
647184610Salfred
648184610Salfredstatic void
649194677Sthompsaudav_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
650184610Salfred{
651194677Sthompsa	struct udav_softc *sc = usbd_xfer_softc(xfer);
652192984Sthompsa	struct usb_ether *ue = &sc->sc_ue;
653194228Sthompsa	struct ifnet *ifp = uether_getifp(ue);
654194677Sthompsa	struct usb_page_cache *pc;
655188412Sthompsa	struct udav_rxpkt stat;
656188412Sthompsa	int len;
657194677Sthompsa	int actlen;
658184610Salfred
659194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
660194677Sthompsa
661184610Salfred	switch (USB_GET_STATE(xfer)) {
662184610Salfred	case USB_ST_TRANSFERRED:
663184610Salfred
664233774Shselasky		if (actlen < (int)(sizeof(stat) + ETHER_CRC_LEN)) {
665184610Salfred			ifp->if_ierrors++;
666184610Salfred			goto tr_setup;
667184610Salfred		}
668194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
669194677Sthompsa		usbd_copy_out(pc, 0, &stat, sizeof(stat));
670194677Sthompsa		actlen -= sizeof(stat);
671194677Sthompsa		len = min(actlen, le16toh(stat.pktlen));
672188412Sthompsa		len -= ETHER_CRC_LEN;
673184610Salfred
674188412Sthompsa		if (stat.rxstat & UDAV_RSR_LCS) {
675184610Salfred			ifp->if_collisions++;
676184610Salfred			goto tr_setup;
677184610Salfred		}
678188412Sthompsa		if (stat.rxstat & UDAV_RSR_ERR) {
679184610Salfred			ifp->if_ierrors++;
680184610Salfred			goto tr_setup;
681184610Salfred		}
682194677Sthompsa		uether_rxbuf(ue, pc, sizeof(stat), len);
683188412Sthompsa		/* FALLTHROUGH */
684184610Salfred	case USB_ST_SETUP:
685184610Salfredtr_setup:
686194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
687194228Sthompsa		usbd_transfer_submit(xfer);
688194228Sthompsa		uether_rxflush(ue);
689184610Salfred		return;
690184610Salfred
691184610Salfred	default:			/* Error */
692188412Sthompsa		DPRINTF("bulk read error, %s\n",
693194677Sthompsa		    usbd_errstr(error));
694188412Sthompsa
695194677Sthompsa		if (error != USB_ERR_CANCELLED) {
696184610Salfred			/* try to clear stall first */
697194677Sthompsa			usbd_xfer_set_stall(xfer);
698188412Sthompsa			goto tr_setup;
699184610Salfred		}
700184610Salfred		return;
701184610Salfred	}
702184610Salfred}
703184610Salfred
704184610Salfredstatic void
705194677Sthompsaudav_intr_callback(struct usb_xfer *xfer, usb_error_t error)
706184610Salfred{
707184610Salfred	switch (USB_GET_STATE(xfer)) {
708184610Salfred	case USB_ST_TRANSFERRED:
709184610Salfred	case USB_ST_SETUP:
710188412Sthompsatr_setup:
711194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
712194228Sthompsa		usbd_transfer_submit(xfer);
713184610Salfred		return;
714184610Salfred
715184610Salfred	default:			/* Error */
716194677Sthompsa		if (error != USB_ERR_CANCELLED) {
717188412Sthompsa			/* try to clear stall first */
718194677Sthompsa			usbd_xfer_set_stall(xfer);
719188412Sthompsa			goto tr_setup;
720184610Salfred		}
721184610Salfred		return;
722184610Salfred	}
723184610Salfred}
724184610Salfred
725184610Salfredstatic void
726192984Sthompsaudav_stop(struct usb_ether *ue)
727184610Salfred{
728188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
729194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
730184610Salfred
731188412Sthompsa	UDAV_LOCK_ASSERT(sc, MA_OWNED);
732184610Salfred
733188412Sthompsa	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
734238466Srpaulo	if (!(sc->sc_flags & UDAV_FLAG_NO_PHY))
735238466Srpaulo		sc->sc_flags &= ~UDAV_FLAG_LINK;
736184610Salfred
737184610Salfred	/*
738184610Salfred	 * stop all the transfers, if not already stopped:
739184610Salfred	 */
740194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]);
741194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]);
742194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]);
743184610Salfred
744188412Sthompsa	udav_reset(sc);
745184610Salfred}
746184610Salfred
747184610Salfredstatic int
748188412Sthompsaudav_ifmedia_upd(struct ifnet *ifp)
749184610Salfred{
750184610Salfred	struct udav_softc *sc = ifp->if_softc;
751184610Salfred	struct mii_data *mii = GET_MII(sc);
752221407Smarius	struct mii_softc *miisc;
753184610Salfred
754188412Sthompsa	UDAV_LOCK_ASSERT(sc, MA_OWNED);
755184610Salfred
756188412Sthompsa        sc->sc_flags &= ~UDAV_FLAG_LINK;
757221407Smarius	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
758221407Smarius		PHY_RESET(miisc);
759184610Salfred	mii_mediachg(mii);
760188412Sthompsa	return (0);
761184610Salfred}
762184610Salfred
763184610Salfredstatic void
764188412Sthompsaudav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
765184610Salfred{
766184610Salfred	struct udav_softc *sc = ifp->if_softc;
767188412Sthompsa	struct mii_data *mii = GET_MII(sc);
768184610Salfred
769188412Sthompsa	UDAV_LOCK(sc);
770188412Sthompsa	mii_pollstat(mii);
771188412Sthompsa	ifmr->ifm_active = mii->mii_media_active;
772188412Sthompsa	ifmr->ifm_status = mii->mii_media_status;
773226479Syongari	UDAV_UNLOCK(sc);
774184610Salfred}
775184610Salfred
776184610Salfredstatic void
777192984Sthompsaudav_tick(struct usb_ether *ue)
778184610Salfred{
779188412Sthompsa	struct udav_softc *sc = ue->ue_sc;
780184610Salfred	struct mii_data *mii = GET_MII(sc);
781184610Salfred
782188412Sthompsa	UDAV_LOCK_ASSERT(sc, MA_OWNED);
783188412Sthompsa
784184610Salfred	mii_tick(mii);
785188412Sthompsa	if ((sc->sc_flags & UDAV_FLAG_LINK) == 0
786188412Sthompsa	    && mii->mii_media_status & IFM_ACTIVE &&
787188412Sthompsa	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
788188412Sthompsa		sc->sc_flags |= UDAV_FLAG_LINK;
789188412Sthompsa		udav_start(ue);
790184610Salfred	}
791184610Salfred}
792184610Salfred
793184610Salfredstatic int
794188412Sthompsaudav_miibus_readreg(device_t dev, int phy, int reg)
795184610Salfred{
796184610Salfred	struct udav_softc *sc = device_get_softc(dev);
797184610Salfred	uint16_t data16;
798184610Salfred	uint8_t val[2];
799188412Sthompsa	int locked;
800184610Salfred
801184610Salfred	/* XXX: one PHY only for the internal PHY */
802188412Sthompsa	if (phy != 0)
803184610Salfred		return (0);
804184610Salfred
805188412Sthompsa	locked = mtx_owned(&sc->sc_mtx);
806188412Sthompsa	if (!locked)
807188412Sthompsa		UDAV_LOCK(sc);
808188412Sthompsa
809184610Salfred	/* select internal PHY and set PHY register address */
810188412Sthompsa	udav_csr_write1(sc, UDAV_EPAR,
811184610Salfred	    UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
812184610Salfred
813184610Salfred	/* select PHY operation and start read command */
814188412Sthompsa	udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
815184610Salfred
816184610Salfred	/* XXX: should we wait? */
817184610Salfred
818184610Salfred	/* end read command */
819188412Sthompsa	UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
820184610Salfred
821184610Salfred	/* retrieve the result from data registers */
822188412Sthompsa	udav_csr_read(sc, UDAV_EPDRL, val, 2);
823184610Salfred
824184610Salfred	data16 = (val[0] | (val[1] << 8));
825184610Salfred
826184610Salfred	DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n",
827184610Salfred	    phy, reg, data16);
828184610Salfred
829188412Sthompsa	if (!locked)
830188412Sthompsa		UDAV_UNLOCK(sc);
831184610Salfred	return (data16);
832184610Salfred}
833184610Salfred
834184610Salfredstatic int
835188412Sthompsaudav_miibus_writereg(device_t dev, int phy, int reg, int data)
836184610Salfred{
837184610Salfred	struct udav_softc *sc = device_get_softc(dev);
838184610Salfred	uint8_t val[2];
839188412Sthompsa	int locked;
840184610Salfred
841184610Salfred	/* XXX: one PHY only for the internal PHY */
842188412Sthompsa	if (phy != 0)
843184610Salfred		return (0);
844184610Salfred
845188412Sthompsa	locked = mtx_owned(&sc->sc_mtx);
846188412Sthompsa	if (!locked)
847188412Sthompsa		UDAV_LOCK(sc);
848188412Sthompsa
849184610Salfred	/* select internal PHY and set PHY register address */
850188412Sthompsa	udav_csr_write1(sc, UDAV_EPAR,
851184610Salfred	    UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
852184610Salfred
853184610Salfred	/* put the value to the data registers */
854184610Salfred	val[0] = (data & 0xff);
855184610Salfred	val[1] = (data >> 8) & 0xff;
856188412Sthompsa	udav_csr_write(sc, UDAV_EPDRL, val, 2);
857184610Salfred
858184610Salfred	/* select PHY operation and start write command */
859188412Sthompsa	udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
860184610Salfred
861184610Salfred	/* XXX: should we wait? */
862184610Salfred
863184610Salfred	/* end write command */
864188412Sthompsa	UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
865184610Salfred
866188412Sthompsa	if (!locked)
867188412Sthompsa		UDAV_UNLOCK(sc);
868184610Salfred	return (0);
869184610Salfred}
870184610Salfred
871184610Salfredstatic void
872188412Sthompsaudav_miibus_statchg(device_t dev)
873184610Salfred{
874184610Salfred	/* nothing to do */
875184610Salfred}
876