From 2d86dfd7473c1264cf9aa28b92f33a3bc66d0dde Mon Sep 17 00:00:00 2001 From: aaron Date: Sun, 26 Mar 2000 18:49:43 +0000 Subject: [PATCH] Driver for USB Ethernet adapters based on the Kawasaki LSI KL5KUSB101B. --- sys/arch/i386/conf/GENERIC | 5 +- sys/dev/usb/files.usb | 17 +- sys/dev/usb/if_kue.c | 1307 ++++++++++++++++++++++++++++++++++++ sys/dev/usb/if_kuereg.h | 181 +++++ 4 files changed, 1508 insertions(+), 2 deletions(-) create mode 100644 sys/dev/usb/if_kue.c create mode 100644 sys/dev/usb/if_kuereg.h diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 2f3c7d08a85..1d47e9180bb 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.161 2000/03/22 18:35:50 mickey Exp $ +# $OpenBSD: GENERIC,v 1.162 2000/03/26 18:49:43 aaron Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -80,6 +80,9 @@ pcmcia* at pcic? controller ? socket ? # USB audio #uaudio* at uhub? port ? configuration ? +# USB Ethernet adapters +#kue* at uhub? port ? # Kawasaki KL5KUSB101B based adapters + # USB Generic driver #ugen* at uhub? port ? configuration ? interface ? diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 6dbc9f49e02..cf1d7ae6923 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.5 1999/09/27 18:03:54 fgsch Exp $ +# $OpenBSD: files.usb,v 1.6 2000/03/26 18:49:44 aaron Exp $ # $NetBSD: files.usb,v 1.8 1999/06/30 06:44:22 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -64,3 +64,18 @@ file dev/usb/umodem.c umodem needs-flag #attach ums at uhub #file dev/usb/ums.c ums needs-flag +# Ethernet adapters +# ADMtek AN986 Pegasus +#device aue: ether, ifnet, mii, ifmedia +#attach aue at uhub +#file dev/usb/if_aue.c aue + +# CATC USB-EL1201A +#device cue: ether, ifnet, ifmedia +#attach cue at uhub +#file dev/usb_if_cue.c cue + +# Kawasaki LSI KL5KUSB101B +device kue: ether, ifnet, ifmedia +attach kue at uhub +file dev/usb/if_kue.c kue diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c new file mode 100644 index 00000000000..6ae0101b812 --- /dev/null +++ b/sys/dev/usb/if_kue.c @@ -0,0 +1,1307 @@ +/* $OpenBSD: if_kue.c,v 1.1 2000/03/26 18:49:44 aaron Exp $ */ +/* $NetBSD: if_kue.c,v 1.21 2000/03/24 22:03:30 augustss Exp $ */ +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. 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 Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD: src/sys/dev/usb/if_kue.c,v 1.14 2000/01/14 01:36:15 wpaul Exp $ + */ + +/* + * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. + * + * Written by Bill Paul + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The KLSI USB to ethernet adapter chip contains an USB serial interface, + * ethernet MAC and embedded microcontroller (called the QT Engine). + * The chip must have firmware loaded into it before it will operate. + * Packets are passed between the chip and host via bulk transfers. + * There is an interrupt endpoint mentioned in the software spec, however + * it's currently unused. This device is 10Mbps half-duplex only, hence + * there is no media selection logic. The MAC supports a 128 entry + * multicast filter, though the exact size of the filter can depend + * on the firmware. Curiously, while the software spec describes various + * ethernet statistics counters, my sample adapter and firmware combination + * claims not to support any statistics counters at all. + * + * Note that once we load the firmware in the device, we have to be + * careful not to load it again: if you restart your computer but + * leave the adapter attached to the USB controller, it may remain + * powered on and retain its firmware. In this case, we don't need + * to load the firmware a second time. + * + * Special thanks to Rob Furr for providing an ADS Technologies + * adapter for development and testing. No monkeys were harmed during + * the development of this driver. + */ + +/* + * Ported to NetBSD and somewhat rewritten by Lennart Augustsson. + */ + +/* + * TODO: + * only use kue_do_request for downloading firmware. + * more DPRINTF + * proper cleanup on errors + */ +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include + +#if NBPFILTER > 0 +#include +#endif + +#ifdef NS +#include +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#ifdef KUE_DEBUG +#define DPRINTF(x) if (kuedebug) logprintf x +#define DPRINTFN(n,x) if (kuedebug >= (n)) logprintf x +int kuedebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +/* + * Various supported device vendors/products. + */ +static struct kue_type kue_devs[] = { + { USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101 }, + { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT }, + { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T }, + { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101 }, + { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET }, + { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2 }, + { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45 }, + { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250 }, + { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460 }, + { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T }, + { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C }, + { USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB }, + { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T }, + { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_USB101 }, + { 0, 0 } +}; + +USB_DECLARE_DRIVER(kue); + +int kue_tx_list_init __P((struct kue_softc *)); +int kue_rx_list_init __P((struct kue_softc *)); +int kue_newbuf __P((struct kue_softc *, struct kue_chain *, + struct mbuf *)); +int kue_send __P((struct kue_softc *, struct mbuf *, int)); +int kue_open_pipes __P((struct kue_softc *)); +void kue_rxeof __P((usbd_xfer_handle, + usbd_private_handle, usbd_status)); +void kue_txeof __P((usbd_xfer_handle, + usbd_private_handle, usbd_status)); +void kue_start __P((struct ifnet *)); +int kue_ioctl __P((struct ifnet *, u_long, caddr_t)); +void kue_init __P((void *)); +void kue_stop __P((struct kue_softc *)); +void kue_watchdog __P((struct ifnet *)); + +void kue_setmulti __P((struct kue_softc *)); +void kue_reset __P((struct kue_softc *)); + +usbd_status kue_ctl __P((struct kue_softc *, int, u_int8_t, + u_int16_t, void *, u_int32_t)); +usbd_status kue_setword __P((struct kue_softc *, u_int8_t, u_int16_t)); +int kue_load_fw __P((struct kue_softc *)); + +#define KUE_DO_REQUEST(dev, req, data) \ + usbd_do_request_flags(dev, req, data, USBD_NO_TSLEEP, NULL) + +usbd_status +kue_setword(sc, breq, word) + struct kue_softc *sc; + u_int8_t breq; + u_int16_t word; +{ + usb_device_request_t req; + usbd_status err; + int s; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = breq; + USETW(req.wValue, word); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + s = splusb(); + err = KUE_DO_REQUEST(sc->kue_udev, &req, NULL); + splx(s); + + return (err); +} + +usbd_status +kue_ctl(sc, rw, breq, val, data, len) + struct kue_softc *sc; + int rw; + u_int8_t breq; + u_int16_t val; + void *data; + u_int32_t len; +{ + usb_device_request_t req; + usbd_status err; + int s; + + DPRINTFN(10,("%s: %s: enter, len=%d\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__, len)); + + if (rw == KUE_CTL_WRITE) + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + else + req.bmRequestType = UT_READ_VENDOR_DEVICE; + + req.bRequest = breq; + USETW(req.wValue, val); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + s = splusb(); + err = KUE_DO_REQUEST(sc->kue_udev, &req, data); + splx(s); + + return (err); +} + +int +kue_load_fw(sc) + struct kue_softc *sc; +{ + usbd_status err; + + DPRINTFN(1,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + /* + * First, check if we even need to load the firmware. + * If the device was still attached when the system was + * rebooted, it may already have firmware loaded in it. + * If this is the case, we don't need to do it again. + * And in fact, if we try to load it again, we'll hang, + * so we have to avoid this condition if we don't want + * to look stupid. + * + * We can test this quickly by issuing a request that + * is only valid after firmware download. + */ + err = kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, + 0, &sc->kue_desc, sizeof(sc->kue_desc)); + if (!err) { + printf("%s: warm boot, no firmware download\n", + USBDEVNAME(sc->kue_dev)); + return (0); + } + + printf("%s: cold boot, downloading firmware\n", + USBDEVNAME(sc->kue_dev)); + + /* Load code segment */ + DPRINTFN(1,("%s: kue_load_fw: download code_seg\n", + USBDEVNAME(sc->kue_dev))); + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_code_seg, sizeof(kue_code_seg)); + if (err) { + printf("%s: failed to load code segment: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + return (EIO); + } + + /* Load fixup segment */ + DPRINTFN(1,("%s: kue_load_fw: download fix_seg\n", + USBDEVNAME(sc->kue_dev))); + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_fix_seg, sizeof(kue_fix_seg)); + if (err) { + printf("%s: failed to load fixup segment: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + return (EIO); + } + + /* Send trigger command. */ + DPRINTFN(1,("%s: kue_load_fw: download trig_seg\n", + USBDEVNAME(sc->kue_dev))); + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_trig_seg, sizeof(kue_trig_seg)); + if (err) { + printf("%s: failed to load trigger segment: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + return (EIO); + } + + usbd_delay_ms(sc->kue_udev, 10); + + /* + * Reload device descriptor. + * Why? The chip without the firmware loaded returns + * one revision code. The chip with the firmware + * loaded and running returns a *different* revision + * code. This confuses the quirk mechanism, which is + * dependent on the revision data. + */ + (void)usbd_reload_device_desc(sc->kue_udev); + + DPRINTFN(1,("%s: %s: done\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + /* Reset the adapter. */ + kue_reset(sc); + + return (0); +} + +void +kue_setmulti(sc) + struct kue_softc *sc; +{ + struct ifnet *ifp; + struct arpcom *ac = &sc->arpcom; + struct ether_multi *enm; + struct ether_multistep step; + int i; + + ifp = &sc->arpcom.ac_if; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI; + sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST; + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); + return; + } + + sc->kue_rxfilt &= ~KUE_RXFILT_ALLMULTI; + + i = 0; + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + if (i == KUE_MCFILTCNT(sc)) + break; +#if 0 + if (memcmp(enm->enm_addrlo, + enm->enm_addrhi, ETHER_ADDR_LEN) != 0) { + ifp->if_flags |= IFF_ALLMULTI; + /* XXX what now? */ + return; + } +#endif + memcpy(KUE_MCFILT(sc, i), enm->enm_addrlo, ETHER_ADDR_LEN); + ETHER_NEXT_MULTI(step, enm); + i++; + } + + if (i == KUE_MCFILTCNT(sc)) + sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI; + else { + sc->kue_rxfilt |= KUE_RXFILT_MULTICAST; + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, + i, sc->kue_mcfilters, i * ETHER_ADDR_LEN); + } + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); +} + +/* + * Issue a SET_CONFIGURATION command to reset the MAC. This should be + * done after the firmware is loaded into the adapter in order to + * bring it into proper operation. + */ +void +kue_reset(sc) + struct kue_softc *sc; +{ + usbd_status err; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + err = usbd_set_config_no(sc->kue_udev, KUE_CONFIG_NO, 0); + if (err) + printf("%s: reset failed\n", USBDEVNAME(sc->kue_dev)); + + /* Wait a little while for the chip to get its brains in order. */ + usbd_delay_ms(sc->kue_udev, 10); +} + +/* + * Probe for a KLSI chip. + */ +USB_MATCH(kue) +{ + USB_MATCH_START(kue, uaa); + struct kue_type *t; + + DPRINTFN(25,("kue_match: enter\n")); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + for (t = kue_devs; t->kue_vid != 0; t++) + if (uaa->vendor == t->kue_vid && uaa->product == t->kue_did) + return (UMATCH_VENDOR_PRODUCT); + + return (UMATCH_NONE); +} + +/* + * Attach the interface. Allocate softc structures, do + * setup and ethernet/BPF attach. + */ +USB_ATTACH(kue) +{ + USB_ATTACH_START(kue, sc, uaa); + char devinfo[1024]; + int s; + struct ifnet *ifp; + usbd_device_handle dev = uaa->device; + usbd_interface_handle iface; + usbd_status err; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + int i; + + DPRINTFN(5,(" : kue_attach: sc=%p, dev=%p", sc, dev)); + + usbd_devinfo(dev, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", USBDEVNAME(sc->kue_dev), devinfo); + + err = usbd_set_config_no(dev, KUE_CONFIG_NO, 0); + if (err) { + printf("%s: setting config no failed\n", + USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + sc->kue_udev = dev; + sc->kue_product = uaa->product; + sc->kue_vendor = uaa->vendor; + + /* Load the firmware into the NIC. */ + if (kue_load_fw(sc)) { + printf("%s: loading firmware failed\n", + USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + err = usbd_device2interface_handle(dev, KUE_IFACE_IDX, &iface); + if (err) { + printf("%s: getting interface handle failed\n", + USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + sc->kue_iface = iface; + id = usbd_get_interface_descriptor(iface); + + /* Find endpoints. */ + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) { + printf("%s: couldn't get ep %d\n", + USBDEVNAME(sc->kue_dev), i); + USB_ATTACH_ERROR_RETURN; + } + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + sc->kue_ed[KUE_ENDPT_RX] = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + sc->kue_ed[KUE_ENDPT_TX] = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + sc->kue_ed[KUE_ENDPT_INTR] = ed->bEndpointAddress; + } + } + + if (sc->kue_ed[KUE_ENDPT_RX] == 0 || sc->kue_ed[KUE_ENDPT_TX] == 0) { + printf("%s: missing endpoint\n", USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + /* Read ethernet descriptor */ + err = kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, + 0, &sc->kue_desc, sizeof(sc->kue_desc)); + if (err) { + printf("%s: could not read Ethernet descriptor\n", + USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + sc->kue_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, + M_USBDEV, M_NOWAIT); + if (sc->kue_mcfilters == NULL) { + printf("%s: no memory for multicast filter buffer\n", + USBDEVNAME(sc->kue_dev)); + USB_ATTACH_ERROR_RETURN; + } + + s = splimp(); + + /* + * A KLSI chip was detected. Inform the world. + */ + printf("%s: Ethernet address %s\n", USBDEVNAME(sc->kue_dev), + ether_sprintf(sc->kue_desc.kue_macaddr)); + + /* Initialize interface info.*/ + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = kue_ioctl; + ifp->if_start = kue_start; + ifp->if_watchdog = kue_watchdog; + strncpy(ifp->if_xname, USBDEVNAME(sc->kue_dev), IFNAMSIZ); + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp); + +#if NBPFILTER > 0 + bpfattach(&sc->arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); +#endif + sc->kue_attached = 1; + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->kue_udev, + USBDEV(sc->kue_dev)); + + USB_ATTACH_SUCCESS_RETURN; +} + +USB_DETACH(kue) +{ + USB_DETACH_START(kue, sc); + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + + s = splusb(); /* XXX why? */ + + if (sc->kue_mcfilters != NULL) { + free(sc->kue_mcfilters, M_USBDEV); + sc->kue_mcfilters = NULL; + } + + if (!sc->kue_attached) { + /* Detached before attached finished, so just bail out. */ + splx(s); + return (0); + } + + if (ifp->if_flags & IFF_RUNNING) + kue_stop(sc); + +#if NBPFILTER > 0 + bpfdetach(ifp); +#endif + ether_ifdetach(ifp); + + if_detach(ifp); + +#ifdef DIAGNOSTIC + if (sc->kue_ep[KUE_ENDPT_TX] != NULL || + sc->kue_ep[KUE_ENDPT_RX] != NULL || + sc->kue_ep[KUE_ENDPT_INTR] != NULL) + printf("%s: detach has active endpoints\n", + USBDEVNAME(sc->kue_dev)); +#endif + + sc->kue_attached = 0; + splx(s); + + return (0); +} + +int +kue_activate(self, act) + device_ptr_t self; + enum devact act; +{ + struct kue_softc *sc = (struct kue_softc *)self; + + DPRINTFN(2,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + sc->kue_dying = 1; + return (EOPNOTSUPP); + break; + } + return (0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +int +kue_newbuf(sc, c, m) + struct kue_softc *sc; + struct kue_chain *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + 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->kue_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->kue_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; + } + + c->kue_mbuf = m_new; + + return (0); +} + +int +kue_rx_list_init(sc) + struct kue_softc *sc; +{ + struct kue_cdata *cd; + struct kue_chain *c; + int i; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + cd = &sc->kue_cdata; + for (i = 0; i < KUE_RX_LIST_CNT; i++) { + c = &cd->kue_rx_chain[i]; + c->kue_sc = sc; + c->kue_idx = i; + if (kue_newbuf(sc, c, NULL) == ENOBUFS) + return (ENOBUFS); + if (c->kue_xfer == NULL) { + c->kue_xfer = usbd_alloc_xfer(sc->kue_udev); + if (c->kue_xfer == NULL) + return (ENOBUFS); + c->kue_buf = usbd_alloc_buffer(c->kue_xfer, KUE_BUFSZ); + if (c->kue_buf == NULL) + return (ENOBUFS); /* XXX free xfer */ + } + } + + return (0); +} + +int +kue_tx_list_init(sc) + struct kue_softc *sc; +{ + struct kue_cdata *cd; + struct kue_chain *c; + int i; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev), __FUNCTION__)); + + cd = &sc->kue_cdata; + for (i = 0; i < KUE_TX_LIST_CNT; i++) { + c = &cd->kue_tx_chain[i]; + c->kue_sc = sc; + c->kue_idx = i; + c->kue_mbuf = NULL; + if (c->kue_xfer == NULL) { + c->kue_xfer = usbd_alloc_xfer(sc->kue_udev); + if (c->kue_xfer == NULL) + return (ENOBUFS); + c->kue_buf = usbd_alloc_buffer(c->kue_xfer, KUE_BUFSZ); + if (c->kue_buf == NULL) + return (ENOBUFS); + } + } + + return (0); +} + +#ifdef __FreeBSD__ +static void +kue_rxstart(ifp) + struct ifnet *ifp; +{ + struct kue_softc *sc; + struct kue_chain *c; + + sc = ifp->if_softc; + c = &sc->kue_cdata.kue_rx_chain[sc->kue_cdata.kue_rx_prod]; + + if (kue_newbuf(sc, c, NULL) == ENOBUFS) { + ifp->if_ierrors++; + return; + } + + /* Setup new transfer. */ + usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX], + c, c->kue_buf, KUE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, kue_rxeof); + usbd_transfer(c->kue_xfer); +} +#endif + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +void +kue_rxeof(xfer, priv, status) + usbd_xfer_handle xfer; + usbd_private_handle priv; + usbd_status status; +{ + struct ether_header *eh; + struct kue_chain *c = priv; + struct kue_softc *sc = c->kue_sc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + int total_len = 0; + int s; + + DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__, status)); + + if (sc->kue_dying) + return; + + if (!(ifp->if_flags & IFF_RUNNING)) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + return; + sc->kue_rx_errs++; + if (usbd_ratecheck(&sc->kue_rx_notice)) { + printf("%s: %u usb errors on rx: %s\n", + USBDEVNAME(sc->kue_dev), sc->kue_rx_errs, + usbd_errstr(status)); + sc->kue_rx_errs = 0; + } + if (status == USBD_STALLED) + usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_RX]); + goto done; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + + DPRINTFN(10,("%s: %s: total_len=%d len=%d\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__, total_len, + UGETW(mtod(c->kue_mbuf, u_int8_t *)))); + + if (total_len <= 1) + goto done; + + m = c->kue_mbuf; + /* copy data to mbuf */ + memcpy(mtod(m, char*), c->kue_buf, total_len); + + /* No errors; receive the packet. */ + total_len = UGETW(mtod(m, u_int8_t *)); + m_adj(m, sizeof(u_int16_t)); + + if (total_len < sizeof(struct ether_header)) { + ifp->if_ierrors++; + goto done; + } + + ifp->if_ipackets++; + m->m_pkthdr.len = m->m_len = total_len; + + m->m_pkthdr.rcvif = ifp; + + s = splimp(); + + /* XXX ugly */ + if (kue_newbuf(sc, c, NULL) == ENOBUFS) { + ifp->if_ierrors++; + goto done1; + } + + eh = mtod(m, struct ether_header *); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + DPRINTFN(10,("%s: %s: deliver %d\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__, m->m_len)); + ether_input(ifp, eh, m); + done1: + splx(s); + + done: + + /* Setup new transfer. */ + usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX], + c, c->kue_buf, KUE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, kue_rxeof); + usbd_transfer(c->kue_xfer); + + DPRINTFN(10,("%s: %s: start rx\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__)); +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +void +kue_txeof(xfer, priv, status) + usbd_xfer_handle xfer; + usbd_private_handle priv; + usbd_status status; +{ + struct kue_chain *c = priv; + struct kue_softc *sc = c->kue_sc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + + if (sc->kue_dying) + return; + + s = splimp(); + + DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__, status)); + + 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->kue_dev), + usbd_errstr(status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_TX]); + splx(s); + return; + } + + ifp->if_opackets++; + + m_freem(c->kue_mbuf); + c->kue_mbuf = NULL; + + if (ifp->if_snd.ifq_head != NULL) + kue_start(ifp); + + splx(s); +} + +int +kue_send(sc, m, idx) + struct kue_softc *sc; + struct mbuf *m; + int idx; +{ + int total_len; + struct kue_chain *c; + usbd_status err; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + c = &sc->kue_cdata.kue_tx_chain[idx]; + + /* + * Copy the mbuf data into a contiguous buffer, leaving two + * bytes at the beginning to hold the frame length. + */ + m_copydata(m, 0, m->m_pkthdr.len, c->kue_buf + 2); + c->kue_mbuf = m; + + total_len = m->m_pkthdr.len + 2; + /* XXX what's this? */ + total_len += 64 - (total_len % 64); + + /* Frame length is specified in the first 2 bytes of the buffer. */ + c->kue_buf[0] = (u_int8_t)m->m_pkthdr.len; + c->kue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); + + /* XXX 10000 */ + usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_TX], + c, c->kue_buf, total_len, USBD_NO_COPY, 10000, kue_txeof); + + /* Transmit */ + err = usbd_transfer(c->kue_xfer); + if (err != USBD_IN_PROGRESS) { + kue_stop(sc); + return (EIO); + } + + sc->kue_cdata.kue_tx_cnt++; + + return (0); +} + +void +kue_start(ifp) + struct ifnet *ifp; +{ + struct kue_softc *sc = ifp->if_softc; + struct mbuf *m_head = NULL; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + if (sc->kue_dying) + return; + + if (ifp->if_flags & IFF_OACTIVE) + return; + + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + return; + + if (kue_send(sc, m_head, 0)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + return; + } + +#if NBPFILTER > 0 + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + 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; +} + +void +kue_init(xsc) + void *xsc; +{ + struct kue_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + u_char *eaddr; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + if (ifp->if_flags & IFF_RUNNING) + return; + + s = splimp(); + + eaddr = sc->arpcom.ac_enaddr; + + /* Set MAC address */ + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, eaddr, ETHER_ADDR_LEN); + + sc->kue_rxfilt = KUE_RXFILT_UNICAST | KUE_RXFILT_BROADCAST; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + sc->kue_rxfilt |= KUE_RXFILT_PROMISC; + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); + + /* I'm not sure how to tune these. */ +#if 0 + /* + * Leave this one alone for now; setting it + * wrong causes lockups on some machines/controllers. + */ + kue_setword(sc, KUE_CMD_SET_SOFS, 1); +#endif + kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64); + + /* Init TX ring. */ + if (kue_tx_list_init(sc) == ENOBUFS) { + printf("%s: tx list init failed\n", USBDEVNAME(sc->kue_dev)); + splx(s); + return; + } + + /* Init RX ring. */ + if (kue_rx_list_init(sc) == ENOBUFS) { + printf("%s: rx list init failed\n", USBDEVNAME(sc->kue_dev)); + splx(s); + return; + } + + /* Load the multicast filter. */ + kue_setmulti(sc); + + if (sc->kue_ep[KUE_ENDPT_RX] == NULL) { + if (kue_open_pipes(sc)) { + splx(s); + return; + } + } + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + splx(s); +} + +int +kue_open_pipes(sc) + struct kue_softc *sc; +{ + usbd_status err; + struct kue_chain *c; + int i; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + /* Open RX and TX pipes. */ + err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_RX], + USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_RX]); + if (err) { + printf("%s: open rx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + return (EIO); + } + + err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_TX], + USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_TX]); + if (err) { + printf("%s: open tx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + return (EIO); + } + + /* Start up the receive pipe. */ + for (i = 0; i < KUE_RX_LIST_CNT; i++) { + c = &sc->kue_cdata.kue_rx_chain[i]; + usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX], + c, c->kue_buf, KUE_BUFSZ, + USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, + kue_rxeof); + DPRINTFN(5,("%s: %s: start read\n", USBDEVNAME(sc->kue_dev), + __FUNCTION__)); + usbd_transfer(c->kue_xfer); + } + + return (0); +} + +int +kue_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct kue_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + if (sc->kue_dying) + return (EIO); + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + kue_init(sc); + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&sc->arpcom, ifa); + break; +#endif /* INET */ +#ifdef NS + case AF_NS: + { + struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = *(union ns_host *) + sc->arpcom.ac_enaddr; + else + memcpy(sc->arpcom.ac_enaddr, + ina->x_host.c_host, + ifp->if_addrlen); + break; + } +#endif /* NS */ + } + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->kue_if_flags & IFF_PROMISC)) { + sc->kue_rxfilt |= KUE_RXFILT_PROMISC; + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, + sc->kue_rxfilt); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->kue_if_flags & IFF_PROMISC) { + sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC; + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, + sc->kue_rxfilt); + } else if (!(ifp->if_flags & IFF_RUNNING)) + kue_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + kue_stop(sc); + } + sc->kue_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + kue_setmulti(sc); + error = 0; + break; + default: + error = EINVAL; + break; + } + + splx(s); + + return (error); +} + +void +kue_watchdog(ifp) + struct ifnet *ifp; +{ + struct kue_softc *sc = ifp->if_softc; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + if (sc->kue_dying) + return; + + ifp->if_oerrors++; + printf("%s: watchdog timeout\n", USBDEVNAME(sc->kue_dev)); + + /* + * The polling business is a kludge to avoid allowing the + * USB code to call tsleep() in usbd_delay_ms(), which will + * kill us since the watchdog routine is invoked from + * interrupt context. + */ + usbd_set_polling(sc->kue_udev, 1); + kue_stop(sc); + kue_init(sc); + usbd_set_polling(sc->kue_udev, 0); + + if (ifp->if_snd.ifq_head != NULL) + kue_start(ifp); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +void +kue_stop(sc) + struct kue_softc *sc; +{ + usbd_status err; + struct ifnet *ifp; + int i; + + DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->kue_dev),__FUNCTION__)); + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + /* Stop transfers. */ + if (sc->kue_ep[KUE_ENDPT_RX] != NULL) { + err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]); + if (err) { + printf("%s: abort rx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_RX]); + if (err) { + printf("%s: close rx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + sc->kue_ep[KUE_ENDPT_RX] = NULL; + } + + if (sc->kue_ep[KUE_ENDPT_TX] != NULL) { + err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]); + if (err) { + printf("%s: abort tx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_TX]); + if (err) { + printf("%s: close tx pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + sc->kue_ep[KUE_ENDPT_TX] = NULL; + } + + if (sc->kue_ep[KUE_ENDPT_INTR] != NULL) { + err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]); + if (err) { + printf("%s: abort intr pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_INTR]); + if (err) { + printf("%s: close intr pipe failed: %s\n", + USBDEVNAME(sc->kue_dev), usbd_errstr(err)); + } + sc->kue_ep[KUE_ENDPT_INTR] = NULL; + } + + /* Free RX resources. */ + for (i = 0; i < KUE_RX_LIST_CNT; i++) { + if (sc->kue_cdata.kue_rx_chain[i].kue_mbuf != NULL) { + m_freem(sc->kue_cdata.kue_rx_chain[i].kue_mbuf); + sc->kue_cdata.kue_rx_chain[i].kue_mbuf = NULL; + } + if (sc->kue_cdata.kue_rx_chain[i].kue_xfer != NULL) { + usbd_free_xfer(sc->kue_cdata.kue_rx_chain[i].kue_xfer); + sc->kue_cdata.kue_rx_chain[i].kue_xfer = NULL; + } + } + + /* Free TX resources. */ + for (i = 0; i < KUE_TX_LIST_CNT; i++) { + if (sc->kue_cdata.kue_tx_chain[i].kue_mbuf != NULL) { + m_freem(sc->kue_cdata.kue_tx_chain[i].kue_mbuf); + sc->kue_cdata.kue_tx_chain[i].kue_mbuf = NULL; + } + if (sc->kue_cdata.kue_tx_chain[i].kue_xfer != NULL) { + usbd_free_xfer(sc->kue_cdata.kue_tx_chain[i].kue_xfer); + sc->kue_cdata.kue_tx_chain[i].kue_xfer = NULL; + } + } + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +} + +#ifdef __FreeBSD__ +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void +kue_shutdown(dev) + device_t dev; +{ + struct kue_softc *sc; + + sc = device_get_softc(dev); + + kue_stop(sc); +} +#endif diff --git a/sys/dev/usb/if_kuereg.h b/sys/dev/usb/if_kuereg.h new file mode 100644 index 00000000000..dba241d9dad --- /dev/null +++ b/sys/dev/usb/if_kuereg.h @@ -0,0 +1,181 @@ +/* $OpenBSD: if_kuereg.h,v 1.1 2000/03/26 18:49:44 aaron Exp $ */ +/* $NetBSD: if_kuereg.h,v 1.9 2000/03/24 22:13:24 augustss Exp $ */ +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. 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 Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD: src/sys/dev/usb/if_kuereg.h,v 1.2 2000/01/06 07:39:07 wpaul Exp $ + */ + +/* + * Definitions for the KLSI KL5KUSB101B USB to ethernet controller. + * The KLSI part is controlled via vendor control requests, the structure + * of which depend a bit on the firmware running on the internal + * microcontroller. The one exception is the 'send scan data' command, + * which is used to load the firmware. + */ + +#define KUE_CONFIG_NO 1 +#define KUE_IFACE_IDX 0 + +#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00 +#define KUE_CMD_SET_MCAST_FILTERS 0x01 +#define KUE_CMD_SET_PKT_FILTER 0x02 +#define KUE_CMD_GET_ETHERSTATS 0x03 +#define KUE_CMD_GET_GPIO 0x04 +#define KUE_CMD_SET_GPIO 0x05 +#define KUE_CMD_SET_MAC 0x06 +#define KUE_CMD_GET_MAC 0x07 +#define KUE_CMD_SET_URB_SIZE 0x08 +#define KUE_CMD_SET_SOFS 0x09 +#define KUE_CMD_SET_EVEN_PKTS 0x0A +#define KUE_CMD_SEND_SCAN 0xFF + +struct kue_ether_desc { + u_int8_t kue_len; + u_int8_t kue_rsvd0; + u_int8_t kue_rsvd1; + u_int8_t kue_macaddr[ETHER_ADDR_LEN]; + u_int8_t kue_etherstats[4]; + u_int8_t kue_maxseg[2]; + u_int8_t kue_mcastfilt[2]; + u_int8_t kue_rsvd2; +}; + +#define KUE_ETHERSTATS(x) \ + (*(u_int32_t *)&(x)->kue_desc.kue_etherstats) +#define KUE_MAXSEG(x) \ + (*(u_int16_t *)&(x)->kue_desc.kue_maxseg) +#define KUE_MCFILTCNT(x) \ + ((*(u_int16_t *)&(x)->kue_desc.kue_mcastfilt) & 0x7FFF) +#define KUE_MCFILT(x, y) \ + (char *)&(sc->kue_mcfilters[y * ETHER_ADDR_LEN]) + +#define KUE_STAT_TX_OK 0x00000001 +#define KUE_STAT_RX_OK 0x00000002 +#define KUE_STAT_TX_ERR 0x00000004 +#define KUE_STAT_RX_ERR 0x00000008 +#define KUE_STAT_RX_NOBUF 0x00000010 +#define KUE_STAT_TX_UCAST_BYTES 0x00000020 +#define KUE_STAT_TX_UCAST_FRAMES 0x00000040 +#define KUE_STAT_TX_MCAST_BYTES 0x00000080 +#define KUE_STAT_TX_MCAST_FRAMES 0x00000100 +#define KUE_STAT_TX_BCAST_BYTES 0x00000200 +#define KUE_STAT_TX_BCAST_FRAMES 0x00000400 +#define KUE_STAT_RX_UCAST_BYTES 0x00000800 +#define KUE_STAT_RX_UCAST_FRAMES 0x00001000 +#define KUE_STAT_RX_MCAST_BYTES 0x00002000 +#define KUE_STAT_RX_MCAST_FRAMES 0x00004000 +#define KUE_STAT_RX_BCAST_BYTES 0x00008000 +#define KUE_STAT_RX_BCAST_FRAMES 0x00010000 +#define KUE_STAT_RX_CRCERR 0x00020000 +#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 +#define KUE_STAT_RX_ALIGNERR 0x00080000 +#define KUE_STAT_TX_SINGLECOLL 0x00100000 +#define KUE_STAT_TX_MULTICOLL 0x00200000 +#define KUE_STAT_TX_DEFERRED 0x00400000 +#define KUE_STAT_TX_MAXCOLLS 0x00800000 +#define KUE_STAT_RX_OVERRUN 0x01000000 +#define KUE_STAT_TX_UNDERRUN 0x02000000 +#define KUE_STAT_TX_SQE_ERR 0x04000000 +#define KUE_STAT_TX_CARRLOSS 0x08000000 +#define KUE_STAT_RX_LATECOLL 0x10000000 + +#define KUE_RXFILT_PROMISC 0x0001 +#define KUE_RXFILT_ALLMULTI 0x0002 +#define KUE_RXFILT_UNICAST 0x0004 +#define KUE_RXFILT_BROADCAST 0x0008 +#define KUE_RXFILT_MULTICAST 0x0010 + +#define KUE_TIMEOUT 1000 +#define ETHER_ALIGN 2 +#define KUE_BUFSZ 1536 +#define KUE_MIN_FRAMELEN 60 + +#define KUE_RX_LIST_CNT 1 +#define KUE_TX_LIST_CNT 1 + +#define KUE_CTL_READ 0x01 +#define KUE_CTL_WRITE 0x02 + +/* + * The interrupt endpoint is currently unused + * by the KLSI part. + */ +#define KUE_ENDPT_RX 0x0 +#define KUE_ENDPT_TX 0x1 +#define KUE_ENDPT_INTR 0x2 +#define KUE_ENDPT_MAX 0x3 + +struct kue_type { + u_int16_t kue_vid; + u_int16_t kue_did; +}; + +struct kue_softc; + +struct kue_chain { + struct kue_softc *kue_sc; + usbd_xfer_handle kue_xfer; + char *kue_buf; + struct mbuf *kue_mbuf; + int kue_idx; +}; + +struct kue_cdata { + struct kue_chain kue_tx_chain[KUE_TX_LIST_CNT]; + struct kue_chain kue_rx_chain[KUE_RX_LIST_CNT]; + int kue_tx_prod; + int kue_tx_cons; + int kue_tx_cnt; + int kue_rx_prod; +}; + +struct kue_softc { + USBBASEDEVICE kue_dev; + + struct arpcom arpcom; + usbd_device_handle kue_udev; + usbd_interface_handle kue_iface; + u_int16_t kue_vendor; + u_int16_t kue_product; + struct kue_ether_desc kue_desc; + int kue_ed[KUE_ENDPT_MAX]; + usbd_pipe_handle kue_ep[KUE_ENDPT_MAX]; + int kue_if_flags; + u_int16_t kue_rxfilt; + u_int8_t *kue_mcfilters; + struct kue_cdata kue_cdata; + + char kue_dying; + char kue_attached; + u_int kue_rx_errs; + struct timeval kue_rx_notice; +}; -- 2.20.1