xref: /freebsd/sys/dev/usb/net/if_udav.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
102ac6454SAndrew Thompson /*	$NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $	*/
202ac6454SAndrew Thompson /*	$nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $	*/
302ac6454SAndrew Thompson /*-
47282444bSPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
57282444bSPedro F. Giffuni  *
602ac6454SAndrew Thompson  * Copyright (c) 2003
702ac6454SAndrew Thompson  *     Shingo WATANABE <nabe@nabechan.org>.  All rights reserved.
802ac6454SAndrew Thompson  *
902ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
1002ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
1102ac6454SAndrew Thompson  * are met:
1202ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1302ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1402ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1502ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1602ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1702ac6454SAndrew Thompson  * 3. Neither the name of the author nor the names of any co-contributors
1802ac6454SAndrew Thompson  *    may be used to endorse or promote products derived from this software
1902ac6454SAndrew Thompson  *    without specific prior written permission.
2002ac6454SAndrew Thompson  *
2102ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2202ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2302ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2402ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2502ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2602ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2702ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2802ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2902ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3002ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3102ac6454SAndrew Thompson  * SUCH DAMAGE.
3202ac6454SAndrew Thompson  *
3302ac6454SAndrew Thompson  */
3402ac6454SAndrew Thompson 
3502ac6454SAndrew Thompson /*
3602ac6454SAndrew Thompson  * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
3702ac6454SAndrew Thompson  * The spec can be found at the following url.
38612cf1caSKevin Lo  *   http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf
3902ac6454SAndrew Thompson  */
4002ac6454SAndrew Thompson 
4102ac6454SAndrew Thompson /*
4202ac6454SAndrew Thompson  * TODO:
4302ac6454SAndrew Thompson  *	Interrupt Endpoint support
4402ac6454SAndrew Thompson  *	External PHYs
4502ac6454SAndrew Thompson  */
4602ac6454SAndrew Thompson 
4702ac6454SAndrew Thompson #include <sys/cdefs.h>
48ed6d949aSAndrew Thompson #include <sys/stdint.h>
49ed6d949aSAndrew Thompson #include <sys/stddef.h>
50ed6d949aSAndrew Thompson #include <sys/param.h>
51ed6d949aSAndrew Thompson #include <sys/queue.h>
52ed6d949aSAndrew Thompson #include <sys/types.h>
53ed6d949aSAndrew Thompson #include <sys/systm.h>
5476039bc8SGleb Smirnoff #include <sys/socket.h>
55ed6d949aSAndrew Thompson #include <sys/kernel.h>
56ed6d949aSAndrew Thompson #include <sys/bus.h>
57ed6d949aSAndrew Thompson #include <sys/module.h>
58ed6d949aSAndrew Thompson #include <sys/lock.h>
59ed6d949aSAndrew Thompson #include <sys/mutex.h>
60ed6d949aSAndrew Thompson #include <sys/condvar.h>
61ed6d949aSAndrew Thompson #include <sys/sysctl.h>
62ed6d949aSAndrew Thompson #include <sys/sx.h>
63ed6d949aSAndrew Thompson #include <sys/unistd.h>
64ed6d949aSAndrew Thompson #include <sys/callout.h>
65ed6d949aSAndrew Thompson #include <sys/malloc.h>
66ed6d949aSAndrew Thompson #include <sys/priv.h>
67ed6d949aSAndrew Thompson 
6876039bc8SGleb Smirnoff #include <net/if.h>
6976039bc8SGleb Smirnoff #include <net/if_var.h>
7031c484adSJustin Hibbits #include <net/if_media.h>
7131c484adSJustin Hibbits 
7231c484adSJustin Hibbits #include <dev/mii/mii.h>
7331c484adSJustin Hibbits #include <dev/mii/miivar.h>
7476039bc8SGleb Smirnoff 
7502ac6454SAndrew Thompson #include <dev/usb/usb.h>
76ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
77ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
78ed6d949aSAndrew Thompson #include "usbdevs.h"
7902ac6454SAndrew Thompson 
8031c484adSJustin Hibbits #include "miibus_if.h"
8131c484adSJustin Hibbits 
8202ac6454SAndrew Thompson #define	USB_DEBUG_VAR udav_debug
8302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
84ed6d949aSAndrew Thompson #include <dev/usb/usb_process.h>
8502ac6454SAndrew Thompson 
8602ac6454SAndrew Thompson #include <dev/usb/net/usb_ethernet.h>
8702ac6454SAndrew Thompson #include <dev/usb/net/if_udavreg.h>
8802ac6454SAndrew Thompson 
8902ac6454SAndrew Thompson /* prototypes */
9002ac6454SAndrew Thompson 
9102ac6454SAndrew Thompson static device_probe_t udav_probe;
9202ac6454SAndrew Thompson static device_attach_t udav_attach;
9302ac6454SAndrew Thompson static device_detach_t udav_detach;
9402ac6454SAndrew Thompson 
95e0a69b51SAndrew Thompson static usb_callback_t udav_bulk_write_callback;
96e0a69b51SAndrew Thompson static usb_callback_t udav_bulk_read_callback;
97e0a69b51SAndrew Thompson static usb_callback_t udav_intr_callback;
9802ac6454SAndrew Thompson 
99e0a69b51SAndrew Thompson static uether_fn_t udav_attach_post;
100e0a69b51SAndrew Thompson static uether_fn_t udav_init;
101e0a69b51SAndrew Thompson static uether_fn_t udav_stop;
102e0a69b51SAndrew Thompson static uether_fn_t udav_start;
103e0a69b51SAndrew Thompson static uether_fn_t udav_tick;
104e0a69b51SAndrew Thompson static uether_fn_t udav_setmulti;
105e0a69b51SAndrew Thompson static uether_fn_t udav_setpromisc;
10602ac6454SAndrew Thompson 
10702ac6454SAndrew Thompson static int	udav_csr_read(struct udav_softc *, uint16_t, void *, int);
10802ac6454SAndrew Thompson static int	udav_csr_write(struct udav_softc *, uint16_t, void *, int);
10902ac6454SAndrew Thompson static uint8_t	udav_csr_read1(struct udav_softc *, uint16_t);
11002ac6454SAndrew Thompson static int	udav_csr_write1(struct udav_softc *, uint16_t, uint8_t);
11102ac6454SAndrew Thompson static void	udav_reset(struct udav_softc *);
112*935b194dSJustin Hibbits static int	udav_ifmedia_upd(if_t);
113*935b194dSJustin Hibbits static void	udav_ifmedia_status(if_t, struct ifmediareq *);
11402ac6454SAndrew Thompson 
11502ac6454SAndrew Thompson static miibus_readreg_t udav_miibus_readreg;
11602ac6454SAndrew Thompson static miibus_writereg_t udav_miibus_writereg;
11702ac6454SAndrew Thompson static miibus_statchg_t udav_miibus_statchg;
11802ac6454SAndrew Thompson 
119760bc48eSAndrew Thompson static const struct usb_config udav_config[UDAV_N_TRANSFER] = {
12002ac6454SAndrew Thompson 	[UDAV_BULK_DT_WR] = {
12102ac6454SAndrew Thompson 		.type = UE_BULK,
12202ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
12302ac6454SAndrew Thompson 		.direction = UE_DIR_OUT,
1244eae601eSAndrew Thompson 		.bufsize = (MCLBYTES + 2),
1254eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
1264eae601eSAndrew Thompson 		.callback = udav_bulk_write_callback,
1274eae601eSAndrew Thompson 		.timeout = 10000,	/* 10 seconds */
12802ac6454SAndrew Thompson 	},
12902ac6454SAndrew Thompson 
13002ac6454SAndrew Thompson 	[UDAV_BULK_DT_RD] = {
13102ac6454SAndrew Thompson 		.type = UE_BULK,
13202ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
13302ac6454SAndrew Thompson 		.direction = UE_DIR_IN,
1344eae601eSAndrew Thompson 		.bufsize = (MCLBYTES + 3),
1354eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
1364eae601eSAndrew Thompson 		.callback = udav_bulk_read_callback,
1374eae601eSAndrew Thompson 		.timeout = 0,	/* no timeout */
13802ac6454SAndrew Thompson 	},
13902ac6454SAndrew Thompson 
14002ac6454SAndrew Thompson 	[UDAV_INTR_DT_RD] = {
14102ac6454SAndrew Thompson 		.type = UE_INTERRUPT,
14202ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
14302ac6454SAndrew Thompson 		.direction = UE_DIR_IN,
1444eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
1454eae601eSAndrew Thompson 		.bufsize = 0,	/* use wMaxPacketSize */
1464eae601eSAndrew Thompson 		.callback = udav_intr_callback,
14702ac6454SAndrew Thompson 	},
14802ac6454SAndrew Thompson };
14902ac6454SAndrew Thompson 
15002ac6454SAndrew Thompson static device_method_t udav_methods[] = {
15102ac6454SAndrew Thompson 	/* Device interface */
15202ac6454SAndrew Thompson 	DEVMETHOD(device_probe, udav_probe),
15302ac6454SAndrew Thompson 	DEVMETHOD(device_attach, udav_attach),
15402ac6454SAndrew Thompson 	DEVMETHOD(device_detach, udav_detach),
15502ac6454SAndrew Thompson 
15602ac6454SAndrew Thompson 	/* MII interface */
15702ac6454SAndrew Thompson 	DEVMETHOD(miibus_readreg, udav_miibus_readreg),
15802ac6454SAndrew Thompson 	DEVMETHOD(miibus_writereg, udav_miibus_writereg),
15902ac6454SAndrew Thompson 	DEVMETHOD(miibus_statchg, udav_miibus_statchg),
16002ac6454SAndrew Thompson 
1614b7ec270SMarius Strobl 	DEVMETHOD_END
16202ac6454SAndrew Thompson };
16302ac6454SAndrew Thompson 
16402ac6454SAndrew Thompson static driver_t udav_driver = {
16502ac6454SAndrew Thompson 	.name = "udav",
16602ac6454SAndrew Thompson 	.methods = udav_methods,
16702ac6454SAndrew Thompson 	.size = sizeof(struct udav_softc),
16802ac6454SAndrew Thompson };
16902ac6454SAndrew Thompson 
170f809f280SWarner Losh static const STRUCT_USB_HOST_ID udav_devs[] = {
171f809f280SWarner Losh 	/* ShanTou DM9601 USB NIC */
172f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)},
173f809f280SWarner Losh 	/* ShanTou ST268 USB NIC */
174f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)},
175f809f280SWarner Losh 	/* Corega USB-TXC */
176f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)},
177f809f280SWarner Losh 	/* ShanTou AMD8515 USB NIC */
178f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ADM8515, 0)},
179f809f280SWarner Losh 	/* Kontron AG USB Ethernet */
180f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_DM9601, 0)},
181f809f280SWarner Losh 	{USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_JP1082,
182f809f280SWarner Losh 	    UDAV_FLAG_NO_PHY)},
183f809f280SWarner Losh };
184f809f280SWarner Losh 
185bc9372d7SJohn Baldwin DRIVER_MODULE(udav, uhub, udav_driver, NULL, NULL);
1863e38757dSJohn Baldwin DRIVER_MODULE(miibus, udav, miibus_driver, 0, 0);
18702ac6454SAndrew Thompson MODULE_DEPEND(udav, uether, 1, 1, 1);
18802ac6454SAndrew Thompson MODULE_DEPEND(udav, usb, 1, 1, 1);
18902ac6454SAndrew Thompson MODULE_DEPEND(udav, ether, 1, 1, 1);
19002ac6454SAndrew Thompson MODULE_DEPEND(udav, miibus, 1, 1, 1);
191910cb8feSAndrew Thompson MODULE_VERSION(udav, 1);
192f809f280SWarner Losh USB_PNP_HOST_INFO(udav_devs);
19302ac6454SAndrew Thompson 
19478abbdd5SHans Petter Selasky static const struct usb_ether_methods udav_ue_methods = {
19502ac6454SAndrew Thompson 	.ue_attach_post = udav_attach_post,
19602ac6454SAndrew Thompson 	.ue_start = udav_start,
19702ac6454SAndrew Thompson 	.ue_init = udav_init,
19802ac6454SAndrew Thompson 	.ue_stop = udav_stop,
19902ac6454SAndrew Thompson 	.ue_tick = udav_tick,
20002ac6454SAndrew Thompson 	.ue_setmulti = udav_setmulti,
20102ac6454SAndrew Thompson 	.ue_setpromisc = udav_setpromisc,
20202ac6454SAndrew Thompson 	.ue_mii_upd = udav_ifmedia_upd,
20302ac6454SAndrew Thompson 	.ue_mii_sts = udav_ifmedia_status,
20402ac6454SAndrew Thompson };
20502ac6454SAndrew Thompson 
20678abbdd5SHans Petter Selasky static const struct usb_ether_methods udav_ue_methods_nophy = {
20778abbdd5SHans Petter Selasky 	.ue_attach_post = udav_attach_post,
20878abbdd5SHans Petter Selasky 	.ue_start = udav_start,
20978abbdd5SHans Petter Selasky 	.ue_init = udav_init,
21078abbdd5SHans Petter Selasky 	.ue_stop = udav_stop,
21178abbdd5SHans Petter Selasky 	.ue_setmulti = udav_setmulti,
21278abbdd5SHans Petter Selasky 	.ue_setpromisc = udav_setpromisc,
21378abbdd5SHans Petter Selasky };
21478abbdd5SHans Petter Selasky 
215b850ecc1SAndrew Thompson #ifdef USB_DEBUG
21602ac6454SAndrew Thompson static int udav_debug = 0;
21702ac6454SAndrew Thompson 
218f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
219f8d2b1f3SPawel Biernacki     "USB udav");
220ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RWTUN, &udav_debug, 0,
22102ac6454SAndrew Thompson     "Debug level");
22202ac6454SAndrew Thompson #endif
22302ac6454SAndrew Thompson 
22402ac6454SAndrew Thompson #define	UDAV_SETBIT(sc, reg, x)	\
22502ac6454SAndrew Thompson 	udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
22602ac6454SAndrew Thompson 
22702ac6454SAndrew Thompson #define	UDAV_CLRBIT(sc, reg, x)	\
22802ac6454SAndrew Thompson 	udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
22902ac6454SAndrew Thompson 
23002ac6454SAndrew Thompson static void
udav_attach_post(struct usb_ether * ue)231760bc48eSAndrew Thompson udav_attach_post(struct usb_ether *ue)
23202ac6454SAndrew Thompson {
233a593f6b8SAndrew Thompson 	struct udav_softc *sc = uether_getsc(ue);
23402ac6454SAndrew Thompson 
23502ac6454SAndrew Thompson 	/* reset the adapter */
23602ac6454SAndrew Thompson 	udav_reset(sc);
23702ac6454SAndrew Thompson 
23802ac6454SAndrew Thompson 	/* Get Ethernet Address */
23902ac6454SAndrew Thompson 	udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN);
24002ac6454SAndrew Thompson }
24102ac6454SAndrew Thompson 
24202ac6454SAndrew Thompson static int
udav_probe(device_t dev)24302ac6454SAndrew Thompson udav_probe(device_t dev)
24402ac6454SAndrew Thompson {
245760bc48eSAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(dev);
24602ac6454SAndrew Thompson 
247f29a0724SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
24802ac6454SAndrew Thompson 		return (ENXIO);
24902ac6454SAndrew Thompson 	if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX)
25002ac6454SAndrew Thompson 		return (ENXIO);
25102ac6454SAndrew Thompson 	if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX)
25202ac6454SAndrew Thompson 		return (ENXIO);
25302ac6454SAndrew Thompson 
254a593f6b8SAndrew Thompson 	return (usbd_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa));
25502ac6454SAndrew Thompson }
25602ac6454SAndrew Thompson 
25702ac6454SAndrew Thompson static int
udav_attach(device_t dev)25802ac6454SAndrew Thompson udav_attach(device_t dev)
25902ac6454SAndrew Thompson {
260760bc48eSAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(dev);
26102ac6454SAndrew Thompson 	struct udav_softc *sc = device_get_softc(dev);
262760bc48eSAndrew Thompson 	struct usb_ether *ue = &sc->sc_ue;
26302ac6454SAndrew Thompson 	uint8_t iface_index;
26402ac6454SAndrew Thompson 	int error;
26502ac6454SAndrew Thompson 
26602ac6454SAndrew Thompson 	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
26702ac6454SAndrew Thompson 
268a593f6b8SAndrew Thompson 	device_set_usb_desc(dev);
26902ac6454SAndrew Thompson 
27002ac6454SAndrew Thompson 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson 	iface_index = UDAV_IFACE_INDEX;
273a593f6b8SAndrew Thompson 	error = usbd_transfer_setup(uaa->device, &iface_index,
27402ac6454SAndrew Thompson 	    sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx);
27502ac6454SAndrew Thompson 	if (error) {
276767cb2e2SAndrew Thompson 		device_printf(dev, "allocating USB transfers failed\n");
27702ac6454SAndrew Thompson 		goto detach;
27802ac6454SAndrew Thompson 	}
27902ac6454SAndrew Thompson 
28063490e32SRui Paulo 	/*
28163490e32SRui Paulo 	 * The JP1082 has an unusable PHY and provides no link information.
28263490e32SRui Paulo 	 */
28363490e32SRui Paulo 	if (sc->sc_flags & UDAV_FLAG_NO_PHY) {
28478abbdd5SHans Petter Selasky 		ue->ue_methods = &udav_ue_methods_nophy;
28563490e32SRui Paulo 		sc->sc_flags |= UDAV_FLAG_LINK;
28678abbdd5SHans Petter Selasky 	} else {
28778abbdd5SHans Petter Selasky 		ue->ue_methods = &udav_ue_methods;
28863490e32SRui Paulo 	}
28963490e32SRui Paulo 
29002ac6454SAndrew Thompson 	ue->ue_sc = sc;
29102ac6454SAndrew Thompson 	ue->ue_dev = dev;
29202ac6454SAndrew Thompson 	ue->ue_udev = uaa->device;
29302ac6454SAndrew Thompson 	ue->ue_mtx = &sc->sc_mtx;
29402ac6454SAndrew Thompson 
295a593f6b8SAndrew Thompson 	error = uether_ifattach(ue);
29602ac6454SAndrew Thompson 	if (error) {
29702ac6454SAndrew Thompson 		device_printf(dev, "could not attach interface\n");
29802ac6454SAndrew Thompson 		goto detach;
29902ac6454SAndrew Thompson 	}
30002ac6454SAndrew Thompson 
30102ac6454SAndrew Thompson 	return (0);			/* success */
30202ac6454SAndrew Thompson 
30302ac6454SAndrew Thompson detach:
30402ac6454SAndrew Thompson 	udav_detach(dev);
30502ac6454SAndrew Thompson 	return (ENXIO);			/* failure */
30602ac6454SAndrew Thompson }
30702ac6454SAndrew Thompson 
30802ac6454SAndrew Thompson static int
udav_detach(device_t dev)30902ac6454SAndrew Thompson udav_detach(device_t dev)
31002ac6454SAndrew Thompson {
31102ac6454SAndrew Thompson 	struct udav_softc *sc = device_get_softc(dev);
312760bc48eSAndrew Thompson 	struct usb_ether *ue = &sc->sc_ue;
31302ac6454SAndrew Thompson 
314a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
315a593f6b8SAndrew Thompson 	uether_ifdetach(ue);
31602ac6454SAndrew Thompson 	mtx_destroy(&sc->sc_mtx);
31702ac6454SAndrew Thompson 
31802ac6454SAndrew Thompson 	return (0);
31902ac6454SAndrew Thompson }
32002ac6454SAndrew Thompson 
32102ac6454SAndrew Thompson #if 0
32202ac6454SAndrew Thompson static int
32302ac6454SAndrew Thompson udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf,
32402ac6454SAndrew Thompson     int len)
32502ac6454SAndrew Thompson {
326760bc48eSAndrew Thompson 	struct usb_device_request req;
32702ac6454SAndrew Thompson 
32802ac6454SAndrew Thompson 	len &= 0xff;
32902ac6454SAndrew Thompson 
33002ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
33102ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_MEM_READ;
33202ac6454SAndrew Thompson 	USETW(req.wValue, 0x0000);
33302ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
33402ac6454SAndrew Thompson 	USETW(req.wLength, len);
33502ac6454SAndrew Thompson 
336a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
33702ac6454SAndrew Thompson }
33802ac6454SAndrew Thompson 
33902ac6454SAndrew Thompson static int
34002ac6454SAndrew Thompson udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf,
34102ac6454SAndrew Thompson     int len)
34202ac6454SAndrew Thompson {
343760bc48eSAndrew Thompson 	struct usb_device_request req;
34402ac6454SAndrew Thompson 
34502ac6454SAndrew Thompson 	len &= 0xff;
34602ac6454SAndrew Thompson 
34702ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
34802ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_MEM_WRITE;
34902ac6454SAndrew Thompson 	USETW(req.wValue, 0x0000);
35002ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
35102ac6454SAndrew Thompson 	USETW(req.wLength, len);
35202ac6454SAndrew Thompson 
353a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
35402ac6454SAndrew Thompson }
35502ac6454SAndrew Thompson 
35602ac6454SAndrew Thompson static int
35702ac6454SAndrew Thompson udav_mem_write1(struct udav_softc *sc, uint16_t offset,
35802ac6454SAndrew Thompson     uint8_t ch)
35902ac6454SAndrew Thompson {
360760bc48eSAndrew Thompson 	struct usb_device_request req;
36102ac6454SAndrew Thompson 
36202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
36302ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_MEM_WRITE1;
36402ac6454SAndrew Thompson 	USETW(req.wValue, ch);
36502ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
36602ac6454SAndrew Thompson 	USETW(req.wLength, 0x0000);
36702ac6454SAndrew Thompson 
368a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
36902ac6454SAndrew Thompson }
37002ac6454SAndrew Thompson #endif
37102ac6454SAndrew Thompson 
37202ac6454SAndrew Thompson static int
udav_csr_read(struct udav_softc * sc,uint16_t offset,void * buf,int len)37302ac6454SAndrew Thompson udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int len)
37402ac6454SAndrew Thompson {
375760bc48eSAndrew Thompson 	struct usb_device_request req;
37602ac6454SAndrew Thompson 
37702ac6454SAndrew Thompson 	len &= 0xff;
37802ac6454SAndrew Thompson 
37902ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
38002ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_REG_READ;
38102ac6454SAndrew Thompson 	USETW(req.wValue, 0x0000);
38202ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
38302ac6454SAndrew Thompson 	USETW(req.wLength, len);
38402ac6454SAndrew Thompson 
385a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
38602ac6454SAndrew Thompson }
38702ac6454SAndrew Thompson 
38802ac6454SAndrew Thompson static int
udav_csr_write(struct udav_softc * sc,uint16_t offset,void * buf,int len)38902ac6454SAndrew Thompson udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int len)
39002ac6454SAndrew Thompson {
391760bc48eSAndrew Thompson 	struct usb_device_request req;
39202ac6454SAndrew Thompson 
39302ac6454SAndrew Thompson 	offset &= 0xff;
39402ac6454SAndrew Thompson 	len &= 0xff;
39502ac6454SAndrew Thompson 
39602ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
39702ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_REG_WRITE;
39802ac6454SAndrew Thompson 	USETW(req.wValue, 0x0000);
39902ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
40002ac6454SAndrew Thompson 	USETW(req.wLength, len);
40102ac6454SAndrew Thompson 
402a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
40302ac6454SAndrew Thompson }
40402ac6454SAndrew Thompson 
40502ac6454SAndrew Thompson static uint8_t
udav_csr_read1(struct udav_softc * sc,uint16_t offset)40602ac6454SAndrew Thompson udav_csr_read1(struct udav_softc *sc, uint16_t offset)
40702ac6454SAndrew Thompson {
40802ac6454SAndrew Thompson 	uint8_t val;
40902ac6454SAndrew Thompson 
41002ac6454SAndrew Thompson 	udav_csr_read(sc, offset, &val, 1);
41102ac6454SAndrew Thompson 	return (val);
41202ac6454SAndrew Thompson }
41302ac6454SAndrew Thompson 
41402ac6454SAndrew Thompson static int
udav_csr_write1(struct udav_softc * sc,uint16_t offset,uint8_t ch)41502ac6454SAndrew Thompson udav_csr_write1(struct udav_softc *sc, uint16_t offset,
41602ac6454SAndrew Thompson     uint8_t ch)
41702ac6454SAndrew Thompson {
418760bc48eSAndrew Thompson 	struct usb_device_request req;
41902ac6454SAndrew Thompson 
42002ac6454SAndrew Thompson 	offset &= 0xff;
42102ac6454SAndrew Thompson 
42202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
42302ac6454SAndrew Thompson 	req.bRequest = UDAV_REQ_REG_WRITE1;
42402ac6454SAndrew Thompson 	USETW(req.wValue, ch);
42502ac6454SAndrew Thompson 	USETW(req.wIndex, offset);
42602ac6454SAndrew Thompson 	USETW(req.wLength, 0x0000);
42702ac6454SAndrew Thompson 
428a593f6b8SAndrew Thompson 	return (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
42902ac6454SAndrew Thompson }
43002ac6454SAndrew Thompson 
43102ac6454SAndrew Thompson static void
udav_init(struct usb_ether * ue)432760bc48eSAndrew Thompson udav_init(struct usb_ether *ue)
43302ac6454SAndrew Thompson {
43402ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
435*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(&sc->sc_ue);
43602ac6454SAndrew Thompson 
43702ac6454SAndrew Thompson 	UDAV_LOCK_ASSERT(sc, MA_OWNED);
43802ac6454SAndrew Thompson 
43902ac6454SAndrew Thompson 	/*
44002ac6454SAndrew Thompson 	 * Cancel pending I/O
44102ac6454SAndrew Thompson 	 */
44202ac6454SAndrew Thompson 	udav_stop(ue);
44302ac6454SAndrew Thompson 
44402ac6454SAndrew Thompson 	/* set MAC address */
445*935b194dSJustin Hibbits 	udav_csr_write(sc, UDAV_PAR, if_getlladdr(ifp), ETHER_ADDR_LEN);
44602ac6454SAndrew Thompson 
44702ac6454SAndrew Thompson 	/* initialize network control register */
44802ac6454SAndrew Thompson 
44902ac6454SAndrew Thompson 	/* disable loopback  */
45002ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
45102ac6454SAndrew Thompson 
45202ac6454SAndrew Thompson 	/* Initialize RX control register */
45302ac6454SAndrew Thompson 	UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
45402ac6454SAndrew Thompson 
45502ac6454SAndrew Thompson 	/* load multicast filter and update promiscious mode bit */
45602ac6454SAndrew Thompson 	udav_setpromisc(ue);
45702ac6454SAndrew Thompson 
45802ac6454SAndrew Thompson 	/* enable RX */
45902ac6454SAndrew Thompson 	UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
46002ac6454SAndrew Thompson 
46102ac6454SAndrew Thompson 	/* clear POWER_DOWN state of internal PHY */
46202ac6454SAndrew Thompson 	UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
46302ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
46402ac6454SAndrew Thompson 
465ed6d949aSAndrew Thompson 	usbd_xfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]);
46602ac6454SAndrew Thompson 
467*935b194dSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
46802ac6454SAndrew Thompson 	udav_start(ue);
46902ac6454SAndrew Thompson }
47002ac6454SAndrew Thompson 
47102ac6454SAndrew Thompson static void
udav_reset(struct udav_softc * sc)47202ac6454SAndrew Thompson udav_reset(struct udav_softc *sc)
47302ac6454SAndrew Thompson {
47402ac6454SAndrew Thompson 	int i;
47502ac6454SAndrew Thompson 
47602ac6454SAndrew Thompson 	/* Select PHY */
47702ac6454SAndrew Thompson #if 1
47802ac6454SAndrew Thompson 	/*
47902ac6454SAndrew Thompson 	 * XXX: force select internal phy.
48002ac6454SAndrew Thompson 	 *	external phy routines are not tested.
48102ac6454SAndrew Thompson 	 */
48202ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
48302ac6454SAndrew Thompson #else
48402ac6454SAndrew Thompson 	if (sc->sc_flags & UDAV_EXT_PHY)
48502ac6454SAndrew Thompson 		UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
48602ac6454SAndrew Thompson 	else
48702ac6454SAndrew Thompson 		UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
48802ac6454SAndrew Thompson #endif
48902ac6454SAndrew Thompson 
49002ac6454SAndrew Thompson 	UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
49102ac6454SAndrew Thompson 
49202ac6454SAndrew Thompson 	for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
49302ac6454SAndrew Thompson 		if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
49402ac6454SAndrew Thompson 			break;
495a593f6b8SAndrew Thompson 		if (uether_pause(&sc->sc_ue, hz / 100))
49602ac6454SAndrew Thompson 			break;
49702ac6454SAndrew Thompson 	}
49802ac6454SAndrew Thompson 
499a593f6b8SAndrew Thompson 	uether_pause(&sc->sc_ue, hz / 100);
50002ac6454SAndrew Thompson }
50102ac6454SAndrew Thompson 
502ae928424SGleb Smirnoff static u_int
udav_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)503ae928424SGleb Smirnoff udav_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
504ae928424SGleb Smirnoff {
505ae928424SGleb Smirnoff 	uint8_t *hashtbl = arg;
506ae928424SGleb Smirnoff 	int h;
507ae928424SGleb Smirnoff 
508ae928424SGleb Smirnoff 	h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 26;
509ae928424SGleb Smirnoff 	hashtbl[h / 8] |= 1 << (h % 8);
510ae928424SGleb Smirnoff 
511ae928424SGleb Smirnoff 	return (1);
512ae928424SGleb Smirnoff }
513ae928424SGleb Smirnoff 
51402ac6454SAndrew Thompson static void
udav_setmulti(struct usb_ether * ue)515760bc48eSAndrew Thompson udav_setmulti(struct usb_ether *ue)
51602ac6454SAndrew Thompson {
51702ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
518*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(&sc->sc_ue);
51902ac6454SAndrew Thompson 	uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
52002ac6454SAndrew Thompson 
52102ac6454SAndrew Thompson 	UDAV_LOCK_ASSERT(sc, MA_OWNED);
52202ac6454SAndrew Thompson 
523*935b194dSJustin Hibbits 	if (if_getflags(ifp) & IFF_ALLMULTI || if_getflags(ifp) & IFF_PROMISC) {
52402ac6454SAndrew Thompson 		UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
52502ac6454SAndrew Thompson 		return;
52602ac6454SAndrew Thompson 	}
52702ac6454SAndrew Thompson 
52802ac6454SAndrew Thompson 	/* first, zot all the existing hash bits */
52902ac6454SAndrew Thompson 	memset(hashtbl, 0x00, sizeof(hashtbl));
53002ac6454SAndrew Thompson 	hashtbl[7] |= 0x80;	/* broadcast address */
53102ac6454SAndrew Thompson 	udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
53202ac6454SAndrew Thompson 
53302ac6454SAndrew Thompson 	/* now program new ones */
534ae928424SGleb Smirnoff 	if_foreach_llmaddr(ifp, udav_hash_maddr, hashtbl);
53502ac6454SAndrew Thompson 
53602ac6454SAndrew Thompson 	/* disable all multicast */
53702ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
53802ac6454SAndrew Thompson 
53902ac6454SAndrew Thompson 	/* write hash value to the register */
54002ac6454SAndrew Thompson 	udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
54102ac6454SAndrew Thompson }
54202ac6454SAndrew Thompson 
54302ac6454SAndrew Thompson static void
udav_setpromisc(struct usb_ether * ue)544760bc48eSAndrew Thompson udav_setpromisc(struct usb_ether *ue)
54502ac6454SAndrew Thompson {
54602ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
547*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(&sc->sc_ue);
54802ac6454SAndrew Thompson 	uint8_t rxmode;
54902ac6454SAndrew Thompson 
55002ac6454SAndrew Thompson 	rxmode = udav_csr_read1(sc, UDAV_RCR);
55102ac6454SAndrew Thompson 	rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC);
55202ac6454SAndrew Thompson 
553*935b194dSJustin Hibbits 	if (if_getflags(ifp) & IFF_PROMISC)
55402ac6454SAndrew Thompson 		rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC;
555*935b194dSJustin Hibbits 	else if (if_getflags(ifp) & IFF_ALLMULTI)
55602ac6454SAndrew Thompson 		rxmode |= UDAV_RCR_ALL;
55702ac6454SAndrew Thompson 
55802ac6454SAndrew Thompson 	/* write new mode bits */
55902ac6454SAndrew Thompson 	udav_csr_write1(sc, UDAV_RCR, rxmode);
56002ac6454SAndrew Thompson }
56102ac6454SAndrew Thompson 
56202ac6454SAndrew Thompson static void
udav_start(struct usb_ether * ue)563760bc48eSAndrew Thompson udav_start(struct usb_ether *ue)
56402ac6454SAndrew Thompson {
56502ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
56602ac6454SAndrew Thompson 
56702ac6454SAndrew Thompson 	/*
56802ac6454SAndrew Thompson 	 * start the USB transfers, if not already started:
56902ac6454SAndrew Thompson 	 */
570a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]);
571a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]);
572a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]);
57302ac6454SAndrew Thompson }
57402ac6454SAndrew Thompson 
57502ac6454SAndrew Thompson static void
udav_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)576ed6d949aSAndrew Thompson udav_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
57702ac6454SAndrew Thompson {
578ed6d949aSAndrew Thompson 	struct udav_softc *sc = usbd_xfer_softc(xfer);
579*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(&sc->sc_ue);
580ed6d949aSAndrew Thompson 	struct usb_page_cache *pc;
58102ac6454SAndrew Thompson 	struct mbuf *m;
58202ac6454SAndrew Thompson 	int extra_len;
58302ac6454SAndrew Thompson 	int temp_len;
58402ac6454SAndrew Thompson 	uint8_t buf[2];
58502ac6454SAndrew Thompson 
58602ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
58702ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
58802ac6454SAndrew Thompson 		DPRINTFN(11, "transfer complete\n");
589ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
59002ac6454SAndrew Thompson 
59102ac6454SAndrew Thompson 		/* FALLTHROUGH */
59202ac6454SAndrew Thompson 	case USB_ST_SETUP:
59302ac6454SAndrew Thompson tr_setup:
59402ac6454SAndrew Thompson 		if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) {
59502ac6454SAndrew Thompson 			/*
59602ac6454SAndrew Thompson 			 * don't send anything if there is no link !
59702ac6454SAndrew Thompson 			 */
59802ac6454SAndrew Thompson 			return;
59902ac6454SAndrew Thompson 		}
600*935b194dSJustin Hibbits 		m = if_dequeue(ifp);
60102ac6454SAndrew Thompson 
60202ac6454SAndrew Thompson 		if (m == NULL)
60302ac6454SAndrew Thompson 			return;
60402ac6454SAndrew Thompson 		if (m->m_pkthdr.len > MCLBYTES)
60502ac6454SAndrew Thompson 			m->m_pkthdr.len = MCLBYTES;
60602ac6454SAndrew Thompson 		if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) {
60702ac6454SAndrew Thompson 			extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len;
60802ac6454SAndrew Thompson 		} else {
60902ac6454SAndrew Thompson 			extra_len = 0;
61002ac6454SAndrew Thompson 		}
61102ac6454SAndrew Thompson 
61202ac6454SAndrew Thompson 		temp_len = (m->m_pkthdr.len + extra_len);
61302ac6454SAndrew Thompson 
61402ac6454SAndrew Thompson 		/*
61502ac6454SAndrew Thompson 		 * the frame length is specified in the first 2 bytes of the
61602ac6454SAndrew Thompson 		 * buffer
61702ac6454SAndrew Thompson 		 */
61802ac6454SAndrew Thompson 		buf[0] = (uint8_t)(temp_len);
61902ac6454SAndrew Thompson 		buf[1] = (uint8_t)(temp_len >> 8);
62002ac6454SAndrew Thompson 
62102ac6454SAndrew Thompson 		temp_len += 2;
62202ac6454SAndrew Thompson 
623ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
624ed6d949aSAndrew Thompson 		usbd_copy_in(pc, 0, buf, 2);
625ed6d949aSAndrew Thompson 		usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len);
62602ac6454SAndrew Thompson 
627ed6d949aSAndrew Thompson 		if (extra_len)
628ed6d949aSAndrew Thompson 			usbd_frame_zero(pc, temp_len - extra_len, extra_len);
62902ac6454SAndrew Thompson 		/*
63002ac6454SAndrew Thompson 		 * if there's a BPF listener, bounce a copy
63102ac6454SAndrew Thompson 		 * of this frame to him:
63202ac6454SAndrew Thompson 		 */
63302ac6454SAndrew Thompson 		BPF_MTAP(ifp, m);
63402ac6454SAndrew Thompson 
63502ac6454SAndrew Thompson 		m_freem(m);
63602ac6454SAndrew Thompson 
637ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, temp_len);
638a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
63902ac6454SAndrew Thompson 		return;
64002ac6454SAndrew Thompson 
64102ac6454SAndrew Thompson 	default:			/* Error */
64202ac6454SAndrew Thompson 		DPRINTFN(11, "transfer error, %s\n",
643ed6d949aSAndrew Thompson 		    usbd_errstr(error));
64402ac6454SAndrew Thompson 
645ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
64602ac6454SAndrew Thompson 
647ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
64802ac6454SAndrew Thompson 			/* try to clear stall first */
649ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
65002ac6454SAndrew Thompson 			goto tr_setup;
65102ac6454SAndrew Thompson 		}
65202ac6454SAndrew Thompson 		return;
65302ac6454SAndrew Thompson 	}
65402ac6454SAndrew Thompson }
65502ac6454SAndrew Thompson 
65602ac6454SAndrew Thompson static void
udav_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)657ed6d949aSAndrew Thompson udav_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
65802ac6454SAndrew Thompson {
659ed6d949aSAndrew Thompson 	struct udav_softc *sc = usbd_xfer_softc(xfer);
660760bc48eSAndrew Thompson 	struct usb_ether *ue = &sc->sc_ue;
661*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(ue);
662ed6d949aSAndrew Thompson 	struct usb_page_cache *pc;
66302ac6454SAndrew Thompson 	struct udav_rxpkt stat;
66402ac6454SAndrew Thompson 	int len;
665ed6d949aSAndrew Thompson 	int actlen;
666ed6d949aSAndrew Thompson 
667ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
66802ac6454SAndrew Thompson 
66902ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
67002ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
67102ac6454SAndrew Thompson 
6726d917491SHans Petter Selasky 		if (actlen < (int)(sizeof(stat) + ETHER_CRC_LEN)) {
673ecc70d3fSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
67402ac6454SAndrew Thompson 			goto tr_setup;
67502ac6454SAndrew Thompson 		}
676ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
677ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, &stat, sizeof(stat));
678ed6d949aSAndrew Thompson 		actlen -= sizeof(stat);
679ed6d949aSAndrew Thompson 		len = min(actlen, le16toh(stat.pktlen));
68002ac6454SAndrew Thompson 		len -= ETHER_CRC_LEN;
68102ac6454SAndrew Thompson 
68202ac6454SAndrew Thompson 		if (stat.rxstat & UDAV_RSR_LCS) {
683ecc70d3fSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
68402ac6454SAndrew Thompson 			goto tr_setup;
68502ac6454SAndrew Thompson 		}
68602ac6454SAndrew Thompson 		if (stat.rxstat & UDAV_RSR_ERR) {
687ecc70d3fSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
68802ac6454SAndrew Thompson 			goto tr_setup;
68902ac6454SAndrew Thompson 		}
690ed6d949aSAndrew Thompson 		uether_rxbuf(ue, pc, sizeof(stat), len);
69102ac6454SAndrew Thompson 		/* FALLTHROUGH */
69202ac6454SAndrew Thompson 	case USB_ST_SETUP:
69302ac6454SAndrew Thompson tr_setup:
694ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
695a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
696a593f6b8SAndrew Thompson 		uether_rxflush(ue);
69702ac6454SAndrew Thompson 		return;
69802ac6454SAndrew Thompson 
69902ac6454SAndrew Thompson 	default:			/* Error */
70002ac6454SAndrew Thompson 		DPRINTF("bulk read error, %s\n",
701ed6d949aSAndrew Thompson 		    usbd_errstr(error));
70202ac6454SAndrew Thompson 
703ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
70402ac6454SAndrew Thompson 			/* try to clear stall first */
705ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
70602ac6454SAndrew Thompson 			goto tr_setup;
70702ac6454SAndrew Thompson 		}
70802ac6454SAndrew Thompson 		return;
70902ac6454SAndrew Thompson 	}
71002ac6454SAndrew Thompson }
71102ac6454SAndrew Thompson 
71202ac6454SAndrew Thompson static void
udav_intr_callback(struct usb_xfer * xfer,usb_error_t error)713ed6d949aSAndrew Thompson udav_intr_callback(struct usb_xfer *xfer, usb_error_t error)
71402ac6454SAndrew Thompson {
71502ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
71602ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
71702ac6454SAndrew Thompson 	case USB_ST_SETUP:
71802ac6454SAndrew Thompson tr_setup:
719ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
720a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
72102ac6454SAndrew Thompson 		return;
72202ac6454SAndrew Thompson 
72302ac6454SAndrew Thompson 	default:			/* Error */
724ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
72502ac6454SAndrew Thompson 			/* try to clear stall first */
726ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
72702ac6454SAndrew Thompson 			goto tr_setup;
72802ac6454SAndrew Thompson 		}
72902ac6454SAndrew Thompson 		return;
73002ac6454SAndrew Thompson 	}
73102ac6454SAndrew Thompson }
73202ac6454SAndrew Thompson 
73302ac6454SAndrew Thompson static void
udav_stop(struct usb_ether * ue)734760bc48eSAndrew Thompson udav_stop(struct usb_ether *ue)
73502ac6454SAndrew Thompson {
73602ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
737*935b194dSJustin Hibbits 	if_t ifp = uether_getifp(&sc->sc_ue);
73802ac6454SAndrew Thompson 
73902ac6454SAndrew Thompson 	UDAV_LOCK_ASSERT(sc, MA_OWNED);
74002ac6454SAndrew Thompson 
741*935b194dSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
74263490e32SRui Paulo 	if (!(sc->sc_flags & UDAV_FLAG_NO_PHY))
74302ac6454SAndrew Thompson 		sc->sc_flags &= ~UDAV_FLAG_LINK;
74402ac6454SAndrew Thompson 
74502ac6454SAndrew Thompson 	/*
74602ac6454SAndrew Thompson 	 * stop all the transfers, if not already stopped:
74702ac6454SAndrew Thompson 	 */
748a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]);
749a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]);
750a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]);
75102ac6454SAndrew Thompson 
75202ac6454SAndrew Thompson 	udav_reset(sc);
75302ac6454SAndrew Thompson }
75402ac6454SAndrew Thompson 
75502ac6454SAndrew Thompson static int
udav_ifmedia_upd(if_t ifp)756*935b194dSJustin Hibbits udav_ifmedia_upd(if_t ifp)
75702ac6454SAndrew Thompson {
758*935b194dSJustin Hibbits 	struct udav_softc *sc = if_getsoftc(ifp);
75902ac6454SAndrew Thompson 	struct mii_data *mii = GET_MII(sc);
7603fcb7a53SMarius Strobl 	struct mii_softc *miisc;
761ebe01023SKevin Lo 	int error;
76202ac6454SAndrew Thompson 
76302ac6454SAndrew Thompson 	UDAV_LOCK_ASSERT(sc, MA_OWNED);
76402ac6454SAndrew Thompson 
76502ac6454SAndrew Thompson         sc->sc_flags &= ~UDAV_FLAG_LINK;
76602ac6454SAndrew Thompson 	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
7673fcb7a53SMarius Strobl 		PHY_RESET(miisc);
768ebe01023SKevin Lo 	error = mii_mediachg(mii);
769ebe01023SKevin Lo 	return (error);
77002ac6454SAndrew Thompson }
77102ac6454SAndrew Thompson 
77202ac6454SAndrew Thompson static void
udav_ifmedia_status(if_t ifp,struct ifmediareq * ifmr)773*935b194dSJustin Hibbits udav_ifmedia_status(if_t ifp, struct ifmediareq *ifmr)
77402ac6454SAndrew Thompson {
775*935b194dSJustin Hibbits 	struct udav_softc *sc = if_getsoftc(ifp);
77602ac6454SAndrew Thompson 	struct mii_data *mii = GET_MII(sc);
77702ac6454SAndrew Thompson 
77802ac6454SAndrew Thompson 	UDAV_LOCK(sc);
77902ac6454SAndrew Thompson 	mii_pollstat(mii);
78002ac6454SAndrew Thompson 	ifmr->ifm_active = mii->mii_media_active;
78102ac6454SAndrew Thompson 	ifmr->ifm_status = mii->mii_media_status;
782e9e549efSPyun YongHyeon 	UDAV_UNLOCK(sc);
78302ac6454SAndrew Thompson }
78402ac6454SAndrew Thompson 
78502ac6454SAndrew Thompson static void
udav_tick(struct usb_ether * ue)786760bc48eSAndrew Thompson udav_tick(struct usb_ether *ue)
78702ac6454SAndrew Thompson {
78802ac6454SAndrew Thompson 	struct udav_softc *sc = ue->ue_sc;
78902ac6454SAndrew Thompson 	struct mii_data *mii = GET_MII(sc);
79002ac6454SAndrew Thompson 
79102ac6454SAndrew Thompson 	UDAV_LOCK_ASSERT(sc, MA_OWNED);
79202ac6454SAndrew Thompson 
79302ac6454SAndrew Thompson 	mii_tick(mii);
79402ac6454SAndrew Thompson 	if ((sc->sc_flags & UDAV_FLAG_LINK) == 0
79502ac6454SAndrew Thompson 	    && mii->mii_media_status & IFM_ACTIVE &&
79602ac6454SAndrew Thompson 	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
79702ac6454SAndrew Thompson 		sc->sc_flags |= UDAV_FLAG_LINK;
79802ac6454SAndrew Thompson 		udav_start(ue);
79902ac6454SAndrew Thompson 	}
80002ac6454SAndrew Thompson }
80102ac6454SAndrew Thompson 
80202ac6454SAndrew Thompson static int
udav_miibus_readreg(device_t dev,int phy,int reg)80302ac6454SAndrew Thompson udav_miibus_readreg(device_t dev, int phy, int reg)
80402ac6454SAndrew Thompson {
80502ac6454SAndrew Thompson 	struct udav_softc *sc = device_get_softc(dev);
80602ac6454SAndrew Thompson 	uint16_t data16;
80702ac6454SAndrew Thompson 	uint8_t val[2];
80802ac6454SAndrew Thompson 	int locked;
80902ac6454SAndrew Thompson 
81002ac6454SAndrew Thompson 	/* XXX: one PHY only for the internal PHY */
81102ac6454SAndrew Thompson 	if (phy != 0)
81202ac6454SAndrew Thompson 		return (0);
81302ac6454SAndrew Thompson 
81402ac6454SAndrew Thompson 	locked = mtx_owned(&sc->sc_mtx);
81502ac6454SAndrew Thompson 	if (!locked)
81602ac6454SAndrew Thompson 		UDAV_LOCK(sc);
81702ac6454SAndrew Thompson 
81802ac6454SAndrew Thompson 	/* select internal PHY and set PHY register address */
81902ac6454SAndrew Thompson 	udav_csr_write1(sc, UDAV_EPAR,
82002ac6454SAndrew Thompson 	    UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
82102ac6454SAndrew Thompson 
82202ac6454SAndrew Thompson 	/* select PHY operation and start read command */
82302ac6454SAndrew Thompson 	udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
82402ac6454SAndrew Thompson 
82502ac6454SAndrew Thompson 	/* XXX: should we wait? */
82602ac6454SAndrew Thompson 
82702ac6454SAndrew Thompson 	/* end read command */
82802ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
82902ac6454SAndrew Thompson 
83002ac6454SAndrew Thompson 	/* retrieve the result from data registers */
83102ac6454SAndrew Thompson 	udav_csr_read(sc, UDAV_EPDRL, val, 2);
83202ac6454SAndrew Thompson 
83302ac6454SAndrew Thompson 	data16 = (val[0] | (val[1] << 8));
83402ac6454SAndrew Thompson 
83502ac6454SAndrew Thompson 	DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n",
83602ac6454SAndrew Thompson 	    phy, reg, data16);
83702ac6454SAndrew Thompson 
83802ac6454SAndrew Thompson 	if (!locked)
83902ac6454SAndrew Thompson 		UDAV_UNLOCK(sc);
84002ac6454SAndrew Thompson 	return (data16);
84102ac6454SAndrew Thompson }
84202ac6454SAndrew Thompson 
84302ac6454SAndrew Thompson static int
udav_miibus_writereg(device_t dev,int phy,int reg,int data)84402ac6454SAndrew Thompson udav_miibus_writereg(device_t dev, int phy, int reg, int data)
84502ac6454SAndrew Thompson {
84602ac6454SAndrew Thompson 	struct udav_softc *sc = device_get_softc(dev);
84702ac6454SAndrew Thompson 	uint8_t val[2];
84802ac6454SAndrew Thompson 	int locked;
84902ac6454SAndrew Thompson 
85002ac6454SAndrew Thompson 	/* XXX: one PHY only for the internal PHY */
85102ac6454SAndrew Thompson 	if (phy != 0)
85202ac6454SAndrew Thompson 		return (0);
85302ac6454SAndrew Thompson 
85402ac6454SAndrew Thompson 	locked = mtx_owned(&sc->sc_mtx);
85502ac6454SAndrew Thompson 	if (!locked)
85602ac6454SAndrew Thompson 		UDAV_LOCK(sc);
85702ac6454SAndrew Thompson 
85802ac6454SAndrew Thompson 	/* select internal PHY and set PHY register address */
85902ac6454SAndrew Thompson 	udav_csr_write1(sc, UDAV_EPAR,
86002ac6454SAndrew Thompson 	    UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
86102ac6454SAndrew Thompson 
86202ac6454SAndrew Thompson 	/* put the value to the data registers */
86302ac6454SAndrew Thompson 	val[0] = (data & 0xff);
86402ac6454SAndrew Thompson 	val[1] = (data >> 8) & 0xff;
86502ac6454SAndrew Thompson 	udav_csr_write(sc, UDAV_EPDRL, val, 2);
86602ac6454SAndrew Thompson 
86702ac6454SAndrew Thompson 	/* select PHY operation and start write command */
86802ac6454SAndrew Thompson 	udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
86902ac6454SAndrew Thompson 
87002ac6454SAndrew Thompson 	/* XXX: should we wait? */
87102ac6454SAndrew Thompson 
87202ac6454SAndrew Thompson 	/* end write command */
87302ac6454SAndrew Thompson 	UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
87402ac6454SAndrew Thompson 
87502ac6454SAndrew Thompson 	if (!locked)
87602ac6454SAndrew Thompson 		UDAV_UNLOCK(sc);
87702ac6454SAndrew Thompson 	return (0);
87802ac6454SAndrew Thompson }
87902ac6454SAndrew Thompson 
88002ac6454SAndrew Thompson static void
udav_miibus_statchg(device_t dev)88102ac6454SAndrew Thompson udav_miibus_statchg(device_t dev)
88202ac6454SAndrew Thompson {
88302ac6454SAndrew Thompson 	/* nothing to do */
88402ac6454SAndrew Thompson }
885