xref: /openbsd/sys/dev/usb/if_smsc.c (revision 81508fe3)
1*81508fe3Sjsg /*	$OpenBSD: if_smsc.c,v 1.39 2024/05/23 03:21:08 jsg 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  *
474b1a56afSjsg  * RX checksumming 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  *
514b1a56afSjsg  * TX checksumming 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 
638fca70d2Sjsg #include <sys/param.h>
648fca70d2Sjsg #include <sys/systm.h>
658fca70d2Sjsg #include <sys/sockio.h>
668fca70d2Sjsg #include <sys/rwlock.h>
678fca70d2Sjsg #include <sys/mbuf.h>
688fca70d2Sjsg 
698fca70d2Sjsg #include <sys/device.h>
708fca70d2Sjsg 
718fca70d2Sjsg #include <machine/bus.h>
728fca70d2Sjsg 
738fca70d2Sjsg #include <net/if.h>
748fca70d2Sjsg #include <net/if_media.h>
758fca70d2Sjsg 
768fca70d2Sjsg #if NBPFILTER > 0
778fca70d2Sjsg #include <net/bpf.h>
788fca70d2Sjsg #endif
798fca70d2Sjsg 
808fca70d2Sjsg #include <netinet/in.h>
818fca70d2Sjsg #include <netinet/if_ether.h>
828fca70d2Sjsg 
838fca70d2Sjsg #include <dev/mii/miivar.h>
848fca70d2Sjsg 
858fca70d2Sjsg #include <dev/usb/usb.h>
868fca70d2Sjsg #include <dev/usb/usbdi.h>
878fca70d2Sjsg #include <dev/usb/usbdi_util.h>
888fca70d2Sjsg #include <dev/usb/usbdivar.h>
898fca70d2Sjsg #include <dev/usb/usbdevs.h>
908fca70d2Sjsg 
918fca70d2Sjsg #include "if_smscreg.h"
928fca70d2Sjsg 
938fca70d2Sjsg /*
948fca70d2Sjsg  * Various supported device vendors/products.
958fca70d2Sjsg  */
968fca70d2Sjsg static const struct usb_devno smsc_devs[] = {
97e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_LAN89530 },
98e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_LAN9530 },
99e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_LAN9730 },
100e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500 },
101e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500A },
102e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500A_ALT },
103e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500A_HAL },
104e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500A_SAL10 },
105e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500_ALT },
106e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9500_SAL10 },
107e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9505 },
108e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9505A },
109e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9505A_HAL },
110e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9505A_SAL10 },
111e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9505_SAL10 },
112e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9512_14 },
113e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9512_14_ALT },
114e0f69281Sjsg 	{ USB_VENDOR_SMC2,	USB_PRODUCT_SMC2_SMSC9512_14_SAL10 }
1158fca70d2Sjsg };
1168fca70d2Sjsg 
117ae343004Smpi #ifdef SMSC_DEBUG
118ae343004Smpi static int smsc_debug = 0;
1198fca70d2Sjsg #define smsc_dbg_printf(sc, fmt, args...) \
1208fca70d2Sjsg 	do { \
1218fca70d2Sjsg 		if (smsc_debug > 0) \
1228fca70d2Sjsg 			printf("debug: " fmt, ##args); \
1238fca70d2Sjsg 	} while(0)
1248fca70d2Sjsg #else
1258fca70d2Sjsg #define smsc_dbg_printf(sc, fmt, args...)
1268fca70d2Sjsg #endif
1278fca70d2Sjsg 
1288fca70d2Sjsg #define smsc_warn_printf(sc, fmt, args...) \
1298fca70d2Sjsg 	printf("%s: warning: " fmt, (sc)->sc_dev.dv_xname, ##args)
1308fca70d2Sjsg 
1318fca70d2Sjsg #define smsc_err_printf(sc, fmt, args...) \
1328fca70d2Sjsg 	printf("%s: error: " fmt, (sc)->sc_dev.dv_xname, ##args)
1338fca70d2Sjsg 
1348fca70d2Sjsg int		 smsc_chip_init(struct smsc_softc *sc);
1358fca70d2Sjsg int		 smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
136f00b3ba9Sbrad void		 smsc_iff(struct smsc_softc *);
1378fca70d2Sjsg int		 smsc_setmacaddress(struct smsc_softc *, const uint8_t *);
1388fca70d2Sjsg 
1398fca70d2Sjsg int		 smsc_match(struct device *, void *, void *);
1408fca70d2Sjsg void		 smsc_attach(struct device *, struct device *, void *);
1418fca70d2Sjsg int		 smsc_detach(struct device *, int);
1428fca70d2Sjsg 
1438fca70d2Sjsg void		 smsc_init(void *);
1448fca70d2Sjsg void		 smsc_stop(struct smsc_softc *);
1458fca70d2Sjsg void		 smsc_start(struct ifnet *);
1468fca70d2Sjsg void		 smsc_reset(struct smsc_softc *);
1478fca70d2Sjsg 
1488fca70d2Sjsg void		 smsc_tick(void *);
1498fca70d2Sjsg void		 smsc_tick_task(void *);
1508fca70d2Sjsg void		 smsc_miibus_statchg(struct device *);
1518fca70d2Sjsg int		 smsc_miibus_readreg(struct device *, int, int);
1528fca70d2Sjsg void		 smsc_miibus_writereg(struct device *, int, int, int);
1538fca70d2Sjsg int		 smsc_ifmedia_upd(struct ifnet *);
1548fca70d2Sjsg void		 smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
1558fca70d2Sjsg void		 smsc_lock_mii(struct smsc_softc *sc);
1568fca70d2Sjsg void		 smsc_unlock_mii(struct smsc_softc *sc);
1578fca70d2Sjsg 
1588fca70d2Sjsg int		 smsc_tx_list_init(struct smsc_softc *);
1598fca70d2Sjsg int		 smsc_rx_list_init(struct smsc_softc *);
1608fca70d2Sjsg int		 smsc_encap(struct smsc_softc *, struct mbuf *, int);
161ab0b1be7Smglocker void		 smsc_rxeof(struct usbd_xfer *, void *, usbd_status);
162ab0b1be7Smglocker void		 smsc_txeof(struct usbd_xfer *, void *, usbd_status);
1638fca70d2Sjsg 
1648fca70d2Sjsg int		 smsc_read_reg(struct smsc_softc *, uint32_t, uint32_t *);
1658fca70d2Sjsg int		 smsc_write_reg(struct smsc_softc *, uint32_t, uint32_t);
1668fca70d2Sjsg int		 smsc_wait_for_bits(struct smsc_softc *, uint32_t, uint32_t);
1678fca70d2Sjsg int		 smsc_sethwcsum(struct smsc_softc *);
1688fca70d2Sjsg 
1698fca70d2Sjsg struct cfdriver smsc_cd = {
1708fca70d2Sjsg 	NULL, "smsc", DV_IFNET
1718fca70d2Sjsg };
1728fca70d2Sjsg 
1738fca70d2Sjsg const struct cfattach smsc_ca = {
17453c6612dSmpi 	sizeof(struct smsc_softc), smsc_match, smsc_attach, smsc_detach,
1758fca70d2Sjsg };
1768fca70d2Sjsg 
177ef8c47aaSjsg #if defined(__arm__) || defined(__arm64__)
178ef8c47aaSjsg 
179ef8c47aaSjsg #include <dev/ofw/openfirm.h>
180ef8c47aaSjsg 
181ef8c47aaSjsg void
smsc_enaddr_OF(struct smsc_softc * sc)182ef8c47aaSjsg smsc_enaddr_OF(struct smsc_softc *sc)
183ef8c47aaSjsg {
184ef1a6c60Skettenis 	char *device = "/axi/usb/hub/ethernet";
185c700b3c9Skettenis 	char prop[128];
186ef8c47aaSjsg 	int node;
187ef8c47aaSjsg 
188ef8c47aaSjsg 	if (sc->sc_dev.dv_unit != 0)
189ef8c47aaSjsg 		return;
190ef8c47aaSjsg 
191ef8c47aaSjsg 	/*
192ef1a6c60Skettenis 	 * Get the Raspberry Pi MAC address from FDT.  This is all
193ef1a6c60Skettenis 	 * much more complicated than strictly needed since the
194ef1a6c60Skettenis 	 * firmware device tree keeps changing as drivers get
195ef1a6c60Skettenis 	 * upstreamed.  Sigh.
196ef1a6c60Skettenis 	 *
197ef1a6c60Skettenis 	 * Ultimately this should just use the "ethernet0" alias and
198ef1a6c60Skettenis 	 * the "local-mac-address" property.
199ef8c47aaSjsg 	 */
200ef8c47aaSjsg 
201ef1a6c60Skettenis 	if ((node = OF_finddevice("/aliases")) == -1)
202ef1a6c60Skettenis 		return;
203ef1a6c60Skettenis 	if (OF_getprop(node, "ethernet0", prop, sizeof(prop)) > 0 ||
204ef1a6c60Skettenis 	    OF_getprop(node, "ethernet", prop, sizeof(prop)) > 0)
205ef1a6c60Skettenis 		device = prop;
206ef1a6c60Skettenis 
207ef1a6c60Skettenis 	if ((node = OF_finddevice(device)) == -1)
208ef1a6c60Skettenis 		return;
209ef1a6c60Skettenis 	if (OF_getprop(node, "local-mac-address", sc->sc_ac.ac_enaddr,
210ef1a6c60Skettenis 	    sizeof(sc->sc_ac.ac_enaddr)) != sizeof(sc->sc_ac.ac_enaddr)) {
211ef8c47aaSjsg 		OF_getprop(node, "mac-address", sc->sc_ac.ac_enaddr,
212ef8c47aaSjsg 		    sizeof(sc->sc_ac.ac_enaddr));
213ef8c47aaSjsg 	}
214ef1a6c60Skettenis }
215ef8c47aaSjsg #else
216ef8c47aaSjsg #define smsc_enaddr_OF(x) do {} while(0)
217ef8c47aaSjsg #endif
218ef8c47aaSjsg 
2198fca70d2Sjsg int
smsc_read_reg(struct smsc_softc * sc,uint32_t off,uint32_t * data)2208fca70d2Sjsg smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data)
2218fca70d2Sjsg {
2228fca70d2Sjsg 	usb_device_request_t req;
2238fca70d2Sjsg 	uint32_t buf;
2248fca70d2Sjsg 	usbd_status err;
2258fca70d2Sjsg 
2268fca70d2Sjsg 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
2278fca70d2Sjsg 	req.bRequest = SMSC_UR_READ_REG;
2288fca70d2Sjsg 	USETW(req.wValue, 0);
2298fca70d2Sjsg 	USETW(req.wIndex, off);
2308fca70d2Sjsg 	USETW(req.wLength, 4);
2318fca70d2Sjsg 
2328fca70d2Sjsg 	err = usbd_do_request(sc->sc_udev, &req, &buf);
2338fca70d2Sjsg 	if (err != 0)
2348fca70d2Sjsg 		smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off);
2358fca70d2Sjsg 
2368fca70d2Sjsg 	*data = letoh32(buf);
2378fca70d2Sjsg 
2388fca70d2Sjsg 	return (err);
2398fca70d2Sjsg }
2408fca70d2Sjsg 
2418fca70d2Sjsg int
smsc_write_reg(struct smsc_softc * sc,uint32_t off,uint32_t data)2428fca70d2Sjsg smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data)
2438fca70d2Sjsg {
2448fca70d2Sjsg 	usb_device_request_t req;
2458fca70d2Sjsg 	uint32_t buf;
2468fca70d2Sjsg 	usbd_status err;
2478fca70d2Sjsg 
2488fca70d2Sjsg 	buf = htole32(data);
2498fca70d2Sjsg 
2508fca70d2Sjsg 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
2518fca70d2Sjsg 	req.bRequest = SMSC_UR_WRITE_REG;
2528fca70d2Sjsg 	USETW(req.wValue, 0);
2538fca70d2Sjsg 	USETW(req.wIndex, off);
2548fca70d2Sjsg 	USETW(req.wLength, 4);
2558fca70d2Sjsg 
2568fca70d2Sjsg 	err = usbd_do_request(sc->sc_udev, &req, &buf);
2578fca70d2Sjsg 	if (err != 0)
2588fca70d2Sjsg 		smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off);
2598fca70d2Sjsg 
2608fca70d2Sjsg 	return (err);
2618fca70d2Sjsg }
2628fca70d2Sjsg 
2638fca70d2Sjsg int
smsc_wait_for_bits(struct smsc_softc * sc,uint32_t reg,uint32_t bits)2648fca70d2Sjsg smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
2658fca70d2Sjsg {
2668fca70d2Sjsg 	uint32_t val;
2678fca70d2Sjsg 	int err, i;
2688fca70d2Sjsg 
2698fca70d2Sjsg 	for (i = 0; i < 100; i++) {
2708fca70d2Sjsg 		if ((err = smsc_read_reg(sc, reg, &val)) != 0)
2718fca70d2Sjsg 			return (err);
2728fca70d2Sjsg 		if (!(val & bits))
2738fca70d2Sjsg 			return (0);
2748fca70d2Sjsg 		DELAY(5);
2758fca70d2Sjsg 	}
2768fca70d2Sjsg 
2778fca70d2Sjsg 	return (1);
2788fca70d2Sjsg }
2798fca70d2Sjsg 
2808fca70d2Sjsg int
smsc_miibus_readreg(struct device * dev,int phy,int reg)2818fca70d2Sjsg smsc_miibus_readreg(struct device *dev, int phy, int reg)
2828fca70d2Sjsg {
2838fca70d2Sjsg 	struct smsc_softc *sc = (struct smsc_softc *)dev;
2848fca70d2Sjsg 	uint32_t addr;
2858fca70d2Sjsg 	uint32_t val = 0;
2868fca70d2Sjsg 
2878fca70d2Sjsg 	smsc_lock_mii(sc);
2888fca70d2Sjsg 	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
2898fca70d2Sjsg 		smsc_warn_printf(sc, "MII is busy\n");
2908fca70d2Sjsg 		goto done;
2918fca70d2Sjsg 	}
2928fca70d2Sjsg 
2938fca70d2Sjsg 	addr = (phy << 11) | (reg << 6) | SMSC_MII_READ;
2948fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MII_ADDR, addr);
2958fca70d2Sjsg 
2968fca70d2Sjsg 	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
2978fca70d2Sjsg 		smsc_warn_printf(sc, "MII read timeout\n");
2988fca70d2Sjsg 
2998fca70d2Sjsg 	smsc_read_reg(sc, SMSC_MII_DATA, &val);
3008fca70d2Sjsg 
3018fca70d2Sjsg done:
3023f6463d4Sjsg 	smsc_unlock_mii(sc);
3038fca70d2Sjsg 	return (val & 0xFFFF);
3048fca70d2Sjsg }
3058fca70d2Sjsg 
3068fca70d2Sjsg void
smsc_miibus_writereg(struct device * dev,int phy,int reg,int val)3078fca70d2Sjsg smsc_miibus_writereg(struct device *dev, int phy, int reg, int val)
3088fca70d2Sjsg {
3098fca70d2Sjsg 	struct smsc_softc *sc = (struct smsc_softc *)dev;
3108fca70d2Sjsg 	uint32_t addr;
3118fca70d2Sjsg 
3128fca70d2Sjsg 	if (sc->sc_phyno != phy)
3138fca70d2Sjsg 		return;
3148fca70d2Sjsg 
3158fca70d2Sjsg 	smsc_lock_mii(sc);
3168fca70d2Sjsg 	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
3178fca70d2Sjsg 		smsc_warn_printf(sc, "MII is busy\n");
318fde537a4Sjsg 		smsc_unlock_mii(sc);
3198fca70d2Sjsg 		return;
3208fca70d2Sjsg 	}
3218fca70d2Sjsg 
3228fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MII_DATA, val);
3238fca70d2Sjsg 
3248fca70d2Sjsg 	addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE;
3258fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MII_ADDR, addr);
3268fca70d2Sjsg 	smsc_unlock_mii(sc);
3278fca70d2Sjsg 
3288fca70d2Sjsg 	if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
3298fca70d2Sjsg 		smsc_warn_printf(sc, "MII write timeout\n");
3308fca70d2Sjsg }
3318fca70d2Sjsg 
3328fca70d2Sjsg void
smsc_miibus_statchg(struct device * dev)3338fca70d2Sjsg smsc_miibus_statchg(struct device *dev)
3348fca70d2Sjsg {
3358fca70d2Sjsg 	struct smsc_softc *sc = (struct smsc_softc *)dev;
3368fca70d2Sjsg 	struct mii_data *mii = &sc->sc_mii;
3378fca70d2Sjsg 	struct ifnet *ifp = &sc->sc_ac.ac_if;
3388fca70d2Sjsg 	int err;
3398fca70d2Sjsg 	uint32_t flow;
3408fca70d2Sjsg 	uint32_t afc_cfg;
3418fca70d2Sjsg 
3428fca70d2Sjsg 	if (mii == NULL || ifp == NULL ||
3438fca70d2Sjsg 	    (ifp->if_flags & IFF_RUNNING) == 0)
3448fca70d2Sjsg 		return;
3458fca70d2Sjsg 
3468fca70d2Sjsg 	/* Use the MII status to determine link status */
3478fca70d2Sjsg 	sc->sc_flags &= ~SMSC_FLAG_LINK;
3488fca70d2Sjsg 	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
3498fca70d2Sjsg 	    (IFM_ACTIVE | IFM_AVALID)) {
3508fca70d2Sjsg 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
3518fca70d2Sjsg 			case IFM_10_T:
3528fca70d2Sjsg 			case IFM_100_TX:
3538fca70d2Sjsg 				sc->sc_flags |= SMSC_FLAG_LINK;
3548fca70d2Sjsg 				break;
3558fca70d2Sjsg 			case IFM_1000_T:
3568fca70d2Sjsg 				/* Gigabit ethernet not supported by chipset */
3578fca70d2Sjsg 				break;
3588fca70d2Sjsg 			default:
3598fca70d2Sjsg 				break;
3608fca70d2Sjsg 		}
3618fca70d2Sjsg 	}
3628fca70d2Sjsg 
3638fca70d2Sjsg 	/* Lost link, do nothing. */
3648fca70d2Sjsg 	if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
3658fca70d2Sjsg 		smsc_dbg_printf(sc, "link flag not set\n");
3668fca70d2Sjsg 		return;
3678fca70d2Sjsg 	}
3688fca70d2Sjsg 
3698fca70d2Sjsg 	err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg);
3708fca70d2Sjsg 	if (err) {
3718fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read initial AFC_CFG, "
3728fca70d2Sjsg 		    "error %d\n", err);
3738fca70d2Sjsg 		return;
3748fca70d2Sjsg 	}
3758fca70d2Sjsg 
3768fca70d2Sjsg 	/* Enable/disable full duplex operation and TX/RX pause */
3778fca70d2Sjsg 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
3788fca70d2Sjsg 		smsc_dbg_printf(sc, "full duplex operation\n");
3798fca70d2Sjsg 		sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN;
3808fca70d2Sjsg 		sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX;
3818fca70d2Sjsg 
3828fca70d2Sjsg 		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
3838fca70d2Sjsg 			flow = 0xffff0002;
3848fca70d2Sjsg 		else
3858fca70d2Sjsg 			flow = 0;
3868fca70d2Sjsg 
3878fca70d2Sjsg 		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
3888fca70d2Sjsg 			afc_cfg |= 0xf;
3898fca70d2Sjsg 		else
3908fca70d2Sjsg 			afc_cfg &= ~0xf;
3918fca70d2Sjsg 
3928fca70d2Sjsg 	} else {
3938fca70d2Sjsg 		smsc_dbg_printf(sc, "half duplex operation\n");
3948fca70d2Sjsg 		sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX;
3958fca70d2Sjsg 		sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN;
3968fca70d2Sjsg 
3978fca70d2Sjsg 		flow = 0;
3988fca70d2Sjsg 		afc_cfg |= 0xf;
3998fca70d2Sjsg 	}
4008fca70d2Sjsg 
4018fca70d2Sjsg 	err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
4028fca70d2Sjsg 	err += smsc_write_reg(sc, SMSC_FLOW, flow);
4038fca70d2Sjsg 	err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg);
4048fca70d2Sjsg 	if (err)
4058fca70d2Sjsg 		smsc_warn_printf(sc, "media change failed, error %d\n", err);
4068fca70d2Sjsg }
4078fca70d2Sjsg 
4088fca70d2Sjsg int
smsc_ifmedia_upd(struct ifnet * ifp)4098fca70d2Sjsg smsc_ifmedia_upd(struct ifnet *ifp)
4108fca70d2Sjsg {
4118fca70d2Sjsg 	struct smsc_softc *sc = ifp->if_softc;
4128fca70d2Sjsg 	struct mii_data *mii = &sc->sc_mii;
4138fca70d2Sjsg 	int err;
4148fca70d2Sjsg 
4158fca70d2Sjsg 	if (mii->mii_instance) {
4168fca70d2Sjsg 		struct mii_softc *miisc;
4178fca70d2Sjsg 
4188fca70d2Sjsg 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
4198fca70d2Sjsg 			mii_phy_reset(miisc);
4208fca70d2Sjsg 	}
4218fca70d2Sjsg 	err = mii_mediachg(mii);
4228fca70d2Sjsg 	return (err);
4238fca70d2Sjsg }
4248fca70d2Sjsg 
4258fca70d2Sjsg void
smsc_ifmedia_sts(struct ifnet * ifp,struct ifmediareq * ifmr)4268fca70d2Sjsg smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
4278fca70d2Sjsg {
4288fca70d2Sjsg 	struct smsc_softc *sc = ifp->if_softc;
4298fca70d2Sjsg 	struct mii_data *mii = &sc->sc_mii;
4308fca70d2Sjsg 
4318fca70d2Sjsg 	mii_pollstat(mii);
4328fca70d2Sjsg 
4338fca70d2Sjsg 	ifmr->ifm_active = mii->mii_media_active;
4348fca70d2Sjsg 	ifmr->ifm_status = mii->mii_media_status;
4358fca70d2Sjsg }
4368fca70d2Sjsg 
4378fca70d2Sjsg static inline uint32_t
smsc_hash(uint8_t addr[ETHER_ADDR_LEN])4388fca70d2Sjsg smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
4398fca70d2Sjsg {
4408fca70d2Sjsg 	return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f;
4418fca70d2Sjsg }
4428fca70d2Sjsg 
4438fca70d2Sjsg void
smsc_iff(struct smsc_softc * sc)444f00b3ba9Sbrad smsc_iff(struct smsc_softc *sc)
4458fca70d2Sjsg {
4468fca70d2Sjsg 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
447f00b3ba9Sbrad 	struct arpcom		*ac = &sc->sc_ac;
4488fca70d2Sjsg 	struct ether_multi	*enm;
4498fca70d2Sjsg 	struct ether_multistep	 step;
4508fca70d2Sjsg 	uint32_t		 hashtbl[2] = { 0, 0 };
4518fca70d2Sjsg 	uint32_t		 hash;
4528fca70d2Sjsg 
4538fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
4548fca70d2Sjsg 		return;
4558fca70d2Sjsg 
456f00b3ba9Sbrad 	sc->sc_mac_csr &= ~(SMSC_MAC_CSR_HPFILT | SMSC_MAC_CSR_MCPAS |
457f00b3ba9Sbrad 	    SMSC_MAC_CSR_PRMS);
458f00b3ba9Sbrad 	ifp->if_flags &= ~IFF_ALLMULTI;
459f00b3ba9Sbrad 
460f00b3ba9Sbrad 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
461f00b3ba9Sbrad 		ifp->if_flags |= IFF_ALLMULTI;
4628fca70d2Sjsg 		sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
463f00b3ba9Sbrad 		if (ifp->if_flags & IFF_PROMISC)
464f00b3ba9Sbrad 			sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS;
4658fca70d2Sjsg 	} else {
4668fca70d2Sjsg 		sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;
4678fca70d2Sjsg 
468f00b3ba9Sbrad 		ETHER_FIRST_MULTI(step, ac, enm);
4698fca70d2Sjsg 		while (enm != NULL) {
4708fca70d2Sjsg 			hash = smsc_hash(enm->enm_addrlo);
471f00b3ba9Sbrad 
4728fca70d2Sjsg 			hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
473f00b3ba9Sbrad 
4748fca70d2Sjsg 			ETHER_NEXT_MULTI(step, enm);
4758fca70d2Sjsg 		}
476f00b3ba9Sbrad 	}
4778fca70d2Sjsg 
4788fca70d2Sjsg 	/* Debug */
479f00b3ba9Sbrad 	if (sc->sc_mac_csr & SMSC_MAC_CSR_MCPAS)
480f00b3ba9Sbrad 		smsc_dbg_printf(sc, "receive all multicast enabled\n");
481f00b3ba9Sbrad 	else if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT)
4828fca70d2Sjsg 		smsc_dbg_printf(sc, "receive select group of macs\n");
4838fca70d2Sjsg 
4848fca70d2Sjsg 	/* Write the hash table and mac control registers */
4858fca70d2Sjsg 	smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]);
4868fca70d2Sjsg 	smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]);
4878fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
4888fca70d2Sjsg }
4898fca70d2Sjsg 
4908fca70d2Sjsg int
smsc_sethwcsum(struct smsc_softc * sc)4918fca70d2Sjsg smsc_sethwcsum(struct smsc_softc *sc)
4928fca70d2Sjsg {
4938fca70d2Sjsg 	struct ifnet *ifp = &sc->sc_ac.ac_if;
4948fca70d2Sjsg 	uint32_t val;
4958fca70d2Sjsg 	int err;
4968fca70d2Sjsg 
4978fca70d2Sjsg 	if (!ifp)
4988fca70d2Sjsg 		return (-EIO);
4998fca70d2Sjsg 
5008fca70d2Sjsg 	err = smsc_read_reg(sc, SMSC_COE_CTRL, &val);
5018fca70d2Sjsg 	if (err != 0) {
5028fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n",
5038fca70d2Sjsg 		    err);
5048fca70d2Sjsg 		return (err);
5058fca70d2Sjsg 	}
5068fca70d2Sjsg 
5078fca70d2Sjsg 	/* Enable/disable the Rx checksum */
5088fca70d2Sjsg 	if (ifp->if_capabilities & IFCAP_CSUM_IPv4)
5098fca70d2Sjsg 		val |= SMSC_COE_CTRL_RX_EN;
5108fca70d2Sjsg 	else
5118fca70d2Sjsg 		val &= ~SMSC_COE_CTRL_RX_EN;
5128fca70d2Sjsg 
5138fca70d2Sjsg 	/* Enable/disable the Tx checksum (currently not supported) */
5148fca70d2Sjsg 	if (ifp->if_capabilities & IFCAP_CSUM_IPv4)
5158fca70d2Sjsg 		val |= SMSC_COE_CTRL_TX_EN;
5168fca70d2Sjsg 	else
5178fca70d2Sjsg 		val &= ~SMSC_COE_CTRL_TX_EN;
5188fca70d2Sjsg 
5198fca70d2Sjsg 	err = smsc_write_reg(sc, SMSC_COE_CTRL, val);
5208fca70d2Sjsg 	if (err != 0) {
5218fca70d2Sjsg 		smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n",
5228fca70d2Sjsg 		    err);
5238fca70d2Sjsg 		return (err);
5248fca70d2Sjsg 	}
5258fca70d2Sjsg 
5268fca70d2Sjsg 	return (0);
5278fca70d2Sjsg }
5288fca70d2Sjsg 
5298fca70d2Sjsg int
smsc_setmacaddress(struct smsc_softc * sc,const uint8_t * addr)5308fca70d2Sjsg smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr)
5318fca70d2Sjsg {
5328fca70d2Sjsg 	int err;
5338fca70d2Sjsg 	uint32_t val;
5348fca70d2Sjsg 
5358fca70d2Sjsg 	smsc_dbg_printf(sc, "setting mac address to "
5368fca70d2Sjsg 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
5378fca70d2Sjsg 	    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
5388fca70d2Sjsg 
5398fca70d2Sjsg 	val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
5408fca70d2Sjsg 	if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0)
5418fca70d2Sjsg 		goto done;
5428fca70d2Sjsg 
5438fca70d2Sjsg 	val = (addr[5] << 8) | addr[4];
5448fca70d2Sjsg 	err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val);
5458fca70d2Sjsg 
5468fca70d2Sjsg done:
5478fca70d2Sjsg 	return (err);
5488fca70d2Sjsg }
5498fca70d2Sjsg 
5508fca70d2Sjsg void
smsc_reset(struct smsc_softc * sc)5518fca70d2Sjsg smsc_reset(struct smsc_softc *sc)
5528fca70d2Sjsg {
5538fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
5548fca70d2Sjsg 		return;
5558fca70d2Sjsg 
5568fca70d2Sjsg 	/* Wait a little while for the chip to get its brains in order. */
5578fca70d2Sjsg 	DELAY(1000);
5588fca70d2Sjsg 
5598fca70d2Sjsg 	/* Reinitialize controller to achieve full reset. */
5608fca70d2Sjsg 	smsc_chip_init(sc);
5618fca70d2Sjsg }
5628fca70d2Sjsg 
5638fca70d2Sjsg void
smsc_init(void * xsc)5648fca70d2Sjsg smsc_init(void *xsc)
5658fca70d2Sjsg {
5668fca70d2Sjsg 	struct smsc_softc	*sc = xsc;
5678fca70d2Sjsg 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
5688fca70d2Sjsg 	struct smsc_chain	*c;
5698fca70d2Sjsg 	usbd_status		 err;
5708fca70d2Sjsg 	int			 s, i;
5718fca70d2Sjsg 
5728fca70d2Sjsg 	s = splnet();
5738fca70d2Sjsg 
5748fca70d2Sjsg 	/* Cancel pending I/O */
5758fca70d2Sjsg 	smsc_stop(sc);
5768fca70d2Sjsg 
5778fca70d2Sjsg 	/* Reset the ethernet interface. */
5788fca70d2Sjsg 	smsc_reset(sc);
5798fca70d2Sjsg 
5808fca70d2Sjsg 	/* Init RX ring. */
5818fca70d2Sjsg 	if (smsc_rx_list_init(sc) == ENOBUFS) {
5828fca70d2Sjsg 		printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
5838fca70d2Sjsg 		splx(s);
5848fca70d2Sjsg 		return;
5858fca70d2Sjsg 	}
5868fca70d2Sjsg 
5878fca70d2Sjsg 	/* Init TX ring. */
5888fca70d2Sjsg 	if (smsc_tx_list_init(sc) == ENOBUFS) {
5898fca70d2Sjsg 		printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
5908fca70d2Sjsg 		splx(s);
5918fca70d2Sjsg 		return;
5928fca70d2Sjsg 	}
5938fca70d2Sjsg 
594f00b3ba9Sbrad 	/* Program promiscuous mode and multicast filters. */
595f00b3ba9Sbrad 	smsc_iff(sc);
5968fca70d2Sjsg 
5978fca70d2Sjsg 	/* Open RX and TX pipes. */
5988fca70d2Sjsg 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_RX],
5998fca70d2Sjsg 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_RX]);
6008fca70d2Sjsg 	if (err) {
6018fca70d2Sjsg 		printf("%s: open rx pipe failed: %s\n",
6028fca70d2Sjsg 		    sc->sc_dev.dv_xname, usbd_errstr(err));
6038fca70d2Sjsg 		splx(s);
6048fca70d2Sjsg 		return;
6058fca70d2Sjsg 	}
6068fca70d2Sjsg 
6078fca70d2Sjsg 	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_TX],
6088fca70d2Sjsg 	    USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_TX]);
6098fca70d2Sjsg 	if (err) {
6108fca70d2Sjsg 		printf("%s: open tx pipe failed: %s\n",
6118fca70d2Sjsg 		    sc->sc_dev.dv_xname, usbd_errstr(err));
6128fca70d2Sjsg 		splx(s);
6138fca70d2Sjsg 		return;
6148fca70d2Sjsg 	}
6158fca70d2Sjsg 
6168fca70d2Sjsg 	/* Start up the receive pipe. */
6178fca70d2Sjsg 	for (i = 0; i < SMSC_RX_LIST_CNT; i++) {
6188fca70d2Sjsg 		c = &sc->sc_cdata.rx_chain[i];
6198fca70d2Sjsg 		usbd_setup_xfer(c->sc_xfer, sc->sc_ep[SMSC_ENDPT_RX],
620015115d7Sjsg 		    c, c->sc_buf, sc->sc_bufsz,
6218fca70d2Sjsg 		    USBD_SHORT_XFER_OK | USBD_NO_COPY,
6228fca70d2Sjsg 		    USBD_NO_TIMEOUT, smsc_rxeof);
6238fca70d2Sjsg 		usbd_transfer(c->sc_xfer);
6248fca70d2Sjsg 	}
6258fca70d2Sjsg 
6268fca70d2Sjsg 	/* TCP/UDP checksum offload engines. */
6278fca70d2Sjsg 	smsc_sethwcsum(sc);
6288fca70d2Sjsg 
6298fca70d2Sjsg 	/* Indicate we are up and running. */
6308fca70d2Sjsg 	ifp->if_flags |= IFF_RUNNING;
631de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
6328fca70d2Sjsg 
6338fca70d2Sjsg 	timeout_add_sec(&sc->sc_stat_ch, 1);
6348fca70d2Sjsg 
6358fca70d2Sjsg 	splx(s);
6368fca70d2Sjsg }
6378fca70d2Sjsg 
6388fca70d2Sjsg void
smsc_start(struct ifnet * ifp)6398fca70d2Sjsg smsc_start(struct ifnet *ifp)
6408fca70d2Sjsg {
6418fca70d2Sjsg 	struct smsc_softc	*sc = ifp->if_softc;
6428fca70d2Sjsg 	struct mbuf		*m_head = NULL;
6438fca70d2Sjsg 
6448fca70d2Sjsg 	/* Don't send anything if there is no link or controller is busy. */
6458fca70d2Sjsg 	if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 ||
646de6cd8fbSdlg 		ifq_is_oactive(&ifp->if_snd)) {
6478fca70d2Sjsg 		return;
6488fca70d2Sjsg 	}
6498fca70d2Sjsg 
6504ced949bSgerhard 	m_head = ifq_dequeue(&ifp->if_snd);
6518fca70d2Sjsg 	if (m_head == NULL)
6528fca70d2Sjsg 		return;
6538fca70d2Sjsg 
6548fca70d2Sjsg 	if (smsc_encap(sc, m_head, 0)) {
6554ced949bSgerhard 		m_freem(m_head);
656de6cd8fbSdlg 		ifq_set_oactive(&ifp->if_snd);
6578fca70d2Sjsg 		return;
6588fca70d2Sjsg 	}
6598fca70d2Sjsg 
6608fca70d2Sjsg #if NBPFILTER > 0
6618fca70d2Sjsg 	if (ifp->if_bpf)
6628fca70d2Sjsg 		bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
6638fca70d2Sjsg #endif
664de6cd8fbSdlg 	ifq_set_oactive(&ifp->if_snd);
6658fca70d2Sjsg }
6668fca70d2Sjsg 
6678fca70d2Sjsg void
smsc_tick(void * xsc)6688fca70d2Sjsg smsc_tick(void *xsc)
6698fca70d2Sjsg {
6708fca70d2Sjsg 	struct smsc_softc *sc = xsc;
6718fca70d2Sjsg 
6728fca70d2Sjsg 	if (sc == NULL)
6738fca70d2Sjsg 		return;
6748fca70d2Sjsg 
6758fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
6768fca70d2Sjsg 		return;
6778fca70d2Sjsg 
6788fca70d2Sjsg 	usb_add_task(sc->sc_udev, &sc->sc_tick_task);
6798fca70d2Sjsg }
6808fca70d2Sjsg 
6818fca70d2Sjsg void
smsc_stop(struct smsc_softc * sc)6828fca70d2Sjsg smsc_stop(struct smsc_softc *sc)
6838fca70d2Sjsg {
6848fca70d2Sjsg 	usbd_status		err;
6858fca70d2Sjsg 	struct ifnet		*ifp;
6868fca70d2Sjsg 	int			i;
6878fca70d2Sjsg 
6888fca70d2Sjsg 	smsc_reset(sc);
6898fca70d2Sjsg 
6908fca70d2Sjsg 	ifp = &sc->sc_ac.ac_if;
6918fca70d2Sjsg 	ifp->if_timer = 0;
692de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
693de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
6948fca70d2Sjsg 
6958fca70d2Sjsg 	timeout_del(&sc->sc_stat_ch);
6968fca70d2Sjsg 
6978fca70d2Sjsg 	/* Stop transfers. */
6988fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) {
6998fca70d2Sjsg 		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_RX]);
7008fca70d2Sjsg 		if (err) {
7018fca70d2Sjsg 			printf("%s: close rx pipe failed: %s\n",
7028fca70d2Sjsg 			    sc->sc_dev.dv_xname, usbd_errstr(err));
7038fca70d2Sjsg 		}
7048fca70d2Sjsg 		sc->sc_ep[SMSC_ENDPT_RX] = NULL;
7058fca70d2Sjsg 	}
7068fca70d2Sjsg 
7078fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_TX] != NULL) {
7088fca70d2Sjsg 		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_TX]);
7098fca70d2Sjsg 		if (err) {
7108fca70d2Sjsg 			printf("%s: close tx pipe failed: %s\n",
7118fca70d2Sjsg 			    sc->sc_dev.dv_xname, usbd_errstr(err));
7128fca70d2Sjsg 		}
7138fca70d2Sjsg 		sc->sc_ep[SMSC_ENDPT_TX] = NULL;
7148fca70d2Sjsg 	}
7158fca70d2Sjsg 
7168fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL) {
7178fca70d2Sjsg 		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_INTR]);
7188fca70d2Sjsg 		if (err) {
7198fca70d2Sjsg 			printf("%s: close intr pipe failed: %s\n",
7208fca70d2Sjsg 			    sc->sc_dev.dv_xname, usbd_errstr(err));
7218fca70d2Sjsg 		}
7228fca70d2Sjsg 		sc->sc_ep[SMSC_ENDPT_INTR] = NULL;
7238fca70d2Sjsg 	}
7248fca70d2Sjsg 
7258fca70d2Sjsg 	/* Free RX resources. */
7268fca70d2Sjsg 	for (i = 0; i < SMSC_RX_LIST_CNT; i++) {
7278fca70d2Sjsg 		if (sc->sc_cdata.rx_chain[i].sc_mbuf != NULL) {
7288fca70d2Sjsg 			m_freem(sc->sc_cdata.rx_chain[i].sc_mbuf);
7298fca70d2Sjsg 			sc->sc_cdata.rx_chain[i].sc_mbuf = NULL;
7308fca70d2Sjsg 		}
7318fca70d2Sjsg 		if (sc->sc_cdata.rx_chain[i].sc_xfer != NULL) {
7328fca70d2Sjsg 			usbd_free_xfer(sc->sc_cdata.rx_chain[i].sc_xfer);
7338fca70d2Sjsg 			sc->sc_cdata.rx_chain[i].sc_xfer = NULL;
7348fca70d2Sjsg 		}
7358fca70d2Sjsg 	}
7368fca70d2Sjsg 
7378fca70d2Sjsg 	/* Free TX resources. */
7388fca70d2Sjsg 	for (i = 0; i < SMSC_TX_LIST_CNT; i++) {
7398fca70d2Sjsg 		if (sc->sc_cdata.tx_chain[i].sc_mbuf != NULL) {
7408fca70d2Sjsg 			m_freem(sc->sc_cdata.tx_chain[i].sc_mbuf);
7418fca70d2Sjsg 			sc->sc_cdata.tx_chain[i].sc_mbuf = NULL;
7428fca70d2Sjsg 		}
7438fca70d2Sjsg 		if (sc->sc_cdata.tx_chain[i].sc_xfer != NULL) {
7448fca70d2Sjsg 			usbd_free_xfer(sc->sc_cdata.tx_chain[i].sc_xfer);
7458fca70d2Sjsg 			sc->sc_cdata.tx_chain[i].sc_xfer = NULL;
7468fca70d2Sjsg 		}
7478fca70d2Sjsg 	}
7488fca70d2Sjsg }
7498fca70d2Sjsg 
7508fca70d2Sjsg int
smsc_chip_init(struct smsc_softc * sc)7518fca70d2Sjsg smsc_chip_init(struct smsc_softc *sc)
7528fca70d2Sjsg {
7538fca70d2Sjsg 	int err;
7548fca70d2Sjsg 	uint32_t reg_val;
7558fca70d2Sjsg 	int burst_cap;
7568fca70d2Sjsg 
7578fca70d2Sjsg 	/* Enter H/W config mode */
7588fca70d2Sjsg 	smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST);
7598fca70d2Sjsg 
7608fca70d2Sjsg 	if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG,
7618fca70d2Sjsg 	    SMSC_HW_CFG_LRST)) != 0) {
7628fca70d2Sjsg 		smsc_warn_printf(sc, "timed-out waiting for reset to "
7638fca70d2Sjsg 		    "complete\n");
7648fca70d2Sjsg 		goto init_failed;
7658fca70d2Sjsg 	}
7668fca70d2Sjsg 
7678fca70d2Sjsg 	/* Reset the PHY */
7688fca70d2Sjsg 	smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST);
7698fca70d2Sjsg 
7708fca70d2Sjsg 	if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL,
77162d9af6cSmestre 	    SMSC_PM_CTRL_PHY_RST)) != 0) {
7728fca70d2Sjsg 		smsc_warn_printf(sc, "timed-out waiting for phy reset to "
7738fca70d2Sjsg 		    "complete\n");
7748fca70d2Sjsg 		goto init_failed;
7758fca70d2Sjsg 	}
7768fca70d2Sjsg 	usbd_delay_ms(sc->sc_udev, 40);
7778fca70d2Sjsg 
7788fca70d2Sjsg 	/* Set the mac address */
7798fca70d2Sjsg 	if ((err = smsc_setmacaddress(sc, sc->sc_ac.ac_enaddr)) != 0) {
7808fca70d2Sjsg 		smsc_warn_printf(sc, "failed to set the MAC address\n");
7818fca70d2Sjsg 		goto init_failed;
7828fca70d2Sjsg 	}
7838fca70d2Sjsg 
7848fca70d2Sjsg 	/*
7858fca70d2Sjsg 	 * Don't know what the HW_CFG_BIR bit is, but following the reset
7868fca70d2Sjsg 	 * sequence as used in the Linux driver.
7878fca70d2Sjsg 	 */
7888fca70d2Sjsg 	if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) != 0) {
7898fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err);
7908fca70d2Sjsg 		goto init_failed;
7918fca70d2Sjsg 	}
7928fca70d2Sjsg 	reg_val |= SMSC_HW_CFG_BIR;
7938fca70d2Sjsg 	smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
7948fca70d2Sjsg 
7958fca70d2Sjsg 	/*
7968fca70d2Sjsg 	 * There is a so called 'turbo mode' that the linux driver supports, it
7978fca70d2Sjsg 	 * seems to allow you to jam multiple frames per Rx transaction.
7988fca70d2Sjsg 	 * By default this driver supports that and therefore allows multiple
7998fca70d2Sjsg 	 * frames per URB.
8008fca70d2Sjsg 	 *
8018fca70d2Sjsg 	 * The xfer buffer size needs to reflect this as well, therefore based
8028fca70d2Sjsg 	 * on the calculations in the Linux driver the RX bufsize is set to
8038fca70d2Sjsg 	 * 18944,
8048fca70d2Sjsg 	 *     bufsz = (16 * 1024 + 5 * 512)
8058fca70d2Sjsg 	 *
8068fca70d2Sjsg 	 * Burst capability is the number of URBs that can be in a burst of
8078fca70d2Sjsg 	 * data/ethernet frames.
8088fca70d2Sjsg 	 */
8098fca70d2Sjsg #ifdef SMSC_TURBO
8108fca70d2Sjsg 	if (sc->sc_udev->speed == USB_SPEED_HIGH)
8118fca70d2Sjsg 		burst_cap = 37;
8128fca70d2Sjsg 	else
8138fca70d2Sjsg 		burst_cap = 128;
8148fca70d2Sjsg #else
8158fca70d2Sjsg 	burst_cap = 0;
8168fca70d2Sjsg #endif
8178fca70d2Sjsg 
8188fca70d2Sjsg 	smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap);
8198fca70d2Sjsg 
8208fca70d2Sjsg 	/* Set the default bulk in delay (magic value from Linux driver) */
8218fca70d2Sjsg 	smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000);
8228fca70d2Sjsg 
8238fca70d2Sjsg 
8248fca70d2Sjsg 
8258fca70d2Sjsg 	/*
8268fca70d2Sjsg 	 * Initialise the RX interface
8278fca70d2Sjsg 	 */
8288fca70d2Sjsg 	if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) < 0) {
8298fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n",
8308fca70d2Sjsg 		    err);
8318fca70d2Sjsg 		goto init_failed;
8328fca70d2Sjsg 	}
8338fca70d2Sjsg 
8348fca70d2Sjsg 	/*
8354b1a56afSjsg 	 * The following settings are used for 'turbo mode', a.k.a multiple
8368fca70d2Sjsg 	 * frames per Rx transaction (again info taken form Linux driver).
8378fca70d2Sjsg 	 */
8388fca70d2Sjsg #ifdef SMSC_TURBO
8398fca70d2Sjsg 	reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE);
8408fca70d2Sjsg #endif
8418fca70d2Sjsg 
8428fca70d2Sjsg 	smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
8438fca70d2Sjsg 
8448fca70d2Sjsg 	/* Clear the status register ? */
8458fca70d2Sjsg 	smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff);
8468fca70d2Sjsg 
8478fca70d2Sjsg 	/* Read and display the revision register */
8488fca70d2Sjsg 	if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) {
8498fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err);
8508fca70d2Sjsg 		goto init_failed;
8518fca70d2Sjsg 	}
8528fca70d2Sjsg 
8538fca70d2Sjsg 	/* GPIO/LED setup */
8548fca70d2Sjsg 	reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED |
8558fca70d2Sjsg 	          SMSC_LED_GPIO_CFG_FDX_LED;
8568fca70d2Sjsg 	smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val);
8578fca70d2Sjsg 
8588fca70d2Sjsg 	/*
8598fca70d2Sjsg 	 * Initialise the TX interface
8608fca70d2Sjsg 	 */
8618fca70d2Sjsg 	smsc_write_reg(sc, SMSC_FLOW, 0);
8628fca70d2Sjsg 
8638fca70d2Sjsg 	smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT);
8648fca70d2Sjsg 
8658fca70d2Sjsg 	/* Read the current MAC configuration */
8668fca70d2Sjsg 	if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) {
8678fca70d2Sjsg 		smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err);
8688fca70d2Sjsg 		goto init_failed;
8698fca70d2Sjsg 	}
8708fca70d2Sjsg 
8718fca70d2Sjsg 	/* Vlan */
8728fca70d2Sjsg 	smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN);
8738fca70d2Sjsg 
8748fca70d2Sjsg 	/*
8758fca70d2Sjsg 	 * Start TX
8768fca70d2Sjsg 	 */
8778fca70d2Sjsg 	sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN;
8788fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
8798fca70d2Sjsg 	smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON);
8808fca70d2Sjsg 
8818fca70d2Sjsg 	/*
8828fca70d2Sjsg 	 * Start RX
8838fca70d2Sjsg 	 */
8848fca70d2Sjsg 	sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN;
8858fca70d2Sjsg 	smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
8868fca70d2Sjsg 
8878fca70d2Sjsg 	return (0);
8888fca70d2Sjsg 
8898fca70d2Sjsg init_failed:
8908fca70d2Sjsg 	smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err);
8918fca70d2Sjsg 	return (err);
8928fca70d2Sjsg }
8938fca70d2Sjsg 
8948fca70d2Sjsg int
smsc_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)8958fca70d2Sjsg smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
8968fca70d2Sjsg {
8978fca70d2Sjsg 	struct smsc_softc	*sc = ifp->if_softc;
8988fca70d2Sjsg 	struct ifreq		*ifr = (struct ifreq *)data;
8998fca70d2Sjsg 	int			s, error = 0;
9008fca70d2Sjsg 
9018fca70d2Sjsg 	s = splnet();
9028fca70d2Sjsg 
9038fca70d2Sjsg 	switch(cmd) {
9048fca70d2Sjsg 	case SIOCSIFADDR:
9058fca70d2Sjsg 		ifp->if_flags |= IFF_UP;
9068fca70d2Sjsg 		if (!(ifp->if_flags & IFF_RUNNING))
9078fca70d2Sjsg 			smsc_init(sc);
9088fca70d2Sjsg 		break;
9098fca70d2Sjsg 
9108fca70d2Sjsg 	case SIOCSIFFLAGS:
9118fca70d2Sjsg 		if (ifp->if_flags & IFF_UP) {
912f00b3ba9Sbrad 			if (ifp->if_flags & IFF_RUNNING)
913f00b3ba9Sbrad 				error = ENETRESET;
914f00b3ba9Sbrad 			else
9158fca70d2Sjsg 				smsc_init(sc);
9168fca70d2Sjsg 		} else {
9178fca70d2Sjsg 			if (ifp->if_flags & IFF_RUNNING)
9188fca70d2Sjsg 				smsc_stop(sc);
9198fca70d2Sjsg 		}
9208fca70d2Sjsg 		break;
9218fca70d2Sjsg 
9228fca70d2Sjsg 	case SIOCGIFMEDIA:
9238fca70d2Sjsg 	case SIOCSIFMEDIA:
924f00b3ba9Sbrad 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
9258fca70d2Sjsg 		break;
9268fca70d2Sjsg 
9278fca70d2Sjsg 	default:
9288fca70d2Sjsg 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
9298fca70d2Sjsg 	}
9308fca70d2Sjsg 
9318fca70d2Sjsg 	if (error == ENETRESET) {
9328fca70d2Sjsg 		if (ifp->if_flags & IFF_RUNNING)
933f00b3ba9Sbrad 			smsc_iff(sc);
9348fca70d2Sjsg 		error = 0;
9358fca70d2Sjsg 	}
9368fca70d2Sjsg 
9378fca70d2Sjsg 	splx(s);
9388fca70d2Sjsg 	return(error);
9398fca70d2Sjsg }
9408fca70d2Sjsg 
9418fca70d2Sjsg int
smsc_match(struct device * parent,void * match,void * aux)9428fca70d2Sjsg smsc_match(struct device *parent, void *match, void *aux)
9438fca70d2Sjsg {
9448fca70d2Sjsg 	struct usb_attach_arg *uaa = aux;
9458fca70d2Sjsg 
946f8ef4bb8Smpi 	if (uaa->iface == NULL || uaa->configno != 1)
9478fca70d2Sjsg 		return UMATCH_NONE;
9488fca70d2Sjsg 
9498fca70d2Sjsg 	return (usb_lookup(smsc_devs, uaa->vendor, uaa->product) != NULL) ?
950f8ef4bb8Smpi 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE;
9518fca70d2Sjsg }
9528fca70d2Sjsg 
9538fca70d2Sjsg void
smsc_attach(struct device * parent,struct device * self,void * aux)9548fca70d2Sjsg smsc_attach(struct device *parent, struct device *self, void *aux)
9558fca70d2Sjsg {
9568fca70d2Sjsg 	struct smsc_softc *sc = (struct smsc_softc *)self;
9578fca70d2Sjsg 	struct usb_attach_arg *uaa = aux;
9588fca70d2Sjsg 	usb_interface_descriptor_t *id;
9598fca70d2Sjsg 	usb_endpoint_descriptor_t *ed;
9608fca70d2Sjsg 	struct mii_data *mii;
9618fca70d2Sjsg 	struct ifnet *ifp;
9628fca70d2Sjsg 	uint32_t mac_h, mac_l;
963f8ef4bb8Smpi 	int s, i;
9648fca70d2Sjsg 
965f8ef4bb8Smpi 	sc->sc_udev = uaa->device;
966f8ef4bb8Smpi 	sc->sc_iface = uaa->iface;
9678fca70d2Sjsg 
9688fca70d2Sjsg 	/* Setup the endpoints for the SMSC LAN95xx device(s) */
9698fca70d2Sjsg 	usb_init_task(&sc->sc_tick_task, smsc_tick_task, sc,
9708fca70d2Sjsg 	    USB_TASK_TYPE_GENERIC);
9718fca70d2Sjsg 	rw_init(&sc->sc_mii_lock, "smscmii");
9728fca70d2Sjsg 	usb_init_task(&sc->sc_stop_task, (void (*)(void *))smsc_stop, sc,
9738fca70d2Sjsg 	    USB_TASK_TYPE_GENERIC);
9748fca70d2Sjsg 
9758fca70d2Sjsg 	id = usbd_get_interface_descriptor(sc->sc_iface);
9768fca70d2Sjsg 
977015115d7Sjsg 	if (sc->sc_udev->speed >= USB_SPEED_HIGH)
978015115d7Sjsg 		sc->sc_bufsz = SMSC_MAX_BUFSZ;
979015115d7Sjsg 	else
980015115d7Sjsg 		sc->sc_bufsz = SMSC_MIN_BUFSZ;
981015115d7Sjsg 
9828fca70d2Sjsg 	/* Find endpoints. */
9838fca70d2Sjsg 	for (i = 0; i < id->bNumEndpoints; i++) {
9848fca70d2Sjsg 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
9858fca70d2Sjsg 		if (!ed) {
9868fca70d2Sjsg 			printf("%s: couldn't get ep %d\n",
9878fca70d2Sjsg 			    sc->sc_dev.dv_xname, i);
9888fca70d2Sjsg 			return;
9898fca70d2Sjsg 		}
9908fca70d2Sjsg 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
9918fca70d2Sjsg 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
9928fca70d2Sjsg 			sc->sc_ed[SMSC_ENDPT_RX] = ed->bEndpointAddress;
9938fca70d2Sjsg 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
9948fca70d2Sjsg 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
9958fca70d2Sjsg 			sc->sc_ed[SMSC_ENDPT_TX] = ed->bEndpointAddress;
9968fca70d2Sjsg 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
9978fca70d2Sjsg 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
9988fca70d2Sjsg 			sc->sc_ed[SMSC_ENDPT_INTR] = ed->bEndpointAddress;
9998fca70d2Sjsg 		}
10008fca70d2Sjsg 	}
10018fca70d2Sjsg 
10028fca70d2Sjsg 	s = splnet();
10038fca70d2Sjsg 
10048fca70d2Sjsg 	ifp = &sc->sc_ac.ac_if;
10058fca70d2Sjsg 	ifp->if_softc = sc;
10068fca70d2Sjsg 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
10078fca70d2Sjsg 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
10088fca70d2Sjsg 	ifp->if_ioctl = smsc_ioctl;
10098fca70d2Sjsg 	ifp->if_start = smsc_start;
10108fca70d2Sjsg 	ifp->if_capabilities = IFCAP_VLAN_MTU;
10118fca70d2Sjsg 
10128fca70d2Sjsg 	/* Setup some of the basics */
10138fca70d2Sjsg 	sc->sc_phyno = 1;
10148fca70d2Sjsg 
10158fca70d2Sjsg 	/*
10168fca70d2Sjsg 	 * Attempt to get the mac address, if an EEPROM is not attached this
10178fca70d2Sjsg 	 * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
10188fca70d2Sjsg 	 * address based on urandom.
10198fca70d2Sjsg 	 */
10208fca70d2Sjsg 	memset(sc->sc_ac.ac_enaddr, 0xff, ETHER_ADDR_LEN);
10218fca70d2Sjsg 
10228fca70d2Sjsg 	/* Check if there is already a MAC address in the register */
10238fca70d2Sjsg 	if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) &&
10248fca70d2Sjsg 	    (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) {
10258fca70d2Sjsg 		sc->sc_ac.ac_enaddr[5] = (uint8_t)((mac_h >> 8) & 0xff);
10268fca70d2Sjsg 		sc->sc_ac.ac_enaddr[4] = (uint8_t)((mac_h) & 0xff);
10278fca70d2Sjsg 		sc->sc_ac.ac_enaddr[3] = (uint8_t)((mac_l >> 24) & 0xff);
10288fca70d2Sjsg 		sc->sc_ac.ac_enaddr[2] = (uint8_t)((mac_l >> 16) & 0xff);
10298fca70d2Sjsg 		sc->sc_ac.ac_enaddr[1] = (uint8_t)((mac_l >> 8) & 0xff);
10308fca70d2Sjsg 		sc->sc_ac.ac_enaddr[0] = (uint8_t)((mac_l) & 0xff);
10318fca70d2Sjsg 	}
10328fca70d2Sjsg 
1033ef8c47aaSjsg 	smsc_enaddr_OF(sc);
1034ef8c47aaSjsg 
10358fca70d2Sjsg 	printf("%s: address %s\n", sc->sc_dev.dv_xname,
10368fca70d2Sjsg 	    ether_sprintf(sc->sc_ac.ac_enaddr));
10378fca70d2Sjsg 
10388fca70d2Sjsg 	/* Initialise the chip for the first time */
10398fca70d2Sjsg 	smsc_chip_init(sc);
10408fca70d2Sjsg 
10418fca70d2Sjsg 	/* Initialize MII/media info. */
10428fca70d2Sjsg 	mii = &sc->sc_mii;
10438fca70d2Sjsg 	mii->mii_ifp = ifp;
10448fca70d2Sjsg 	mii->mii_readreg = smsc_miibus_readreg;
10458fca70d2Sjsg 	mii->mii_writereg = smsc_miibus_writereg;
10468fca70d2Sjsg 	mii->mii_statchg = smsc_miibus_statchg;
10478fca70d2Sjsg 	mii->mii_flags = MIIF_AUTOTSLEEP;
10488fca70d2Sjsg 
10498fca70d2Sjsg 	ifmedia_init(&mii->mii_media, 0, smsc_ifmedia_upd, smsc_ifmedia_sts);
10508fca70d2Sjsg 	mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
10518fca70d2Sjsg 
10528fca70d2Sjsg 	if (LIST_FIRST(&mii->mii_phys) == NULL) {
10538fca70d2Sjsg 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
10548fca70d2Sjsg 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
10558fca70d2Sjsg 	} else
10568fca70d2Sjsg 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
10578fca70d2Sjsg 
10588fca70d2Sjsg 	if_attach(ifp);
10598fca70d2Sjsg 	ether_ifattach(ifp);
10608fca70d2Sjsg 
10618fca70d2Sjsg 	timeout_set(&sc->sc_stat_ch, smsc_tick, sc);
10628fca70d2Sjsg 
10638fca70d2Sjsg 	splx(s);
10648fca70d2Sjsg }
10658fca70d2Sjsg 
10668fca70d2Sjsg int
smsc_detach(struct device * self,int flags)10678fca70d2Sjsg smsc_detach(struct device *self, int flags)
10688fca70d2Sjsg {
10698fca70d2Sjsg 	struct smsc_softc *sc = (struct smsc_softc *)self;
10708fca70d2Sjsg 	struct ifnet *ifp = &sc->sc_ac.ac_if;
10718fca70d2Sjsg 	int s;
10728fca70d2Sjsg 
10738fca70d2Sjsg 	if (timeout_initialized(&sc->sc_stat_ch))
10748fca70d2Sjsg 		timeout_del(&sc->sc_stat_ch);
10758fca70d2Sjsg 
10768fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_TX] != NULL)
10778fca70d2Sjsg 		usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_TX]);
10788fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_RX] != NULL)
10798fca70d2Sjsg 		usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_RX]);
10808fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL)
10818fca70d2Sjsg 		usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_INTR]);
10828fca70d2Sjsg 
10838fca70d2Sjsg 	/*
10848fca70d2Sjsg 	 * Remove any pending tasks.  They cannot be executing because they run
10858fca70d2Sjsg 	 * in the same thread as detach.
10868fca70d2Sjsg 	 */
10878fca70d2Sjsg 	usb_rem_task(sc->sc_udev, &sc->sc_tick_task);
10888fca70d2Sjsg 	usb_rem_task(sc->sc_udev, &sc->sc_stop_task);
10898fca70d2Sjsg 
10908fca70d2Sjsg 	s = splusb();
10918fca70d2Sjsg 
10928fca70d2Sjsg 	if (--sc->sc_refcnt >= 0) {
10938fca70d2Sjsg 		/* Wait for processes to go away */
10948fca70d2Sjsg 		usb_detach_wait(&sc->sc_dev);
10958fca70d2Sjsg 	}
10968fca70d2Sjsg 
10978fca70d2Sjsg 	if (ifp->if_flags & IFF_RUNNING)
10988fca70d2Sjsg 		smsc_stop(sc);
10998fca70d2Sjsg 
11008fca70d2Sjsg 	mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
11018fca70d2Sjsg 	ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
11028fca70d2Sjsg 	if (ifp->if_softc != NULL) {
11038fca70d2Sjsg 		ether_ifdetach(ifp);
11048fca70d2Sjsg 		if_detach(ifp);
11058fca70d2Sjsg 	}
11068fca70d2Sjsg 
11078fca70d2Sjsg #ifdef DIAGNOSTIC
11088fca70d2Sjsg 	if (sc->sc_ep[SMSC_ENDPT_TX] != NULL ||
11098fca70d2Sjsg 	    sc->sc_ep[SMSC_ENDPT_RX] != NULL ||
11108fca70d2Sjsg 	    sc->sc_ep[SMSC_ENDPT_INTR] != NULL)
11118fca70d2Sjsg 		printf("%s: detach has active endpoints\n",
11128fca70d2Sjsg 		    sc->sc_dev.dv_xname);
11138fca70d2Sjsg #endif
11148fca70d2Sjsg 
11158fca70d2Sjsg 	splx(s);
11168fca70d2Sjsg 
11178fca70d2Sjsg 	return (0);
11188fca70d2Sjsg }
11198fca70d2Sjsg 
11208fca70d2Sjsg void
smsc_tick_task(void * xsc)11218fca70d2Sjsg smsc_tick_task(void *xsc)
11228fca70d2Sjsg {
11238fca70d2Sjsg 	int			 s;
11248fca70d2Sjsg 	struct smsc_softc	*sc = xsc;
11258fca70d2Sjsg 	struct mii_data		*mii;
11268fca70d2Sjsg 
11278fca70d2Sjsg 	if (sc == NULL)
11288fca70d2Sjsg 		return;
11298fca70d2Sjsg 
11308fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
11318fca70d2Sjsg 		return;
11328fca70d2Sjsg 	mii = &sc->sc_mii;
11338fca70d2Sjsg 	if (mii == NULL)
11348fca70d2Sjsg 		return;
11358fca70d2Sjsg 
11368fca70d2Sjsg 	s = splnet();
11378fca70d2Sjsg 
11388fca70d2Sjsg 	mii_tick(mii);
11398fca70d2Sjsg 	if ((sc->sc_flags & SMSC_FLAG_LINK) == 0)
11408fca70d2Sjsg 		smsc_miibus_statchg(&sc->sc_dev);
11418fca70d2Sjsg 	timeout_add_sec(&sc->sc_stat_ch, 1);
11428fca70d2Sjsg 
11438fca70d2Sjsg 	splx(s);
11448fca70d2Sjsg }
11458fca70d2Sjsg 
11468fca70d2Sjsg void
smsc_lock_mii(struct smsc_softc * sc)11478fca70d2Sjsg smsc_lock_mii(struct smsc_softc *sc)
11488fca70d2Sjsg {
11498fca70d2Sjsg 	sc->sc_refcnt++;
11508fca70d2Sjsg 	rw_enter_write(&sc->sc_mii_lock);
11518fca70d2Sjsg }
11528fca70d2Sjsg 
11538fca70d2Sjsg void
smsc_unlock_mii(struct smsc_softc * sc)11548fca70d2Sjsg smsc_unlock_mii(struct smsc_softc *sc)
11558fca70d2Sjsg {
11568fca70d2Sjsg 	rw_exit_write(&sc->sc_mii_lock);
11578fca70d2Sjsg 	if (--sc->sc_refcnt < 0)
11588fca70d2Sjsg 		usb_detach_wakeup(&sc->sc_dev);
11598fca70d2Sjsg }
11608fca70d2Sjsg 
11618fca70d2Sjsg void
smsc_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1162ab0b1be7Smglocker smsc_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
11638fca70d2Sjsg {
11648fca70d2Sjsg 	struct smsc_chain	*c = (struct smsc_chain *)priv;
11658fca70d2Sjsg 	struct smsc_softc	*sc = c->sc_sc;
11668fca70d2Sjsg 	struct ifnet		*ifp = &sc->sc_ac.ac_if;
11678fca70d2Sjsg 	u_char			*buf = c->sc_buf;
11688fca70d2Sjsg 	uint32_t		total_len;
11698fca70d2Sjsg 	uint16_t		pktlen = 0;
1170469696c0Smpi 	struct mbuf_list	ml = MBUF_LIST_INITIALIZER();
11718fca70d2Sjsg 	struct mbuf		*m;
11728fca70d2Sjsg 	int			s;
11738fca70d2Sjsg 	uint32_t		rxhdr;
11748fca70d2Sjsg 
11758fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
11768fca70d2Sjsg 		return;
11778fca70d2Sjsg 
11788fca70d2Sjsg 	if (!(ifp->if_flags & IFF_RUNNING))
11798fca70d2Sjsg 		return;
11808fca70d2Sjsg 
11818fca70d2Sjsg 	if (status != USBD_NORMAL_COMPLETION) {
11828fca70d2Sjsg 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
11838fca70d2Sjsg 			return;
11848fca70d2Sjsg 		if (usbd_ratecheck(&sc->sc_rx_notice)) {
11858fca70d2Sjsg 			printf("%s: usb errors on rx: %s\n",
11868fca70d2Sjsg 			    sc->sc_dev.dv_xname, usbd_errstr(status));
11878fca70d2Sjsg 		}
11888fca70d2Sjsg 		if (status == USBD_STALLED)
11898fca70d2Sjsg 			usbd_clear_endpoint_stall_async(sc->sc_ep[SMSC_ENDPT_RX]);
11908fca70d2Sjsg 		goto done;
11918fca70d2Sjsg 	}
11928fca70d2Sjsg 
11938fca70d2Sjsg 	usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
11948fca70d2Sjsg 	smsc_dbg_printf(sc, "xfer status total_len %d\n", total_len);
11958fca70d2Sjsg 
11968fca70d2Sjsg 	do {
11978fca70d2Sjsg 		if (total_len < sizeof(rxhdr)) {
11988fca70d2Sjsg 			smsc_dbg_printf(sc, "total_len %d < sizeof(rxhdr) %d\n",
11998fca70d2Sjsg 			    total_len, sizeof(rxhdr));
12008fca70d2Sjsg 			ifp->if_ierrors++;
12018fca70d2Sjsg 			goto done;
12028fca70d2Sjsg 		}
12038fca70d2Sjsg 
12048fca70d2Sjsg 		buf += pktlen;
12058fca70d2Sjsg 
12068fca70d2Sjsg 		memcpy(&rxhdr, buf, sizeof(rxhdr));
12078fca70d2Sjsg 		rxhdr = letoh32(rxhdr);
12088fca70d2Sjsg 		total_len -= sizeof(rxhdr);
12098fca70d2Sjsg 
12108fca70d2Sjsg 		if (rxhdr & SMSC_RX_STAT_ERROR) {
12118fca70d2Sjsg 			smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr);
12128fca70d2Sjsg 			ifp->if_ierrors++;
12138fca70d2Sjsg 			goto done;
12148fca70d2Sjsg 		}
12158fca70d2Sjsg 
12168fca70d2Sjsg 		pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr);
12178fca70d2Sjsg 		smsc_dbg_printf(sc, "rxeof total_len %d pktlen %d rxhdr "
12188fca70d2Sjsg 		    "0x%08x\n", total_len, pktlen, rxhdr);
12198fca70d2Sjsg 		if (pktlen > total_len) {
12208fca70d2Sjsg 			smsc_dbg_printf(sc, "pktlen %d > total_len %d\n",
12218fca70d2Sjsg 			    pktlen, total_len);
12228fca70d2Sjsg 			ifp->if_ierrors++;
12238fca70d2Sjsg 			goto done;
12248fca70d2Sjsg 		}
12258fca70d2Sjsg 
12268fca70d2Sjsg 		buf += sizeof(rxhdr);
12278fca70d2Sjsg 
1228d3fe6d07Sbrad 		if (total_len < pktlen)
12298fca70d2Sjsg 			total_len = 0;
12308fca70d2Sjsg 		else
12318fca70d2Sjsg 			total_len -= pktlen;
12328fca70d2Sjsg 
1233a97ca9a9Skettenis 		m = m_devget(buf, pktlen, ETHER_ALIGN);
12348fca70d2Sjsg 		if (m == NULL) {
1235a97ca9a9Skettenis 			smsc_dbg_printf(sc, "m_devget returned NULL\n");
12368fca70d2Sjsg 			ifp->if_ierrors++;
12378fca70d2Sjsg 			goto done;
12388fca70d2Sjsg 		}
12398fca70d2Sjsg 
1240469696c0Smpi 		ml_enqueue(&ml, m);
12418fca70d2Sjsg 	} while (total_len > 0);
12428fca70d2Sjsg 
12438fca70d2Sjsg done:
1244469696c0Smpi 	s = splnet();
1245469696c0Smpi 	if_input(ifp, &ml);
1246469696c0Smpi 	splx(s);
1247015115d7Sjsg 	memset(c->sc_buf, 0, sc->sc_bufsz);
12488fca70d2Sjsg 
12498fca70d2Sjsg 	/* Setup new transfer. */
12508fca70d2Sjsg 	usbd_setup_xfer(xfer, sc->sc_ep[SMSC_ENDPT_RX],
1251015115d7Sjsg 	    c, c->sc_buf, sc->sc_bufsz,
12528fca70d2Sjsg 	    USBD_SHORT_XFER_OK | USBD_NO_COPY,
12538fca70d2Sjsg 	    USBD_NO_TIMEOUT, smsc_rxeof);
12548fca70d2Sjsg 	usbd_transfer(xfer);
12558fca70d2Sjsg 
12568fca70d2Sjsg 	return;
12578fca70d2Sjsg }
12588fca70d2Sjsg 
12598fca70d2Sjsg void
smsc_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1260ab0b1be7Smglocker smsc_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
12618fca70d2Sjsg {
12628fca70d2Sjsg 	struct smsc_softc	*sc;
12638fca70d2Sjsg 	struct smsc_chain	*c;
12648fca70d2Sjsg 	struct ifnet		*ifp;
12658fca70d2Sjsg 	int			s;
12668fca70d2Sjsg 
12678fca70d2Sjsg 	c = priv;
12688fca70d2Sjsg 	sc = c->sc_sc;
12698fca70d2Sjsg 	ifp = &sc->sc_ac.ac_if;
12708fca70d2Sjsg 
12718fca70d2Sjsg 	if (usbd_is_dying(sc->sc_udev))
12728fca70d2Sjsg 		return;
12738fca70d2Sjsg 
12748fca70d2Sjsg 	s = splnet();
12758fca70d2Sjsg 
12768fca70d2Sjsg 	if (status != USBD_NORMAL_COMPLETION) {
12778fca70d2Sjsg 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
12788fca70d2Sjsg 			splx(s);
12798fca70d2Sjsg 			return;
12808fca70d2Sjsg 		}
12818fca70d2Sjsg 		ifp->if_oerrors++;
12828fca70d2Sjsg 		printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname,
12838fca70d2Sjsg 		    usbd_errstr(status));
12848fca70d2Sjsg 		if (status == USBD_STALLED)
12858fca70d2Sjsg 			usbd_clear_endpoint_stall_async(sc->sc_ep[SMSC_ENDPT_TX]);
12868fca70d2Sjsg 		splx(s);
12878fca70d2Sjsg 		return;
12888fca70d2Sjsg 	}
12898fca70d2Sjsg 
12908fca70d2Sjsg 	ifp->if_timer = 0;
1291de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
12928fca70d2Sjsg 
12938fca70d2Sjsg 	m_freem(c->sc_mbuf);
12948fca70d2Sjsg 	c->sc_mbuf = NULL;
12958fca70d2Sjsg 
12960cae21bdSpatrick 	if (ifq_empty(&ifp->if_snd) == 0)
12978fca70d2Sjsg 		smsc_start(ifp);
12988fca70d2Sjsg 
12998fca70d2Sjsg 	splx(s);
13008fca70d2Sjsg }
13018fca70d2Sjsg 
13028fca70d2Sjsg int
smsc_tx_list_init(struct smsc_softc * sc)13038fca70d2Sjsg smsc_tx_list_init(struct smsc_softc *sc)
13048fca70d2Sjsg {
13058fca70d2Sjsg 	struct smsc_cdata *cd;
13068fca70d2Sjsg 	struct smsc_chain *c;
13078fca70d2Sjsg 	int i;
13088fca70d2Sjsg 
13098fca70d2Sjsg 	cd = &sc->sc_cdata;
13108fca70d2Sjsg 	for (i = 0; i < SMSC_TX_LIST_CNT; i++) {
13118fca70d2Sjsg 		c = &cd->tx_chain[i];
13128fca70d2Sjsg 		c->sc_sc = sc;
13138fca70d2Sjsg 		c->sc_idx = i;
13148fca70d2Sjsg 		c->sc_mbuf = NULL;
13158fca70d2Sjsg 		if (c->sc_xfer == NULL) {
13168fca70d2Sjsg 			c->sc_xfer = usbd_alloc_xfer(sc->sc_udev);
13178fca70d2Sjsg 			if (c->sc_xfer == NULL)
13188fca70d2Sjsg 				return (ENOBUFS);
13198fca70d2Sjsg 			c->sc_buf = usbd_alloc_buffer(c->sc_xfer,
1320015115d7Sjsg 			    sc->sc_bufsz);
13218fca70d2Sjsg 			if (c->sc_buf == NULL) {
13228fca70d2Sjsg 				usbd_free_xfer(c->sc_xfer);
13238fca70d2Sjsg 				return (ENOBUFS);
13248fca70d2Sjsg 			}
13258fca70d2Sjsg 		}
13268fca70d2Sjsg 	}
13278fca70d2Sjsg 
13288fca70d2Sjsg 	return (0);
13298fca70d2Sjsg }
13308fca70d2Sjsg 
13318fca70d2Sjsg int
smsc_rx_list_init(struct smsc_softc * sc)13328fca70d2Sjsg smsc_rx_list_init(struct smsc_softc *sc)
13338fca70d2Sjsg {
13348fca70d2Sjsg 	struct smsc_cdata *cd;
13358fca70d2Sjsg 	struct smsc_chain *c;
13368fca70d2Sjsg 	int i;
13378fca70d2Sjsg 
13388fca70d2Sjsg 	cd = &sc->sc_cdata;
13398fca70d2Sjsg 	for (i = 0; i < SMSC_RX_LIST_CNT; i++) {
13408fca70d2Sjsg 		c = &cd->rx_chain[i];
13418fca70d2Sjsg 		c->sc_sc = sc;
13428fca70d2Sjsg 		c->sc_idx = i;
13438fca70d2Sjsg 		c->sc_mbuf = NULL;
13448fca70d2Sjsg 		if (c->sc_xfer == NULL) {
13458fca70d2Sjsg 			c->sc_xfer = usbd_alloc_xfer(sc->sc_udev);
13468fca70d2Sjsg 			if (c->sc_xfer == NULL)
13478fca70d2Sjsg 				return (ENOBUFS);
13488fca70d2Sjsg 			c->sc_buf = usbd_alloc_buffer(c->sc_xfer,
1349015115d7Sjsg 			    sc->sc_bufsz);
13508fca70d2Sjsg 			if (c->sc_buf == NULL) {
13518fca70d2Sjsg 				usbd_free_xfer(c->sc_xfer);
13528fca70d2Sjsg 				return (ENOBUFS);
13538fca70d2Sjsg 			}
13548fca70d2Sjsg 		}
13558fca70d2Sjsg 	}
13568fca70d2Sjsg 
13578fca70d2Sjsg 	return (0);
13588fca70d2Sjsg }
13598fca70d2Sjsg 
13608fca70d2Sjsg int
smsc_encap(struct smsc_softc * sc,struct mbuf * m,int idx)13618fca70d2Sjsg smsc_encap(struct smsc_softc *sc, struct mbuf *m, int idx)
13628fca70d2Sjsg {
13638fca70d2Sjsg 	struct smsc_chain	*c;
13648fca70d2Sjsg 	usbd_status		 err;
13658fca70d2Sjsg 	uint32_t		 txhdr;
13668fca70d2Sjsg 	uint32_t		 frm_len = 0;
13678fca70d2Sjsg 
13688fca70d2Sjsg 	c = &sc->sc_cdata.tx_chain[idx];
13698fca70d2Sjsg 
13708fca70d2Sjsg 	/*
13718fca70d2Sjsg 	 * Each frame is prefixed with two 32-bit values describing the
13728fca70d2Sjsg 	 * length of the packet and buffer.
13738fca70d2Sjsg 	 */
13748fca70d2Sjsg 	txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) |
13758fca70d2Sjsg 			SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG;
13768fca70d2Sjsg 	txhdr = htole32(txhdr);
13778fca70d2Sjsg 	memcpy(c->sc_buf, &txhdr, sizeof(txhdr));
13788fca70d2Sjsg 
13798fca70d2Sjsg 	txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len);
13808fca70d2Sjsg 	txhdr = htole32(txhdr);
13818fca70d2Sjsg 	memcpy(c->sc_buf + 4, &txhdr, sizeof(txhdr));
13828fca70d2Sjsg 
13838fca70d2Sjsg 	frm_len += 8;
13848fca70d2Sjsg 
13858fca70d2Sjsg 	/* Next copy in the actual packet */
13868fca70d2Sjsg 	m_copydata(m, 0, m->m_pkthdr.len, c->sc_buf + frm_len);
13878fca70d2Sjsg 	frm_len += m->m_pkthdr.len;
13888fca70d2Sjsg 
13898fca70d2Sjsg 	c->sc_mbuf = m;
13908fca70d2Sjsg 
13918fca70d2Sjsg 	usbd_setup_xfer(c->sc_xfer, sc->sc_ep[SMSC_ENDPT_TX],
13928fca70d2Sjsg 	    c, c->sc_buf, frm_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
13938fca70d2Sjsg 	    10000, smsc_txeof);
13948fca70d2Sjsg 
13958fca70d2Sjsg 	err = usbd_transfer(c->sc_xfer);
13968fca70d2Sjsg 	if (err != USBD_IN_PROGRESS) {
13974ced949bSgerhard 		c->sc_mbuf = NULL;
13988fca70d2Sjsg 		smsc_stop(sc);
13998fca70d2Sjsg 		return (EIO);
14008fca70d2Sjsg 	}
14018fca70d2Sjsg 
14028fca70d2Sjsg 	sc->sc_cdata.tx_cnt++;
14038fca70d2Sjsg 
14048fca70d2Sjsg 	return (0);
14058fca70d2Sjsg }
1406