Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/usb Add usmsc(4) - a driver for the SMSC95XX USB eth...
details: https://anonhg.NetBSD.org/src/rev/902017837db7
branches: trunk
changeset: 783823:902017837db7
user: skrll <skrll%NetBSD.org@localhost>
date: Wed Jan 09 23:02:59 2013 +0000
description:
Add usmsc(4) - a driver for the SMSC95XX USB ethernet devices.
This is a port of the OpenBSD driver which itself was a port of the
FreeBSD driver.
sorry mrg. jared made me do it.
diffstat:
sys/dev/usb/files.usb | 7 +-
sys/dev/usb/if_smsc.c | 1502 +++++++++++++++++++++++++++++++++++++++++
sys/dev/usb/if_smscreg.h | 255 ++++++
sys/dev/usb/if_smscvar.h | 94 ++
sys/dev/usb/ucycom.c | 6 +-
sys/dev/usb/usbdevices.config | 3 +-
6 files changed, 1863 insertions(+), 4 deletions(-)
diffs (truncated from 1928 to 300 lines):
diff -r acd2c562ce25 -r 902017837db7 sys/dev/usb/files.usb
--- a/sys/dev/usb/files.usb Wed Jan 09 23:02:58 2013 +0000
+++ b/sys/dev/usb/files.usb Wed Jan 09 23:02:59 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.usb,v 1.125 2012/09/01 18:17:51 jakllsch Exp $
+# $NetBSD: files.usb,v 1.126 2013/01/09 23:02:59 skrll Exp $
#
# Config file and device description for machine-independent USB code.
# Included by ports that need it. Ports that use it must provide
@@ -378,6 +378,11 @@
attach upgt at usbdevif
file dev/usb/if_upgt.c upgt
+# SMSC LAN95xx
+device usmsc: arp, ether, ifnet, mii, mii_phy
+attach usmsc at usbdevif
+file dev/usb/if_smsc.c usmsc
+
# Ralink Technology RT2500USB
device ural: arp, ether, ifnet, wlan, firmload
attach ural at usbdevif
diff -r acd2c562ce25 -r 902017837db7 sys/dev/usb/if_smsc.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/dev/usb/if_smsc.c Wed Jan 09 23:02:59 2013 +0000
@@ -0,0 +1,1502 @@
+/* $NetBSD: if_smsc.c,v 1.1 2013/01/09 23:02:59 skrll Exp $ */
+
+/* $OpenBSD: if_smsc.c,v 1.4 2012/09/27 12:38:11 jsg Exp $ */
+/* $FreeBSD: src/sys/dev/usb/net/if_smsc.c,v 1.1 2012/08/15 04:03:55 gonzo Exp $ */
+/*-
+ * Copyright (c) 2012
+ * Ben Gray <bgray%freebsd.org@localhost>.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * SMSC LAN9xxx devices (http://www.smsc.com/)
+ *
+ * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that
+ * support USB 2.0 and 10/100 Mbps Ethernet.
+ *
+ * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter.
+ * The driver only covers the Ethernet part, the standard USB hub driver
+ * supports the hub part.
+ *
+ * This driver is closely modelled on the Linux driver written and copyrighted
+ * by SMSC.
+ *
+ * H/W TCP & UDP Checksum Offloading
+ * ---------------------------------
+ * The chip supports both tx and rx offloading of UDP & TCP checksums, this
+ * feature can be dynamically enabled/disabled.
+ *
+ * RX checksuming is performed across bytes after the IPv4 header to the end of
+ * the Ethernet frame, this means if the frame is padded with non-zero values
+ * the H/W checksum will be incorrect, however the rx code compensates for this.
+ *
+ * TX checksuming is more complicated, the device requires a special header to
+ * be prefixed onto the start of the frame which indicates the start and end
+ * positions of the UDP or TCP frame. This requires the driver to manually
+ * go through the packet data and decode the headers prior to sending.
+ * On Linux they generally provide cues to the location of the csum and the
+ * area to calculate it over, on FreeBSD we seem to have to do it all ourselves,
+ * hence this is not as optimal and therefore h/w tX checksum is currently not
+ * implemented.
+ */
+
+#include "vlan.h"
+#include "opt_usb.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+
+#include <sys/device.h>
+
+#include <sys/rnd.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_ether.h>
+
+#include <net/bpf.h>
+
+#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>
+#endif
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/if_smscreg.h>
+#include <dev/usb/if_smscvar.h>
+
+#include "ioconf.h"
+
+#ifdef USB_DEBUG
+int smsc_debug = 0;
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb_devno smsc_devs[] = {
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN89530 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN9530 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN9730 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_ALT },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_HAL },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_SAL10 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500_ALT },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500_SAL10 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A_HAL },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A_SAL10 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505_SAL10 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14 },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14_ALT },
+ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14_SAL10 }
+};
+
+#ifdef USB_DEBUG
+#define smsc_dbg_printf(sc, fmt, args...) \
+ do { \
+ if (smsc_debug > 0) \
+ printf("debug: " fmt, ##args); \
+ } while(0)
+#else
+#define smsc_dbg_printf(sc, fmt, args...)
+#endif
+
+#define smsc_warn_printf(sc, fmt, args...) \
+ printf("%s: warning: " fmt, device_xname((sc)->sc_dev), ##args)
+
+#define smsc_err_printf(sc, fmt, args...) \
+ printf("%s: error: " fmt, device_xname((sc)->sc_dev), ##args)
+
+/* Function declarations */
+int smsc_chip_init(struct smsc_softc *);
+void smsc_setmulti(struct smsc_softc *);
+int smsc_setmacaddress(struct smsc_softc *, const uint8_t *);
+
+int smsc_match(device_t, cfdata_t, void *);
+void smsc_attach(device_t, device_t, void *);
+int smsc_detach(device_t, int);
+int smsc_activate(device_t, enum devact);
+
+int smsc_init(struct ifnet *);
+void smsc_start(struct ifnet *);
+int smsc_ioctl(struct ifnet *, u_long, void *);
+void smsc_stop(struct ifnet *, int);
+
+void smsc_reset(struct smsc_softc *);
+struct mbuf *smsc_newbuf(void);
+
+void smsc_tick(void *);
+void smsc_tick_task(void *);
+void smsc_miibus_statchg(struct ifnet *);
+int smsc_miibus_readreg(device_t, int, int);
+void smsc_miibus_writereg(device_t, int, int, int);
+int smsc_ifmedia_upd(struct ifnet *);
+void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+void smsc_lock_mii(struct smsc_softc *);
+void smsc_unlock_mii(struct smsc_softc *);
+
+int smsc_tx_list_init(struct smsc_softc *);
+int smsc_rx_list_init(struct smsc_softc *);
+int smsc_encap(struct smsc_softc *, struct mbuf *, int);
+void smsc_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+void smsc_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+int smsc_read_reg(struct smsc_softc *, uint32_t, uint32_t *);
+int smsc_write_reg(struct smsc_softc *, uint32_t, uint32_t);
+int smsc_wait_for_bits(struct smsc_softc *, uint32_t, uint32_t);
+int smsc_sethwcsum(struct smsc_softc *);
+
+CFATTACH_DECL_NEW(usmsc, sizeof(struct smsc_softc), smsc_match, smsc_attach,
+ smsc_detach, smsc_activate);
+
+int
+smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data)
+{
+ usb_device_request_t req;
+ uint32_t buf;
+ usbd_status err;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = SMSC_UR_READ_REG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, off);
+ USETW(req.wLength, 4);
+
+ err = usbd_do_request(sc->sc_udev, &req, &buf);
+ if (err != 0)
+ smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off);
+
+ *data = le32toh(buf);
+
+ return (err);
+}
+
+int
+smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data)
+{
+ usb_device_request_t req;
+ uint32_t buf;
+ usbd_status err;
+
+ buf = htole32(data);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = SMSC_UR_WRITE_REG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, off);
+ USETW(req.wLength, 4);
+
+ err = usbd_do_request(sc->sc_udev, &req, &buf);
+ if (err != 0)
+ smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off);
+
+ return (err);
+}
+
+int
+smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
+{
+ uint32_t val;
+ int err, i;
+
+ for (i = 0; i < 100; i++) {
+ if ((err = smsc_read_reg(sc, reg, &val)) != 0)
+ return (err);
+ if (!(val & bits))
+ return (0);
+ DELAY(5);
+ }
+
+ return (1);
+}
+
+int
+smsc_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct smsc_softc *sc = device_private(dev);
+ uint32_t addr;
+ uint32_t val = 0;
+
+ smsc_lock_mii(sc);
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
+ smsc_warn_printf(sc, "MII is busy\n");
+ goto done;
+ }
+
+ addr = (phy << 11) | (reg << 6) | SMSC_MII_READ;
+ smsc_write_reg(sc, SMSC_MII_ADDR, addr);
+
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
+ smsc_warn_printf(sc, "MII read timeout\n");
+
+ smsc_read_reg(sc, SMSC_MII_DATA, &val);
Home |
Main Index |
Thread Index |
Old Index