xref: /openbsd/sys/dev/usb/if_smsc.c (revision 469696c0)
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, &reg_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, &reg_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