8258b792b7
matching (and handling) a whole device and those which match an interface only. This will allow to enforce some rules, eg that the former don't use interface information for matching or that the latter don't modify global device state. The previous way left too much freedom do the drivers which led to inconsistencies and abuse. For now, I've not changed locators and submatch rules, this will happen later. There should not be any change in behaviour, except in the case of some drivers which did behave inconsistently: if_atu, if_axe, uep: matched the configured device in the interface stage, but did configuration again. I've converted them to match in the device stage. ustir, utoppy: matched in the interface stage, but only against vendor/device information, and used any configuration/interface without checking. Changed to match in device stage, and added some simple code to configure and use the first interface. If you have one of those devices, please test!
1615 lines
37 KiB
C
1615 lines
37 KiB
C
/* $NetBSD: if_url.c,v 1.26 2007/03/13 13:51:54 drochner Exp $ */
|
|
/*
|
|
* Copyright (c) 2001, 2002
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* The RTL8150L(Realtek USB to fast ethernet controller) spec can be found at
|
|
* ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/8150v14.pdf
|
|
* ftp://152.104.125.40/lancard/data_sheet/8150/8150v14.pdf
|
|
*/
|
|
|
|
/*
|
|
* TODO:
|
|
* Interrupt Endpoint support
|
|
* External PHYs
|
|
* powerhook() support?
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: if_url.c,v 1.26 2007/03/13 13:51:54 drochner Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
#include "bpfilter.h"
|
|
#include "rnd.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/device.h>
|
|
#if 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>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
#define BPF_MTAP(ifp, m) bpf_mtap((ifp)->if_bpf, (m))
|
|
|
|
#include <net/if_ether.h>
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#include <dev/mii/mii.h>
|
|
#include <dev/mii/miivar.h>
|
|
#include <dev/mii/urlphyreg.h>
|
|
|
|
#include <dev/usb/usb.h>
|
|
#include <dev/usb/usbdi.h>
|
|
#include <dev/usb/usbdi_util.h>
|
|
#include <dev/usb/usbdevs.h>
|
|
|
|
#include <dev/usb/if_urlreg.h>
|
|
|
|
|
|
/* Function declarations */
|
|
USB_DECLARE_DRIVER(url);
|
|
|
|
Static int url_openpipes(struct url_softc *);
|
|
Static int url_rx_list_init(struct url_softc *);
|
|
Static int url_tx_list_init(struct url_softc *);
|
|
Static int url_newbuf(struct url_softc *, struct url_chain *, struct mbuf *);
|
|
Static void url_start(struct ifnet *);
|
|
Static int url_send(struct url_softc *, struct mbuf *, int);
|
|
Static void url_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
|
|
Static void url_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
|
|
Static void url_tick(void *);
|
|
Static void url_tick_task(void *);
|
|
Static int url_ioctl(struct ifnet *, u_long, void *);
|
|
Static void url_stop_task(struct url_softc *);
|
|
Static void url_stop(struct ifnet *, int);
|
|
Static void url_watchdog(struct ifnet *);
|
|
Static int url_ifmedia_change(struct ifnet *);
|
|
Static void url_ifmedia_status(struct ifnet *, struct ifmediareq *);
|
|
Static void url_lock_mii(struct url_softc *);
|
|
Static void url_unlock_mii(struct url_softc *);
|
|
Static int url_int_miibus_readreg(device_ptr_t, int, int);
|
|
Static void url_int_miibus_writereg(device_ptr_t, int, int, int);
|
|
Static void url_miibus_statchg(device_ptr_t);
|
|
Static int url_init(struct ifnet *);
|
|
Static void url_setmulti(struct url_softc *);
|
|
Static void url_reset(struct url_softc *);
|
|
|
|
Static int url_csr_read_1(struct url_softc *, int);
|
|
Static int url_csr_read_2(struct url_softc *, int);
|
|
Static int url_csr_write_1(struct url_softc *, int, int);
|
|
Static int url_csr_write_2(struct url_softc *, int, int);
|
|
Static int url_csr_write_4(struct url_softc *, int, int);
|
|
Static int url_mem(struct url_softc *, int, int, void *, int);
|
|
|
|
/* Macros */
|
|
#ifdef URL_DEBUG
|
|
#define DPRINTF(x) if (urldebug) logprintf x
|
|
#define DPRINTFN(n,x) if (urldebug >= (n)) logprintf x
|
|
int urldebug = 0;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#define DPRINTFN(n,x)
|
|
#endif
|
|
|
|
#define URL_SETBIT(sc, reg, x) \
|
|
url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) | (x))
|
|
|
|
#define URL_SETBIT2(sc, reg, x) \
|
|
url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) | (x))
|
|
|
|
#define URL_CLRBIT(sc, reg, x) \
|
|
url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) & ~(x))
|
|
|
|
#define URL_CLRBIT2(sc, reg, x) \
|
|
url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) & ~(x))
|
|
|
|
static const struct url_type {
|
|
struct usb_devno url_dev;
|
|
u_int16_t url_flags;
|
|
#define URL_EXT_PHY 0x0001
|
|
} url_devs [] = {
|
|
/* MELCO LUA-KTX */
|
|
{{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX }, 0},
|
|
/* Realtek RTL8150L Generic (GREEN HOUSE USBKR100) */
|
|
{{ USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_RTL8150L}, 0},
|
|
/* Longshine LCS-8138TX */
|
|
{{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_LCS8138TX}, 0},
|
|
/* Micronet SP128AR */
|
|
{{ USB_VENDOR_MICRONET, USB_PRODUCT_MICRONET_SP128AR}, 0},
|
|
/* OQO model 01 */
|
|
{{ USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01}, 0},
|
|
};
|
|
#define url_lookup(v, p) ((const struct url_type *)usb_lookup(url_devs, v, p))
|
|
|
|
|
|
/* Probe */
|
|
USB_MATCH(url)
|
|
{
|
|
USB_MATCH_START(url, uaa);
|
|
|
|
return (url_lookup(uaa->vendor, uaa->product) != NULL ?
|
|
UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
|
|
}
|
|
/* Attach */
|
|
USB_ATTACH(url)
|
|
{
|
|
USB_ATTACH_START(url, 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 *devinfop;
|
|
char *devname = USBDEVNAME(sc->sc_dev);
|
|
struct ifnet *ifp;
|
|
struct mii_data *mii;
|
|
u_char eaddr[ETHER_ADDR_LEN];
|
|
int i, s;
|
|
|
|
devinfop = usbd_devinfo_alloc(dev, 0);
|
|
USB_ATTACH_SETUP;
|
|
printf("%s: %s\n", devname, devinfop);
|
|
usbd_devinfo_free(devinfop);
|
|
|
|
/* Move the device into the configured state. */
|
|
err = usbd_set_config_no(dev, URL_CONFIG_NO, 1);
|
|
if (err) {
|
|
printf("%s: setting config no failed\n", devname);
|
|
goto bad;
|
|
}
|
|
|
|
usb_init_task(&sc->sc_tick_task, url_tick_task, sc);
|
|
lockinit(&sc->sc_mii_lock, PZERO, "urlmii", 0, 0);
|
|
usb_init_task(&sc->sc_stop_task, (void (*)(void *)) url_stop_task, sc);
|
|
|
|
/* get control interface */
|
|
err = usbd_device2interface_handle(dev, URL_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 = url_lookup(uaa->vendor, uaa->product)->url_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;
|
|
}
|
|
|
|
s = splnet();
|
|
|
|
/* reset the adapter */
|
|
url_reset(sc);
|
|
|
|
/* Get Ethernet Address */
|
|
err = url_mem(sc, URL_CMD_READMEM, URL_IDR0, (void *)eaddr,
|
|
ETHER_ADDR_LEN);
|
|
if (err) {
|
|
printf("%s: read MAC address failed\n", devname);
|
|
splx(s);
|
|
goto bad;
|
|
}
|
|
|
|
/* Print Ethernet Address */
|
|
printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr));
|
|
|
|
/* initialize interface information */
|
|
ifp = GET_IFP(sc);
|
|
ifp->if_softc = sc;
|
|
ifp->if_mtu = ETHERMTU;
|
|
strncpy(ifp->if_xname, devname, IFNAMSIZ);
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_start = url_start;
|
|
ifp->if_ioctl = url_ioctl;
|
|
ifp->if_watchdog = url_watchdog;
|
|
ifp->if_init = url_init;
|
|
ifp->if_stop = url_stop;
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
/*
|
|
* Do ifmedia setup.
|
|
*/
|
|
mii = &sc->sc_mii;
|
|
mii->mii_ifp = ifp;
|
|
mii->mii_readreg = url_int_miibus_readreg;
|
|
mii->mii_writereg = url_int_miibus_writereg;
|
|
#if 0
|
|
if (sc->sc_flags & URL_EXT_PHY) {
|
|
mii->mii_readreg = url_ext_miibus_readreg;
|
|
mii->mii_writereg = url_ext_miibus_writereg;
|
|
}
|
|
#endif
|
|
mii->mii_statchg = url_miibus_statchg;
|
|
mii->mii_flags = MIIF_AUTOTSLEEP;
|
|
ifmedia_init(&mii->mii_media, 0,
|
|
url_ifmedia_change, url_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);
|
|
|
|
#if NRND > 0
|
|
rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0);
|
|
#endif
|
|
|
|
usb_callout_init(sc->sc_stat_ch);
|
|
sc->sc_attached = 1;
|
|
splx(s);
|
|
|
|
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(url)
|
|
{
|
|
USB_DETACH_START(url, sc);
|
|
struct ifnet *ifp = GET_IFP(sc);
|
|
int s;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
/* Detached before attached finished */
|
|
if (!sc->sc_attached)
|
|
return (0);
|
|
|
|
usb_uncallout(sc->sc_stat_ch, url_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);
|
|
|
|
s = splusb();
|
|
|
|
if (--sc->sc_refcnt >= 0) {
|
|
/* Wait for processes to go away */
|
|
usb_detach_wait(USBDEV(sc->sc_dev));
|
|
}
|
|
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
url_stop(GET_IFP(sc), 1);
|
|
|
|
#if NRND > 0
|
|
rnd_detach_source(&sc->rnd_source);
|
|
#endif
|
|
mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
|
|
ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
|
|
ether_ifdetach(ifp);
|
|
if_detach(ifp);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (sc->sc_pipe_tx != NULL)
|
|
printf("%s: detach has active tx endpoint.\n",
|
|
USBDEVNAME(sc->sc_dev));
|
|
if (sc->sc_pipe_rx != NULL)
|
|
printf("%s: detach has active rx endpoint.\n",
|
|
USBDEVNAME(sc->sc_dev));
|
|
if (sc->sc_pipe_intr != NULL)
|
|
printf("%s: detach has active intr endpoint.\n",
|
|
USBDEVNAME(sc->sc_dev));
|
|
#endif
|
|
|
|
sc->sc_attached = 0;
|
|
|
|
splx(s);
|
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
|
|
USBDEV(sc->sc_dev));
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* read/write memory */
|
|
Static int
|
|
url_mem(struct url_softc *sc, int cmd, 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", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
if (cmd == URL_CMD_READMEM)
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
else
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = URL_REQ_MEM;
|
|
USETW(req.wValue, offset);
|
|
USETW(req.wIndex, 0x0000);
|
|
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: url_mem(): %s failed. off=%04x, err=%d\n",
|
|
USBDEVNAME(sc->sc_dev),
|
|
cmd == URL_CMD_READMEM ? "read" : "write",
|
|
offset, err));
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
/* read 1byte from register */
|
|
Static int
|
|
url_csr_read_1(struct url_softc *sc, int reg)
|
|
{
|
|
u_int8_t val = 0;
|
|
|
|
DPRINTFN(0x100,
|
|
("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
return (url_mem(sc, URL_CMD_READMEM, reg, &val, 1) ? 0 : val);
|
|
}
|
|
|
|
/* read 2bytes from register */
|
|
Static int
|
|
url_csr_read_2(struct url_softc *sc, int reg)
|
|
{
|
|
uWord val;
|
|
|
|
DPRINTFN(0x100,
|
|
("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
USETW(val, 0);
|
|
return (url_mem(sc, URL_CMD_READMEM, reg, &val, 2) ? 0 : UGETW(val));
|
|
}
|
|
|
|
/* write 1byte to register */
|
|
Static int
|
|
url_csr_write_1(struct url_softc *sc, int reg, int aval)
|
|
{
|
|
u_int8_t val = aval;
|
|
|
|
DPRINTFN(0x100,
|
|
("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 1) ? -1 : 0);
|
|
}
|
|
|
|
/* write 2bytes to register */
|
|
Static int
|
|
url_csr_write_2(struct url_softc *sc, int reg, int aval)
|
|
{
|
|
uWord val;
|
|
|
|
DPRINTFN(0x100,
|
|
("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
USETW(val, aval);
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 2) ? -1 : 0);
|
|
}
|
|
|
|
/* write 4bytes to register */
|
|
Static int
|
|
url_csr_write_4(struct url_softc *sc, int reg, int aval)
|
|
{
|
|
uDWord val;
|
|
|
|
DPRINTFN(0x100,
|
|
("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
USETDW(val, aval);
|
|
|
|
if (sc->sc_dying)
|
|
return (0);
|
|
|
|
return (url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 4) ? -1 : 0);
|
|
}
|
|
|
|
Static int
|
|
url_init(struct ifnet *ifp)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct mii_data *mii = GET_MII(sc);
|
|
u_char *eaddr;
|
|
int i, s;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (EIO);
|
|
|
|
s = splnet();
|
|
|
|
/* Cancel pending I/O and free all TX/RX buffers */
|
|
url_stop(ifp, 1);
|
|
|
|
eaddr = LLADDR(ifp->if_sadl);
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
url_csr_write_1(sc, URL_IDR0 + i, eaddr[i]);
|
|
|
|
/* Init transmission control register */
|
|
URL_CLRBIT(sc, URL_TCR,
|
|
URL_TCR_TXRR1 | URL_TCR_TXRR0 |
|
|
URL_TCR_IFG1 | URL_TCR_IFG0 |
|
|
URL_TCR_NOCRC);
|
|
|
|
/* Init receive control register */
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_TAIL | URL_RCR_AD);
|
|
if (ifp->if_flags & IFF_BROADCAST)
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_AB);
|
|
else
|
|
URL_CLRBIT2(sc, URL_RCR, URL_RCR_AB);
|
|
|
|
/* If we want promiscuous mode, accept all physical frames. */
|
|
if (ifp->if_flags & IFF_PROMISC)
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);
|
|
else
|
|
URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);
|
|
|
|
|
|
/* Initialize transmit ring */
|
|
if (url_tx_list_init(sc) == ENOBUFS) {
|
|
printf("%s: tx list init failed\n", USBDEVNAME(sc->sc_dev));
|
|
splx(s);
|
|
return (EIO);
|
|
}
|
|
|
|
/* Initialize receive ring */
|
|
if (url_rx_list_init(sc) == ENOBUFS) {
|
|
printf("%s: rx list init failed\n", USBDEVNAME(sc->sc_dev));
|
|
splx(s);
|
|
return (EIO);
|
|
}
|
|
|
|
/* Load the multicast filter */
|
|
url_setmulti(sc);
|
|
|
|
/* Enable RX and TX */
|
|
URL_SETBIT(sc, URL_CR, URL_CR_TE | URL_CR_RE);
|
|
|
|
mii_mediachg(mii);
|
|
|
|
if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) {
|
|
if (url_openpipes(sc)) {
|
|
splx(s);
|
|
return (EIO);
|
|
}
|
|
}
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
splx(s);
|
|
|
|
usb_callout(sc->sc_stat_ch, hz, url_tick, sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
Static void
|
|
url_reset(struct url_softc *sc)
|
|
{
|
|
int i;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
URL_SETBIT(sc, URL_CR, URL_CR_SOFT_RST);
|
|
|
|
for (i = 0; i < URL_TX_TIMEOUT; i++) {
|
|
if (!(url_csr_read_1(sc, URL_CR) & URL_CR_SOFT_RST))
|
|
break;
|
|
delay(10); /* XXX */
|
|
}
|
|
|
|
delay(10000); /* XXX */
|
|
}
|
|
|
|
int
|
|
url_activate(device_ptr_t self, enum devact act)
|
|
{
|
|
struct url_softc *sc = (struct url_softc *)self;
|
|
|
|
DPRINTF(("%s: %s: enter, act=%d\n", USBDEVNAME(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);
|
|
}
|
|
|
|
#define url_calchash(addr) (ether_crc32_be((addr), ETHER_ADDR_LEN) >> 26)
|
|
|
|
|
|
Static void
|
|
url_setmulti(struct url_softc *sc)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct ether_multi *enm;
|
|
struct ether_multistep step;
|
|
u_int32_t hashes[2] = { 0, 0 };
|
|
int h = 0;
|
|
int mcnt = 0;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
ifp = GET_IFP(sc);
|
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);
|
|
return;
|
|
} else if (ifp->if_flags & IFF_ALLMULTI) {
|
|
allmulti:
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM);
|
|
URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAP);
|
|
return;
|
|
}
|
|
|
|
/* first, zot all the existing hash bits */
|
|
url_csr_write_4(sc, URL_MAR0, 0);
|
|
url_csr_write_4(sc, URL_MAR4, 0);
|
|
|
|
/* now program new ones */
|
|
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 = url_calchash(enm->enm_addrlo);
|
|
if (h < 32)
|
|
hashes[0] |= (1 << h);
|
|
else
|
|
hashes[1] |= (1 << (h -32));
|
|
mcnt++;
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
}
|
|
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
|
|
|
URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP);
|
|
|
|
if (mcnt){
|
|
URL_SETBIT2(sc, URL_RCR, URL_RCR_AM);
|
|
} else {
|
|
URL_CLRBIT2(sc, URL_RCR, URL_RCR_AM);
|
|
}
|
|
url_csr_write_4(sc, URL_MAR0, hashes[0]);
|
|
url_csr_write_4(sc, URL_MAR4, hashes[1]);
|
|
}
|
|
|
|
Static int
|
|
url_openpipes(struct url_softc *sc)
|
|
{
|
|
struct url_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",
|
|
USBDEVNAME(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",
|
|
USBDEVNAME(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.url_ibuf, URL_INTR_PKGLEN,
|
|
url_intr, USBD_DEFAULT_INTERVAL);
|
|
if (err) {
|
|
printf("%s: open intr pipe failed: %s\n",
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
|
error = EIO;
|
|
goto done;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Start up the receive pipe. */
|
|
for (i = 0; i < URL_RX_LIST_CNT; i++) {
|
|
c = &sc->sc_cdata.url_rx_chain[i];
|
|
usbd_setup_xfer(c->url_xfer, sc->sc_pipe_rx,
|
|
c, c->url_buf, URL_BUFSZ,
|
|
USBD_SHORT_XFER_OK | USBD_NO_COPY,
|
|
USBD_NO_TIMEOUT, url_rxeof);
|
|
(void)usbd_transfer(c->url_xfer);
|
|
DPRINTF(("%s: %s: start read\n", USBDEVNAME(sc->sc_dev),
|
|
__func__));
|
|
}
|
|
|
|
done:
|
|
if (--sc->sc_refcnt < 0)
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
|
|
return (error);
|
|
}
|
|
|
|
Static int
|
|
url_newbuf(struct url_softc *sc, struct url_chain *c, struct mbuf *m)
|
|
{
|
|
struct mbuf *m_new = NULL;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (m == NULL) {
|
|
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
|
if (m_new == NULL) {
|
|
printf("%s: no memory for rx list "
|
|
"-- packet dropped!\n", USBDEVNAME(sc->sc_dev));
|
|
return (ENOBUFS);
|
|
}
|
|
MCLGET(m_new, M_DONTWAIT);
|
|
if (!(m_new->m_flags & M_EXT)) {
|
|
printf("%s: no memory for rx list "
|
|
"-- packet dropped!\n", USBDEVNAME(sc->sc_dev));
|
|
m_freem(m_new);
|
|
return (ENOBUFS);
|
|
}
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
} else {
|
|
m_new = m;
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
m_new->m_data = m_new->m_ext.ext_buf;
|
|
}
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
c->url_mbuf = m_new;
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
Static int
|
|
url_rx_list_init(struct url_softc *sc)
|
|
{
|
|
struct url_cdata *cd;
|
|
struct url_chain *c;
|
|
int i;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
cd = &sc->sc_cdata;
|
|
for (i = 0; i < URL_RX_LIST_CNT; i++) {
|
|
c = &cd->url_rx_chain[i];
|
|
c->url_sc = sc;
|
|
c->url_idx = i;
|
|
if (url_newbuf(sc, c, NULL) == ENOBUFS)
|
|
return (ENOBUFS);
|
|
if (c->url_xfer == NULL) {
|
|
c->url_xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
if (c->url_xfer == NULL)
|
|
return (ENOBUFS);
|
|
c->url_buf = usbd_alloc_buffer(c->url_xfer, URL_BUFSZ);
|
|
if (c->url_buf == NULL) {
|
|
usbd_free_xfer(c->url_xfer);
|
|
return (ENOBUFS);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
Static int
|
|
url_tx_list_init(struct url_softc *sc)
|
|
{
|
|
struct url_cdata *cd;
|
|
struct url_chain *c;
|
|
int i;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
cd = &sc->sc_cdata;
|
|
for (i = 0; i < URL_TX_LIST_CNT; i++) {
|
|
c = &cd->url_tx_chain[i];
|
|
c->url_sc = sc;
|
|
c->url_idx = i;
|
|
c->url_mbuf = NULL;
|
|
if (c->url_xfer == NULL) {
|
|
c->url_xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
if (c->url_xfer == NULL)
|
|
return (ENOBUFS);
|
|
c->url_buf = usbd_alloc_buffer(c->url_xfer, URL_BUFSZ);
|
|
if (c->url_buf == NULL) {
|
|
usbd_free_xfer(c->url_xfer);
|
|
return (ENOBUFS);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
Static void
|
|
url_start(struct ifnet *ifp)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct mbuf *m_head = NULL;
|
|
|
|
DPRINTF(("%s: %s: enter, link=%d\n", USBDEVNAME(sc->sc_dev),
|
|
__func__, sc->sc_link));
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
if (!sc->sc_link)
|
|
return;
|
|
|
|
if (ifp->if_flags & IFF_OACTIVE)
|
|
return;
|
|
|
|
IFQ_POLL(&ifp->if_snd, m_head);
|
|
if (m_head == NULL)
|
|
return;
|
|
|
|
if (url_send(sc, m_head, 0)) {
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
return;
|
|
}
|
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m_head);
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf)
|
|
bpf_mtap(ifp->if_bpf, m_head);
|
|
#endif
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
/* Set a timeout in case the chip goes out to lunch. */
|
|
ifp->if_timer = 5;
|
|
}
|
|
|
|
Static int
|
|
url_send(struct url_softc *sc, struct mbuf *m, int idx)
|
|
{
|
|
int total_len;
|
|
struct url_chain *c;
|
|
usbd_status err;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__func__));
|
|
|
|
c = &sc->sc_cdata.url_tx_chain[idx];
|
|
|
|
/* Copy the mbuf data into a contiguous buffer */
|
|
m_copydata(m, 0, m->m_pkthdr.len, c->url_buf);
|
|
c->url_mbuf = m;
|
|
total_len = m->m_pkthdr.len;
|
|
|
|
if (total_len < URL_MIN_FRAME_LEN) {
|
|
memset(c->url_buf + total_len, 0,
|
|
URL_MIN_FRAME_LEN - total_len);
|
|
total_len = URL_MIN_FRAME_LEN;
|
|
}
|
|
usbd_setup_xfer(c->url_xfer, sc->sc_pipe_tx, c, c->url_buf, total_len,
|
|
USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
|
|
URL_TX_TIMEOUT, url_txeof);
|
|
|
|
/* Transmit */
|
|
sc->sc_refcnt++;
|
|
err = usbd_transfer(c->url_xfer);
|
|
if (--sc->sc_refcnt < 0)
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
if (err != USBD_IN_PROGRESS) {
|
|
printf("%s: url_send error=%s\n", USBDEVNAME(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", USBDEVNAME(sc->sc_dev),
|
|
__func__, total_len));
|
|
|
|
sc->sc_cdata.url_tx_cnt++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
Static void
|
|
url_txeof(usbd_xfer_handle xfer, usbd_private_handle priv,
|
|
usbd_status status)
|
|
{
|
|
struct url_chain *c = priv;
|
|
struct url_softc *sc = c->url_sc;
|
|
struct ifnet *ifp = GET_IFP(sc);
|
|
int s;
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
s = splnet();
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
ifp->if_timer = 0;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
|
|
splx(s);
|
|
return;
|
|
}
|
|
ifp->if_oerrors++;
|
|
printf("%s: usb error on tx: %s\n", USBDEVNAME(sc->sc_dev),
|
|
usbd_errstr(status));
|
|
if (status == USBD_STALLED) {
|
|
sc->sc_refcnt++;
|
|
usbd_clear_endpoint_stall_async(sc->sc_pipe_tx);
|
|
if (--sc->sc_refcnt < 0)
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
}
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
ifp->if_opackets++;
|
|
|
|
m_freem(c->url_mbuf);
|
|
c->url_mbuf = NULL;
|
|
|
|
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
|
url_start(ifp);
|
|
|
|
splx(s);
|
|
}
|
|
|
|
Static void
|
|
url_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
|
|
{
|
|
struct url_chain *c = priv;
|
|
struct url_softc *sc = c->url_sc;
|
|
struct ifnet *ifp = GET_IFP(sc);
|
|
struct mbuf *m;
|
|
u_int32_t total_len;
|
|
url_rxhdr_t rxhdr;
|
|
int s;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(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",
|
|
USBDEVNAME(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_async(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);
|
|
|
|
memcpy(mtod(c->url_mbuf, char *), c->url_buf, total_len);
|
|
|
|
if (total_len <= ETHER_CRC_LEN) {
|
|
ifp->if_ierrors++;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(&rxhdr, c->url_buf + total_len - ETHER_CRC_LEN, sizeof(rxhdr));
|
|
|
|
DPRINTF(("%s: RX Status: %dbytes%s%s%s%s packets\n",
|
|
USBDEVNAME(sc->sc_dev),
|
|
UGETW(rxhdr) & URL_RXHDR_BYTEC_MASK,
|
|
UGETW(rxhdr) & URL_RXHDR_VALID_MASK ? ", Valid" : "",
|
|
UGETW(rxhdr) & URL_RXHDR_RUNTPKT_MASK ? ", Runt" : "",
|
|
UGETW(rxhdr) & URL_RXHDR_PHYPKT_MASK ? ", Physical match" : "",
|
|
UGETW(rxhdr) & URL_RXHDR_MCASTPKT_MASK ? ", Multicast" : ""));
|
|
|
|
if ((UGETW(rxhdr) & URL_RXHDR_VALID_MASK) == 0) {
|
|
ifp->if_ierrors++;
|
|
goto done;
|
|
}
|
|
|
|
ifp->if_ipackets++;
|
|
total_len -= ETHER_CRC_LEN;
|
|
|
|
m = c->url_mbuf;
|
|
m->m_pkthdr.len = m->m_len = total_len;
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
s = splnet();
|
|
|
|
if (url_newbuf(sc, c, NULL) == ENOBUFS) {
|
|
ifp->if_ierrors++;
|
|
goto done1;
|
|
}
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf)
|
|
BPF_MTAP(ifp, m);
|
|
#endif
|
|
|
|
DPRINTF(("%s: %s: deliver %d\n", USBDEVNAME(sc->sc_dev),
|
|
__func__, m->m_len));
|
|
IF_INPUT(ifp, m);
|
|
|
|
done1:
|
|
splx(s);
|
|
|
|
done:
|
|
/* Setup new transfer */
|
|
usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->url_buf, URL_BUFSZ,
|
|
USBD_SHORT_XFER_OK | USBD_NO_COPY,
|
|
USBD_NO_TIMEOUT, url_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", USBDEVNAME(sc->sc_dev), __func__));
|
|
}
|
|
|
|
#if 0
|
|
Static void url_intr()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
Static int
|
|
url_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
struct mii_data *mii;
|
|
int s, error = 0;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return (EIO);
|
|
|
|
s = splnet();
|
|
|
|
switch (cmd) {
|
|
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 (error == ENETRESET) {
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
url_setmulti(sc);
|
|
error = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
}
|
|
|
|
Static void
|
|
url_watchdog(struct ifnet *ifp)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct url_chain *c;
|
|
usbd_status stat;
|
|
int s;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
ifp->if_oerrors++;
|
|
printf("%s: watchdog timeout\n", USBDEVNAME(sc->sc_dev));
|
|
|
|
s = splusb();
|
|
c = &sc->sc_cdata.url_tx_chain[0];
|
|
usbd_get_xfer_status(c->url_xfer, NULL, NULL, NULL, &stat);
|
|
url_txeof(c->url_xfer, c, stat);
|
|
|
|
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
|
url_start(ifp);
|
|
splx(s);
|
|
}
|
|
|
|
Static void
|
|
url_stop_task(struct url_softc *sc)
|
|
{
|
|
url_stop(GET_IFP(sc), 1);
|
|
}
|
|
|
|
/* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
|
|
Static void
|
|
url_stop(struct ifnet *ifp, int disable)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
usbd_status err;
|
|
int i;
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
url_reset(sc);
|
|
|
|
usb_uncallout(sc->sc_stat_ch, url_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",
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
|
err = usbd_close_pipe(sc->sc_pipe_rx);
|
|
if (err)
|
|
printf("%s: close rx pipe failed: %s\n",
|
|
USBDEVNAME(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",
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
|
err = usbd_close_pipe(sc->sc_pipe_tx);
|
|
if (err)
|
|
printf("%s: close tx pipe failed: %s\n",
|
|
USBDEVNAME(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",
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
|
err = usbd_close_pipe(sc->sc_pipe_intr);
|
|
if (err)
|
|
printf("%s: close intr pipe failed: %s\n",
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
|
sc->sc_pipe_intr = NULL;
|
|
}
|
|
#endif
|
|
|
|
/* Free RX resources. */
|
|
for (i = 0; i < URL_RX_LIST_CNT; i++) {
|
|
if (sc->sc_cdata.url_rx_chain[i].url_mbuf != NULL) {
|
|
m_freem(sc->sc_cdata.url_rx_chain[i].url_mbuf);
|
|
sc->sc_cdata.url_rx_chain[i].url_mbuf = NULL;
|
|
}
|
|
if (sc->sc_cdata.url_rx_chain[i].url_xfer != NULL) {
|
|
usbd_free_xfer(sc->sc_cdata.url_rx_chain[i].url_xfer);
|
|
sc->sc_cdata.url_rx_chain[i].url_xfer = NULL;
|
|
}
|
|
}
|
|
|
|
/* Free TX resources. */
|
|
for (i = 0; i < URL_TX_LIST_CNT; i++) {
|
|
if (sc->sc_cdata.url_tx_chain[i].url_mbuf != NULL) {
|
|
m_freem(sc->sc_cdata.url_tx_chain[i].url_mbuf);
|
|
sc->sc_cdata.url_tx_chain[i].url_mbuf = NULL;
|
|
}
|
|
if (sc->sc_cdata.url_tx_chain[i].url_xfer != NULL) {
|
|
usbd_free_xfer(sc->sc_cdata.url_tx_chain[i].url_xfer);
|
|
sc->sc_cdata.url_tx_chain[i].url_xfer = NULL;
|
|
}
|
|
}
|
|
|
|
sc->sc_link = 0;
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
}
|
|
|
|
/* Set media options */
|
|
Static int
|
|
url_ifmedia_change(struct ifnet *ifp)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct mii_data *mii = GET_MII(sc);
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(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
|
|
url_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
struct url_softc *sc = ifp->if_softc;
|
|
struct mii_data *mii = GET_MII(sc);
|
|
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0) {
|
|
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
|
|
url_tick(void *xsc)
|
|
{
|
|
struct url_softc *sc = xsc;
|
|
|
|
if (sc == NULL)
|
|
return;
|
|
|
|
DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(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
|
|
url_tick_task(void *xsc)
|
|
{
|
|
struct url_softc *sc = xsc;
|
|
struct ifnet *ifp;
|
|
struct mii_data *mii;
|
|
int s;
|
|
|
|
if (sc == NULL)
|
|
return;
|
|
|
|
DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
|
|
__func__));
|
|
|
|
if (sc->sc_dying)
|
|
return;
|
|
|
|
ifp = GET_IFP(sc);
|
|
mii = GET_MII(sc);
|
|
|
|
if (mii == NULL)
|
|
return;
|
|
|
|
s = splnet();
|
|
|
|
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",
|
|
USBDEVNAME(sc->sc_dev), __func__));
|
|
sc->sc_link++;
|
|
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
|
url_start(ifp);
|
|
}
|
|
}
|
|
|
|
usb_callout(sc->sc_stat_ch, hz, url_tick, sc);
|
|
|
|
splx(s);
|
|
}
|
|
|
|
/* Get exclusive access to the MII registers */
|
|
Static void
|
|
url_lock_mii(struct url_softc *sc)
|
|
{
|
|
DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
|
|
__func__));
|
|
|
|
sc->sc_refcnt++;
|
|
lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL);
|
|
}
|
|
|
|
Static void
|
|
url_unlock_mii(struct url_softc *sc)
|
|
{
|
|
DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
|
|
__func__));
|
|
|
|
lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL);
|
|
if (--sc->sc_refcnt < 0)
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
}
|
|
|
|
Static int
|
|
url_int_miibus_readreg(device_ptr_t dev, int phy, int reg)
|
|
{
|
|
struct url_softc *sc;
|
|
u_int16_t val;
|
|
|
|
if (dev == NULL)
|
|
return (0);
|
|
|
|
sc = USBGETSOFTC(dev);
|
|
|
|
DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg));
|
|
|
|
if (sc->sc_dying) {
|
|
#ifdef DIAGNOSTIC
|
|
printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
|
|
__func__);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/* XXX: one PHY only for the RTL8150 internal PHY */
|
|
if (phy != 0) {
|
|
DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy));
|
|
return (0);
|
|
}
|
|
|
|
url_lock_mii(sc);
|
|
|
|
switch (reg) {
|
|
case MII_BMCR: /* Control Register */
|
|
reg = URL_BMCR;
|
|
break;
|
|
case MII_BMSR: /* Status Register */
|
|
reg = URL_BMSR;
|
|
break;
|
|
case MII_PHYIDR1:
|
|
case MII_PHYIDR2:
|
|
val = 0;
|
|
goto R_DONE;
|
|
break;
|
|
case MII_ANAR: /* Autonegotiation advertisement */
|
|
reg = URL_ANAR;
|
|
break;
|
|
case MII_ANLPAR: /* Autonegotiation link partner abilities */
|
|
reg = URL_ANLP;
|
|
break;
|
|
case URLPHY_MSR: /* Media Status Register */
|
|
reg = URL_MSR;
|
|
break;
|
|
default:
|
|
printf("%s: %s: bad register %04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, reg);
|
|
val = 0;
|
|
goto R_DONE;
|
|
break;
|
|
}
|
|
|
|
if (reg == URL_MSR)
|
|
val = url_csr_read_1(sc, reg);
|
|
else
|
|
val = url_csr_read_2(sc, reg);
|
|
|
|
R_DONE:
|
|
DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg, val));
|
|
|
|
url_unlock_mii(sc);
|
|
return (val);
|
|
}
|
|
|
|
Static void
|
|
url_int_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
|
|
{
|
|
struct url_softc *sc;
|
|
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
sc = USBGETSOFTC(dev);
|
|
|
|
DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg, data));
|
|
|
|
if (sc->sc_dying) {
|
|
#ifdef DIAGNOSTIC
|
|
printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
|
|
__func__);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* XXX: one PHY only for the RTL8150 internal PHY */
|
|
if (phy != 0) {
|
|
DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy));
|
|
return;
|
|
}
|
|
|
|
url_lock_mii(sc);
|
|
|
|
switch (reg) {
|
|
case MII_BMCR: /* Control Register */
|
|
reg = URL_BMCR;
|
|
break;
|
|
case MII_BMSR: /* Status Register */
|
|
reg = URL_BMSR;
|
|
break;
|
|
case MII_PHYIDR1:
|
|
case MII_PHYIDR2:
|
|
goto W_DONE;
|
|
break;
|
|
case MII_ANAR: /* Autonegotiation advertisement */
|
|
reg = URL_ANAR;
|
|
break;
|
|
case MII_ANLPAR: /* Autonegotiation link partner abilities */
|
|
reg = URL_ANLP;
|
|
break;
|
|
case URLPHY_MSR: /* Media Status Register */
|
|
reg = URL_MSR;
|
|
break;
|
|
default:
|
|
printf("%s: %s: bad register %04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, reg);
|
|
goto W_DONE;
|
|
break;
|
|
}
|
|
|
|
if (reg == URL_MSR)
|
|
url_csr_write_1(sc, reg, data);
|
|
else
|
|
url_csr_write_2(sc, reg, data);
|
|
W_DONE:
|
|
|
|
url_unlock_mii(sc);
|
|
return;
|
|
}
|
|
|
|
Static void
|
|
url_miibus_statchg(device_ptr_t dev)
|
|
{
|
|
#ifdef URL_DEBUG
|
|
struct url_softc *sc;
|
|
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
sc = USBGETSOFTC(dev);
|
|
DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
|
|
#endif
|
|
/* Nothing to do */
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* external PHYs support, but not test.
|
|
*/
|
|
Static int
|
|
url_ext_miibus_redreg(device_ptr_t dev, int phy, int reg)
|
|
{
|
|
struct url_softc *sc = USBGETSOFTC(dev);
|
|
u_int16_t val;
|
|
|
|
DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg));
|
|
|
|
if (sc->sc_dying) {
|
|
#ifdef DIAGNOSTIC
|
|
printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
|
|
__func__);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
url_lock_mii(sc);
|
|
|
|
url_csr_write_1(sc, URL_PHYADD, phy & URL_PHYADD_MASK);
|
|
/*
|
|
* RTL8150L will initiate a MII management data transaction
|
|
* if PHYCNT_OWN bit is set 1 by software. After transaction,
|
|
* this bit is auto cleared by TRL8150L.
|
|
*/
|
|
url_csr_write_1(sc, URL_PHYCNT,
|
|
(reg | URL_PHYCNT_PHYOWN) & ~URL_PHYCNT_RWCR);
|
|
for (i = 0; i < URL_TIMEOUT; i++) {
|
|
if ((url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN) == 0)
|
|
break;
|
|
}
|
|
if (i == URL_TIMEOUT) {
|
|
printf("%s: MII read timed out\n", USBDEVNAME(sc->sc_dev));
|
|
}
|
|
|
|
val = url_csr_read_2(sc, URL_PHYDAT);
|
|
|
|
DPRINTF(("%s: %s: phy=%d reg=0x%04x => 0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg, val));
|
|
|
|
url_unlock_mii(sc);
|
|
return (val);
|
|
}
|
|
|
|
Static void
|
|
url_ext_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
|
|
{
|
|
struct url_softc *sc = USBGETSOFTC(dev);
|
|
|
|
DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n",
|
|
USBDEVNAME(sc->sc_dev), __func__, phy, reg, data));
|
|
|
|
if (sc->sc_dying) {
|
|
#ifdef DIAGNOSTIC
|
|
printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
|
|
__func__);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
url_lock_mii(sc);
|
|
|
|
url_csr_write_2(sc, URL_PHYDAT, data);
|
|
url_csr_write_1(sc, URL_PHYADD, phy);
|
|
url_csr_write_1(sc, URL_PHYCNT, reg | URL_PHYCNT_RWCR); /* Write */
|
|
|
|
for (i=0; i < URL_TIMEOUT; i++) {
|
|
if (url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN)
|
|
break;
|
|
}
|
|
|
|
if (i == URL_TIMEOUT) {
|
|
printf("%s: MII write timed out\n",
|
|
USBDEVNAME(sc->sc_dev));
|
|
}
|
|
|
|
url_unlock_mii(sc);
|
|
return;
|
|
}
|
|
#endif
|
|
|