Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 9 Apr 2012 17:33:35 +0000 (UTC)
From:      Ben Gray <bgray@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r234065 - projects/armv6/sys/dev/usb/net
Message-ID:  <201204091733.q39HXZja071060@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <ben.r.gray@gmail.com>.
+ * Copyright (c) 2012
+ *	Ben Gray <bgray@freebsd.org>.
  * 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 <sys/cdefs.h>
 __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 <sys/stdint.h>
 #include <sys/stddef.h>
 #include <sys/param.h>
@@ -53,6 +80,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/callout.h>
 #include <sys/malloc.h>
 #include <sys/priv.h>
+#include <sys/random.h>
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
@@ -63,22 +91,9 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usb_debug.h>
 #include <dev/usb/usb_process.h>
 
-#include <dev/usb/net/usb_ethernet.h>
-#include <dev/usb/net/if_smscreg.h>
 #include <dev/usb/usb_device.h>
-
-/*
- * 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 <dev/usb/net/usb_ethernet.h>
+#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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201204091733.q39HXZja071060>