Index: sys/dev/usb/net/if_lan78xx.c =================================================================== --- /dev/null +++ sys/dev/usb/net/if_lan78xx.c @@ -0,0 +1,2156 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2012 Ben Gray . + * Copyright (C) 2018 The FreeBSD Foundation + * This software was developed by Arshan Khanifar + * under sponsorship from the FreeBSD Foundation. + * + * 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 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Microchip LAN78XX devices (http://www.microchip.com/wwwproducts/en/LAN7800) + * + * The LAN78XX devices are stand-alone USB to Ethernet chips that + * support USB 3.1 and 10/100/1000 Mbps Ethernet. + * + * This driver closely resembles the old if_smsc driver, specific procedures + * relating to the lan78xx chip are modelled based on the lan78xx Linux driver + * written by Microchip: + * https://github.com/torvalds/linux/blob/master/drivers/net/usb/lan78xx.c + * + * UNIMPLEMENTED FEATURES + * ------------------ + * There are a bunch of features that the chip supports but have not been implemented + * in this driver yet. This will serve as a TODO list for the author and other + * contributors to consider. + * 1. RX/TX checksum offloading: nothing has been implemented yet for TX checksumming, + * RX checksumming works with ICMP messages, but its broken for TCP/UDP packets. + * 2. Direct address translation filtering: implemented but not tested yet. + * 3. VLAN tag removal: not implemented yet. + * 4. Reading MAC address from the device tree: this is specific to R-Pi 3B+ model. + * Currently, the driver assigns a random MAC address itself. + * 5. Support for USB interrupt endpoints. + * 6. Latency Tolerance Messaging (LTM) support. + * 7. TCP LSO support. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "opt_platform.h" + +/* For reading the mac address from device tree.*/ +#ifdef FDT +#include +#include +#include +#endif + +#include +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR lan78xx_debug +#include +#include + +#include + +#include + +#ifdef USB_DEBUG +static int lan78xx_debug = 0; + +SYSCTL_NODE(_hw_usb, OID_AUTO, lan78xx, CTLFLAG_RW, 0, "USB lan78xx"); +SYSCTL_INT(_hw_usb_lan78xx, OID_AUTO, debug, CTLFLAG_RWTUN, &lan78xx_debug, 0, + "Debug level"); +#endif + +#define LAN78XX_DEFAULT_RX_CSUM_ENABLE (false) +#define LAN78XX_DEFAULT_TX_CSUM_ENABLE (false) +#define LAN78XX_DEFAULT_TSO_CSUM_ENABLE (false) + +/* + * Various supported device vendors/products. + */ +static const struct usb_device_id lan78xx_devs[] = { +#define LAN78XX_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) } + LAN78XX_DEV(LAN7800, 0), +#undef LAN78XX_DEV +}; + +#ifdef USB_DEBUG +#define lan78xx_dbg_printf(sc, fmt, args...) \ + do { \ + if (lan78xx_debug > 0) \ + device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ + } while(0) +#else +#define lan78xx_dbg_printf(sc, fmt, args...) do { } while (0) +#endif + +#define lan78xx_warn_printf(sc, fmt, args...) \ + device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) + +#define lan78xx_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)) + +/* USB endpoints. */ + +enum { + LAN78XX_BULK_DT_RD, + LAN78XX_BULK_DT_WR, + /* + * the LAN78XX device does support interrupt endpoints, + * but they're not needed as we poll on MII status. + * LAN78XX_INTR_DT_WR, + * LAN78XX_INTR_DT_RD, + */ + LAN78XX_N_TRANSFER, +}; + +struct lan78xx_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[LAN78XX_N_TRANSFER]; + int sc_phyno; + + /* The following stores the settings in the mac control (MAC_CSR) register */ + uint32_t sc_rfe_ctl; + uint32_t sc_mdix_ctl; + uint32_t sc_rev_id; + uint32_t sc_mchash_table[LAN78XX_DP_SEL_VHF_HASH_LEN]; + uint32_t sc_pfilter_table[LAN78XX_NUM_PFILTER_ADDRS_][2]; + + uint32_t sc_flags; +#define LAN78XX_FLAG_LINK 0x0001 +}; + +#define LAN78XX_IFACE_IDX 0 + +#define LAN78XX_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define LAN78XX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define LAN78XX_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) + + +static device_probe_t lan78xx_probe; +static device_attach_t lan78xx_attach; +static device_detach_t lan78xx_detach; + +static usb_callback_t lan78xx_bulk_read_callback; +static usb_callback_t lan78xx_bulk_write_callback; + +static miibus_readreg_t lan78xx_miibus_readreg; +static miibus_writereg_t lan78xx_miibus_writereg; +static miibus_statchg_t lan78xx_miibus_statchg; + +static int lan78xx_attach_post_sub(struct usb_ether *ue); +static uether_fn_t lan78xx_attach_post; +static uether_fn_t lan78xx_init; +static uether_fn_t lan78xx_stop; +static uether_fn_t lan78xx_start; +static uether_fn_t lan78xx_tick; +static uether_fn_t lan78xx_setmulti; +static uether_fn_t lan78xx_setpromisc; + +static int lan78xx_ifmedia_upd(struct ifnet *); +static void lan78xx_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int lan78xx_chip_init(struct lan78xx_softc *sc); +static int lan78xx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); + +static const struct usb_config lan78xx_config[LAN78XX_N_TRANSFER] = { + + [LAN78XX_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .frames = 16, + .bufsize = 16 * (MCLBYTES + 16), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = lan78xx_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [LAN78XX_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 20480, /* bytes */ + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = lan78xx_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + /* The LAN78XX chip supports interrupt endpoints, however they aren't + * needed as we poll on the MII status. + */ +}; + +static const struct usb_ether_methods lan78xx_ue_methods = { + .ue_attach_post = lan78xx_attach_post, + .ue_attach_post_sub = lan78xx_attach_post_sub, + .ue_start = lan78xx_start, + .ue_ioctl = lan78xx_ioctl, + .ue_init = lan78xx_init, + .ue_stop = lan78xx_stop, + .ue_tick = lan78xx_tick, + .ue_setmulti = lan78xx_setmulti, + .ue_setpromisc = lan78xx_setpromisc, + .ue_mii_upd = lan78xx_ifmedia_upd, + .ue_mii_sts = lan78xx_ifmedia_sts, +}; + +/** + * lan78xx_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: + * 0 on success, a USB_ERR_?? error code on failure. + */ +static int +lan78xx_read_reg(struct lan78xx_softc *sc, uint32_t off, uint32_t *data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = LAN78XX_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) + lan78xx_warn_printf(sc, "Failed to read register 0x%0x\n", off); + + *data = le32toh(buf); + + return (err); +} + +/** + * lan78xx_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: + * 0 on success, a USB_ERR_?? error code on failure. + */ +static int +lan78xx_write_reg(struct lan78xx_softc *sc, uint32_t off, uint32_t data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + buf = htole32(data); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = LAN78XX_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) + lan78xx_warn_printf(sc, "Failed to write register 0x%0x\n", off); + + return (err); +} + +/** + * lan78xx_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 on success, or a USB_ERR_?? error code on failure. + */ +static int +lan78xx_wait_for_bits(struct lan78xx_softc *sc, uint32_t reg, uint32_t bits) +{ + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + uint32_t val; + int err; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = lan78xx_read_reg(sc, reg, &val)) != 0) + return (err); + if (!(val & bits)) + return (0); + uether_pause(&sc->sc_ue, hz / 100); + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); + + return (USB_ERR_TIMEOUT); +} + +/** + * lan78xx_eeprom_read_raw - 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, or a USB_ERR_?? error code on failure. + */ +static int +lan78xx_eeprom_read_raw(struct lan78xx_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) +{ + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + int err, locked; + uint32_t val, saved; + uint16_t i; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + err = lan78xx_read_reg(sc, LAN78XX_HW_CFG, &val); + saved = val; + + val &= ~(LAN78XX_HW_CFG_LEDO_EN_ | LAN78XX_HW_CFG_LED1_EN_); + err = lan78xx_write_reg(sc, LAN78XX_HW_CFG, val); + + err = lan78xx_wait_for_bits(sc, LAN78XX_E2P_CMD, LAN78XX_E2P_CMD_BUSY_); + + if (err != 0) { + lan78xx_warn_printf(sc, "eeprom busy, failed to read data\n"); + goto done; + } + + /* start reading the bytes, one at a time */ + for (i = 0; i < buflen; i++) { + + val = LAN78XX_E2P_CMD_BUSY_ | LAN78XX_E2P_CMD_READ_; + val |= (LAN78XX_E2P_CMD_ADDR_MASK_ & (off + i)); + if ((err = lan78xx_write_reg(sc, LAN78XX_E2P_CMD, val)) != 0) + goto done; + + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = lan78xx_read_reg(sc, LAN78XX_E2P_CMD, &val)) != 0) + goto done; + if (!(val & LAN78XX_E2P_CMD_BUSY_) || (val & LAN78XX_E2P_CMD_TIMEOUT_)) + break; + + uether_pause(&sc->sc_ue, hz / 100); + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); + + if (val & (LAN78XX_E2P_CMD_BUSY_ | LAN78XX_E2P_CMD_TIMEOUT_)) { + lan78xx_warn_printf(sc, "eeprom command failed\n"); + err = USB_ERR_IOERROR; + break; + } + + if ((err = lan78xx_read_reg(sc, LAN78XX_E2P_DATA, &val)) != 0) + goto done; + + buf[i] = (val & 0xff); + } + +done: + if (!locked) + LAN78XX_UNLOCK(sc); + lan78xx_write_reg(sc, LAN78XX_HW_CFG, saved); + return (err); +} + +/** + * lan78xx_eeprom_read - Reads the attached EEPROM, confirms that EEPROM is programmed + * @sc: soft context + * @off: the eeprom address offset + * @buf: stores the bytes + * @buflen: the number of bytes to read + * + * RETURNS: + * 0 on success, or a USB_ERR_?? error code on failure. + */ +static int +lan78xx_eeprom_read(struct lan78xx_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) +{ + uint8_t sig; + int ret; + + ret = lan78xx_eeprom_read_raw(sc, LAN78XX_E2P_INDICATOR_OFFSET, &sig, 1); + + if ((ret == 0) && (sig == LAN78XX_E2P_INDICATOR)) { + ret = lan78xx_eeprom_read_raw(sc, off, buf, buflen); + lan78xx_dbg_printf(sc, "EEPROM present\n"); + } else { + ret = -EINVAL; + lan78xx_dbg_printf(sc, "EEPROM not present\n"); + } + return ret; +} + +/** + * lan78xx_otp_read_raw + * @sc: soft context + * @off: the otp address offset + * @buf: stores the bytes + * @buflen: the number of bytes to read + * + * Simply reads bytes from the OTP. + * + * LOCKING: + * The function takes and releases the device lock if it is not already held. + * + * RETURNS: + * 0 on success, or a USB_ERR_?? error code on failure. + * + */ +static int +lan78xx_otp_read_raw(struct lan78xx_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) +{ + int locked, err; + uint32_t val; + uint16_t i; + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + err = lan78xx_read_reg(sc, LAN78XX_OTP_PWR_DN, &val); + + // checking if bit is set + if (val & LAN78XX_OTP_PWR_DN_PWRDN_N) { + // clearing it, then waiting for it to be cleared + lan78xx_write_reg(sc, LAN78XX_OTP_PWR_DN, 0); + err = lan78xx_wait_for_bits(sc, LAN78XX_OTP_PWR_DN, LAN78XX_OTP_PWR_DN_PWRDN_N); + if (err != 0) { + lan78xx_warn_printf(sc, "OTP off? failed to read data\n"); + goto done; + } + } + /* start reading the bytes, one at a time */ + for (i = 0; i < buflen; i++) { + err = lan78xx_write_reg(sc, LAN78XX_OTP_ADDR1, + ((off + i) >> 8) & LAN78XX_OTP_ADDR1_15_11); + err = lan78xx_write_reg(sc, LAN78XX_OTP_ADDR2, + ((off + i) & LAN78XX_OTP_ADDR2_10_3)); + err = lan78xx_write_reg(sc, LAN78XX_OTP_FUNC_CMD, LAN78XX_OTP_FUNC_CMD_READ_); + err = lan78xx_write_reg(sc, LAN78XX_OTP_CMD_GO, LAN78XX_OTP_CMD_GO_GO_); + + err = lan78xx_wait_for_bits(sc, LAN78XX_OTP_STATUS, LAN78XX_OTP_STATUS_BUSY_); + if (err != 0) { + lan78xx_warn_printf(sc, "OTP busy failed to read data\n"); + goto done; + } + + if ((err = lan78xx_read_reg(sc, LAN78XX_OTP_RD_DATA, &val)) != 0) + goto done; + + buf[i] = (uint8_t)(val & 0xff); + } + +done: + if (!locked) + LAN78XX_UNLOCK(sc); + return (err); + +} + +/** + * lan78xx_otp_read + * @sc: soft context + * @off: the otp address offset + * @buf: stores the bytes + * @buflen: the number of bytes to read + * + * Simply reads bytes from the otp. + * + * LOCKING: + * The function takes and releases the device lock if it is not already held. + * + * RETURNS: + * 0 on success, or a USB_ERR_?? error code on failure. + * + */ +static int +lan78xx_otp_read(struct lan78xx_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) +{ + uint8_t sig; + int err; + + err = lan78xx_otp_read_raw(sc, LAN78XX_OTP_INDICATOR_OFFSET, &sig, 1); + if (err == 0) { + if (sig == LAN78XX_OTP_INDICATOR_1) { + } else if (sig == LAN78XX_OTP_INDICATOR_2) { + off += 0x100; + } else { + err = -EINVAL; + } + if(!err) + err = lan78xx_otp_read_raw(sc, off, buf, buflen); + } + return err; +} + +/** + * lan78xx_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 the MAC is programmed with + * values from the EEPROM. + * + * LOCKING: + * Should be called with the LAN78XX lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +lan78xx_setmacaddress(struct lan78xx_softc *sc, const uint8_t *addr) +{ + int err; + uint32_t val; + + lan78xx_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]); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + if ((err = lan78xx_write_reg(sc, LAN78XX_RX_ADDRL, val)) != 0) + goto done; + + val = (addr[5] << 8) | addr[4]; + err = lan78xx_write_reg(sc, LAN78XX_RX_ADDRH, val); + +done: + return (err); +} + +/** + * lan78xx_set_rx_max_frame_length + * @sc: driver soft context + * @size: pointer to array contain at least 6 bytes of the mac + * + * Sets the maximum frame length to be received. Frames bigger than + * this size are aborted. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +lan78xx_set_rx_max_frame_length(struct lan78xx_softc *sc, int size) +{ + int err = 0; + uint32_t buf; + bool rxenabled; + + /* first we have to disable rx before changing the length */ + + err = lan78xx_read_reg(sc, LAN78XX_MAC_RX, &buf); + rxenabled = ((buf & LAN78XX_MAC_RX_EN_) != 0); + + if (rxenabled) { + buf &= ~LAN78XX_MAC_RX_EN_; + err = lan78xx_write_reg(sc, LAN78XX_MAC_RX, buf); + } + + /* setting max frame length */ + + buf &= ~LAN78XX_MAC_RX_MAX_FR_SIZE_MASK_; + buf |= (((size + 4) << LAN78XX_MAC_RX_MAX_FR_SIZE_SHIFT_) & LAN78XX_MAC_RX_MAX_FR_SIZE_MASK_); + err = lan78xx_write_reg(sc, LAN78XX_MAC_RX, buf); + + /* If it were enabled before, we enable it back. */ + + if (rxenabled) { + buf |= LAN78XX_MAC_RX_EN_; + err = lan78xx_write_reg(sc, LAN78XX_MAC_RX, buf); + } + + return 0; +} + +/** + * lan78xx_miibus_readreg - Reads a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy reading from + * @reg: the register address + * + * Attempts to read a PHY register indirectly through the USB controller registers. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + * + * RETURNS: + * Returns the 16-bits read from the MII register, if this function fails 0 + * is returned. + */ +static int +lan78xx_miibus_readreg(device_t dev, int phy, int reg) { + + struct lan78xx_softc *sc = device_get_softc(dev); + int locked; + uint32_t addr, val; + + val = 0; + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + if (lan78xx_wait_for_bits(sc, LAN78XX_MII_ACCESS, LAN78XX_MII_BUSY_) != 0) { + lan78xx_warn_printf(sc, "MII is busy\n"); + goto done; + } + + addr = (phy << 11) | (reg << 6) | LAN78XX_MII_READ_ | LAN78XX_MII_BUSY_; + lan78xx_write_reg(sc, LAN78XX_MII_ACCESS, addr); + + if (lan78xx_wait_for_bits(sc, LAN78XX_MII_ACCESS, LAN78XX_MII_BUSY_) != 0) { + lan78xx_warn_printf(sc, "MII read timeout\n"); + goto done; + } + + lan78xx_read_reg(sc, LAN78XX_MII_DATA, &val); + val = le32toh(val); + +done: + if (!locked) + LAN78XX_UNLOCK(sc); + + return (val & 0xFFFF); +} + +/** + * lan78xx_miibus_writereg - Writes a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy writing to + * @reg: the register address + * @val: the value to write + * + * Attempts to write to a PHY register through the usb controller registers. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + * + * RETURNS: + * Always returns 0 regardless of success or failure. + */ +static int +lan78xx_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct lan78xx_softc *sc = device_get_softc(dev); + int locked; + uint32_t addr; + + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + if (lan78xx_wait_for_bits(sc, LAN78XX_MII_ACCESS, LAN78XX_MII_BUSY_) != 0) { + lan78xx_warn_printf(sc, "MII is busy\n"); + goto done; + } + + val = htole32(val); + lan78xx_write_reg(sc, LAN78XX_MII_DATA, val); + + addr = (phy << 11) | (reg << 6) | LAN78XX_MII_WRITE_ | LAN78XX_MII_BUSY_; + lan78xx_write_reg(sc, LAN78XX_MII_ACCESS, addr); + + if (lan78xx_wait_for_bits(sc, LAN78XX_MII_ACCESS, LAN78XX_MII_BUSY_) != 0) + lan78xx_warn_printf(sc, "MII write timeout\n"); + +done: + if (!locked) + LAN78XX_UNLOCK(sc); + return (0); +} + +/* + * lan78xx_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. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + */ +static void +lan78xx_miibus_statchg(device_t dev) +{ + struct lan78xx_softc *sc = device_get_softc(dev); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + struct ifnet *ifp; + int locked; + int err; + uint32_t flow = 0; + uint32_t fct_flow = 0; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + 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 &= ~LAN78XX_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + lan78xx_dbg_printf(sc, "media is active\n"); + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= LAN78XX_FLAG_LINK; + lan78xx_dbg_printf(sc, "10/100 ethernet\n"); + break; + case IFM_1000_T: + sc->sc_flags |= LAN78XX_FLAG_LINK; + lan78xx_dbg_printf(sc, "Gigabit ethernet\n"); + break; + default: + break; + } + } + /* Lost link, do nothing. */ + if ((sc->sc_flags & LAN78XX_FLAG_LINK) == 0) { + lan78xx_dbg_printf(sc, "link flag not set\n"); + goto done; + } + + err = lan78xx_read_reg(sc, LAN78XX_FCT_FLOW, &fct_flow); + if (err) { + lan78xx_warn_printf(sc, "failed to read initial flow control thresholds, 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) { + lan78xx_dbg_printf(sc, "full duplex operation\n"); + + /* enable transmit MAC flow control function */ + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + flow |= LAN78XX_FLOW_CR_TX_FCEN_ | 0xFFFF; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + flow |= LAN78XX_FLOW_CR_RX_FCEN_; + } + + switch(usbd_get_speed(sc->sc_ue.ue_udev)) { + case USB_SPEED_SUPER: + fct_flow = 0x817; + break; + case USB_SPEED_HIGH: + fct_flow = 0x211; + break; + default: + break; + } + + err += lan78xx_write_reg(sc, LAN78XX_FLOW, flow); + err += lan78xx_write_reg(sc, LAN78XX_FCT_FLOW, fct_flow); + if (err) + lan78xx_warn_printf(sc, "media change failed, error %d\n", err); + +done: + if (!locked) + LAN78XX_UNLOCK(sc); +} + +/* + * lan78xx_set_mdix_auto - Configures the device to enable automatic crossover + * and polarity detection. LAN78XX provides HP Auto-MDIX functionality + * for seamless crossover and polarity detection. Linux's ethtool allows + * for manually forcing MDI or MDIX, but that feature is not preferred here. + * + * @sc: driver soft context + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + */ +static void +lan78xx_set_mdix_auto(struct lan78xx_softc *sc) +{ + uint32_t buf, err; + + err = lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, + LAN78XX_EXT_PAGE_ACCESS, LAN78XX_EXT_PAGE_SPACE_1); + + buf = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, LAN78XX_EXT_MODE_CTRL); + buf &= ~LAN78XX_EXT_MODE_CTRL_MDIX_MASK_; + buf |= LAN78XX_EXT_MODE_CTRL_AUTO_MDIX_; + + lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, + LAN78XX_EXT_MODE_CTRL, buf); + + err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, + LAN78XX_EXT_PAGE_ACCESS, LAN78XX_EXT_PAGE_SPACE_0); + + if (err != 0) + lan78xx_warn_printf(sc, "error setting PHY's MDIX status\n"); + + sc->sc_mdix_ctl = buf; +} + +/** + * lan78xx_phy_init - Initialises the in-built LAN78XX phy + * @sc: driver soft context + * + * Resets the PHY part of the chip and then initialises it to default + * values. The 'link down' and 'auto-negotiation complete' interrupts + * from the PHY are also enabled, however we don't monitor the interrupt + * endpoints for the moment. + * + * RETURNS: + * Returns 0 on success or EIO if failed to reset the PHY. + */ +static int +lan78xx_phy_init(struct lan78xx_softc *sc) +{ + lan78xx_dbg_printf(sc, "Initializing PHY.\n"); + uint16_t bmcr; + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + /* Reset phy and wait for reset to complete */ + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET); + + start_ticks = ticks; + do { + uether_pause(&sc->sc_ue, hz / 100); + bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + } while ((bmcr & BMCR_RESET) && ((ticks - start_ticks) < max_ticks)); + + if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) { + lan78xx_err_printf(sc, "PHY reset timed-out\n"); + return (EIO); + } + + /* Setup the phy to interrupt when the link goes down or autoneg completes */ + lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, LAN78XX_PHY_INTR_STAT); + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, LAN78XX_PHY_INTR_MASK, + (LAN78XX_PHY_INTR_ANEG_COMP | LAN78XX_PHY_INTR_LINK_CHANGE)); + + /* Enabling Auto-MDIX for crossover and polarity detection. */ + lan78xx_set_mdix_auto(sc); + + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, + ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */ + ANAR_CSMA | + ANAR_FC | + ANAR_PAUSE_ASYM); + + /* Restart auto-negotation */ + bmcr |= BMCR_STARTNEG; + bmcr |= BMCR_AUTOEN; + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr); + bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + return (0); +} + + +/** + * lan78xx_chip_init - Initialises the chip after power on + * @sc: driver soft context + * + * This initialisation sequence is modelled on the procedure in the Linux + * driver. + * + * RETURNS: + * Returns 0 on success or an error code on failure. + */ +static int +lan78xx_chip_init(struct lan78xx_softc *sc) +{ + int err; + int locked; + uint32_t buf; + uint32_t burst_cap; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + LAN78XX_LOCK(sc); + + /* Enter H/W config mode */ + lan78xx_write_reg(sc, LAN78XX_HW_CFG, LAN78XX_HW_CFG_LRST_); + + if ((err = lan78xx_wait_for_bits(sc, LAN78XX_HW_CFG, LAN78XX_HW_CFG_LRST_)) != 0) { + lan78xx_warn_printf(sc, "timed-out waiting for lite reset to complete\n"); + goto init_failed; + } + + /* Set the mac address */ + if ((err = lan78xx_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) { + lan78xx_warn_printf(sc, "failed to set the MAC address\n"); + goto init_failed; + } + + /* Read and display the revision register */ + if ((err = lan78xx_read_reg(sc, LAN78XX_ID_REV, &sc->sc_rev_id)) < 0) { + lan78xx_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err); + goto init_failed; + } + + device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n", + (sc->sc_rev_id & LAN78XX_ID_REV_CHIP_ID_MASK_) >> 16, + (sc->sc_rev_id & LAN78XX_ID_REV_CHIP_REV_MASK_)); + + /* respond to BULK-IN tokens with a NAK when RX FIFO is empty */ + + if ((err = lan78xx_read_reg(sc, LAN78XX_USB_CFG0, &buf)) != 0) { + lan78xx_warn_printf(sc, "failed to read LAN78XX_USB_CFG0: %d\n", err); + goto init_failed; + } + buf |= LAN78XX_USB_CFG_BIR_; + lan78xx_write_reg(sc, LAN78XX_USB_CFG0, buf); + + /* + * LTM support will go here. + */ + + /* configuring the burst cap */ + switch(usbd_get_speed(sc->sc_ue.ue_udev)) { + case USB_SPEED_SUPER: + burst_cap = LAN78XX_DEFAULT_BURST_CAP_SIZE/LAN78XX_SS_USB_PKT_SIZE; + break; + case USB_SPEED_HIGH: + burst_cap = LAN78XX_DEFAULT_BURST_CAP_SIZE/LAN78XX_HS_USB_PKT_SIZE; + break; + default: + burst_cap = LAN78XX_DEFAULT_BURST_CAP_SIZE/LAN78XX_FS_USB_PKT_SIZE; + } + + lan78xx_write_reg(sc, LAN78XX_BURST_CAP, burst_cap); + + /* Set the default bulk in delay (same value from Linux driver) */ + lan78xx_write_reg(sc, LAN78XX_BULK_IN_DLY, LAN78XX_DEFAULT_BULK_IN_DELAY); + + /* Multiple ethernet frames per USB packets */ + err = lan78xx_read_reg(sc, LAN78XX_HW_CFG, &buf); + buf |= LAN78XX_HW_CFG_MEF_; + err = lan78xx_write_reg(sc, LAN78XX_HW_CFG, buf); + + /* Enable burst cap */ + if ((err = lan78xx_read_reg(sc, LAN78XX_USB_CFG0, &buf)) < 0) { + lan78xx_warn_printf(sc, "failed to read USB_CFG0: (err = %d)\n", err); + goto init_failed; + } + buf |= LAN78XX_USB_CFG_BCE_; + err = lan78xx_write_reg(sc, LAN78XX_USB_CFG0, buf); + + /* + * + * Set FCL's RX and TX FIFO sizes: according to data sheet this is already the + * default value. But we initialize it to the same value anyways, as that's + * what the Linux driver does. + * + */ + + buf = (LAN78XX_MAX_RX_FIFO_SIZE - 512) / 512; + err = lan78xx_write_reg(sc, LAN78XX_FCT_RX_FIFO_END, buf); + + buf = (LAN78XX_MAX_TX_FIFO_SIZE - 512) / 512; + err = lan78xx_write_reg(sc, LAN78XX_FCT_TX_FIFO_END, buf); + + /* Enabling interrupts. (Not using them for now) */ + + err = lan78xx_write_reg(sc, LAN78XX_INT_STS, LAN78XX_INT_STS_CLEAR_ALL_); + + /* + * Initializing flow control registers to 0. These registers are properly set + * is handled in link-reset function in the Linux driver. + */ + + err = lan78xx_write_reg(sc, LAN78XX_FLOW, 0); + err = lan78xx_write_reg(sc, LAN78XX_FCT_FLOW, 0); + + /* + * Settings for the RFE, we enable broadcast and destination address perfect + * filtering. + */ + + err = lan78xx_read_reg(sc, LAN78XX_RFE_CTL, &buf); + buf |= LAN78XX_RFE_CTL_BCAST_EN_ | LAN78XX_RFE_CTL_DA_PERFECT_; + err = lan78xx_write_reg(sc, LAN78XX_RFE_CTL, buf); + + /* + * At this point the Linux driver writes multicast tables, and enables + * checksum engines. But in FreeBSD that gets done in lan78xx_init, + * which gets called when the interface is brought up. + */ + + /* Reset the PHY */ + lan78xx_write_reg(sc, LAN78XX_PMT_CTL, LAN78XX_PMT_CTL_PHY_RST_); + if ((err = lan78xx_wait_for_bits(sc, LAN78XX_PMT_CTL, LAN78XX_PMT_CTL_PHY_RST_)) != 0) { + lan78xx_warn_printf(sc, "timed-out waiting for phy reset to complete\n"); + goto init_failed; + } + + /* Enable automatic duplex detection and automatic speed detection. */ + err = lan78xx_read_reg(sc, LAN78XX_MAC_CR, &buf); + buf |= LAN78XX_MAC_CR_AUTO_DUPLEX_ | LAN78XX_MAC_CR_AUTO_SPEED_; + err = lan78xx_write_reg(sc, LAN78XX_MAC_CR, buf); + + /* + * Enable PHY interrupts (Not really getting used for now) + * INT_EP_CTL: interrupt endpoint control register + * phy events cause interrupts to be issued + */ + err = lan78xx_read_reg(sc, LAN78XX_INT_EP_CTL, &buf); + buf |= LAN78XX_INT_ENP_PHY_INT; + err = lan78xx_write_reg(sc, LAN78XX_INT_EP_CTL, buf); + + /* + * Enables mac's transmitter. it'll transmit frames + * from the buffer onto the cable. + */ + err = lan78xx_read_reg(sc, LAN78XX_MAC_TX, &buf); + buf |= LAN78XX_MAC_TX_TXEN_; + err = lan78xx_write_reg(sc, LAN78XX_MAC_TX, buf); + + /* + * FIFO is capable of transmitting frames to MAC + */ + err = lan78xx_read_reg(sc, LAN78XX_FCT_TX_CTL, &buf); + buf |= LAN78XX_FCT_TX_CTL_EN_; + err = lan78xx_write_reg(sc, LAN78XX_FCT_TX_CTL, buf); + + /* + * Set max frame length: + * In linux this is dev->mtu (which by default is 1500) + VLAN_ETH_HLEN = 1518 + */ + + err = lan78xx_set_rx_max_frame_length(sc, ETHER_MAX_LEN); + + /* + * Initialise the PHY + */ + if ((err = lan78xx_phy_init(sc)) != 0) + goto init_failed; + + /* + * enable MAC RX + */ + err = lan78xx_read_reg(sc, LAN78XX_MAC_RX, &buf); + buf |= LAN78XX_MAC_RX_EN_; + err = lan78xx_write_reg(sc, LAN78XX_MAC_RX, buf); + + /* + * enable FIFO controller RX + */ + err = lan78xx_read_reg(sc, LAN78XX_FCT_RX_CTL, &buf); + buf |= LAN78XX_FCT_TX_CTL_EN_; + err = lan78xx_write_reg(sc, LAN78XX_FCT_RX_CTL, buf); + + return 0; + +init_failed: + if (!locked) + LAN78XX_UNLOCK(sc); + + lan78xx_err_printf(sc, "lan78xx_chip_init failed (err=%d)\n", err); + return (err); +} + +static void +lan78xx_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct lan78xx_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct mbuf *m; + struct usb_page_cache *pc; + uint16_t pktlen; + uint32_t rx_cmd_a, rx_cmd_b; + uint16_t rx_cmd_c; + int off; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + lan78xx_dbg_printf(sc, "rx : actlen %d\n", actlen); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* There is always a zero length frame after bringing the IF up */ + if (actlen < (sizeof(rx_cmd_a) + ETHER_CRC_LEN)) + goto tr_setup; + + /* There maybe multiple packets in the USB frame, each will have a + * header and each needs to have it's own mbuf allocated and populated + * for it. + */ + pc = usbd_xfer_get_frame(xfer, 0); + off = 0; + + while (off < actlen) { + + /* The frame header is always aligned on a 4 byte boundary */ + off = ((off + 0x3) & ~0x3); + + /* extract RX CMD A */ + usbd_copy_out(pc, off, &rx_cmd_a, sizeof(rx_cmd_a)); + off += (sizeof(rx_cmd_a)); + rx_cmd_a = le32toh(rx_cmd_a); + + /* extract RX CMD B */ + usbd_copy_out(pc, off, &rx_cmd_b, sizeof(rx_cmd_b)); + off += (sizeof(rx_cmd_b)); + rx_cmd_b = le32toh(rx_cmd_b); + + /* extract RX CMD C */ + usbd_copy_out(pc, off, &rx_cmd_c, sizeof(rx_cmd_c)); + off += (sizeof(rx_cmd_c)); + rx_cmd_b = le32toh(rx_cmd_c); + + pktlen = (rx_cmd_a & LAN78XX_RX_CMD_A_LEN_MASK_); + + lan78xx_dbg_printf(sc, "rx : rx_cmd_a 0x%08x : rx_cmd_b 0x%08x :" + " rx_cmd_c 0x%04x : pktlen %d : actlen %d : off %d\n", + rx_cmd_a, rx_cmd_b, rx_cmd_c, pktlen, actlen, off); + + if (rx_cmd_a & LAN78XX_RX_CMD_A_RED_) { + lan78xx_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rx_cmd_a); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + } else { + /* Check if the ethernet frame is too big or too small */ + if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off))) + goto tr_setup; + + /* Create a new mbuf to store the packet in */ + m = uether_newbuf(); + if (m == NULL) { + lan78xx_warn_printf(sc, "failed to create new mbuf\n"); + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + goto tr_setup; + } + + usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); + + /* Check if RX checksums are computed, and offload them */ + if ((ifp->if_capabilities & IFCAP_RXCSUM) && + !(rx_cmd_a & LAN78XX_RX_CMD_A_ICSM_)) { + struct ether_header *eh; + eh = mtod(m, struct ether_header *); + /* Remove the extra 2 bytes of the csum */ + //pktlen -= 2; // is this even needed? :O + + /* + * The checksum appears to be simplistically calculated + * over the protocol headers up to the end of the eth frame. + * Which means if the eth frame is padded the csum calculation + * is incorrectly performed over the padding bytes as well. + * Therefore to be safe we ignore the H/W csum on + * frames less than or equal to 64 bytes. + * + * Protocols checksummed TCP, UDP, ICMP, IGMP, IP + */ + if (pktlen > ETHER_MIN_LEN) { + + /* Indicate the csum has been calculated */ + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; + + /* Copy the checksum from the last 2 bytes + * of the transfer and put in the csum_data field. + */ + usbd_copy_out(pc, (off + pktlen), + &m->m_pkthdr.csum_data, 2); + + /* The data is copied in network order, but the + * csum algorithm in the kernel expects it to be + * in host network order. + */ + m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data); + + lan78xx_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n", + m->m_pkthdr.csum_data); + } + } + + /* Finally enqueue the mbuf on the receive queue */ + if (pktlen < (4 + ETHER_HDR_LEN)) { + m_freem(m); + goto tr_setup; + } + /* Remove 4 trailing bytes */ + uether_rxmbuf(ue, m, pktlen - 4); + } + + /* Update the offset to move to the next potential packet */ + off += pktlen; + } + + /* FALLTHROUGH */ + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: + if (error != USB_ERR_CANCELLED) { + lan78xx_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error)); + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +/** + * lan78xx_bulk_write_callback - Write callback used to send ethernet frame(s) + * @xfer: the USB transfer + * @error: error code if the transfers is in an errored state + * + * The main write function that pulls ethernet frames off the queue and sends + * them out. + * + */ + +static void +lan78xx_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct lan78xx_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + int nframes; + uint32_t frm_len = 0, tx_cmd_a = 0, tx_cmd_b = 0; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + lan78xx_dbg_printf(sc, "USB TRANSFER status: USB_ST_TRANSFERRED\n"); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + case USB_ST_SETUP: + lan78xx_dbg_printf(sc, "USB TRANSFER status: USB_ST_SETUP\n"); +tr_setup: + if ((sc->sc_flags & LAN78XX_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + lan78xx_dbg_printf(sc, "sc->sc_flags & LAN78XX_FLAG_LINK: %d\n", + (sc->sc_flags & LAN78XX_FLAG_LINK)); + lan78xx_dbg_printf(sc, "ifp->if_drv_flags & IFF_DRV_OACTIVE: %d\n", + (ifp->if_drv_flags & IFF_DRV_OACTIVE)); + lan78xx_dbg_printf(sc, "USB TRANSFER not sending: no link or controller is busy \n"); + /* Don't send anything if there is no link or controller is busy. */ + return; + } + for (nframes = 0; nframes < 16 && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, + nframes); + frm_len = 0; + pc = usbd_xfer_get_frame(xfer, nframes); + + /* Each frame is prefixed with two 32-bit values describing the + * length of the packet and buffer. + */ + tx_cmd_a = (m->m_pkthdr.len & LAN78XX_TX_CMD_A_LEN_MASK_) | + LAN78XX_TX_CMD_A_FCS_; + tx_cmd_a = htole32(tx_cmd_a); + usbd_copy_in(pc, 0, &tx_cmd_a, sizeof(tx_cmd_a)); + + tx_cmd_b = 0; + /* TCP LSO Support will probably be implemented here. */ + tx_cmd_b = htole32(tx_cmd_b); + usbd_copy_in(pc, 4, &tx_cmd_b, sizeof(tx_cmd_b)); + + frm_len += 8; + /* Next copy in the actual packet */ + usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len); + frm_len += m->m_pkthdr.len; + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* If there's a BPF listener, bounce a copy of this frame to him */ + BPF_MTAP(ifp, m); + m_freem(m); + + /* Set frame length. */ + usbd_xfer_set_frame_len(xfer, nframes, frm_len); + } + + lan78xx_dbg_printf(sc, "USB TRANSFER nframes: %d\n", nframes); + if (nframes != 0) { + lan78xx_dbg_printf(sc, "USB TRANSFER submit attempt\n"); + usbd_xfer_set_frames(xfer, nframes); + usbd_transfer_submit(xfer); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + return; + + default: + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (error != USB_ERR_CANCELLED) { + lan78xx_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error)); + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +/** + * lan78xx_attach_post - Called after the driver attached to the USB interface + * @ue: the USB ethernet device + * + * This is where the chip is intialised for the first time. This is different + * from the lan78xx_init() function in that that one is designed to setup the + * H/W to match the UE settings and can be called after a reset. + * + */ +static void +lan78xx_attach_post(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + uint32_t mac_h, mac_l; + //int err; + lan78xx_dbg_printf(sc, "Calling lan78xx_attach_post.\n"); + + /* Setup some of the basics */ + sc->sc_phyno = 1; + + /* Attempt to get the mac address, if an EEPROM is not attached this + * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC + * address based on urandom. + */ + memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN); + + uint32_t val; + lan78xx_read_reg(sc, 0, &val); + + /* Check if there is already a MAC address in the register */ + if ((lan78xx_read_reg(sc, LAN78XX_RX_ADDRL, &mac_l) == 0) && + (lan78xx_read_reg(sc, LAN78XX_RX_ADDRH, &mac_h) == 0)) { + sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); + sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); + sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); + sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); + sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); + sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); + } + + /* MAC address is not set so try to read from EEPROM, if that fails generate + * a random MAC address. + */ + if (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { + if ((lan78xx_eeprom_read(sc, LAN78XX_E2P_MAC_OFFSET, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN) == 0) || + (lan78xx_otp_read(sc, LAN78XX_OTP_MAC_OFFSET, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN) == 0)) { + if(ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { + lan78xx_dbg_printf(sc, "MAC read from EEPROM\n"); + } else { + lan78xx_dbg_printf(sc, "MAC assigned randomly\n"); + read_random(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); + sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */ + sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */ + } + } else { + lan78xx_dbg_printf(sc, "MAC assigned randomly\n"); + arc4rand(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN, 0); + sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */ + sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */ + } + } else { + lan78xx_dbg_printf(sc, "MAC assigned from registers\n"); + } + + /* Initialise the chip for the first time */ + lan78xx_chip_init(sc); +} + +/** + * lan78xx_attach_post_sub - Called after the driver attached to the USB interface + * @ue: the USB ethernet device + * + * Most of this is boilerplate code and copied from the base USB ethernet + * driver. It has been overriden so that we can indicate to the system that + * the chip supports H/W checksumming. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +lan78xx_attach_post_sub(struct usb_ether *ue) +{ + struct lan78xx_softc *sc; + struct ifnet *ifp; + int error; + + sc = uether_getsc(ue); + lan78xx_dbg_printf(sc, "Calling lan78xx_attach_post_sub.\n"); + ifp = ue->ue_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = uether_start; + ifp->if_ioctl = lan78xx_ioctl; + ifp->if_init = uether_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + /* + * The chip supports TCP/UDP checksum offloading on TX and RX paths, however + * currently only RX checksum is supported in the driver (see top of file). + */ + ifp->if_hwassist = 0; + if (LAN78XX_DEFAULT_RX_CSUM_ENABLE) + ifp->if_capabilities |= IFCAP_RXCSUM; + + if (LAN78XX_DEFAULT_TX_CSUM_ENABLE) + ifp->if_capabilities |= IFCAP_TXCSUM; + + /* + * In the Linux driver they also enable scatter/gather (NETIF_F_SG) here, + * that's something related to socket buffers used in Linux. FreeBSD doesn't + * have that as an interface feature. + */ + + if (LAN78XX_DEFAULT_TSO_CSUM_ENABLE) + ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_TSO6; + + + /* TX checksuming is disabled (for now?) + ifp->if_capabilities |= IFCAP_TXCSUM; + ifp->if_capenable |= IFCAP_TXCSUM; + ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + */ + + ifp->if_capenable = ifp->if_capabilities; + + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0); + mtx_unlock(&Giant); + + return 0; +} + +/** + * lan78xx_start - Starts communication with the LAN78XX95xx chip + * @ue: USB ether interface + * + * + * + */ +static void +lan78xx_start(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[LAN78XX_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[LAN78XX_BULK_DT_WR]); +} + +/** + * lan78xx_ioctl - ioctl function for the device + * @ifp: interface pointer + * @cmd: the ioctl command + * @data: data passed in the ioctl call, typically a pointer to struct ifreq. + * + * The ioctl routine is overridden to detect change requests for the H/W + * checksum capabilities. + * + * RETURNS: + * 0 on success and an error code on failure. + */ +static int +lan78xx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct usb_ether *ue = ifp->if_softc; + struct lan78xx_softc *sc; + struct ifreq *ifr; + int rc; + int mask; + int reinit; + + if (cmd == SIOCSIFCAP) { + + sc = uether_getsc(ue); + ifr = (struct ifreq *)data; + + LAN78XX_LOCK(sc); + + rc = 0; + reinit = 0; + + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + + /* Modify the RX CSUM enable bits */ + if ((mask & IFCAP_RXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_RXCSUM; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + reinit = 1; + } + } + + LAN78XX_UNLOCK(sc); + if (reinit) + uether_init(ue); + + } else { + rc = uether_ioctl(ifp, cmd, data); + } + + return (rc); +} + +/** + * lan78xx_reset - Reset the SMSC chip + * @sc: device soft context + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +lan78xx_reset(struct lan78xx_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + lan78xx_warn_printf(sc, "reset failed (ignored)\n"); + + /* Wait a little while for the chip to get its brains in order. */ + uether_pause(&sc->sc_ue, hz / 100); + + /* Reinitialize controller to achieve full reset. */ + lan78xx_chip_init(sc); +} + +/** + * lan78xx_set_addr_filter + * + * @sc: device soft context + * @index: index of the entry to the perfect address table + * @addr: address to be written + * + */ +static void +lan78xx_set_addr_filter(struct lan78xx_softc *sc, int index, uint8_t addr[ETHER_ADDR_LEN]) +{ + uint32_t tmp; + + if ((sc) && (index > 0) && (index < LAN78XX_NUM_PFILTER_ADDRS_)) { + tmp = addr[3]; + tmp |= addr[2] | (tmp << 8); + tmp |= addr[1] | (tmp << 8); + tmp |= addr[0] | (tmp << 8); + sc->sc_pfilter_table[index][1] = tmp; + tmp = addr[5]; + tmp |= addr[4] | (tmp << 8); + tmp |= LAN78XX_PFILTER_ADDR_VALID_ | LAN78XX_PFILTER_ADDR_TYPE_DST_; + sc->sc_pfilter_table[index][0] = tmp; + } +} + +/** + * lan78xx_dataport_write - write to the selected RAM + * @sc: The device soft context. + * @ram_select: Select which RAM to access. + * @addr: Starting address to write to. + * @buf: word-sized buffer to write to RAM, starting at @addr. + * @length: length of @buf + * + * + * RETURNS: + * 0 if write successful. + */ +static int +lan78xx_dataport_write(struct lan78xx_softc *sc, uint32_t ram_select, uint32_t addr, + uint32_t length, uint32_t *buf) +{ + uint32_t dp_sel; + int i, ret; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + ret = lan78xx_wait_for_bits(sc, LAN78XX_DP_SEL, LAN78XX_DP_SEL_DPRDY_); + if (ret < 0) + goto done; + + ret = lan78xx_read_reg(sc, LAN78XX_DP_SEL, &dp_sel); + + dp_sel &= ~LAN78XX_DP_SEL_RSEL_MASK_; + dp_sel |= ram_select; + + ret = lan78xx_write_reg(sc, LAN78XX_DP_SEL, dp_sel); + + for (i = 0; i < length; i++) { + ret = lan78xx_write_reg(sc, LAN78XX_DP_ADDR, addr + i); + + ret = lan78xx_write_reg(sc, LAN78XX_DP_DATA, buf[i]); + + ret = lan78xx_write_reg(sc, LAN78XX_DP_CMD, LAN78XX_DP_CMD_WRITE_); + + ret = lan78xx_wait_for_bits(sc, LAN78XX_DP_SEL, LAN78XX_DP_SEL_DPRDY_); + if (ret != 0) + goto done; + } + +done: + return ret; +} + +/** + * lan78xx_multicast_write + * @sc: device's soft context + * + * Writes perfect addres filters and hash address filters to their + * corresponding registers and RAMs. + * + */ +static void +lan78xx_multicast_write(struct lan78xx_softc *sc) +{ + int i, ret; + lan78xx_dataport_write(sc, LAN78XX_DP_SEL_RSEL_VLAN_DA_, + LAN78XX_DP_SEL_VHF_VLAN_LEN, LAN78XX_DP_SEL_VHF_HASH_LEN, + sc->sc_mchash_table); + + for (i = 1; i < LAN78XX_NUM_PFILTER_ADDRS_; i++) { + ret = lan78xx_write_reg(sc, LAN78XX_PFILTER_HI(i), 0); + ret = lan78xx_write_reg(sc, LAN78XX_PFILTER_LO(i), + sc->sc_pfilter_table[i][1]); + ret = lan78xx_write_reg(sc, LAN78XX_PFILTER_HI(i), + sc->sc_pfilter_table[i][0]); + } +} + +/** + * lan78xx_hash - Calculate the hash of a mac address + * @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 value which is the hash of the mac address. + */ +static inline uint32_t +lan78xx_hash(uint8_t addr[ETHER_ADDR_LEN]) +{ + return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; +} + +/** + * lan78xx_setmulti - Setup multicast + * @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. + * + * LOCKING: + * Should be called with the LAN78XX lock held. + */ +static void +lan78xx_setmulti(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + uint8_t i, *addr; + struct ifmultiaddr *ifma; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_rfe_ctl &= ~(LAN78XX_RFE_CTL_UCAST_EN_ | LAN78XX_RFE_CTL_MCAST_EN_ | + LAN78XX_RFE_CTL_DA_PERFECT_ | LAN78XX_RFE_CTL_MCAST_HASH_); + + /* Initializing hash filter table */ + for (i = 0; i < LAN78XX_DP_SEL_VHF_HASH_LEN; i++) + sc->sc_mchash_table[i] = 0; + + /* Initializing perfect filter table */ + for (i = 1; i < LAN78XX_NUM_PFILTER_ADDRS_; i++) { + sc->sc_pfilter_table[i][0] = + sc->sc_pfilter_table[i][1] = 0; + } + + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_BCAST_EN_; + + if (ifp->if_flags & IFF_PROMISC) { + lan78xx_dbg_printf(sc, "promiscuous mode enabled\n"); + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_MCAST_EN_ | LAN78XX_RFE_CTL_UCAST_EN_; + } else if (ifp->if_flags & IFF_ALLMULTI){ + lan78xx_dbg_printf(sc, "receive all multicast enabled\n"); + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_MCAST_EN_; + } else { + /* Take the lock of the mac address list before hashing each of them */ + if_maddr_rlock(ifp); + if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) { + i = 1; + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + /* first we fill up the perfect address table */ + addr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); + if (i < 33) { + lan78xx_set_addr_filter(sc, i, addr); + } else { + uint32_t bitnum = lan78xx_hash(addr); + sc->sc_mchash_table[bitnum / 32] |= + (1 << (bitnum % 32)); + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_MCAST_HASH_; + } + i++; + } + } + if_maddr_runlock(ifp); + lan78xx_multicast_write(sc); + } + lan78xx_write_reg(sc, LAN78XX_RFE_CTL, sc->sc_rfe_ctl); +} + +/** + * lan78xx_setpromisc - Enables/disables promiscuous mode + * @ue: usb ethernet device context + * + * LOCKING: + * Should be called with the LAN78XX lock held. + */ +static void +lan78xx_setpromisc(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + lan78xx_dbg_printf(sc, "promiscuous mode %sabled\n", + (ifp->if_flags & IFF_PROMISC) ? "en" : "dis"); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_MCAST_EN_ | LAN78XX_RFE_CTL_UCAST_EN_; + else + sc->sc_rfe_ctl &= ~(LAN78XX_RFE_CTL_MCAST_EN_); + + lan78xx_write_reg(sc, LAN78XX_RFE_CTL, sc->sc_rfe_ctl); +} + +/** + * lan78xx_sethwcsum - Enable or disable H/W UDP and TCP checksumming + * @sc: driver soft context + * + * LOCKING: + * Should be called with the LAN78XX lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int lan78xx_sethwcsum(struct lan78xx_softc *sc) +{ + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + int err; + + if (!ifp) + return (-EIO); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_capabilities & IFCAP_RXCSUM) { + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_IGMP_COE_ | LAN78XX_RFE_CTL_ICMP_COE_; + sc->sc_rfe_ctl |= LAN78XX_RFE_CTL_TCPUDP_COE_ | LAN78XX_RFE_CTL_IP_COE_; + } else { + sc->sc_rfe_ctl &= ~(LAN78XX_RFE_CTL_IGMP_COE_ | LAN78XX_RFE_CTL_ICMP_COE_); + sc->sc_rfe_ctl &= ~(LAN78XX_RFE_CTL_TCPUDP_COE_ | LAN78XX_RFE_CTL_IP_COE_); + } + + sc->sc_rfe_ctl &= ~LAN78XX_RFE_CTL_VLAN_FILTER_; + + err = lan78xx_write_reg(sc, LAN78XX_RFE_CTL, sc->sc_rfe_ctl); + + if (err != 0) { + lan78xx_warn_printf(sc, "failed to write LAN78XX_RFE_CTL (err=%d)\n", err); + return (err); + } + + return (0); +} + +/** + * lan78xx_ifmedia_upd - Set media options + * @ifp: interface pointer + * + * 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. + */ +static int +lan78xx_ifmedia_upd(struct ifnet *ifp) +{ + struct lan78xx_softc *sc = ifp->if_softc; + lan78xx_dbg_printf(sc, "Calling lan78xx_ifmedia_upd.\n"); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + struct mii_softc *miisc; + int err; + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + err = mii_mediachg(mii); + return (err); +} + +/** + * lan78xx_init - Initialises the LAN95xx chip + * @ue: USB ether interface + * + * Called when the interface is brought up (i.e. ifconfig ue0 up), this + * initialise the interface and the rx/tx pipes. + * + * LOCKING: + * Should be called with the LAN78XX lock held. + */ +static void +lan78xx_init(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + lan78xx_dbg_printf(sc, "Calling lan78xx_init.\n"); + struct ifnet *ifp = uether_getifp(ue); + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + if (lan78xx_setmacaddress(sc, IF_LLADDR(ifp))) + lan78xx_dbg_printf(sc, "setting MAC address failed\n"); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* Cancel pending I/O */ + lan78xx_stop(ue); + + /* Reset the ethernet interface. */ + lan78xx_reset(sc); + + /* Load the multicast filter. */ + lan78xx_setmulti(ue); + + /* TCP/UDP checksum offload engines. */ + lan78xx_sethwcsum(sc); + + usbd_xfer_set_stall(sc->sc_xfer[LAN78XX_BULK_DT_WR]); + + /* Indicate we are up and running. */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* Switch to selected media. */ + lan78xx_ifmedia_upd(ifp); + lan78xx_start(ue); +} + +/** + * lan78xx_stop - Stops communication with the LAN78xx chip + * @ue: USB ether interface + * + * + * + */ +static void +lan78xx_stop(struct usb_ether *ue) +{ + struct lan78xx_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~LAN78XX_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[LAN78XX_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[LAN78XX_BULK_DT_RD]); +} + + +/** + * lan78xx_tick - Called periodically to monitor the state of the LAN95xx chip + * @ue: USB ether interface + * + * Simply calls the mii status functions to check the state of the link. + * + * LOCKING: + * Should be called with the LAN78XX lock held. + */ +static void +lan78xx_tick(struct usb_ether *ue) +{ + + struct lan78xx_softc *sc = uether_getsc(ue); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + + LAN78XX_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & LAN78XX_FLAG_LINK) == 0) { + lan78xx_miibus_statchg(ue->ue_dev); + if ((sc->sc_flags & LAN78XX_FLAG_LINK) != 0) + lan78xx_start(ue); + } +} + +/** + * lan78xx_ifmedia_sts - Report current media status + * @ifp: inet interface pointer + * @ifmr: interface media request + * + * Basically boilerplate code that simply calls the mii functions to get the + * media status. + * + * LOCKING: + * Internally takes and releases the device lock. + */ +static void +lan78xx_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct lan78xx_softc *sc = ifp->if_softc; + struct mii_data *mii = uether_getmii(&sc->sc_ue); + + LAN78XX_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + LAN78XX_UNLOCK(sc); +} + +/** + * lan78xx_probe - Probe the interface. + * @dev: lan78xx device handle + * + * Checks if the device is a match for this driver. + * + * RETURNS: + * Returns 0 on success or an error code on failure. + */ +static int +lan78xx_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != LAN78XX_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != LAN78XX_IFACE_IDX) + return (ENXIO); + return (usbd_lookup_id_by_uaa(lan78xx_devs, sizeof(lan78xx_devs), uaa)); +} + +/** + * lan78xx_attach - Attach the interface. + * @dev: lan78xx device handle + * + * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +lan78xx_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct lan78xx_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int err; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Setup the endpoints for the Microchip LAN78xx device(s) */ + iface_index = LAN78XX_IFACE_IDX; + err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + lan78xx_config, LAN78XX_N_TRANSFER, sc, &sc->sc_mtx); + if (err) { + device_printf(dev, "error: allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &lan78xx_ue_methods; + + err = uether_ifattach(ue); + if (err) { + device_printf(dev, "error: could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + lan78xx_detach(dev); + return (ENXIO); /* failure */ +} + +/** + * lan78xx_detach - Detach the interface. + * @dev: lan78xx device handle + * + * RETURNS: + * Returns 0. + */ +static int +lan78xx_detach(device_t dev) +{ + + struct lan78xx_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, LAN78XX_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static device_method_t lan78xx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, lan78xx_probe), + DEVMETHOD(device_attach, lan78xx_attach), + DEVMETHOD(device_detach, lan78xx_detach), + + ///* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + ///* MII interface */ + DEVMETHOD(miibus_readreg, lan78xx_miibus_readreg), + DEVMETHOD(miibus_writereg, lan78xx_miibus_writereg), + DEVMETHOD(miibus_statchg, lan78xx_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t lan78xx_driver = { + .name = "lan78xx", + .methods = lan78xx_methods, + .size = sizeof(struct lan78xx_softc), +}; + +static devclass_t lan78xx_devclass; + +DRIVER_MODULE(lan78xx, uhub, lan78xx_driver, lan78xx_devclass, NULL, 0); +DRIVER_MODULE(miibus, lan78xx, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(lan78xx, uether, 1, 1, 1); +MODULE_DEPEND(lan78xx, usb, 1, 1, 1); +MODULE_DEPEND(lan78xx, ether, 1, 1, 1); +MODULE_DEPEND(lan78xx, miibus, 1, 1, 1); +MODULE_VERSION(lan78xx, 1); +USB_PNP_HOST_INFO(lan78xx_devs); + Index: sys/dev/usb/net/if_lan78xxreg.h =================================================================== --- /dev/null +++ sys/dev/usb/net/if_lan78xxreg.h @@ -0,0 +1,395 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * Definitions for the Microchip LAN78XX USB-to-Ethernet controllers. + * + * This information was mostly taken from the LAN7800 manual. However, + * some undocumented registers come from the Linux driver (lan78xx.h). + * + */ + +#ifndef _IF_LAN78REG_H_ +#define _IF_LAN78REG_H_ + +/* USB Vendor Requests */ + +#define LAN78XX_UR_WRITE_REG 0xA0 +#define LAN78XX_UR_READ_REG 0xA1 +#define LAN78XX_UR_GET_STATS 0xA2 + +/* Device ID and revision register */ + +#define LAN78XX_ID_REV 0x000 +#define LAN78XX_ID_REV_CHIP_ID_MASK_ 0xFFFF0000UL +#define LAN78XX_ID_REV_CHIP_REV_MASK_ 0x0000FFFFUL + +/* Device interrupt status register. */ + +#define LAN78XX_INT_STS 0x00C +#define LAN78XX_INT_STS_CLEAR_ALL_ 0xFFFFFFFFUL + +/* Hardware Configuration Register. */ + +#define LAN78XX_HW_CFG 0x010 +#define LAN78XX_HW_CFG_LED3_EN_ (0x1UL << 23) +#define LAN78XX_HW_CFG_LED2_EN_ (0x1UL << 22) +#define LAN78XX_HW_CFG_LED1_EN_ (0x1UL << 21) +#define LAN78XX_HW_CFG_LEDO_EN_ (0x1UL << 20) +#define LAN78XX_HW_CFG_MEF_ (0x1UL << 4) +#define LAN78XX_HW_CFG_ETC_ (0x1UL << 3) +#define LAN78XX_HW_CFG_LRST_ (0x1UL << 1) /* Lite reset */ +#define LAN78XX_HW_CFG_SRST_ (0x1UL << 0) /* Soft reset */ + +/* Power Management Control Register. */ + +#define LAN78XX_PMT_CTL 0x014 +#define LAN78XX_PMT_CTL_PHY_RST_ (0x1UL << 4) /* PHY reset */ +#define LAN78XX_PMT_CTL_WOL_EN_ (0x1UL << 3) /* PHY wake-on-lan enable */ +#define LAN78XX_PMT_CTL_PHY_WAKE_EN_ (0x1UL << 2) /* PHY interrupt as a wake up event*/ + +/* GPIO Configuration 0 Register. */ + +#define GPIO_CFG0 0x018 + +/* GPIO Configuration 1 Register. */ + +#define GPIO_CFG1 0x01C + +/* GPIO wake enable and polarity register. */ + +#define GPIO_WAKE 0x020 + +/* RX Command A */ + +#define LAN78XX_RX_CMD_A_RED_ (0x1UL << 22) /* Receive Error Detected */ +#define LAN78XX_RX_CMD_A_ICSM_ (0x1UL << 14) +#define LAN78XX_RX_CMD_A_LEN_MASK_ 0x00003FFFUL + +/* TX Command A */ + +#define LAN78XX_TX_CMD_A_LEN_MASK_ 0x000FFFFFUL +#define LAN78XX_TX_CMD_A_FCS_ (0x1UL << 22) + +/* Data Port Select Register */ + +#define LAN78XX_DP_SEL 0x024 +#define LAN78XX_DP_SEL_DPRDY_ (0x1UL << 31) +#define LAN78XX_DP_SEL_RSEL_VLAN_DA_ (0x1UL << 0) /* RFE VLAN and DA Hash Table */ +#define LAN78XX_DP_SEL_RSEL_MASK_ 0x0000000F +#define LAN78XX_DP_SEL_VHF_HASH_LEN 16 +#define LAN78XX_DP_SEL_VHF_VLAN_LEN 128 + +/* Data Port Command Register */ + +#define LAN78XX_DP_CMD 0x028 +#define LAN78XX_DP_CMD_WRITE_ (0x1UL << 0) /* 1 for write */ +#define LAN78XX_DP_CMD_READ_ (0x0UL << 0) /* 0 for read */ + +/* Data Port Address Register */ + +#define LAN78XX_DP_ADDR 0x02C + +/* Data Port Data Register */ + +#define LAN78XX_DP_DATA 0x030 + +/* EEPROM Command Register */ + +#define LAN78XX_E2P_CMD 0x040 +#define LAN78XX_E2P_CMD_MASK_ 0x70000000UL +#define LAN78XX_E2P_CMD_ADDR_MASK_ 0x000001FFUL +#define LAN78XX_E2P_CMD_BUSY_ (0x1UL << 31) +#define LAN78XX_E2P_CMD_READ_ (0x0UL << 28) +#define LAN78XX_E2P_CMD_WRITE_ (0x3UL << 28) +#define LAN78XX_E2P_CMD_ERASE_ (0x5UL << 28) +#define LAN78XX_E2P_CMD_RELOAD_ (0x7UL << 28) +#define LAN78XX_E2P_CMD_TIMEOUT_ (0x1UL << 10) +#define LAN78XX_E2P_MAC_OFFSET 0x01 +#define LAN78XX_E2P_INDICATOR_OFFSET 0x00 + +/* EEPROM Data Register */ + +#define LAN78XX_E2P_DATA 0x044 +#define LAN78XX_E2P_INDICATOR 0xA5 /* Indicates an EEPROM is present */ + +/* Packet sizes. */ + +#define LAN78XX_SS_USB_PKT_SIZE 1024 +#define LAN78XX_HS_USB_PKT_SIZE 512 +#define LAN78XX_FS_USB_PKT_SIZE 64 + +/* Receive Filtering Engine Control Register */ + +#define LAN78XX_RFE_CTL 0x0B0 +#define LAN78XX_RFE_CTL_IGMP_COE_ (0x1U << 14) +#define LAN78XX_RFE_CTL_ICMP_COE_ (0x1U << 13) +#define LAN78XX_RFE_CTL_TCPUDP_COE_ (0x1U << 12) +#define LAN78XX_RFE_CTL_IP_COE_ (0x1U << 11) +#define LAN78XX_RFE_CTL_BCAST_EN_ (0x1U << 10) +#define LAN78XX_RFE_CTL_MCAST_EN_ (0x1U << 9) +#define LAN78XX_RFE_CTL_UCAST_EN_ (0x1U << 8) +#define LAN78XX_RFE_CTL_VLAN_FILTER_ (0x1U << 5) +#define LAN78XX_RFE_CTL_MCAST_HASH_ (0x1U << 3) +#define LAN78XX_RFE_CTL_DA_PERFECT_ (0x1U << 1) + +/* End address of the RX FIFO */ + +#define LAN78XX_FCT_RX_FIFO_END 0x0C8 +#define LAN78XX_FCT_RX_FIFO_END_MASK_ 0x0000007FUL +#define LAN78XX_MAX_RX_FIFO_SIZE (12 * 1024) + +/* End address of the TX FIFO */ + +#define LAN78XX_FCT_TX_FIFO_END 0x0CC +#define LAN78XX_FCT_TX_FIFO_END_MASK_ 0x0000003FUL +#define LAN78XX_MAX_TX_FIFO_SIZE (12 * 1024) + +/* USB Configuration Register 0 */ + +#define LAN78XX_USB_CFG0 0x080 +#define LAN78XX_USB_CFG_BIR_ (0x1U << 6) /* Bulk-In Empty response */ +#define LAN78XX_USB_CFG_BCE_ (0x1U << 5) /* Burst Cap Enable */ + +/* USB Configuration Register 1 */ + +#define LAN78XX_USB_CFG1 0x084 + +/* USB Configuration Register 2 */ + +#define LAN78XX_USB_CFG2 0x088 + +/* USB bConfigIndex: it only has one configuration. */ + +#define LAN78XX_CONFIG_INDEX 0 + +/* Burst Cap Register */ + +#define LAN78XX_BURST_CAP 0x090 +#define LAN78XX_DEFAULT_BURST_CAP_SIZE LAN78XX_MAX_TX_FIFO_SIZE + +/* Bulk-In Delay Register */ + +#define LAN78XX_BULK_IN_DLY 0x094 +#define LAN78XX_DEFAULT_BULK_IN_DELAY 0x0800 + +/* Interrupt Endpoint Control Register */ + +#define LAN78XX_INT_EP_CTL 0x098 +#define LAN78XX_INT_ENP_PHY_INT (0x1U << 17) /* PHY Enable */ + +/* Registers on the phy, accessed via MII/MDIO */ + +#define LAN78XX_PHY_INTR_STAT 25 +#define LAN78XX_PHY_INTR_MASK 26 +#define LAN78XX_PHY_INTR_LINK_CHANGE (0x1U << 13) +#define LAN78XX_PHY_INTR_ANEG_COMP (0x1U << 10) +#define LAN78XX_EXT_PAGE_ACCESS 0x1F +#define LAN78XX_EXT_PAGE_SPACE_0 0x0000 +#define LAN78XX_EXT_PAGE_SPACE_1 0x0001 +#define LAN78XX_EXT_PAGE_SPACE_2 0x0002 + +/* Extended Register Page 1 Space */ + +#define LAN78XX_EXT_MODE_CTRL 0x0013 +#define LAN78XX_EXT_MODE_CTRL_MDIX_MASK_ 0x000C +#define LAN78XX_EXT_MODE_CTRL_AUTO_MDIX_ 0x0000 + +/* FCT Flow Control Threshold Register */ + +#define LAN78XX_FCT_FLOW 0x0D0 + +/* FCT RX FIFO Control Register */ + +#define LAN78XX_FCT_RX_CTL 0x0C0 + +/* FCT TX FIFO Control Register */ + +#define LAN78XX_FCT_TX_CTL 0x0C4 +#define LAN78XX_FCT_TX_CTL_EN_ (0x1U << 31) + +/* MAC Control Register */ + +#define LAN78XX_MAC_CR 0x100 +#define LAN78XX_MAC_CR_AUTO_DUPLEX_ (0x1U << 12) /* Automatic Duplex Detection */ +#define LAN78XX_MAC_CR_AUTO_SPEED_ (0x1U << 11) + +/* MAC Receive Register */ + +#define LAN78XX_MAC_RX 0x104 +#define LAN78XX_MAC_RX_MAX_FR_SIZE_MASK_ 0x3FFF0000 +#define LAN78XX_MAC_RX_MAX_FR_SIZE_SHIFT_ 16 +#define LAN78XX_MAC_RX_EN_ (0x1U << 0) /* Enable Receiver */ + +/* MAC Transmit Register */ + +#define LAN78XX_MAC_TX 0x108 +#define LAN78XX_MAC_TX_TXEN_ (0x1U << 0) /* Enable Transmitter */ + +/* Flow Control Register */ + +#define LAN78XX_FLOW 0x10C +#define LAN78XX_FLOW_CR_TX_FCEN_ (0x1U << 30) /* TX Flow Control Enable */ +#define LAN78XX_FLOW_CR_RX_FCEN_ (0x1U << 29) /* RX Flow Control Enable */ + +/* MAC Receive Address Registers */ + +#define LAN78XX_RX_ADDRH 0x118 /* High */ +#define LAN78XX_RX_ADDRL 0x11C /* Low */ + +/* MII Access Register */ + +#define LAN78XX_MII_ACCESS 0x120 +#define LAN78XX_MII_BUSY_ (0x1UL << 0) +#define LAN78XX_MII_READ_ (0x0UL << 1) +#define LAN78XX_MII_WRITE_ (0x1UL << 1) + +/* MII Data Register */ + +#define LAN78XX_MII_DATA 0x124 + + /* MAC address perfect filter registers (ADDR_FILTx) */ + +#define LAN78XX_PFILTER_BASE 0x400 +#define LAN78XX_PFILTER_HIX 0x00 +#define LAN78XX_PFILTER_LOX 0x04 +#define LAN78XX_NUM_PFILTER_ADDRS_ 33 +#define LAN78XX_PFILTER_ADDR_VALID_ (0x1UL << 31) +#define LAN78XX_PFILTER_ADDR_TYPE_SRC_ (0x1UL << 30) +#define LAN78XX_PFILTER_ADDR_TYPE_DST_ (0x0UL << 30) +#define LAN78XX_PFILTER_HI(index) (LAN78XX_PFILTER_BASE + (8 * (index)) + (LAN78XX_PFILTER_HIX)) +#define LAN78XX_PFILTER_LO(index) (LAN78XX_PFILTER_BASE + (8 * (index)) + (LAN78XX_PFILTER_LOX)) + +/* + * These registers are not documented in the datasheet. Stolen + * from the Linux driver. + */ + +#define LAN78XX_OTP_BASE_ADDR 0x01000 +#define LAN78XX_OTP_PWR_DN (LAN78XX_OTP_BASE_ADDR + 4 * 0x00) +#define LAN78XX_OTP_PWR_DN_PWRDN_N 0x01 +#define LAN78XX_OTP_ADDR1 (LAN78XX_OTP_BASE_ADDR + 4 * 0x01) +#define LAN78XX_OTP_ADDR1_15_11 0x1F +#define LAN78XX_OTP_ADDR2 (LAN78XX_OTP_BASE_ADDR + 4 * 0x02) +#define LAN78XX_OTP_ADDR2_10_3 0xFF +#define LAN78XX_OTP_ADDR3 (LAN78XX_OTP_BASE_ADDR + 4 * 0x03) +#define LAN78XX_OTP_ADDR3_2_0 0x03 +#define LAN78XX_OTP_RD_DATA (LAN78XX_OTP_BASE_ADDR + 4 * 0x06) +#define LAN78XX_OTP_FUNC_CMD (LAN78XX_OTP_BASE_ADDR + 4 * 0x08) +#define LAN78XX_OTP_FUNC_CMD_RESET 0x04 +#define LAN78XX_OTP_FUNC_CMD_PROGRAM_ 0x02 +#define LAN78XX_OTP_FUNC_CMD_READ_ 0x01 +#define LAN78XX_OTP_MAC_OFFSET 0x01 +#define LAN78XX_OTP_INDICATOR_OFFSET 0x00 +#define LAN78XX_OTP_INDICATOR_1 0xF3 +#define LAN78XX_OTP_INDICATOR_2 0xF7 +#define LAN78XX_OTP_CMD_GO (LAN78XX_OTP_BASE_ADDR + 4 * 0x0A) +#define LAN78XX_OTP_CMD_GO_GO_ 0x01 +#define LAN78XX_OTP_STATUS (LAN78XX_OTP_BASE_ADDR + 4 * 0x0A) +#define LAN78XX_OTP_STATUS_OTP_LOCK_ 0x10 +#define LAN78XX_OTP_STATUS_BUSY_ 0x01 + +/* Some unused registers, from the data sheet. */ + +#define LAN78XX_BOS_ATTR 0x050 +#define LAN78XX_SS_ATTR 0x054 +#define LAN78XX_HS_ATTR 0x058 +#define LAN78XX_FS_ATTR 0x05C +#define LAN78XX_STRNG_ATTR0 0x060 +#define LAN78XX_STRNG_ATTR1 0x064 +#define LAN78XX_FLAG_ATTR 0x068 +#define LAN78XX_SW_GP_1 0x06C +#define LAN78XX_SW_GP_2 0x070 +#define LAN78XX_SW_GP_3 0x074 +#define LAN78XX_VLAN_TYPE 0x0B4 +#define LAN78XX_RX_DP_STOR 0x0D4 +#define LAN78XX_TX_DP_STOR 0x0D8 +#define LAN78XX_LTM_BELT_IDLE0 0x0E0 +#define LAN78XX_LTM_BELT_IDLE1 0x0E4 +#define LAN78XX_LTM_BELT_ACT0 0x0E8 +#define LAN78XX_LTM_BELT_ACT1 0x0EC +#define LAN78XX_LTM_INACTIVE0 0x0F0 +#define LAN78XX_LTM_INACTIVE1 0x0F4 + +#define LAN78XX_RAND_SEED 0x110 +#define LAN78XX_ERR_STS 0x114 + +#define LAN78XX_EEE_TX_LPI_REQUEST_DELAY_CNT 0x130 +#define LAN78XX_EEE_TW_TX_SYS 0x134 +#define LAN78XX_EEE_TX_LPI_AUTO_REMOVAL_DELAY 0x138 + +#define LAN78XX_WUCSR1 0x140 +#define LAN78XX_WK_SRC 0x144 +#define LAN78XX_WUF_CFG_BASE 0x150 +#define LAN78XX_WUF_MASK_BASE 0x200 +#define LAN78XX_WUCSR2 0x600 + +#define LAN78XX_NSx_IPV6_ADDR_DEST_0 0x610 +#define LAN78XX_NSx_IPV6_ADDR_DEST_1 0x614 +#define LAN78XX_NSx_IPV6_ADDR_DEST_2 0x618 +#define LAN78XX_NSx_IPV6_ADDR_DEST_3 0x61C + +#define LAN78XX_NSx_IPV6_ADDR_SRC_0 0x620 +#define LAN78XX_NSx_IPV6_ADDR_SRC_1 0x624 +#define LAN78XX_NSx_IPV6_ADDR_SRC_2 0x628 +#define LAN78XX_NSx_IPV6_ADDR_SRC_3 0x62C + +#define LAN78XX_NSx_ICMPV6_ADDR0_0 0x630 +#define LAN78XX_NSx_ICMPV6_ADDR0_1 0x634 +#define LAN78XX_NSx_ICMPV6_ADDR0_2 0x638 +#define LAN78XX_NSx_ICMPV6_ADDR0_3 0x63C + +#define LAN78XX_NSx_ICMPV6_ADDR1_0 0x640 +#define LAN78XX_NSx_ICMPV6_ADDR1_1 0x644 +#define LAN78XX_NSx_ICMPV6_ADDR1_2 0x648 +#define LAN78XX_NSx_ICMPV6_ADDR1_3 0x64C + +#define LAN78XX_NSx_IPV6_ADDR_DEST 0x650 +#define LAN78XX_NSx_IPV6_ADDR_SRC 0x660 +#define LAN78XX_NSx_ICMPV6_ADDR0 0x670 +#define LAN78XX_NSx_ICMPV6_ADDR1 0x680 + +#define LAN78XX_SYN_IPV4_ADDR_SRC 0x690 +#define LAN78XX_SYN_IPV4_ADDR_DEST 0x694 +#define LAN78XX_SYN_IPV4_TCP_PORTS 0x698 + +#define LAN78XX_SYN_IPV6_ADDR_SRC_0 0x69C +#define LAN78XX_SYN_IPV6_ADDR_SRC_1 0x6A0 +#define LAN78XX_SYN_IPV6_ADDR_SRC_2 0x6A4 +#define LAN78XX_SYN_IPV6_ADDR_SRC_3 0x6A8 + +#define LAN78XX_SYN_IPV6_ADDR_DEST_0 0x6AC +#define LAN78XX_SYN_IPV6_ADDR_DEST_1 0x6B0 +#define LAN78XX_SYN_IPV6_ADDR_DEST_2 0x6B4 +#define LAN78XX_SYN_IPV6_ADDR_DEST_3 0x6B8 + +#define LAN78XX_SYN_IPV6_TCP_PORTS 0x6BC +#define LAN78XX_ARP_SPA 0x6C0 +#define LAN78XX_ARP_TPA 0x6C4 +#define LAN78XX_PHY_DEV_ID 0x700 + +#endif /* _IF_LAN78REG_H_ */ Index: sys/dev/usb/usbdevs =================================================================== --- sys/dev/usb/usbdevs +++ sys/dev/usb/usbdevs @@ -4320,6 +4320,7 @@ product SMC2 2020HUB 0x2020 USB Hub product SMC2 2514HUB 0x2514 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless +product SMC2 LAN7800 0x7800 USB/Ethernet product SMC2 LAN9500_ETH 0x9500 USB/Ethernet product SMC2 LAN9505_ETH 0x9505 USB/Ethernet product SMC2 LAN9530_ETH 0x9530 USB/Ethernet Index: sys/modules/usb/lan78xx/Makefile =================================================================== --- /dev/null +++ sys/modules/usb/lan78xx/Makefile @@ -0,0 +1,11 @@ +S= ${SRCTOP}/sys + +.PATH: $S/dev/usb/net + +KMOD= if_lan78xx +SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h usbdevs.h \ + miibus_if.h miidevs.h opt_inet.h opt_platform.h ofw_bus_if.h \ + if_lan78xx.c +SRCS+= + +.include