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