summaryrefslogtreecommitdiff
path: root/sys/dev/usb/if_upl.c
diff options
context:
space:
mode:
authorAaron Campbell <aaron@cvs.openbsd.org>2001-01-28 17:45:20 +0000
committerAaron Campbell <aaron@cvs.openbsd.org>2001-01-28 17:45:20 +0000
commit96a358cd69059369aed1936438b4ad559c8718ae (patch)
treebcdab9d879d5990c0754a24446952f43396ac16e /sys/dev/usb/if_upl.c
parentfb1d49e57899e8b95880ed53ab3ab227c291b17e (diff)
Add driver for Prolific PL2301/PL2302 host-to-host adapter. From NetBSD.
/* XXX - untested */ Note: just reminding myself that I need to create a nice diff for Lennart to further portablize the USB code (I fell behind in doing this). In particular, for this driver, the IF_INPUT() macro just does ether_input() in OpenBSD, but we really want to call the local upl_input() routine.
Diffstat (limited to 'sys/dev/usb/if_upl.c')
-rw-r--r--sys/dev/usb/if_upl.c1116
1 files changed, 1116 insertions, 0 deletions
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
new file mode 100644
index 00000000000..6432ea66dcc
--- /dev/null
+++ b/sys/dev/usb/if_upl.c
@@ -0,0 +1,1116 @@
+/* $OpenBSD: if_upl.c,v 1.1 2001/01/28 17:45:18 aaron Exp $ */
+/* $NetBSD: if_upl.c,v 1.10 2000/12/08 02:24:07 augustss Exp $ */
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * 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 the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * Prolific PL2301/PL2302 driver
+ */
+
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#include "opt_ns.h"
+#include "rnd.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/callout.h>
+#else
+#include <sys/timeout.h>
+#endif
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.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_types.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#define BPF_MTAP(ifp, m) bpf_mtap((ifp)->if_bpf, (m))
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#if defined(__NetBSD__)
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_inarp.h>
+#else
+#error upl without INET?
+#endif
+#endif
+
+#if defined(__OpenBSD__)
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#else
+#error upl without INET?
+#endif
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+/*
+ * 7 6 5 4 3 2 1 0
+ * tx rx 1 0
+ * 1110 0000 rxdata
+ * 1010 0000 idle
+ * 0010 0000 tx over
+ * 0110 tx over + rxd
+ */
+
+#define UPL_RXDATA 0x40
+#define UPL_TXOK 0x80
+
+#define UPL_INTR_PKTLEN 1
+
+#define UPL_CONFIG_NO 1
+#define UPL_IFACE_IDX 0
+
+/***/
+
+#define UPL_INTR_INTERVAL 20
+
+#define UPL_BUFSZ 1024
+
+#define UPL_RX_FRAMES 1
+#define UPL_TX_FRAMES 1
+
+#define UPL_RX_LIST_CNT 1
+#define UPL_TX_LIST_CNT 1
+
+#define UPL_ENDPT_RX 0x0
+#define UPL_ENDPT_TX 0x1
+#define UPL_ENDPT_INTR 0x2
+#define UPL_ENDPT_MAX 0x3
+
+struct upl_type {
+ u_int16_t upl_vid;
+ u_int16_t upl_did;
+};
+
+struct upl_softc;
+
+struct upl_chain {
+ struct upl_softc *upl_sc;
+ usbd_xfer_handle upl_xfer;
+ char *upl_buf;
+ struct mbuf *upl_mbuf;
+ int upl_idx;
+};
+
+struct upl_cdata {
+ struct upl_chain upl_tx_chain[UPL_TX_LIST_CNT];
+ struct upl_chain upl_rx_chain[UPL_RX_LIST_CNT];
+ int upl_tx_prod;
+ int upl_tx_cons;
+ int upl_tx_cnt;
+ int upl_rx_prod;
+};
+
+struct upl_softc {
+ USBBASEDEVICE sc_dev;
+
+ struct ifnet sc_if;
+#if NRND > 0
+ rndsource_element_t sc_rnd_source;
+#endif
+
+ usb_callout_t sc_stat_ch;
+
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ u_int16_t sc_vendor;
+ u_int16_t sc_product;
+ int sc_ed[UPL_ENDPT_MAX];
+ usbd_pipe_handle sc_ep[UPL_ENDPT_MAX];
+ struct upl_cdata sc_cdata;
+
+ uByte sc_ibuf;
+
+ char sc_dying;
+ char sc_attached;
+ u_int sc_rx_errs;
+ struct timeval sc_rx_notice;
+ u_int sc_intr_errs;
+};
+
+#ifdef UPL_DEBUG
+#define DPRINTF(x) if (upldebug) logprintf x
+#define DPRINTFN(n,x) if (upldebug >= (n)) logprintf x
+int upldebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+Static struct upl_type sc_devs[] = {
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301 },
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302 },
+ { 0, 0 }
+};
+
+USB_DECLARE_DRIVER(upl);
+
+Static int upl_openpipes(struct upl_softc *);
+Static int upl_tx_list_init(struct upl_softc *);
+Static int upl_rx_list_init(struct upl_softc *);
+Static int upl_newbuf(struct upl_softc *, struct upl_chain *, struct mbuf *);
+Static int upl_send(struct upl_softc *, struct mbuf *, int);
+Static void upl_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void upl_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void upl_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void upl_start(struct ifnet *);
+Static int upl_ioctl(struct ifnet *, u_long, caddr_t);
+Static void upl_init(void *);
+Static void upl_stop(struct upl_softc *);
+Static void upl_watchdog(struct ifnet *);
+
+Static int upl_output(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+Static void upl_input(struct ifnet *, struct mbuf *);
+
+/*
+ * Probe for a Prolific chip.
+ */
+USB_MATCH(upl)
+{
+ USB_MATCH_START(upl, uaa);
+ struct upl_type *t;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (t = sc_devs; t->upl_vid != 0; t++)
+ if (uaa->vendor == t->upl_vid && uaa->product == t->upl_did)
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(upl)
+{
+ USB_ATTACH_START(upl, sc, uaa);
+ char devinfo[1024];
+ int s;
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usbd_status err;
+ struct ifnet *ifp;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ DPRINTFN(5,(" : upl_attach: sc=%p, dev=%p", sc, dev));
+
+ usbd_devinfo(dev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ err = usbd_set_config_no(dev, UPL_CONFIG_NO, 1);
+ if (err) {
+ printf("%s: setting config no failed\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_udev = dev;
+ sc->sc_product = uaa->product;
+ sc->sc_vendor = uaa->vendor;
+
+ err = usbd_device2interface_handle(dev, UPL_IFACE_IDX, &iface);
+ if (err) {
+ printf("%s: getting interface handle failed\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_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->sc_dev), i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_ed[UPL_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_ed[UPL_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_ed[UPL_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ if (sc->sc_ed[UPL_ENDPT_RX] == 0 || sc->sc_ed[UPL_ENDPT_TX] == 0 ||
+ sc->sc_ed[UPL_ENDPT_INTR] == 0) {
+ printf("%s: missing endpoint\n", USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ s = splimp();
+
+ /* Initialize interface info.*/
+ ifp = &sc->sc_if;
+ ifp->if_softc = sc;
+ ifp->if_mtu = UPL_BUFSZ;
+ ifp->if_flags = IFF_POINTOPOINT | IFF_NOARP | IFF_SIMPLEX;
+ ifp->if_ioctl = upl_ioctl;
+ ifp->if_start = upl_start;
+ ifp->if_watchdog = upl_watchdog;
+ strncpy(ifp->if_xname, USBDEVNAME(sc->sc_dev), IFNAMSIZ);
+
+ ifp->if_type = IFT_OTHER;
+ ifp->if_addrlen = 0;
+ ifp->if_hdrlen = 0;
+ ifp->if_output = upl_output;
+#if defined(__NetBSD__)
+ ifp->if_input = upl_input;
+#endif
+ ifp->if_baudrate = 12000000;
+
+ /* Attach the interface. */
+ if_attach(ifp);
+
+#if NBPFILTER > 0
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+ bpfattach(ifp, DLT_EN10MB, 0);
+#else
+ bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+#endif
+#if NRND > 0
+ rnd_attach_source(&sc->sc_rnd_source, USBDEVNAME(sc->sc_dev),
+ RND_TYPE_NET, 0);
+#endif
+
+ sc->sc_attached = 1;
+ splx(s);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+USB_DETACH(upl)
+{
+ USB_DETACH_START(upl, sc);
+ struct ifnet *ifp = &sc->sc_if;
+ int s;
+
+ DPRINTFN(2,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__));
+
+ s = splusb();
+
+ if (!sc->sc_attached) {
+ /* Detached before attached finished, so just bail out. */
+ splx(s);
+ return (0);
+ }
+
+ if (ifp->if_flags & IFF_RUNNING)
+ upl_stop(sc);
+
+#if NRND > 0
+ rnd_detach_source(&sc->sc_rnd_source);
+#endif
+#if NBPFILTER > 0
+ bpfdetach(ifp);
+#endif
+
+ if_detach(ifp);
+
+#ifdef DIAGNOSTIC
+ if (sc->sc_ep[UPL_ENDPT_TX] != NULL ||
+ sc->sc_ep[UPL_ENDPT_RX] != NULL ||
+ sc->sc_ep[UPL_ENDPT_INTR] != NULL)
+ printf("%s: detach has active endpoints\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);
+}
+
+int
+upl_activate(device_ptr_t self, enum devact act)
+{
+ struct upl_softc *sc = (struct upl_softc *)self;
+
+ DPRINTFN(2,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__));
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ /* Deactivate the interface. */
+ if_deactivate(&sc->sc_if);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+Static int
+upl_newbuf(struct upl_softc *sc, struct upl_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ DPRINTFN(8,("%s: %s: enter\n", USBDEVNAME(sc->sc_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->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;
+ }
+
+ c->upl_mbuf = m_new;
+
+ return (0);
+}
+
+Static int
+upl_rx_list_init(struct upl_softc *sc)
+{
+ struct upl_cdata *cd;
+ struct upl_chain *c;
+ int i;
+
+ DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__));
+
+ cd = &sc->sc_cdata;
+ for (i = 0; i < UPL_RX_LIST_CNT; i++) {
+ c = &cd->upl_rx_chain[i];
+ c->upl_sc = sc;
+ c->upl_idx = i;
+ if (upl_newbuf(sc, c, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ if (c->upl_xfer == NULL) {
+ c->upl_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (c->upl_xfer == NULL)
+ return (ENOBUFS);
+ c->upl_buf = usbd_alloc_buffer(c->upl_xfer, UPL_BUFSZ);
+ if (c->upl_buf == NULL) {
+ usbd_free_xfer(c->upl_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+Static int
+upl_tx_list_init(struct upl_softc *sc)
+{
+ struct upl_cdata *cd;
+ struct upl_chain *c;
+ int i;
+
+ DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __FUNCTION__));
+
+ cd = &sc->sc_cdata;
+ for (i = 0; i < UPL_TX_LIST_CNT; i++) {
+ c = &cd->upl_tx_chain[i];
+ c->upl_sc = sc;
+ c->upl_idx = i;
+ c->upl_mbuf = NULL;
+ if (c->upl_xfer == NULL) {
+ c->upl_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (c->upl_xfer == NULL)
+ return (ENOBUFS);
+ c->upl_buf = usbd_alloc_buffer(c->upl_xfer, UPL_BUFSZ);
+ if (c->upl_buf == NULL) {
+ usbd_free_xfer(c->upl_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+Static void
+upl_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upl_chain *c = priv;
+ struct upl_softc *sc = c->upl_sc;
+ struct ifnet *ifp = &sc->sc_if;
+ struct mbuf *m;
+ int total_len = 0;
+ int s;
+
+ if (sc->sc_dying)
+ return;
+
+ if (!(ifp->if_flags & IFF_RUNNING))
+ 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)
+ usbd_clear_endpoint_stall(sc->sc_ep[UPL_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ DPRINTFN(9,("%s: %s: enter status=%d length=%d\n",
+ USBDEVNAME(sc->sc_dev), __FUNCTION__, status, total_len));
+
+ m = c->upl_mbuf;
+ memcpy(mtod(c->upl_mbuf, char *), c->upl_buf, total_len);
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ m->m_pkthdr.rcvif = ifp;
+
+ s = splimp();
+
+ /* XXX ugly */
+ if (upl_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ goto done1;
+ }
+
+#if NBPFILTER > 0
+ /*
+ * Handle BPF listeners. Let the BPF user see the packet, but
+ * don't pass it up to the ether_input() layer unless it's
+ * a broadcast packet, multicast packet, matches our ethernet
+ * address or the interface is in promiscuous mode.
+ */
+ if (ifp->if_bpf) {
+ BPF_MTAP(ifp, m);
+ }
+#endif
+
+ DPRINTFN(10,("%s: %s: deliver %d\n", USBDEVNAME(sc->sc_dev),
+ __FUNCTION__, m->m_len));
+
+#if defined(__NetBSD__)
+ IF_INPUT(ifp, m);
+#else
+ upl_input(ifp, m);
+#endif
+
+ done1:
+ splx(s);
+
+ done:
+#if 1
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->upl_xfer, sc->sc_ep[UPL_ENDPT_RX],
+ c, c->upl_buf, UPL_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, upl_rxeof);
+ usbd_transfer(c->upl_xfer);
+
+ DPRINTFN(10,("%s: %s: start rx\n", USBDEVNAME(sc->sc_dev),
+ __FUNCTION__));
+#endif
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+Static void
+upl_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upl_chain *c = priv;
+ struct upl_softc *sc = c->upl_sc;
+ struct ifnet *ifp = &sc->sc_if;
+ int s;
+
+ if (sc->sc_dying)
+ return;
+
+ s = splimp();
+
+ DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->sc_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->sc_dev),
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->sc_ep[UPL_ENDPT_TX]);
+ splx(s);
+ return;
+ }
+
+ ifp->if_opackets++;
+
+ m_freem(c->upl_mbuf);
+ c->upl_mbuf = NULL;
+
+ if (ifp->if_snd.ifq_head != NULL)
+ upl_start(ifp);
+
+ splx(s);
+}
+
+Static int
+upl_send(struct upl_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct upl_chain *c;
+ usbd_status err;
+
+ c = &sc->sc_cdata.upl_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->upl_buf);
+ c->upl_mbuf = m;
+
+ total_len = m->m_pkthdr.len;
+
+ DPRINTFN(10,("%s: %s: total_len=%d\n",
+ USBDEVNAME(sc->sc_dev), __FUNCTION__, total_len));
+
+ usbd_setup_xfer(c->upl_xfer, sc->sc_ep[UPL_ENDPT_TX],
+ c, c->upl_buf, total_len, USBD_NO_COPY, USBD_DEFAULT_TIMEOUT,
+ upl_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->upl_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ printf("%s: upl_send error=%s\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(err));
+ upl_stop(sc);
+ return (EIO);
+ }
+
+ sc->sc_cdata.upl_tx_cnt++;
+
+ return (0);
+}
+
+Static void
+upl_start(struct ifnet *ifp)
+{
+ struct upl_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ if (sc->sc_dying)
+ return;
+
+ DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__));
+
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL)
+ return;
+
+ if (upl_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, m_head);
+#endif
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+}
+
+Static void
+upl_init(void *xsc)
+{
+ struct upl_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ int s;
+
+ if (sc->sc_dying)
+ return;
+
+ DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__));
+
+ if (ifp->if_flags & IFF_RUNNING)
+ return;
+
+ s = splimp();
+
+ /* Init TX ring. */
+ if (upl_tx_list_init(sc) == ENOBUFS) {
+ printf("%s: tx list init failed\n", USBDEVNAME(sc->sc_dev));
+ splx(s);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (upl_rx_list_init(sc) == ENOBUFS) {
+ printf("%s: rx list init failed\n", USBDEVNAME(sc->sc_dev));
+ splx(s);
+ return;
+ }
+
+ if (sc->sc_ep[UPL_ENDPT_RX] == NULL) {
+ if (upl_openpipes(sc)) {
+ splx(s);
+ return;
+ }
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ splx(s);
+}
+
+Static int
+upl_openpipes(struct upl_softc *sc)
+{
+ struct upl_chain *c;
+ usbd_status err;
+ int i;
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UPL_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->sc_ep[UPL_ENDPT_RX]);
+ if (err) {
+ printf("%s: open rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ return (EIO);
+ }
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UPL_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->sc_ep[UPL_ENDPT_TX]);
+ if (err) {
+ printf("%s: open tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ return (EIO);
+ }
+ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UPL_ENDPT_INTR],
+ USBD_EXCLUSIVE_USE, &sc->sc_ep[UPL_ENDPT_INTR], sc,
+ &sc->sc_ibuf, UPL_INTR_PKTLEN, upl_intr,
+ UPL_INTR_INTERVAL);
+ if (err) {
+ printf("%s: open intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ return (EIO);
+ }
+
+
+#if 1
+ /* Start up the receive pipe. */
+ for (i = 0; i < UPL_RX_LIST_CNT; i++) {
+ c = &sc->sc_cdata.upl_rx_chain[i];
+ usbd_setup_xfer(c->upl_xfer, sc->sc_ep[UPL_ENDPT_RX],
+ c, c->upl_buf, UPL_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ upl_rxeof);
+ usbd_transfer(c->upl_xfer);
+ }
+#endif
+
+ return (0);
+}
+
+Static void
+upl_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upl_softc *sc = priv;
+ struct ifnet *ifp = &sc->sc_if;
+ uByte stat;
+
+ DPRINTFN(15,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__));
+
+ if (sc->sc_dying)
+ return;
+
+ if (!(ifp->if_flags & IFF_RUNNING))
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ return;
+ }
+ sc->sc_intr_errs++;
+ if (usbd_ratecheck(&sc->sc_rx_notice)) {
+ printf("%s: %u usb errors on intr: %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_rx_errs,
+ usbd_errstr(status));
+ sc->sc_intr_errs = 0;
+ }
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->sc_ep[UPL_ENDPT_RX]);
+ return;
+ }
+
+ stat = sc->sc_ibuf;
+
+ if (stat == 0)
+ return;
+
+ DPRINTFN(10,("%s: %s: stat=0x%02x\n", USBDEVNAME(sc->sc_dev),
+ __FUNCTION__, stat));
+
+}
+
+Static int
+upl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct upl_softc *sc = ifp->if_softc;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTFN(5,("%s: %s: cmd=0x%08lx\n",
+ USBDEVNAME(sc->sc_dev), __FUNCTION__, command));
+
+ s = splimp();
+
+ switch(command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ upl_init(sc);
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ 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 *)
+ LLADDR(ifp->if_sadl);
+ else
+ memcpy(LLADDR(ifp->if_sadl),
+ ina->x_host.c_host,
+ ifp->if_addrlen);
+ break;
+ }
+#endif /* NS */
+ }
+ break;
+
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > UPL_BUFSZ)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING))
+ upl_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ upl_stop(sc);
+ }
+ error = 0;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+Static void
+upl_watchdog(struct ifnet *ifp)
+{
+ struct upl_softc *sc = ifp->if_softc;
+
+ DPRINTFN(5,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__));
+
+ if (sc->sc_dying)
+ return;
+
+ ifp->if_oerrors++;
+ printf("%s: watchdog timeout\n", USBDEVNAME(sc->sc_dev));
+
+ upl_stop(sc);
+ upl_init(sc);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ upl_start(ifp);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+Static void
+upl_stop(struct upl_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__FUNCTION__));
+
+ ifp = &sc->sc_if;
+ ifp->if_timer = 0;
+
+ /* Stop transfers. */
+ if (sc->sc_ep[UPL_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->sc_ep[UPL_ENDPT_RX]);
+ if (err) {
+ printf("%s: abort rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->sc_ep[UPL_ENDPT_RX]);
+ if (err) {
+ printf("%s: close rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ sc->sc_ep[UPL_ENDPT_RX] = NULL;
+ }
+
+ if (sc->sc_ep[UPL_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->sc_ep[UPL_ENDPT_TX]);
+ if (err) {
+ printf("%s: abort tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->sc_ep[UPL_ENDPT_TX]);
+ if (err) {
+ printf("%s: close tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ sc->sc_ep[UPL_ENDPT_TX] = NULL;
+ }
+
+ if (sc->sc_ep[UPL_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->sc_ep[UPL_ENDPT_INTR]);
+ if (err) {
+ printf("%s: abort intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->sc_ep[UPL_ENDPT_INTR]);
+ if (err) {
+ printf("%s: close intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ }
+ sc->sc_ep[UPL_ENDPT_INTR] = NULL;
+ }
+
+ /* Free RX resources. */
+ for (i = 0; i < UPL_RX_LIST_CNT; i++) {
+ if (sc->sc_cdata.upl_rx_chain[i].upl_mbuf != NULL) {
+ m_freem(sc->sc_cdata.upl_rx_chain[i].upl_mbuf);
+ sc->sc_cdata.upl_rx_chain[i].upl_mbuf = NULL;
+ }
+ if (sc->sc_cdata.upl_rx_chain[i].upl_xfer != NULL) {
+ usbd_free_xfer(sc->sc_cdata.upl_rx_chain[i].upl_xfer);
+ sc->sc_cdata.upl_rx_chain[i].upl_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < UPL_TX_LIST_CNT; i++) {
+ if (sc->sc_cdata.upl_tx_chain[i].upl_mbuf != NULL) {
+ m_freem(sc->sc_cdata.upl_tx_chain[i].upl_mbuf);
+ sc->sc_cdata.upl_tx_chain[i].upl_mbuf = NULL;
+ }
+ if (sc->sc_cdata.upl_tx_chain[i].upl_xfer != NULL) {
+ usbd_free_xfer(sc->sc_cdata.upl_tx_chain[i].upl_xfer);
+ sc->sc_cdata.upl_tx_chain[i].upl_xfer = NULL;
+ }
+ }
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+}
+
+Static int
+upl_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt0)
+{
+ int s;
+
+ DPRINTFN(10,("%s: %s: enter\n",
+ USBDEVNAME(((struct upl_softc *)ifp->if_softc)->sc_dev),
+ __FUNCTION__));
+
+ s = splimp();
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(s);
+ return (ENOBUFS);
+ }
+ ifp->if_obytes += m->m_pkthdr.len;
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ (*ifp->if_start)(ifp);
+ splx(s);
+
+ return (0);
+}
+
+Static void
+upl_input(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ifqueue *inq;
+ int s;
+
+ /* XXX Assume all traffic is IP */
+
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+
+ s = splimp();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(s);
+#if 0
+ if (sc->sc_flags & SC_DEBUG)
+ printf("%s: input queue full\n", ifp->if_xname);
+#endif
+ ifp->if_iqdrops++;
+ return;
+ }
+ IF_ENQUEUE(inq, m);
+ splx(s);
+ ifp->if_ipackets++;
+ ifp->if_ibytes += m->m_len;
+ ifp->if_lastchange = time;
+}