From owner-svn-src-projects@FreeBSD.ORG Mon Apr 9 17:33:35 2012 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id AB690106568E; Mon, 9 Apr 2012 17:33:35 +0000 (UTC) (envelope-from bgray@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 954568FC08; Mon, 9 Apr 2012 17:33:35 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q39HXZEO071063; Mon, 9 Apr 2012 17:33:35 GMT (envelope-from bgray@svn.freebsd.org) Received: (from bgray@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q39HXZja071060; Mon, 9 Apr 2012 17:33:35 GMT (envelope-from bgray@svn.freebsd.org) Message-Id: <201204091733.q39HXZja071060@svn.freebsd.org> From: Ben Gray Date: Mon, 9 Apr 2012 17:33:35 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r234065 - projects/armv6/sys/dev/usb/net X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Apr 2012 17:33:35 -0000 Author: bgray Date: Mon Apr 9 17:33:35 2012 New Revision: 234065 URL: http://svn.freebsd.org/changeset/base/234065 Log: Merged in SMSC driver changes, includes support for H/W checksuming and multiple frames in the RX/TX urbs. Modified: projects/armv6/sys/dev/usb/net/if_smsc.c projects/armv6/sys/dev/usb/net/if_smscreg.h Modified: projects/armv6/sys/dev/usb/net/if_smsc.c ============================================================================== --- projects/armv6/sys/dev/usb/net/if_smsc.c Mon Apr 9 17:05:18 2012 (r234064) +++ projects/armv6/sys/dev/usb/net/if_smsc.c Mon Apr 9 17:33:35 2012 (r234065) @@ -1,6 +1,6 @@ /*- - * Copyright (c) 2011 - * Ben Gray . + * Copyright (c) 2012 + * Ben Gray . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,30 +11,57 @@ * 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. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR + * 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 BEN GRAY 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. + * 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. */ #include __FBSDID("$FreeBSD$"); /* - * SMSC LAN9xxx devices. + * 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 #include #include @@ -53,6 +80,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -63,22 +91,9 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include #include - -/* - * From looking at the Linux SMSC logs I believe the LAN95xx devices have - * the following endpoints: - * Endpoints In 1 Out 2 Int 3 - * - */ -enum { - SMSC_BULK_DT_RD, - SMSC_BULK_DT_WR, - SMSC_INTR_DT_RD, - SMSC_N_TRANSFER, -}; +#include +#include "if_smscreg.h" #ifdef USB_DEBUG static int smsc_debug = 0; @@ -97,26 +112,32 @@ static const struct usb_device_id smsc_d #undef SMSC_DEV }; -struct smsc_softc { - struct usb_ether sc_ue; - struct mtx sc_mtx; - struct usb_xfer *sc_xfer[SMSC_N_TRANSFER]; - int sc_phyno; - - /* The following stores the settings in the mac control (SMSC_REG_MAC_CR) register */ - uint32_t sc_mac_cr; - - uint32_t sc_flags; -#define SMSC_FLAG_LINK 0x0001 -#define SMSC_FLAG_LAN9514 0x1000 /* LAN9514 */ -}; +#ifdef USB_DEBUG +#define smsc_dbg_printf(sc, fmt, args...) \ + do { \ + if (smsc_debug > 0) \ + device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ + } while(0) +#else +#define smsc_dbg_printf(sc, fmt, args...) +#endif + +#define smsc_warn_printf(sc, fmt, args...) \ + device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) + +#define smsc_err_printf(sc, fmt, args...) \ + device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args) + + +#define ETHER_IS_ZERO(addr) \ + (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) + +#define ETHER_IS_VALID(addr) \ + (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr)) + -#define SMSC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define SMSC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define SMSC_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) -#define SMSC_TIMEOUT 100 /* 10*ms */ static device_probe_t smsc_probe; static device_attach_t smsc_attach; @@ -124,12 +145,14 @@ static device_detach_t smsc_detach; static usb_callback_t smsc_bulk_read_callback; static usb_callback_t smsc_bulk_write_callback; -static usb_callback_t smsc_intr_callback; static miibus_readreg_t smsc_miibus_readreg; static miibus_writereg_t smsc_miibus_writereg; static miibus_statchg_t smsc_miibus_statchg; +#if __FreeBSD_version > 1000000 +static int smsc_attach_post_sub(struct usb_ether *ue); +#endif static uether_fn_t smsc_attach_post; static uether_fn_t smsc_init; static uether_fn_t smsc_stop; @@ -142,6 +165,7 @@ static int smsc_ifmedia_upd(struct ifnet static void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int smsc_chip_init(struct smsc_softc *sc); +static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static const struct usb_config smsc_config[SMSC_N_TRANSFER] = { @@ -150,7 +174,7 @@ static const struct usb_config smsc_conf .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .frames = 16, - .bufsize = 16 * MCLBYTES, + .bufsize = 16 * (MCLBYTES + 16), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = smsc_bulk_write_callback, .timeout = 10000, /* 10 seconds */ @@ -160,25 +184,24 @@ static const struct usb_config smsc_conf .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, - .bufsize = 18944, /* bytes */ + .bufsize = 20480, /* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = smsc_bulk_read_callback, .timeout = 0, /* no timeout */ }, - [SMSC_INTR_DT_RD] = { - .type = UE_INTERRUPT, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .bufsize = 0, /* use wMaxPacketSize */ - .callback = smsc_intr_callback, - }, + /* The SMSC chip supports an interrupt endpoints, however they aren't + * needed as we poll on the MII status. + */ }; static const struct usb_ether_methods smsc_ue_methods = { .ue_attach_post = smsc_attach_post, +#if __FreeBSD_version > 1000000 + .ue_attach_post_sub = smsc_attach_post_sub, +#endif .ue_start = smsc_start, + .ue_ioctl = smsc_ioctl, .ue_init = smsc_init, .ue_stop = smsc_stop, .ue_tick = smsc_tick, @@ -191,14 +214,17 @@ static const struct usb_ether_methods sm /** * smsc_read_reg - Reads a 32-bit register on the device * @sc: driver soft context - * + * @off: offset of the register + * @data: pointer a value that will be populated with the register value * + * LOCKING: + * The device lock must be held before calling this function. * * RETURNS: - * Register value or 0 if read failed + * 0 on success, a USB_ERR_?? error code on failure. */ -static uint32_t -smsc_read_reg(struct smsc_softc *sc, uint32_t off) +static int +smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data) { struct usb_device_request req; uint32_t buf; @@ -207,30 +233,33 @@ smsc_read_reg(struct smsc_softc *sc, uin SMSC_LOCK_ASSERT(sc, MA_OWNED); req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = SMSC_UR_READ; + req.bRequest = SMSC_UR_READ_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); - if (err != 0) { - device_printf(sc->sc_ue.ue_dev, "Failed to read register 0x%0x, err = %d\n", off, err); - return (0); - } + if (err != 0) + smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off); - return le32toh(buf); + *data = le32toh(buf); + + return (err); } /** - * smsc_write_reg - Reads a 32-bit register on the device + * smsc_write_reg - Writes a 32-bit register on the device * @sc: driver soft context - * + * @off: offset of the register + * @data: the 32-bit value to write into the register * + * LOCKING: + * The device lock must be held before calling this function. * * RETURNS: - * Nothing + * 0 on success, a USB_ERR_?? error code on failure. */ -static void +static int smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data) { struct usb_device_request req; @@ -242,153 +271,139 @@ smsc_write_reg(struct smsc_softc *sc, ui buf = htole32(data); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - req.bRequest = SMSC_UR_WRITE; + req.bRequest = SMSC_UR_WRITE_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); if (err != 0) - device_printf(sc->sc_ue.ue_dev, "Failed to write register 0x%0x, err = %d\n", off, err); + smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off); + return (err); } + /** - * smsc_wait_for_bits - Reads data from eeprom - * @sc: driver soft context - * @reg: register number - * @bits: bit to wait for to clear + * smsc_wait_for_bits - Polls on a register value until bits are cleared + * @sc: soft context + * @reg: offset of the register + * @bits: if the bits are clear the function returns + * + * LOCKING: + * The device lock must be held before calling this function. * * RETURNS: - * 0 if succeeded, -1 if timed out + * 0 on success, or a USB_ERR_?? error code on failure. */ static int smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits) { - int i; + usb_ticks_t start_ticks; + usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); uint32_t val; + int err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); - for (i = 0; i != SMSC_TIMEOUT; i++) { - val = smsc_read_reg(sc, SMSC_REG_E2P_CMD); + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = smsc_read_reg(sc, reg, &val)) != 0) + return (err); if (!(val & bits)) - break; - if (uether_pause(&sc->sc_ue, hz / 100)) - break; - } - - if (i == SMSC_TIMEOUT) - return (-1); + return (0); + + uether_pause(&sc->sc_ue, hz / 100); + } while ((ticks - start_ticks) < max_ticks); - return (0); + return (USB_ERR_TIMEOUT); } /** - * smsc_read_eeprom - Reads data from eeprom - * @sc: driver soft context - * @off: EEPROM offset - * @data: memory to read data to - * @length: read length bytes + * smsc_eeprom_read - Reads the attached EEPROM + * @sc: soft context + * @off: the eeprom address offset + * @buf: stores the bytes + * @buflen: the number of bytes to read * - * + * Simply reads bytes from an attached eeprom. + * + * LOCKING: + * The function takes and releases the device lock if it is not already held. * * RETURNS: - * 0 on success, -1 otherwise + * 0 on success, or a USB_ERR_?? error code on failure. */ static int -smsc_read_eeprom(struct smsc_softc *sc, uint32_t off, uint8_t *data, int length) +smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) { - int timedout, i; + usb_ticks_t start_ticks; + usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + int err; + int locked; uint32_t val; + uint16_t i; - if (smsc_wait_for_bits(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY)) { - device_printf(sc->sc_ue.ue_dev, "Timed-out waiting for busy EEPROM\n"); - return (-1); - } - - for (i = 0; i < length; i++) { - smsc_write_reg(sc, SMSC_REG_E2P_CMD, - E2P_CMD_BUSY | E2P_CMD_READ | ((off+i) & E2P_CMD_ADDR)); - - timedout = smsc_wait_for_bits(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY); - val = smsc_read_reg(sc, SMSC_REG_E2P_CMD); - if (timedout || (val & E2P_CMD_TIMEOUT)) { - device_printf(sc->sc_ue.ue_dev, - "Timed-out reading from EEPROM\n"); - return (-1); - } + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); - val = smsc_read_reg(sc, SMSC_REG_E2P_DATA); - data[i] = val & E2P_DATA_MASK; + err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY); + if (err != 0) { + smsc_warn_printf(sc, "eeprom busy, failed to read data\n"); + goto done; } - return (0); -} - -#if 0 -/** - * smsc_write_eeprom - Reads data from eeprom - * @sc: driver soft context - * @off: EEPROM offset - * @data: memory to write - * @length: write length bytes - * - * RETURNS: - * 0 on success, -1 otherwise - */ -static int -smsc_write_eeprom(struct smsc_softc *sc, uint32_t off, uint8_t *data, int length) -{ - int timedout, i; - uint32_t val; - - if (smsc_wait_for_bits(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY)) { - device_printf(sc->sc_ue.ue_dev, "Timed-out waiting for busy EEPROM\n"); - return (-1); - } + /* start reading the bytes, one at a time */ + for (i = 0; i < buflen; i++) { + + val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off + i)); + if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0) + goto done; + + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 0) + goto done; + if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & SMSC_EEPROM_CMD_TIMEOUT)) + break; - /* - * Write/Erase - */ - smsc_write_reg(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY | E2P_CMD_EWEN); - timedout = smsc_wait_for_bits(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY); - val = smsc_read_reg(sc, SMSC_REG_E2P_CMD); - - if (timedout || (val & E2P_CMD_TIMEOUT)) { - device_printf(sc->sc_ue.ue_dev, "Timed-out erasing EEPROM\n"); - return (-1); - } - - for (i = 0; i < length; i++) { - val = data[i]; - smsc_write_reg(sc, SMSC_REG_E2P_DATA, val); - smsc_write_reg(sc, SMSC_REG_E2P_CMD, - E2P_CMD_BUSY | E2P_CMD_WRITE | ((off+i) & E2P_CMD_ADDR)); - - timedout = smsc_wait_for_bits(sc, SMSC_REG_E2P_CMD, E2P_CMD_BUSY); - val = smsc_read_reg(sc, SMSC_REG_E2P_CMD); - - if (timedout || (val & E2P_CMD_TIMEOUT)) { - device_printf(sc->sc_ue.ue_dev, - "Timed-out writing EEPROM %d %x\n", i, val); - return (-1); + uether_pause(&sc->sc_ue, hz / 100); + } while ((ticks - start_ticks) < max_ticks); + + if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) { + smsc_warn_printf(sc, "eeprom command failed\n"); + err = USB_ERR_IOERROR; + break; } + + if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0) + goto done; + + buf[i] = (val & 0xff); } + +done: + if (!locked) + SMSC_UNLOCK(sc); - return (0); + return (err); } -#endif /** * smsc_miibus_readreg - Reads a MII/MDIO register * @dev: usb ether device - * @phy: the number of phy writing to + * @phy: the number of phy reading from * @reg: the register address - * @val: the value to write * + * Attempts to read a phy register over the MII bus. * + * LOCKING: + * Takes and releases the device mutex lock if not already held. * * RETURNS: - * Returns 0 on success or a negative error code. + * Returns the 16-bits read from the MII register, if this function fails 0 + * is returned. */ static int smsc_miibus_readreg(device_t dev, int phy, int reg) @@ -397,27 +412,26 @@ smsc_miibus_readreg(device_t dev, int ph int locked; uint32_t addr; uint32_t val = 0; - int i; locked = mtx_owned(&sc->sc_mtx); if (!locked) SMSC_LOCK(sc); - addr = (phy << 11) | (reg << 6) | MII_READ; - smsc_write_reg(sc, SMSC_REG_MII_ADDR, addr); - - for (i = 0; i != SMSC_TIMEOUT; i++) { - if (!(smsc_read_reg(sc, SMSC_REG_MII_ADDR) & MII_BUSY)) - break; - if (uether_pause(&sc->sc_ue, hz / 100)) - break; + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { + smsc_warn_printf(sc, "MII is busy\n"); + goto done; } - if (i == SMSC_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); + addr = (phy << 11) | (reg << 6) | SMSC_MII_READ; + smsc_write_reg(sc, SMSC_MII_ADDR, addr); - val = smsc_read_reg(sc, SMS_REG_MII_DATA); + 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); + val = le32toh(val); + +done: if (!locked) SMSC_UNLOCK(sc); @@ -431,10 +445,13 @@ smsc_miibus_readreg(device_t dev, int ph * @reg: the register address * @val: the value to write * + * Attempts to write a phy register over the MII bus. * + * LOCKING: + * Takes and releases the device mutex lock if not already held. * * RETURNS: - * 0 + * Always returns 0 regardless of success or failure. */ static int smsc_miibus_writereg(device_t dev, int phy, int reg, int val) @@ -442,7 +459,6 @@ smsc_miibus_writereg(device_t dev, int p struct smsc_softc *sc = device_get_softc(dev); int locked; uint32_t addr; - int i; if (sc->sc_phyno != phy) return (0); @@ -451,38 +467,37 @@ smsc_miibus_writereg(device_t dev, int p if (!locked) SMSC_LOCK(sc); - val = htole32(val); - smsc_write_reg(sc, SMS_REG_MII_DATA, val); + 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) | MII_WRITE; - smsc_write_reg(sc, SMSC_REG_MII_ADDR, addr); + val = htole32(val); + smsc_write_reg(sc, SMSC_MII_DATA, val); - for (i = 0; i != SMSC_TIMEOUT; i++) { - if (!(smsc_read_reg(sc, SMSC_REG_MII_ADDR) & MII_BUSY)) - break; - if (uether_pause(&sc->sc_ue, hz / 100)) - break; - } + addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE; + smsc_write_reg(sc, SMSC_MII_ADDR, addr); - if (i == SMSC_TIMEOUT) - device_printf(sc->sc_ue.ue_dev, "MII write timed out\n"); + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) + smsc_warn_printf(sc, "MII write timeout\n"); +done: if (!locked) SMSC_UNLOCK(sc); - return (0); } /** - * smsc_miibus_statchg - Called when the MII status changes + * smsc_miibus_statchg - Called to detect phy status change * @dev: usb ether device * + * This function is called periodically by the system to poll for status + * changes of the link. * - * - * RETURNS: - * Returns 0 on success or a negative error code. + * LOCKING: + * Takes and releases the device mutex lock if not already held. */ static void smsc_miibus_statchg(device_t dev) @@ -491,6 +506,9 @@ smsc_miibus_statchg(device_t dev) struct mii_data *mii = uether_getmii(&sc->sc_ue); struct ifnet *ifp; int locked; + int err; + uint32_t flow; + uint32_t afc_cfg; locked = mtx_owned(&sc->sc_mtx); if (!locked) @@ -500,7 +518,7 @@ smsc_miibus_statchg(device_t dev) if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; - + /* Use the MII status to determine link status */ sc->sc_flags &= ~SMSC_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == @@ -517,21 +535,49 @@ smsc_miibus_statchg(device_t dev) break; } } - + /* Lost link, do nothing. */ - if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + smsc_dbg_printf(sc, "link flag not set\n"); goto done; + } - /* Enable/disable full duplex operation */ + err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg); + if (err) { + smsc_warn_printf(sc, "failed to read initial AFC_CFG, error %d\n", err); + goto done; + } + + /* Enable/disable full duplex operation and TX/RX pause */ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { - sc->sc_mac_cr &= ~MAC_CR_RCVOWN; - sc->sc_mac_cr |= MAC_CR_FDPX; + smsc_dbg_printf(sc, "full duplex operation\n"); + sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN; + sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + flow = 0xffff0002; + else + flow = 0; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + afc_cfg |= 0xf; + else + afc_cfg &= ~0xf; + } else { - sc->sc_mac_cr &= ~MAC_CR_FDPX; - sc->sc_mac_cr |= MAC_CR_RCVOWN; + smsc_dbg_printf(sc, "half duplex operation\n"); + sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX; + sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN; + + flow = 0; + afc_cfg |= 0xf; } - - smsc_write_reg(sc, SMSC_REG_MAC_CR, sc->sc_mac_cr); + + err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); + err += smsc_write_reg(sc, SMSC_FLOW, flow); + err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg); + if (err) + smsc_warn_printf(sc, "media change failed, error %d\n", err); done: if (!locked) @@ -545,6 +591,9 @@ done: * Basically boilerplate code that simply calls the mii functions to set the * media options. * + * LOCKING: + * The device lock must be held before this function is called. + * * RETURNS: * Returns 0 on success or a negative error code. */ @@ -569,14 +618,14 @@ smsc_ifmedia_upd(struct ifnet *ifp) /** * smsc_ifmedia_sts - Report current media status - * @ifp: - * @ifmr: + * @ifp: inet interface pointer + * @ifmr: interface media request * * Basically boilerplate code that simply calls the mii functions to get the * media status. * - * RETURNS: - * Returns 0 on success or a negative error code. + * LOCKING: + * Internally takes and releases the device lock. */ static void smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) @@ -596,11 +645,14 @@ smsc_ifmedia_sts(struct ifnet *ifp, stru /** * smsc_hash - Calculate the hash of a mac address - * @addr: The mac address to calculate the has on + * @addr: The mac address to calculate the hash on * + * This function is used when configuring a range of m'cast mac addresses to + * filter on. The hash of the mac address is put in the device's mac hash + * table. * * RETURNS: - * Returns a value from 0-63 which is the hash of the mac address. + * Returns a value from 0-63 value which is the hash of the mac address. */ static inline uint32_t smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) @@ -610,11 +662,13 @@ smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) /** * smsc_setmulti - Setup multicast - * @ue: + * @ue: usb ethernet device context * + * Tells the device to either accept frames with a multicast mac address, a + * select group of m'cast mac addresses or just the devices mac address. * - * RETURNS: - * Returns 0 on success or a negative error code. + * LOCKING: + * Should be called with the SMSC lock held. */ static void smsc_setmulti(struct usb_ether *ue) @@ -627,14 +681,10 @@ smsc_setmulti(struct usb_ether *ue) SMSC_LOCK_ASSERT(sc, MA_OWNED); - if (ifp->if_flags & IFF_PROMISC) { - /* Enter promiscuous mode and set the bits accordingly */ - sc->sc_mac_cr |= MAC_CR_PRMS; - sc->sc_mac_cr &= ~(MAC_CR_MCPAS | MAC_CR_HPFILT); - } else if (ifp->if_flags & IFF_ALLMULTI) { - /* Enter multicaste mode and set the bits accordingly */ - sc->sc_mac_cr |= MAC_CR_MCPAS; - sc->sc_mac_cr &= ~(MAC_CR_PRMS | MAC_CR_HPFILT); + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + smsc_dbg_printf(sc, "receive all multicast enabled\n"); + sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS; + sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT; } else { /* Take the lock of the mac address list before hashing each of them */ @@ -644,8 +694,8 @@ smsc_setmulti(struct usb_ether *ue) /* We are filtering on a set of address so calculate hashes of each * of the address and set the corresponding bits in the register. */ - sc->sc_mac_cr |= MAC_CR_HPFILT; - sc->sc_mac_cr &= ~(MAC_CR_PRMS | MAC_CR_MCPAS); + sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT; + sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) @@ -656,25 +706,31 @@ smsc_setmulti(struct usb_ether *ue) } } else { /* Only receive packets with destination set to our mac address */ - sc->sc_mac_cr &= ~(MAC_CR_PRMS | MAC_CR_MCPAS | MAC_CR_HPFILT); + sc->sc_mac_csr &= ~(SMSC_MAC_CSR_MCPAS | SMSC_MAC_CSR_HPFILT); } if_maddr_runlock(ifp); + + /* Debug */ + if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) + smsc_dbg_printf(sc, "receive select group of macs\n"); + else + smsc_dbg_printf(sc, "receive own packets only\n"); } /* Write the hash table and mac control registers */ - smsc_write_reg(sc, SMSC_REG_HASHH, hashtbl[1]); - smsc_write_reg(sc, SMSC_REG_HASHL, hashtbl[0]); - smsc_write_reg(sc, SMSC_REG_MAC_CR, sc->sc_mac_cr); + smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]); + smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]); + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); } + /** - * smsc_setpromisc - Setup promiscuous mode - * @ue: - * + * smsc_setpromisc - Enables/disables promiscuous mode + * @ue: usb ethernet device context * - * RETURNS: - * Returns 0 on success or a negative error code. + * LOCKING: + * Should be called with the SMSC lock held. */ static void smsc_setpromisc(struct usb_ether *ue) @@ -682,55 +738,111 @@ smsc_setpromisc(struct usb_ether *ue) struct smsc_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); - device_printf(sc->sc_ue.ue_dev, "promiscuous mode enabled\n"); + smsc_dbg_printf(sc, "promiscuous mode %sabled\n", + (ifp->if_flags & IFF_PROMISC) ? "en" : "dis"); SMSC_LOCK_ASSERT(sc, MA_OWNED); - /* Set/clear the promiscuous bit based on setting */ - if (ifp->if_flags & IFF_PROMISC) { - sc->sc_mac_cr |= MAC_CR_PRMS; - } else { - sc->sc_mac_cr &= ~MAC_CR_PRMS; + if (ifp->if_flags & IFF_PROMISC) + sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; + else + sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS; + + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); +} + + +/** + * smsc_sethwcsum - Enable or disable H/W UDP and TCP checksumming + * @sc: driver soft context + * + * LOCKING: + * Should be called with the SMSC lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int smsc_sethwcsum(struct smsc_softc *sc) +{ + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + uint32_t val; + int err; + + if (!ifp) + return (-EIO); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + err = smsc_read_reg(sc, SMSC_COE_CTRL, &val); + if (err != 0) { + smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", err); + return (err); + } + + /* Enable/disable the Rx checksum */ + if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_RXCSUM) + val |= SMSC_COE_CTRL_RX_EN; + else + val &= ~SMSC_COE_CTRL_RX_EN; + + /* Enable/disable the Tx checksum (currently not supported) */ + if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_TXCSUM) + val |= SMSC_COE_CTRL_TX_EN; + else + val &= ~SMSC_COE_CTRL_TX_EN; + + err = smsc_write_reg(sc, SMSC_COE_CTRL, val); + if (err != 0) { + smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", err); + return (err); } - /* Write mac control registers */ - smsc_write_reg(sc, SMSC_REG_MAC_CR, sc->sc_mac_cr); + return (0); } + /** - * smsc_set_mac_address - Sets the mac address in the device + * smsc_setmacaddress - Sets the mac address in the device * @sc: driver soft context * @addr: pointer to array contain at least 6 bytes of the mac * - * Writes the MAC address into the device, usually this doesn't need to be - * done because typically the MAC is read from the attached EEPROM. + * Writes the MAC address into the device, usually the MAC is programmed with + * values from the EEPROM. * + * LOCKING: + * Should be called with the SMSC lock held. * * RETURNS: * Returns 0 on success or a negative error code. */ -static void -smsc_set_mac_address(struct smsc_softc *sc, const uint8_t *addr) +static int +smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr) { - uint32_t tmp; + int err; + uint32_t val; + + smsc_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); - /* Program the lower 4 bytes of the MAC */ - tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; - smsc_write_reg(sc, SMSC_REG_ADDRL, tmp); + val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0) + goto done; - /* Program the upper 2 bytes of the MAC */ - tmp = addr[5] << 8 | addr[4]; - smsc_write_reg(sc, SMSC_REG_ADDRH, tmp); + val = (addr[5] << 8) | addr[4]; + err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val); + +done: + return (err); } /** - * smsc_reset - Reset the SMSC interface + * smsc_reset - Reset the SMSC chip * @sc: device soft context * - * - * - * RETURNS: - * Returns 0 on success or a negative error code. + * LOCKING: + * Should be called with the SMSC lock held. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***