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