freebsd-dev/sys/dev/usb/if_udav.c
Ian Dowse 9145721957 Use a different task queue for host controller and peripheral driver
tasks. Since the host controllers rely on tasks to process transfer
timeouts, if a synchronous transfer from a driver was invoked from
a task and timed out, it would never complete because the single
task thread was stuck performing the synchronous transfer so couldn't
process the timeout.

This affected the axe, udav and ural drivers.

Problem hardware provided by:	guido
2006-10-19 01:15:58 +00:00

1979 lines
45 KiB
C

/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */
/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */
/* $FreeBSD$ */
/*-
* Copyright (c) 2003
* Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
* The spec can be found at the following url.
* http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
*/
/*
* TODO:
* Interrupt Endpoint support
* External PHYs
* powerhook() support?
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#if defined(__NetBSD__)
#include "opt_ns.h"
#endif
#if defined(__NetBSD__)
#include "bpfilter.h"
#endif
#if defined(__FreeBSD__)
#define NBPFILTER 1
#endif
#if defined(__NetBSD__)
#include "rnd.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#if defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/lockmgr.h>
#include <sys/sockio.h>
#endif
#if defined(__NetBSD__)
#include <sys/device.h>
#endif
#if defined(NRND) && NRND > 0
#include <sys/rnd.h>
#endif
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net/if_types.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if defined(__NetBSD__)
#ifndef BPF_MTAP
#define BPF_MTAP(_ifp, _m) do { \
if ((_ifp)->if_bpf)) { \
bpf_mtap((_ifp)->if_bpf, (_m)) ; \
} \
} while (0)
#endif
#endif
#if defined(__NetBSD__)
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_inarp.h>
#endif /* INET */
#elif defined(__FreeBSD__) /* defined(__NetBSD__) */
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif /* defined(__FreeBSD__) */
#if defined(__NetBSD__)
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#endif /* defined (__NetBSD__) */
#include <sys/bus.h>
#include <machine/bus.h>
#if __FreeBSD_version < 500000
#include <machine/clock.h>
#endif
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include "usbdevs.h"
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_ethersubr.h>
#include <dev/usb/if_udavreg.h>
#if defined(__FreeBSD__)
MODULE_DEPEND(udav, usb, 1, 1, 1);
MODULE_DEPEND(udav, ether, 1, 1, 1);
MODULE_DEPEND(udav, miibus, 1, 1, 1);
#endif
/* "device miibus" required. See GENERIC if you get errors here. */
#include "miibus_if.h"
#if !defined(__FreeBSD__)
/* Function declarations */
USB_DECLARE_DRIVER(udav);
#endif
#if defined(__FreeBSD__)
static int udav_match(device_t);
static int udav_attach(device_t);
static int udav_detach(device_t);
static void udav_shutdown(device_t);
#endif
static int udav_openpipes(struct udav_softc *);
static void udav_start(struct ifnet *);
static int udav_send(struct udav_softc *, struct mbuf *, int);
static void udav_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
#if defined(__FreeBSD__)
static void udav_rxstart(struct ifnet *ifp);
#endif
static void udav_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
static void udav_tick(void *);
static void udav_tick_task(void *);
static int udav_ioctl(struct ifnet *, u_long, caddr_t);
static void udav_stop_task(struct udav_softc *);
static void udav_stop(struct ifnet *, int);
static void udav_watchdog(struct ifnet *);
static int udav_ifmedia_change(struct ifnet *);
static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
static void udav_lock_mii(struct udav_softc *);
static void udav_unlock_mii(struct udav_softc *);
static int udav_miibus_readreg(device_t, int, int);
static void udav_miibus_writereg(device_t, int, int, int);
static void udav_miibus_statchg(device_t);
#if defined(__NetBSD__)
static int udav_init(struct ifnet *);
#elif defined(__FreeBSD__)
static void udav_init(void *);
#endif
static void udav_setmulti(struct udav_softc *);
static void udav_reset(struct udav_softc *);
static int udav_csr_read(struct udav_softc *, int, void *, int);
static int udav_csr_write(struct udav_softc *, int, void *, int);
static int udav_csr_read1(struct udav_softc *, int);
static int udav_csr_write1(struct udav_softc *, int, unsigned char);
#if 0
static int udav_mem_read(struct udav_softc *, int, void *, int);
static int udav_mem_write(struct udav_softc *, int, void *, int);
static int udav_mem_write1(struct udav_softc *, int, unsigned char);
#endif
#if defined(__FreeBSD__)
static device_method_t udav_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, udav_match),
DEVMETHOD(device_attach, udav_attach),
DEVMETHOD(device_detach, udav_detach),
DEVMETHOD(device_shutdown, udav_shutdown),
/* bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
/* MII interface */
DEVMETHOD(miibus_readreg, udav_miibus_readreg),
DEVMETHOD(miibus_writereg, udav_miibus_writereg),
DEVMETHOD(miibus_statchg, udav_miibus_statchg),
{ 0, 0 }
};
static driver_t udav_driver = {
"udav",
udav_methods,
sizeof(struct udav_softc)
};
static devclass_t udav_devclass;
DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, usbd_driver_load, 0);
DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
#endif /* defined(__FreeBSD__) */
/* Macros */
#ifdef UDAV_DEBUG
#define DPRINTF(x) if (udavdebug) logprintf x
#define DPRINTFN(n,x) if (udavdebug >= (n)) logprintf x
int udavdebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define delay(d) DELAY(d)
#define UDAV_SETBIT(sc, reg, x) \
udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
#define UDAV_CLRBIT(sc, reg, x) \
udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
static const struct udav_type {
struct usb_devno udav_dev;
u_int16_t udav_flags;
#define UDAV_EXT_PHY 0x0001
} udav_devs [] = {
/* Corega USB-TXC */
{{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC }, 0},
#if 0
/* DAVICOM DM9601 Generic? */
/* XXX: The following ids was obtained from the data sheet. */
{{ 0x0a46, 0x9601 }, 0},
#endif
};
#define udav_lookup(v, p) ((const struct udav_type *)usb_lookup(udav_devs, v, p))
/* Probe */
USB_MATCH(udav)
{
USB_MATCH_START(udav, uaa);
if (uaa->iface != NULL)
return (UMATCH_NONE);
return (udav_lookup(uaa->vendor, uaa->product) != NULL ?
UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
}
/* Attach */
USB_ATTACH(udav)
{
USB_ATTACH_START(udav, sc, uaa);
usbd_device_handle dev = uaa->device;
usbd_interface_handle iface;
usbd_status err;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
char devinfo[1024];
const char *devname ;
struct ifnet *ifp;
#if defined(__NetBSD__)
struct mii_data *mii;
#endif
u_char eaddr[ETHER_ADDR_LEN];
int i;
#if defined(__NetBSD__)
int s;
#endif
bzero(sc, sizeof(struct udav_softc));
usbd_devinfo(dev, 0, devinfo);
USB_ATTACH_SETUP;
devname = device_get_nameunit(sc->sc_dev);
printf("%s: %s\n", devname, devinfo);
/* Move the device into the configured state. */
err = usbd_set_config_no(dev, UDAV_CONFIG_NO, 1);
if (err) {
printf("%s: setting config no failed\n", devname);
goto bad;
}
usb_init_task(&sc->sc_tick_task, udav_tick_task, sc);
lockinit(&sc->sc_mii_lock, PZERO, "udavmii", 0, 0);
usb_init_task(&sc->sc_stop_task, (void (*)(void *)) udav_stop_task, sc);
/* get control interface */
err = usbd_device2interface_handle(dev, UDAV_IFACE_INDEX, &iface);
if (err) {
printf("%s: failed to get interface, err=%s\n", devname,
usbd_errstr(err));
goto bad;
}
sc->sc_udev = dev;
sc->sc_ctl_iface = iface;
sc->sc_flags = udav_lookup(uaa->vendor, uaa->product)->udav_flags;
/* get interface descriptor */
id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
/* find endpoints */
sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1;
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
if (ed == NULL) {
printf("%s: couldn't get endpoint %d\n", devname, i);
goto bad;
}
if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */
else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */
else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT &&
UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
sc->sc_intrin_no = ed->bEndpointAddress; /* Status */
}
if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 ||
sc->sc_intrin_no == -1) {
printf("%s: missing endpoint\n", devname);
goto bad;
}
#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
#endif
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
/* reset the adapter */
udav_reset(sc);
/* Get Ethernet Address */
err = udav_csr_read(sc, UDAV_PAR, (void *)eaddr, ETHER_ADDR_LEN);
if (err) {
printf("%s: read MAC address failed\n", devname);
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
mtx_destroy(&sc->sc_mtx);
#endif
goto bad;
}
/* Print Ethernet Address */
printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr));
/* initialize interface infomation */
#if defined(__FreeBSD__)
ifp = GET_IFP(sc) = if_alloc(IFT_ETHER);
if (ifp == NULL) {
printf("%s: can not if_alloc\n", devname);
UDAV_UNLOCK(sc);
mtx_destroy(&sc->sc_mtx);
goto bad;
}
#else
ifp = GET_IFP(sc);
#endif
ifp->if_softc = sc;
ifp->if_mtu = ETHERMTU;
#if defined(__NetBSD__)
strncpy(ifp->if_xname, devname, IFNAMSIZ);
#elif defined(__FreeBSD__)
if_initname(ifp, "udav", device_get_unit(self));
#endif
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
IFF_NEEDSGIANT;
ifp->if_start = udav_start;
ifp->if_ioctl = udav_ioctl;
ifp->if_watchdog = udav_watchdog;
ifp->if_init = udav_init;
#if defined(__NetBSD__)
ifp->if_stop = udav_stop;
#endif
#if defined(__FreeBSD__)
ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
#endif
#if defined(__NetBSD__)
IFQ_SET_READY(&ifp->if_snd);
#endif
#if defined(__NetBSD__)
/*
* Do ifmedia setup.
*/
mii = &sc->sc_mii;
mii->mii_ifp = ifp;
mii->mii_readreg = udav_miibus_readreg;
mii->mii_writereg = udav_miibus_writereg;
mii->mii_statchg = udav_miibus_statchg;
mii->mii_flags = MIIF_AUTOTSLEEP;
ifmedia_init(&mii->mii_media, 0,
udav_ifmedia_change, udav_ifmedia_status);
mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
if (LIST_FIRST(&mii->mii_phys) == NULL) {
ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
} else
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
/* attach the interface */
if_attach(ifp);
Ether_ifattach(ifp, eaddr);
#elif defined(__FreeBSD__)
if (mii_phy_probe(self, &sc->sc_miibus,
udav_ifmedia_change, udav_ifmedia_status)) {
printf("%s: MII without any PHY!\n", device_get_nameunit(sc->sc_dev));
if_free(ifp);
UDAV_UNLOCK(sc);
mtx_destroy(&sc->sc_mtx);
USB_ATTACH_ERROR_RETURN;
}
sc->sc_qdat.ifp = ifp;
sc->sc_qdat.if_rxstart = udav_rxstart;
/*
* Call MI attach routine.
*/
ether_ifattach(ifp, eaddr);
#endif
#if defined(NRND) && NRND > 0
rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0);
#endif
usb_callout_init(sc->sc_stat_ch);
#if defined(__FreeBSD__)
usb_register_netisr();
#endif
sc->sc_attached = 1;
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev));
USB_ATTACH_SUCCESS_RETURN;
bad:
sc->sc_dying = 1;
USB_ATTACH_ERROR_RETURN;
}
/* detach */
USB_DETACH(udav)
{
USB_DETACH_START(udav, sc);
struct ifnet *ifp = GET_IFP(sc);
#if defined(__NetBSD__)
int s;
#endif
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
/* Detached before attached finished */
if (!sc->sc_attached)
return (0);
UDAV_LOCK(sc);
usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
/* Remove any pending tasks */
usb_rem_task(sc->sc_udev, &sc->sc_tick_task);
usb_rem_task(sc->sc_udev, &sc->sc_stop_task);
#if defined(__NetBSD__)
s = splusb();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
if (--sc->sc_refcnt >= 0) {
/* Wait for processes to go away */
usb_detach_wait(USBDEV(sc->sc_dev));
}
#if defined(__FreeBSD__)
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
#else
if (ifp->if_flags & IFF_RUNNING)
#endif
udav_stop(GET_IFP(sc), 1);
#if defined(NRND) && NRND > 0
rnd_detach_source(&sc->rnd_source);
#endif
#if defined(__NetBSD__)
mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
#endif
ether_ifdetach(ifp);
#if defined(__NetBSD__)
if_detach(ifp);
#endif
#if defined(__FreeBSD__)
if_free(ifp);
#endif
#ifdef DIAGNOSTIC
if (sc->sc_pipe_tx != NULL)
printf("%s: detach has active tx endpoint.\n",
device_get_nameunit(sc->sc_dev));
if (sc->sc_pipe_rx != NULL)
printf("%s: detach has active rx endpoint.\n",
device_get_nameunit(sc->sc_dev));
if (sc->sc_pipe_intr != NULL)
printf("%s: detach has active intr endpoint.\n",
device_get_nameunit(sc->sc_dev));
#endif
sc->sc_attached = 0;
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
#if defined(__FreeBSD__)
mtx_destroy(&sc->sc_mtx);
#endif
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
USBDEV(sc->sc_dev));
return (0);
}
#if 0
/* read memory */
static int
udav_mem_read(struct udav_softc *sc, int offset, void *buf, int len)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xffff;
len &= 0xff;
req.bmRequestType = UT_READ_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_MEM_READ;
USETW(req.wValue, 0x0000);
USETW(req.wIndex, offset);
USETW(req.wLength, len);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, buf);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
/* write memory */
static int
udav_mem_write(struct udav_softc *sc, int offset, void *buf, int len)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xffff;
len &= 0xff;
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_MEM_WRITE;
USETW(req.wValue, 0x0000);
USETW(req.wIndex, offset);
USETW(req.wLength, len);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, buf);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
/* write memory */
static int
udav_mem_write1(struct udav_softc *sc, int offset, unsigned char ch)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xffff;
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_MEM_WRITE1;
USETW(req.wValue, ch);
USETW(req.wIndex, offset);
USETW(req.wLength, 0x0000);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, NULL);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
#endif
/* read register(s) */
static int
udav_csr_read(struct udav_softc *sc, int offset, void *buf, int len)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xff;
len &= 0xff;
req.bmRequestType = UT_READ_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_REG_READ;
USETW(req.wValue, 0x0000);
USETW(req.wIndex, offset);
USETW(req.wLength, len);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, buf);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
/* write register(s) */
static int
udav_csr_write(struct udav_softc *sc, int offset, void *buf, int len)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xff;
len &= 0xff;
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_REG_WRITE;
USETW(req.wValue, 0x0000);
USETW(req.wIndex, offset);
USETW(req.wLength, len);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, buf);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
static int
udav_csr_read1(struct udav_softc *sc, int offset)
{
u_int8_t val = 0;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
return (udav_csr_read(sc, offset, &val, 1) ? 0 : val);
}
/* write a register */
static int
udav_csr_write1(struct udav_softc *sc, int offset, unsigned char ch)
{
usb_device_request_t req;
usbd_status err;
if (sc == NULL)
return (0);
DPRINTFN(0x200,
("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
offset &= 0xff;
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UDAV_REQ_REG_WRITE1;
USETW(req.wValue, ch);
USETW(req.wIndex, offset);
USETW(req.wLength, 0x0000);
sc->sc_refcnt++;
err = usbd_do_request(sc->sc_udev, &req, NULL);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err) {
DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
device_get_nameunit(sc->sc_dev), __func__, offset, err));
}
return (err);
}
#if defined(__NetBSD__)
static int
udav_init(struct ifnet *ifp)
#elif defined(__FreeBSD__)
static void
udav_init(void *xsc)
#endif
{
#if defined(__NetBSD__)
struct udav_softc *sc = ifp->if_softc;
#elif defined(__FreeBSD__)
struct udav_softc *sc = (struct udav_softc *)xsc;
struct ifnet *ifp = GET_IFP(sc);
#endif
struct mii_data *mii = GET_MII(sc);
u_char *eaddr;
#if defined(__NetBSD__)
int s;
#endif
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
#if defined(__NetBSD__)
return (EIO);
#elif defined(__FreeBSD__)
return ;
#endif
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
/* Cancel pending I/O and free all TX/RX buffers */
udav_stop(ifp, 1);
#if defined(__NetBSD__)
eaddr = LLADDR(ifp->if_sadl);
#elif defined(__FreeBSD__)
eaddr = IF_LLADDR(ifp);
#endif
udav_csr_write(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
/* Initialize network control register */
/* Disable loopback */
UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
/* Initialize RX control register */
UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
/* If we want promiscuous mode, accept all physical frames. */
if (ifp->if_flags & IFF_PROMISC)
UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
else
UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
/* Initialize transmit ring */
if (usb_ether_tx_list_init(sc, &sc->sc_cdata,
sc->sc_udev) == ENOBUFS) {
printf("%s: tx list init failed\n", device_get_nameunit(sc->sc_dev));
#if defined(__NetBSD__)
splx(s);
return (EIO);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
return ;
#endif
}
/* Initialize receive ring */
if (usb_ether_rx_list_init(sc, &sc->sc_cdata,
sc->sc_udev) == ENOBUFS) {
printf("%s: rx list init failed\n", device_get_nameunit(sc->sc_dev));
#if defined(__NetBSD__)
splx(s);
return (EIO);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
return ;
#endif
}
/* Load the multicast filter */
udav_setmulti(sc);
/* Enable RX */
UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
/* clear POWER_DOWN state of internal PHY */
UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
mii_mediachg(mii);
if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) {
if (udav_openpipes(sc)) {
#if defined(__NetBSD__)
splx(s);
return (EIO);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
return ;
#endif
}
}
#if defined(__FreeBSD__)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
#else
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
#endif
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
#if defined(__NetBSD__)
return (0);
#elif defined(__FreeBSD__)
return ;
#endif
}
static void
udav_reset(struct udav_softc *sc)
{
int i;
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return;
/* Select PHY */
#if 1
/*
* XXX: force select internal phy.
* external phy routines are not tested.
*/
UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
#else
if (sc->sc_flags & UDAV_EXT_PHY) {
UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
} else {
UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
}
#endif
UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
break;
delay(10); /* XXX */
}
delay(10000); /* XXX */
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
udav_activate(device_t self, enum devact act)
{
struct udav_softc *sc = (struct udav_softc *)self;
DPRINTF(("%s: %s: enter, act=%d\n", device_get_nameunit(sc->sc_dev),
__func__, act));
switch (act) {
case DVACT_ACTIVATE:
return (EOPNOTSUPP);
break;
case DVACT_DEACTIVATE:
if_deactivate(&sc->sc_ec.ec_if);
sc->sc_dying = 1;
break;
}
return (0);
}
#endif
#define UDAV_BITS 6
#define UDAV_CALCHASH(addr) \
(ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1))
static void
udav_setmulti(struct udav_softc *sc)
{
struct ifnet *ifp;
#if defined(__NetBSD__)
struct ether_multi *enm;
struct ether_multistep step;
#elif defined(__FreeBSD__)
struct ifmultiaddr *ifma;
#endif
u_int8_t hashes[8];
int h = 0;
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return;
ifp = GET_IFP(sc);
if (ifp->if_flags & IFF_PROMISC) {
UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
return;
} else if (ifp->if_flags & IFF_ALLMULTI) {
#if defined(__NetBSD__)
allmulti:
#endif
ifp->if_flags |= IFF_ALLMULTI;
UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_PRMSC);
return;
}
/* first, zot all the existing hash bits */
memset(hashes, 0x00, sizeof(hashes));
hashes[7] |= 0x80; /* broadcast address */
udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
/* now program new ones */
#if defined(__NetBSD__)
ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
ETHER_ADDR_LEN) != 0)
goto allmulti;
h = UDAV_CALCHASH(enm->enm_addrlo);
hashes[h>>3] |= 1 << (h & 0x7);
ETHER_NEXT_MULTI(step, enm);
}
#elif defined(__FreeBSD__)
IF_ADDR_LOCK(ifp);
#if __FreeBSD_version >= 500000
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
#else
LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
#endif
{
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
h = UDAV_CALCHASH(LLADDR((struct sockaddr_dl *)
ifma->ifma_addr));
hashes[h>>3] |= 1 << (h & 0x7);
}
IF_ADDR_UNLOCK(ifp);
#endif
/* disable all multicast */
ifp->if_flags &= ~IFF_ALLMULTI;
UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
/* write hash value to the register */
udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
}
static int
udav_openpipes(struct udav_softc *sc)
{
struct ue_chain *c;
usbd_status err;
int i;
int error = 0;
if (sc->sc_dying)
return (EIO);
sc->sc_refcnt++;
/* Open RX pipe */
err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no,
USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx);
if (err) {
printf("%s: open rx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
error = EIO;
goto done;
}
/* Open TX pipe */
err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no,
USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx);
if (err) {
printf("%s: open tx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
error = EIO;
goto done;
}
#if 0
/* XXX: interrupt endpoint is not yet supported */
/* Open Interrupt pipe */
err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no,
USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc,
&sc->sc_cdata.ue_ibuf, UDAV_INTR_PKGLEN,
udav_intr, UDAV_INTR_INTERVAL);
if (err) {
printf("%s: open intr pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
error = EIO;
goto done;
}
#endif
/* Start up the receive pipe. */
for (i = 0; i < UE_RX_LIST_CNT; i++) {
c = &sc->sc_cdata.ue_rx_chain[i];
usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx,
c, c->ue_buf, UE_BUFSZ,
USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, udav_rxeof);
(void)usbd_transfer(c->ue_xfer);
DPRINTF(("%s: %s: start read\n", device_get_nameunit(sc->sc_dev),
__func__));
}
done:
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
return (error);
}
static void
udav_start(struct ifnet *ifp)
{
struct udav_softc *sc = ifp->if_softc;
struct mbuf *m_head = NULL;
DPRINTF(("%s: %s: enter, link=%d\n", device_get_nameunit(sc->sc_dev),
__func__, sc->sc_link));
if (sc->sc_dying)
return;
if (!sc->sc_link)
return;
#if defined(__FreeBSD__)
if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
#else
if (ifp->if_flags & IFF_OACTIVE)
#endif
return;
#if defined(__NetBSD__)
IFQ_POLL(&ifp->if_snd, m_head);
#elif defined(__FreeBSD__)
IF_DEQUEUE(&ifp->if_snd, m_head);
#endif
if (m_head == NULL)
return;
if (udav_send(sc, m_head, 0)) {
#if defined(__FreeBSD__)
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
#else
ifp->if_flags |= IFF_OACTIVE;
#endif
return;
}
#if defined(__NetBSD__)
IFQ_DEQUEUE(&ifp->if_snd, m_head);
#endif
#if NBPFILTER > 0
BPF_MTAP(ifp, m_head);
#endif
#if defined(__FreeBSD__)
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
#else
ifp->if_flags |= IFF_OACTIVE;
#endif
/* Set a timeout in case the chip goes out to lunch. */
ifp->if_timer = 5;
}
static int
udav_send(struct udav_softc *sc, struct mbuf *m, int idx)
{
int total_len;
struct ue_chain *c;
usbd_status err;
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__));
c = &sc->sc_cdata.ue_tx_chain[idx];
/* Copy the mbuf data into a contiguous buffer */
/* first 2 bytes are packet length */
m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2);
c->ue_mbuf = m;
total_len = m->m_pkthdr.len;
if (total_len < UDAV_MIN_FRAME_LEN) {
memset(c->ue_buf + 2 + total_len, 0,
UDAV_MIN_FRAME_LEN - total_len);
total_len = UDAV_MIN_FRAME_LEN;
}
/* Frame length is specified in the first 2bytes of the buffer */
c->ue_buf[0] = (u_int8_t)total_len;
c->ue_buf[1] = (u_int8_t)(total_len >> 8);
total_len += 2;
usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_tx, c, c->ue_buf, total_len,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
UDAV_TX_TIMEOUT, udav_txeof);
/* Transmit */
sc->sc_refcnt++;
err = usbd_transfer(c->ue_xfer);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
if (err != USBD_IN_PROGRESS) {
printf("%s: udav_send error=%s\n", device_get_nameunit(sc->sc_dev),
usbd_errstr(err));
/* Stop the interface */
usb_add_task(sc->sc_udev, &sc->sc_stop_task, USB_TASKQ_DRIVER);
return (EIO);
}
DPRINTF(("%s: %s: send %d bytes\n", device_get_nameunit(sc->sc_dev),
__func__, total_len));
sc->sc_cdata.ue_tx_cnt++;
return (0);
}
static void
udav_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ue_chain *c = priv;
struct udav_softc *sc = c->ue_sc;
struct ifnet *ifp = GET_IFP(sc);
#if defined(__NetBSD__)
int s;
#endif
if (sc->sc_dying)
return;
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
ifp->if_timer = 0;
#if defined(__FreeBSD__)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
#else
ifp->if_flags &= ~IFF_OACTIVE;
#endif
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
return;
}
ifp->if_oerrors++;
printf("%s: usb error on tx: %s\n", device_get_nameunit(sc->sc_dev),
usbd_errstr(status));
if (status == USBD_STALLED) {
sc->sc_refcnt++;
usbd_clear_endpoint_stall(sc->sc_pipe_tx);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
}
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
return;
}
ifp->if_opackets++;
m_freem(c->ue_mbuf);
c->ue_mbuf = NULL;
#if defined(__NetBSD__)
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
#elif defined(__FreeBSD__)
if ( ifp->if_snd.ifq_head != NULL )
#endif
udav_start(ifp);
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
}
static void
udav_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ue_chain *c = priv;
struct udav_softc *sc = c->ue_sc;
struct ifnet *ifp = GET_IFP(sc);
struct mbuf *m;
u_int32_t total_len;
u_int8_t *pktstat;
#if defined(__NetBSD__)
int s;
#endif
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__));
if (sc->sc_dying)
return;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
sc->sc_rx_errs++;
if (usbd_ratecheck(&sc->sc_rx_notice)) {
printf("%s: %u usb errors on rx: %s\n",
device_get_nameunit(sc->sc_dev), sc->sc_rx_errs,
usbd_errstr(status));
sc->sc_rx_errs = 0;
}
if (status == USBD_STALLED) {
sc->sc_refcnt++;
usbd_clear_endpoint_stall(sc->sc_pipe_rx);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
}
goto done;
}
usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
/* copy data to mbuf */
m = c->ue_mbuf;
memcpy(mtod(m, char *), c->ue_buf, total_len);
/* first byte in received data */
pktstat = mtod(m, u_int8_t *);
m_adj(m, sizeof(u_int8_t));
DPRINTF(("%s: RX Status: 0x%02x\n", device_get_nameunit(sc->sc_dev), *pktstat));
total_len = UGETW(mtod(m, u_int8_t *));
m_adj(m, sizeof(u_int16_t));
if (*pktstat & UDAV_RSR_LCS) {
ifp->if_collisions++;
goto done;
}
if (total_len < sizeof(struct ether_header) ||
*pktstat & UDAV_RSR_ERR) {
ifp->if_ierrors++;
goto done;
}
ifp->if_ipackets++;
total_len -= ETHER_CRC_LEN;
m->m_pkthdr.len = m->m_len = total_len;
#if defined(__NetBSD__)
m->m_pkthdr.rcvif = ifp;
#elif defined(__FreeBSD__)
m->m_pkthdr.rcvif = (struct ifnet *)&sc->sc_qdat;
#endif
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
#if defined(__NetBSD__)
c->ue_mbuf = usb_ether_newbuf();
if (c->ue_mbuf == NULL) {
printf("%s: no memory for rx list "
"-- packet dropped!\n", device_get_nameunit(sc->sc_dev));
ifp->if_ierrors++;
goto done1;
}
#endif
#if NBPFILTER > 0
BPF_MTAP(ifp, m);
#endif
DPRINTF(("%s: %s: deliver %d\n", device_get_nameunit(sc->sc_dev),
__func__, m->m_len));
#if defined(__NetBSD__)
IF_INPUT(ifp, m);
#endif
#if defined(__FreeBSD__)
usb_ether_input(m);
UDAV_UNLOCK(sc);
return ;
#endif
#if defined(__NetBSD__)
done1:
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
done:
/* Setup new transfer */
usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->ue_buf, UE_BUFSZ,
USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, udav_rxeof);
sc->sc_refcnt++;
usbd_transfer(xfer);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
DPRINTF(("%s: %s: start rx\n", device_get_nameunit(sc->sc_dev), __func__));
}
#if 0
static void udav_intr()
{
}
#endif
static int
udav_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct udav_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct mii_data *mii;
#if defined(__NetBSD__)
int s;
#endif
int error = 0;
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (EIO);
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
switch (cmd) {
#if defined(__FreeBSD__)
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
ifp->if_flags & IFF_PROMISC) {
UDAV_SETBIT(sc, UDAV_RCR,
UDAV_RCR_ALL|UDAV_RCR_PRMSC);
} else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
!(ifp->if_flags & IFF_PROMISC)) {
if (ifp->if_flags & IFF_ALLMULTI)
UDAV_CLRBIT(sc, UDAV_RCR,
UDAV_RCR_PRMSC);
else
UDAV_CLRBIT(sc, UDAV_RCR,
UDAV_RCR_ALL|UDAV_RCR_PRMSC);
} else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
udav_init(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
udav_stop(ifp, 1);
}
error = 0;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
udav_setmulti(sc);
error = 0;
break;
#endif
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
mii = GET_MII(sc);
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
break;
default:
error = ether_ioctl(ifp, cmd, data);
#if defined(__NetBSD__)
if (error == ENETRESET) {
udav_setmulti(sc);
error = 0;
}
#endif
break;
}
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
return (error);
}
static void
udav_watchdog(struct ifnet *ifp)
{
struct udav_softc *sc = ifp->if_softc;
struct ue_chain *c;
usbd_status stat;
#if defined(__NetBSD__)
int s;
#endif
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
ifp->if_oerrors++;
printf("%s: watchdog timeout\n", device_get_nameunit(sc->sc_dev));
#if defined(__NetBSD__)
s = splusb();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc)
#endif
c = &sc->sc_cdata.ue_tx_chain[0];
usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
udav_txeof(c->ue_xfer, c, stat);
#if defined(__NetBSD__)
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
#elif defined(__FreeBSD__)
if ( ifp->if_snd.ifq_head != NULL )
#endif
udav_start(ifp);
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
}
static void
udav_stop_task(struct udav_softc *sc)
{
udav_stop(GET_IFP(sc), 1);
}
/* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
static void
udav_stop(struct ifnet *ifp, int disable)
{
struct udav_softc *sc = ifp->if_softc;
usbd_status err;
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
ifp->if_timer = 0;
udav_reset(sc);
usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
/* Stop transfers */
/* RX endpoint */
if (sc->sc_pipe_rx != NULL) {
err = usbd_abort_pipe(sc->sc_pipe_rx);
if (err)
printf("%s: abort rx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
err = usbd_close_pipe(sc->sc_pipe_rx);
if (err)
printf("%s: close rx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
sc->sc_pipe_rx = NULL;
}
/* TX endpoint */
if (sc->sc_pipe_tx != NULL) {
err = usbd_abort_pipe(sc->sc_pipe_tx);
if (err)
printf("%s: abort tx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
err = usbd_close_pipe(sc->sc_pipe_tx);
if (err)
printf("%s: close tx pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
sc->sc_pipe_tx = NULL;
}
#if 0
/* XXX: Interrupt endpoint is not yet supported!! */
/* Interrupt endpoint */
if (sc->sc_pipe_intr != NULL) {
err = usbd_abort_pipe(sc->sc_pipe_intr);
if (err)
printf("%s: abort intr pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
err = usbd_close_pipe(sc->sc_pipe_intr);
if (err)
printf("%s: close intr pipe failed: %s\n",
device_get_nameunit(sc->sc_dev), usbd_errstr(err));
sc->sc_pipe_intr = NULL;
}
#endif
/* Free RX resources. */
usb_ether_rx_list_free(&sc->sc_cdata);
/* Free TX resources. */
usb_ether_tx_list_free(&sc->sc_cdata);
sc->sc_link = 0;
#if defined(__FreeBSD__)
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
#else
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
#endif
}
/* Set media options */
static int
udav_ifmedia_change(struct ifnet *ifp)
{
struct udav_softc *sc = ifp->if_softc;
struct mii_data *mii = GET_MII(sc);
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return (0);
sc->sc_link = 0;
if (mii->mii_instance) {
struct mii_softc *miisc;
for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
miisc = LIST_NEXT(miisc, mii_list))
mii_phy_reset(miisc);
}
return (mii_mediachg(mii));
}
/* Report current media status. */
static void
udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct udav_softc *sc = ifp->if_softc;
struct mii_data *mii = GET_MII(sc);
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
if (sc->sc_dying)
return;
#if defined(__FreeBSD__)
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
#else
if ((ifp->if_flags & IFF_RUNNING) == 0) {
#endif
ifmr->ifm_active = IFM_ETHER | IFM_NONE;
ifmr->ifm_status = 0;
return;
}
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
}
static void
udav_tick(void *xsc)
{
struct udav_softc *sc = xsc;
if (sc == NULL)
return;
DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
__func__));
if (sc->sc_dying)
return;
/* Perform periodic stuff in process context */
usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER);
}
static void
udav_tick_task(void *xsc)
{
struct udav_softc *sc = xsc;
struct ifnet *ifp;
struct mii_data *mii;
#if defined(__NetBSD__)
int s;
#endif
if (sc == NULL)
return;
DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
__func__));
if (sc->sc_dying)
return;
ifp = GET_IFP(sc);
mii = GET_MII(sc);
if (mii == NULL)
return;
#if defined(__NetBSD__)
s = splnet();
#elif defined(__FreeBSD__)
UDAV_LOCK(sc);
#endif
mii_tick(mii);
if (!sc->sc_link) {
mii_pollstat(mii);
if (mii->mii_media_status & IFM_ACTIVE &&
IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
DPRINTF(("%s: %s: got link\n",
device_get_nameunit(sc->sc_dev), __func__));
sc->sc_link++;
#if defined(__NetBSD__)
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
#elif defined(__FreeBSD__)
if ( ifp->if_snd.ifq_head != NULL )
#endif
udav_start(ifp);
}
}
usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
#if defined(__NetBSD__)
splx(s);
#elif defined(__FreeBSD__)
UDAV_UNLOCK(sc);
#endif
}
/* Get exclusive access to the MII registers */
static void
udav_lock_mii(struct udav_softc *sc)
{
DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
__func__));
sc->sc_refcnt++;
#if defined(__NetBSD__)
lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL);
#elif defined(__FreeBSD__)
lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL, NULL);
#endif
}
static void
udav_unlock_mii(struct udav_softc *sc)
{
DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
__func__));
#if defined(__NetBSD__)
lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL);
#elif defined(__FreeBSD__)
lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL, NULL);
#endif
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
}
static int
udav_miibus_readreg(device_t dev, int phy, int reg)
{
struct udav_softc *sc;
u_int8_t val[2];
u_int16_t data16;
if (dev == NULL)
return (0);
sc = USBGETSOFTC(dev);
DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n",
device_get_nameunit(sc->sc_dev), __func__, phy, reg));
if (sc->sc_dying) {
#ifdef DIAGNOSTIC
printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev),
__func__);
#endif
return (0);
}
/* XXX: one PHY only for the internal PHY */
if (phy != 0) {
DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
device_get_nameunit(sc->sc_dev), __func__, phy));
return (0);
}
udav_lock_mii(sc);
/* select internal PHY and set PHY register address */
udav_csr_write1(sc, UDAV_EPAR,
UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
/* select PHY operation and start read command */
udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
/* XXX: should be wait? */
/* end read command */
UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
/* retrieve the result from data registers */
udav_csr_read(sc, UDAV_EPDRL, val, 2);
udav_unlock_mii(sc);
data16 = val[0] | (val[1] << 8);
DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n",
device_get_nameunit(sc->sc_dev), __func__, phy, reg, data16));
return (data16);
}
static void
udav_miibus_writereg(device_t dev, int phy, int reg, int data)
{
struct udav_softc *sc;
u_int8_t val[2];
if (dev == NULL)
return;
sc = USBGETSOFTC(dev);
DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n",
device_get_nameunit(sc->sc_dev), __func__, phy, reg, data));
if (sc->sc_dying) {
#ifdef DIAGNOSTIC
printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev),
__func__);
#endif
return;
}
/* XXX: one PHY only for the internal PHY */
if (phy != 0) {
DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
device_get_nameunit(sc->sc_dev), __func__, phy));
return;
}
udav_lock_mii(sc);
/* select internal PHY and set PHY register address */
udav_csr_write1(sc, UDAV_EPAR,
UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
/* put the value to the data registers */
val[0] = data & 0xff;
val[1] = (data >> 8) & 0xff;
udav_csr_write(sc, UDAV_EPDRL, val, 2);
/* select PHY operation and start write command */
udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
/* XXX: should be wait? */
/* end write command */
UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
udav_unlock_mii(sc);
return;
}
static void
udav_miibus_statchg(device_t dev)
{
#ifdef UDAV_DEBUG
struct udav_softc *sc;
if (dev == NULL)
return;
sc = USBGETSOFTC(dev);
DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
#endif
/* Nothing to do */
}
#if defined(__FreeBSD__)
/*
* Stop all chip I/O so that the kernel's probe routines don't
* get confused by errant DMAs when rebooting.
*/
static void
udav_shutdown(device_t dev)
{
struct udav_softc *sc;
sc = device_get_softc(dev);
udav_stop_task(sc);
return;
}
static void
udav_rxstart(struct ifnet *ifp)
{
struct udav_softc *sc;
struct ue_chain *c;
sc = ifp->if_softc;
UDAV_LOCK(sc);
c = &sc->sc_cdata.ue_rx_chain[sc->sc_cdata.ue_rx_prod];
c->ue_mbuf = usb_ether_newbuf();
if (c->ue_mbuf == NULL) {
printf("%s: no memory for rx list "
"-- packet dropped!\n", device_get_nameunit(sc->sc_dev));
ifp->if_ierrors++;
UDAV_UNLOCK(sc);
return;
}
/* Setup new transfer. */
usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx,
c, c->ue_buf, UE_BUFSZ,
USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, udav_rxeof);
usbd_transfer(c->ue_xfer);
UDAV_UNLOCK(sc);
return;
}
#endif