1*469696c0Smpi /* $OpenBSD: if_smsc.c,v 1.17 2015/03/23 08:41:52 mpi Exp $ */ 28fca70d2Sjsg /* $FreeBSD: src/sys/dev/usb/net/if_smsc.c,v 1.1 2012/08/15 04:03:55 gonzo Exp $ */ 38fca70d2Sjsg /*- 48fca70d2Sjsg * Copyright (c) 2012 58fca70d2Sjsg * Ben Gray <bgray@freebsd.org>. 68fca70d2Sjsg * All rights reserved. 78fca70d2Sjsg * 88fca70d2Sjsg * Redistribution and use in source and binary forms, with or without 98fca70d2Sjsg * modification, are permitted provided that the following conditions 108fca70d2Sjsg * are met: 118fca70d2Sjsg * 1. Redistributions of source code must retain the above copyright 128fca70d2Sjsg * notice, this list of conditions and the following disclaimer. 138fca70d2Sjsg * 2. Redistributions in binary form must reproduce the above copyright 148fca70d2Sjsg * notice, this list of conditions and the following disclaimer in the 158fca70d2Sjsg * documentation and/or other materials provided with the distribution. 168fca70d2Sjsg * 178fca70d2Sjsg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 188fca70d2Sjsg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198fca70d2Sjsg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 208fca70d2Sjsg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 218fca70d2Sjsg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 228fca70d2Sjsg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 238fca70d2Sjsg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 248fca70d2Sjsg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 258fca70d2Sjsg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 268fca70d2Sjsg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 278fca70d2Sjsg */ 288fca70d2Sjsg 298fca70d2Sjsg /* 308fca70d2Sjsg * SMSC LAN9xxx devices (http://www.smsc.com/) 318fca70d2Sjsg * 328fca70d2Sjsg * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that 338fca70d2Sjsg * support USB 2.0 and 10/100 Mbps Ethernet. 348fca70d2Sjsg * 358fca70d2Sjsg * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter. 368fca70d2Sjsg * The driver only covers the Ethernet part, the standard USB hub driver 378fca70d2Sjsg * supports the hub part. 388fca70d2Sjsg * 398fca70d2Sjsg * This driver is closely modelled on the Linux driver written and copyrighted 408fca70d2Sjsg * by SMSC. 418fca70d2Sjsg * 428fca70d2Sjsg * H/W TCP & UDP Checksum Offloading 438fca70d2Sjsg * --------------------------------- 448fca70d2Sjsg * The chip supports both tx and rx offloading of UDP & TCP checksums, this 458fca70d2Sjsg * feature can be dynamically enabled/disabled. 468fca70d2Sjsg * 478fca70d2Sjsg * RX checksuming is performed across bytes after the IPv4 header to the end of 488fca70d2Sjsg * the Ethernet frame, this means if the frame is padded with non-zero values 498fca70d2Sjsg * the H/W checksum will be incorrect, however the rx code compensates for this. 508fca70d2Sjsg * 518fca70d2Sjsg * TX checksuming is more complicated, the device requires a special header to 528fca70d2Sjsg * be prefixed onto the start of the frame which indicates the start and end 538fca70d2Sjsg * positions of the UDP or TCP frame. This requires the driver to manually 548fca70d2Sjsg * go through the packet data and decode the headers prior to sending. 558fca70d2Sjsg * On Linux they generally provide cues to the location of the csum and the 568fca70d2Sjsg * area to calculate it over, on FreeBSD we seem to have to do it all ourselves, 578fca70d2Sjsg * hence this is not as optimal and therefore h/w tX checksum is currently not 588fca70d2Sjsg * implemented. 598fca70d2Sjsg */ 608fca70d2Sjsg 618fca70d2Sjsg #include "bpfilter.h" 628fca70d2Sjsg #include "vlan.h" 638fca70d2Sjsg 648fca70d2Sjsg #include <sys/param.h> 658fca70d2Sjsg #include <sys/systm.h> 668fca70d2Sjsg #include <sys/sockio.h> 678fca70d2Sjsg #include <sys/rwlock.h> 688fca70d2Sjsg #include <sys/mbuf.h> 698fca70d2Sjsg #include <sys/kernel.h> 708fca70d2Sjsg #include <sys/socket.h> 718fca70d2Sjsg 728fca70d2Sjsg #include <sys/device.h> 738fca70d2Sjsg 748fca70d2Sjsg #include <machine/bus.h> 758fca70d2Sjsg 768fca70d2Sjsg #include <net/if.h> 778fca70d2Sjsg #include <net/if_dl.h> 788fca70d2Sjsg #include <net/if_media.h> 798fca70d2Sjsg 808fca70d2Sjsg #if NBPFILTER > 0 818fca70d2Sjsg #include <net/bpf.h> 828fca70d2Sjsg #endif 838fca70d2Sjsg 848fca70d2Sjsg #include <netinet/in.h> 858fca70d2Sjsg #include <netinet/if_ether.h> 868fca70d2Sjsg 878fca70d2Sjsg #include <dev/mii/miivar.h> 888fca70d2Sjsg 898fca70d2Sjsg #include <dev/usb/usb.h> 908fca70d2Sjsg #include <dev/usb/usbdi.h> 918fca70d2Sjsg #include <dev/usb/usbdi_util.h> 928fca70d2Sjsg #include <dev/usb/usbdivar.h> 938fca70d2Sjsg #include <dev/usb/usbdevs.h> 948fca70d2Sjsg 958fca70d2Sjsg #include "if_smscreg.h" 968fca70d2Sjsg 978fca70d2Sjsg /* 988fca70d2Sjsg * Various supported device vendors/products. 998fca70d2Sjsg */ 1008fca70d2Sjsg static const struct usb_devno smsc_devs[] = { 101e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN89530 }, 102e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN9530 }, 103e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN9730 }, 104e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500 }, 105e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A }, 106e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_ALT }, 107e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_HAL }, 108e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500A_SAL10 }, 109e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500_ALT }, 110e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9500_SAL10 }, 111e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505 }, 112e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A }, 113e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A_HAL }, 114e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505A_SAL10 }, 115e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9505_SAL10 }, 116e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14 }, 117e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14_ALT }, 118e0f69281Sjsg { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_SMSC9512_14_SAL10 } 1198fca70d2Sjsg }; 1208fca70d2Sjsg 121ae343004Smpi #ifdef SMSC_DEBUG 122ae343004Smpi static int smsc_debug = 0; 1238fca70d2Sjsg #define smsc_dbg_printf(sc, fmt, args...) \ 1248fca70d2Sjsg do { \ 1258fca70d2Sjsg if (smsc_debug > 0) \ 1268fca70d2Sjsg printf("debug: " fmt, ##args); \ 1278fca70d2Sjsg } while(0) 1288fca70d2Sjsg #else 1298fca70d2Sjsg #define smsc_dbg_printf(sc, fmt, args...) 1308fca70d2Sjsg #endif 1318fca70d2Sjsg 1328fca70d2Sjsg #define smsc_warn_printf(sc, fmt, args...) \ 1338fca70d2Sjsg printf("%s: warning: " fmt, (sc)->sc_dev.dv_xname, ##args) 1348fca70d2Sjsg 1358fca70d2Sjsg #define smsc_err_printf(sc, fmt, args...) \ 1368fca70d2Sjsg printf("%s: error: " fmt, (sc)->sc_dev.dv_xname, ##args) 1378fca70d2Sjsg 1388fca70d2Sjsg int smsc_chip_init(struct smsc_softc *sc); 1398fca70d2Sjsg int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 140f00b3ba9Sbrad void smsc_iff(struct smsc_softc *); 1418fca70d2Sjsg int smsc_setmacaddress(struct smsc_softc *, const uint8_t *); 1428fca70d2Sjsg 1438fca70d2Sjsg int smsc_match(struct device *, void *, void *); 1448fca70d2Sjsg void smsc_attach(struct device *, struct device *, void *); 1458fca70d2Sjsg int smsc_detach(struct device *, int); 1468fca70d2Sjsg 1478fca70d2Sjsg void smsc_init(void *); 1488fca70d2Sjsg void smsc_stop(struct smsc_softc *); 1498fca70d2Sjsg void smsc_start(struct ifnet *); 1508fca70d2Sjsg void smsc_reset(struct smsc_softc *); 1518fca70d2Sjsg struct mbuf *smsc_newbuf(void); 1528fca70d2Sjsg 1538fca70d2Sjsg void smsc_tick(void *); 1548fca70d2Sjsg void smsc_tick_task(void *); 1558fca70d2Sjsg void smsc_miibus_statchg(struct device *); 1568fca70d2Sjsg int smsc_miibus_readreg(struct device *, int, int); 1578fca70d2Sjsg void smsc_miibus_writereg(struct device *, int, int, int); 1588fca70d2Sjsg int smsc_ifmedia_upd(struct ifnet *); 1598fca70d2Sjsg void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *); 1608fca70d2Sjsg void smsc_lock_mii(struct smsc_softc *sc); 1618fca70d2Sjsg void smsc_unlock_mii(struct smsc_softc *sc); 1628fca70d2Sjsg 1638fca70d2Sjsg int smsc_tx_list_init(struct smsc_softc *); 1648fca70d2Sjsg int smsc_rx_list_init(struct smsc_softc *); 1658fca70d2Sjsg int smsc_encap(struct smsc_softc *, struct mbuf *, int); 166ab0b1be7Smglocker void smsc_rxeof(struct usbd_xfer *, void *, usbd_status); 167ab0b1be7Smglocker void smsc_txeof(struct usbd_xfer *, void *, usbd_status); 1688fca70d2Sjsg 1698fca70d2Sjsg int smsc_read_reg(struct smsc_softc *, uint32_t, uint32_t *); 1708fca70d2Sjsg int smsc_write_reg(struct smsc_softc *, uint32_t, uint32_t); 1718fca70d2Sjsg int smsc_wait_for_bits(struct smsc_softc *, uint32_t, uint32_t); 1728fca70d2Sjsg int smsc_sethwcsum(struct smsc_softc *); 1738fca70d2Sjsg 1748fca70d2Sjsg struct cfdriver smsc_cd = { 1758fca70d2Sjsg NULL, "smsc", DV_IFNET 1768fca70d2Sjsg }; 1778fca70d2Sjsg 1788fca70d2Sjsg const struct cfattach smsc_ca = { 17953c6612dSmpi sizeof(struct smsc_softc), smsc_match, smsc_attach, smsc_detach, 1808fca70d2Sjsg }; 1818fca70d2Sjsg 1828fca70d2Sjsg int 1838fca70d2Sjsg smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data) 1848fca70d2Sjsg { 1858fca70d2Sjsg usb_device_request_t req; 1868fca70d2Sjsg uint32_t buf; 1878fca70d2Sjsg usbd_status err; 1888fca70d2Sjsg 1898fca70d2Sjsg req.bmRequestType = UT_READ_VENDOR_DEVICE; 1908fca70d2Sjsg req.bRequest = SMSC_UR_READ_REG; 1918fca70d2Sjsg USETW(req.wValue, 0); 1928fca70d2Sjsg USETW(req.wIndex, off); 1938fca70d2Sjsg USETW(req.wLength, 4); 1948fca70d2Sjsg 1958fca70d2Sjsg err = usbd_do_request(sc->sc_udev, &req, &buf); 1968fca70d2Sjsg if (err != 0) 1978fca70d2Sjsg smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off); 1988fca70d2Sjsg 1998fca70d2Sjsg *data = letoh32(buf); 2008fca70d2Sjsg 2018fca70d2Sjsg return (err); 2028fca70d2Sjsg } 2038fca70d2Sjsg 2048fca70d2Sjsg int 2058fca70d2Sjsg smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data) 2068fca70d2Sjsg { 2078fca70d2Sjsg usb_device_request_t req; 2088fca70d2Sjsg uint32_t buf; 2098fca70d2Sjsg usbd_status err; 2108fca70d2Sjsg 2118fca70d2Sjsg buf = htole32(data); 2128fca70d2Sjsg 2138fca70d2Sjsg req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 2148fca70d2Sjsg req.bRequest = SMSC_UR_WRITE_REG; 2158fca70d2Sjsg USETW(req.wValue, 0); 2168fca70d2Sjsg USETW(req.wIndex, off); 2178fca70d2Sjsg USETW(req.wLength, 4); 2188fca70d2Sjsg 2198fca70d2Sjsg err = usbd_do_request(sc->sc_udev, &req, &buf); 2208fca70d2Sjsg if (err != 0) 2218fca70d2Sjsg smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off); 2228fca70d2Sjsg 2238fca70d2Sjsg return (err); 2248fca70d2Sjsg } 2258fca70d2Sjsg 2268fca70d2Sjsg int 2278fca70d2Sjsg smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits) 2288fca70d2Sjsg { 2298fca70d2Sjsg uint32_t val; 2308fca70d2Sjsg int err, i; 2318fca70d2Sjsg 2328fca70d2Sjsg for (i = 0; i < 100; i++) { 2338fca70d2Sjsg if ((err = smsc_read_reg(sc, reg, &val)) != 0) 2348fca70d2Sjsg return (err); 2358fca70d2Sjsg if (!(val & bits)) 2368fca70d2Sjsg return (0); 2378fca70d2Sjsg DELAY(5); 2388fca70d2Sjsg } 2398fca70d2Sjsg 2408fca70d2Sjsg return (1); 2418fca70d2Sjsg } 2428fca70d2Sjsg 2438fca70d2Sjsg int 2448fca70d2Sjsg smsc_miibus_readreg(struct device *dev, int phy, int reg) 2458fca70d2Sjsg { 2468fca70d2Sjsg struct smsc_softc *sc = (struct smsc_softc *)dev; 2478fca70d2Sjsg uint32_t addr; 2488fca70d2Sjsg uint32_t val = 0; 2498fca70d2Sjsg 2508fca70d2Sjsg smsc_lock_mii(sc); 2518fca70d2Sjsg if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { 2528fca70d2Sjsg smsc_warn_printf(sc, "MII is busy\n"); 2538fca70d2Sjsg goto done; 2548fca70d2Sjsg } 2558fca70d2Sjsg 2568fca70d2Sjsg addr = (phy << 11) | (reg << 6) | SMSC_MII_READ; 2578fca70d2Sjsg smsc_write_reg(sc, SMSC_MII_ADDR, addr); 2588fca70d2Sjsg 2598fca70d2Sjsg if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) 2608fca70d2Sjsg smsc_warn_printf(sc, "MII read timeout\n"); 2618fca70d2Sjsg 2628fca70d2Sjsg smsc_read_reg(sc, SMSC_MII_DATA, &val); 2638fca70d2Sjsg 2648fca70d2Sjsg done: 2653f6463d4Sjsg smsc_unlock_mii(sc); 2668fca70d2Sjsg return (val & 0xFFFF); 2678fca70d2Sjsg } 2688fca70d2Sjsg 2698fca70d2Sjsg void 2708fca70d2Sjsg smsc_miibus_writereg(struct device *dev, int phy, int reg, int val) 2718fca70d2Sjsg { 2728fca70d2Sjsg struct smsc_softc *sc = (struct smsc_softc *)dev; 2738fca70d2Sjsg uint32_t addr; 2748fca70d2Sjsg 2758fca70d2Sjsg if (sc->sc_phyno != phy) 2768fca70d2Sjsg return; 2778fca70d2Sjsg 2788fca70d2Sjsg smsc_lock_mii(sc); 2798fca70d2Sjsg if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { 2808fca70d2Sjsg smsc_warn_printf(sc, "MII is busy\n"); 2818fca70d2Sjsg return; 2828fca70d2Sjsg } 2838fca70d2Sjsg 2848fca70d2Sjsg smsc_write_reg(sc, SMSC_MII_DATA, val); 2858fca70d2Sjsg 2868fca70d2Sjsg addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE; 2878fca70d2Sjsg smsc_write_reg(sc, SMSC_MII_ADDR, addr); 2888fca70d2Sjsg smsc_unlock_mii(sc); 2898fca70d2Sjsg 2908fca70d2Sjsg if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) 2918fca70d2Sjsg smsc_warn_printf(sc, "MII write timeout\n"); 2928fca70d2Sjsg } 2938fca70d2Sjsg 2948fca70d2Sjsg void 2958fca70d2Sjsg smsc_miibus_statchg(struct device *dev) 2968fca70d2Sjsg { 2978fca70d2Sjsg struct smsc_softc *sc = (struct smsc_softc *)dev; 2988fca70d2Sjsg struct mii_data *mii = &sc->sc_mii; 2998fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 3008fca70d2Sjsg int err; 3018fca70d2Sjsg uint32_t flow; 3028fca70d2Sjsg uint32_t afc_cfg; 3038fca70d2Sjsg 3048fca70d2Sjsg if (mii == NULL || ifp == NULL || 3058fca70d2Sjsg (ifp->if_flags & IFF_RUNNING) == 0) 3068fca70d2Sjsg return; 3078fca70d2Sjsg 3088fca70d2Sjsg /* Use the MII status to determine link status */ 3098fca70d2Sjsg sc->sc_flags &= ~SMSC_FLAG_LINK; 3108fca70d2Sjsg if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 3118fca70d2Sjsg (IFM_ACTIVE | IFM_AVALID)) { 3128fca70d2Sjsg switch (IFM_SUBTYPE(mii->mii_media_active)) { 3138fca70d2Sjsg case IFM_10_T: 3148fca70d2Sjsg case IFM_100_TX: 3158fca70d2Sjsg sc->sc_flags |= SMSC_FLAG_LINK; 3168fca70d2Sjsg break; 3178fca70d2Sjsg case IFM_1000_T: 3188fca70d2Sjsg /* Gigabit ethernet not supported by chipset */ 3198fca70d2Sjsg break; 3208fca70d2Sjsg default: 3218fca70d2Sjsg break; 3228fca70d2Sjsg } 3238fca70d2Sjsg } 3248fca70d2Sjsg 3258fca70d2Sjsg /* Lost link, do nothing. */ 3268fca70d2Sjsg if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { 3278fca70d2Sjsg smsc_dbg_printf(sc, "link flag not set\n"); 3288fca70d2Sjsg return; 3298fca70d2Sjsg } 3308fca70d2Sjsg 3318fca70d2Sjsg err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg); 3328fca70d2Sjsg if (err) { 3338fca70d2Sjsg smsc_warn_printf(sc, "failed to read initial AFC_CFG, " 3348fca70d2Sjsg "error %d\n", err); 3358fca70d2Sjsg return; 3368fca70d2Sjsg } 3378fca70d2Sjsg 3388fca70d2Sjsg /* Enable/disable full duplex operation and TX/RX pause */ 3398fca70d2Sjsg if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 3408fca70d2Sjsg smsc_dbg_printf(sc, "full duplex operation\n"); 3418fca70d2Sjsg sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN; 3428fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX; 3438fca70d2Sjsg 3448fca70d2Sjsg if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 3458fca70d2Sjsg flow = 0xffff0002; 3468fca70d2Sjsg else 3478fca70d2Sjsg flow = 0; 3488fca70d2Sjsg 3498fca70d2Sjsg if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 3508fca70d2Sjsg afc_cfg |= 0xf; 3518fca70d2Sjsg else 3528fca70d2Sjsg afc_cfg &= ~0xf; 3538fca70d2Sjsg 3548fca70d2Sjsg } else { 3558fca70d2Sjsg smsc_dbg_printf(sc, "half duplex operation\n"); 3568fca70d2Sjsg sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX; 3578fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN; 3588fca70d2Sjsg 3598fca70d2Sjsg flow = 0; 3608fca70d2Sjsg afc_cfg |= 0xf; 3618fca70d2Sjsg } 3628fca70d2Sjsg 3638fca70d2Sjsg err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); 3648fca70d2Sjsg err += smsc_write_reg(sc, SMSC_FLOW, flow); 3658fca70d2Sjsg err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg); 3668fca70d2Sjsg if (err) 3678fca70d2Sjsg smsc_warn_printf(sc, "media change failed, error %d\n", err); 3688fca70d2Sjsg } 3698fca70d2Sjsg 3708fca70d2Sjsg int 3718fca70d2Sjsg smsc_ifmedia_upd(struct ifnet *ifp) 3728fca70d2Sjsg { 3738fca70d2Sjsg struct smsc_softc *sc = ifp->if_softc; 3748fca70d2Sjsg struct mii_data *mii = &sc->sc_mii; 3758fca70d2Sjsg int err; 3768fca70d2Sjsg 3778fca70d2Sjsg if (mii->mii_instance) { 3788fca70d2Sjsg struct mii_softc *miisc; 3798fca70d2Sjsg 3808fca70d2Sjsg LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 3818fca70d2Sjsg mii_phy_reset(miisc); 3828fca70d2Sjsg } 3838fca70d2Sjsg err = mii_mediachg(mii); 3848fca70d2Sjsg return (err); 3858fca70d2Sjsg } 3868fca70d2Sjsg 3878fca70d2Sjsg void 3888fca70d2Sjsg smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 3898fca70d2Sjsg { 3908fca70d2Sjsg struct smsc_softc *sc = ifp->if_softc; 3918fca70d2Sjsg struct mii_data *mii = &sc->sc_mii; 3928fca70d2Sjsg 3938fca70d2Sjsg mii_pollstat(mii); 3948fca70d2Sjsg 3958fca70d2Sjsg ifmr->ifm_active = mii->mii_media_active; 3968fca70d2Sjsg ifmr->ifm_status = mii->mii_media_status; 3978fca70d2Sjsg } 3988fca70d2Sjsg 3998fca70d2Sjsg static inline uint32_t 4008fca70d2Sjsg smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) 4018fca70d2Sjsg { 4028fca70d2Sjsg return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; 4038fca70d2Sjsg } 4048fca70d2Sjsg 4058fca70d2Sjsg void 406f00b3ba9Sbrad smsc_iff(struct smsc_softc *sc) 4078fca70d2Sjsg { 4088fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 409f00b3ba9Sbrad struct arpcom *ac = &sc->sc_ac; 4108fca70d2Sjsg struct ether_multi *enm; 4118fca70d2Sjsg struct ether_multistep step; 4128fca70d2Sjsg uint32_t hashtbl[2] = { 0, 0 }; 4138fca70d2Sjsg uint32_t hash; 4148fca70d2Sjsg 4158fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 4168fca70d2Sjsg return; 4178fca70d2Sjsg 418f00b3ba9Sbrad sc->sc_mac_csr &= ~(SMSC_MAC_CSR_HPFILT | SMSC_MAC_CSR_MCPAS | 419f00b3ba9Sbrad SMSC_MAC_CSR_PRMS); 420f00b3ba9Sbrad ifp->if_flags &= ~IFF_ALLMULTI; 421f00b3ba9Sbrad 422f00b3ba9Sbrad if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) { 423f00b3ba9Sbrad ifp->if_flags |= IFF_ALLMULTI; 4248fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS; 425f00b3ba9Sbrad if (ifp->if_flags & IFF_PROMISC) 426f00b3ba9Sbrad sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; 4278fca70d2Sjsg } else { 4288fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT; 4298fca70d2Sjsg 430f00b3ba9Sbrad ETHER_FIRST_MULTI(step, ac, enm); 4318fca70d2Sjsg while (enm != NULL) { 4328fca70d2Sjsg hash = smsc_hash(enm->enm_addrlo); 433f00b3ba9Sbrad 4348fca70d2Sjsg hashtbl[hash >> 5] |= 1 << (hash & 0x1F); 435f00b3ba9Sbrad 4368fca70d2Sjsg ETHER_NEXT_MULTI(step, enm); 4378fca70d2Sjsg } 438f00b3ba9Sbrad } 4398fca70d2Sjsg 4408fca70d2Sjsg /* Debug */ 441f00b3ba9Sbrad if (sc->sc_mac_csr & SMSC_MAC_CSR_MCPAS) 442f00b3ba9Sbrad smsc_dbg_printf(sc, "receive all multicast enabled\n"); 443f00b3ba9Sbrad else if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) 4448fca70d2Sjsg smsc_dbg_printf(sc, "receive select group of macs\n"); 4458fca70d2Sjsg 4468fca70d2Sjsg /* Write the hash table and mac control registers */ 4478fca70d2Sjsg smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]); 4488fca70d2Sjsg smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]); 4498fca70d2Sjsg smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); 4508fca70d2Sjsg } 4518fca70d2Sjsg 4528fca70d2Sjsg int 4538fca70d2Sjsg smsc_sethwcsum(struct smsc_softc *sc) 4548fca70d2Sjsg { 4558fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 4568fca70d2Sjsg uint32_t val; 4578fca70d2Sjsg int err; 4588fca70d2Sjsg 4598fca70d2Sjsg if (!ifp) 4608fca70d2Sjsg return (-EIO); 4618fca70d2Sjsg 4628fca70d2Sjsg err = smsc_read_reg(sc, SMSC_COE_CTRL, &val); 4638fca70d2Sjsg if (err != 0) { 4648fca70d2Sjsg smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", 4658fca70d2Sjsg err); 4668fca70d2Sjsg return (err); 4678fca70d2Sjsg } 4688fca70d2Sjsg 4698fca70d2Sjsg /* Enable/disable the Rx checksum */ 4708fca70d2Sjsg if (ifp->if_capabilities & IFCAP_CSUM_IPv4) 4718fca70d2Sjsg val |= SMSC_COE_CTRL_RX_EN; 4728fca70d2Sjsg else 4738fca70d2Sjsg val &= ~SMSC_COE_CTRL_RX_EN; 4748fca70d2Sjsg 4758fca70d2Sjsg /* Enable/disable the Tx checksum (currently not supported) */ 4768fca70d2Sjsg if (ifp->if_capabilities & IFCAP_CSUM_IPv4) 4778fca70d2Sjsg val |= SMSC_COE_CTRL_TX_EN; 4788fca70d2Sjsg else 4798fca70d2Sjsg val &= ~SMSC_COE_CTRL_TX_EN; 4808fca70d2Sjsg 4818fca70d2Sjsg err = smsc_write_reg(sc, SMSC_COE_CTRL, val); 4828fca70d2Sjsg if (err != 0) { 4838fca70d2Sjsg smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", 4848fca70d2Sjsg err); 4858fca70d2Sjsg return (err); 4868fca70d2Sjsg } 4878fca70d2Sjsg 4888fca70d2Sjsg return (0); 4898fca70d2Sjsg } 4908fca70d2Sjsg 4918fca70d2Sjsg int 4928fca70d2Sjsg smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr) 4938fca70d2Sjsg { 4948fca70d2Sjsg int err; 4958fca70d2Sjsg uint32_t val; 4968fca70d2Sjsg 4978fca70d2Sjsg smsc_dbg_printf(sc, "setting mac address to " 4988fca70d2Sjsg "%02x:%02x:%02x:%02x:%02x:%02x\n", 4998fca70d2Sjsg addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 5008fca70d2Sjsg 5018fca70d2Sjsg val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; 5028fca70d2Sjsg if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0) 5038fca70d2Sjsg goto done; 5048fca70d2Sjsg 5058fca70d2Sjsg val = (addr[5] << 8) | addr[4]; 5068fca70d2Sjsg err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val); 5078fca70d2Sjsg 5088fca70d2Sjsg done: 5098fca70d2Sjsg return (err); 5108fca70d2Sjsg } 5118fca70d2Sjsg 5128fca70d2Sjsg void 5138fca70d2Sjsg smsc_reset(struct smsc_softc *sc) 5148fca70d2Sjsg { 5158fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 5168fca70d2Sjsg return; 5178fca70d2Sjsg 5188fca70d2Sjsg /* Wait a little while for the chip to get its brains in order. */ 5198fca70d2Sjsg DELAY(1000); 5208fca70d2Sjsg 5218fca70d2Sjsg /* Reinitialize controller to achieve full reset. */ 5228fca70d2Sjsg smsc_chip_init(sc); 5238fca70d2Sjsg } 5248fca70d2Sjsg 5258fca70d2Sjsg void 5268fca70d2Sjsg smsc_init(void *xsc) 5278fca70d2Sjsg { 5288fca70d2Sjsg struct smsc_softc *sc = xsc; 5298fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 5308fca70d2Sjsg struct smsc_chain *c; 5318fca70d2Sjsg usbd_status err; 5328fca70d2Sjsg int s, i; 5338fca70d2Sjsg 5348fca70d2Sjsg s = splnet(); 5358fca70d2Sjsg 5368fca70d2Sjsg /* Cancel pending I/O */ 5378fca70d2Sjsg smsc_stop(sc); 5388fca70d2Sjsg 5398fca70d2Sjsg /* Reset the ethernet interface. */ 5408fca70d2Sjsg smsc_reset(sc); 5418fca70d2Sjsg 5428fca70d2Sjsg /* Init RX ring. */ 5438fca70d2Sjsg if (smsc_rx_list_init(sc) == ENOBUFS) { 5448fca70d2Sjsg printf("%s: rx list init failed\n", sc->sc_dev.dv_xname); 5458fca70d2Sjsg splx(s); 5468fca70d2Sjsg return; 5478fca70d2Sjsg } 5488fca70d2Sjsg 5498fca70d2Sjsg /* Init TX ring. */ 5508fca70d2Sjsg if (smsc_tx_list_init(sc) == ENOBUFS) { 5518fca70d2Sjsg printf("%s: tx list init failed\n", sc->sc_dev.dv_xname); 5528fca70d2Sjsg splx(s); 5538fca70d2Sjsg return; 5548fca70d2Sjsg } 5558fca70d2Sjsg 556f00b3ba9Sbrad /* Program promiscuous mode and multicast filters. */ 557f00b3ba9Sbrad smsc_iff(sc); 5588fca70d2Sjsg 5598fca70d2Sjsg /* Open RX and TX pipes. */ 5608fca70d2Sjsg err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_RX], 5618fca70d2Sjsg USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_RX]); 5628fca70d2Sjsg if (err) { 5638fca70d2Sjsg printf("%s: open rx pipe failed: %s\n", 5648fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 5658fca70d2Sjsg splx(s); 5668fca70d2Sjsg return; 5678fca70d2Sjsg } 5688fca70d2Sjsg 5698fca70d2Sjsg err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_TX], 5708fca70d2Sjsg USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_TX]); 5718fca70d2Sjsg if (err) { 5728fca70d2Sjsg printf("%s: open tx pipe failed: %s\n", 5738fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 5748fca70d2Sjsg splx(s); 5758fca70d2Sjsg return; 5768fca70d2Sjsg } 5778fca70d2Sjsg 5788fca70d2Sjsg /* Start up the receive pipe. */ 5798fca70d2Sjsg for (i = 0; i < SMSC_RX_LIST_CNT; i++) { 5808fca70d2Sjsg c = &sc->sc_cdata.rx_chain[i]; 5818fca70d2Sjsg usbd_setup_xfer(c->sc_xfer, sc->sc_ep[SMSC_ENDPT_RX], 582015115d7Sjsg c, c->sc_buf, sc->sc_bufsz, 5838fca70d2Sjsg USBD_SHORT_XFER_OK | USBD_NO_COPY, 5848fca70d2Sjsg USBD_NO_TIMEOUT, smsc_rxeof); 5858fca70d2Sjsg usbd_transfer(c->sc_xfer); 5868fca70d2Sjsg } 5878fca70d2Sjsg 5888fca70d2Sjsg /* TCP/UDP checksum offload engines. */ 5898fca70d2Sjsg smsc_sethwcsum(sc); 5908fca70d2Sjsg 5918fca70d2Sjsg /* Indicate we are up and running. */ 5928fca70d2Sjsg ifp->if_flags |= IFF_RUNNING; 5938fca70d2Sjsg ifp->if_flags &= ~IFF_OACTIVE; 5948fca70d2Sjsg 5958fca70d2Sjsg timeout_add_sec(&sc->sc_stat_ch, 1); 5968fca70d2Sjsg 5978fca70d2Sjsg splx(s); 5988fca70d2Sjsg } 5998fca70d2Sjsg 6008fca70d2Sjsg void 6018fca70d2Sjsg smsc_start(struct ifnet *ifp) 6028fca70d2Sjsg { 6038fca70d2Sjsg struct smsc_softc *sc = ifp->if_softc; 6048fca70d2Sjsg struct mbuf *m_head = NULL; 6058fca70d2Sjsg 6068fca70d2Sjsg /* Don't send anything if there is no link or controller is busy. */ 6078fca70d2Sjsg if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 || 6088fca70d2Sjsg (ifp->if_flags & IFF_OACTIVE) != 0) { 6098fca70d2Sjsg return; 6108fca70d2Sjsg } 6118fca70d2Sjsg 6128fca70d2Sjsg IFQ_POLL(&ifp->if_snd, m_head); 6138fca70d2Sjsg if (m_head == NULL) 6148fca70d2Sjsg return; 6158fca70d2Sjsg 6168fca70d2Sjsg if (smsc_encap(sc, m_head, 0)) { 6178fca70d2Sjsg ifp->if_flags |= IFF_OACTIVE; 6188fca70d2Sjsg return; 6198fca70d2Sjsg } 6208fca70d2Sjsg IFQ_DEQUEUE(&ifp->if_snd, m_head); 6218fca70d2Sjsg 6228fca70d2Sjsg #if NBPFILTER > 0 6238fca70d2Sjsg if (ifp->if_bpf) 6248fca70d2Sjsg bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); 6258fca70d2Sjsg #endif 6268fca70d2Sjsg ifp->if_flags |= IFF_OACTIVE; 6278fca70d2Sjsg } 6288fca70d2Sjsg 6298fca70d2Sjsg void 6308fca70d2Sjsg smsc_tick(void *xsc) 6318fca70d2Sjsg { 6328fca70d2Sjsg struct smsc_softc *sc = xsc; 6338fca70d2Sjsg 6348fca70d2Sjsg if (sc == NULL) 6358fca70d2Sjsg return; 6368fca70d2Sjsg 6378fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 6388fca70d2Sjsg return; 6398fca70d2Sjsg 6408fca70d2Sjsg usb_add_task(sc->sc_udev, &sc->sc_tick_task); 6418fca70d2Sjsg } 6428fca70d2Sjsg 6438fca70d2Sjsg void 6448fca70d2Sjsg smsc_stop(struct smsc_softc *sc) 6458fca70d2Sjsg { 6468fca70d2Sjsg usbd_status err; 6478fca70d2Sjsg struct ifnet *ifp; 6488fca70d2Sjsg int i; 6498fca70d2Sjsg 6508fca70d2Sjsg smsc_reset(sc); 6518fca70d2Sjsg 6528fca70d2Sjsg ifp = &sc->sc_ac.ac_if; 6538fca70d2Sjsg ifp->if_timer = 0; 6548fca70d2Sjsg ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 6558fca70d2Sjsg 6568fca70d2Sjsg timeout_del(&sc->sc_stat_ch); 6578fca70d2Sjsg 6588fca70d2Sjsg /* Stop transfers. */ 6598fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) { 6601e087f7cSpirofti usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_RX]); 6618fca70d2Sjsg err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_RX]); 6628fca70d2Sjsg if (err) { 6638fca70d2Sjsg printf("%s: close rx pipe failed: %s\n", 6648fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 6658fca70d2Sjsg } 6668fca70d2Sjsg sc->sc_ep[SMSC_ENDPT_RX] = NULL; 6678fca70d2Sjsg } 6688fca70d2Sjsg 6698fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_TX] != NULL) { 6701e087f7cSpirofti usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_TX]); 6718fca70d2Sjsg err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_TX]); 6728fca70d2Sjsg if (err) { 6738fca70d2Sjsg printf("%s: close tx pipe failed: %s\n", 6748fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 6758fca70d2Sjsg } 6768fca70d2Sjsg sc->sc_ep[SMSC_ENDPT_TX] = NULL; 6778fca70d2Sjsg } 6788fca70d2Sjsg 6798fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL) { 6801e087f7cSpirofti usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_INTR]); 6818fca70d2Sjsg err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_INTR]); 6828fca70d2Sjsg if (err) { 6838fca70d2Sjsg printf("%s: close intr pipe failed: %s\n", 6848fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 6858fca70d2Sjsg } 6868fca70d2Sjsg sc->sc_ep[SMSC_ENDPT_INTR] = NULL; 6878fca70d2Sjsg } 6888fca70d2Sjsg 6898fca70d2Sjsg /* Free RX resources. */ 6908fca70d2Sjsg for (i = 0; i < SMSC_RX_LIST_CNT; i++) { 6918fca70d2Sjsg if (sc->sc_cdata.rx_chain[i].sc_mbuf != NULL) { 6928fca70d2Sjsg m_freem(sc->sc_cdata.rx_chain[i].sc_mbuf); 6938fca70d2Sjsg sc->sc_cdata.rx_chain[i].sc_mbuf = NULL; 6948fca70d2Sjsg } 6958fca70d2Sjsg if (sc->sc_cdata.rx_chain[i].sc_xfer != NULL) { 6968fca70d2Sjsg usbd_free_xfer(sc->sc_cdata.rx_chain[i].sc_xfer); 6978fca70d2Sjsg sc->sc_cdata.rx_chain[i].sc_xfer = NULL; 6988fca70d2Sjsg } 6998fca70d2Sjsg } 7008fca70d2Sjsg 7018fca70d2Sjsg /* Free TX resources. */ 7028fca70d2Sjsg for (i = 0; i < SMSC_TX_LIST_CNT; i++) { 7038fca70d2Sjsg if (sc->sc_cdata.tx_chain[i].sc_mbuf != NULL) { 7048fca70d2Sjsg m_freem(sc->sc_cdata.tx_chain[i].sc_mbuf); 7058fca70d2Sjsg sc->sc_cdata.tx_chain[i].sc_mbuf = NULL; 7068fca70d2Sjsg } 7078fca70d2Sjsg if (sc->sc_cdata.tx_chain[i].sc_xfer != NULL) { 7088fca70d2Sjsg usbd_free_xfer(sc->sc_cdata.tx_chain[i].sc_xfer); 7098fca70d2Sjsg sc->sc_cdata.tx_chain[i].sc_xfer = NULL; 7108fca70d2Sjsg } 7118fca70d2Sjsg } 7128fca70d2Sjsg } 7138fca70d2Sjsg 7148fca70d2Sjsg int 7158fca70d2Sjsg smsc_chip_init(struct smsc_softc *sc) 7168fca70d2Sjsg { 7178fca70d2Sjsg int err; 7188fca70d2Sjsg uint32_t reg_val; 7198fca70d2Sjsg int burst_cap; 7208fca70d2Sjsg 7218fca70d2Sjsg /* Enter H/W config mode */ 7228fca70d2Sjsg smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST); 7238fca70d2Sjsg 7248fca70d2Sjsg if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG, 7258fca70d2Sjsg SMSC_HW_CFG_LRST)) != 0) { 7268fca70d2Sjsg smsc_warn_printf(sc, "timed-out waiting for reset to " 7278fca70d2Sjsg "complete\n"); 7288fca70d2Sjsg goto init_failed; 7298fca70d2Sjsg } 7308fca70d2Sjsg 7318fca70d2Sjsg /* Reset the PHY */ 7328fca70d2Sjsg smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST); 7338fca70d2Sjsg 7348fca70d2Sjsg if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL, 7358fca70d2Sjsg SMSC_PM_CTRL_PHY_RST) != 0)) { 7368fca70d2Sjsg smsc_warn_printf(sc, "timed-out waiting for phy reset to " 7378fca70d2Sjsg "complete\n"); 7388fca70d2Sjsg goto init_failed; 7398fca70d2Sjsg } 7408fca70d2Sjsg usbd_delay_ms(sc->sc_udev, 40); 7418fca70d2Sjsg 7428fca70d2Sjsg /* Set the mac address */ 7438fca70d2Sjsg if ((err = smsc_setmacaddress(sc, sc->sc_ac.ac_enaddr)) != 0) { 7448fca70d2Sjsg smsc_warn_printf(sc, "failed to set the MAC address\n"); 7458fca70d2Sjsg goto init_failed; 7468fca70d2Sjsg } 7478fca70d2Sjsg 7488fca70d2Sjsg /* 7498fca70d2Sjsg * Don't know what the HW_CFG_BIR bit is, but following the reset 7508fca70d2Sjsg * sequence as used in the Linux driver. 7518fca70d2Sjsg */ 7528fca70d2Sjsg if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) != 0) { 7538fca70d2Sjsg smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err); 7548fca70d2Sjsg goto init_failed; 7558fca70d2Sjsg } 7568fca70d2Sjsg reg_val |= SMSC_HW_CFG_BIR; 7578fca70d2Sjsg smsc_write_reg(sc, SMSC_HW_CFG, reg_val); 7588fca70d2Sjsg 7598fca70d2Sjsg /* 7608fca70d2Sjsg * There is a so called 'turbo mode' that the linux driver supports, it 7618fca70d2Sjsg * seems to allow you to jam multiple frames per Rx transaction. 7628fca70d2Sjsg * By default this driver supports that and therefore allows multiple 7638fca70d2Sjsg * frames per URB. 7648fca70d2Sjsg * 7658fca70d2Sjsg * The xfer buffer size needs to reflect this as well, therefore based 7668fca70d2Sjsg * on the calculations in the Linux driver the RX bufsize is set to 7678fca70d2Sjsg * 18944, 7688fca70d2Sjsg * bufsz = (16 * 1024 + 5 * 512) 7698fca70d2Sjsg * 7708fca70d2Sjsg * Burst capability is the number of URBs that can be in a burst of 7718fca70d2Sjsg * data/ethernet frames. 7728fca70d2Sjsg */ 7738fca70d2Sjsg #ifdef SMSC_TURBO 7748fca70d2Sjsg if (sc->sc_udev->speed == USB_SPEED_HIGH) 7758fca70d2Sjsg burst_cap = 37; 7768fca70d2Sjsg else 7778fca70d2Sjsg burst_cap = 128; 7788fca70d2Sjsg #else 7798fca70d2Sjsg burst_cap = 0; 7808fca70d2Sjsg #endif 7818fca70d2Sjsg 7828fca70d2Sjsg smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap); 7838fca70d2Sjsg 7848fca70d2Sjsg /* Set the default bulk in delay (magic value from Linux driver) */ 7858fca70d2Sjsg smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000); 7868fca70d2Sjsg 7878fca70d2Sjsg 7888fca70d2Sjsg 7898fca70d2Sjsg /* 7908fca70d2Sjsg * Initialise the RX interface 7918fca70d2Sjsg */ 7928fca70d2Sjsg if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_val)) < 0) { 7938fca70d2Sjsg smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n", 7948fca70d2Sjsg err); 7958fca70d2Sjsg goto init_failed; 7968fca70d2Sjsg } 7978fca70d2Sjsg 7988fca70d2Sjsg /* 7998fca70d2Sjsg * The following setings are used for 'turbo mode', a.k.a multiple 8008fca70d2Sjsg * frames per Rx transaction (again info taken form Linux driver). 8018fca70d2Sjsg */ 8028fca70d2Sjsg #ifdef SMSC_TURBO 8038fca70d2Sjsg reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE); 8048fca70d2Sjsg #endif 8058fca70d2Sjsg 8068fca70d2Sjsg smsc_write_reg(sc, SMSC_HW_CFG, reg_val); 8078fca70d2Sjsg 8088fca70d2Sjsg /* Clear the status register ? */ 8098fca70d2Sjsg smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff); 8108fca70d2Sjsg 8118fca70d2Sjsg /* Read and display the revision register */ 8128fca70d2Sjsg if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) { 8138fca70d2Sjsg smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err); 8148fca70d2Sjsg goto init_failed; 8158fca70d2Sjsg } 8168fca70d2Sjsg 8178fca70d2Sjsg /* GPIO/LED setup */ 8188fca70d2Sjsg reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED | 8198fca70d2Sjsg SMSC_LED_GPIO_CFG_FDX_LED; 8208fca70d2Sjsg smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val); 8218fca70d2Sjsg 8228fca70d2Sjsg /* 8238fca70d2Sjsg * Initialise the TX interface 8248fca70d2Sjsg */ 8258fca70d2Sjsg smsc_write_reg(sc, SMSC_FLOW, 0); 8268fca70d2Sjsg 8278fca70d2Sjsg smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT); 8288fca70d2Sjsg 8298fca70d2Sjsg /* Read the current MAC configuration */ 8308fca70d2Sjsg if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) { 8318fca70d2Sjsg smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err); 8328fca70d2Sjsg goto init_failed; 8338fca70d2Sjsg } 8348fca70d2Sjsg 8358fca70d2Sjsg /* Vlan */ 8368fca70d2Sjsg smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN); 8378fca70d2Sjsg 8388fca70d2Sjsg /* 8398fca70d2Sjsg * Start TX 8408fca70d2Sjsg */ 8418fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN; 8428fca70d2Sjsg smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); 8438fca70d2Sjsg smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON); 8448fca70d2Sjsg 8458fca70d2Sjsg /* 8468fca70d2Sjsg * Start RX 8478fca70d2Sjsg */ 8488fca70d2Sjsg sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN; 8498fca70d2Sjsg smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); 8508fca70d2Sjsg 8518fca70d2Sjsg return (0); 8528fca70d2Sjsg 8538fca70d2Sjsg init_failed: 8548fca70d2Sjsg smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err); 8558fca70d2Sjsg return (err); 8568fca70d2Sjsg } 8578fca70d2Sjsg 8588fca70d2Sjsg int 8598fca70d2Sjsg smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 8608fca70d2Sjsg { 8618fca70d2Sjsg struct smsc_softc *sc = ifp->if_softc; 8628fca70d2Sjsg struct ifreq *ifr = (struct ifreq *)data; 8638fca70d2Sjsg struct ifaddr *ifa = (struct ifaddr *)data; 8648fca70d2Sjsg int s, error = 0; 8658fca70d2Sjsg 8668fca70d2Sjsg s = splnet(); 8678fca70d2Sjsg 8688fca70d2Sjsg switch(cmd) { 8698fca70d2Sjsg case SIOCSIFADDR: 8708fca70d2Sjsg ifp->if_flags |= IFF_UP; 8718fca70d2Sjsg if (!(ifp->if_flags & IFF_RUNNING)) 8728fca70d2Sjsg smsc_init(sc); 8738fca70d2Sjsg if (ifa->ifa_addr->sa_family == AF_INET) 8748fca70d2Sjsg arp_ifinit(&sc->sc_ac, ifa); 8758fca70d2Sjsg break; 8768fca70d2Sjsg 8778fca70d2Sjsg case SIOCSIFFLAGS: 8788fca70d2Sjsg if (ifp->if_flags & IFF_UP) { 879f00b3ba9Sbrad if (ifp->if_flags & IFF_RUNNING) 880f00b3ba9Sbrad error = ENETRESET; 881f00b3ba9Sbrad else 8828fca70d2Sjsg smsc_init(sc); 8838fca70d2Sjsg } else { 8848fca70d2Sjsg if (ifp->if_flags & IFF_RUNNING) 8858fca70d2Sjsg smsc_stop(sc); 8868fca70d2Sjsg } 8878fca70d2Sjsg break; 8888fca70d2Sjsg 8898fca70d2Sjsg case SIOCGIFMEDIA: 8908fca70d2Sjsg case SIOCSIFMEDIA: 891f00b3ba9Sbrad error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); 8928fca70d2Sjsg break; 8938fca70d2Sjsg 8948fca70d2Sjsg default: 8958fca70d2Sjsg error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); 8968fca70d2Sjsg } 8978fca70d2Sjsg 8988fca70d2Sjsg if (error == ENETRESET) { 8998fca70d2Sjsg if (ifp->if_flags & IFF_RUNNING) 900f00b3ba9Sbrad smsc_iff(sc); 9018fca70d2Sjsg error = 0; 9028fca70d2Sjsg } 9038fca70d2Sjsg 9048fca70d2Sjsg splx(s); 9058fca70d2Sjsg return(error); 9068fca70d2Sjsg } 9078fca70d2Sjsg 9088fca70d2Sjsg int 9098fca70d2Sjsg smsc_match(struct device *parent, void *match, void *aux) 9108fca70d2Sjsg { 9118fca70d2Sjsg struct usb_attach_arg *uaa = aux; 9128fca70d2Sjsg 9138fca70d2Sjsg if (uaa->iface != NULL) 9148fca70d2Sjsg return UMATCH_NONE; 9158fca70d2Sjsg 9168fca70d2Sjsg return (usb_lookup(smsc_devs, uaa->vendor, uaa->product) != NULL) ? 9178fca70d2Sjsg UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 9188fca70d2Sjsg } 9198fca70d2Sjsg 9208fca70d2Sjsg void 9218fca70d2Sjsg smsc_attach(struct device *parent, struct device *self, void *aux) 9228fca70d2Sjsg { 9238fca70d2Sjsg struct smsc_softc *sc = (struct smsc_softc *)self; 9248fca70d2Sjsg struct usb_attach_arg *uaa = aux; 925ab0b1be7Smglocker struct usbd_device *dev = uaa->device; 9268fca70d2Sjsg usb_interface_descriptor_t *id; 9278fca70d2Sjsg usb_endpoint_descriptor_t *ed; 9288fca70d2Sjsg struct mii_data *mii; 9298fca70d2Sjsg struct ifnet *ifp; 9308fca70d2Sjsg int err, s, i; 9318fca70d2Sjsg uint32_t mac_h, mac_l; 9328fca70d2Sjsg 9338fca70d2Sjsg sc->sc_udev = dev; 9348fca70d2Sjsg 9358fca70d2Sjsg err = usbd_set_config_no(dev, SMSC_CONFIG_INDEX, 1); 9368fca70d2Sjsg 9378fca70d2Sjsg /* Setup the endpoints for the SMSC LAN95xx device(s) */ 9388fca70d2Sjsg usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc, 9398fca70d2Sjsg USB_TASK_TYPE_GENERIC); 9408fca70d2Sjsg rw_init(&sc->sc_mii_lock, "smscmii"); 9418fca70d2Sjsg usb_init_task(&sc->sc_stop_task, (void (*)(void *))smsc_stop, sc, 9428fca70d2Sjsg USB_TASK_TYPE_GENERIC); 9438fca70d2Sjsg 9448fca70d2Sjsg err = usbd_device2interface_handle(dev, SMSC_IFACE_IDX, &sc->sc_iface); 9458fca70d2Sjsg if (err) { 9468fca70d2Sjsg printf("%s: getting interface handle failed\n", 9478fca70d2Sjsg sc->sc_dev.dv_xname); 9488fca70d2Sjsg return; 9498fca70d2Sjsg } 9508fca70d2Sjsg 9518fca70d2Sjsg id = usbd_get_interface_descriptor(sc->sc_iface); 9528fca70d2Sjsg 953015115d7Sjsg if (sc->sc_udev->speed >= USB_SPEED_HIGH) 954015115d7Sjsg sc->sc_bufsz = SMSC_MAX_BUFSZ; 955015115d7Sjsg else 956015115d7Sjsg sc->sc_bufsz = SMSC_MIN_BUFSZ; 957015115d7Sjsg 9588fca70d2Sjsg /* Find endpoints. */ 9598fca70d2Sjsg for (i = 0; i < id->bNumEndpoints; i++) { 9608fca70d2Sjsg ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 9618fca70d2Sjsg if (!ed) { 9628fca70d2Sjsg printf("%s: couldn't get ep %d\n", 9638fca70d2Sjsg sc->sc_dev.dv_xname, i); 9648fca70d2Sjsg return; 9658fca70d2Sjsg } 9668fca70d2Sjsg if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 9678fca70d2Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 9688fca70d2Sjsg sc->sc_ed[SMSC_ENDPT_RX] = ed->bEndpointAddress; 9698fca70d2Sjsg } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 9708fca70d2Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 9718fca70d2Sjsg sc->sc_ed[SMSC_ENDPT_TX] = ed->bEndpointAddress; 9728fca70d2Sjsg } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 9738fca70d2Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 9748fca70d2Sjsg sc->sc_ed[SMSC_ENDPT_INTR] = ed->bEndpointAddress; 9758fca70d2Sjsg } 9768fca70d2Sjsg } 9778fca70d2Sjsg 9788fca70d2Sjsg s = splnet(); 9798fca70d2Sjsg 9808fca70d2Sjsg ifp = &sc->sc_ac.ac_if; 9818fca70d2Sjsg ifp->if_softc = sc; 9828fca70d2Sjsg strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); 9838fca70d2Sjsg ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 9848fca70d2Sjsg ifp->if_ioctl = smsc_ioctl; 9858fca70d2Sjsg ifp->if_start = smsc_start; 9868fca70d2Sjsg ifp->if_capabilities = IFCAP_VLAN_MTU; 9878fca70d2Sjsg 9888fca70d2Sjsg /* Setup some of the basics */ 9898fca70d2Sjsg sc->sc_phyno = 1; 9908fca70d2Sjsg 9918fca70d2Sjsg /* 9928fca70d2Sjsg * Attempt to get the mac address, if an EEPROM is not attached this 9938fca70d2Sjsg * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC 9948fca70d2Sjsg * address based on urandom. 9958fca70d2Sjsg */ 9968fca70d2Sjsg memset(sc->sc_ac.ac_enaddr, 0xff, ETHER_ADDR_LEN); 9978fca70d2Sjsg 9988fca70d2Sjsg /* Check if there is already a MAC address in the register */ 9998fca70d2Sjsg if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) && 10008fca70d2Sjsg (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) { 10018fca70d2Sjsg sc->sc_ac.ac_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); 10028fca70d2Sjsg sc->sc_ac.ac_enaddr[4] = (uint8_t)((mac_h) & 0xff); 10038fca70d2Sjsg sc->sc_ac.ac_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); 10048fca70d2Sjsg sc->sc_ac.ac_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); 10058fca70d2Sjsg sc->sc_ac.ac_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); 10068fca70d2Sjsg sc->sc_ac.ac_enaddr[0] = (uint8_t)((mac_l) & 0xff); 10078fca70d2Sjsg } 10088fca70d2Sjsg 10098fca70d2Sjsg printf("%s: address %s\n", sc->sc_dev.dv_xname, 10108fca70d2Sjsg ether_sprintf(sc->sc_ac.ac_enaddr)); 10118fca70d2Sjsg 10128fca70d2Sjsg /* Initialise the chip for the first time */ 10138fca70d2Sjsg smsc_chip_init(sc); 10148fca70d2Sjsg 10158fca70d2Sjsg IFQ_SET_READY(&ifp->if_snd); 10168fca70d2Sjsg 10178fca70d2Sjsg /* Initialize MII/media info. */ 10188fca70d2Sjsg mii = &sc->sc_mii; 10198fca70d2Sjsg mii->mii_ifp = ifp; 10208fca70d2Sjsg mii->mii_readreg = smsc_miibus_readreg; 10218fca70d2Sjsg mii->mii_writereg = smsc_miibus_writereg; 10228fca70d2Sjsg mii->mii_statchg = smsc_miibus_statchg; 10238fca70d2Sjsg mii->mii_flags = MIIF_AUTOTSLEEP; 10248fca70d2Sjsg 10258fca70d2Sjsg ifmedia_init(&mii->mii_media, 0, smsc_ifmedia_upd, smsc_ifmedia_sts); 10268fca70d2Sjsg mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); 10278fca70d2Sjsg 10288fca70d2Sjsg if (LIST_FIRST(&mii->mii_phys) == NULL) { 10298fca70d2Sjsg ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); 10308fca70d2Sjsg ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); 10318fca70d2Sjsg } else 10328fca70d2Sjsg ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); 10338fca70d2Sjsg 10348fca70d2Sjsg if_attach(ifp); 10358fca70d2Sjsg ether_ifattach(ifp); 10368fca70d2Sjsg 10378fca70d2Sjsg timeout_set(&sc->sc_stat_ch, smsc_tick, sc); 10388fca70d2Sjsg 10398fca70d2Sjsg splx(s); 10408fca70d2Sjsg } 10418fca70d2Sjsg 10428fca70d2Sjsg int 10438fca70d2Sjsg smsc_detach(struct device *self, int flags) 10448fca70d2Sjsg { 10458fca70d2Sjsg struct smsc_softc *sc = (struct smsc_softc *)self; 10468fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 10478fca70d2Sjsg int s; 10488fca70d2Sjsg 10498fca70d2Sjsg if (timeout_initialized(&sc->sc_stat_ch)) 10508fca70d2Sjsg timeout_del(&sc->sc_stat_ch); 10518fca70d2Sjsg 10528fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_TX] != NULL) 10538fca70d2Sjsg usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_TX]); 10548fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) 10558fca70d2Sjsg usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_RX]); 10568fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL) 10578fca70d2Sjsg usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_INTR]); 10588fca70d2Sjsg 10598fca70d2Sjsg /* 10608fca70d2Sjsg * Remove any pending tasks. They cannot be executing because they run 10618fca70d2Sjsg * in the same thread as detach. 10628fca70d2Sjsg */ 10638fca70d2Sjsg usb_rem_task(sc->sc_udev, &sc->sc_tick_task); 10648fca70d2Sjsg usb_rem_task(sc->sc_udev, &sc->sc_stop_task); 10658fca70d2Sjsg 10668fca70d2Sjsg s = splusb(); 10678fca70d2Sjsg 10688fca70d2Sjsg if (--sc->sc_refcnt >= 0) { 10698fca70d2Sjsg /* Wait for processes to go away */ 10708fca70d2Sjsg usb_detach_wait(&sc->sc_dev); 10718fca70d2Sjsg } 10728fca70d2Sjsg 10738fca70d2Sjsg if (ifp->if_flags & IFF_RUNNING) 10748fca70d2Sjsg smsc_stop(sc); 10758fca70d2Sjsg 10768fca70d2Sjsg mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); 10778fca70d2Sjsg ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); 10788fca70d2Sjsg if (ifp->if_softc != NULL) { 10798fca70d2Sjsg ether_ifdetach(ifp); 10808fca70d2Sjsg if_detach(ifp); 10818fca70d2Sjsg } 10828fca70d2Sjsg 10838fca70d2Sjsg #ifdef DIAGNOSTIC 10848fca70d2Sjsg if (sc->sc_ep[SMSC_ENDPT_TX] != NULL || 10858fca70d2Sjsg sc->sc_ep[SMSC_ENDPT_RX] != NULL || 10868fca70d2Sjsg sc->sc_ep[SMSC_ENDPT_INTR] != NULL) 10878fca70d2Sjsg printf("%s: detach has active endpoints\n", 10888fca70d2Sjsg sc->sc_dev.dv_xname); 10898fca70d2Sjsg #endif 10908fca70d2Sjsg 10918fca70d2Sjsg if (--sc->sc_refcnt >= 0) { 10928fca70d2Sjsg /* Wait for processes to go away. */ 10938fca70d2Sjsg usb_detach_wait(&sc->sc_dev); 10948fca70d2Sjsg } 10958fca70d2Sjsg splx(s); 10968fca70d2Sjsg 10978fca70d2Sjsg return (0); 10988fca70d2Sjsg } 10998fca70d2Sjsg 11008fca70d2Sjsg void 11018fca70d2Sjsg smsc_tick_task(void *xsc) 11028fca70d2Sjsg { 11038fca70d2Sjsg int s; 11048fca70d2Sjsg struct smsc_softc *sc = xsc; 11058fca70d2Sjsg struct ifnet *ifp; 11068fca70d2Sjsg struct mii_data *mii; 11078fca70d2Sjsg 11088fca70d2Sjsg if (sc == NULL) 11098fca70d2Sjsg return; 11108fca70d2Sjsg 11118fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 11128fca70d2Sjsg return; 11138fca70d2Sjsg ifp = &sc->sc_ac.ac_if; 11148fca70d2Sjsg mii = &sc->sc_mii; 11158fca70d2Sjsg if (mii == NULL) 11168fca70d2Sjsg return; 11178fca70d2Sjsg 11188fca70d2Sjsg s = splnet(); 11198fca70d2Sjsg 11208fca70d2Sjsg mii_tick(mii); 11218fca70d2Sjsg if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) 11228fca70d2Sjsg smsc_miibus_statchg(&sc->sc_dev); 11238fca70d2Sjsg timeout_add_sec(&sc->sc_stat_ch, 1); 11248fca70d2Sjsg 11258fca70d2Sjsg splx(s); 11268fca70d2Sjsg } 11278fca70d2Sjsg 11288fca70d2Sjsg void 11298fca70d2Sjsg smsc_lock_mii(struct smsc_softc *sc) 11308fca70d2Sjsg { 11318fca70d2Sjsg sc->sc_refcnt++; 11328fca70d2Sjsg rw_enter_write(&sc->sc_mii_lock); 11338fca70d2Sjsg } 11348fca70d2Sjsg 11358fca70d2Sjsg void 11368fca70d2Sjsg smsc_unlock_mii(struct smsc_softc *sc) 11378fca70d2Sjsg { 11388fca70d2Sjsg rw_exit_write(&sc->sc_mii_lock); 11398fca70d2Sjsg if (--sc->sc_refcnt < 0) 11408fca70d2Sjsg usb_detach_wakeup(&sc->sc_dev); 11418fca70d2Sjsg } 11428fca70d2Sjsg 11438fca70d2Sjsg void 1144ab0b1be7Smglocker smsc_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) 11458fca70d2Sjsg { 11468fca70d2Sjsg struct smsc_chain *c = (struct smsc_chain *)priv; 11478fca70d2Sjsg struct smsc_softc *sc = c->sc_sc; 11488fca70d2Sjsg struct ifnet *ifp = &sc->sc_ac.ac_if; 11498fca70d2Sjsg u_char *buf = c->sc_buf; 11508fca70d2Sjsg uint32_t total_len; 11518fca70d2Sjsg uint16_t pktlen = 0; 1152*469696c0Smpi struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 11538fca70d2Sjsg struct mbuf *m; 11548fca70d2Sjsg int s; 11558fca70d2Sjsg uint32_t rxhdr; 11568fca70d2Sjsg 11578fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 11588fca70d2Sjsg return; 11598fca70d2Sjsg 11608fca70d2Sjsg if (!(ifp->if_flags & IFF_RUNNING)) 11618fca70d2Sjsg return; 11628fca70d2Sjsg 11638fca70d2Sjsg if (status != USBD_NORMAL_COMPLETION) { 11648fca70d2Sjsg if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 11658fca70d2Sjsg return; 11668fca70d2Sjsg if (usbd_ratecheck(&sc->sc_rx_notice)) { 11678fca70d2Sjsg printf("%s: usb errors on rx: %s\n", 11688fca70d2Sjsg sc->sc_dev.dv_xname, usbd_errstr(status)); 11698fca70d2Sjsg } 11708fca70d2Sjsg if (status == USBD_STALLED) 11718fca70d2Sjsg usbd_clear_endpoint_stall_async(sc->sc_ep[SMSC_ENDPT_RX]); 11728fca70d2Sjsg goto done; 11738fca70d2Sjsg } 11748fca70d2Sjsg 11758fca70d2Sjsg usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); 11768fca70d2Sjsg smsc_dbg_printf(sc, "xfer status total_len %d\n", total_len); 11778fca70d2Sjsg 11788fca70d2Sjsg do { 11798fca70d2Sjsg if (total_len < sizeof(rxhdr)) { 11808fca70d2Sjsg smsc_dbg_printf(sc, "total_len %d < sizeof(rxhdr) %d\n", 11818fca70d2Sjsg total_len, sizeof(rxhdr)); 11828fca70d2Sjsg ifp->if_ierrors++; 11838fca70d2Sjsg goto done; 11848fca70d2Sjsg } 11858fca70d2Sjsg 11868fca70d2Sjsg buf += pktlen; 11878fca70d2Sjsg 11888fca70d2Sjsg memcpy(&rxhdr, buf, sizeof(rxhdr)); 11898fca70d2Sjsg rxhdr = letoh32(rxhdr); 11908fca70d2Sjsg total_len -= sizeof(rxhdr); 11918fca70d2Sjsg 11928fca70d2Sjsg if (rxhdr & SMSC_RX_STAT_ERROR) { 11938fca70d2Sjsg smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr); 11948fca70d2Sjsg ifp->if_ierrors++; 11958fca70d2Sjsg goto done; 11968fca70d2Sjsg } 11978fca70d2Sjsg 11988fca70d2Sjsg pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); 11998fca70d2Sjsg smsc_dbg_printf(sc, "rxeof total_len %d pktlen %d rxhdr " 12008fca70d2Sjsg "0x%08x\n", total_len, pktlen, rxhdr); 12018fca70d2Sjsg if (pktlen > total_len) { 12028fca70d2Sjsg smsc_dbg_printf(sc, "pktlen %d > total_len %d\n", 12038fca70d2Sjsg pktlen, total_len); 12048fca70d2Sjsg ifp->if_ierrors++; 12058fca70d2Sjsg goto done; 12068fca70d2Sjsg } 12078fca70d2Sjsg 12088fca70d2Sjsg buf += sizeof(rxhdr); 12098fca70d2Sjsg 1210d3fe6d07Sbrad if (total_len < pktlen) 12118fca70d2Sjsg total_len = 0; 12128fca70d2Sjsg else 12138fca70d2Sjsg total_len -= pktlen; 12148fca70d2Sjsg 12158fca70d2Sjsg m = smsc_newbuf(); 12168fca70d2Sjsg if (m == NULL) { 12178fca70d2Sjsg smsc_dbg_printf(sc, "smc_newbuf returned NULL\n"); 12188fca70d2Sjsg ifp->if_ierrors++; 12198fca70d2Sjsg goto done; 12208fca70d2Sjsg } 12218fca70d2Sjsg 12228fca70d2Sjsg ifp->if_ipackets++; 12238fca70d2Sjsg m->m_pkthdr.len = m->m_len = pktlen; 12248fca70d2Sjsg m_adj(m, ETHER_ALIGN); 12258fca70d2Sjsg 12268fca70d2Sjsg memcpy(mtod(m, char *), buf, pktlen); 12278fca70d2Sjsg 1228*469696c0Smpi ml_enqueue(&ml, m); 12298fca70d2Sjsg } while (total_len > 0); 12308fca70d2Sjsg 12318fca70d2Sjsg done: 1232*469696c0Smpi s = splnet(); 1233*469696c0Smpi if_input(ifp, &ml); 1234*469696c0Smpi splx(s); 1235015115d7Sjsg memset(c->sc_buf, 0, sc->sc_bufsz); 12368fca70d2Sjsg 12378fca70d2Sjsg /* Setup new transfer. */ 12388fca70d2Sjsg usbd_setup_xfer(xfer, sc->sc_ep[SMSC_ENDPT_RX], 1239015115d7Sjsg c, c->sc_buf, sc->sc_bufsz, 12408fca70d2Sjsg USBD_SHORT_XFER_OK | USBD_NO_COPY, 12418fca70d2Sjsg USBD_NO_TIMEOUT, smsc_rxeof); 12428fca70d2Sjsg usbd_transfer(xfer); 12438fca70d2Sjsg 12448fca70d2Sjsg return; 12458fca70d2Sjsg } 12468fca70d2Sjsg 12478fca70d2Sjsg void 1248ab0b1be7Smglocker smsc_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) 12498fca70d2Sjsg { 12508fca70d2Sjsg struct smsc_softc *sc; 12518fca70d2Sjsg struct smsc_chain *c; 12528fca70d2Sjsg struct ifnet *ifp; 12538fca70d2Sjsg int s; 12548fca70d2Sjsg 12558fca70d2Sjsg c = priv; 12568fca70d2Sjsg sc = c->sc_sc; 12578fca70d2Sjsg ifp = &sc->sc_ac.ac_if; 12588fca70d2Sjsg 12598fca70d2Sjsg if (usbd_is_dying(sc->sc_udev)) 12608fca70d2Sjsg return; 12618fca70d2Sjsg 12628fca70d2Sjsg s = splnet(); 12638fca70d2Sjsg 12648fca70d2Sjsg if (status != USBD_NORMAL_COMPLETION) { 12658fca70d2Sjsg if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { 12668fca70d2Sjsg splx(s); 12678fca70d2Sjsg return; 12688fca70d2Sjsg } 12698fca70d2Sjsg ifp->if_oerrors++; 12708fca70d2Sjsg printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname, 12718fca70d2Sjsg usbd_errstr(status)); 12728fca70d2Sjsg if (status == USBD_STALLED) 12738fca70d2Sjsg usbd_clear_endpoint_stall_async(sc->sc_ep[SMSC_ENDPT_TX]); 12748fca70d2Sjsg splx(s); 12758fca70d2Sjsg return; 12768fca70d2Sjsg } 12778fca70d2Sjsg 12788fca70d2Sjsg ifp->if_timer = 0; 12798fca70d2Sjsg ifp->if_flags &= ~IFF_OACTIVE; 12808fca70d2Sjsg 12818fca70d2Sjsg m_freem(c->sc_mbuf); 12828fca70d2Sjsg c->sc_mbuf = NULL; 12838fca70d2Sjsg 12848fca70d2Sjsg if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) 12858fca70d2Sjsg smsc_start(ifp); 12868fca70d2Sjsg 12878fca70d2Sjsg ifp->if_opackets++; 12888fca70d2Sjsg splx(s); 12898fca70d2Sjsg } 12908fca70d2Sjsg 12918fca70d2Sjsg int 12928fca70d2Sjsg smsc_tx_list_init(struct smsc_softc *sc) 12938fca70d2Sjsg { 12948fca70d2Sjsg struct smsc_cdata *cd; 12958fca70d2Sjsg struct smsc_chain *c; 12968fca70d2Sjsg int i; 12978fca70d2Sjsg 12988fca70d2Sjsg cd = &sc->sc_cdata; 12998fca70d2Sjsg for (i = 0; i < SMSC_TX_LIST_CNT; i++) { 13008fca70d2Sjsg c = &cd->tx_chain[i]; 13018fca70d2Sjsg c->sc_sc = sc; 13028fca70d2Sjsg c->sc_idx = i; 13038fca70d2Sjsg c->sc_mbuf = NULL; 13048fca70d2Sjsg if (c->sc_xfer == NULL) { 13058fca70d2Sjsg c->sc_xfer = usbd_alloc_xfer(sc->sc_udev); 13068fca70d2Sjsg if (c->sc_xfer == NULL) 13078fca70d2Sjsg return (ENOBUFS); 13088fca70d2Sjsg c->sc_buf = usbd_alloc_buffer(c->sc_xfer, 1309015115d7Sjsg sc->sc_bufsz); 13108fca70d2Sjsg if (c->sc_buf == NULL) { 13118fca70d2Sjsg usbd_free_xfer(c->sc_xfer); 13128fca70d2Sjsg return (ENOBUFS); 13138fca70d2Sjsg } 13148fca70d2Sjsg } 13158fca70d2Sjsg } 13168fca70d2Sjsg 13178fca70d2Sjsg return (0); 13188fca70d2Sjsg } 13198fca70d2Sjsg 13208fca70d2Sjsg int 13218fca70d2Sjsg smsc_rx_list_init(struct smsc_softc *sc) 13228fca70d2Sjsg { 13238fca70d2Sjsg struct smsc_cdata *cd; 13248fca70d2Sjsg struct smsc_chain *c; 13258fca70d2Sjsg int i; 13268fca70d2Sjsg 13278fca70d2Sjsg cd = &sc->sc_cdata; 13288fca70d2Sjsg for (i = 0; i < SMSC_RX_LIST_CNT; i++) { 13298fca70d2Sjsg c = &cd->rx_chain[i]; 13308fca70d2Sjsg c->sc_sc = sc; 13318fca70d2Sjsg c->sc_idx = i; 13328fca70d2Sjsg c->sc_mbuf = NULL; 13338fca70d2Sjsg if (c->sc_xfer == NULL) { 13348fca70d2Sjsg c->sc_xfer = usbd_alloc_xfer(sc->sc_udev); 13358fca70d2Sjsg if (c->sc_xfer == NULL) 13368fca70d2Sjsg return (ENOBUFS); 13378fca70d2Sjsg c->sc_buf = usbd_alloc_buffer(c->sc_xfer, 1338015115d7Sjsg sc->sc_bufsz); 13398fca70d2Sjsg if (c->sc_buf == NULL) { 13408fca70d2Sjsg usbd_free_xfer(c->sc_xfer); 13418fca70d2Sjsg return (ENOBUFS); 13428fca70d2Sjsg } 13438fca70d2Sjsg } 13448fca70d2Sjsg } 13458fca70d2Sjsg 13468fca70d2Sjsg return (0); 13478fca70d2Sjsg } 13488fca70d2Sjsg 13498fca70d2Sjsg struct mbuf * 13508fca70d2Sjsg smsc_newbuf(void) 13518fca70d2Sjsg { 13528fca70d2Sjsg struct mbuf *m; 13538fca70d2Sjsg 13548fca70d2Sjsg MGETHDR(m, M_DONTWAIT, MT_DATA); 13558fca70d2Sjsg if (m == NULL) 13568fca70d2Sjsg return (NULL); 13578fca70d2Sjsg 13588fca70d2Sjsg MCLGET(m, M_DONTWAIT); 13598fca70d2Sjsg if (!(m->m_flags & M_EXT)) { 13608fca70d2Sjsg m_freem(m); 13618fca70d2Sjsg return (NULL); 13628fca70d2Sjsg } 13638fca70d2Sjsg 13648fca70d2Sjsg return (m); 13658fca70d2Sjsg } 13668fca70d2Sjsg 13678fca70d2Sjsg int 13688fca70d2Sjsg smsc_encap(struct smsc_softc *sc, struct mbuf *m, int idx) 13698fca70d2Sjsg { 13708fca70d2Sjsg struct smsc_chain *c; 13718fca70d2Sjsg usbd_status err; 13728fca70d2Sjsg uint32_t txhdr; 13738fca70d2Sjsg uint32_t frm_len = 0; 13748fca70d2Sjsg 13758fca70d2Sjsg c = &sc->sc_cdata.tx_chain[idx]; 13768fca70d2Sjsg 13778fca70d2Sjsg /* 13788fca70d2Sjsg * Each frame is prefixed with two 32-bit values describing the 13798fca70d2Sjsg * length of the packet and buffer. 13808fca70d2Sjsg */ 13818fca70d2Sjsg txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) | 13828fca70d2Sjsg SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG; 13838fca70d2Sjsg txhdr = htole32(txhdr); 13848fca70d2Sjsg memcpy(c->sc_buf, &txhdr, sizeof(txhdr)); 13858fca70d2Sjsg 13868fca70d2Sjsg txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len); 13878fca70d2Sjsg txhdr = htole32(txhdr); 13888fca70d2Sjsg memcpy(c->sc_buf + 4, &txhdr, sizeof(txhdr)); 13898fca70d2Sjsg 13908fca70d2Sjsg frm_len += 8; 13918fca70d2Sjsg 13928fca70d2Sjsg /* Next copy in the actual packet */ 13938fca70d2Sjsg m_copydata(m, 0, m->m_pkthdr.len, c->sc_buf + frm_len); 13948fca70d2Sjsg frm_len += m->m_pkthdr.len; 13958fca70d2Sjsg 13968fca70d2Sjsg c->sc_mbuf = m; 13978fca70d2Sjsg 13988fca70d2Sjsg usbd_setup_xfer(c->sc_xfer, sc->sc_ep[SMSC_ENDPT_TX], 13998fca70d2Sjsg c, c->sc_buf, frm_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 14008fca70d2Sjsg 10000, smsc_txeof); 14018fca70d2Sjsg 14028fca70d2Sjsg err = usbd_transfer(c->sc_xfer); 14038fca70d2Sjsg if (err != USBD_IN_PROGRESS) { 14048fca70d2Sjsg smsc_stop(sc); 14058fca70d2Sjsg return (EIO); 14068fca70d2Sjsg } 14078fca70d2Sjsg 14088fca70d2Sjsg sc->sc_cdata.tx_cnt++; 14098fca70d2Sjsg 14108fca70d2Sjsg return (0); 14118fca70d2Sjsg } 1412