xref: /illumos-gate/usr/src/uts/common/io/usbgem/usbgem.c (revision 7089fa46)
16716431bSRobert Mustacchi /*
26716431bSRobert Mustacchi  * usbgem.c: General USB to Fast Ethernet mac driver framework
36716431bSRobert Mustacchi  *
46716431bSRobert Mustacchi  * Copyright (c) 2002-2012 Masayuki Murayama.  All rights reserved.
56716431bSRobert Mustacchi  *
66716431bSRobert Mustacchi  * Redistribution and use in source and binary forms, with or without
76716431bSRobert Mustacchi  * modification, are permitted provided that the following conditions are met:
86716431bSRobert Mustacchi  *
96716431bSRobert Mustacchi  * 1. Redistributions of source code must retain the above copyright notice,
106716431bSRobert Mustacchi  *    this list of conditions and the following disclaimer.
116716431bSRobert Mustacchi  *
126716431bSRobert Mustacchi  * 2. Redistributions in binary form must reproduce the above copyright notice,
136716431bSRobert Mustacchi  *    this list of conditions and the following disclaimer in the documentation
146716431bSRobert Mustacchi  *    and/or other materials provided with the distribution.
156716431bSRobert Mustacchi  *
166716431bSRobert Mustacchi  * 3. Neither the name of the author nor the names of its contributors may be
176716431bSRobert Mustacchi  *    used to endorse or promote products derived from this software without
186716431bSRobert Mustacchi  *    specific prior written permission.
196716431bSRobert Mustacchi  *
206716431bSRobert Mustacchi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
216716431bSRobert Mustacchi  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
226716431bSRobert Mustacchi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
236716431bSRobert Mustacchi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
246716431bSRobert Mustacchi  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
256716431bSRobert Mustacchi  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
266716431bSRobert Mustacchi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
276716431bSRobert Mustacchi  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
286716431bSRobert Mustacchi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
296716431bSRobert Mustacchi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
306716431bSRobert Mustacchi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
316716431bSRobert Mustacchi  * DAMAGE.
326716431bSRobert Mustacchi  */
336716431bSRobert Mustacchi 
3444bf619dSJohn Levon /*
3544bf619dSJohn Levon  * Copyright 2019 Joyent, Inc.
36*7089fa46SGarrett D'Amore  * Copyright 2022 Garrett D'Amore
3744bf619dSJohn Levon  */
3844bf619dSJohn Levon 
396716431bSRobert Mustacchi /*
406716431bSRobert Mustacchi  * Change log
416716431bSRobert Mustacchi  */
426716431bSRobert Mustacchi 
436716431bSRobert Mustacchi /*
446716431bSRobert Mustacchi  * TODO:
45485f90e9SToomas Soome  *	implement DELAYED_START
466716431bSRobert Mustacchi  */
476716431bSRobert Mustacchi 
486716431bSRobert Mustacchi /*
496716431bSRobert Mustacchi  * System Header files.
506716431bSRobert Mustacchi  */
516716431bSRobert Mustacchi #include <sys/types.h>
526716431bSRobert Mustacchi #include <sys/conf.h>
536716431bSRobert Mustacchi #include <sys/debug.h>
546716431bSRobert Mustacchi #include <sys/kmem.h>
556716431bSRobert Mustacchi #include <sys/vtrace.h>
566716431bSRobert Mustacchi #include <sys/ethernet.h>
576716431bSRobert Mustacchi #include <sys/modctl.h>
586716431bSRobert Mustacchi #include <sys/errno.h>
596716431bSRobert Mustacchi #include <sys/ddi.h>
606716431bSRobert Mustacchi #include <sys/sunddi.h>
616716431bSRobert Mustacchi #include <sys/stream.h>		/* required for MBLK* */
626716431bSRobert Mustacchi #include <sys/strsun.h>		/* required for mionack() */
636716431bSRobert Mustacchi #include <sys/byteorder.h>
646716431bSRobert Mustacchi 
656716431bSRobert Mustacchi #include <sys/usb/usba.h>
666716431bSRobert Mustacchi #include <inet/common.h>
676716431bSRobert Mustacchi #include <inet/led.h>
686716431bSRobert Mustacchi #include <inet/mi.h>
696716431bSRobert Mustacchi #include <inet/nd.h>
706716431bSRobert Mustacchi 
716716431bSRobert Mustacchi /* supplement definitions */
726716431bSRobert Mustacchi extern const char *usb_str_cr(usb_cr_t);
736716431bSRobert Mustacchi 
746716431bSRobert Mustacchi #include <sys/note.h>
756716431bSRobert Mustacchi 
766716431bSRobert Mustacchi #include "usbgem_mii.h"
776716431bSRobert Mustacchi #include "usbgem.h"
786716431bSRobert Mustacchi 
796716431bSRobert Mustacchi char	ident[] = "usb general ethernet mac driver v" VERSION;
806716431bSRobert Mustacchi 
816716431bSRobert Mustacchi /* Debugging support */
826716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
836716431bSRobert Mustacchi static int usbgem_debug = USBGEM_DEBUG_LEVEL;
846716431bSRobert Mustacchi #define	DPRINTF(n, args)	if (usbgem_debug > (n)) cmn_err args
856716431bSRobert Mustacchi #else
866716431bSRobert Mustacchi #define	DPRINTF(n, args)
876716431bSRobert Mustacchi #endif
886716431bSRobert Mustacchi 
896716431bSRobert Mustacchi /*
906716431bSRobert Mustacchi  * Useful macros and typedefs
916716431bSRobert Mustacchi  */
926716431bSRobert Mustacchi #define	ROUNDUP(x, a)		(((x) + (a) - 1) & ~((a) - 1))
936716431bSRobert Mustacchi #define	DEFAULT_PIPE(dp)	((dp)->reg_data->dev_default_ph)
946716431bSRobert Mustacchi #define	VTAG_SIZE	4
956716431bSRobert Mustacchi #define	BOOLEAN(x)	((x) != 0)
966716431bSRobert Mustacchi /*
976716431bSRobert Mustacchi  * configuration parameters
986716431bSRobert Mustacchi  */
996716431bSRobert Mustacchi #define	USBDRV_MAJOR_VER	2
1006716431bSRobert Mustacchi #define	USBDRV_MINOR_VER	0
1016716431bSRobert Mustacchi 
1026716431bSRobert Mustacchi #define	ETHERHEADERL	(sizeof (struct ether_header))
1036716431bSRobert Mustacchi #define	MAXPKTLEN(dp)	((dp)->mtu + ETHERHEADERL)
1046716431bSRobert Mustacchi #define	MAXPKTBUF(dp)	((dp)->mtu + ETHERHEADERL + ETHERFCSL)
1056716431bSRobert Mustacchi 
1066716431bSRobert Mustacchi #define	WATCH_INTERVAL_FAST	drv_usectohz(100*1000)
1076716431bSRobert Mustacchi 
1086716431bSRobert Mustacchi #define	STOP_GRACEFUL	B_TRUE
1096716431bSRobert Mustacchi 
1106716431bSRobert Mustacchi /*
1116716431bSRobert Mustacchi  * Private functions
1126716431bSRobert Mustacchi  */
1136716431bSRobert Mustacchi static int usbgem_open_pipes(struct usbgem_dev *dp);
1146716431bSRobert Mustacchi static int usbgem_close_pipes(struct usbgem_dev *dp);
1156716431bSRobert Mustacchi static void usbgem_intr_cb(usb_pipe_handle_t, usb_intr_req_t *);
1166716431bSRobert Mustacchi static void usbgem_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
1176716431bSRobert Mustacchi static void usbgem_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
1186716431bSRobert Mustacchi 
1196716431bSRobert Mustacchi static int usbgem_mii_start(struct usbgem_dev *);
1206716431bSRobert Mustacchi static void usbgem_mii_stop(struct usbgem_dev *);
1216716431bSRobert Mustacchi 
1226716431bSRobert Mustacchi /* local buffer management */
1236716431bSRobert Mustacchi static int usbgem_init_rx_buf(struct usbgem_dev *);
1246716431bSRobert Mustacchi 
1256716431bSRobert Mustacchi /* internal mac interfaces */
1266716431bSRobert Mustacchi static void usbgem_tx_timeout(struct usbgem_dev *);
1276716431bSRobert Mustacchi static void usbgem_mii_link_watcher(struct usbgem_dev *);
1286716431bSRobert Mustacchi static int usbgem_mac_init(struct usbgem_dev *);
1296716431bSRobert Mustacchi static int usbgem_mac_start(struct usbgem_dev *);
1306716431bSRobert Mustacchi static int usbgem_mac_stop(struct usbgem_dev *, int, boolean_t);
1316716431bSRobert Mustacchi static void usbgem_mac_ioctl(struct usbgem_dev *, queue_t *, mblk_t *);
1326716431bSRobert Mustacchi 
1336716431bSRobert Mustacchi int usbgem_speed_value[] = {10, 100, 1000};
1346716431bSRobert Mustacchi 
1356716431bSRobert Mustacchi static int usbgem_ctrl_retry = 5;
1366716431bSRobert Mustacchi 
1376716431bSRobert Mustacchi /* usb event support */
1386716431bSRobert Mustacchi static int usbgem_disconnect_cb(dev_info_t *dip);
1396716431bSRobert Mustacchi static int usbgem_reconnect_cb(dev_info_t *dip);
1406716431bSRobert Mustacchi int usbgem_suspend(dev_info_t *dip);
1416716431bSRobert Mustacchi int usbgem_resume(dev_info_t *dip);
1426716431bSRobert Mustacchi 
1436716431bSRobert Mustacchi static uint8_t usbgem_bcastaddr[] = {
1446716431bSRobert Mustacchi 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1456716431bSRobert Mustacchi };
1466716431bSRobert Mustacchi 
1476716431bSRobert Mustacchi extern struct mod_ops mod_miscops;
1486716431bSRobert Mustacchi 
1496716431bSRobert Mustacchi static struct modlmisc modlmisc = {
1506716431bSRobert Mustacchi 	&mod_miscops,
1516716431bSRobert Mustacchi 	"usbgem v" VERSION,
1526716431bSRobert Mustacchi };
1536716431bSRobert Mustacchi 
1546716431bSRobert Mustacchi static struct modlinkage modlinkage = {
1556716431bSRobert Mustacchi 	MODREV_1, &modlmisc, NULL
1566716431bSRobert Mustacchi };
1576716431bSRobert Mustacchi 
1586716431bSRobert Mustacchi /*
1596716431bSRobert Mustacchi  * _init : done
1606716431bSRobert Mustacchi  */
1616716431bSRobert Mustacchi int
_init(void)1626716431bSRobert Mustacchi _init(void)
1636716431bSRobert Mustacchi {
164485f90e9SToomas Soome 	int	status;
1656716431bSRobert Mustacchi 
1666716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!usbgem: _init: called"));
1676716431bSRobert Mustacchi 	status = mod_install(&modlinkage);
1686716431bSRobert Mustacchi 
1696716431bSRobert Mustacchi 	return (status);
1706716431bSRobert Mustacchi }
1716716431bSRobert Mustacchi 
1726716431bSRobert Mustacchi /*
1736716431bSRobert Mustacchi  * _fini : done
1746716431bSRobert Mustacchi  */
1756716431bSRobert Mustacchi int
_fini(void)1766716431bSRobert Mustacchi _fini(void)
1776716431bSRobert Mustacchi {
1786716431bSRobert Mustacchi 	int	status;
1796716431bSRobert Mustacchi 
1806716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!usbgem: _fini: called"));
1816716431bSRobert Mustacchi 	status = mod_remove(&modlinkage);
1826716431bSRobert Mustacchi 	return (status);
1836716431bSRobert Mustacchi }
1846716431bSRobert Mustacchi 
1856716431bSRobert Mustacchi int
_info(struct modinfo * modinfop)1866716431bSRobert Mustacchi _info(struct modinfo *modinfop)
1876716431bSRobert Mustacchi {
1886716431bSRobert Mustacchi 	return (mod_info(&modlinkage, modinfop));
1896716431bSRobert Mustacchi }
1906716431bSRobert Mustacchi 
1916716431bSRobert Mustacchi /* ============================================================== */
1926716431bSRobert Mustacchi /*
1936716431bSRobert Mustacchi  * Ether CRC calculation utilities
1946716431bSRobert Mustacchi  */
1956716431bSRobert Mustacchi /* ============================================================== */
1966716431bSRobert Mustacchi /*
1976716431bSRobert Mustacchi  * Ether CRC calculation according to 21143 data sheet
1986716431bSRobert Mustacchi  */
1996716431bSRobert Mustacchi #define	CRC32_POLY_LE	0xedb88320
2006716431bSRobert Mustacchi uint32_t
usbgem_ether_crc_le(const uint8_t * addr)2016716431bSRobert Mustacchi usbgem_ether_crc_le(const uint8_t *addr)
2026716431bSRobert Mustacchi {
2036716431bSRobert Mustacchi 	int		idx;
2046716431bSRobert Mustacchi 	int		bit;
2056716431bSRobert Mustacchi 	uint_t		data;
2066716431bSRobert Mustacchi 	uint32_t	crc = 0xffffffff;
2076716431bSRobert Mustacchi 
2086716431bSRobert Mustacchi 	crc = 0xffffffff;
2096716431bSRobert Mustacchi 	for (idx = 0; idx < ETHERADDRL; idx++) {
2106716431bSRobert Mustacchi 		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
2116716431bSRobert Mustacchi 			crc = (crc >> 1) ^
2126716431bSRobert Mustacchi 			    (((crc ^ data) & 1) ? CRC32_POLY_LE : 0);
2136716431bSRobert Mustacchi 		}
2146716431bSRobert Mustacchi 	}
2156716431bSRobert Mustacchi 	return	(crc);
2166716431bSRobert Mustacchi }
2176716431bSRobert Mustacchi 
2186716431bSRobert Mustacchi #define	CRC32_POLY_BE	0x04c11db7
2196716431bSRobert Mustacchi uint32_t
usbgem_ether_crc_be(const uint8_t * addr)2206716431bSRobert Mustacchi usbgem_ether_crc_be(const uint8_t *addr)
2216716431bSRobert Mustacchi {
2226716431bSRobert Mustacchi 	int		idx;
2236716431bSRobert Mustacchi 	int		bit;
2246716431bSRobert Mustacchi 	uint_t		data;
2256716431bSRobert Mustacchi 	uint32_t	crc;
2266716431bSRobert Mustacchi 
2276716431bSRobert Mustacchi 	crc = 0xffffffff;
2286716431bSRobert Mustacchi 	for (idx = 0; idx < ETHERADDRL; idx++) {
2296716431bSRobert Mustacchi 		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
2306716431bSRobert Mustacchi 			crc = (crc << 1) ^
2316716431bSRobert Mustacchi 			    ((((crc >> 31) ^ data) & 1) ? CRC32_POLY_BE : 0);
2326716431bSRobert Mustacchi 		}
2336716431bSRobert Mustacchi 	}
2346716431bSRobert Mustacchi 	return (crc);
2356716431bSRobert Mustacchi }
2366716431bSRobert Mustacchi 
2376716431bSRobert Mustacchi int
usbgem_prop_get_int(struct usbgem_dev * dp,char * prop_template,int def_val)2386716431bSRobert Mustacchi usbgem_prop_get_int(struct usbgem_dev *dp, char *prop_template, int def_val)
2396716431bSRobert Mustacchi {
2406716431bSRobert Mustacchi 	char	propname[32];
2416716431bSRobert Mustacchi 
2426716431bSRobert Mustacchi 	(void) sprintf(propname, prop_template, dp->name);
2436716431bSRobert Mustacchi 
2446716431bSRobert Mustacchi 	return (ddi_prop_get_int(DDI_DEV_T_ANY, dp->dip,
2456716431bSRobert Mustacchi 	    DDI_PROP_DONTPASS, propname, def_val));
2466716431bSRobert Mustacchi }
2476716431bSRobert Mustacchi 
2486716431bSRobert Mustacchi static int
usbgem_population(uint32_t x)2496716431bSRobert Mustacchi usbgem_population(uint32_t x)
2506716431bSRobert Mustacchi {
2516716431bSRobert Mustacchi 	int	i;
2526716431bSRobert Mustacchi 	int	cnt;
2536716431bSRobert Mustacchi 
2546716431bSRobert Mustacchi 	cnt = 0;
2556716431bSRobert Mustacchi 	for (i = 0; i < 32; i++) {
2566716431bSRobert Mustacchi 		if (x & (1 << i)) {
2576716431bSRobert Mustacchi 			cnt++;
2586716431bSRobert Mustacchi 		}
2596716431bSRobert Mustacchi 	}
2606716431bSRobert Mustacchi 	return (cnt);
2616716431bSRobert Mustacchi }
2626716431bSRobert Mustacchi 
2636716431bSRobert Mustacchi static clock_t
usbgem_timestamp_nz()2646716431bSRobert Mustacchi usbgem_timestamp_nz()
2656716431bSRobert Mustacchi {
2666716431bSRobert Mustacchi 	clock_t	now;
2676716431bSRobert Mustacchi 	now = ddi_get_lbolt();
2686716431bSRobert Mustacchi 	return (now ? now : (clock_t)1);
2696716431bSRobert Mustacchi }
2706716431bSRobert Mustacchi 
2716716431bSRobert Mustacchi /* ============================================================== */
2726716431bSRobert Mustacchi /*
2736716431bSRobert Mustacchi  * hardware operations
2746716431bSRobert Mustacchi  */
2756716431bSRobert Mustacchi /* ============================================================== */
2766716431bSRobert Mustacchi static int
usbgem_hal_reset_chip(struct usbgem_dev * dp)2776716431bSRobert Mustacchi usbgem_hal_reset_chip(struct usbgem_dev *dp)
2786716431bSRobert Mustacchi {
2796716431bSRobert Mustacchi 	int	err;
2806716431bSRobert Mustacchi 
2816716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
2826716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_reset_chip)(dp);
2836716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
2846716431bSRobert Mustacchi 	return (err);
2856716431bSRobert Mustacchi }
2866716431bSRobert Mustacchi 
2876716431bSRobert Mustacchi static int
usbgem_hal_init_chip(struct usbgem_dev * dp)2886716431bSRobert Mustacchi usbgem_hal_init_chip(struct usbgem_dev *dp)
2896716431bSRobert Mustacchi {
2906716431bSRobert Mustacchi 	int	err;
2916716431bSRobert Mustacchi 
2926716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
2936716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_init_chip)(dp);
2946716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
2956716431bSRobert Mustacchi 	return (err);
2966716431bSRobert Mustacchi }
2976716431bSRobert Mustacchi 
2986716431bSRobert Mustacchi static int
usbgem_hal_attach_chip(struct usbgem_dev * dp)2996716431bSRobert Mustacchi usbgem_hal_attach_chip(struct usbgem_dev *dp)
3006716431bSRobert Mustacchi {
3016716431bSRobert Mustacchi 	int	err;
3026716431bSRobert Mustacchi 
3036716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3046716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_attach_chip)(dp);
3056716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3066716431bSRobert Mustacchi 	return (err);
3076716431bSRobert Mustacchi }
3086716431bSRobert Mustacchi 
3096716431bSRobert Mustacchi static int
usbgem_hal_set_rx_filter(struct usbgem_dev * dp)3106716431bSRobert Mustacchi usbgem_hal_set_rx_filter(struct usbgem_dev *dp)
3116716431bSRobert Mustacchi {
3126716431bSRobert Mustacchi 	int	err;
3136716431bSRobert Mustacchi 
3146716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3156716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_set_rx_filter)(dp);
3166716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3176716431bSRobert Mustacchi 	return (err);
3186716431bSRobert Mustacchi }
3196716431bSRobert Mustacchi 
3206716431bSRobert Mustacchi static int
usbgem_hal_set_media(struct usbgem_dev * dp)3216716431bSRobert Mustacchi usbgem_hal_set_media(struct usbgem_dev *dp)
3226716431bSRobert Mustacchi {
3236716431bSRobert Mustacchi 	int	err;
3246716431bSRobert Mustacchi 
3256716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3266716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_set_media)(dp);
3276716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3286716431bSRobert Mustacchi 	return (err);
3296716431bSRobert Mustacchi }
3306716431bSRobert Mustacchi 
3316716431bSRobert Mustacchi static int
usbgem_hal_start_chip(struct usbgem_dev * dp)3326716431bSRobert Mustacchi usbgem_hal_start_chip(struct usbgem_dev *dp)
3336716431bSRobert Mustacchi {
3346716431bSRobert Mustacchi 	int	err;
3356716431bSRobert Mustacchi 
3366716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3376716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_start_chip)(dp);
3386716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3396716431bSRobert Mustacchi 	return (err);
3406716431bSRobert Mustacchi }
3416716431bSRobert Mustacchi 
3426716431bSRobert Mustacchi static int
usbgem_hal_stop_chip(struct usbgem_dev * dp)3436716431bSRobert Mustacchi usbgem_hal_stop_chip(struct usbgem_dev *dp)
3446716431bSRobert Mustacchi {
3456716431bSRobert Mustacchi 	int	err;
3466716431bSRobert Mustacchi 
3476716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3486716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_stop_chip)(dp);
3496716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3506716431bSRobert Mustacchi 	return (err);
3516716431bSRobert Mustacchi }
3526716431bSRobert Mustacchi 
3536716431bSRobert Mustacchi static int
usbgem_hal_get_stats(struct usbgem_dev * dp)3546716431bSRobert Mustacchi usbgem_hal_get_stats(struct usbgem_dev *dp)
3556716431bSRobert Mustacchi {
3566716431bSRobert Mustacchi 	int	err;
3576716431bSRobert Mustacchi 
3586716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
3596716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_get_stats)(dp);
3606716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
3616716431bSRobert Mustacchi 	return (err);
3626716431bSRobert Mustacchi }
3636716431bSRobert Mustacchi 
3646716431bSRobert Mustacchi 
3656716431bSRobert Mustacchi /* ============================================================== */
3666716431bSRobert Mustacchi /*
3676716431bSRobert Mustacchi  * USB pipe management
3686716431bSRobert Mustacchi  */
3696716431bSRobert Mustacchi /* ============================================================== */
3706716431bSRobert Mustacchi static boolean_t
usbgem_rx_start_unit(struct usbgem_dev * dp,usb_bulk_req_t * req)3716716431bSRobert Mustacchi usbgem_rx_start_unit(struct usbgem_dev *dp, usb_bulk_req_t *req)
3726716431bSRobert Mustacchi {
3736716431bSRobert Mustacchi 	mblk_t	*mp;
3746716431bSRobert Mustacchi 	int	err;
3756716431bSRobert Mustacchi 	usb_flags_t	flags;
3766716431bSRobert Mustacchi 
3776716431bSRobert Mustacchi 	ASSERT(req);
3786716431bSRobert Mustacchi 
3796716431bSRobert Mustacchi 	mp = allocb(dp->rx_buf_len, BPRI_MED);
3806716431bSRobert Mustacchi 	if (mp == NULL) {
3816716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: failed to allocate mblk",
3826716431bSRobert Mustacchi 		    dp->name, __func__);
3836716431bSRobert Mustacchi 		goto err;
3846716431bSRobert Mustacchi 	}
3856716431bSRobert Mustacchi 
3866716431bSRobert Mustacchi 	req->bulk_len = dp->rx_buf_len;
3876716431bSRobert Mustacchi 	req->bulk_data = mp;
3886716431bSRobert Mustacchi 	req->bulk_client_private = (usb_opaque_t)dp;
3896716431bSRobert Mustacchi 	req->bulk_timeout = 0;
3906716431bSRobert Mustacchi 	req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK;
3916716431bSRobert Mustacchi 	req->bulk_cb = usbgem_bulkin_cb;
3926716431bSRobert Mustacchi 	req->bulk_exc_cb = usbgem_bulkin_cb;
3936716431bSRobert Mustacchi 	req->bulk_completion_reason = 0;
3946716431bSRobert Mustacchi 	req->bulk_cb_flags = 0;
3956716431bSRobert Mustacchi 
3966716431bSRobert Mustacchi 	flags = 0;
3976716431bSRobert Mustacchi 	err = usb_pipe_bulk_xfer(dp->bulkin_pipe, req, flags);
3986716431bSRobert Mustacchi 
3996716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
4006716431bSRobert Mustacchi 		cmn_err(CE_WARN, "%s: failed to bulk_xfer for rx, err:%d",
4016716431bSRobert Mustacchi 		    dp->name, err);
4026716431bSRobert Mustacchi 
4036716431bSRobert Mustacchi 		/* free req and mp */
4046716431bSRobert Mustacchi 		usb_free_bulk_req(req);
4056716431bSRobert Mustacchi 		goto err;
4066716431bSRobert Mustacchi 	}
4076716431bSRobert Mustacchi 	return (B_TRUE);
4086716431bSRobert Mustacchi err:
4096716431bSRobert Mustacchi 	return (B_FALSE);
4106716431bSRobert Mustacchi }
4116716431bSRobert Mustacchi 
4126716431bSRobert Mustacchi /* ============================================================== */
4136716431bSRobert Mustacchi /*
4146716431bSRobert Mustacchi  * Rx/Tx buffer management
4156716431bSRobert Mustacchi  */
4166716431bSRobert Mustacchi /* ============================================================== */
4176716431bSRobert Mustacchi static int
usbgem_init_rx_buf(struct usbgem_dev * dp)4186716431bSRobert Mustacchi usbgem_init_rx_buf(struct usbgem_dev *dp)
4196716431bSRobert Mustacchi {
4206716431bSRobert Mustacchi 	int	i;
4216716431bSRobert Mustacchi 	usb_bulk_req_t	*req;
4226716431bSRobert Mustacchi 
4236716431bSRobert Mustacchi 	ASSERT(dp->mac_state == MAC_STATE_ONLINE);
4246716431bSRobert Mustacchi 
4256716431bSRobert Mustacchi 	for (i = 0; i < dp->ugc.usbgc_rx_list_max; i++) {
4266716431bSRobert Mustacchi 		req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP);
4276716431bSRobert Mustacchi 		if (req == NULL) {
4286716431bSRobert Mustacchi 			cmn_err(CE_WARN,
4296716431bSRobert Mustacchi 			    "!%s: %s: failed to allocate bulkreq for rx",
4306716431bSRobert Mustacchi 			    dp->name, __func__);
4316716431bSRobert Mustacchi 			return (USB_FAILURE);
4326716431bSRobert Mustacchi 		}
4336716431bSRobert Mustacchi 		if (!usbgem_rx_start_unit(dp, req)) {
4346716431bSRobert Mustacchi 			return (USB_FAILURE);
4356716431bSRobert Mustacchi 		}
4366716431bSRobert Mustacchi 		mutex_enter(&dp->rxlock);
4376716431bSRobert Mustacchi 		dp->rx_busy_cnt++;
4386716431bSRobert Mustacchi 		mutex_exit(&dp->rxlock);
4396716431bSRobert Mustacchi 	}
4406716431bSRobert Mustacchi 	return (USB_SUCCESS);
4416716431bSRobert Mustacchi }
4426716431bSRobert Mustacchi 
4436716431bSRobert Mustacchi /* ============================================================== */
4446716431bSRobert Mustacchi /*
4456716431bSRobert Mustacchi  * memory resource management
4466716431bSRobert Mustacchi  */
4476716431bSRobert Mustacchi /* ============================================================== */
4486716431bSRobert Mustacchi static int
usbgem_free_memory(struct usbgem_dev * dp)4496716431bSRobert Mustacchi usbgem_free_memory(struct usbgem_dev *dp)
4506716431bSRobert Mustacchi {
4516716431bSRobert Mustacchi 	usb_bulk_req_t	*req;
4526716431bSRobert Mustacchi 
4536716431bSRobert Mustacchi 	/* free all tx requst structure */
4546716431bSRobert Mustacchi 	while ((req = dp->tx_free_list) != NULL) {
4556716431bSRobert Mustacchi 		dp->tx_free_list =
4566716431bSRobert Mustacchi 		    (usb_bulk_req_t *)req->bulk_client_private;
4576716431bSRobert Mustacchi 		req->bulk_data = NULL;
4586716431bSRobert Mustacchi 		usb_free_bulk_req(req);
4596716431bSRobert Mustacchi 	}
4606716431bSRobert Mustacchi 	return (USB_SUCCESS);
4616716431bSRobert Mustacchi }
4626716431bSRobert Mustacchi 
4636716431bSRobert Mustacchi static int
usbgem_alloc_memory(struct usbgem_dev * dp)4646716431bSRobert Mustacchi usbgem_alloc_memory(struct usbgem_dev *dp)
4656716431bSRobert Mustacchi {
4666716431bSRobert Mustacchi 	int	i;
4676716431bSRobert Mustacchi 	usb_bulk_req_t	*req;
4686716431bSRobert Mustacchi 
4696716431bSRobert Mustacchi 	/* allocate tx requests */
4706716431bSRobert Mustacchi 	dp->tx_free_list = NULL;
4716716431bSRobert Mustacchi 	for (i = 0; i < dp->ugc.usbgc_tx_list_max; i++) {
4726716431bSRobert Mustacchi 		req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP);
4736716431bSRobert Mustacchi 		if (req == NULL) {
4746716431bSRobert Mustacchi 			cmn_err(CE_WARN,
4756716431bSRobert Mustacchi 			    "%s:%s failed to allocate tx requests",
4766716431bSRobert Mustacchi 			    dp->name, __func__);
4776716431bSRobert Mustacchi 
4786716431bSRobert Mustacchi 			/* free partially allocated tx requests */
4796716431bSRobert Mustacchi 			(void) usbgem_free_memory(dp);
4806716431bSRobert Mustacchi 			return (USB_FAILURE);
4816716431bSRobert Mustacchi 		}
4826716431bSRobert Mustacchi 
4836716431bSRobert Mustacchi 		/* add the new one allocated into tx free list */
4846716431bSRobert Mustacchi 		req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
4856716431bSRobert Mustacchi 		dp->tx_free_list = req;
4866716431bSRobert Mustacchi 	}
4876716431bSRobert Mustacchi 
4886716431bSRobert Mustacchi 	return (USB_SUCCESS);
4896716431bSRobert Mustacchi }
4906716431bSRobert Mustacchi 
4916716431bSRobert Mustacchi /* ========================================================== */
4926716431bSRobert Mustacchi /*
4936716431bSRobert Mustacchi  * Start transmission.
4946716431bSRobert Mustacchi  * Return zero on success,
4956716431bSRobert Mustacchi  */
4966716431bSRobert Mustacchi /* ========================================================== */
4976716431bSRobert Mustacchi 
4986716431bSRobert Mustacchi #ifdef TXTIMEOUT_TEST
4996716431bSRobert Mustacchi static int usbgem_send_cnt = 0;
5006716431bSRobert Mustacchi #endif
5016716431bSRobert Mustacchi 
5026716431bSRobert Mustacchi /*
5036716431bSRobert Mustacchi  * usbgem_send is used only to send data packet into ethernet line.
5046716431bSRobert Mustacchi  */
5056716431bSRobert Mustacchi static mblk_t *
usbgem_send_common(struct usbgem_dev * dp,mblk_t * mp,uint32_t flags)5066716431bSRobert Mustacchi usbgem_send_common(struct usbgem_dev *dp, mblk_t *mp, uint32_t flags)
5076716431bSRobert Mustacchi {
5086716431bSRobert Mustacchi 	int		err;
5096716431bSRobert Mustacchi 	mblk_t		*new;
5106716431bSRobert Mustacchi 	usb_bulk_req_t	*req;
5116716431bSRobert Mustacchi 	int		mcast;
5126716431bSRobert Mustacchi 	int		bcast;
5136716431bSRobert Mustacchi 	int		len;
5146716431bSRobert Mustacchi 	boolean_t	intr;
5156716431bSRobert Mustacchi 	usb_flags_t	usb_flags = 0;
5166716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
5176716431bSRobert Mustacchi 	usb_pipe_state_t	p_state;
5186716431bSRobert Mustacchi #endif
5196716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
5206716431bSRobert Mustacchi 
5216716431bSRobert Mustacchi 	intr = (flags & 1) != 0;
5226716431bSRobert Mustacchi 	len = msgdsize(mp);
5236716431bSRobert Mustacchi 	bcast = 0;
5246716431bSRobert Mustacchi 	mcast = 0;
5256716431bSRobert Mustacchi 	if (mp->b_rptr[0] & 1) {
5266716431bSRobert Mustacchi 		if (bcmp(mp->b_rptr, &usbgem_bcastaddr, ETHERADDRL) == 0) {
5276716431bSRobert Mustacchi 			bcast = 1;
5286716431bSRobert Mustacchi 		} else {
5296716431bSRobert Mustacchi 			mcast = 1;
5306716431bSRobert Mustacchi 		}
5316716431bSRobert Mustacchi 	}
5326716431bSRobert Mustacchi 	new = (*dp->ugc.usbgc_tx_make_packet)(dp, mp);
5336716431bSRobert Mustacchi 	if (new == NULL) {
5346716431bSRobert Mustacchi 		/*
5356716431bSRobert Mustacchi 		 * no memory resource. we don't stop downstream,
5366716431bSRobert Mustacchi 		 * we just discard the packet.
5376716431bSRobert Mustacchi 		 */
5386716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT, "!%s: %s: no memory",
5396716431bSRobert Mustacchi 		    dp->name, __func__));
5406716431bSRobert Mustacchi 		freemsg(mp);
5416716431bSRobert Mustacchi 
5426716431bSRobert Mustacchi 		mutex_enter(&dp->txlock);
5436716431bSRobert Mustacchi 		dp->stats.noxmtbuf++;
5446716431bSRobert Mustacchi 		dp->stats.errxmt++;
5456716431bSRobert Mustacchi 		mutex_exit(&dp->txlock);
5466716431bSRobert Mustacchi 
5476716431bSRobert Mustacchi 		return (NULL);
5486716431bSRobert Mustacchi 	}
5496716431bSRobert Mustacchi 
5506716431bSRobert Mustacchi 	ASSERT(new->b_cont == NULL);
5516716431bSRobert Mustacchi 
5526716431bSRobert Mustacchi 	mutex_enter(&dp->txlock);
5536716431bSRobert Mustacchi 	if (dp->tx_free_list == NULL) {
5546716431bSRobert Mustacchi 		/*
5556716431bSRobert Mustacchi 		 * no tx free slot
5566716431bSRobert Mustacchi 		 */
5576716431bSRobert Mustacchi 		ASSERT(dp->tx_busy_cnt == dp->ugc.usbgc_tx_list_max);
5586716431bSRobert Mustacchi 		mutex_exit(&dp->txlock);
5596716431bSRobert Mustacchi 
5606716431bSRobert Mustacchi 		DPRINTF(4, (CE_CONT, "!%s: %s: no free slot",
5616716431bSRobert Mustacchi 		    dp->name, __func__));
5626716431bSRobert Mustacchi 		if (new && new != mp) {
5636716431bSRobert Mustacchi 			/* free reallocated message */
5646716431bSRobert Mustacchi 			freemsg(new);
5656716431bSRobert Mustacchi 		}
5666716431bSRobert Mustacchi 		return (mp);
5676716431bSRobert Mustacchi 	}
5686716431bSRobert Mustacchi 	req = dp->tx_free_list;
5696716431bSRobert Mustacchi 	dp->tx_free_list = (usb_bulk_req_t *)req->bulk_client_private;
5706716431bSRobert Mustacchi 	dp->tx_busy_cnt++;
5716716431bSRobert Mustacchi 
5726716431bSRobert Mustacchi 	if (dp->tx_free_list == NULL) {
5736716431bSRobert Mustacchi 		intr = B_TRUE;
5746716431bSRobert Mustacchi 	}
5756716431bSRobert Mustacchi 	if (intr) {
5766716431bSRobert Mustacchi 		dp->tx_intr_pended++;
5776716431bSRobert Mustacchi 	}
5786716431bSRobert Mustacchi 	DB_TCI(new) = intr;
5796716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
5806716431bSRobert Mustacchi 	new->b_datap->db_cksum32 = dp->tx_seq_num;
5816716431bSRobert Mustacchi 	dp->tx_seq_num++;
5826716431bSRobert Mustacchi #endif
5836716431bSRobert Mustacchi 	dp->stats.obytes += len;
5846716431bSRobert Mustacchi 	dp->stats.opackets++;
5856716431bSRobert Mustacchi 	if (bcast | mcast) {
5866716431bSRobert Mustacchi 		dp->stats.obcast += bcast;
5876716431bSRobert Mustacchi 		dp->stats.omcast += mcast;
5886716431bSRobert Mustacchi 	}
5896716431bSRobert Mustacchi 	mutex_exit(&dp->txlock);
5906716431bSRobert Mustacchi 
5916716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!%s: %s: sending", dp->name, __func__));
5926716431bSRobert Mustacchi 
5936716431bSRobert Mustacchi 	req->bulk_len = (long)new->b_wptr - (long)new->b_rptr;
5946716431bSRobert Mustacchi 	req->bulk_data = new;
5956716431bSRobert Mustacchi 	req->bulk_client_private = (usb_opaque_t)dp;
5966716431bSRobert Mustacchi 	req->bulk_timeout = dp->bulkout_timeout;	/* in second */
5976716431bSRobert Mustacchi 	req->bulk_attributes = 0;
5986716431bSRobert Mustacchi 	req->bulk_cb = usbgem_bulkout_cb;
5996716431bSRobert Mustacchi 	req->bulk_exc_cb = usbgem_bulkout_cb;
6006716431bSRobert Mustacchi 	req->bulk_completion_reason = 0;
6016716431bSRobert Mustacchi 	req->bulk_cb_flags = 0;
6026716431bSRobert Mustacchi 
6036716431bSRobert Mustacchi 	if (intr) {
6046716431bSRobert Mustacchi 		usb_flags = USB_FLAGS_SLEEP;
6056716431bSRobert Mustacchi 	}
6066716431bSRobert Mustacchi 	if ((err = usb_pipe_bulk_xfer(dp->bulkout_pipe, req, usb_flags))
6076716431bSRobert Mustacchi 	    != USB_SUCCESS) {
6086716431bSRobert Mustacchi 
6096716431bSRobert Mustacchi 		/* failed to transfer the packet, discard it. */
6106716431bSRobert Mustacchi 		freemsg(new);
6116716431bSRobert Mustacchi 		req->bulk_data = NULL;
6126716431bSRobert Mustacchi 
6136716431bSRobert Mustacchi 		/* recycle the request block */
6146716431bSRobert Mustacchi 		mutex_enter(&dp->txlock);
6156716431bSRobert Mustacchi 		dp->tx_busy_cnt--;
6166716431bSRobert Mustacchi 		req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
6176716431bSRobert Mustacchi 		dp->tx_free_list = req;
6186716431bSRobert Mustacchi 		mutex_exit(&dp->txlock);
6196716431bSRobert Mustacchi 
6206716431bSRobert Mustacchi 		cmn_err(CE_NOTE,
6216716431bSRobert Mustacchi 		    "%s: %s: usb_pipe_bulk_xfer: failed: err:%d",
6226716431bSRobert Mustacchi 		    dp->name, __func__, err);
6236716431bSRobert Mustacchi 
6246716431bSRobert Mustacchi 		/* we use another flag to indicate error state. */
6256716431bSRobert Mustacchi 		if (dp->fatal_error == (clock_t)0) {
6266716431bSRobert Mustacchi 			dp->fatal_error = usbgem_timestamp_nz();
6276716431bSRobert Mustacchi 		}
6286716431bSRobert Mustacchi 	} else {
6296716431bSRobert Mustacchi 		/* record the start time */
6306716431bSRobert Mustacchi 		dp->tx_start_time = ddi_get_lbolt();
6316716431bSRobert Mustacchi 	}
6326716431bSRobert Mustacchi 
6336716431bSRobert Mustacchi 	if (err == USB_SUCCESS && (usb_flags & USB_FLAGS_SLEEP)) {
6346716431bSRobert Mustacchi 		usbgem_bulkout_cb(dp->bulkout_pipe, req);
6356716431bSRobert Mustacchi 	}
6366716431bSRobert Mustacchi 
6376716431bSRobert Mustacchi 	if (new != mp) {
6386716431bSRobert Mustacchi 		freemsg(mp);
6396716431bSRobert Mustacchi 	}
6406716431bSRobert Mustacchi 	return (NULL);
6416716431bSRobert Mustacchi }
6426716431bSRobert Mustacchi 
6436716431bSRobert Mustacchi int
usbgem_restart_nic(struct usbgem_dev * dp)6446716431bSRobert Mustacchi usbgem_restart_nic(struct usbgem_dev *dp)
6456716431bSRobert Mustacchi {
6466716431bSRobert Mustacchi 	int	ret;
6476716431bSRobert Mustacchi 	int	flags = 0;
6486716431bSRobert Mustacchi 
6496716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
6506716431bSRobert Mustacchi 
6516716431bSRobert Mustacchi 	ASSERT(dp->mac_state != MAC_STATE_DISCONNECTED);
6526716431bSRobert Mustacchi 
6536716431bSRobert Mustacchi 	/*
6546716431bSRobert Mustacchi 	 * ensure to stop the nic
6556716431bSRobert Mustacchi 	 */
6566716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_ONLINE) {
6576716431bSRobert Mustacchi 		(void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL);
6586716431bSRobert Mustacchi 	}
6596716431bSRobert Mustacchi 
6606716431bSRobert Mustacchi 	/* now the nic become quiescent, reset the chip */
6616716431bSRobert Mustacchi 	if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) {
6626716431bSRobert Mustacchi 		cmn_err(CE_WARN, "%s: %s: failed to reset chip",
6636716431bSRobert Mustacchi 		    dp->name, __func__);
6646716431bSRobert Mustacchi 		goto err;
6656716431bSRobert Mustacchi 	}
6666716431bSRobert Mustacchi 
6676716431bSRobert Mustacchi 	/*
6686716431bSRobert Mustacchi 	 * restore the nic state step by step
6696716431bSRobert Mustacchi 	 */
6706716431bSRobert Mustacchi 	if (dp->nic_state < NIC_STATE_INITIALIZED) {
6716716431bSRobert Mustacchi 		goto done;
6726716431bSRobert Mustacchi 	}
6736716431bSRobert Mustacchi 
6746716431bSRobert Mustacchi 	if (usbgem_mac_init(dp) != USB_SUCCESS) {
6756716431bSRobert Mustacchi 		cmn_err(CE_WARN, "%s: %s: failed to initialize chip",
6766716431bSRobert Mustacchi 		    dp->name, __func__);
6776716431bSRobert Mustacchi 		goto err;
6786716431bSRobert Mustacchi 	}
6796716431bSRobert Mustacchi 
6806716431bSRobert Mustacchi 	/* setup mac address and enable rx filter */
6816716431bSRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
6826716431bSRobert Mustacchi 	dp->rxmode |= RXMODE_ENABLE;
6836716431bSRobert Mustacchi 	ret = usbgem_hal_set_rx_filter(dp);
6846716431bSRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
6856716431bSRobert Mustacchi 	if (ret != USB_SUCCESS) {
6866716431bSRobert Mustacchi 		goto err;
6876716431bSRobert Mustacchi 	}
6886716431bSRobert Mustacchi 
6896716431bSRobert Mustacchi 	/*
6906716431bSRobert Mustacchi 	 * update the link state asynchronously
6916716431bSRobert Mustacchi 	 */
6926716431bSRobert Mustacchi 	cv_signal(&dp->link_watcher_wait_cv);
6936716431bSRobert Mustacchi 
6946716431bSRobert Mustacchi 	/*
6956716431bSRobert Mustacchi 	 * XXX - a panic happened because of linkdown.
6966716431bSRobert Mustacchi 	 * We must check mii_state here, because the link can be down just
6976716431bSRobert Mustacchi 	 * before the restart event happen. If the link is down now,
6986716431bSRobert Mustacchi 	 * gem_mac_start() will be called from gem_mii_link_check() when
6996716431bSRobert Mustacchi 	 * the link become up later.
7006716431bSRobert Mustacchi 	 */
7016716431bSRobert Mustacchi 	if (dp->mii_state == MII_STATE_LINKUP) {
7026716431bSRobert Mustacchi 		if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
7036716431bSRobert Mustacchi 			goto err;
7046716431bSRobert Mustacchi 		}
7056716431bSRobert Mustacchi 		if (dp->nic_state < NIC_STATE_ONLINE) {
7066716431bSRobert Mustacchi 			goto done;
7076716431bSRobert Mustacchi 		}
7086716431bSRobert Mustacchi 
7096716431bSRobert Mustacchi 		(void) usbgem_mac_start(dp);
7106716431bSRobert Mustacchi 
7116716431bSRobert Mustacchi 	}
7126716431bSRobert Mustacchi done:
7136716431bSRobert Mustacchi 	return (USB_SUCCESS);
7146716431bSRobert Mustacchi err:
7156716431bSRobert Mustacchi 	return (USB_FAILURE);
7166716431bSRobert Mustacchi }
7176716431bSRobert Mustacchi 
7186716431bSRobert Mustacchi static void
usbgem_tx_timeout(struct usbgem_dev * dp)7196716431bSRobert Mustacchi usbgem_tx_timeout(struct usbgem_dev *dp)
7206716431bSRobert Mustacchi {
7216716431bSRobert Mustacchi 	uint_t	rwlock;
7226716431bSRobert Mustacchi 	clock_t	now;
7236716431bSRobert Mustacchi 
7246716431bSRobert Mustacchi 	for (; ; ) {
7256716431bSRobert Mustacchi 		mutex_enter(&dp->tx_watcher_lock);
726720b1687SToomas Soome 		(void) cv_timedwait(&dp->tx_watcher_cv, &dp->tx_watcher_lock,
7276716431bSRobert Mustacchi 		    dp->tx_watcher_interval + ddi_get_lbolt());
7286716431bSRobert Mustacchi 		mutex_exit(&dp->tx_watcher_lock);
7296716431bSRobert Mustacchi 
7306716431bSRobert Mustacchi 		if (dp->tx_watcher_stop) {
7316716431bSRobert Mustacchi 			break;
7326716431bSRobert Mustacchi 		}
7336716431bSRobert Mustacchi 
7346716431bSRobert Mustacchi 		now = ddi_get_lbolt();
7356716431bSRobert Mustacchi 
7366716431bSRobert Mustacchi 		rwlock = RW_READER;
7376716431bSRobert Mustacchi again:
7386716431bSRobert Mustacchi 		rw_enter(&dp->dev_state_lock, rwlock);
7396716431bSRobert Mustacchi 
7406716431bSRobert Mustacchi 		if ((dp->mac_state != MAC_STATE_DISCONNECTED &&
7416716431bSRobert Mustacchi 		    dp->fatal_error &&
7426716431bSRobert Mustacchi 		    now - dp->fatal_error >= dp->ugc.usbgc_tx_timeout) ||
7436716431bSRobert Mustacchi 		    (dp->mac_state == MAC_STATE_ONLINE &&
7446716431bSRobert Mustacchi 		    dp->mii_state == MII_STATE_LINKUP &&
7456716431bSRobert Mustacchi 		    dp->tx_busy_cnt != 0 &&
7466716431bSRobert Mustacchi 		    now - dp->tx_start_time >= dp->ugc.usbgc_tx_timeout)) {
7476716431bSRobert Mustacchi 			if (rwlock == RW_READER) {
7486716431bSRobert Mustacchi 				/*
7496716431bSRobert Mustacchi 				 * Upgrade dev_state_lock from shared mode
7506716431bSRobert Mustacchi 				 * to exclusive mode to restart nic
7516716431bSRobert Mustacchi 				 */
7526716431bSRobert Mustacchi 				rwlock = RW_WRITER;
7536716431bSRobert Mustacchi 				rw_exit(&dp->dev_state_lock);
7546716431bSRobert Mustacchi 				goto again;
7556716431bSRobert Mustacchi 			}
7566716431bSRobert Mustacchi 			cmn_err(CE_WARN, "%s: %s: restarting the nic:"
7576716431bSRobert Mustacchi 			    " fatal_error:%ld nic_state:%d"
7586716431bSRobert Mustacchi 			    " mac_state:%d starttime:%ld",
7596716431bSRobert Mustacchi 			    dp->name, __func__,
7606716431bSRobert Mustacchi 			    dp->fatal_error ? now - dp->fatal_error: 0,
7616716431bSRobert Mustacchi 			    dp->nic_state, dp->mac_state,
7626716431bSRobert Mustacchi 			    dp->tx_busy_cnt ? now - dp->tx_start_time : 0);
7636716431bSRobert Mustacchi 
7646716431bSRobert Mustacchi 			(void) usbgem_restart_nic(dp);
7656716431bSRobert Mustacchi 		}
7666716431bSRobert Mustacchi 
7676716431bSRobert Mustacchi 		rw_exit(&dp->dev_state_lock);
7686716431bSRobert Mustacchi 	}
7696716431bSRobert Mustacchi }
7706716431bSRobert Mustacchi 
7716716431bSRobert Mustacchi static int
usbgem_tx_watcher_start(struct usbgem_dev * dp)7726716431bSRobert Mustacchi usbgem_tx_watcher_start(struct usbgem_dev *dp)
7736716431bSRobert Mustacchi {
7746716431bSRobert Mustacchi 	int	err;
7756716431bSRobert Mustacchi 	kthread_t	*wdth;
7766716431bSRobert Mustacchi 
7776716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
7786716431bSRobert Mustacchi 
7796716431bSRobert Mustacchi 	/* make a first call of uwgem_lw_link_check() */
7806716431bSRobert Mustacchi 	dp->tx_watcher_stop = 0;
7816716431bSRobert Mustacchi 	dp->tx_watcher_interval = drv_usectohz(1000*1000);
7826716431bSRobert Mustacchi 
7836716431bSRobert Mustacchi 	wdth = thread_create(NULL, 0, usbgem_tx_timeout, dp, 0, &p0,
7846716431bSRobert Mustacchi 	    TS_RUN, minclsyspri);
7856716431bSRobert Mustacchi 	if (wdth == NULL) {
7866716431bSRobert Mustacchi 		cmn_err(CE_WARN,
7876716431bSRobert Mustacchi 		    "!%s: %s: failed to create a tx_watcher thread",
7886716431bSRobert Mustacchi 		    dp->name, __func__);
7896716431bSRobert Mustacchi 		return (USB_FAILURE);
7906716431bSRobert Mustacchi 	}
7916716431bSRobert Mustacchi 	dp->tx_watcher_did = wdth->t_did;
7926716431bSRobert Mustacchi 
7936716431bSRobert Mustacchi 	return (USB_SUCCESS);
7946716431bSRobert Mustacchi }
7956716431bSRobert Mustacchi 
7966716431bSRobert Mustacchi static void
usbgem_tx_watcher_stop(struct usbgem_dev * dp)7976716431bSRobert Mustacchi usbgem_tx_watcher_stop(struct usbgem_dev *dp)
7986716431bSRobert Mustacchi {
7996716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
8006716431bSRobert Mustacchi 	if (dp->tx_watcher_did) {
8016716431bSRobert Mustacchi 		/* Ensure timer routine stopped */
8026716431bSRobert Mustacchi 		dp->tx_watcher_stop = 1;
8036716431bSRobert Mustacchi 		cv_signal(&dp->tx_watcher_cv);
8046716431bSRobert Mustacchi 		thread_join(dp->tx_watcher_did);
805485f90e9SToomas Soome 		dp->tx_watcher_did = 0;
8066716431bSRobert Mustacchi 	}
8076716431bSRobert Mustacchi }
8086716431bSRobert Mustacchi 
8096716431bSRobert Mustacchi /* ================================================================== */
8106716431bSRobert Mustacchi /*
8116716431bSRobert Mustacchi  * Callback handlers
8126716431bSRobert Mustacchi  */
8136716431bSRobert Mustacchi /* ================================================================== */
8146716431bSRobert Mustacchi static void
usbgem_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)8156716431bSRobert Mustacchi usbgem_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
8166716431bSRobert Mustacchi {
8176716431bSRobert Mustacchi 	mblk_t	*newmp;
8186716431bSRobert Mustacchi 	mblk_t	*mp;
8196716431bSRobert Mustacchi 	mblk_t	*tp;
8206716431bSRobert Mustacchi 	uint64_t	len = 0;
8216716431bSRobert Mustacchi 	int	pkts = 0;
8226716431bSRobert Mustacchi 	int	bcast = 0;
8236716431bSRobert Mustacchi 	int	mcast = 0;
8246716431bSRobert Mustacchi 	boolean_t	busy;
8256716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
8266716431bSRobert Mustacchi 
8276716431bSRobert Mustacchi 	dp = (struct usbgem_dev *)req->bulk_client_private;
8286716431bSRobert Mustacchi 	mp = req->bulk_data;
8296716431bSRobert Mustacchi 	req->bulk_data = NULL;
8306716431bSRobert Mustacchi 
8316716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!%s: %s: mp:%p, cr:%s(%d)",
8326716431bSRobert Mustacchi 	    dp->name, __func__, mp,
8336716431bSRobert Mustacchi 	    usb_str_cr(req->bulk_completion_reason),
8346716431bSRobert Mustacchi 	    req->bulk_completion_reason));
8356716431bSRobert Mustacchi 
8366716431bSRobert Mustacchi 	/*
8376716431bSRobert Mustacchi 	 * we cannot acquire dev_state_lock because the routine
8386716431bSRobert Mustacchi 	 * must be executed during usbgem_mac_stop() to avoid
8396716431bSRobert Mustacchi 	 * dead lock.
8406716431bSRobert Mustacchi 	 * we use a simle membar operation to get the state correctly.
8416716431bSRobert Mustacchi 	 */
8426716431bSRobert Mustacchi 	membar_consumer();
8436716431bSRobert Mustacchi 
8446716431bSRobert Mustacchi 	if (req->bulk_completion_reason == USB_CR_OK &&
8456716431bSRobert Mustacchi 	    dp->nic_state == NIC_STATE_ONLINE) {
8466716431bSRobert Mustacchi 		newmp = (*dp->ugc.usbgc_rx_make_packet)(dp, mp);
8476716431bSRobert Mustacchi 
8486716431bSRobert Mustacchi 		if (newmp != mp) {
8496716431bSRobert Mustacchi 			/* the message has been reallocated, free old one */
8506716431bSRobert Mustacchi 			freemsg(mp);
8516716431bSRobert Mustacchi 		}
8526716431bSRobert Mustacchi 
8536716431bSRobert Mustacchi 		/* the message may includes one or more ethernet packets */
8546716431bSRobert Mustacchi 		for (tp = newmp; tp; tp = tp->b_next) {
8556716431bSRobert Mustacchi 			len += (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr;
8566716431bSRobert Mustacchi 			pkts++;
8576716431bSRobert Mustacchi 			if (tp->b_rptr[0] & 1) {
8586716431bSRobert Mustacchi 				if (bcmp(tp->b_rptr, &usbgem_bcastaddr,
8596716431bSRobert Mustacchi 				    ETHERADDRL) == 0) {
8606716431bSRobert Mustacchi 					bcast++;
8616716431bSRobert Mustacchi 				} else {
8626716431bSRobert Mustacchi 					mcast++;
8636716431bSRobert Mustacchi 				}
8646716431bSRobert Mustacchi 			}
8656716431bSRobert Mustacchi 		}
8666716431bSRobert Mustacchi 
8676716431bSRobert Mustacchi 		/* send up if it is a valid packet */
8686716431bSRobert Mustacchi 		mac_rx(dp->mh, NULL, newmp);
8696716431bSRobert Mustacchi 	} else {
8706716431bSRobert Mustacchi 		freemsg(mp);
8716716431bSRobert Mustacchi 		len = 0;
8726716431bSRobert Mustacchi 	}
8736716431bSRobert Mustacchi 
8746716431bSRobert Mustacchi 	mutex_enter(&dp->rxlock);
8756716431bSRobert Mustacchi 	/* update rx_active */
8766716431bSRobert Mustacchi 	if (dp->rx_active) {
8776716431bSRobert Mustacchi 		dp->rx_active = dp->mac_state == MAC_STATE_ONLINE;
8786716431bSRobert Mustacchi 	}
8796716431bSRobert Mustacchi 
8806716431bSRobert Mustacchi 	dp->stats.rbytes += len;
8816716431bSRobert Mustacchi 	dp->stats.rpackets += pkts;
8826716431bSRobert Mustacchi 	if (bcast | mcast) {
8836716431bSRobert Mustacchi 		dp->stats.rbcast += bcast;
8846716431bSRobert Mustacchi 		dp->stats.rmcast += mcast;
8856716431bSRobert Mustacchi 	}
8866716431bSRobert Mustacchi 	mutex_exit(&dp->rxlock);
8876716431bSRobert Mustacchi 
8886716431bSRobert Mustacchi 	if (dp->rx_active) {
8896716431bSRobert Mustacchi 		/* prepare to receive the next packets */
8906716431bSRobert Mustacchi 		if (usbgem_rx_start_unit(dp, req)) {
8916716431bSRobert Mustacchi 			/* we successed */
8926716431bSRobert Mustacchi 			goto done;
8936716431bSRobert Mustacchi 		}
8946716431bSRobert Mustacchi 		cmn_err(CE_WARN,
8956716431bSRobert Mustacchi 		    "!%s: %s: failed to fill next rx packet",
8966716431bSRobert Mustacchi 		    dp->name, __func__);
8976716431bSRobert Mustacchi 		/*
8986716431bSRobert Mustacchi 		 * we use another flag to indicate error state.
8996716431bSRobert Mustacchi 		 * if we acquire dev_state_lock for RW_WRITER here,
9006716431bSRobert Mustacchi 		 * usbgem_mac_stop() may hang.
9016716431bSRobert Mustacchi 		 */
9026716431bSRobert Mustacchi 		if (dp->fatal_error == (clock_t)0) {
9036716431bSRobert Mustacchi 			dp->fatal_error = usbgem_timestamp_nz();
9046716431bSRobert Mustacchi 		}
9056716431bSRobert Mustacchi 	} else {
9066716431bSRobert Mustacchi 		/* no need to prepare the next packets */
9076716431bSRobert Mustacchi 		usb_free_bulk_req(req);
9086716431bSRobert Mustacchi 	}
9096716431bSRobert Mustacchi 
9106716431bSRobert Mustacchi 	mutex_enter(&dp->rxlock);
9116716431bSRobert Mustacchi 	dp->rx_active = B_FALSE;
9126716431bSRobert Mustacchi 	dp->rx_busy_cnt--;
9136716431bSRobert Mustacchi 	if (dp->rx_busy_cnt == 0) {
9146716431bSRobert Mustacchi 		/* wake up someone waits for me */
9156716431bSRobert Mustacchi 		cv_broadcast(&dp->rx_drain_cv);
9166716431bSRobert Mustacchi 	}
9176716431bSRobert Mustacchi 	mutex_exit(&dp->rxlock);
9186716431bSRobert Mustacchi done:
9196716431bSRobert Mustacchi 	;
9206716431bSRobert Mustacchi }
9216716431bSRobert Mustacchi 
9226716431bSRobert Mustacchi static void
usbgem_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)9236716431bSRobert Mustacchi usbgem_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
9246716431bSRobert Mustacchi {
9256716431bSRobert Mustacchi 	boolean_t	intr;
9266716431bSRobert Mustacchi 	boolean_t	tx_sched;
9276716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
9286716431bSRobert Mustacchi 
9296716431bSRobert Mustacchi 	dp = (struct usbgem_dev *)req->bulk_client_private;
9306716431bSRobert Mustacchi 	tx_sched = B_FALSE;
9316716431bSRobert Mustacchi 
9326716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT,
9336716431bSRobert Mustacchi 	    "!%s: %s: cr:%s(%d) cb_flags:0x%x head:%d tail:%d",
9346716431bSRobert Mustacchi 	    dp->name, __func__,
9356716431bSRobert Mustacchi 	    usb_str_cr(req->bulk_completion_reason),
9366716431bSRobert Mustacchi 	    req->bulk_completion_reason,
9376716431bSRobert Mustacchi 	    req->bulk_cb_flags,
9386716431bSRobert Mustacchi 	    dp->tx_busy_cnt));
9396716431bSRobert Mustacchi 
9406716431bSRobert Mustacchi 	/* we have finished to transfer the packet into tx fifo */
9416716431bSRobert Mustacchi 	intr = DB_TCI(req->bulk_data);
9426716431bSRobert Mustacchi 	freemsg(req->bulk_data);
9436716431bSRobert Mustacchi 
9446716431bSRobert Mustacchi 	if (req->bulk_completion_reason != USB_CR_OK &&
9456716431bSRobert Mustacchi 	    dp->fatal_error == (clock_t)0) {
9466716431bSRobert Mustacchi 		dp->fatal_error = usbgem_timestamp_nz();
9476716431bSRobert Mustacchi 	}
9486716431bSRobert Mustacchi 
9496716431bSRobert Mustacchi 	mutex_enter(&dp->txlock);
9506716431bSRobert Mustacchi 
9516716431bSRobert Mustacchi 	if (intr) {
9526716431bSRobert Mustacchi 		ASSERT(dp->tx_intr_pended > 0);
9536716431bSRobert Mustacchi 		/* find the last interrupt we have scheduled */
9546716431bSRobert Mustacchi 		if (--(dp->tx_intr_pended) == 0) {
9556716431bSRobert Mustacchi 			tx_sched = B_TRUE;
9566716431bSRobert Mustacchi 		}
9576716431bSRobert Mustacchi 	}
9586716431bSRobert Mustacchi 
9596716431bSRobert Mustacchi 	ASSERT(dp->tx_busy_cnt > 0);
9606716431bSRobert Mustacchi 	req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
9616716431bSRobert Mustacchi 	dp->tx_free_list = req;
9626716431bSRobert Mustacchi 	dp->tx_busy_cnt--;
9636716431bSRobert Mustacchi 
9646716431bSRobert Mustacchi #ifdef CONFIG_TX_LIMITER
9656716431bSRobert Mustacchi 	if (tx_sched) {
9666716431bSRobert Mustacchi 		dp->tx_max_packets =
9676716431bSRobert Mustacchi 		    min(dp->tx_max_packets + 1, dp->ugc.usbgc_tx_list_max);
9686716431bSRobert Mustacchi 	}
9696716431bSRobert Mustacchi #endif
9706716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_ONLINE && dp->tx_busy_cnt == 0) {
9716716431bSRobert Mustacchi 		cv_broadcast(&dp->tx_drain_cv);
9726716431bSRobert Mustacchi 	}
9736716431bSRobert Mustacchi 
9746716431bSRobert Mustacchi 	mutex_exit(&dp->txlock);
9756716431bSRobert Mustacchi 
9766716431bSRobert Mustacchi 	if (tx_sched) {
9776716431bSRobert Mustacchi 		mac_tx_update(dp->mh);
9786716431bSRobert Mustacchi 	}
9796716431bSRobert Mustacchi }
9806716431bSRobert Mustacchi 
9816716431bSRobert Mustacchi static void
usbgem_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)9826716431bSRobert Mustacchi usbgem_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
9836716431bSRobert Mustacchi {
9846716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
9856716431bSRobert Mustacchi 
9866716431bSRobert Mustacchi 	dp = (struct usbgem_dev *)req->intr_client_private;
9876716431bSRobert Mustacchi 	dp->stats.intr++;
9886716431bSRobert Mustacchi 
9896716431bSRobert Mustacchi 	if (req->intr_completion_reason == USB_CR_OK) {
9906716431bSRobert Mustacchi 		(*dp->ugc.usbgc_interrupt)(dp, req->intr_data);
9916716431bSRobert Mustacchi 	}
9926716431bSRobert Mustacchi 
9936716431bSRobert Mustacchi 	/* free the request and data */
9946716431bSRobert Mustacchi 	usb_free_intr_req(req);
9956716431bSRobert Mustacchi }
9966716431bSRobert Mustacchi 
9976716431bSRobert Mustacchi /* ======================================================================== */
9986716431bSRobert Mustacchi /*
9996716431bSRobert Mustacchi  * MII support routines
10006716431bSRobert Mustacchi  */
10016716431bSRobert Mustacchi /* ======================================================================== */
10026716431bSRobert Mustacchi static void
usbgem_choose_forcedmode(struct usbgem_dev * dp)10036716431bSRobert Mustacchi usbgem_choose_forcedmode(struct usbgem_dev *dp)
10046716431bSRobert Mustacchi {
10056716431bSRobert Mustacchi 	/* choose media mode */
10066716431bSRobert Mustacchi 	if (dp->anadv_1000fdx || dp->anadv_1000hdx) {
10076716431bSRobert Mustacchi 		dp->speed = USBGEM_SPD_1000;
10086716431bSRobert Mustacchi 		dp->full_duplex = dp->anadv_1000fdx;
10096716431bSRobert Mustacchi 	} else if (dp->anadv_100fdx || dp->anadv_100t4) {
10106716431bSRobert Mustacchi 		dp->speed = USBGEM_SPD_100;
10116716431bSRobert Mustacchi 		dp->full_duplex = B_TRUE;
10126716431bSRobert Mustacchi 	} else if (dp->anadv_100hdx) {
10136716431bSRobert Mustacchi 		dp->speed = USBGEM_SPD_100;
10146716431bSRobert Mustacchi 		dp->full_duplex = B_FALSE;
10156716431bSRobert Mustacchi 	} else {
10166716431bSRobert Mustacchi 		dp->speed = USBGEM_SPD_10;
10176716431bSRobert Mustacchi 		dp->full_duplex = dp->anadv_10fdx;
10186716431bSRobert Mustacchi 	}
10196716431bSRobert Mustacchi }
10206716431bSRobert Mustacchi 
10216716431bSRobert Mustacchi static uint16_t
usbgem_mii_read(struct usbgem_dev * dp,uint_t reg,int * errp)10226716431bSRobert Mustacchi usbgem_mii_read(struct usbgem_dev *dp, uint_t reg, int *errp)
10236716431bSRobert Mustacchi {
10246716431bSRobert Mustacchi 	uint16_t	val;
10256716431bSRobert Mustacchi 
10266716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
10276716431bSRobert Mustacchi 	val = (*dp->ugc.usbgc_mii_read)(dp, reg, errp);
10286716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
10296716431bSRobert Mustacchi 
10306716431bSRobert Mustacchi 	return (val);
10316716431bSRobert Mustacchi }
10326716431bSRobert Mustacchi 
10336716431bSRobert Mustacchi static void
usbgem_mii_write(struct usbgem_dev * dp,uint_t reg,uint16_t val,int * errp)10346716431bSRobert Mustacchi usbgem_mii_write(struct usbgem_dev *dp, uint_t reg, uint16_t val, int *errp)
10356716431bSRobert Mustacchi {
10366716431bSRobert Mustacchi 	sema_p(&dp->hal_op_lock);
10376716431bSRobert Mustacchi 	(*dp->ugc.usbgc_mii_write)(dp, reg, val, errp);
10386716431bSRobert Mustacchi 	sema_v(&dp->hal_op_lock);
10396716431bSRobert Mustacchi }
10406716431bSRobert Mustacchi 
10416716431bSRobert Mustacchi static int
usbgem_mii_probe(struct usbgem_dev * dp)10426716431bSRobert Mustacchi usbgem_mii_probe(struct usbgem_dev *dp)
10436716431bSRobert Mustacchi {
10446716431bSRobert Mustacchi 	int	err;
10456716431bSRobert Mustacchi 
10466716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_mii_probe)(dp);
10476716431bSRobert Mustacchi 	return (err);
10486716431bSRobert Mustacchi }
10496716431bSRobert Mustacchi 
10506716431bSRobert Mustacchi static int
usbgem_mii_init(struct usbgem_dev * dp)10516716431bSRobert Mustacchi usbgem_mii_init(struct usbgem_dev *dp)
10526716431bSRobert Mustacchi {
10536716431bSRobert Mustacchi 	int	err;
10546716431bSRobert Mustacchi 
10556716431bSRobert Mustacchi 	err = (*dp->ugc.usbgc_mii_init)(dp);
10566716431bSRobert Mustacchi 	return (err);
10576716431bSRobert Mustacchi }
10586716431bSRobert Mustacchi 
10596716431bSRobert Mustacchi #define	fc_cap_decode(x)	\
10606716431bSRobert Mustacchi 	((((x) & MII_ABILITY_PAUSE) != 0 ? 1 : 0) |	\
10616716431bSRobert Mustacchi 	(((x) & MII_ABILITY_ASM_DIR) != 0 ? 2 : 0))
10626716431bSRobert Mustacchi 
10636716431bSRobert Mustacchi int
usbgem_mii_config_default(struct usbgem_dev * dp,int * errp)10646716431bSRobert Mustacchi usbgem_mii_config_default(struct usbgem_dev *dp, int *errp)
10656716431bSRobert Mustacchi {
10666716431bSRobert Mustacchi 	uint16_t	mii_stat;
10676716431bSRobert Mustacchi 	uint16_t	val;
10686716431bSRobert Mustacchi 
10696716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
10706716431bSRobert Mustacchi 
10716716431bSRobert Mustacchi 	/*
10726716431bSRobert Mustacchi 	 * Configure bits in advertisement register
10736716431bSRobert Mustacchi 	 */
10746716431bSRobert Mustacchi 	mii_stat = dp->mii_status;
10756716431bSRobert Mustacchi 
10766716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: MII_STATUS reg:%b",
10776716431bSRobert Mustacchi 	    dp->name, __func__, mii_stat, MII_STATUS_BITS));
10786716431bSRobert Mustacchi 
10796716431bSRobert Mustacchi 	if ((mii_stat & MII_STATUS_ABILITY_TECH) == 0) {
10806716431bSRobert Mustacchi 		/* it's funny */
10816716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: wrong ability bits: mii_status:%b",
10826716431bSRobert Mustacchi 		    dp->name, mii_stat, MII_STATUS_BITS);
10836716431bSRobert Mustacchi 		return (USB_FAILURE);
10846716431bSRobert Mustacchi 	}
10856716431bSRobert Mustacchi 
10866716431bSRobert Mustacchi 	/* Do not change the rest of ability bits in advert reg */
10876716431bSRobert Mustacchi 	val = usbgem_mii_read(dp, MII_AN_ADVERT, errp) & ~MII_ABILITY_ALL;
10886716431bSRobert Mustacchi 	if (*errp != USB_SUCCESS) {
10896716431bSRobert Mustacchi 		goto usberr;
10906716431bSRobert Mustacchi 	}
10916716431bSRobert Mustacchi 
10926716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT,
10936716431bSRobert Mustacchi 	    "!%s: %s: 100T4:%d 100F:%d 100H:%d 10F:%d 10H:%d",
10946716431bSRobert Mustacchi 	    dp->name, __func__,
10956716431bSRobert Mustacchi 	    dp->anadv_100t4, dp->anadv_100fdx, dp->anadv_100hdx,
10966716431bSRobert Mustacchi 	    dp->anadv_10fdx, dp->anadv_10hdx));
10976716431bSRobert Mustacchi 
10986716431bSRobert Mustacchi 	/* set technology bits */
10996716431bSRobert Mustacchi 	if (dp->anadv_100t4) {
11006716431bSRobert Mustacchi 		val |= MII_ABILITY_100BASE_T4;
11016716431bSRobert Mustacchi 	}
11026716431bSRobert Mustacchi 	if (dp->anadv_100fdx) {
11036716431bSRobert Mustacchi 		val |= MII_ABILITY_100BASE_TX_FD;
11046716431bSRobert Mustacchi 	}
11056716431bSRobert Mustacchi 	if (dp->anadv_100hdx) {
11066716431bSRobert Mustacchi 		val |= MII_ABILITY_100BASE_TX;
11076716431bSRobert Mustacchi 	}
11086716431bSRobert Mustacchi 	if (dp->anadv_10fdx) {
11096716431bSRobert Mustacchi 		val |= MII_ABILITY_10BASE_T_FD;
11106716431bSRobert Mustacchi 	}
11116716431bSRobert Mustacchi 	if (dp->anadv_10hdx) {
11126716431bSRobert Mustacchi 		val |= MII_ABILITY_10BASE_T;
11136716431bSRobert Mustacchi 	}
11146716431bSRobert Mustacchi 
11156716431bSRobert Mustacchi 	/* set flow control capabilities */
11166716431bSRobert Mustacchi 	if (dp->anadv_pause) {
11176716431bSRobert Mustacchi 		val |= MII_ABILITY_PAUSE;
11186716431bSRobert Mustacchi 	}
11196716431bSRobert Mustacchi 	if (dp->anadv_asmpause) {
11206716431bSRobert Mustacchi 		val |= MII_ABILITY_ASM_DIR;
11216716431bSRobert Mustacchi 	}
11226716431bSRobert Mustacchi 
11236716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT,
11246716431bSRobert Mustacchi 	    "!%s: %s: setting MII_AN_ADVERT reg:%b, pause:%d, asmpause:%d",
11256716431bSRobert Mustacchi 	    dp->name, __func__, val, MII_ABILITY_BITS,
11266716431bSRobert Mustacchi 	    dp->anadv_pause, dp->anadv_asmpause));
11276716431bSRobert Mustacchi 
11286716431bSRobert Mustacchi 	usbgem_mii_write(dp, MII_AN_ADVERT, val, errp);
11296716431bSRobert Mustacchi 	if (*errp != USB_SUCCESS) {
11306716431bSRobert Mustacchi 		goto usberr;
11316716431bSRobert Mustacchi 	}
11326716431bSRobert Mustacchi 
11336716431bSRobert Mustacchi 	if (dp->mii_status & MII_STATUS_XSTATUS) {
11346716431bSRobert Mustacchi 		/*
11356716431bSRobert Mustacchi 		 * 1000Base-T GMII support
11366716431bSRobert Mustacchi 		 */
11376716431bSRobert Mustacchi 		if (!dp->anadv_autoneg) {
11386716431bSRobert Mustacchi 			/* enable manual configuration */
11396716431bSRobert Mustacchi 			val = MII_1000TC_CFG_EN;
11406716431bSRobert Mustacchi 			if (dp->anadv_1000t_ms == 2) {
11416716431bSRobert Mustacchi 				val |= MII_1000TC_CFG_VAL;
11426716431bSRobert Mustacchi 			}
11436716431bSRobert Mustacchi 		} else {
11446716431bSRobert Mustacchi 			val = 0;
11456716431bSRobert Mustacchi 			if (dp->anadv_1000fdx) {
11466716431bSRobert Mustacchi 				val |= MII_1000TC_ADV_FULL;
11476716431bSRobert Mustacchi 			}
11486716431bSRobert Mustacchi 			if (dp->anadv_1000hdx) {
11496716431bSRobert Mustacchi 				val |= MII_1000TC_ADV_HALF;
11506716431bSRobert Mustacchi 			}
11516716431bSRobert Mustacchi 			switch (dp->anadv_1000t_ms) {
11526716431bSRobert Mustacchi 			case 1:
11536716431bSRobert Mustacchi 				/* slave */
11546716431bSRobert Mustacchi 				val |= MII_1000TC_CFG_EN;
11556716431bSRobert Mustacchi 				break;
11566716431bSRobert Mustacchi 
11576716431bSRobert Mustacchi 			case 2:
11586716431bSRobert Mustacchi 				/* master */
11596716431bSRobert Mustacchi 				val |= MII_1000TC_CFG_EN | MII_1000TC_CFG_VAL;
11606716431bSRobert Mustacchi 				break;
11616716431bSRobert Mustacchi 
11626716431bSRobert Mustacchi 			default:
11636716431bSRobert Mustacchi 				/* auto: do nothing */
11646716431bSRobert Mustacchi 				break;
11656716431bSRobert Mustacchi 			}
11666716431bSRobert Mustacchi 		}
11676716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT,
11686716431bSRobert Mustacchi 		    "!%s: %s: setting MII_1000TC reg:%b",
11696716431bSRobert Mustacchi 		    dp->name, __func__, val, MII_1000TC_BITS));
11706716431bSRobert Mustacchi 
11716716431bSRobert Mustacchi 		usbgem_mii_write(dp, MII_1000TC, val, errp);
11726716431bSRobert Mustacchi 		if (*errp != USB_SUCCESS) {
11736716431bSRobert Mustacchi 			goto usberr;
11746716431bSRobert Mustacchi 		}
11756716431bSRobert Mustacchi 	}
11766716431bSRobert Mustacchi 	return (USB_SUCCESS);
11776716431bSRobert Mustacchi 
11786716431bSRobert Mustacchi usberr:
11796716431bSRobert Mustacchi 	return (*errp);
11806716431bSRobert Mustacchi }
11816716431bSRobert Mustacchi 
11826716431bSRobert Mustacchi static char *usbgem_fc_type[] = {
11836716431bSRobert Mustacchi 	"without",
11846716431bSRobert Mustacchi 	"with symmetric",
11856716431bSRobert Mustacchi 	"with tx",
11866716431bSRobert Mustacchi 	"with rx",
11876716431bSRobert Mustacchi };
11886716431bSRobert Mustacchi 
11896716431bSRobert Mustacchi #define	USBGEM_LINKUP(dp)	mac_link_update((dp)->mh, LINK_STATE_UP)
11906716431bSRobert Mustacchi #define	USBGEM_LINKDOWN(dp)	mac_link_update((dp)->mh, LINK_STATE_DOWN)
11916716431bSRobert Mustacchi 
11926716431bSRobert Mustacchi static uint8_t usbgem_fc_result[4 /* my cap */][4 /* lp cap */] = {
11936716431bSRobert Mustacchi /*	 none	symm	tx	rx/symm */
11946716431bSRobert Mustacchi /* none */
11956716431bSRobert Mustacchi 	{FLOW_CONTROL_NONE,
11966716431bSRobert Mustacchi 		FLOW_CONTROL_NONE,
11976716431bSRobert Mustacchi 			FLOW_CONTROL_NONE,
11986716431bSRobert Mustacchi 				FLOW_CONTROL_NONE},
11996716431bSRobert Mustacchi /* sym */
12006716431bSRobert Mustacchi 	{FLOW_CONTROL_NONE,
12016716431bSRobert Mustacchi 		FLOW_CONTROL_SYMMETRIC,
12026716431bSRobert Mustacchi 			FLOW_CONTROL_NONE,
12036716431bSRobert Mustacchi 				FLOW_CONTROL_SYMMETRIC},
12046716431bSRobert Mustacchi /* tx */
12056716431bSRobert Mustacchi 	{FLOW_CONTROL_NONE,
12066716431bSRobert Mustacchi 		FLOW_CONTROL_NONE,
12076716431bSRobert Mustacchi 			FLOW_CONTROL_NONE,
12086716431bSRobert Mustacchi 				FLOW_CONTROL_TX_PAUSE},
12096716431bSRobert Mustacchi /* rx/symm */
12106716431bSRobert Mustacchi 	{FLOW_CONTROL_NONE,
12116716431bSRobert Mustacchi 		FLOW_CONTROL_SYMMETRIC,
12126716431bSRobert Mustacchi 			FLOW_CONTROL_RX_PAUSE,
12136716431bSRobert Mustacchi 				FLOW_CONTROL_SYMMETRIC},
12146716431bSRobert Mustacchi };
12156716431bSRobert Mustacchi 
12166716431bSRobert Mustacchi static boolean_t
usbgem_mii_link_check(struct usbgem_dev * dp,int * oldstatep,int * newstatep)12176716431bSRobert Mustacchi usbgem_mii_link_check(struct usbgem_dev *dp, int *oldstatep, int *newstatep)
12186716431bSRobert Mustacchi {
12196716431bSRobert Mustacchi 	boolean_t	tx_sched = B_FALSE;
12206716431bSRobert Mustacchi 	uint16_t	status;
12216716431bSRobert Mustacchi 	uint16_t	advert;
12226716431bSRobert Mustacchi 	uint16_t	lpable;
12236716431bSRobert Mustacchi 	uint16_t	exp;
12246716431bSRobert Mustacchi 	uint16_t	ctl1000;
12256716431bSRobert Mustacchi 	uint16_t	stat1000;
12266716431bSRobert Mustacchi 	uint16_t	val;
12276716431bSRobert Mustacchi 	clock_t		now;
12286716431bSRobert Mustacchi 	clock_t		diff;
12296716431bSRobert Mustacchi 	int		linkdown_action;
12306716431bSRobert Mustacchi 	boolean_t	fix_phy = B_FALSE;
12316716431bSRobert Mustacchi 	int		err;
12326716431bSRobert Mustacchi 	uint_t		rwlock;
12336716431bSRobert Mustacchi 
12346716431bSRobert Mustacchi 	DPRINTF(4, (CE_CONT, "!%s: %s: time:%d state:%d",
12356716431bSRobert Mustacchi 	    dp->name, __func__, ddi_get_lbolt(), dp->mii_state));
12366716431bSRobert Mustacchi 
12376716431bSRobert Mustacchi 	if (dp->mii_state != MII_STATE_LINKUP) {
12386716431bSRobert Mustacchi 		rwlock = RW_WRITER;
12396716431bSRobert Mustacchi 	} else {
12406716431bSRobert Mustacchi 		rwlock = RW_READER;
12416716431bSRobert Mustacchi 	}
12426716431bSRobert Mustacchi again:
12436716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, rwlock);
12446716431bSRobert Mustacchi 
12456716431bSRobert Mustacchi 	/* save old mii state */
12466716431bSRobert Mustacchi 	*oldstatep = dp->mii_state;
12476716431bSRobert Mustacchi 
12486716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
12496716431bSRobert Mustacchi 		/* stop periodic execution of the link watcher */
12506716431bSRobert Mustacchi 		dp->mii_interval = 0;
12516716431bSRobert Mustacchi 		tx_sched = B_FALSE;
12526716431bSRobert Mustacchi 		goto next;
12536716431bSRobert Mustacchi 	}
12546716431bSRobert Mustacchi 
12556716431bSRobert Mustacchi 	now = ddi_get_lbolt();
12566716431bSRobert Mustacchi 	diff = now - dp->mii_last_check;
12576716431bSRobert Mustacchi 	dp->mii_last_check = now;
12586716431bSRobert Mustacchi 
12596716431bSRobert Mustacchi 	/*
12606716431bSRobert Mustacchi 	 * For NWAM, don't show linkdown state right
12616716431bSRobert Mustacchi 	 * when the device is attached.
12626716431bSRobert Mustacchi 	 */
12636716431bSRobert Mustacchi 	if (dp->linkup_delay > 0) {
12646716431bSRobert Mustacchi 		if (dp->linkup_delay > diff) {
12656716431bSRobert Mustacchi 			dp->linkup_delay -= diff;
12666716431bSRobert Mustacchi 		} else {
12676716431bSRobert Mustacchi 			/* link up timeout */
12686716431bSRobert Mustacchi 			dp->linkup_delay = -1;
12696716431bSRobert Mustacchi 		}
12706716431bSRobert Mustacchi 	}
12716716431bSRobert Mustacchi 
12726716431bSRobert Mustacchi next_nowait:
12736716431bSRobert Mustacchi 	switch (dp->mii_state) {
12746716431bSRobert Mustacchi 	case MII_STATE_UNKNOWN:
12756716431bSRobert Mustacchi 		goto reset_phy;
12766716431bSRobert Mustacchi 
12776716431bSRobert Mustacchi 	case MII_STATE_RESETTING:
12786716431bSRobert Mustacchi 		dp->mii_timer -= diff;
12796716431bSRobert Mustacchi 		if (dp->mii_timer > 0) {
12806716431bSRobert Mustacchi 			/* don't read phy registers in resetting */
12816716431bSRobert Mustacchi 			dp->mii_interval = WATCH_INTERVAL_FAST;
12826716431bSRobert Mustacchi 			goto next;
12836716431bSRobert Mustacchi 		}
12846716431bSRobert Mustacchi 
12856716431bSRobert Mustacchi 		val = usbgem_mii_read(dp, MII_CONTROL, &err);
12866716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
12876716431bSRobert Mustacchi 			goto usberr;
12886716431bSRobert Mustacchi 		}
12896716431bSRobert Mustacchi 		if (val & MII_CONTROL_RESET) {
12906716431bSRobert Mustacchi 			cmn_err(CE_NOTE,
12916716431bSRobert Mustacchi 			    "!%s: time:%ld resetting phy not complete."
12926716431bSRobert Mustacchi 			    " mii_control:0x%b",
12936716431bSRobert Mustacchi 			    dp->name, ddi_get_lbolt(),
12946716431bSRobert Mustacchi 			    val, MII_CONTROL_BITS);
12956716431bSRobert Mustacchi 		}
12966716431bSRobert Mustacchi 
12976716431bSRobert Mustacchi 		/* ensure neither isolated nor pwrdown nor auto-nego mode */
12986716431bSRobert Mustacchi 		usbgem_mii_write(dp, MII_CONTROL, 0, &err);
12996716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
13006716431bSRobert Mustacchi 			goto usberr;
13016716431bSRobert Mustacchi 		}
13026716431bSRobert Mustacchi #if USBGEM_DEBUG_LEVEL > 10
13036716431bSRobert Mustacchi 		val = usbgem_mii_read(dp, MII_CONTROL, &err);
13046716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: readback control %b",
13056716431bSRobert Mustacchi 		    dp->name, val, MII_CONTROL_BITS);
13066716431bSRobert Mustacchi #endif
13076716431bSRobert Mustacchi 		/* As resetting PHY has completed, configure PHY registers */
13086716431bSRobert Mustacchi 		if ((*dp->ugc.usbgc_mii_config)(dp, &err) != USB_SUCCESS) {
13096716431bSRobert Mustacchi 			/* we failed to configure PHY */
13106716431bSRobert Mustacchi 			goto usberr;
13116716431bSRobert Mustacchi 		}
13126716431bSRobert Mustacchi 
13136716431bSRobert Mustacchi 		/* prepare for forced mode */
13146716431bSRobert Mustacchi 		usbgem_choose_forcedmode(dp);
13156716431bSRobert Mustacchi 
13166716431bSRobert Mustacchi 		dp->mii_lpable = 0;
13176716431bSRobert Mustacchi 		dp->mii_advert = 0;
13186716431bSRobert Mustacchi 		dp->mii_exp = 0;
13196716431bSRobert Mustacchi 		dp->mii_ctl1000 = 0;
13206716431bSRobert Mustacchi 		dp->mii_stat1000 = 0;
13216716431bSRobert Mustacchi 
13226716431bSRobert Mustacchi 		dp->flow_control = FLOW_CONTROL_NONE;
13236716431bSRobert Mustacchi 
13246716431bSRobert Mustacchi 		if (!dp->anadv_autoneg) {
13256716431bSRobert Mustacchi 			/* skip auto-negotiation phase */
13266716431bSRobert Mustacchi 			dp->mii_state = MII_STATE_MEDIA_SETUP;
13276716431bSRobert Mustacchi 			dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
13286716431bSRobert Mustacchi 			goto next_nowait;
13296716431bSRobert Mustacchi 		}
13306716431bSRobert Mustacchi 
13316716431bSRobert Mustacchi 		/* issue an auto-negotiation command */
13326716431bSRobert Mustacchi 		goto autonego;
13336716431bSRobert Mustacchi 
13346716431bSRobert Mustacchi 	case MII_STATE_AUTONEGOTIATING:
13356716431bSRobert Mustacchi 		/*
13366716431bSRobert Mustacchi 		 * Autonegotiation in progress
13376716431bSRobert Mustacchi 		 */
13386716431bSRobert Mustacchi 		dp->mii_timer -= diff;
13396716431bSRobert Mustacchi 		if (dp->mii_timer -
13406716431bSRobert Mustacchi 		    (dp->ugc.usbgc_mii_an_timeout - dp->ugc.usbgc_mii_an_wait)
13416716431bSRobert Mustacchi 		    > 0) {
13426716431bSRobert Mustacchi 			/* wait for minimum time (2.3 - 2.5 sec) */
13436716431bSRobert Mustacchi 			dp->mii_interval = WATCH_INTERVAL_FAST;
13446716431bSRobert Mustacchi 			goto next;
13456716431bSRobert Mustacchi 		}
13466716431bSRobert Mustacchi 
13476716431bSRobert Mustacchi 		/* read PHY status */
13486716431bSRobert Mustacchi 		status = usbgem_mii_read(dp, MII_STATUS, &err);
13496716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
13506716431bSRobert Mustacchi 			goto usberr;
13516716431bSRobert Mustacchi 		}
13526716431bSRobert Mustacchi 		DPRINTF(4, (CE_CONT,
13536716431bSRobert Mustacchi 		    "!%s: %s: called: mii_state:%d MII_STATUS reg:%b",
13546716431bSRobert Mustacchi 		    dp->name, __func__, dp->mii_state,
13556716431bSRobert Mustacchi 		    status, MII_STATUS_BITS));
13566716431bSRobert Mustacchi 
13576716431bSRobert Mustacchi 		if (status & MII_STATUS_REMFAULT) {
13586716431bSRobert Mustacchi 			/*
13596716431bSRobert Mustacchi 			 * The link parnert told me something wrong happend.
13606716431bSRobert Mustacchi 			 * What do we do ?
13616716431bSRobert Mustacchi 			 */
13626716431bSRobert Mustacchi 			cmn_err(CE_CONT,
13636716431bSRobert Mustacchi 			    "!%s: auto-negotiation failed: remote fault",
13646716431bSRobert Mustacchi 			    dp->name);
13656716431bSRobert Mustacchi 			goto autonego;
13666716431bSRobert Mustacchi 		}
13676716431bSRobert Mustacchi 
13686716431bSRobert Mustacchi 		if ((status & MII_STATUS_ANDONE) == 0) {
13696716431bSRobert Mustacchi 			if (dp->mii_timer <= 0) {
13706716431bSRobert Mustacchi 				/*
13716716431bSRobert Mustacchi 				 * Auto-negotiation has been timed out,
13726716431bSRobert Mustacchi 				 * Reset PHY and try again.
13736716431bSRobert Mustacchi 				 */
13746716431bSRobert Mustacchi 				if (!dp->mii_supress_msg) {
13756716431bSRobert Mustacchi 					cmn_err(CE_WARN,
13766716431bSRobert Mustacchi 					    "!%s: auto-negotiation failed:"
13776716431bSRobert Mustacchi 					    " timeout",
13786716431bSRobert Mustacchi 					    dp->name);
13796716431bSRobert Mustacchi 					dp->mii_supress_msg = B_TRUE;
13806716431bSRobert Mustacchi 				}
13816716431bSRobert Mustacchi 				goto autonego;
13826716431bSRobert Mustacchi 			}
13836716431bSRobert Mustacchi 			/*
13846716431bSRobert Mustacchi 			 * Auto-negotiation is in progress. Wait for a while.
13856716431bSRobert Mustacchi 			 */
13866716431bSRobert Mustacchi 			dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
13876716431bSRobert Mustacchi 			goto next;
13886716431bSRobert Mustacchi 		}
13896716431bSRobert Mustacchi 
13906716431bSRobert Mustacchi 		/*
13916716431bSRobert Mustacchi 		 * Auto-negotiation has been completed. Let's go to AN_DONE.
13926716431bSRobert Mustacchi 		 */
13936716431bSRobert Mustacchi 		dp->mii_state = MII_STATE_AN_DONE;
13946716431bSRobert Mustacchi 		dp->mii_supress_msg = B_FALSE;
13956716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT,
13966716431bSRobert Mustacchi 		    "!%s: auto-negotiation completed, MII_STATUS:%b",
13976716431bSRobert Mustacchi 		    dp->name, status, MII_STATUS_BITS));
13986716431bSRobert Mustacchi 
13996716431bSRobert Mustacchi 		if (dp->ugc.usbgc_mii_an_delay > 0) {
14006716431bSRobert Mustacchi 			dp->mii_timer = dp->ugc.usbgc_mii_an_delay;
14016716431bSRobert Mustacchi 			dp->mii_interval = drv_usectohz(20*1000);
14026716431bSRobert Mustacchi 			goto next;
14036716431bSRobert Mustacchi 		}
14046716431bSRobert Mustacchi 
14056716431bSRobert Mustacchi 		dp->mii_timer = 0;
14066716431bSRobert Mustacchi 		diff = 0;
14076716431bSRobert Mustacchi 		goto next_nowait;
14086716431bSRobert Mustacchi 
14096716431bSRobert Mustacchi 	case MII_STATE_AN_DONE:
14106716431bSRobert Mustacchi 		/*
14116716431bSRobert Mustacchi 		 * Auto-negotiation has done. Now we can set up media.
14126716431bSRobert Mustacchi 		 */
14136716431bSRobert Mustacchi 		dp->mii_timer -= diff;
14146716431bSRobert Mustacchi 		if (dp->mii_timer > 0) {
14156716431bSRobert Mustacchi 			/* wait for a while */
14166716431bSRobert Mustacchi 			dp->mii_interval = WATCH_INTERVAL_FAST;
14176716431bSRobert Mustacchi 			goto next;
14186716431bSRobert Mustacchi 		}
14196716431bSRobert Mustacchi 
14206716431bSRobert Mustacchi 		/*
14216716431bSRobert Mustacchi 		 * Setup speed and duplex mode according with
14226716431bSRobert Mustacchi 		 * the result of auto negotiation.
14236716431bSRobert Mustacchi 		 */
14246716431bSRobert Mustacchi 
14256716431bSRobert Mustacchi 		/*
14266716431bSRobert Mustacchi 		 * Read registers required to determin current
14276716431bSRobert Mustacchi 		 * duplex mode and media speed.
14286716431bSRobert Mustacchi 		 */
14296716431bSRobert Mustacchi 		if (dp->ugc.usbgc_mii_an_delay > 0) {
14306716431bSRobert Mustacchi 			/* the 'status' variable is not initialized yet */
14316716431bSRobert Mustacchi 			status = usbgem_mii_read(dp, MII_STATUS, &err);
14326716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
14336716431bSRobert Mustacchi 				goto usberr;
14346716431bSRobert Mustacchi 			}
14356716431bSRobert Mustacchi 		}
14366716431bSRobert Mustacchi 		advert = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
14376716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
14386716431bSRobert Mustacchi 			goto usberr;
14396716431bSRobert Mustacchi 		}
14406716431bSRobert Mustacchi 		lpable = usbgem_mii_read(dp, MII_AN_LPABLE, &err);
14416716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
14426716431bSRobert Mustacchi 			goto usberr;
14436716431bSRobert Mustacchi 		}
14446716431bSRobert Mustacchi 		exp = usbgem_mii_read(dp, MII_AN_EXPANSION, &err);
14456716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
14466716431bSRobert Mustacchi 			goto usberr;
14476716431bSRobert Mustacchi 		}
14486716431bSRobert Mustacchi 		if (exp == 0xffff) {
14496716431bSRobert Mustacchi 			/* some phys don't have exp register */
14506716431bSRobert Mustacchi 			exp = 0;
14516716431bSRobert Mustacchi 		}
14526716431bSRobert Mustacchi 
14536716431bSRobert Mustacchi 		ctl1000 = 0;
14546716431bSRobert Mustacchi 		stat1000 = 0;
14556716431bSRobert Mustacchi 		if (dp->mii_status & MII_STATUS_XSTATUS) {
14566716431bSRobert Mustacchi 			ctl1000 = usbgem_mii_read(dp, MII_1000TC, &err);
14576716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
14586716431bSRobert Mustacchi 				goto usberr;
14596716431bSRobert Mustacchi 			}
14606716431bSRobert Mustacchi 			stat1000 = usbgem_mii_read(dp, MII_1000TS, &err);
14616716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
14626716431bSRobert Mustacchi 				goto usberr;
14636716431bSRobert Mustacchi 			}
14646716431bSRobert Mustacchi 		}
14656716431bSRobert Mustacchi 		dp->mii_lpable = lpable;
14666716431bSRobert Mustacchi 		dp->mii_advert = advert;
14676716431bSRobert Mustacchi 		dp->mii_exp = exp;
14686716431bSRobert Mustacchi 		dp->mii_ctl1000 = ctl1000;
14696716431bSRobert Mustacchi 		dp->mii_stat1000 = stat1000;
14706716431bSRobert Mustacchi 
14716716431bSRobert Mustacchi 		cmn_err(CE_CONT,
14726716431bSRobert Mustacchi 		    "!%s: auto-negotiation done: "
14736716431bSRobert Mustacchi 		    "status:%b, advert:%b, lpable:%b, exp:%b",
14746716431bSRobert Mustacchi 		    dp->name,
14756716431bSRobert Mustacchi 		    status, MII_STATUS_BITS,
14766716431bSRobert Mustacchi 		    advert, MII_ABILITY_BITS,
14776716431bSRobert Mustacchi 		    lpable, MII_ABILITY_BITS,
14786716431bSRobert Mustacchi 		    exp, MII_AN_EXP_BITS);
14796716431bSRobert Mustacchi 
14806716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT, "!%s: MII_STATUS:%b",
14816716431bSRobert Mustacchi 		    dp->name, status, MII_STATUS_BITS));
14826716431bSRobert Mustacchi 
14836716431bSRobert Mustacchi 		if (dp->mii_status & MII_STATUS_XSTATUS) {
14846716431bSRobert Mustacchi 			cmn_err(CE_CONT,
14856716431bSRobert Mustacchi 			    "! MII_1000TC reg:%b, MII_1000TS reg:%b",
14866716431bSRobert Mustacchi 			    ctl1000, MII_1000TC_BITS,
14876716431bSRobert Mustacchi 			    stat1000, MII_1000TS_BITS);
14886716431bSRobert Mustacchi 		}
14896716431bSRobert Mustacchi 
14906716431bSRobert Mustacchi 		if (usbgem_population(lpable) <= 1 &&
14916716431bSRobert Mustacchi 		    (exp & MII_AN_EXP_LPCANAN) == 0) {
14926716431bSRobert Mustacchi 			if ((advert & MII_ABILITY_TECH) != lpable) {
14936716431bSRobert Mustacchi 				cmn_err(CE_WARN,
14946716431bSRobert Mustacchi 				    "!%s: but the link partner doesn't seem"
14956716431bSRobert Mustacchi 				    " to have auto-negotiation capability."
14966716431bSRobert Mustacchi 				    " please check the link configuration.",
14976716431bSRobert Mustacchi 				    dp->name);
14986716431bSRobert Mustacchi 			}
14996716431bSRobert Mustacchi 			/*
15006716431bSRobert Mustacchi 			 * it should be a result of pararell detection,
15016716431bSRobert Mustacchi 			 * which cannot detect duplex mode.
15026716431bSRobert Mustacchi 			 */
15036716431bSRobert Mustacchi 			if ((advert & lpable) == 0 &&
15046716431bSRobert Mustacchi 			    lpable & MII_ABILITY_10BASE_T) {
15056716431bSRobert Mustacchi 				/* no common technology, try 10M half mode */
15066716431bSRobert Mustacchi 				lpable |= advert & MII_ABILITY_10BASE_T;
15076716431bSRobert Mustacchi 				fix_phy = B_TRUE;
15086716431bSRobert Mustacchi 			}
15096716431bSRobert Mustacchi 		} else if (lpable == 0) {
15106716431bSRobert Mustacchi 			cmn_err(CE_WARN, "!%s: wrong lpable.", dp->name);
15116716431bSRobert Mustacchi 			goto reset_phy;
15126716431bSRobert Mustacchi 		}
15136716431bSRobert Mustacchi 		/*
15146716431bSRobert Mustacchi 		 * configure current link mode according to AN priority.
15156716431bSRobert Mustacchi 		 */
15166716431bSRobert Mustacchi 		val = advert & lpable;
15176716431bSRobert Mustacchi 		if ((ctl1000 & MII_1000TC_ADV_FULL) &&
15186716431bSRobert Mustacchi 		    (stat1000 & MII_1000TS_LP_FULL)) {
15196716431bSRobert Mustacchi 			/* 1000BaseT & full duplex */
15206716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_1000;
15216716431bSRobert Mustacchi 			dp->full_duplex = B_TRUE;
15226716431bSRobert Mustacchi 		} else if ((ctl1000 & MII_1000TC_ADV_HALF) &&
15236716431bSRobert Mustacchi 		    (stat1000 & MII_1000TS_LP_HALF)) {
15246716431bSRobert Mustacchi 			/* 1000BaseT & half duplex */
15256716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_1000;
15266716431bSRobert Mustacchi 			dp->full_duplex = B_FALSE;
15276716431bSRobert Mustacchi 		} else if ((val & MII_ABILITY_100BASE_TX_FD)) {
15286716431bSRobert Mustacchi 			/* 100BaseTx & fullduplex */
15296716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_100;
15306716431bSRobert Mustacchi 			dp->full_duplex = B_TRUE;
15316716431bSRobert Mustacchi 		} else if ((val & MII_ABILITY_100BASE_T4)) {
15326716431bSRobert Mustacchi 			/* 100BaseTx & fullduplex */
15336716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_100;
15346716431bSRobert Mustacchi 			dp->full_duplex = B_TRUE;
15356716431bSRobert Mustacchi 		} else if ((val & MII_ABILITY_100BASE_TX)) {
15366716431bSRobert Mustacchi 			/* 100BaseTx & half duplex */
15376716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_100;
15386716431bSRobert Mustacchi 			dp->full_duplex = B_FALSE;
15396716431bSRobert Mustacchi 		} else if ((val & MII_ABILITY_10BASE_T_FD)) {
15406716431bSRobert Mustacchi 			/* 10BaseT & full duplex */
15416716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_10;
15426716431bSRobert Mustacchi 			dp->full_duplex = B_TRUE;
15436716431bSRobert Mustacchi 		} else if ((val & MII_ABILITY_10BASE_T)) {
15446716431bSRobert Mustacchi 			/* 10BaseT & half duplex */
15456716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_10;
15466716431bSRobert Mustacchi 			dp->full_duplex = B_FALSE;
15476716431bSRobert Mustacchi 		} else {
15486716431bSRobert Mustacchi 			/*
15496716431bSRobert Mustacchi 			 * the link partner doesn't seem to have
15506716431bSRobert Mustacchi 			 * auto-negotiation capability and our PHY
15516716431bSRobert Mustacchi 			 * could not report current mode correctly.
15526716431bSRobert Mustacchi 			 * We guess current mode by mii_control register.
15536716431bSRobert Mustacchi 			 */
15546716431bSRobert Mustacchi 			val = usbgem_mii_read(dp, MII_CONTROL, &err);
15556716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
15566716431bSRobert Mustacchi 				goto usberr;
15576716431bSRobert Mustacchi 			}
15586716431bSRobert Mustacchi 
15596716431bSRobert Mustacchi 			/* select 100m half or 10m half */
15606716431bSRobert Mustacchi 			dp->speed = (val & MII_CONTROL_100MB) ?
15616716431bSRobert Mustacchi 			    USBGEM_SPD_100 : USBGEM_SPD_10;
15626716431bSRobert Mustacchi 			dp->full_duplex = B_FALSE;
15636716431bSRobert Mustacchi 			fix_phy = B_TRUE;
15646716431bSRobert Mustacchi 
15656716431bSRobert Mustacchi 			cmn_err(CE_NOTE,
15666716431bSRobert Mustacchi 			    "!%s: auto-negotiation done but "
15676716431bSRobert Mustacchi 			    "common ability not found.\n"
15686716431bSRobert Mustacchi 			    "PHY state: control:%b advert:%b lpable:%b\n"
15696716431bSRobert Mustacchi 			    "guessing %d Mbps %s duplex mode",
15706716431bSRobert Mustacchi 			    dp->name,
15716716431bSRobert Mustacchi 			    val, MII_CONTROL_BITS,
15726716431bSRobert Mustacchi 			    advert, MII_ABILITY_BITS,
15736716431bSRobert Mustacchi 			    lpable, MII_ABILITY_BITS,
15746716431bSRobert Mustacchi 			    usbgem_speed_value[dp->speed],
15756716431bSRobert Mustacchi 			    dp->full_duplex ? "full" : "half");
15766716431bSRobert Mustacchi 		}
15776716431bSRobert Mustacchi 
15786716431bSRobert Mustacchi 		if (dp->full_duplex) {
15796716431bSRobert Mustacchi 			dp->flow_control =
15806716431bSRobert Mustacchi 			    usbgem_fc_result[fc_cap_decode(advert)]
15816716431bSRobert Mustacchi 			    [fc_cap_decode(lpable)];
15826716431bSRobert Mustacchi 		} else {
15836716431bSRobert Mustacchi 			dp->flow_control = FLOW_CONTROL_NONE;
15846716431bSRobert Mustacchi 		}
15856716431bSRobert Mustacchi 		dp->mii_state = MII_STATE_MEDIA_SETUP;
15866716431bSRobert Mustacchi 		dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
15876716431bSRobert Mustacchi 		goto next_nowait;
15886716431bSRobert Mustacchi 
15896716431bSRobert Mustacchi 	case MII_STATE_MEDIA_SETUP:
15906716431bSRobert Mustacchi 		DPRINTF(2, (CE_CONT, "!%s: setup midia mode", dp->name));
15916716431bSRobert Mustacchi 
15926716431bSRobert Mustacchi 		/* assume the link state is down */
15936716431bSRobert Mustacchi 		dp->mii_state = MII_STATE_LINKDOWN;
15946716431bSRobert Mustacchi 		dp->mii_supress_msg = B_FALSE;
15956716431bSRobert Mustacchi 
15966716431bSRobert Mustacchi 		/* use short interval */
15976716431bSRobert Mustacchi 		dp->mii_interval = WATCH_INTERVAL_FAST;
15986716431bSRobert Mustacchi 
15996716431bSRobert Mustacchi 		if ((!dp->anadv_autoneg) ||
16006716431bSRobert Mustacchi 		    dp->ugc.usbgc_mii_an_oneshot || fix_phy) {
16016716431bSRobert Mustacchi 
16026716431bSRobert Mustacchi 			/*
16036716431bSRobert Mustacchi 			 * write the result of auto negotiation back.
16046716431bSRobert Mustacchi 			 */
16056716431bSRobert Mustacchi 			val = usbgem_mii_read(dp, MII_CONTROL, &err);
16066716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
16076716431bSRobert Mustacchi 				goto usberr;
16086716431bSRobert Mustacchi 			}
16096716431bSRobert Mustacchi 			val &= ~(MII_CONTROL_SPEED | MII_CONTROL_FDUPLEX |
16106716431bSRobert Mustacchi 			    MII_CONTROL_ANE   | MII_CONTROL_RSAN);
16116716431bSRobert Mustacchi 
16126716431bSRobert Mustacchi 			if (dp->full_duplex) {
16136716431bSRobert Mustacchi 				val |= MII_CONTROL_FDUPLEX;
16146716431bSRobert Mustacchi 			}
16156716431bSRobert Mustacchi 
16166716431bSRobert Mustacchi 			switch (dp->speed) {
16176716431bSRobert Mustacchi 			case USBGEM_SPD_1000:
16186716431bSRobert Mustacchi 				val |= MII_CONTROL_1000MB;
16196716431bSRobert Mustacchi 				break;
16206716431bSRobert Mustacchi 
16216716431bSRobert Mustacchi 			case USBGEM_SPD_100:
16226716431bSRobert Mustacchi 				val |= MII_CONTROL_100MB;
16236716431bSRobert Mustacchi 				break;
16246716431bSRobert Mustacchi 
16256716431bSRobert Mustacchi 			default:
16266716431bSRobert Mustacchi 				cmn_err(CE_WARN, "%s: unknown speed:%d",
16276716431bSRobert Mustacchi 				    dp->name, dp->speed);
16286716431bSRobert Mustacchi 				/* FALLTHROUGH */
16296716431bSRobert Mustacchi 
16306716431bSRobert Mustacchi 			case USBGEM_SPD_10:
16316716431bSRobert Mustacchi 				/* for USBGEM_SPD_10, do nothing */
16326716431bSRobert Mustacchi 				break;
16336716431bSRobert Mustacchi 			}
16346716431bSRobert Mustacchi 
16356716431bSRobert Mustacchi 			if (dp->mii_status & MII_STATUS_XSTATUS) {
16366716431bSRobert Mustacchi 				usbgem_mii_write(dp,
16376716431bSRobert Mustacchi 				    MII_1000TC, MII_1000TC_CFG_EN, &err);
16386716431bSRobert Mustacchi 				if (err != USB_SUCCESS) {
16396716431bSRobert Mustacchi 					goto usberr;
16406716431bSRobert Mustacchi 				}
16416716431bSRobert Mustacchi 			}
16426716431bSRobert Mustacchi 			usbgem_mii_write(dp, MII_CONTROL, val, &err);
16436716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
16446716431bSRobert Mustacchi 				goto usberr;
16456716431bSRobert Mustacchi 			}
16466716431bSRobert Mustacchi 		}
16476716431bSRobert Mustacchi 		/*
16486716431bSRobert Mustacchi 		 * XXX -- nic state should be one of
16496716431bSRobert Mustacchi 		 * NIC_STATE_DISCONNECTED
16506716431bSRobert Mustacchi 		 * NIC_STATE_STOPPED
16516716431bSRobert Mustacchi 		 * NIC_STATE_INITIALIZED
16526716431bSRobert Mustacchi 		 * NIC_STATE_ONLINE
16536716431bSRobert Mustacchi 		 */
16546716431bSRobert Mustacchi 		if (dp->nic_state >= NIC_STATE_INITIALIZED) {
16556716431bSRobert Mustacchi 			/* notify the result of autonegotiation to mac */
16566716431bSRobert Mustacchi 			if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
16576716431bSRobert Mustacchi 				goto usberr;
16586716431bSRobert Mustacchi 			}
16596716431bSRobert Mustacchi 		}
16606716431bSRobert Mustacchi 		goto next_nowait;
16616716431bSRobert Mustacchi 
16626716431bSRobert Mustacchi 	case MII_STATE_LINKDOWN:
16636716431bSRobert Mustacchi 		status = usbgem_mii_read(dp, MII_STATUS, &err);
16646716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
16656716431bSRobert Mustacchi 			goto usberr;
16666716431bSRobert Mustacchi 		}
16676716431bSRobert Mustacchi 		if (status & MII_STATUS_LINKUP) {
16686716431bSRobert Mustacchi 			/*
16696716431bSRobert Mustacchi 			 * Link is going up
16706716431bSRobert Mustacchi 			 */
16716716431bSRobert Mustacchi 			dp->mii_state = MII_STATE_LINKUP;
16726716431bSRobert Mustacchi 			dp->mii_supress_msg = B_FALSE;
16736716431bSRobert Mustacchi 
16746716431bSRobert Mustacchi 			DPRINTF(0, (CE_CONT,
16756716431bSRobert Mustacchi 			    "!%s: link up detected: status:%b",
16766716431bSRobert Mustacchi 			    dp->name, status, MII_STATUS_BITS));
16776716431bSRobert Mustacchi 
16786716431bSRobert Mustacchi 			/*
16796716431bSRobert Mustacchi 			 * MII_CONTROL_100MB and  MII_CONTROL_FDUPLEX are
16806716431bSRobert Mustacchi 			 * ignored when MII_CONTROL_ANE is set.
16816716431bSRobert Mustacchi 			 */
16826716431bSRobert Mustacchi 			cmn_err(CE_CONT,
16836716431bSRobert Mustacchi 			    "!%s: Link up: %d Mbps %s duplex %s flow control",
16846716431bSRobert Mustacchi 			    dp->name,
16856716431bSRobert Mustacchi 			    usbgem_speed_value[dp->speed],
16866716431bSRobert Mustacchi 			    dp->full_duplex ? "full" : "half",
16876716431bSRobert Mustacchi 			    usbgem_fc_type[dp->flow_control]);
16886716431bSRobert Mustacchi 
16896716431bSRobert Mustacchi 			dp->mii_interval =
16906716431bSRobert Mustacchi 			    dp->ugc.usbgc_mii_link_watch_interval;
16916716431bSRobert Mustacchi 
16926716431bSRobert Mustacchi 			if (dp->ugc.usbgc_mii_hw_link_detection &&
16936716431bSRobert Mustacchi 			    dp->nic_state == NIC_STATE_ONLINE) {
16946716431bSRobert Mustacchi 				dp->mii_interval = 0;
16956716431bSRobert Mustacchi 			}
16966716431bSRobert Mustacchi 
16976716431bSRobert Mustacchi 			if (dp->nic_state == NIC_STATE_ONLINE) {
16986716431bSRobert Mustacchi 				if (dp->mac_state == MAC_STATE_INITIALIZED) {
16996716431bSRobert Mustacchi 					(void) usbgem_mac_start(dp);
17006716431bSRobert Mustacchi 				}
17016716431bSRobert Mustacchi 				tx_sched = B_TRUE;
17026716431bSRobert Mustacchi 			}
17036716431bSRobert Mustacchi 
17046716431bSRobert Mustacchi 			goto next;
17056716431bSRobert Mustacchi 		}
17066716431bSRobert Mustacchi 
17076716431bSRobert Mustacchi 		dp->mii_supress_msg = B_TRUE;
17086716431bSRobert Mustacchi 		if (dp->anadv_autoneg) {
17096716431bSRobert Mustacchi 			dp->mii_timer -= diff;
17106716431bSRobert Mustacchi 			if (dp->mii_timer <= 0) {
17116716431bSRobert Mustacchi 				/*
17126716431bSRobert Mustacchi 				 * the link down timer expired.
17136716431bSRobert Mustacchi 				 * need to restart auto-negotiation.
17146716431bSRobert Mustacchi 				 */
17156716431bSRobert Mustacchi 				linkdown_action =
17166716431bSRobert Mustacchi 				    dp->ugc.usbgc_mii_linkdown_timeout_action;
17176716431bSRobert Mustacchi 				goto restart_autonego;
17186716431bSRobert Mustacchi 			}
17196716431bSRobert Mustacchi 		}
17206716431bSRobert Mustacchi 		/* don't change mii_state */
17216716431bSRobert Mustacchi 		goto next;
17226716431bSRobert Mustacchi 
17236716431bSRobert Mustacchi 	case MII_STATE_LINKUP:
17246716431bSRobert Mustacchi 		if (rwlock == RW_READER) {
17256716431bSRobert Mustacchi 			/* first pass, read mii status */
17266716431bSRobert Mustacchi 			status = usbgem_mii_read(dp, MII_STATUS, &err);
17276716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
17286716431bSRobert Mustacchi 				goto usberr;
17296716431bSRobert Mustacchi 			}
17306716431bSRobert Mustacchi 		}
17316716431bSRobert Mustacchi 		if ((status & MII_STATUS_LINKUP) == 0) {
17326716431bSRobert Mustacchi 			/*
17336716431bSRobert Mustacchi 			 * Link is going down
17346716431bSRobert Mustacchi 			 */
17356716431bSRobert Mustacchi 			cmn_err(CE_NOTE,
17366716431bSRobert Mustacchi 			    "!%s: link down detected: status:%b",
17376716431bSRobert Mustacchi 			    dp->name, status, MII_STATUS_BITS);
17386716431bSRobert Mustacchi 			/*
17396716431bSRobert Mustacchi 			 * Acquire exclusive lock to change mii_state
17406716431bSRobert Mustacchi 			 */
17416716431bSRobert Mustacchi 			if (rwlock == RW_READER) {
17426716431bSRobert Mustacchi 				rwlock = RW_WRITER;
17436716431bSRobert Mustacchi 				rw_exit(&dp->dev_state_lock);
17446716431bSRobert Mustacchi 				goto again;
17456716431bSRobert Mustacchi 			}
17466716431bSRobert Mustacchi 
17476716431bSRobert Mustacchi 			dp->mii_state = MII_STATE_LINKDOWN;
17486716431bSRobert Mustacchi 			dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
17496716431bSRobert Mustacchi 
17506716431bSRobert Mustacchi 			/*
17516716431bSRobert Mustacchi 			 * As we may change the state of the device,
17526716431bSRobert Mustacchi 			 * let us acquire exclusive lock for the state.
17536716431bSRobert Mustacchi 			 */
17546716431bSRobert Mustacchi 			if (dp->nic_state == NIC_STATE_ONLINE &&
17556716431bSRobert Mustacchi 			    dp->mac_state == MAC_STATE_ONLINE &&
17566716431bSRobert Mustacchi 			    dp->ugc.usbgc_mii_stop_mac_on_linkdown) {
17576716431bSRobert Mustacchi 				(void) usbgem_restart_nic(dp);
17586716431bSRobert Mustacchi 				/* drain tx */
17596716431bSRobert Mustacchi 				tx_sched = B_TRUE;
17606716431bSRobert Mustacchi 			}
17616716431bSRobert Mustacchi 
17626716431bSRobert Mustacchi 			if (dp->anadv_autoneg) {
17636716431bSRobert Mustacchi 				/* need to restart auto-negotiation */
17646716431bSRobert Mustacchi 				linkdown_action =
17656716431bSRobert Mustacchi 				    dp->ugc.usbgc_mii_linkdown_action;
17666716431bSRobert Mustacchi 				goto restart_autonego;
17676716431bSRobert Mustacchi 			}
17686716431bSRobert Mustacchi 			/*
17696716431bSRobert Mustacchi 			 * don't use hw link down detection until the link
17706716431bSRobert Mustacchi 			 * status become stable for a while.
17716716431bSRobert Mustacchi 			 */
17726716431bSRobert Mustacchi 			dp->mii_interval =
17736716431bSRobert Mustacchi 			    dp->ugc.usbgc_mii_link_watch_interval;
17746716431bSRobert Mustacchi 
17756716431bSRobert Mustacchi 			goto next;
17766716431bSRobert Mustacchi 		}
17776716431bSRobert Mustacchi 
17786716431bSRobert Mustacchi 		/*
17796716431bSRobert Mustacchi 		 * still link up, no need to change mii_state
17806716431bSRobert Mustacchi 		 */
17816716431bSRobert Mustacchi 		if (dp->ugc.usbgc_mii_hw_link_detection &&
17826716431bSRobert Mustacchi 		    dp->nic_state == NIC_STATE_ONLINE) {
17836716431bSRobert Mustacchi 			/*
17846716431bSRobert Mustacchi 			 * no need to check link status periodicly
17856716431bSRobert Mustacchi 			 * if nic can generate interrupts when link go down.
17866716431bSRobert Mustacchi 			 */
17876716431bSRobert Mustacchi 			dp->mii_interval = 0;
17886716431bSRobert Mustacchi 		}
17896716431bSRobert Mustacchi 		goto next;
17906716431bSRobert Mustacchi 	}
17916716431bSRobert Mustacchi 	/* NOTREACHED */
17926716431bSRobert Mustacchi 	cmn_err(CE_PANIC, "!%s: %s: not reached", dp->name, __func__);
17936716431bSRobert Mustacchi 
17946716431bSRobert Mustacchi 	/*
17956716431bSRobert Mustacchi 	 * Actions for new state.
17966716431bSRobert Mustacchi 	 */
17976716431bSRobert Mustacchi restart_autonego:
17986716431bSRobert Mustacchi 	switch (linkdown_action) {
17996716431bSRobert Mustacchi 	case MII_ACTION_RESET:
18006716431bSRobert Mustacchi 		if (!dp->mii_supress_msg) {
18016716431bSRobert Mustacchi 			cmn_err(CE_CONT, "!%s: resetting PHY", dp->name);
18026716431bSRobert Mustacchi 		}
18036716431bSRobert Mustacchi 		dp->mii_supress_msg = B_TRUE;
18046716431bSRobert Mustacchi 		goto reset_phy;
18056716431bSRobert Mustacchi 
18066716431bSRobert Mustacchi 	case MII_ACTION_NONE:
18076716431bSRobert Mustacchi 		dp->mii_supress_msg = B_TRUE;
18086716431bSRobert Mustacchi 		if (dp->ugc.usbgc_mii_an_oneshot) {
18096716431bSRobert Mustacchi 			goto autonego;
18106716431bSRobert Mustacchi 		}
18116716431bSRobert Mustacchi 		/* PHY will restart autonego automatically */
18126716431bSRobert Mustacchi 		dp->mii_state = MII_STATE_AUTONEGOTIATING;
18136716431bSRobert Mustacchi 		dp->mii_timer = dp->ugc.usbgc_mii_an_timeout;
18146716431bSRobert Mustacchi 		dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
18156716431bSRobert Mustacchi 		goto next;
18166716431bSRobert Mustacchi 
18176716431bSRobert Mustacchi 	case MII_ACTION_RSA:
18186716431bSRobert Mustacchi 		if (!dp->mii_supress_msg) {
18196716431bSRobert Mustacchi 			cmn_err(CE_CONT, "!%s: restarting auto-negotiation",
18206716431bSRobert Mustacchi 			    dp->name);
18216716431bSRobert Mustacchi 		}
18226716431bSRobert Mustacchi 		dp->mii_supress_msg = B_TRUE;
18236716431bSRobert Mustacchi 		goto autonego;
18246716431bSRobert Mustacchi 
18256716431bSRobert Mustacchi 	default:
18266716431bSRobert Mustacchi 		cmn_err(CE_PANIC, "!%s: unknowm linkdown action: %d",
18276716431bSRobert Mustacchi 		    dp->name, dp->ugc.usbgc_mii_linkdown_action);
18286716431bSRobert Mustacchi 	}
18296716431bSRobert Mustacchi 	/* NOTREACHED */
18306716431bSRobert Mustacchi 
18316716431bSRobert Mustacchi reset_phy:
18326716431bSRobert Mustacchi 	if (!dp->mii_supress_msg) {
18336716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: resetting PHY", dp->name);
18346716431bSRobert Mustacchi 	}
18356716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_RESETTING;
18366716431bSRobert Mustacchi 	dp->mii_timer = dp->ugc.usbgc_mii_reset_timeout;
18376716431bSRobert Mustacchi 	if (!dp->ugc.usbgc_mii_dont_reset) {
18386716431bSRobert Mustacchi 		usbgem_mii_write(dp, MII_CONTROL, MII_CONTROL_RESET, &err);
18396716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
18406716431bSRobert Mustacchi 			goto usberr;
18416716431bSRobert Mustacchi 		}
18426716431bSRobert Mustacchi 	}
18436716431bSRobert Mustacchi 	dp->mii_interval = WATCH_INTERVAL_FAST;
18446716431bSRobert Mustacchi 	goto next;
18456716431bSRobert Mustacchi 
18466716431bSRobert Mustacchi autonego:
18476716431bSRobert Mustacchi 	if (!dp->mii_supress_msg) {
18486716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: auto-negotiation started", dp->name);
18496716431bSRobert Mustacchi 	}
18506716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_AUTONEGOTIATING;
18516716431bSRobert Mustacchi 	dp->mii_timer = dp->ugc.usbgc_mii_an_timeout;
18526716431bSRobert Mustacchi 
18536716431bSRobert Mustacchi 	/* start/restart autoneg */
18546716431bSRobert Mustacchi 	val = usbgem_mii_read(dp, MII_CONTROL, &err) &
18556716431bSRobert Mustacchi 	    ~(MII_CONTROL_ISOLATE | MII_CONTROL_PWRDN | MII_CONTROL_RESET);
18566716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
18576716431bSRobert Mustacchi 		goto usberr;
18586716431bSRobert Mustacchi 	}
18596716431bSRobert Mustacchi 	if (val & MII_CONTROL_ANE) {
18606716431bSRobert Mustacchi 		val |= MII_CONTROL_RSAN;
18616716431bSRobert Mustacchi 	}
18626716431bSRobert Mustacchi 	usbgem_mii_write(dp, MII_CONTROL,
18636716431bSRobert Mustacchi 	    val | dp->ugc.usbgc_mii_an_cmd | MII_CONTROL_ANE, &err);
18646716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
18656716431bSRobert Mustacchi 		goto usberr;
18666716431bSRobert Mustacchi 	}
18676716431bSRobert Mustacchi 
18686716431bSRobert Mustacchi 	dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
18696716431bSRobert Mustacchi 	goto next;
18706716431bSRobert Mustacchi 
18716716431bSRobert Mustacchi usberr:
18726716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_UNKNOWN;
18736716431bSRobert Mustacchi 	dp->mii_interval = dp->ugc.usbgc_mii_link_watch_interval;
18746716431bSRobert Mustacchi 	tx_sched = B_TRUE;
18756716431bSRobert Mustacchi 
18766716431bSRobert Mustacchi next:
18776716431bSRobert Mustacchi 	*newstatep = dp->mii_state;
18786716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
18796716431bSRobert Mustacchi 	return (tx_sched);
18806716431bSRobert Mustacchi }
18816716431bSRobert Mustacchi 
18826716431bSRobert Mustacchi static void
usbgem_mii_link_watcher(struct usbgem_dev * dp)18836716431bSRobert Mustacchi usbgem_mii_link_watcher(struct usbgem_dev *dp)
18846716431bSRobert Mustacchi {
18856716431bSRobert Mustacchi 	int		old_mii_state;
18866716431bSRobert Mustacchi 	int		new_mii_state;
18876716431bSRobert Mustacchi 	boolean_t	tx_sched;
18886716431bSRobert Mustacchi 
18896716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
18906716431bSRobert Mustacchi 
18916716431bSRobert Mustacchi 	for (; ; ) {
18926716431bSRobert Mustacchi 
18936716431bSRobert Mustacchi 		mutex_enter(&dp->link_watcher_lock);
18946716431bSRobert Mustacchi 		if (dp->mii_interval) {
18956716431bSRobert Mustacchi 			(void) cv_timedwait(&dp->link_watcher_wait_cv,
18966716431bSRobert Mustacchi 			    &dp->link_watcher_lock,
18976716431bSRobert Mustacchi 			    dp->mii_interval + ddi_get_lbolt());
18986716431bSRobert Mustacchi 		} else {
18996716431bSRobert Mustacchi 			cv_wait(&dp->link_watcher_wait_cv,
19006716431bSRobert Mustacchi 			    &dp->link_watcher_lock);
19016716431bSRobert Mustacchi 		}
19026716431bSRobert Mustacchi 		mutex_exit(&dp->link_watcher_lock);
19036716431bSRobert Mustacchi 
19046716431bSRobert Mustacchi 		if (dp->link_watcher_stop) {
19056716431bSRobert Mustacchi 			break;
19066716431bSRobert Mustacchi 		}
19076716431bSRobert Mustacchi 
19086716431bSRobert Mustacchi 		/* we block callbacks from disconnect/suspend and restart */
19096716431bSRobert Mustacchi 		tx_sched = usbgem_mii_link_check(dp,
19106716431bSRobert Mustacchi 		    &old_mii_state, &new_mii_state);
19116716431bSRobert Mustacchi 
19126716431bSRobert Mustacchi 		/*
19136716431bSRobert Mustacchi 		 * gld v2 notifier functions are not able to
19146716431bSRobert Mustacchi 		 * be called with any locks in this layer.
19156716431bSRobert Mustacchi 		 */
19166716431bSRobert Mustacchi 		if (tx_sched) {
19176716431bSRobert Mustacchi 			/* kick potentially stopped downstream */
19186716431bSRobert Mustacchi 			mac_tx_update(dp->mh);
19196716431bSRobert Mustacchi 		}
19206716431bSRobert Mustacchi 
19216716431bSRobert Mustacchi 		if (old_mii_state != new_mii_state) {
19226716431bSRobert Mustacchi 			/* notify new mii link state */
19236716431bSRobert Mustacchi 			if (new_mii_state == MII_STATE_LINKUP) {
19246716431bSRobert Mustacchi 				dp->linkup_delay = 0;
19256716431bSRobert Mustacchi 				USBGEM_LINKUP(dp);
19266716431bSRobert Mustacchi 			} else if (dp->linkup_delay <= 0) {
19276716431bSRobert Mustacchi 				USBGEM_LINKDOWN(dp);
19286716431bSRobert Mustacchi 			}
19296716431bSRobert Mustacchi 		} else if (dp->linkup_delay < 0) {
19306716431bSRobert Mustacchi 			/* first linkup timeout */
19316716431bSRobert Mustacchi 			dp->linkup_delay = 0;
19326716431bSRobert Mustacchi 			USBGEM_LINKDOWN(dp);
19336716431bSRobert Mustacchi 		}
19346716431bSRobert Mustacchi 	}
19356716431bSRobert Mustacchi 
19366716431bSRobert Mustacchi 	thread_exit();
19376716431bSRobert Mustacchi }
19386716431bSRobert Mustacchi 
19396716431bSRobert Mustacchi void
usbgem_mii_update_link(struct usbgem_dev * dp)19406716431bSRobert Mustacchi usbgem_mii_update_link(struct usbgem_dev *dp)
19416716431bSRobert Mustacchi {
19426716431bSRobert Mustacchi 	cv_signal(&dp->link_watcher_wait_cv);
19436716431bSRobert Mustacchi }
19446716431bSRobert Mustacchi 
19456716431bSRobert Mustacchi int
usbgem_mii_probe_default(struct usbgem_dev * dp)19466716431bSRobert Mustacchi usbgem_mii_probe_default(struct usbgem_dev *dp)
19476716431bSRobert Mustacchi {
19486716431bSRobert Mustacchi 	int		phy;
19496716431bSRobert Mustacchi 	uint16_t	status;
19506716431bSRobert Mustacchi 	uint16_t	xstatus;
19516716431bSRobert Mustacchi 	int		err;
19526716431bSRobert Mustacchi 	uint16_t	adv;
19536716431bSRobert Mustacchi 	uint16_t	adv_org;
19546716431bSRobert Mustacchi 
19556716431bSRobert Mustacchi 	DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__));
19566716431bSRobert Mustacchi 
19576716431bSRobert Mustacchi 	/*
19586716431bSRobert Mustacchi 	 * Scan PHY
19596716431bSRobert Mustacchi 	 */
19606716431bSRobert Mustacchi 	dp->mii_status = 0;
19616716431bSRobert Mustacchi 
19626716431bSRobert Mustacchi 	/* Try default phy first */
19636716431bSRobert Mustacchi 	if (dp->mii_phy_addr) {
19646716431bSRobert Mustacchi 		status = usbgem_mii_read(dp, MII_STATUS, &err);
19656716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
19666716431bSRobert Mustacchi 			goto usberr;
19676716431bSRobert Mustacchi 		}
19686716431bSRobert Mustacchi 		if (status != 0xffff && status != 0x0000) {
19696716431bSRobert Mustacchi 			goto PHY_found;
19706716431bSRobert Mustacchi 		}
19716716431bSRobert Mustacchi 
19726716431bSRobert Mustacchi 		if (dp->mii_phy_addr < 0) {
19736716431bSRobert Mustacchi 			cmn_err(CE_NOTE,
19746716431bSRobert Mustacchi 		    "!%s: failed to probe default internal and/or non-MII PHY",
19756716431bSRobert Mustacchi 			    dp->name);
19766716431bSRobert Mustacchi 			return (USB_FAILURE);
19776716431bSRobert Mustacchi 		}
19786716431bSRobert Mustacchi 
19796716431bSRobert Mustacchi 		cmn_err(CE_NOTE,
19806716431bSRobert Mustacchi 		    "!%s: failed to probe default MII PHY at %d",
19816716431bSRobert Mustacchi 		    dp->name, dp->mii_phy_addr);
19826716431bSRobert Mustacchi 	}
19836716431bSRobert Mustacchi 
19846716431bSRobert Mustacchi 	/* Try all possible address */
19856716431bSRobert Mustacchi 	for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) {
19866716431bSRobert Mustacchi 		dp->mii_phy_addr = phy;
19876716431bSRobert Mustacchi 		status = usbgem_mii_read(dp, MII_STATUS, &err);
19886716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
19896716431bSRobert Mustacchi 			DPRINTF(0, (CE_CONT,
19906716431bSRobert Mustacchi 			    "!%s: %s: mii_read(status) failed",
19916716431bSRobert Mustacchi 			    dp->name, __func__));
19926716431bSRobert Mustacchi 			goto usberr;
19936716431bSRobert Mustacchi 		}
19946716431bSRobert Mustacchi 
19956716431bSRobert Mustacchi 		if (status != 0xffff && status != 0x0000) {
19966716431bSRobert Mustacchi 			usbgem_mii_write(dp, MII_CONTROL, 0, &err);
19976716431bSRobert Mustacchi 			if (err != USB_SUCCESS) {
19986716431bSRobert Mustacchi 				DPRINTF(0, (CE_CONT,
19996716431bSRobert Mustacchi 				    "!%s: %s: mii_write(control) failed",
20006716431bSRobert Mustacchi 				    dp->name, __func__));
20016716431bSRobert Mustacchi 				goto usberr;
20026716431bSRobert Mustacchi 			}
20036716431bSRobert Mustacchi 			goto PHY_found;
20046716431bSRobert Mustacchi 		}
20056716431bSRobert Mustacchi 	}
20066716431bSRobert Mustacchi 	for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) {
20076716431bSRobert Mustacchi 		dp->mii_phy_addr = phy;
20086716431bSRobert Mustacchi 		usbgem_mii_write(dp, MII_CONTROL, 0, &err);
20096716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
20106716431bSRobert Mustacchi 			DPRINTF(0, (CE_CONT,
20116716431bSRobert Mustacchi 			    "!%s: %s: mii_write(control) failed",
20126716431bSRobert Mustacchi 			    dp->name, __func__));
20136716431bSRobert Mustacchi 			goto usberr;
20146716431bSRobert Mustacchi 		}
20156716431bSRobert Mustacchi 		status = usbgem_mii_read(dp, MII_STATUS, &err);
20166716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
20176716431bSRobert Mustacchi 			DPRINTF(0, (CE_CONT,
20186716431bSRobert Mustacchi 			    "!%s: %s: mii_read(status) failed",
20196716431bSRobert Mustacchi 			    dp->name, __func__));
20206716431bSRobert Mustacchi 			goto usberr;
20216716431bSRobert Mustacchi 		}
20226716431bSRobert Mustacchi 
20236716431bSRobert Mustacchi 		if (status != 0xffff && status != 0) {
20246716431bSRobert Mustacchi 			goto PHY_found;
20256716431bSRobert Mustacchi 		}
20266716431bSRobert Mustacchi 	}
20276716431bSRobert Mustacchi 
20286716431bSRobert Mustacchi 	cmn_err(CE_NOTE, "!%s: no MII PHY found", dp->name);
20296716431bSRobert Mustacchi 	return (USB_FAILURE);
20306716431bSRobert Mustacchi 
20316716431bSRobert Mustacchi PHY_found:
20326716431bSRobert Mustacchi 	dp->mii_status = status;
20336716431bSRobert Mustacchi 	dp->mii_status_ro = ~status;
20346716431bSRobert Mustacchi 	dp->mii_phy_id = usbgem_mii_read(dp, MII_PHYIDH, &err) << 16;
20356716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
20366716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT,
20376716431bSRobert Mustacchi 		    "!%s: %s: mii_read(PHYIDH) failed",
20386716431bSRobert Mustacchi 		    dp->name, __func__));
20396716431bSRobert Mustacchi 		goto usberr;
20406716431bSRobert Mustacchi 	}
20416716431bSRobert Mustacchi 	dp->mii_phy_id |= usbgem_mii_read(dp, MII_PHYIDL, &err);
20426716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
20436716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT,
20446716431bSRobert Mustacchi 		    "!%s: %s: mii_read(PHYIDL) failed",
20456716431bSRobert Mustacchi 		    dp->name, __func__));
20466716431bSRobert Mustacchi 		goto usberr;
20476716431bSRobert Mustacchi 	}
20486716431bSRobert Mustacchi 
20496716431bSRobert Mustacchi 	if (dp->mii_phy_addr < 0) {
20506716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: using internal/non-MII PHY(0x%08x)",
20516716431bSRobert Mustacchi 		    dp->name, dp->mii_phy_id);
20526716431bSRobert Mustacchi 	} else {
20536716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: MII PHY (0x%08x) found at %d",
20546716431bSRobert Mustacchi 		    dp->name, dp->mii_phy_id, dp->mii_phy_addr);
20556716431bSRobert Mustacchi 	}
20566716431bSRobert Mustacchi 
20576716431bSRobert Mustacchi 	cmn_err(CE_CONT,
20586716431bSRobert Mustacchi 	    "!%s: PHY control:%b, status:%b, advert:%b, lpar:%b, exp:%b",
20596716431bSRobert Mustacchi 	    dp->name,
20606716431bSRobert Mustacchi 	    usbgem_mii_read(dp, MII_CONTROL, &err), MII_CONTROL_BITS,
20616716431bSRobert Mustacchi 	    status, MII_STATUS_BITS,
20626716431bSRobert Mustacchi 	    usbgem_mii_read(dp, MII_AN_ADVERT, &err), MII_ABILITY_BITS,
20636716431bSRobert Mustacchi 	    usbgem_mii_read(dp, MII_AN_LPABLE, &err), MII_ABILITY_BITS,
20646716431bSRobert Mustacchi 	    usbgem_mii_read(dp, MII_AN_EXPANSION, &err), MII_AN_EXP_BITS);
20656716431bSRobert Mustacchi 
20666716431bSRobert Mustacchi 	dp->mii_xstatus = 0;
20676716431bSRobert Mustacchi 	if (status & MII_STATUS_XSTATUS) {
20686716431bSRobert Mustacchi 		dp->mii_xstatus = usbgem_mii_read(dp, MII_XSTATUS, &err);
20696716431bSRobert Mustacchi 
20706716431bSRobert Mustacchi 		cmn_err(CE_CONT, "!%s: xstatus:%b",
20716716431bSRobert Mustacchi 		    dp->name, dp->mii_xstatus, MII_XSTATUS_BITS);
20726716431bSRobert Mustacchi 	}
20736716431bSRobert Mustacchi 	dp->mii_xstatus_ro = ~dp->mii_xstatus;
20746716431bSRobert Mustacchi 
20756716431bSRobert Mustacchi 	/* check if the phy can advertize pause abilities */
20766716431bSRobert Mustacchi 	adv_org = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
20776716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
20786716431bSRobert Mustacchi 		goto usberr;
20796716431bSRobert Mustacchi 	}
20806716431bSRobert Mustacchi 
20816716431bSRobert Mustacchi 	usbgem_mii_write(dp, MII_AN_ADVERT,
20826716431bSRobert Mustacchi 	    MII_ABILITY_PAUSE | MII_ABILITY_ASM_DIR, &err);
20836716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
20846716431bSRobert Mustacchi 		goto usberr;
20856716431bSRobert Mustacchi 	}
20866716431bSRobert Mustacchi 
20876716431bSRobert Mustacchi 	adv = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
20886716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
20896716431bSRobert Mustacchi 		goto usberr;
20906716431bSRobert Mustacchi 	}
20916716431bSRobert Mustacchi 
20926716431bSRobert Mustacchi 	if ((adv & MII_ABILITY_PAUSE) == 0) {
20936716431bSRobert Mustacchi 		dp->ugc.usbgc_flow_control &= ~1;
20946716431bSRobert Mustacchi 	}
20956716431bSRobert Mustacchi 
20966716431bSRobert Mustacchi 	if ((adv & MII_ABILITY_ASM_DIR) == 0) {
20976716431bSRobert Mustacchi 		dp->ugc.usbgc_flow_control &= ~2;
20986716431bSRobert Mustacchi 	}
20996716431bSRobert Mustacchi 
21006716431bSRobert Mustacchi 	usbgem_mii_write(dp, MII_AN_ADVERT, adv_org, &err);
21016716431bSRobert Mustacchi 	if (err != USB_SUCCESS) {
21026716431bSRobert Mustacchi 		goto usberr;
21036716431bSRobert Mustacchi 	}
21046716431bSRobert Mustacchi 	return (USB_SUCCESS);
21056716431bSRobert Mustacchi 
21066716431bSRobert Mustacchi usberr:
21076716431bSRobert Mustacchi 	return (USB_FAILURE);
21086716431bSRobert Mustacchi }
21096716431bSRobert Mustacchi 
21106716431bSRobert Mustacchi int
usbgem_mii_init_default(struct usbgem_dev * dp)21116716431bSRobert Mustacchi usbgem_mii_init_default(struct usbgem_dev *dp)
21126716431bSRobert Mustacchi {
21136716431bSRobert Mustacchi 	/* ENPTY */
21146716431bSRobert Mustacchi 	return (USB_SUCCESS);
21156716431bSRobert Mustacchi }
21166716431bSRobert Mustacchi 
21176716431bSRobert Mustacchi static int
usbgem_mii_start(struct usbgem_dev * dp)21186716431bSRobert Mustacchi usbgem_mii_start(struct usbgem_dev *dp)
21196716431bSRobert Mustacchi {
21206716431bSRobert Mustacchi 	int	err;
21216716431bSRobert Mustacchi 	kthread_t	*lwth;
21226716431bSRobert Mustacchi 
21236716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
21246716431bSRobert Mustacchi 
21256716431bSRobert Mustacchi 	/* make a first call of usbgem_mii_link_check() */
21266716431bSRobert Mustacchi 	dp->link_watcher_stop = 0;
21276716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_UNKNOWN;
21286716431bSRobert Mustacchi 	dp->mii_interval = drv_usectohz(1000*1000); /* 1sec */
21296716431bSRobert Mustacchi 	dp->mii_last_check = ddi_get_lbolt();
21306716431bSRobert Mustacchi 	dp->linkup_delay = 600 * drv_usectohz(1000*1000); /* 10 minutes */
21316716431bSRobert Mustacchi 
21326716431bSRobert Mustacchi 	lwth = thread_create(NULL, 0, usbgem_mii_link_watcher, dp, 0, &p0,
21336716431bSRobert Mustacchi 	    TS_RUN, minclsyspri);
21346716431bSRobert Mustacchi 	if (lwth == NULL) {
21356716431bSRobert Mustacchi 		cmn_err(CE_WARN,
21366716431bSRobert Mustacchi 		    "!%s: %s: failed to create a link watcher thread",
21376716431bSRobert Mustacchi 		    dp->name, __func__);
21386716431bSRobert Mustacchi 		return (USB_FAILURE);
21396716431bSRobert Mustacchi 	}
21406716431bSRobert Mustacchi 	dp->link_watcher_did = lwth->t_did;
21416716431bSRobert Mustacchi 
21426716431bSRobert Mustacchi 	return (USB_SUCCESS);
21436716431bSRobert Mustacchi }
21446716431bSRobert Mustacchi 
21456716431bSRobert Mustacchi static void
usbgem_mii_stop(struct usbgem_dev * dp)21466716431bSRobert Mustacchi usbgem_mii_stop(struct usbgem_dev *dp)
21476716431bSRobert Mustacchi {
21486716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
21496716431bSRobert Mustacchi 
21506716431bSRobert Mustacchi 	/* Ensure timer routine stopped */
21516716431bSRobert Mustacchi 	dp->link_watcher_stop = 1;
21526716431bSRobert Mustacchi 	cv_signal(&dp->link_watcher_wait_cv);
21536716431bSRobert Mustacchi 	thread_join(dp->link_watcher_did);
21546716431bSRobert Mustacchi }
21556716431bSRobert Mustacchi 
21566716431bSRobert Mustacchi /* ============================================================== */
21576716431bSRobert Mustacchi /*
21586716431bSRobert Mustacchi  * internal mac register operation interface
21596716431bSRobert Mustacchi  */
21606716431bSRobert Mustacchi /* ============================================================== */
21616716431bSRobert Mustacchi /*
21626716431bSRobert Mustacchi  * usbgem_mac_init: cold start
21636716431bSRobert Mustacchi  */
21646716431bSRobert Mustacchi static int
usbgem_mac_init(struct usbgem_dev * dp)21656716431bSRobert Mustacchi usbgem_mac_init(struct usbgem_dev *dp)
21666716431bSRobert Mustacchi {
21676716431bSRobert Mustacchi 	int	err;
21686716431bSRobert Mustacchi 
21696716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
21706716431bSRobert Mustacchi 
21716716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
21726716431bSRobert Mustacchi 		/* pretend we succeeded */
21736716431bSRobert Mustacchi 		return (USB_SUCCESS);
21746716431bSRobert Mustacchi 	}
21756716431bSRobert Mustacchi 
21766716431bSRobert Mustacchi 	ASSERT(dp->mac_state == MAC_STATE_STOPPED);
21776716431bSRobert Mustacchi 
21786716431bSRobert Mustacchi 	/* reset fatal error timestamp */
21796716431bSRobert Mustacchi 	dp->fatal_error = (clock_t)0;
21806716431bSRobert Mustacchi 
21816716431bSRobert Mustacchi 	/* reset tx side state */
21826716431bSRobert Mustacchi 	mutex_enter(&dp->txlock);
21836716431bSRobert Mustacchi 	dp->tx_busy_cnt = 0;
21846716431bSRobert Mustacchi 	dp->tx_max_packets = dp->ugc.usbgc_tx_list_max;
21856716431bSRobert Mustacchi 	mutex_exit(&dp->txlock);
21866716431bSRobert Mustacchi 
21876716431bSRobert Mustacchi 	/* reset rx side state */
21886716431bSRobert Mustacchi 	mutex_enter(&dp->rxlock);
21896716431bSRobert Mustacchi 	dp->rx_busy_cnt = 0;
21906716431bSRobert Mustacchi 	mutex_exit(&dp->rxlock);
21916716431bSRobert Mustacchi 
21926716431bSRobert Mustacchi 	err = usbgem_hal_init_chip(dp);
21936716431bSRobert Mustacchi 	if (err == USB_SUCCESS) {
21946716431bSRobert Mustacchi 		dp->mac_state = MAC_STATE_INITIALIZED;
21956716431bSRobert Mustacchi 	}
21966716431bSRobert Mustacchi 
21976716431bSRobert Mustacchi 	return (err);
21986716431bSRobert Mustacchi }
21996716431bSRobert Mustacchi 
22006716431bSRobert Mustacchi /*
22016716431bSRobert Mustacchi  * usbgem_mac_start: warm start
22026716431bSRobert Mustacchi  */
22036716431bSRobert Mustacchi static int
usbgem_mac_start(struct usbgem_dev * dp)22046716431bSRobert Mustacchi usbgem_mac_start(struct usbgem_dev *dp)
22056716431bSRobert Mustacchi {
22066716431bSRobert Mustacchi 	int	err;
22076716431bSRobert Mustacchi 	int	i;
22086716431bSRobert Mustacchi 	usb_flags_t	flags = 0;
22096716431bSRobert Mustacchi 	usb_intr_req_t	*req;
22106716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
22116716431bSRobert Mustacchi 	usb_pipe_state_t	p_state;
22126716431bSRobert Mustacchi #endif
22136716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
22146716431bSRobert Mustacchi 
22156716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
22166716431bSRobert Mustacchi 		/* do nothing but don't return failure */
22176716431bSRobert Mustacchi 		return (USB_SUCCESS);
22186716431bSRobert Mustacchi 	}
22196716431bSRobert Mustacchi 
22206716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_INITIALIZED) {
22216716431bSRobert Mustacchi 		/* don't return failer */
22226716431bSRobert Mustacchi 		DPRINTF(0, (CE_CONT,
22236716431bSRobert Mustacchi 		    "!%s: %s: mac_state(%d) is not MAC_STATE_INITIALIZED",
22246716431bSRobert Mustacchi 		    dp->name, __func__, dp->mac_state));
22256716431bSRobert Mustacchi 		goto x;
22266716431bSRobert Mustacchi 	}
22276716431bSRobert Mustacchi 
22286716431bSRobert Mustacchi 	dp->mac_state = MAC_STATE_ONLINE;
22296716431bSRobert Mustacchi 
22306716431bSRobert Mustacchi 	if (usbgem_hal_start_chip(dp) != USB_SUCCESS) {
22316716431bSRobert Mustacchi 		cmn_err(CE_NOTE,
22326716431bSRobert Mustacchi 		    "!%s: %s: usb error was detected during start_chip",
22336716431bSRobert Mustacchi 		    dp->name, __func__);
22346716431bSRobert Mustacchi 		goto x;
22356716431bSRobert Mustacchi 	}
22366716431bSRobert Mustacchi 
22376716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
22386716431bSRobert Mustacchi 	usb_pipe_get_state(dp->intr_pipe, &p_state, 0);
22396716431bSRobert Mustacchi 	ASSERT(p_state == USB_PIPE_STATE_IDLE);
22406716431bSRobert Mustacchi #endif /* USBGEM_DEBUG_LEVEL */
22416716431bSRobert Mustacchi 
22426716431bSRobert Mustacchi 	if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
22436716431bSRobert Mustacchi 
22446716431bSRobert Mustacchi 		/* make a request for interrupt */
22456716431bSRobert Mustacchi 
22466716431bSRobert Mustacchi 		req = usb_alloc_intr_req(dp->dip, 0, USB_FLAGS_SLEEP);
22476716431bSRobert Mustacchi 		if (req == NULL) {
22486716431bSRobert Mustacchi 			cmn_err(CE_WARN, "!%s: %s: failed to allocate intreq",
22496716431bSRobert Mustacchi 			    dp->name, __func__);
22506716431bSRobert Mustacchi 			goto x;
22516716431bSRobert Mustacchi 		}
22526716431bSRobert Mustacchi 		req->intr_data = NULL;
22536716431bSRobert Mustacchi 		req->intr_client_private = (usb_opaque_t)dp;
22546716431bSRobert Mustacchi 		req->intr_timeout = 0;
22556716431bSRobert Mustacchi 		req->intr_attributes =
22566716431bSRobert Mustacchi 		    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
22576716431bSRobert Mustacchi 		req->intr_len = dp->ep_intr->wMaxPacketSize;
22586716431bSRobert Mustacchi 		req->intr_cb = usbgem_intr_cb;
22596716431bSRobert Mustacchi 		req->intr_exc_cb = usbgem_intr_cb;
22606716431bSRobert Mustacchi 		req->intr_completion_reason = 0;
22616716431bSRobert Mustacchi 		req->intr_cb_flags = 0;
22626716431bSRobert Mustacchi 
22636716431bSRobert Mustacchi 		err = usb_pipe_intr_xfer(dp->intr_pipe, req, flags);
22646716431bSRobert Mustacchi 		if (err != USB_SUCCESS) {
22656716431bSRobert Mustacchi 			cmn_err(CE_WARN,
22666716431bSRobert Mustacchi 			    "%s: err:%d failed to start polling of intr pipe",
22676716431bSRobert Mustacchi 			    dp->name, err);
22686716431bSRobert Mustacchi 			goto x;
22696716431bSRobert Mustacchi 		}
22706716431bSRobert Mustacchi 	}
22716716431bSRobert Mustacchi 
22726716431bSRobert Mustacchi 	/* kick to receive the first packet */
22736716431bSRobert Mustacchi 	if (usbgem_init_rx_buf(dp) != USB_SUCCESS) {
22746716431bSRobert Mustacchi 		goto err_stop_intr;
22756716431bSRobert Mustacchi 	}
22766716431bSRobert Mustacchi 	dp->rx_active = B_TRUE;
22776716431bSRobert Mustacchi 
22786716431bSRobert Mustacchi 	return (USB_SUCCESS);
22796716431bSRobert Mustacchi 
22806716431bSRobert Mustacchi err_stop_intr:
22816716431bSRobert Mustacchi 	/* stop the interrupt pipe */
22826716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: FAULURE", dp->name, __func__));
22836716431bSRobert Mustacchi 	if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
22846716431bSRobert Mustacchi 		usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP);
22856716431bSRobert Mustacchi 	}
22866716431bSRobert Mustacchi x:
22876716431bSRobert Mustacchi 	ASSERT(dp->mac_state == MAC_STATE_ONLINE);
22886716431bSRobert Mustacchi 	/* we use another flag to indicate error state. */
22896716431bSRobert Mustacchi 	if (dp->fatal_error == (clock_t)0) {
22906716431bSRobert Mustacchi 		dp->fatal_error = usbgem_timestamp_nz();
22916716431bSRobert Mustacchi 	}
22926716431bSRobert Mustacchi 	return (USB_FAILURE);
22936716431bSRobert Mustacchi }
22946716431bSRobert Mustacchi 
22956716431bSRobert Mustacchi static int
usbgem_mac_stop(struct usbgem_dev * dp,int new_state,boolean_t graceful)22966716431bSRobert Mustacchi usbgem_mac_stop(struct usbgem_dev *dp, int new_state, boolean_t graceful)
22976716431bSRobert Mustacchi {
22986716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
22996716431bSRobert Mustacchi 
23006716431bSRobert Mustacchi 	/*
23016716431bSRobert Mustacchi 	 * we must have writer lock for dev_state_lock
23026716431bSRobert Mustacchi 	 */
23036716431bSRobert Mustacchi 	ASSERT(new_state == MAC_STATE_STOPPED ||
23046716431bSRobert Mustacchi 	    new_state == MAC_STATE_DISCONNECTED);
23056716431bSRobert Mustacchi 
23066716431bSRobert Mustacchi 	/* stop polling interrupt pipe */
23076716431bSRobert Mustacchi 	if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
23086716431bSRobert Mustacchi 		usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP);
23096716431bSRobert Mustacchi 	}
23106716431bSRobert Mustacchi 
23116716431bSRobert Mustacchi 	if (new_state == MAC_STATE_STOPPED || graceful) {
23126716431bSRobert Mustacchi 		/* stop the nic hardware completely */
23136716431bSRobert Mustacchi 		if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) {
23146716431bSRobert Mustacchi 			(void) usbgem_hal_reset_chip(dp);
23156716431bSRobert Mustacchi 		}
23166716431bSRobert Mustacchi 	}
23176716431bSRobert Mustacchi 
23186716431bSRobert Mustacchi 	/* stop preparing new rx packets and sending new packets */
23196716431bSRobert Mustacchi 	dp->mac_state = new_state;
23206716431bSRobert Mustacchi 
23216716431bSRobert Mustacchi 	/* other processors must get mac_state correctly after here */
23226716431bSRobert Mustacchi 	membar_producer();
23236716431bSRobert Mustacchi 
23246716431bSRobert Mustacchi 	/* cancel all requests we have sent */
23256716431bSRobert Mustacchi 	usb_pipe_reset(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
23266716431bSRobert Mustacchi 	usb_pipe_reset(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
23276716431bSRobert Mustacchi 
23286716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT,
23296716431bSRobert Mustacchi 	    "!%s: %s: rx_busy_cnt:%d tx_busy_cnt:%d",
23306716431bSRobert Mustacchi 	    dp->name, __func__, dp->rx_busy_cnt, dp->tx_busy_cnt));
23316716431bSRobert Mustacchi 
23326716431bSRobert Mustacchi 	/*
23336716431bSRobert Mustacchi 	 * Here all rx packets has been cancelled and their call back
23346716431bSRobert Mustacchi 	 * function has been exeuted, because we called usb_pipe_reset
23356716431bSRobert Mustacchi 	 * synchronously.
23366716431bSRobert Mustacchi 	 * So actually we just ensure rx_busy_cnt == 0.
23376716431bSRobert Mustacchi 	 */
23386716431bSRobert Mustacchi 	mutex_enter(&dp->rxlock);
23396716431bSRobert Mustacchi 	while (dp->rx_busy_cnt > 0) {
23406716431bSRobert Mustacchi 		cv_wait(&dp->rx_drain_cv, &dp->rxlock);
23416716431bSRobert Mustacchi 	}
23426716431bSRobert Mustacchi 	mutex_exit(&dp->rxlock);
23436716431bSRobert Mustacchi 
23446716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: rx_busy_cnt is %d now",
23456716431bSRobert Mustacchi 	    dp->name, __func__, dp->rx_busy_cnt));
23466716431bSRobert Mustacchi 
23476716431bSRobert Mustacchi 	mutex_enter(&dp->txlock);
23486716431bSRobert Mustacchi 	while (dp->tx_busy_cnt > 0) {
23496716431bSRobert Mustacchi 		cv_wait(&dp->tx_drain_cv, &dp->txlock);
23506716431bSRobert Mustacchi 	}
23516716431bSRobert Mustacchi 	mutex_exit(&dp->txlock);
23526716431bSRobert Mustacchi 
23536716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: tx_busy_cnt is %d now",
23546716431bSRobert Mustacchi 	    dp->name, __func__, dp->tx_busy_cnt));
23556716431bSRobert Mustacchi 
23566716431bSRobert Mustacchi 	return (USB_SUCCESS);
23576716431bSRobert Mustacchi }
23586716431bSRobert Mustacchi 
23596716431bSRobert Mustacchi static int
usbgem_add_multicast(struct usbgem_dev * dp,const uint8_t * ep)23606716431bSRobert Mustacchi usbgem_add_multicast(struct usbgem_dev *dp, const uint8_t *ep)
23616716431bSRobert Mustacchi {
23626716431bSRobert Mustacchi 	int	cnt;
23636716431bSRobert Mustacchi 	int	err;
23646716431bSRobert Mustacchi 
23656716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
23666716431bSRobert Mustacchi 
23676716431bSRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
23686716431bSRobert Mustacchi 	if (dp->mc_count_req++ < USBGEM_MAXMC) {
23696716431bSRobert Mustacchi 		/* append the new address at the end of the mclist */
23706716431bSRobert Mustacchi 		cnt = dp->mc_count;
23716716431bSRobert Mustacchi 		bcopy(ep, dp->mc_list[cnt].addr.ether_addr_octet,
23726716431bSRobert Mustacchi 		    ETHERADDRL);
23736716431bSRobert Mustacchi 		if (dp->ugc.usbgc_multicast_hash) {
23746716431bSRobert Mustacchi 			dp->mc_list[cnt].hash =
23756716431bSRobert Mustacchi 			    (*dp->ugc.usbgc_multicast_hash)(dp, ep);
23766716431bSRobert Mustacchi 		}
23776716431bSRobert Mustacchi 		dp->mc_count = cnt + 1;
23786716431bSRobert Mustacchi 	}
23796716431bSRobert Mustacchi 
23806716431bSRobert Mustacchi 	if (dp->mc_count_req != dp->mc_count) {
23816716431bSRobert Mustacchi 		/* multicast address list overflow */
23826716431bSRobert Mustacchi 		dp->rxmode |= RXMODE_MULTI_OVF;
23836716431bSRobert Mustacchi 	} else {
23846716431bSRobert Mustacchi 		dp->rxmode &= ~RXMODE_MULTI_OVF;
23856716431bSRobert Mustacchi 	}
23866716431bSRobert Mustacchi 
23876716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
23886716431bSRobert Mustacchi 		/* tell new multicast list to the hardware */
23896716431bSRobert Mustacchi 		err = usbgem_hal_set_rx_filter(dp);
23906716431bSRobert Mustacchi 	}
23916716431bSRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
23926716431bSRobert Mustacchi 
23936716431bSRobert Mustacchi 	return (err);
23946716431bSRobert Mustacchi }
23956716431bSRobert Mustacchi 
23966716431bSRobert Mustacchi static int
usbgem_remove_multicast(struct usbgem_dev * dp,const uint8_t * ep)23976716431bSRobert Mustacchi usbgem_remove_multicast(struct usbgem_dev *dp, const uint8_t *ep)
23986716431bSRobert Mustacchi {
23996716431bSRobert Mustacchi 	size_t		len;
24006716431bSRobert Mustacchi 	int		i;
24016716431bSRobert Mustacchi 	int		cnt;
24026716431bSRobert Mustacchi 	int		err;
24036716431bSRobert Mustacchi 
24046716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
24056716431bSRobert Mustacchi 
24066716431bSRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
24076716431bSRobert Mustacchi 	dp->mc_count_req--;
24086716431bSRobert Mustacchi 	cnt = dp->mc_count;
24096716431bSRobert Mustacchi 	for (i = 0; i < cnt; i++) {
24106716431bSRobert Mustacchi 		if (bcmp(ep, &dp->mc_list[i].addr, ETHERADDRL)) {
24116716431bSRobert Mustacchi 			continue;
24126716431bSRobert Mustacchi 		}
24136716431bSRobert Mustacchi 		/* shrink the mclist by copying forward */
24146716431bSRobert Mustacchi 		len = (cnt - (i + 1)) * sizeof (*dp->mc_list);
24156716431bSRobert Mustacchi 		if (len > 0) {
24166716431bSRobert Mustacchi 			bcopy(&dp->mc_list[i+1], &dp->mc_list[i], len);
24176716431bSRobert Mustacchi 		}
24186716431bSRobert Mustacchi 		dp->mc_count--;
24196716431bSRobert Mustacchi 		break;
24206716431bSRobert Mustacchi 	}
24216716431bSRobert Mustacchi 
24226716431bSRobert Mustacchi 	if (dp->mc_count_req != dp->mc_count) {
24236716431bSRobert Mustacchi 		/* multicast address list overflow */
24246716431bSRobert Mustacchi 		dp->rxmode |= RXMODE_MULTI_OVF;
24256716431bSRobert Mustacchi 	} else {
24266716431bSRobert Mustacchi 		dp->rxmode &= ~RXMODE_MULTI_OVF;
24276716431bSRobert Mustacchi 	}
24286716431bSRobert Mustacchi 
24296716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
24306716431bSRobert Mustacchi 		err = usbgem_hal_set_rx_filter(dp);
24316716431bSRobert Mustacchi 	}
24326716431bSRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
24336716431bSRobert Mustacchi 
24346716431bSRobert Mustacchi 	return (err);
24356716431bSRobert Mustacchi }
24366716431bSRobert Mustacchi 
24376716431bSRobert Mustacchi 
24386716431bSRobert Mustacchi /* ============================================================== */
24396716431bSRobert Mustacchi /*
24406716431bSRobert Mustacchi  * ioctl
24416716431bSRobert Mustacchi  */
24426716431bSRobert Mustacchi /* ============================================================== */
24436716431bSRobert Mustacchi enum ioc_reply {
24446716431bSRobert Mustacchi 	IOC_INVAL = -1,				/* bad, NAK with EINVAL	*/
24456716431bSRobert Mustacchi 	IOC_DONE,				/* OK, reply sent	*/
24466716431bSRobert Mustacchi 	IOC_ACK,				/* OK, just send ACK	*/
24476716431bSRobert Mustacchi 	IOC_REPLY,				/* OK, just send reply	*/
24486716431bSRobert Mustacchi 	IOC_RESTART_ACK,			/* OK, restart & ACK	*/
24496716431bSRobert Mustacchi 	IOC_RESTART_REPLY			/* OK, restart & reply	*/
24506716431bSRobert Mustacchi };
24516716431bSRobert Mustacchi 
24526716431bSRobert Mustacchi 
24536716431bSRobert Mustacchi static int
usbgem_get_def_val(struct usbgem_dev * dp,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)24546716431bSRobert Mustacchi usbgem_get_def_val(struct usbgem_dev *dp, mac_prop_id_t pr_num,
24556716431bSRobert Mustacchi     uint_t pr_valsize, void *pr_val)
24566716431bSRobert Mustacchi {
24576716431bSRobert Mustacchi 	link_flowctrl_t fl;
24586716431bSRobert Mustacchi 	int err = 0;
24596716431bSRobert Mustacchi 
24606716431bSRobert Mustacchi 	ASSERT(pr_valsize > 0);
24616716431bSRobert Mustacchi 	switch (pr_num) {
24626716431bSRobert Mustacchi 	case MAC_PROP_AUTONEG:
24636716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
24646716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
24656716431bSRobert Mustacchi 		break;
24666716431bSRobert Mustacchi 
24676716431bSRobert Mustacchi 	case MAC_PROP_FLOWCTRL:
24686716431bSRobert Mustacchi 		if (pr_valsize < sizeof (link_flowctrl_t)) {
24696716431bSRobert Mustacchi 			return (EINVAL);
24706716431bSRobert Mustacchi 		}
24716716431bSRobert Mustacchi 		switch (dp->ugc.usbgc_flow_control) {
24726716431bSRobert Mustacchi 		case FLOW_CONTROL_NONE:
24736716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_NONE;
24746716431bSRobert Mustacchi 			break;
24756716431bSRobert Mustacchi 		case FLOW_CONTROL_SYMMETRIC:
24766716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_BI;
24776716431bSRobert Mustacchi 			break;
24786716431bSRobert Mustacchi 		case FLOW_CONTROL_TX_PAUSE:
24796716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_TX;
24806716431bSRobert Mustacchi 			break;
24816716431bSRobert Mustacchi 		case FLOW_CONTROL_RX_PAUSE:
24826716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_RX;
24836716431bSRobert Mustacchi 			break;
24846716431bSRobert Mustacchi 		}
24856716431bSRobert Mustacchi 		bcopy(&fl, pr_val, sizeof (fl));
24866716431bSRobert Mustacchi 		break;
24876716431bSRobert Mustacchi 
24886716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000FDX_CAP:
24896716431bSRobert Mustacchi 	case MAC_PROP_EN_1000FDX_CAP:
24906716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
24916716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
24926716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
24936716431bSRobert Mustacchi 		break;
24946716431bSRobert Mustacchi 
24956716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000HDX_CAP:
24966716431bSRobert Mustacchi 	case MAC_PROP_EN_1000HDX_CAP:
24976716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
24986716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
24996716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
25006716431bSRobert Mustacchi 		break;
25016716431bSRobert Mustacchi 
25026716431bSRobert Mustacchi 	case MAC_PROP_ADV_100T4_CAP:
25036716431bSRobert Mustacchi 	case MAC_PROP_EN_100T4_CAP:
25046716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
25056716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
25066716431bSRobert Mustacchi 		break;
25076716431bSRobert Mustacchi 
25086716431bSRobert Mustacchi 	case MAC_PROP_ADV_100FDX_CAP:
25096716431bSRobert Mustacchi 	case MAC_PROP_EN_100FDX_CAP:
25106716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
25116716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
25126716431bSRobert Mustacchi 		break;
25136716431bSRobert Mustacchi 
25146716431bSRobert Mustacchi 	case MAC_PROP_ADV_100HDX_CAP:
25156716431bSRobert Mustacchi 	case MAC_PROP_EN_100HDX_CAP:
25166716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
25176716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
25186716431bSRobert Mustacchi 		break;
25196716431bSRobert Mustacchi 
25206716431bSRobert Mustacchi 	case MAC_PROP_ADV_10FDX_CAP:
25216716431bSRobert Mustacchi 	case MAC_PROP_EN_10FDX_CAP:
25226716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
25236716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
25246716431bSRobert Mustacchi 		break;
25256716431bSRobert Mustacchi 
25266716431bSRobert Mustacchi 	case MAC_PROP_ADV_10HDX_CAP:
25276716431bSRobert Mustacchi 	case MAC_PROP_EN_10HDX_CAP:
25286716431bSRobert Mustacchi 		*(uint8_t *)pr_val =
25296716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_status & MII_STATUS_10);
25306716431bSRobert Mustacchi 		break;
25316716431bSRobert Mustacchi 
25326716431bSRobert Mustacchi 	default:
25336716431bSRobert Mustacchi 		err = ENOTSUP;
25346716431bSRobert Mustacchi 		break;
25356716431bSRobert Mustacchi 	}
25366716431bSRobert Mustacchi 	return (err);
25376716431bSRobert Mustacchi }
25386716431bSRobert Mustacchi 
25396716431bSRobert Mustacchi static void
usbgem_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)25406716431bSRobert Mustacchi usbgem_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
25416716431bSRobert Mustacchi     mac_prop_info_handle_t prh)
25426716431bSRobert Mustacchi {
25436716431bSRobert Mustacchi 	struct usbgem_dev *dp = arg;
25446716431bSRobert Mustacchi 	link_flowctrl_t fl;
25456716431bSRobert Mustacchi 
25466716431bSRobert Mustacchi 	/*
25476716431bSRobert Mustacchi 	 * By default permissions are read/write unless specified
25486716431bSRobert Mustacchi 	 * otherwise by the driver.
25496716431bSRobert Mustacchi 	 */
25506716431bSRobert Mustacchi 
25516716431bSRobert Mustacchi 	switch (pr_num) {
25526716431bSRobert Mustacchi 	case MAC_PROP_DUPLEX:
25536716431bSRobert Mustacchi 	case MAC_PROP_SPEED:
25546716431bSRobert Mustacchi 	case MAC_PROP_STATUS:
25556716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000FDX_CAP:
25566716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000HDX_CAP:
25576716431bSRobert Mustacchi 	case MAC_PROP_ADV_100FDX_CAP:
25586716431bSRobert Mustacchi 	case MAC_PROP_ADV_100HDX_CAP:
25596716431bSRobert Mustacchi 	case MAC_PROP_ADV_10FDX_CAP:
25606716431bSRobert Mustacchi 	case MAC_PROP_ADV_10HDX_CAP:
25616716431bSRobert Mustacchi 	case MAC_PROP_ADV_100T4_CAP:
25626716431bSRobert Mustacchi 	case MAC_PROP_EN_100T4_CAP:
25636716431bSRobert Mustacchi 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
25646716431bSRobert Mustacchi 		break;
25656716431bSRobert Mustacchi 
25666716431bSRobert Mustacchi 	case MAC_PROP_EN_1000FDX_CAP:
25676716431bSRobert Mustacchi 		if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0) {
25686716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
25696716431bSRobert Mustacchi 			    BOOLEAN(
25706716431bSRobert Mustacchi 			    dp->mii_xstatus & MII_XSTATUS_1000BASET_FD));
25716716431bSRobert Mustacchi 		} else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD)
25726716431bSRobert Mustacchi 		    == 0) {
25736716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
25746716431bSRobert Mustacchi 			    BOOLEAN(
25756716431bSRobert Mustacchi 			    dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD));
25766716431bSRobert Mustacchi 		} else {
25776716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
25786716431bSRobert Mustacchi 		}
25796716431bSRobert Mustacchi 		break;
25806716431bSRobert Mustacchi 
25816716431bSRobert Mustacchi 	case MAC_PROP_EN_1000HDX_CAP:
25826716431bSRobert Mustacchi 		if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0) {
25836716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
25846716431bSRobert Mustacchi 			    BOOLEAN(
25856716431bSRobert Mustacchi 			    dp->mii_xstatus & MII_XSTATUS_1000BASET));
25866716431bSRobert Mustacchi 		} else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) {
25876716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
25886716431bSRobert Mustacchi 			    BOOLEAN(
25896716431bSRobert Mustacchi 			    dp->mii_xstatus & MII_XSTATUS_1000BASEX));
25906716431bSRobert Mustacchi 		} else {
25916716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
25926716431bSRobert Mustacchi 		}
25936716431bSRobert Mustacchi 		break;
25946716431bSRobert Mustacchi 
25956716431bSRobert Mustacchi 	case MAC_PROP_EN_100FDX_CAP:
25966716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) {
25976716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
25986716431bSRobert Mustacchi 			    BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD));
25996716431bSRobert Mustacchi 		} else {
26006716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
26016716431bSRobert Mustacchi 		}
26026716431bSRobert Mustacchi 		break;
26036716431bSRobert Mustacchi 
26046716431bSRobert Mustacchi 	case MAC_PROP_EN_100HDX_CAP:
26056716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) {
26066716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
26076716431bSRobert Mustacchi 			    BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX));
26086716431bSRobert Mustacchi 		} else {
26096716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
26106716431bSRobert Mustacchi 		}
26116716431bSRobert Mustacchi 		break;
26126716431bSRobert Mustacchi 
26136716431bSRobert Mustacchi 	case MAC_PROP_EN_10FDX_CAP:
26146716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
26156716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
26166716431bSRobert Mustacchi 			    BOOLEAN(dp->mii_status & MII_STATUS_10_FD));
26176716431bSRobert Mustacchi 		} else {
26186716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
26196716431bSRobert Mustacchi 		}
26206716431bSRobert Mustacchi 		break;
26216716431bSRobert Mustacchi 
26226716431bSRobert Mustacchi 	case MAC_PROP_EN_10HDX_CAP:
26236716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_10) == 0) {
26246716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
26256716431bSRobert Mustacchi 			    BOOLEAN(dp->mii_status & MII_STATUS_10));
26266716431bSRobert Mustacchi 		} else {
26276716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
26286716431bSRobert Mustacchi 		}
26296716431bSRobert Mustacchi 		break;
26306716431bSRobert Mustacchi 
26316716431bSRobert Mustacchi 	case MAC_PROP_AUTONEG:
26326716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) {
26336716431bSRobert Mustacchi 			mac_prop_info_set_default_uint8(prh,
26346716431bSRobert Mustacchi 			    BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG));
26356716431bSRobert Mustacchi 		} else {
26366716431bSRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
26376716431bSRobert Mustacchi 		}
26386716431bSRobert Mustacchi 		break;
26396716431bSRobert Mustacchi 
26406716431bSRobert Mustacchi 	case MAC_PROP_FLOWCTRL:
26416716431bSRobert Mustacchi 		switch (dp->ugc.usbgc_flow_control) {
26426716431bSRobert Mustacchi 		case FLOW_CONTROL_NONE:
26436716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_NONE;
26446716431bSRobert Mustacchi 			break;
26456716431bSRobert Mustacchi 		case FLOW_CONTROL_SYMMETRIC:
26466716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_BI;
26476716431bSRobert Mustacchi 			break;
26486716431bSRobert Mustacchi 		case FLOW_CONTROL_TX_PAUSE:
26496716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_TX;
26506716431bSRobert Mustacchi 			break;
26516716431bSRobert Mustacchi 		case FLOW_CONTROL_RX_PAUSE:
26526716431bSRobert Mustacchi 			fl = LINK_FLOWCTRL_RX;
26536716431bSRobert Mustacchi 			break;
26546716431bSRobert Mustacchi 		}
26556716431bSRobert Mustacchi 		mac_prop_info_set_default_link_flowctrl(prh, fl);
26566716431bSRobert Mustacchi 		break;
26576716431bSRobert Mustacchi 
26586716431bSRobert Mustacchi 	case MAC_PROP_MTU:
26596716431bSRobert Mustacchi 		mac_prop_info_set_range_uint32(prh,
26606716431bSRobert Mustacchi 		    dp->ugc.usbgc_min_mtu, dp->ugc.usbgc_max_mtu);
26616716431bSRobert Mustacchi 		break;
26626716431bSRobert Mustacchi 
26636716431bSRobert Mustacchi 	case MAC_PROP_PRIVATE:
26646716431bSRobert Mustacchi 		break;
26656716431bSRobert Mustacchi 	}
26666716431bSRobert Mustacchi }
26676716431bSRobert Mustacchi 
26686716431bSRobert Mustacchi static int
usbgem_m_setprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)26696716431bSRobert Mustacchi usbgem_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
26706716431bSRobert Mustacchi     uint_t pr_valsize, const void *pr_val)
26716716431bSRobert Mustacchi {
26726716431bSRobert Mustacchi 	struct usbgem_dev *dp = arg;
26736716431bSRobert Mustacchi 	int err = 0;
26746716431bSRobert Mustacchi 	boolean_t	update = B_FALSE;
26756716431bSRobert Mustacchi 	link_flowctrl_t flowctrl;
26766716431bSRobert Mustacchi 	uint32_t cur_mtu, new_mtu;
26776716431bSRobert Mustacchi 
26786716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
26796716431bSRobert Mustacchi 	switch (pr_num) {
26806716431bSRobert Mustacchi 	case MAC_PROP_EN_1000FDX_CAP:
26816716431bSRobert Mustacchi 		if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0 ||
26826716431bSRobert Mustacchi 		    (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD) == 0) {
26836716431bSRobert Mustacchi 			if (dp->anadv_1000fdx != *(uint8_t *)pr_val) {
26846716431bSRobert Mustacchi 				dp->anadv_1000fdx = *(uint8_t *)pr_val;
26856716431bSRobert Mustacchi 				update = B_TRUE;
26866716431bSRobert Mustacchi 			}
26876716431bSRobert Mustacchi 		} else {
26886716431bSRobert Mustacchi 			err = ENOTSUP;
26896716431bSRobert Mustacchi 		}
26906716431bSRobert Mustacchi 		break;
26916716431bSRobert Mustacchi 
26926716431bSRobert Mustacchi 	case MAC_PROP_EN_1000HDX_CAP:
26936716431bSRobert Mustacchi 		if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0 ||
26946716431bSRobert Mustacchi 		    (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) {
26956716431bSRobert Mustacchi 			if (dp->anadv_1000hdx != *(uint8_t *)pr_val) {
26966716431bSRobert Mustacchi 				dp->anadv_1000hdx = *(uint8_t *)pr_val;
26976716431bSRobert Mustacchi 				update = B_TRUE;
26986716431bSRobert Mustacchi 			}
26996716431bSRobert Mustacchi 		} else {
27006716431bSRobert Mustacchi 			err = ENOTSUP;
27016716431bSRobert Mustacchi 		}
27026716431bSRobert Mustacchi 		break;
27036716431bSRobert Mustacchi 
27046716431bSRobert Mustacchi 	case MAC_PROP_EN_100FDX_CAP:
27056716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) {
27066716431bSRobert Mustacchi 			if (dp->anadv_100fdx != *(uint8_t *)pr_val) {
27076716431bSRobert Mustacchi 				dp->anadv_100fdx = *(uint8_t *)pr_val;
27086716431bSRobert Mustacchi 				update = B_TRUE;
27096716431bSRobert Mustacchi 			}
27106716431bSRobert Mustacchi 		} else {
27116716431bSRobert Mustacchi 			err = ENOTSUP;
27126716431bSRobert Mustacchi 		}
27136716431bSRobert Mustacchi 		break;
27146716431bSRobert Mustacchi 
27156716431bSRobert Mustacchi 	case MAC_PROP_EN_100HDX_CAP:
27166716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) {
27176716431bSRobert Mustacchi 			if (dp->anadv_100hdx != *(uint8_t *)pr_val) {
27186716431bSRobert Mustacchi 				dp->anadv_100hdx = *(uint8_t *)pr_val;
27196716431bSRobert Mustacchi 				update = B_TRUE;
27206716431bSRobert Mustacchi 			}
27216716431bSRobert Mustacchi 		} else {
27226716431bSRobert Mustacchi 			err = ENOTSUP;
27236716431bSRobert Mustacchi 		}
27246716431bSRobert Mustacchi 		break;
27256716431bSRobert Mustacchi 
27266716431bSRobert Mustacchi 	case MAC_PROP_EN_10FDX_CAP:
27276716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
27286716431bSRobert Mustacchi 			if (dp->anadv_10fdx != *(uint8_t *)pr_val) {
27296716431bSRobert Mustacchi 				dp->anadv_10fdx = *(uint8_t *)pr_val;
27306716431bSRobert Mustacchi 				update = B_TRUE;
27316716431bSRobert Mustacchi 			}
27326716431bSRobert Mustacchi 		} else {
27336716431bSRobert Mustacchi 			err = ENOTSUP;
27346716431bSRobert Mustacchi 		}
27356716431bSRobert Mustacchi 		break;
27366716431bSRobert Mustacchi 
27376716431bSRobert Mustacchi 	case MAC_PROP_EN_10HDX_CAP:
27386716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
27396716431bSRobert Mustacchi 			if (dp->anadv_10hdx != *(uint8_t *)pr_val) {
27406716431bSRobert Mustacchi 				dp->anadv_10hdx = *(uint8_t *)pr_val;
27416716431bSRobert Mustacchi 				update = B_TRUE;
27426716431bSRobert Mustacchi 			}
27436716431bSRobert Mustacchi 		} else {
27446716431bSRobert Mustacchi 			err = ENOTSUP;
27456716431bSRobert Mustacchi 		}
27466716431bSRobert Mustacchi 		break;
27476716431bSRobert Mustacchi 
27486716431bSRobert Mustacchi 	case MAC_PROP_AUTONEG:
27496716431bSRobert Mustacchi 		if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) {
27506716431bSRobert Mustacchi 			if (dp->anadv_autoneg != *(uint8_t *)pr_val) {
27516716431bSRobert Mustacchi 				dp->anadv_autoneg = *(uint8_t *)pr_val;
27526716431bSRobert Mustacchi 				update = B_TRUE;
27536716431bSRobert Mustacchi 			}
27546716431bSRobert Mustacchi 		} else {
27556716431bSRobert Mustacchi 			err = ENOTSUP;
27566716431bSRobert Mustacchi 		}
27576716431bSRobert Mustacchi 		break;
27586716431bSRobert Mustacchi 
27596716431bSRobert Mustacchi 	case MAC_PROP_FLOWCTRL:
27606716431bSRobert Mustacchi 		bcopy(pr_val, &flowctrl, sizeof (flowctrl));
27616716431bSRobert Mustacchi 
27626716431bSRobert Mustacchi 		switch (flowctrl) {
27636716431bSRobert Mustacchi 		default:
27646716431bSRobert Mustacchi 			err = EINVAL;
27656716431bSRobert Mustacchi 			break;
27666716431bSRobert Mustacchi 
27676716431bSRobert Mustacchi 		case LINK_FLOWCTRL_NONE:
27686716431bSRobert Mustacchi 			if (dp->flow_control != FLOW_CONTROL_NONE) {
27696716431bSRobert Mustacchi 				dp->flow_control = FLOW_CONTROL_NONE;
27706716431bSRobert Mustacchi 				update = B_TRUE;
27716716431bSRobert Mustacchi 			}
27726716431bSRobert Mustacchi 			break;
27736716431bSRobert Mustacchi 
27746716431bSRobert Mustacchi 		case LINK_FLOWCTRL_RX:
27756716431bSRobert Mustacchi 			if (dp->flow_control != FLOW_CONTROL_RX_PAUSE) {
27766716431bSRobert Mustacchi 				dp->flow_control = FLOW_CONTROL_RX_PAUSE;
27776716431bSRobert Mustacchi 				update = B_TRUE;
27786716431bSRobert Mustacchi 			}
27796716431bSRobert Mustacchi 			break;
27806716431bSRobert Mustacchi 
27816716431bSRobert Mustacchi 		case LINK_FLOWCTRL_TX:
27826716431bSRobert Mustacchi 			if (dp->flow_control != FLOW_CONTROL_TX_PAUSE) {
27836716431bSRobert Mustacchi 				dp->flow_control = FLOW_CONTROL_TX_PAUSE;
27846716431bSRobert Mustacchi 				update = B_TRUE;
27856716431bSRobert Mustacchi 			}
27866716431bSRobert Mustacchi 			break;
27876716431bSRobert Mustacchi 
27886716431bSRobert Mustacchi 		case LINK_FLOWCTRL_BI:
27896716431bSRobert Mustacchi 			if (dp->flow_control != FLOW_CONTROL_SYMMETRIC) {
27906716431bSRobert Mustacchi 				dp->flow_control = FLOW_CONTROL_SYMMETRIC;
27916716431bSRobert Mustacchi 				update = B_TRUE;
27926716431bSRobert Mustacchi 			}
27936716431bSRobert Mustacchi 			break;
27946716431bSRobert Mustacchi 		}
27956716431bSRobert Mustacchi 		break;
27966716431bSRobert Mustacchi 
27976716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000FDX_CAP:
27986716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000HDX_CAP:
27996716431bSRobert Mustacchi 	case MAC_PROP_ADV_100FDX_CAP:
28006716431bSRobert Mustacchi 	case MAC_PROP_ADV_100HDX_CAP:
28016716431bSRobert Mustacchi 	case MAC_PROP_ADV_10FDX_CAP:
28026716431bSRobert Mustacchi 	case MAC_PROP_ADV_10HDX_CAP:
28036716431bSRobert Mustacchi 	case MAC_PROP_STATUS:
28046716431bSRobert Mustacchi 	case MAC_PROP_SPEED:
28056716431bSRobert Mustacchi 	case MAC_PROP_DUPLEX:
28066716431bSRobert Mustacchi 		err = ENOTSUP; /* read-only prop. Can't set this. */
28076716431bSRobert Mustacchi 		break;
28086716431bSRobert Mustacchi 
28096716431bSRobert Mustacchi 	case MAC_PROP_MTU:
28106716431bSRobert Mustacchi 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
28116716431bSRobert Mustacchi 		if (new_mtu != dp->mtu) {
28126716431bSRobert Mustacchi 			err = EINVAL;
28136716431bSRobert Mustacchi 		}
28146716431bSRobert Mustacchi 		break;
28156716431bSRobert Mustacchi 
28166716431bSRobert Mustacchi 	case MAC_PROP_PRIVATE:
28176716431bSRobert Mustacchi 		err = ENOTSUP;
28186716431bSRobert Mustacchi 		break;
28196716431bSRobert Mustacchi 
28206716431bSRobert Mustacchi 	default:
28216716431bSRobert Mustacchi 		err = ENOTSUP;
28226716431bSRobert Mustacchi 		break;
28236716431bSRobert Mustacchi 	}
28246716431bSRobert Mustacchi 
28256716431bSRobert Mustacchi 	if (update) {
28266716431bSRobert Mustacchi 		/* sync with PHY */
28276716431bSRobert Mustacchi 		usbgem_choose_forcedmode(dp);
28286716431bSRobert Mustacchi 		dp->mii_state = MII_STATE_UNKNOWN;
28296716431bSRobert Mustacchi 		cv_signal(&dp->link_watcher_wait_cv);
28306716431bSRobert Mustacchi 	}
28316716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
28326716431bSRobert Mustacchi 	return (err);
28336716431bSRobert Mustacchi }
28346716431bSRobert Mustacchi 
28356716431bSRobert Mustacchi static int
usbgem_m_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)28366716431bSRobert Mustacchi usbgem_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
28376716431bSRobert Mustacchi     uint_t pr_valsize, void *pr_val)
28386716431bSRobert Mustacchi {
28396716431bSRobert Mustacchi 	struct usbgem_dev *dp = arg;
28406716431bSRobert Mustacchi 	int err = 0;
28416716431bSRobert Mustacchi 	link_flowctrl_t flowctrl;
28426716431bSRobert Mustacchi 	uint64_t tmp = 0;
28436716431bSRobert Mustacchi 
28446716431bSRobert Mustacchi 	if (pr_valsize == 0) {
28456716431bSRobert Mustacchi 		return (EINVAL);
28466716431bSRobert Mustacchi 	}
2847ceb6b962SRobert Mustacchi 
28486716431bSRobert Mustacchi 	bzero(pr_val, pr_valsize);
28496716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
28506716431bSRobert Mustacchi 	switch (pr_num) {
28516716431bSRobert Mustacchi 	case MAC_PROP_DUPLEX:
28526716431bSRobert Mustacchi 		if (pr_valsize >= sizeof (link_duplex_t)) {
28536716431bSRobert Mustacchi 			if (dp->mii_state != MII_STATE_LINKUP) {
28546716431bSRobert Mustacchi 				*(link_duplex_t *)pr_val = LINK_DUPLEX_UNKNOWN;
28556716431bSRobert Mustacchi 			} else if (dp->full_duplex) {
28566716431bSRobert Mustacchi 				*(link_duplex_t *)pr_val = LINK_DUPLEX_FULL;
28576716431bSRobert Mustacchi 			} else {
28586716431bSRobert Mustacchi 				*(link_duplex_t *)pr_val = LINK_DUPLEX_HALF;
28596716431bSRobert Mustacchi 			}
28606716431bSRobert Mustacchi 		} else {
28616716431bSRobert Mustacchi 			err = EINVAL;
28626716431bSRobert Mustacchi 		}
28636716431bSRobert Mustacchi 		break;
28646716431bSRobert Mustacchi 	case MAC_PROP_SPEED:
28656716431bSRobert Mustacchi 		if (pr_valsize >= sizeof (uint64_t)) {
28666716431bSRobert Mustacchi 			switch (dp->speed) {
28676716431bSRobert Mustacchi 			case USBGEM_SPD_1000:
28686716431bSRobert Mustacchi 				tmp = 1000000000;
28696716431bSRobert Mustacchi 				break;
28706716431bSRobert Mustacchi 			case USBGEM_SPD_100:
28716716431bSRobert Mustacchi 				tmp = 100000000;
28726716431bSRobert Mustacchi 				break;
28736716431bSRobert Mustacchi 			case USBGEM_SPD_10:
28746716431bSRobert Mustacchi 				tmp = 10000000;
28756716431bSRobert Mustacchi 				break;
28766716431bSRobert Mustacchi 			default:
28776716431bSRobert Mustacchi 				tmp = 0;
28786716431bSRobert Mustacchi 			}
28796716431bSRobert Mustacchi 			bcopy(&tmp, pr_val, sizeof (tmp));
28806716431bSRobert Mustacchi 		} else {
28816716431bSRobert Mustacchi 			err = EINVAL;
28826716431bSRobert Mustacchi 		}
28836716431bSRobert Mustacchi 		break;
28846716431bSRobert Mustacchi 
28856716431bSRobert Mustacchi 	case MAC_PROP_AUTONEG:
28866716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_autoneg;
28876716431bSRobert Mustacchi 		break;
28886716431bSRobert Mustacchi 
28896716431bSRobert Mustacchi 	case MAC_PROP_FLOWCTRL:
28906716431bSRobert Mustacchi 		if (pr_valsize >= sizeof (link_flowctrl_t)) {
28916716431bSRobert Mustacchi 			switch (dp->flow_control) {
28926716431bSRobert Mustacchi 			case FLOW_CONTROL_NONE:
28936716431bSRobert Mustacchi 				flowctrl = LINK_FLOWCTRL_NONE;
28946716431bSRobert Mustacchi 				break;
28956716431bSRobert Mustacchi 			case FLOW_CONTROL_RX_PAUSE:
28966716431bSRobert Mustacchi 				flowctrl = LINK_FLOWCTRL_RX;
28976716431bSRobert Mustacchi 				break;
28986716431bSRobert Mustacchi 			case FLOW_CONTROL_TX_PAUSE:
28996716431bSRobert Mustacchi 				flowctrl = LINK_FLOWCTRL_TX;
29006716431bSRobert Mustacchi 				break;
29016716431bSRobert Mustacchi 			case FLOW_CONTROL_SYMMETRIC:
29026716431bSRobert Mustacchi 				flowctrl = LINK_FLOWCTRL_BI;
29036716431bSRobert Mustacchi 				break;
29046716431bSRobert Mustacchi 			}
29056716431bSRobert Mustacchi 			bcopy(&flowctrl, pr_val, sizeof (flowctrl));
29066716431bSRobert Mustacchi 		} else {
29076716431bSRobert Mustacchi 			err = EINVAL;
29086716431bSRobert Mustacchi 		}
29096716431bSRobert Mustacchi 		break;
29106716431bSRobert Mustacchi 
29116716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000FDX_CAP:
29126716431bSRobert Mustacchi 	case MAC_PROP_ADV_1000HDX_CAP:
29136716431bSRobert Mustacchi 	case MAC_PROP_ADV_100FDX_CAP:
29146716431bSRobert Mustacchi 	case MAC_PROP_ADV_100HDX_CAP:
29156716431bSRobert Mustacchi 	case MAC_PROP_ADV_10FDX_CAP:
29166716431bSRobert Mustacchi 	case MAC_PROP_ADV_10HDX_CAP:
29176716431bSRobert Mustacchi 	case MAC_PROP_ADV_100T4_CAP:
29186716431bSRobert Mustacchi 		usbgem_get_def_val(dp, pr_num, pr_valsize, pr_val);
29196716431bSRobert Mustacchi 		break;
29206716431bSRobert Mustacchi 
29216716431bSRobert Mustacchi 	case MAC_PROP_EN_1000FDX_CAP:
29226716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_1000fdx;
29236716431bSRobert Mustacchi 		break;
29246716431bSRobert Mustacchi 
29256716431bSRobert Mustacchi 	case MAC_PROP_EN_1000HDX_CAP:
29266716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_1000hdx;
29276716431bSRobert Mustacchi 		break;
29286716431bSRobert Mustacchi 
29296716431bSRobert Mustacchi 	case MAC_PROP_EN_100FDX_CAP:
29306716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_100fdx;
29316716431bSRobert Mustacchi 		break;
29326716431bSRobert Mustacchi 
29336716431bSRobert Mustacchi 	case MAC_PROP_EN_100HDX_CAP:
29346716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_100hdx;
29356716431bSRobert Mustacchi 		break;
29366716431bSRobert Mustacchi 
29376716431bSRobert Mustacchi 	case MAC_PROP_EN_10FDX_CAP:
29386716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_10fdx;
29396716431bSRobert Mustacchi 		break;
29406716431bSRobert Mustacchi 
29416716431bSRobert Mustacchi 	case MAC_PROP_EN_10HDX_CAP:
29426716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_10hdx;
29436716431bSRobert Mustacchi 		break;
29446716431bSRobert Mustacchi 
29456716431bSRobert Mustacchi 	case MAC_PROP_EN_100T4_CAP:
29466716431bSRobert Mustacchi 		*(uint8_t *)pr_val = dp->anadv_100t4;
29476716431bSRobert Mustacchi 		break;
29486716431bSRobert Mustacchi 
29496716431bSRobert Mustacchi 	case MAC_PROP_PRIVATE:
29506716431bSRobert Mustacchi 		err = ENOTSUP;
29516716431bSRobert Mustacchi 		break;
29526716431bSRobert Mustacchi 
29536716431bSRobert Mustacchi 	default:
29546716431bSRobert Mustacchi 		err = ENOTSUP;
29556716431bSRobert Mustacchi 		break;
29566716431bSRobert Mustacchi 	}
29576716431bSRobert Mustacchi 
29586716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
29596716431bSRobert Mustacchi 	return (err);
29606716431bSRobert Mustacchi }
29616716431bSRobert Mustacchi 
2962ceb6b962SRobert Mustacchi static void
usbgem_mac_ioctl(struct usbgem_dev * dp,queue_t * wq,mblk_t * mp)2963ceb6b962SRobert Mustacchi usbgem_mac_ioctl(struct usbgem_dev *dp, queue_t *wq, mblk_t *mp)
2964ceb6b962SRobert Mustacchi {
2965ceb6b962SRobert Mustacchi 	struct iocblk	*iocp;
2966ceb6b962SRobert Mustacchi 	enum ioc_reply	status;
2967ceb6b962SRobert Mustacchi 
2968ceb6b962SRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
2969ceb6b962SRobert Mustacchi 
2970ceb6b962SRobert Mustacchi 	/*
2971ceb6b962SRobert Mustacchi 	 * Validate the command before bothering with the mutex ...
2972ceb6b962SRobert Mustacchi 	 */
2973ceb6b962SRobert Mustacchi 	iocp = (void *)mp->b_rptr;
2974ceb6b962SRobert Mustacchi 	iocp->ioc_error = 0;
2975ceb6b962SRobert Mustacchi 
2976ceb6b962SRobert Mustacchi 	DPRINTF(1, (CE_CONT, "%s: %s cmd:0x%x", dp->name, __func__,
2977ceb6b962SRobert Mustacchi 	    iocp->ioc_cmd));
2978ceb6b962SRobert Mustacchi 
2979ceb6b962SRobert Mustacchi 	miocnak(wq, mp, 0, EINVAL);
2980ceb6b962SRobert Mustacchi }
2981ceb6b962SRobert Mustacchi 
2982ceb6b962SRobert Mustacchi static int
usbgem_mac_xcvr_inuse(struct usbgem_dev * dp)2983ceb6b962SRobert Mustacchi usbgem_mac_xcvr_inuse(struct usbgem_dev *dp)
2984ceb6b962SRobert Mustacchi {
2985ceb6b962SRobert Mustacchi 	int	val = XCVR_UNDEFINED;
2986ceb6b962SRobert Mustacchi 
2987ceb6b962SRobert Mustacchi 	if ((dp->mii_status & MII_STATUS_XSTATUS) == 0) {
2988ceb6b962SRobert Mustacchi 		if (dp->mii_status & MII_STATUS_100_BASE_T4) {
2989ceb6b962SRobert Mustacchi 			val = XCVR_100T4;
2990ceb6b962SRobert Mustacchi 		} else if (dp->mii_status &
2991ceb6b962SRobert Mustacchi 		    (MII_STATUS_100_BASEX_FD |
2992ceb6b962SRobert Mustacchi 		    MII_STATUS_100_BASEX)) {
2993ceb6b962SRobert Mustacchi 			val = XCVR_100X;
2994ceb6b962SRobert Mustacchi 		} else if (dp->mii_status &
2995ceb6b962SRobert Mustacchi 		    (MII_STATUS_100_BASE_T2_FD |
2996ceb6b962SRobert Mustacchi 		    MII_STATUS_100_BASE_T2)) {
2997ceb6b962SRobert Mustacchi 			val = XCVR_100T2;
2998ceb6b962SRobert Mustacchi 		} else if (dp->mii_status &
2999ceb6b962SRobert Mustacchi 		    (MII_STATUS_10_FD | MII_STATUS_10)) {
3000ceb6b962SRobert Mustacchi 			val = XCVR_10;
3001ceb6b962SRobert Mustacchi 		}
3002ceb6b962SRobert Mustacchi 	} else if (dp->mii_xstatus &
3003ceb6b962SRobert Mustacchi 	    (MII_XSTATUS_1000BASET_FD | MII_XSTATUS_1000BASET)) {
3004ceb6b962SRobert Mustacchi 		val = XCVR_1000T;
3005ceb6b962SRobert Mustacchi 	} else if (dp->mii_xstatus &
3006ceb6b962SRobert Mustacchi 	    (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASEX)) {
3007ceb6b962SRobert Mustacchi 		val = XCVR_1000X;
3008ceb6b962SRobert Mustacchi 	}
3009ceb6b962SRobert Mustacchi 
3010ceb6b962SRobert Mustacchi 	return (val);
3011ceb6b962SRobert Mustacchi }
3012ceb6b962SRobert Mustacchi 
30136716431bSRobert Mustacchi /* ============================================================== */
30146716431bSRobert Mustacchi /*
3015ceb6b962SRobert Mustacchi  * GLDv3 interface
30166716431bSRobert Mustacchi  */
30176716431bSRobert Mustacchi /* ============================================================== */
3018ceb6b962SRobert Mustacchi static int	usbgem_m_getstat(void *, uint_t, uint64_t *);
3019ceb6b962SRobert Mustacchi static int	usbgem_m_start(void *);
3020ceb6b962SRobert Mustacchi static void	usbgem_m_stop(void *);
3021ceb6b962SRobert Mustacchi static int	usbgem_m_setpromisc(void *, boolean_t);
3022ceb6b962SRobert Mustacchi static int	usbgem_m_multicst(void *, boolean_t, const uint8_t *);
3023ceb6b962SRobert Mustacchi static int	usbgem_m_unicst(void *, const uint8_t *);
3024ceb6b962SRobert Mustacchi static mblk_t	*usbgem_m_tx(void *, mblk_t *);
3025ceb6b962SRobert Mustacchi static void	usbgem_m_ioctl(void *, queue_t *, mblk_t *);
3026ceb6b962SRobert Mustacchi static int	usbgem_m_setprop(void *, const char *, mac_prop_id_t,
3027ceb6b962SRobert Mustacchi     uint_t, const void *);
3028ceb6b962SRobert Mustacchi static int	usbgem_m_getprop(void *, const char *, mac_prop_id_t,
3029ceb6b962SRobert Mustacchi     uint_t, void *);
30306716431bSRobert Mustacchi 
3031ceb6b962SRobert Mustacchi static mac_callbacks_t gem_m_callbacks = {
3032ceb6b962SRobert Mustacchi 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
3033ceb6b962SRobert Mustacchi 	usbgem_m_getstat,
3034ceb6b962SRobert Mustacchi 	usbgem_m_start,
3035ceb6b962SRobert Mustacchi 	usbgem_m_stop,
3036ceb6b962SRobert Mustacchi 	usbgem_m_setpromisc,
3037ceb6b962SRobert Mustacchi 	usbgem_m_multicst,
3038ceb6b962SRobert Mustacchi 	usbgem_m_unicst,
3039ceb6b962SRobert Mustacchi 	usbgem_m_tx,
3040ceb6b962SRobert Mustacchi 	NULL,
3041ceb6b962SRobert Mustacchi 	usbgem_m_ioctl,
3042ceb6b962SRobert Mustacchi 	NULL, /* m_getcapab */
3043ceb6b962SRobert Mustacchi 	NULL,
3044ceb6b962SRobert Mustacchi 	NULL,
3045ceb6b962SRobert Mustacchi 	usbgem_m_setprop,
3046ceb6b962SRobert Mustacchi 	usbgem_m_getprop,
3047ceb6b962SRobert Mustacchi 	usbgem_m_propinfo,
30486716431bSRobert Mustacchi };
30496716431bSRobert Mustacchi 
30506716431bSRobert Mustacchi static int
usbgem_m_start(void * arg)3051ceb6b962SRobert Mustacchi usbgem_m_start(void *arg)
30526716431bSRobert Mustacchi {
3053ceb6b962SRobert Mustacchi 	int	ret;
3054ceb6b962SRobert Mustacchi 	int	err;
3055ceb6b962SRobert Mustacchi 	struct usbgem_dev *dp = arg;
30566716431bSRobert Mustacchi 
3057ceb6b962SRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
30586716431bSRobert Mustacchi 
3059ceb6b962SRobert Mustacchi 	err = EIO;
30606716431bSRobert Mustacchi 
3061ceb6b962SRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
3062ceb6b962SRobert Mustacchi 	dp->nic_state = NIC_STATE_ONLINE;
30636716431bSRobert Mustacchi 
3064ceb6b962SRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
3065ceb6b962SRobert Mustacchi 		err = 0;
3066ceb6b962SRobert Mustacchi 		goto x;
3067ceb6b962SRobert Mustacchi 	}
3068ceb6b962SRobert Mustacchi 	if (usbgem_mac_init(dp) != USB_SUCCESS) {
3069ceb6b962SRobert Mustacchi 		goto x;
3070ceb6b962SRobert Mustacchi 	}
30716716431bSRobert Mustacchi 
3072ceb6b962SRobert Mustacchi 	/* initialize rx filter state */
3073ceb6b962SRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
3074ceb6b962SRobert Mustacchi 	dp->mc_count = 0;
3075ceb6b962SRobert Mustacchi 	dp->mc_count_req = 0;
30766716431bSRobert Mustacchi 
3077ceb6b962SRobert Mustacchi 	bcopy(dp->dev_addr.ether_addr_octet,
3078ceb6b962SRobert Mustacchi 	    dp->cur_addr.ether_addr_octet, ETHERADDRL);
3079ceb6b962SRobert Mustacchi 	dp->rxmode |= RXMODE_ENABLE;
30806716431bSRobert Mustacchi 
3081ceb6b962SRobert Mustacchi 	ret = usbgem_hal_set_rx_filter(dp);
3082ceb6b962SRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
30836716431bSRobert Mustacchi 
3084ceb6b962SRobert Mustacchi 	if (ret != USB_SUCCESS) {
3085ceb6b962SRobert Mustacchi 		goto x;
3086ceb6b962SRobert Mustacchi 	}
30876716431bSRobert Mustacchi 
3088ceb6b962SRobert Mustacchi 	if (dp->mii_state == MII_STATE_LINKUP) {
3089ceb6b962SRobert Mustacchi 		/* setup media mode if the link have been up */
3090ceb6b962SRobert Mustacchi 		if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
3091ceb6b962SRobert Mustacchi 			goto x;
3092ceb6b962SRobert Mustacchi 		}
3093ceb6b962SRobert Mustacchi 		if (usbgem_mac_start(dp) != USB_SUCCESS) {
3094ceb6b962SRobert Mustacchi 			goto x;
3095ceb6b962SRobert Mustacchi 		}
3096ceb6b962SRobert Mustacchi 	}
30976716431bSRobert Mustacchi 
3098ceb6b962SRobert Mustacchi 	err = 0;
3099ceb6b962SRobert Mustacchi x:
3100ceb6b962SRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
3101ceb6b962SRobert Mustacchi 	return (err);
3102ceb6b962SRobert Mustacchi }
31036716431bSRobert Mustacchi 
3104ceb6b962SRobert Mustacchi static void
usbgem_m_stop(void * arg)3105ceb6b962SRobert Mustacchi usbgem_m_stop(void *arg)
3106ceb6b962SRobert Mustacchi {
3107ceb6b962SRobert Mustacchi 	struct usbgem_dev	*dp = arg;
31086716431bSRobert Mustacchi 
3109ceb6b962SRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
31106716431bSRobert Mustacchi 
3111ceb6b962SRobert Mustacchi 	/* stop rx gracefully */
3112ceb6b962SRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
3113ceb6b962SRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
3114ceb6b962SRobert Mustacchi 	dp->rxmode &= ~RXMODE_ENABLE;
31156716431bSRobert Mustacchi 
3116ceb6b962SRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
3117ceb6b962SRobert Mustacchi 		(void) usbgem_hal_set_rx_filter(dp);
3118ceb6b962SRobert Mustacchi 	}
3119ceb6b962SRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
3120ceb6b962SRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
31216716431bSRobert Mustacchi 
3122ceb6b962SRobert Mustacchi 	/* make the nic state inactive */
3123ceb6b962SRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
3124ceb6b962SRobert Mustacchi 	dp->nic_state = NIC_STATE_STOPPED;
31256716431bSRobert Mustacchi 
31266716431bSRobert Mustacchi 	/* stop mac completely */
31276716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
31286716431bSRobert Mustacchi 		(void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL);
31296716431bSRobert Mustacchi 	}
31306716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
31316716431bSRobert Mustacchi }
31326716431bSRobert Mustacchi 
31336716431bSRobert Mustacchi static int
usbgem_m_multicst(void * arg,boolean_t add,const uint8_t * ep)31346716431bSRobert Mustacchi usbgem_m_multicst(void *arg, boolean_t add, const uint8_t *ep)
31356716431bSRobert Mustacchi {
31366716431bSRobert Mustacchi 	int	err;
31376716431bSRobert Mustacchi 	int	ret;
31386716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
31396716431bSRobert Mustacchi 
31406716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
31416716431bSRobert Mustacchi 
31426716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
31436716431bSRobert Mustacchi 	if (add) {
31446716431bSRobert Mustacchi 		ret = usbgem_add_multicast(dp, ep);
31456716431bSRobert Mustacchi 	} else {
31466716431bSRobert Mustacchi 		ret = usbgem_remove_multicast(dp, ep);
31476716431bSRobert Mustacchi 	}
31486716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
31496716431bSRobert Mustacchi 
31506716431bSRobert Mustacchi 	err = 0;
31516716431bSRobert Mustacchi 	if (ret != USB_SUCCESS) {
31526716431bSRobert Mustacchi 		err = EIO;
31536716431bSRobert Mustacchi 	}
31546716431bSRobert Mustacchi 
31556716431bSRobert Mustacchi 	return (err);
31566716431bSRobert Mustacchi }
31576716431bSRobert Mustacchi 
31586716431bSRobert Mustacchi static int
usbgem_m_setpromisc(void * arg,boolean_t on)31596716431bSRobert Mustacchi usbgem_m_setpromisc(void *arg, boolean_t on)
31606716431bSRobert Mustacchi {
31616716431bSRobert Mustacchi 	int	err;
31626716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
31636716431bSRobert Mustacchi 
31646716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
31656716431bSRobert Mustacchi 
31666716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
31676716431bSRobert Mustacchi 
31686716431bSRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
31696716431bSRobert Mustacchi 	if (on) {
31706716431bSRobert Mustacchi 		dp->rxmode |= RXMODE_PROMISC;
31716716431bSRobert Mustacchi 	} else {
31726716431bSRobert Mustacchi 		dp->rxmode &= ~RXMODE_PROMISC;
31736716431bSRobert Mustacchi 	}
31746716431bSRobert Mustacchi 
31756716431bSRobert Mustacchi 	err = 0;
31766716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
31776716431bSRobert Mustacchi 		if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) {
31786716431bSRobert Mustacchi 			err = EIO;
31796716431bSRobert Mustacchi 		}
31806716431bSRobert Mustacchi 	}
31816716431bSRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
31826716431bSRobert Mustacchi 
31836716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
31846716431bSRobert Mustacchi 
31856716431bSRobert Mustacchi 	return (err);
31866716431bSRobert Mustacchi }
31876716431bSRobert Mustacchi 
31886716431bSRobert Mustacchi int
usbgem_m_getstat(void * arg,uint_t stat,uint64_t * valp)31896716431bSRobert Mustacchi usbgem_m_getstat(void *arg, uint_t stat, uint64_t *valp)
31906716431bSRobert Mustacchi {
31916716431bSRobert Mustacchi 	uint64_t	val;
31926716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
31936716431bSRobert Mustacchi 	struct usbgem_stats	*gstp = &dp->stats;
31946716431bSRobert Mustacchi 
31956716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
31966716431bSRobert Mustacchi 
31976716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
31986716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
31996716431bSRobert Mustacchi 		rw_exit(&dp->dev_state_lock);
32006716431bSRobert Mustacchi 		return (0);
32016716431bSRobert Mustacchi 	}
32026716431bSRobert Mustacchi 
3203*7089fa46SGarrett D'Amore 	(void) usbgem_hal_get_stats(dp);
3204720b1687SToomas Soome 	rw_exit(&dp->dev_state_lock);
32056716431bSRobert Mustacchi 
32066716431bSRobert Mustacchi 	switch (stat) {
32076716431bSRobert Mustacchi 	case MAC_STAT_IFSPEED:
32086716431bSRobert Mustacchi 		val = usbgem_speed_value[dp->speed] *1000000ull;
32096716431bSRobert Mustacchi 		break;
32106716431bSRobert Mustacchi 
32116716431bSRobert Mustacchi 	case MAC_STAT_MULTIRCV:
32126716431bSRobert Mustacchi 		val = gstp->rmcast;
32136716431bSRobert Mustacchi 		break;
32146716431bSRobert Mustacchi 
32156716431bSRobert Mustacchi 	case MAC_STAT_BRDCSTRCV:
32166716431bSRobert Mustacchi 		val = gstp->rbcast;
32176716431bSRobert Mustacchi 		break;
32186716431bSRobert Mustacchi 
32196716431bSRobert Mustacchi 	case MAC_STAT_MULTIXMT:
32206716431bSRobert Mustacchi 		val = gstp->omcast;
32216716431bSRobert Mustacchi 		break;
32226716431bSRobert Mustacchi 
32236716431bSRobert Mustacchi 	case MAC_STAT_BRDCSTXMT:
32246716431bSRobert Mustacchi 		val = gstp->obcast;
32256716431bSRobert Mustacchi 		break;
32266716431bSRobert Mustacchi 
32276716431bSRobert Mustacchi 	case MAC_STAT_NORCVBUF:
32286716431bSRobert Mustacchi 		val = gstp->norcvbuf + gstp->missed;
32296716431bSRobert Mustacchi 		break;
32306716431bSRobert Mustacchi 
32316716431bSRobert Mustacchi 	case MAC_STAT_IERRORS:
32326716431bSRobert Mustacchi 		val = gstp->errrcv;
32336716431bSRobert Mustacchi 		break;
32346716431bSRobert Mustacchi 
32356716431bSRobert Mustacchi 	case MAC_STAT_NOXMTBUF:
32366716431bSRobert Mustacchi 		val = gstp->noxmtbuf;
32376716431bSRobert Mustacchi 		break;
32386716431bSRobert Mustacchi 
32396716431bSRobert Mustacchi 	case MAC_STAT_OERRORS:
32406716431bSRobert Mustacchi 		val = gstp->errxmt;
32416716431bSRobert Mustacchi 		break;
32426716431bSRobert Mustacchi 
32436716431bSRobert Mustacchi 	case MAC_STAT_COLLISIONS:
32446716431bSRobert Mustacchi 		val = gstp->collisions;
32456716431bSRobert Mustacchi 		break;
32466716431bSRobert Mustacchi 
32476716431bSRobert Mustacchi 	case MAC_STAT_RBYTES:
32486716431bSRobert Mustacchi 		val = gstp->rbytes;
32496716431bSRobert Mustacchi 		break;
32506716431bSRobert Mustacchi 
32516716431bSRobert Mustacchi 	case MAC_STAT_IPACKETS:
32526716431bSRobert Mustacchi 		val = gstp->rpackets;
32536716431bSRobert Mustacchi 		break;
32546716431bSRobert Mustacchi 
32556716431bSRobert Mustacchi 	case MAC_STAT_OBYTES:
32566716431bSRobert Mustacchi 		val = gstp->obytes;
32576716431bSRobert Mustacchi 		break;
32586716431bSRobert Mustacchi 
32596716431bSRobert Mustacchi 	case MAC_STAT_OPACKETS:
32606716431bSRobert Mustacchi 		val = gstp->opackets;
32616716431bSRobert Mustacchi 		break;
32626716431bSRobert Mustacchi 
32636716431bSRobert Mustacchi 	case MAC_STAT_UNDERFLOWS:
32646716431bSRobert Mustacchi 		val = gstp->underflow;
32656716431bSRobert Mustacchi 		break;
32666716431bSRobert Mustacchi 
32676716431bSRobert Mustacchi 	case MAC_STAT_OVERFLOWS:
32686716431bSRobert Mustacchi 		val = gstp->overflow;
32696716431bSRobert Mustacchi 		break;
32706716431bSRobert Mustacchi 
32716716431bSRobert Mustacchi 	case ETHER_STAT_ALIGN_ERRORS:
32726716431bSRobert Mustacchi 		val = gstp->frame;
32736716431bSRobert Mustacchi 		break;
32746716431bSRobert Mustacchi 
32756716431bSRobert Mustacchi 	case ETHER_STAT_FCS_ERRORS:
32766716431bSRobert Mustacchi 		val = gstp->crc;
32776716431bSRobert Mustacchi 		break;
32786716431bSRobert Mustacchi 
32796716431bSRobert Mustacchi 	case ETHER_STAT_FIRST_COLLISIONS:
32806716431bSRobert Mustacchi 		val = gstp->first_coll;
32816716431bSRobert Mustacchi 		break;
32826716431bSRobert Mustacchi 
32836716431bSRobert Mustacchi 	case ETHER_STAT_MULTI_COLLISIONS:
32846716431bSRobert Mustacchi 		val = gstp->multi_coll;
32856716431bSRobert Mustacchi 		break;
32866716431bSRobert Mustacchi 
32876716431bSRobert Mustacchi 	case ETHER_STAT_SQE_ERRORS:
32886716431bSRobert Mustacchi 		val = gstp->sqe;
32896716431bSRobert Mustacchi 		break;
32906716431bSRobert Mustacchi 
32916716431bSRobert Mustacchi 	case ETHER_STAT_DEFER_XMTS:
32926716431bSRobert Mustacchi 		val = gstp->defer;
32936716431bSRobert Mustacchi 		break;
32946716431bSRobert Mustacchi 
32956716431bSRobert Mustacchi 	case ETHER_STAT_TX_LATE_COLLISIONS:
32966716431bSRobert Mustacchi 		val = gstp->xmtlatecoll;
32976716431bSRobert Mustacchi 		break;
32986716431bSRobert Mustacchi 
32996716431bSRobert Mustacchi 	case ETHER_STAT_EX_COLLISIONS:
33006716431bSRobert Mustacchi 		val = gstp->excoll;
33016716431bSRobert Mustacchi 		break;
33026716431bSRobert Mustacchi 
33036716431bSRobert Mustacchi 	case ETHER_STAT_MACXMT_ERRORS:
33046716431bSRobert Mustacchi 		val = gstp->xmit_internal_err;
33056716431bSRobert Mustacchi 		break;
33066716431bSRobert Mustacchi 
33076716431bSRobert Mustacchi 	case ETHER_STAT_CARRIER_ERRORS:
33086716431bSRobert Mustacchi 		val = gstp->nocarrier;
33096716431bSRobert Mustacchi 		break;
33106716431bSRobert Mustacchi 
33116716431bSRobert Mustacchi 	case ETHER_STAT_TOOLONG_ERRORS:
33126716431bSRobert Mustacchi 		val = gstp->frame_too_long;
33136716431bSRobert Mustacchi 		break;
33146716431bSRobert Mustacchi 
33156716431bSRobert Mustacchi 	case ETHER_STAT_MACRCV_ERRORS:
33166716431bSRobert Mustacchi 		val = gstp->rcv_internal_err;
33176716431bSRobert Mustacchi 		break;
33186716431bSRobert Mustacchi 
33196716431bSRobert Mustacchi 	case ETHER_STAT_XCVR_ADDR:
33206716431bSRobert Mustacchi 		val = dp->mii_phy_addr;
33216716431bSRobert Mustacchi 		break;
33226716431bSRobert Mustacchi 
33236716431bSRobert Mustacchi 	case ETHER_STAT_XCVR_ID:
33246716431bSRobert Mustacchi 		val = dp->mii_phy_id;
33256716431bSRobert Mustacchi 		break;
33266716431bSRobert Mustacchi 
33276716431bSRobert Mustacchi 	case ETHER_STAT_XCVR_INUSE:
33286716431bSRobert Mustacchi 		val = usbgem_mac_xcvr_inuse(dp);
33296716431bSRobert Mustacchi 		break;
33306716431bSRobert Mustacchi 
33316716431bSRobert Mustacchi 	case ETHER_STAT_CAP_1000FDX:
33326716431bSRobert Mustacchi 		val = (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
33336716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
33346716431bSRobert Mustacchi 		break;
33356716431bSRobert Mustacchi 
33366716431bSRobert Mustacchi 	case ETHER_STAT_CAP_1000HDX:
33376716431bSRobert Mustacchi 		val = (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
33386716431bSRobert Mustacchi 		    (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
33396716431bSRobert Mustacchi 		break;
33406716431bSRobert Mustacchi 
33416716431bSRobert Mustacchi 	case ETHER_STAT_CAP_100FDX:
33426716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
33436716431bSRobert Mustacchi 		break;
33446716431bSRobert Mustacchi 
33456716431bSRobert Mustacchi 	case ETHER_STAT_CAP_100HDX:
33466716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
33476716431bSRobert Mustacchi 		break;
33486716431bSRobert Mustacchi 
33496716431bSRobert Mustacchi 	case ETHER_STAT_CAP_10FDX:
33506716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
33516716431bSRobert Mustacchi 		break;
33526716431bSRobert Mustacchi 
33536716431bSRobert Mustacchi 	case ETHER_STAT_CAP_10HDX:
33546716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_10);
33556716431bSRobert Mustacchi 		break;
33566716431bSRobert Mustacchi 
33576716431bSRobert Mustacchi 	case ETHER_STAT_CAP_ASMPAUSE:
33586716431bSRobert Mustacchi 		val = dp->ugc.usbgc_flow_control > FLOW_CONTROL_SYMMETRIC;
33596716431bSRobert Mustacchi 		break;
33606716431bSRobert Mustacchi 
33616716431bSRobert Mustacchi 	case ETHER_STAT_CAP_PAUSE:
33626716431bSRobert Mustacchi 		val = dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE;
33636716431bSRobert Mustacchi 		break;
33646716431bSRobert Mustacchi 
33656716431bSRobert Mustacchi 	case ETHER_STAT_CAP_AUTONEG:
33666716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
33676716431bSRobert Mustacchi 		break;
33686716431bSRobert Mustacchi 
33696716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_1000FDX:
33706716431bSRobert Mustacchi 		val = dp->anadv_1000fdx;
33716716431bSRobert Mustacchi 		break;
33726716431bSRobert Mustacchi 
33736716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_1000HDX:
33746716431bSRobert Mustacchi 		val = dp->anadv_1000hdx;
33756716431bSRobert Mustacchi 		break;
33766716431bSRobert Mustacchi 
33776716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_100FDX:
33786716431bSRobert Mustacchi 		val = dp->anadv_100fdx;
33796716431bSRobert Mustacchi 		break;
33806716431bSRobert Mustacchi 
33816716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_100HDX:
33826716431bSRobert Mustacchi 		val = dp->anadv_100hdx;
33836716431bSRobert Mustacchi 		break;
33846716431bSRobert Mustacchi 
33856716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_10FDX:
33866716431bSRobert Mustacchi 		val = dp->anadv_10fdx;
33876716431bSRobert Mustacchi 		break;
33886716431bSRobert Mustacchi 
33896716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_10HDX:
33906716431bSRobert Mustacchi 		val = dp->anadv_10hdx;
33916716431bSRobert Mustacchi 		break;
33926716431bSRobert Mustacchi 
33936716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
33946716431bSRobert Mustacchi 		val = dp->anadv_asmpause;
33956716431bSRobert Mustacchi 		break;
33966716431bSRobert Mustacchi 
33976716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_PAUSE:
33986716431bSRobert Mustacchi 		val = dp->anadv_pause;
33996716431bSRobert Mustacchi 		break;
34006716431bSRobert Mustacchi 
34016716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_AUTONEG:
34026716431bSRobert Mustacchi 		val = dp->anadv_autoneg;
34036716431bSRobert Mustacchi 		break;
34046716431bSRobert Mustacchi 
34056716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_1000FDX:
34066716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL);
34076716431bSRobert Mustacchi 		break;
34086716431bSRobert Mustacchi 
34096716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_1000HDX:
34106716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF);
34116716431bSRobert Mustacchi 		break;
34126716431bSRobert Mustacchi 
34136716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_100FDX:
34146716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD);
34156716431bSRobert Mustacchi 		break;
34166716431bSRobert Mustacchi 
34176716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_100HDX:
34186716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX);
34196716431bSRobert Mustacchi 		break;
34206716431bSRobert Mustacchi 
34216716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_10FDX:
34226716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD);
34236716431bSRobert Mustacchi 		break;
34246716431bSRobert Mustacchi 
34256716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_10HDX:
34266716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T);
34276716431bSRobert Mustacchi 		break;
34286716431bSRobert Mustacchi 
34296716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_ASMPAUSE:
34306716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_ASM_DIR);
34316716431bSRobert Mustacchi 		break;
34326716431bSRobert Mustacchi 
34336716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_PAUSE:
34346716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_PAUSE);
34356716431bSRobert Mustacchi 		break;
34366716431bSRobert Mustacchi 
34376716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_AUTONEG:
34386716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
34396716431bSRobert Mustacchi 		break;
34406716431bSRobert Mustacchi 
34416716431bSRobert Mustacchi 	case ETHER_STAT_LINK_ASMPAUSE:
34426716431bSRobert Mustacchi 		val = BOOLEAN(dp->flow_control & 2);
34436716431bSRobert Mustacchi 		break;
34446716431bSRobert Mustacchi 
34456716431bSRobert Mustacchi 	case ETHER_STAT_LINK_PAUSE:
34466716431bSRobert Mustacchi 		val = BOOLEAN(dp->flow_control & 1);
34476716431bSRobert Mustacchi 		break;
34486716431bSRobert Mustacchi 
34496716431bSRobert Mustacchi 	case ETHER_STAT_LINK_AUTONEG:
34506716431bSRobert Mustacchi 		val = dp->anadv_autoneg &&
34516716431bSRobert Mustacchi 		    BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
34526716431bSRobert Mustacchi 		break;
34536716431bSRobert Mustacchi 
34546716431bSRobert Mustacchi 	case ETHER_STAT_LINK_DUPLEX:
34556716431bSRobert Mustacchi 		val = (dp->mii_state == MII_STATE_LINKUP) ?
34566716431bSRobert Mustacchi 		    (dp->full_duplex ? 2 : 1) : 0;
34576716431bSRobert Mustacchi 		break;
34586716431bSRobert Mustacchi 
34596716431bSRobert Mustacchi 	case ETHER_STAT_TOOSHORT_ERRORS:
34606716431bSRobert Mustacchi 		val = gstp->runt;
34616716431bSRobert Mustacchi 		break;
34626716431bSRobert Mustacchi #ifdef NEVER	/* it doesn't make sense */
34636716431bSRobert Mustacchi 	case ETHER_STAT_CAP_REMFAULT:
34646716431bSRobert Mustacchi 		val = B_TRUE;
34656716431bSRobert Mustacchi 		break;
34666716431bSRobert Mustacchi 
34676716431bSRobert Mustacchi 	case ETHER_STAT_ADV_REMFAULT:
34686716431bSRobert Mustacchi 		val = dp->anadv_remfault;
34696716431bSRobert Mustacchi 		break;
34706716431bSRobert Mustacchi #endif
34716716431bSRobert Mustacchi 	case ETHER_STAT_LP_REMFAULT:
34726716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_AN_ADVERT_REMFAULT);
34736716431bSRobert Mustacchi 		break;
34746716431bSRobert Mustacchi 
34756716431bSRobert Mustacchi 	case ETHER_STAT_JABBER_ERRORS:
34766716431bSRobert Mustacchi 		val = gstp->jabber;
34776716431bSRobert Mustacchi 		break;
34786716431bSRobert Mustacchi 
34796716431bSRobert Mustacchi 	case ETHER_STAT_CAP_100T4:
34806716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
34816716431bSRobert Mustacchi 		break;
34826716431bSRobert Mustacchi 
34836716431bSRobert Mustacchi 	case ETHER_STAT_ADV_CAP_100T4:
34846716431bSRobert Mustacchi 		val = dp->anadv_100t4;
34856716431bSRobert Mustacchi 		break;
34866716431bSRobert Mustacchi 
34876716431bSRobert Mustacchi 	case ETHER_STAT_LP_CAP_100T4:
34886716431bSRobert Mustacchi 		val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_T4);
34896716431bSRobert Mustacchi 		break;
34906716431bSRobert Mustacchi 
34916716431bSRobert Mustacchi 	default:
34926716431bSRobert Mustacchi #if GEM_DEBUG_LEVEL > 2
34936716431bSRobert Mustacchi 		cmn_err(CE_WARN,
34946716431bSRobert Mustacchi 		    "%s: unrecognized parameter value = %d",
34956716431bSRobert Mustacchi 		    __func__, stat);
34966716431bSRobert Mustacchi #endif
34976716431bSRobert Mustacchi 		*valp = 0;
34986716431bSRobert Mustacchi 		return (ENOTSUP);
34996716431bSRobert Mustacchi 	}
35006716431bSRobert Mustacchi 
35016716431bSRobert Mustacchi 	*valp = val;
35026716431bSRobert Mustacchi 
35036716431bSRobert Mustacchi 	return (0);
35046716431bSRobert Mustacchi }
35056716431bSRobert Mustacchi 
35066716431bSRobert Mustacchi static int
usbgem_m_unicst(void * arg,const uint8_t * mac)35076716431bSRobert Mustacchi usbgem_m_unicst(void *arg, const uint8_t *mac)
35086716431bSRobert Mustacchi {
35096716431bSRobert Mustacchi 	int	err;
35106716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
35116716431bSRobert Mustacchi 
35126716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
35136716431bSRobert Mustacchi 
35146716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
35156716431bSRobert Mustacchi 
35166716431bSRobert Mustacchi 	sema_p(&dp->rxfilter_lock);
35176716431bSRobert Mustacchi 	bcopy(mac, dp->cur_addr.ether_addr_octet, ETHERADDRL);
35186716431bSRobert Mustacchi 	dp->rxmode |= RXMODE_ENABLE;
35196716431bSRobert Mustacchi 
35206716431bSRobert Mustacchi 	err = 0;
35216716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED) {
35226716431bSRobert Mustacchi 		if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) {
35236716431bSRobert Mustacchi 			err = EIO;
35246716431bSRobert Mustacchi 		}
35256716431bSRobert Mustacchi 	}
35266716431bSRobert Mustacchi 	sema_v(&dp->rxfilter_lock);
35276716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
35286716431bSRobert Mustacchi 
35296716431bSRobert Mustacchi 	return (err);
35306716431bSRobert Mustacchi }
35316716431bSRobert Mustacchi 
35326716431bSRobert Mustacchi /*
35336716431bSRobert Mustacchi  * usbgem_m_tx is used only for sending data packets into ethernet wire.
35346716431bSRobert Mustacchi  */
35356716431bSRobert Mustacchi static mblk_t *
usbgem_m_tx(void * arg,mblk_t * mp_head)35366716431bSRobert Mustacchi usbgem_m_tx(void *arg, mblk_t *mp_head)
35376716431bSRobert Mustacchi {
35386716431bSRobert Mustacchi 	int	limit;
35396716431bSRobert Mustacchi 	mblk_t	*mp;
35406716431bSRobert Mustacchi 	mblk_t	*nmp;
35416716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
35426716431bSRobert Mustacchi 
35436716431bSRobert Mustacchi 	DPRINTF(4, (CE_CONT, "!%s: %s: called", dp->name, __func__));
35446716431bSRobert Mustacchi 
35456716431bSRobert Mustacchi 	mp = mp_head;
35466716431bSRobert Mustacchi 
35476716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
35486716431bSRobert Mustacchi 
35496716431bSRobert Mustacchi 	if (dp->mii_state != MII_STATE_LINKUP ||
35506716431bSRobert Mustacchi 	    dp->mac_state != MAC_STATE_ONLINE) {
35516716431bSRobert Mustacchi 		/* some nics hate to send packets during the link is down */
35526716431bSRobert Mustacchi 		for (; mp; mp = nmp) {
35536716431bSRobert Mustacchi 			nmp = mp->b_next;
35546716431bSRobert Mustacchi 			mp->b_next = NULL;
35556716431bSRobert Mustacchi 			freemsg(mp);
35566716431bSRobert Mustacchi 		}
35576716431bSRobert Mustacchi 		goto x;
35586716431bSRobert Mustacchi 	}
35596716431bSRobert Mustacchi 
35606716431bSRobert Mustacchi 	ASSERT(dp->nic_state == NIC_STATE_ONLINE);
35616716431bSRobert Mustacchi 
35626716431bSRobert Mustacchi 	limit = dp->tx_max_packets;
35636716431bSRobert Mustacchi 	for (; limit-- && mp; mp = nmp) {
35646716431bSRobert Mustacchi 		nmp = mp->b_next;
35656716431bSRobert Mustacchi 		mp->b_next = NULL;
35666716431bSRobert Mustacchi 		if (usbgem_send_common(dp, mp,
35676716431bSRobert Mustacchi 		    (limit == 0 && nmp) ? 1 : 0)) {
35686716431bSRobert Mustacchi 			mp->b_next = nmp;
35696716431bSRobert Mustacchi 			break;
35706716431bSRobert Mustacchi 		}
35716716431bSRobert Mustacchi 	}
35726716431bSRobert Mustacchi #ifdef CONFIG_TX_LIMITER
35736716431bSRobert Mustacchi 	if (mp == mp_head) {
35746716431bSRobert Mustacchi 		/* no packets were sent, descrease allocation limit */
35756716431bSRobert Mustacchi 		mutex_enter(&dp->txlock);
35766716431bSRobert Mustacchi 		dp->tx_max_packets = max(dp->tx_max_packets - 1, 1);
35776716431bSRobert Mustacchi 		mutex_exit(&dp->txlock);
35786716431bSRobert Mustacchi 	}
35796716431bSRobert Mustacchi #endif
35806716431bSRobert Mustacchi x:
35816716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
35826716431bSRobert Mustacchi 
35836716431bSRobert Mustacchi 	return (mp);
35846716431bSRobert Mustacchi }
35856716431bSRobert Mustacchi 
35866716431bSRobert Mustacchi static void
usbgem_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)35876716431bSRobert Mustacchi usbgem_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
35886716431bSRobert Mustacchi {
35896716431bSRobert Mustacchi 	struct usbgem_dev	*dp = arg;
35906716431bSRobert Mustacchi 
35916716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called",
35926716431bSRobert Mustacchi 	    ((struct usbgem_dev *)arg)->name, __func__));
35936716431bSRobert Mustacchi 
35946716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_READER);
35956716431bSRobert Mustacchi 	usbgem_mac_ioctl((struct usbgem_dev *)arg, wq, mp);
35966716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
35976716431bSRobert Mustacchi }
35986716431bSRobert Mustacchi 
35996716431bSRobert Mustacchi static void
usbgem_gld3_init(struct usbgem_dev * dp,mac_register_t * macp)36006716431bSRobert Mustacchi usbgem_gld3_init(struct usbgem_dev *dp, mac_register_t *macp)
36016716431bSRobert Mustacchi {
36026716431bSRobert Mustacchi 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
36036716431bSRobert Mustacchi 	macp->m_driver = dp;
36046716431bSRobert Mustacchi 	macp->m_dip = dp->dip;
36056716431bSRobert Mustacchi 	macp->m_src_addr = dp->dev_addr.ether_addr_octet;
36066716431bSRobert Mustacchi 	macp->m_callbacks = &gem_m_callbacks;
36076716431bSRobert Mustacchi 	macp->m_min_sdu = 0;
36086716431bSRobert Mustacchi 	macp->m_max_sdu = dp->mtu;
36096716431bSRobert Mustacchi 
36106716431bSRobert Mustacchi 	if (dp->misc_flag & USBGEM_VLAN) {
36116716431bSRobert Mustacchi 		macp->m_margin = VTAG_SIZE;
36126716431bSRobert Mustacchi 	}
36136716431bSRobert Mustacchi }
36146716431bSRobert Mustacchi 
36156716431bSRobert Mustacchi /* ======================================================================== */
36166716431bSRobert Mustacchi /*
36176716431bSRobert Mustacchi  * .conf interface
36186716431bSRobert Mustacchi  */
36196716431bSRobert Mustacchi /* ======================================================================== */
36206716431bSRobert Mustacchi void
usbgem_generate_macaddr(struct usbgem_dev * dp,uint8_t * mac)36216716431bSRobert Mustacchi usbgem_generate_macaddr(struct usbgem_dev *dp, uint8_t *mac)
36226716431bSRobert Mustacchi {
36236716431bSRobert Mustacchi 	extern char	hw_serial[];
36246716431bSRobert Mustacchi 	char		*hw_serial_p;
36256716431bSRobert Mustacchi 	int		i;
36266716431bSRobert Mustacchi 	uint64_t	val;
36276716431bSRobert Mustacchi 	uint64_t	key;
36286716431bSRobert Mustacchi 
36296716431bSRobert Mustacchi 	cmn_err(CE_NOTE,
36306716431bSRobert Mustacchi 	    "!%s: using temp ether address,"
36316716431bSRobert Mustacchi 	    " do not use this for long time",
36326716431bSRobert Mustacchi 	    dp->name);
36336716431bSRobert Mustacchi 
36346716431bSRobert Mustacchi 	/* prefer a fixed address for DHCP */
36356716431bSRobert Mustacchi 	hw_serial_p = &hw_serial[0];
36366716431bSRobert Mustacchi 	val = stoi(&hw_serial_p);
36376716431bSRobert Mustacchi 
36386716431bSRobert Mustacchi 	key = 0;
36396716431bSRobert Mustacchi 	for (i = 0; i < USBGEM_NAME_LEN; i++) {
36406716431bSRobert Mustacchi 		if (dp->name[i] == 0) {
36416716431bSRobert Mustacchi 			break;
36426716431bSRobert Mustacchi 		}
36436716431bSRobert Mustacchi 		key ^= dp->name[i];
36446716431bSRobert Mustacchi 	}
36456716431bSRobert Mustacchi 	key ^= ddi_get_instance(dp->dip);
36466716431bSRobert Mustacchi 	val ^= key << 32;
36476716431bSRobert Mustacchi 
36486716431bSRobert Mustacchi 	/* generate a local address */
36496716431bSRobert Mustacchi 	mac[0] = 0x02;
36506716431bSRobert Mustacchi 	mac[1] = (uint8_t)(val >> 32);
36516716431bSRobert Mustacchi 	mac[2] = (uint8_t)(val >> 24);
36526716431bSRobert Mustacchi 	mac[3] = (uint8_t)(val >> 16);
36536716431bSRobert Mustacchi 	mac[4] = (uint8_t)(val >> 8);
36546716431bSRobert Mustacchi 	mac[5] = (uint8_t)val;
36556716431bSRobert Mustacchi }
36566716431bSRobert Mustacchi 
36576716431bSRobert Mustacchi boolean_t
usbgem_get_mac_addr_conf(struct usbgem_dev * dp)36586716431bSRobert Mustacchi usbgem_get_mac_addr_conf(struct usbgem_dev *dp)
36596716431bSRobert Mustacchi {
36606716431bSRobert Mustacchi 	char		propname[32];
36616716431bSRobert Mustacchi 	char		*valstr;
36626716431bSRobert Mustacchi 	uint8_t		mac[ETHERADDRL];
36636716431bSRobert Mustacchi 	char		*cp;
36646716431bSRobert Mustacchi 	int		c;
36656716431bSRobert Mustacchi 	int		i;
36666716431bSRobert Mustacchi 	int		j;
36676716431bSRobert Mustacchi 	uint8_t		v;
36686716431bSRobert Mustacchi 	uint8_t		d;
36696716431bSRobert Mustacchi 	uint8_t		ored;
36706716431bSRobert Mustacchi 
36716716431bSRobert Mustacchi 	DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__));
36726716431bSRobert Mustacchi 	/*
36736716431bSRobert Mustacchi 	 * Get ethernet address from .conf file
36746716431bSRobert Mustacchi 	 */
36756716431bSRobert Mustacchi 	(void) sprintf(propname, "mac-addr");
36766716431bSRobert Mustacchi 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dp->dip,
36776716431bSRobert Mustacchi 	    DDI_PROP_DONTPASS, propname, &valstr)) != DDI_PROP_SUCCESS) {
36786716431bSRobert Mustacchi 		return (B_FALSE);
36796716431bSRobert Mustacchi 	}
36806716431bSRobert Mustacchi 
36816716431bSRobert Mustacchi 	if (strlen(valstr) != ETHERADDRL*3-1) {
36826716431bSRobert Mustacchi 		goto syntax_err;
36836716431bSRobert Mustacchi 	}
36846716431bSRobert Mustacchi 
36856716431bSRobert Mustacchi 	cp = valstr;
36866716431bSRobert Mustacchi 	j = 0;
36876716431bSRobert Mustacchi 	ored = 0;
36886716431bSRobert Mustacchi 	for (;;) {
36896716431bSRobert Mustacchi 		v = 0;
36906716431bSRobert Mustacchi 		for (i = 0; i < 2; i++) {
36916716431bSRobert Mustacchi 			c = *cp++;
36926716431bSRobert Mustacchi 
36936716431bSRobert Mustacchi 			if (c >= 'a' && c <= 'f') {
36946716431bSRobert Mustacchi 				d = c - 'a' + 10;
36956716431bSRobert Mustacchi 			} else if (c >= 'A' && c <= 'F') {
36966716431bSRobert Mustacchi 				d = c - 'A' + 10;
36976716431bSRobert Mustacchi 			} else if (c >= '0' && c <= '9') {
36986716431bSRobert Mustacchi 				d = c - '0';
36996716431bSRobert Mustacchi 			} else {
37006716431bSRobert Mustacchi 				goto syntax_err;
37016716431bSRobert Mustacchi 			}
37026716431bSRobert Mustacchi 			v = (v << 4) | d;
37036716431bSRobert Mustacchi 		}
37046716431bSRobert Mustacchi 
37056716431bSRobert Mustacchi 		mac[j++] = v;
37066716431bSRobert Mustacchi 		ored |= v;
37076716431bSRobert Mustacchi 		if (j == ETHERADDRL) {
37086716431bSRobert Mustacchi 			/* done */
37096716431bSRobert Mustacchi 			break;
37106716431bSRobert Mustacchi 		}
37116716431bSRobert Mustacchi 
37126716431bSRobert Mustacchi 		c = *cp++;
37136716431bSRobert Mustacchi 		if (c != ':') {
37146716431bSRobert Mustacchi 			goto syntax_err;
37156716431bSRobert Mustacchi 		}
37166716431bSRobert Mustacchi 	}
37176716431bSRobert Mustacchi 
37186716431bSRobert Mustacchi 	if (ored == 0) {
37196716431bSRobert Mustacchi 		usbgem_generate_macaddr(dp, mac);
37206716431bSRobert Mustacchi 	}
37216716431bSRobert Mustacchi 	for (i = 0; i < ETHERADDRL; i++) {
37226716431bSRobert Mustacchi 		dp->dev_addr.ether_addr_octet[i] = mac[i];
37236716431bSRobert Mustacchi 	}
37246716431bSRobert Mustacchi 	ddi_prop_free(valstr);
37256716431bSRobert Mustacchi 	return (B_TRUE);
37266716431bSRobert Mustacchi 
37276716431bSRobert Mustacchi syntax_err:
37286716431bSRobert Mustacchi 	cmn_err(CE_CONT,
37296716431bSRobert Mustacchi 	    "!%s: read mac addr: trying .conf: syntax err %s",
37306716431bSRobert Mustacchi 	    dp->name, valstr);
37316716431bSRobert Mustacchi 	ddi_prop_free(valstr);
37326716431bSRobert Mustacchi 
37336716431bSRobert Mustacchi 	return (B_FALSE);
37346716431bSRobert Mustacchi }
37356716431bSRobert Mustacchi 
37366716431bSRobert Mustacchi static void
usbgem_read_conf(struct usbgem_dev * dp)37376716431bSRobert Mustacchi usbgem_read_conf(struct usbgem_dev *dp)
37386716431bSRobert Mustacchi {
37396716431bSRobert Mustacchi 	int	val;
37406716431bSRobert Mustacchi 
37416716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
37426716431bSRobert Mustacchi 
37436716431bSRobert Mustacchi 	/*
37446716431bSRobert Mustacchi 	 * Get media mode infomation from .conf file
37456716431bSRobert Mustacchi 	 */
37466716431bSRobert Mustacchi 	dp->anadv_autoneg = usbgem_prop_get_int(dp, "adv_autoneg_cap", 1) != 0;
37476716431bSRobert Mustacchi 	dp->anadv_1000fdx = usbgem_prop_get_int(dp, "adv_1000fdx_cap", 1) != 0;
37486716431bSRobert Mustacchi 	dp->anadv_1000hdx = usbgem_prop_get_int(dp, "adv_1000hdx_cap", 1) != 0;
37496716431bSRobert Mustacchi 	dp->anadv_100t4 = usbgem_prop_get_int(dp, "adv_100T4_cap", 1) != 0;
37506716431bSRobert Mustacchi 	dp->anadv_100fdx = usbgem_prop_get_int(dp, "adv_100fdx_cap", 1) != 0;
37516716431bSRobert Mustacchi 	dp->anadv_100hdx = usbgem_prop_get_int(dp, "adv_100hdx_cap", 1) != 0;
37526716431bSRobert Mustacchi 	dp->anadv_10fdx = usbgem_prop_get_int(dp, "adv_10fdx_cap", 1) != 0;
37536716431bSRobert Mustacchi 	dp->anadv_10hdx = usbgem_prop_get_int(dp, "adv_10hdx_cap", 1) != 0;
37546716431bSRobert Mustacchi 	dp->anadv_1000t_ms = usbgem_prop_get_int(dp, "adv_1000t_ms", 0);
37556716431bSRobert Mustacchi 
37566716431bSRobert Mustacchi 	if ((ddi_prop_exists(DDI_DEV_T_ANY, dp->dip,
37576716431bSRobert Mustacchi 	    DDI_PROP_DONTPASS, "full-duplex"))) {
37586716431bSRobert Mustacchi 		dp->full_duplex =
37596716431bSRobert Mustacchi 		    usbgem_prop_get_int(dp, "full-duplex", 1) != 0;
37606716431bSRobert Mustacchi 		dp->anadv_autoneg = B_FALSE;
37616716431bSRobert Mustacchi 		if (dp->full_duplex) {
37626716431bSRobert Mustacchi 			dp->anadv_1000hdx = B_FALSE;
37636716431bSRobert Mustacchi 			dp->anadv_100hdx = B_FALSE;
37646716431bSRobert Mustacchi 			dp->anadv_10hdx = B_FALSE;
37656716431bSRobert Mustacchi 		} else {
37666716431bSRobert Mustacchi 			dp->anadv_1000fdx = B_FALSE;
37676716431bSRobert Mustacchi 			dp->anadv_100fdx = B_FALSE;
37686716431bSRobert Mustacchi 			dp->anadv_10fdx = B_FALSE;
37696716431bSRobert Mustacchi 		}
37706716431bSRobert Mustacchi 	}
37716716431bSRobert Mustacchi 
37726716431bSRobert Mustacchi 	if ((val = usbgem_prop_get_int(dp, "speed", 0)) > 0) {
37736716431bSRobert Mustacchi 		dp->anadv_autoneg = B_FALSE;
37746716431bSRobert Mustacchi 		switch (val) {
37756716431bSRobert Mustacchi 		case 1000:
37766716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_1000;
37776716431bSRobert Mustacchi 			dp->anadv_100t4 = B_FALSE;
37786716431bSRobert Mustacchi 			dp->anadv_100fdx = B_FALSE;
37796716431bSRobert Mustacchi 			dp->anadv_100hdx = B_FALSE;
37806716431bSRobert Mustacchi 			dp->anadv_10fdx = B_FALSE;
37816716431bSRobert Mustacchi 			dp->anadv_10hdx = B_FALSE;
37826716431bSRobert Mustacchi 			break;
37836716431bSRobert Mustacchi 		case 100:
37846716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_100;
37856716431bSRobert Mustacchi 			dp->anadv_1000fdx = B_FALSE;
37866716431bSRobert Mustacchi 			dp->anadv_1000hdx = B_FALSE;
37876716431bSRobert Mustacchi 			dp->anadv_10fdx = B_FALSE;
37886716431bSRobert Mustacchi 			dp->anadv_10hdx = B_FALSE;
37896716431bSRobert Mustacchi 			break;
37906716431bSRobert Mustacchi 		case 10:
37916716431bSRobert Mustacchi 			dp->speed = USBGEM_SPD_10;
37926716431bSRobert Mustacchi 			dp->anadv_1000fdx = B_FALSE;
37936716431bSRobert Mustacchi 			dp->anadv_1000hdx = B_FALSE;
37946716431bSRobert Mustacchi 			dp->anadv_100t4 = B_FALSE;
37956716431bSRobert Mustacchi 			dp->anadv_100fdx = B_FALSE;
37966716431bSRobert Mustacchi 			dp->anadv_100hdx = B_FALSE;
37976716431bSRobert Mustacchi 			break;
37986716431bSRobert Mustacchi 		default:
37996716431bSRobert Mustacchi 			cmn_err(CE_WARN,
38006716431bSRobert Mustacchi 			    "!%s: property %s: illegal value:%d",
38016716431bSRobert Mustacchi 			    dp->name, "speed", val);
38026716431bSRobert Mustacchi 			dp->anadv_autoneg = B_TRUE;
38036716431bSRobert Mustacchi 			break;
38046716431bSRobert Mustacchi 		}
38056716431bSRobert Mustacchi 	}
38066716431bSRobert Mustacchi 	val = usbgem_prop_get_int(dp,
38076716431bSRobert Mustacchi 	    "adv_pause", dp->ugc.usbgc_flow_control & 1);
38086716431bSRobert Mustacchi 	val |= usbgem_prop_get_int(dp,
38096716431bSRobert Mustacchi 	    "adv_asmpause", BOOLEAN(dp->ugc.usbgc_flow_control & 2)) << 1;
38106716431bSRobert Mustacchi 	if (val > FLOW_CONTROL_RX_PAUSE || val < FLOW_CONTROL_NONE) {
38116716431bSRobert Mustacchi 		cmn_err(CE_WARN,
38126716431bSRobert Mustacchi 		    "!%s: property %s: illegal value:%d",
38136716431bSRobert Mustacchi 		    dp->name, "flow-control", val);
38146716431bSRobert Mustacchi 	} else {
38156716431bSRobert Mustacchi 		val = min(val, dp->ugc.usbgc_flow_control);
38166716431bSRobert Mustacchi 	}
38176716431bSRobert Mustacchi 	dp->anadv_pause = BOOLEAN(val & 1);
38186716431bSRobert Mustacchi 	dp->anadv_asmpause = BOOLEAN(val & 2);
38196716431bSRobert Mustacchi 
38206716431bSRobert Mustacchi 	dp->mtu = usbgem_prop_get_int(dp, "mtu", dp->mtu);
38216716431bSRobert Mustacchi 	dp->txthr = usbgem_prop_get_int(dp, "txthr", dp->txthr);
38226716431bSRobert Mustacchi 	dp->rxthr = usbgem_prop_get_int(dp, "rxthr", dp->rxthr);
38236716431bSRobert Mustacchi 	dp->txmaxdma = usbgem_prop_get_int(dp, "txmaxdma", dp->txmaxdma);
38246716431bSRobert Mustacchi 	dp->rxmaxdma = usbgem_prop_get_int(dp, "rxmaxdma", dp->rxmaxdma);
38256716431bSRobert Mustacchi #ifdef GEM_CONFIG_POLLING
38266716431bSRobert Mustacchi 	dp->poll_pkt_delay =
38276716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "pkt_delay", dp->poll_pkt_delay);
38286716431bSRobert Mustacchi 
38296716431bSRobert Mustacchi 	dp->max_poll_interval[GEM_SPD_10] =
38306716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "max_poll_interval_10",
38316716431bSRobert Mustacchi 	    dp->max_poll_interval[GEM_SPD_10]);
38326716431bSRobert Mustacchi 	dp->max_poll_interval[GEM_SPD_100] =
38336716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "max_poll_interval_100",
38346716431bSRobert Mustacchi 	    dp->max_poll_interval[GEM_SPD_100]);
38356716431bSRobert Mustacchi 	dp->max_poll_interval[GEM_SPD_1000] =
38366716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "max_poll_interval_1000",
38376716431bSRobert Mustacchi 	    dp->max_poll_interval[GEM_SPD_1000]);
38386716431bSRobert Mustacchi 
38396716431bSRobert Mustacchi 	dp->min_poll_interval[GEM_SPD_10] =
38406716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "min_poll_interval_10",
38416716431bSRobert Mustacchi 	    dp->min_poll_interval[GEM_SPD_10]);
38426716431bSRobert Mustacchi 	dp->min_poll_interval[GEM_SPD_100] =
38436716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "min_poll_interval_100",
38446716431bSRobert Mustacchi 	    dp->min_poll_interval[GEM_SPD_100]);
38456716431bSRobert Mustacchi 	dp->min_poll_interval[GEM_SPD_1000] =
38466716431bSRobert Mustacchi 	    usbgem_prop_get_int(dp, "min_poll_interval_1000",
38476716431bSRobert Mustacchi 	    dp->min_poll_interval[GEM_SPD_1000]);
38486716431bSRobert Mustacchi #endif
38496716431bSRobert Mustacchi }
38506716431bSRobert Mustacchi 
38516716431bSRobert Mustacchi /*
38526716431bSRobert Mustacchi  * attach/detatch/usb support
38536716431bSRobert Mustacchi  */
38546716431bSRobert Mustacchi /* ======================================================================== */
38556716431bSRobert Mustacchi int
usbgem_ctrl_out(struct usbgem_dev * dp,uint8_t reqt,uint8_t req,uint16_t val,uint16_t ix,uint16_t len,void * bp,int size)38566716431bSRobert Mustacchi usbgem_ctrl_out(struct usbgem_dev *dp,
3857720b1687SToomas Soome     uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
3858720b1687SToomas Soome     void *bp, int size)
38596716431bSRobert Mustacchi {
38606716431bSRobert Mustacchi 	mblk_t			*data;
38616716431bSRobert Mustacchi 	usb_ctrl_setup_t	setup;
38626716431bSRobert Mustacchi 	usb_cr_t		completion_reason;
38636716431bSRobert Mustacchi 	usb_cb_flags_t		cb_flags;
38646716431bSRobert Mustacchi 	usb_flags_t		flags;
38656716431bSRobert Mustacchi 	int			i;
38666716431bSRobert Mustacchi 	int			ret;
38676716431bSRobert Mustacchi 
38686716431bSRobert Mustacchi 	DPRINTF(4, (CE_CONT, "!%s: %s "
38696716431bSRobert Mustacchi 	    "reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x "
38706716431bSRobert Mustacchi 	    "bp:0x%p nic_state:%d",
38716716431bSRobert Mustacchi 	    dp->name, __func__, reqt, req, val, ix, len, bp, dp->nic_state));
38726716431bSRobert Mustacchi 
38736716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
38746716431bSRobert Mustacchi 		return (USB_PIPE_ERROR);
38756716431bSRobert Mustacchi 	}
38766716431bSRobert Mustacchi 
38776716431bSRobert Mustacchi 	data = NULL;
38786716431bSRobert Mustacchi 	if (size > 0) {
38796716431bSRobert Mustacchi 		if ((data = allocb(size, 0)) == NULL) {
38806716431bSRobert Mustacchi 			return (USB_FAILURE);
38816716431bSRobert Mustacchi 		}
38826716431bSRobert Mustacchi 
38836716431bSRobert Mustacchi 		bcopy(bp, data->b_rptr, size);
38846716431bSRobert Mustacchi 		data->b_wptr = data->b_rptr + size;
38856716431bSRobert Mustacchi 	}
38866716431bSRobert Mustacchi 
38876716431bSRobert Mustacchi 	setup.bmRequestType = reqt;
38886716431bSRobert Mustacchi 	setup.bRequest = req;
38896716431bSRobert Mustacchi 	setup.wValue = val;
38906716431bSRobert Mustacchi 	setup.wIndex = ix;
38916716431bSRobert Mustacchi 	setup.wLength = len;
38926716431bSRobert Mustacchi 	setup.attrs = 0;	/* attributes */
38936716431bSRobert Mustacchi 
38946716431bSRobert Mustacchi 	for (i = usbgem_ctrl_retry; i > 0; i--) {
38956716431bSRobert Mustacchi 		completion_reason = 0;
38966716431bSRobert Mustacchi 		cb_flags = 0;
38976716431bSRobert Mustacchi 
38986716431bSRobert Mustacchi 		ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp),
38996716431bSRobert Mustacchi 		    &setup, &data, &completion_reason, &cb_flags, 0);
39006716431bSRobert Mustacchi 
39016716431bSRobert Mustacchi 		if (ret == USB_SUCCESS) {
39026716431bSRobert Mustacchi 			break;
39036716431bSRobert Mustacchi 		}
39046716431bSRobert Mustacchi 		if (i == 1) {
39056716431bSRobert Mustacchi 			cmn_err(CE_WARN,
39066716431bSRobert Mustacchi 			    "!%s: %s failed: "
39076716431bSRobert Mustacchi 			    "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x "
39086716431bSRobert Mustacchi 			    "ret:%d cr:%s(%d), cb_flags:0x%x %s",
39096716431bSRobert Mustacchi 			    dp->name, __func__, reqt, req, val, ix, len,
39106716431bSRobert Mustacchi 			    ret, usb_str_cr(completion_reason),
39116716431bSRobert Mustacchi 			    completion_reason,
39126716431bSRobert Mustacchi 			    cb_flags,
39136716431bSRobert Mustacchi 			    (i > 1) ? "retrying..." : "fatal");
39146716431bSRobert Mustacchi 		}
39156716431bSRobert Mustacchi 	}
39166716431bSRobert Mustacchi 
39176716431bSRobert Mustacchi 	if (data != NULL) {
39186716431bSRobert Mustacchi 		freemsg(data);
39196716431bSRobert Mustacchi 	}
39206716431bSRobert Mustacchi 
39216716431bSRobert Mustacchi 	return (ret);
39226716431bSRobert Mustacchi }
39236716431bSRobert Mustacchi 
39246716431bSRobert Mustacchi int
usbgem_ctrl_in(struct usbgem_dev * dp,uint8_t reqt,uint8_t req,uint16_t val,uint16_t ix,uint16_t len,void * bp,int size)39256716431bSRobert Mustacchi usbgem_ctrl_in(struct usbgem_dev *dp,
3926720b1687SToomas Soome     uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
3927720b1687SToomas Soome     void *bp, int size)
39286716431bSRobert Mustacchi {
39296716431bSRobert Mustacchi 	mblk_t			*data;
39306716431bSRobert Mustacchi 	usb_ctrl_setup_t	setup;
39316716431bSRobert Mustacchi 	usb_cr_t		completion_reason;
39326716431bSRobert Mustacchi 	usb_cb_flags_t		cb_flags;
39336716431bSRobert Mustacchi 	int			i;
39346716431bSRobert Mustacchi 	int			ret;
39356716431bSRobert Mustacchi 	int			reclen;
39366716431bSRobert Mustacchi 
39376716431bSRobert Mustacchi 	DPRINTF(4, (CE_CONT,
39386716431bSRobert Mustacchi 	    "!%s: %s:"
39396716431bSRobert Mustacchi 	    " reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x"
39406716431bSRobert Mustacchi 	    " bp:x%p mac_state:%d",
39416716431bSRobert Mustacchi 	    dp->name, __func__, reqt, req, val, ix, len, bp, dp->mac_state));
39426716431bSRobert Mustacchi 
39436716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
39446716431bSRobert Mustacchi 		return (USB_PIPE_ERROR);
39456716431bSRobert Mustacchi 	}
39466716431bSRobert Mustacchi 
39476716431bSRobert Mustacchi 	data = NULL;
39486716431bSRobert Mustacchi 
39496716431bSRobert Mustacchi 	setup.bmRequestType = reqt;
39506716431bSRobert Mustacchi 	setup.bRequest = req;
39516716431bSRobert Mustacchi 	setup.wValue = val;
39526716431bSRobert Mustacchi 	setup.wIndex = ix;
39536716431bSRobert Mustacchi 	setup.wLength = len;
39546716431bSRobert Mustacchi 	setup.attrs = USB_ATTRS_AUTOCLEARING;	/* XXX */
39556716431bSRobert Mustacchi 
39566716431bSRobert Mustacchi 	for (i = usbgem_ctrl_retry; i > 0; i--) {
39576716431bSRobert Mustacchi 		completion_reason = 0;
39586716431bSRobert Mustacchi 		cb_flags = 0;
39596716431bSRobert Mustacchi 		ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp), &setup, &data,
39606716431bSRobert Mustacchi 		    &completion_reason, &cb_flags, 0);
39616716431bSRobert Mustacchi 
39626716431bSRobert Mustacchi 		if (ret == USB_SUCCESS) {
39636716431bSRobert Mustacchi 			reclen = msgdsize(data);
39646716431bSRobert Mustacchi 			bcopy(data->b_rptr, bp, min(reclen, size));
39656716431bSRobert Mustacchi 			break;
39666716431bSRobert Mustacchi 		}
39676716431bSRobert Mustacchi 		if (i == 1) {
39686716431bSRobert Mustacchi 			cmn_err(CE_WARN,
39696716431bSRobert Mustacchi 			    "!%s: %s failed: "
39706716431bSRobert Mustacchi 			    "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x "
39716716431bSRobert Mustacchi 			    "ret:%d cr:%s(%d) cb_flags:0x%x %s",
39726716431bSRobert Mustacchi 			    dp->name, __func__,
39736716431bSRobert Mustacchi 			    reqt, req, val, ix, len,
39746716431bSRobert Mustacchi 			    ret, usb_str_cr(completion_reason),
39756716431bSRobert Mustacchi 			    completion_reason,
39766716431bSRobert Mustacchi 			    cb_flags,
39776716431bSRobert Mustacchi 			    (i > 1) ? "retrying..." : "fatal");
39786716431bSRobert Mustacchi 		}
39796716431bSRobert Mustacchi 	}
39806716431bSRobert Mustacchi 
39816716431bSRobert Mustacchi 	if (data) {
39826716431bSRobert Mustacchi 		freemsg(data);
39836716431bSRobert Mustacchi 	}
39846716431bSRobert Mustacchi 
39856716431bSRobert Mustacchi 	return (ret);
39866716431bSRobert Mustacchi }
39876716431bSRobert Mustacchi 
39886716431bSRobert Mustacchi int
usbgem_ctrl_out_val(struct usbgem_dev * dp,uint8_t reqt,uint8_t req,uint16_t val,uint16_t ix,uint16_t len,uint32_t v)39896716431bSRobert Mustacchi usbgem_ctrl_out_val(struct usbgem_dev *dp,
39906716431bSRobert Mustacchi     uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
39916716431bSRobert Mustacchi     uint32_t v)
39926716431bSRobert Mustacchi {
39936716431bSRobert Mustacchi 	uint8_t	buf[4];
39946716431bSRobert Mustacchi 
39956716431bSRobert Mustacchi 	/* convert to little endian from native byte order */
39966716431bSRobert Mustacchi 	switch (len) {
39976716431bSRobert Mustacchi 	case 4:
39986716431bSRobert Mustacchi 		buf[3] = v >> 24;
39996716431bSRobert Mustacchi 		buf[2] = v >> 16;
40006716431bSRobert Mustacchi 		/* FALLTHROUGH */
40016716431bSRobert Mustacchi 	case 2:
40026716431bSRobert Mustacchi 		buf[1] = v >> 8;
40036716431bSRobert Mustacchi 		/* FALLTHROUGH */
40046716431bSRobert Mustacchi 	case 1:
40056716431bSRobert Mustacchi 		buf[0] = v;
40066716431bSRobert Mustacchi 	}
40076716431bSRobert Mustacchi 
40086716431bSRobert Mustacchi 	return (usbgem_ctrl_out(dp, reqt, req, val, ix, len, buf, len));
40096716431bSRobert Mustacchi }
40106716431bSRobert Mustacchi 
40116716431bSRobert Mustacchi int
usbgem_ctrl_in_val(struct usbgem_dev * dp,uint8_t reqt,uint8_t req,uint16_t val,uint16_t ix,uint16_t len,void * valp)40126716431bSRobert Mustacchi usbgem_ctrl_in_val(struct usbgem_dev *dp,
40136716431bSRobert Mustacchi     uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
40146716431bSRobert Mustacchi     void *valp)
40156716431bSRobert Mustacchi {
40166716431bSRobert Mustacchi 	uint8_t		buf[4];
40176716431bSRobert Mustacchi 	uint_t		v;
40186716431bSRobert Mustacchi 	int		err;
40196716431bSRobert Mustacchi 
40206716431bSRobert Mustacchi #ifdef SANITY
40216716431bSRobert Mustacchi 	bzero(buf, sizeof (buf));
40226716431bSRobert Mustacchi #endif
40236716431bSRobert Mustacchi 	err = usbgem_ctrl_in(dp, reqt, req, val, ix, len, buf, len);
40246716431bSRobert Mustacchi 	if (err == USB_SUCCESS) {
40256716431bSRobert Mustacchi 		v = 0;
40266716431bSRobert Mustacchi 		switch (len) {
40276716431bSRobert Mustacchi 		case 4:
40286716431bSRobert Mustacchi 			v |= buf[3] << 24;
40296716431bSRobert Mustacchi 			v |= buf[2] << 16;
40306716431bSRobert Mustacchi 			/* FALLTHROUGH */
40316716431bSRobert Mustacchi 		case 2:
40326716431bSRobert Mustacchi 			v |= buf[1] << 8;
40336716431bSRobert Mustacchi 			/* FALLTHROUGH */
40346716431bSRobert Mustacchi 		case 1:
40356716431bSRobert Mustacchi 			v |= buf[0];
40366716431bSRobert Mustacchi 		}
40376716431bSRobert Mustacchi 
40386716431bSRobert Mustacchi 		switch (len) {
40396716431bSRobert Mustacchi 		case 4:
40406716431bSRobert Mustacchi 			*(uint32_t *)valp = v;
40416716431bSRobert Mustacchi 			break;
40426716431bSRobert Mustacchi 		case 2:
40436716431bSRobert Mustacchi 			*(uint16_t *)valp = v;
40446716431bSRobert Mustacchi 			break;
40456716431bSRobert Mustacchi 		case 1:
40466716431bSRobert Mustacchi 			*(uint8_t *)valp = v;
40476716431bSRobert Mustacchi 			break;
40486716431bSRobert Mustacchi 		}
40496716431bSRobert Mustacchi 	}
40506716431bSRobert Mustacchi 	return (err);
40516716431bSRobert Mustacchi }
40526716431bSRobert Mustacchi 
40536716431bSRobert Mustacchi /*
40546716431bSRobert Mustacchi  * Attach / detach / disconnect / reconnect management
40556716431bSRobert Mustacchi  */
40566716431bSRobert Mustacchi static int
usbgem_open_pipes(struct usbgem_dev * dp)40576716431bSRobert Mustacchi usbgem_open_pipes(struct usbgem_dev *dp)
40586716431bSRobert Mustacchi {
40596716431bSRobert Mustacchi 	int			i;
40606716431bSRobert Mustacchi 	int			ret;
40616716431bSRobert Mustacchi 	int			ifnum;
40626716431bSRobert Mustacchi 	int			alt;
40636716431bSRobert Mustacchi 	usb_client_dev_data_t	*reg_data;
40646716431bSRobert Mustacchi 	usb_ep_data_t		*ep_tree_node;
40656716431bSRobert Mustacchi 
40666716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
40676716431bSRobert Mustacchi 
40686716431bSRobert Mustacchi 	ifnum = dp->ugc.usbgc_ifnum;
40696716431bSRobert Mustacchi 	alt = dp->ugc.usbgc_alt;
40706716431bSRobert Mustacchi 
40716716431bSRobert Mustacchi 	ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
40726716431bSRobert Mustacchi 	    0, USB_EP_ATTR_BULK, USB_EP_DIR_IN);
40736716431bSRobert Mustacchi 	if (ep_tree_node == NULL) {
40746716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: ep_bulkin is NULL",
40756716431bSRobert Mustacchi 		    dp->name, __func__);
40766716431bSRobert Mustacchi 		goto err;
40776716431bSRobert Mustacchi 	}
40786716431bSRobert Mustacchi 	dp->ep_bulkin = &ep_tree_node->ep_descr;
40796716431bSRobert Mustacchi 
40806716431bSRobert Mustacchi 	ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
40816716431bSRobert Mustacchi 	    0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
40826716431bSRobert Mustacchi 	if (ep_tree_node == NULL) {
40836716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: ep_bulkout is NULL",
40846716431bSRobert Mustacchi 		    dp->name, __func__);
40856716431bSRobert Mustacchi 		goto err;
40866716431bSRobert Mustacchi 	}
40876716431bSRobert Mustacchi 	dp->ep_bulkout = &ep_tree_node->ep_descr;
40886716431bSRobert Mustacchi 
40896716431bSRobert Mustacchi 	ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
40906716431bSRobert Mustacchi 	    0, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
40916716431bSRobert Mustacchi 	if (ep_tree_node) {
40926716431bSRobert Mustacchi 		dp->ep_intr = &ep_tree_node->ep_descr;
40936716431bSRobert Mustacchi 	} else {
40946716431bSRobert Mustacchi 		/* don't care */
40956716431bSRobert Mustacchi 		DPRINTF(1, (CE_CONT, "!%s: %s: ep_intr is NULL",
40966716431bSRobert Mustacchi 		    dp->name, __func__));
40976716431bSRobert Mustacchi 		dp->ep_intr = NULL;
40986716431bSRobert Mustacchi 	}
40996716431bSRobert Mustacchi 
41006716431bSRobert Mustacchi 	/* XXX -- no need to open default pipe */
41016716431bSRobert Mustacchi 
41026716431bSRobert Mustacchi 	/* open bulk out pipe */
41036716431bSRobert Mustacchi 	bzero(&dp->policy_bulkout, sizeof (usb_pipe_policy_t));
41046716431bSRobert Mustacchi 	dp->policy_bulkout.pp_max_async_reqs = 1;
41056716431bSRobert Mustacchi 
41066716431bSRobert Mustacchi 	if ((ret = usb_pipe_open(dp->dip,
41076716431bSRobert Mustacchi 	    dp->ep_bulkout, &dp->policy_bulkout, USB_FLAGS_SLEEP,
41086716431bSRobert Mustacchi 	    &dp->bulkout_pipe)) != USB_SUCCESS) {
41096716431bSRobert Mustacchi 		cmn_err(CE_WARN,
41106716431bSRobert Mustacchi 		    "!%s: %s: err:%x: failed to open bulk-out pipe",
41116716431bSRobert Mustacchi 		    dp->name, __func__, ret);
41126716431bSRobert Mustacchi 		dp->bulkout_pipe = NULL;
41136716431bSRobert Mustacchi 		goto err;
41146716431bSRobert Mustacchi 	}
41156716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: bulkout_pipe opened successfully",
41166716431bSRobert Mustacchi 	    dp->name, __func__));
41176716431bSRobert Mustacchi 
41186716431bSRobert Mustacchi 	/* open bulk in pipe */
41196716431bSRobert Mustacchi 	bzero(&dp->policy_bulkin, sizeof (usb_pipe_policy_t));
41206716431bSRobert Mustacchi 	dp->policy_bulkin.pp_max_async_reqs = 1;
41216716431bSRobert Mustacchi 	if ((ret = usb_pipe_open(dp->dip,
41226716431bSRobert Mustacchi 	    dp->ep_bulkin, &dp->policy_bulkin, USB_FLAGS_SLEEP,
41236716431bSRobert Mustacchi 	    &dp->bulkin_pipe)) != USB_SUCCESS) {
41246716431bSRobert Mustacchi 		cmn_err(CE_WARN,
41256716431bSRobert Mustacchi 		    "!%s: %s: ret:%x failed to open bulk-in pipe",
41266716431bSRobert Mustacchi 		    dp->name, __func__, ret);
41276716431bSRobert Mustacchi 		dp->bulkin_pipe = NULL;
41286716431bSRobert Mustacchi 		goto err;
41296716431bSRobert Mustacchi 	}
41306716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: bulkin_pipe opened successfully",
41316716431bSRobert Mustacchi 	    dp->name, __func__));
41326716431bSRobert Mustacchi 
41336716431bSRobert Mustacchi 	if (dp->ep_intr) {
41346716431bSRobert Mustacchi 		/* open interrupt pipe */
41356716431bSRobert Mustacchi 		bzero(&dp->policy_interrupt, sizeof (usb_pipe_policy_t));
41366716431bSRobert Mustacchi 		dp->policy_interrupt.pp_max_async_reqs = 1;
41376716431bSRobert Mustacchi 		if ((ret = usb_pipe_open(dp->dip, dp->ep_intr,
41386716431bSRobert Mustacchi 		    &dp->policy_interrupt, USB_FLAGS_SLEEP,
41396716431bSRobert Mustacchi 		    &dp->intr_pipe)) != USB_SUCCESS) {
41406716431bSRobert Mustacchi 			cmn_err(CE_WARN,
41416716431bSRobert Mustacchi 			    "!%s: %s: ret:%x failed to open interrupt pipe",
41426716431bSRobert Mustacchi 			    dp->name, __func__, ret);
41436716431bSRobert Mustacchi 			dp->intr_pipe = NULL;
41446716431bSRobert Mustacchi 			goto err;
41456716431bSRobert Mustacchi 		}
41466716431bSRobert Mustacchi 	}
41476716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: intr_pipe opened successfully",
41486716431bSRobert Mustacchi 	    dp->name, __func__));
41496716431bSRobert Mustacchi 
41506716431bSRobert Mustacchi 	return (USB_SUCCESS);
41516716431bSRobert Mustacchi 
41526716431bSRobert Mustacchi err:
41536716431bSRobert Mustacchi 	if (dp->bulkin_pipe) {
41546716431bSRobert Mustacchi 		usb_pipe_close(dp->dip,
41556716431bSRobert Mustacchi 		    dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
41566716431bSRobert Mustacchi 		dp->bulkin_pipe = NULL;
41576716431bSRobert Mustacchi 	}
41586716431bSRobert Mustacchi 	if (dp->bulkout_pipe) {
41596716431bSRobert Mustacchi 		usb_pipe_close(dp->dip,
41606716431bSRobert Mustacchi 		    dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
41616716431bSRobert Mustacchi 		dp->bulkout_pipe = NULL;
41626716431bSRobert Mustacchi 	}
41636716431bSRobert Mustacchi 	if (dp->intr_pipe) {
41646716431bSRobert Mustacchi 		usb_pipe_close(dp->dip,
41656716431bSRobert Mustacchi 		    dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0);
41666716431bSRobert Mustacchi 		dp->intr_pipe = NULL;
41676716431bSRobert Mustacchi 	}
41686716431bSRobert Mustacchi 
41696716431bSRobert Mustacchi 	return (USB_FAILURE);
41706716431bSRobert Mustacchi }
41716716431bSRobert Mustacchi 
41726716431bSRobert Mustacchi static int
usbgem_close_pipes(struct usbgem_dev * dp)41736716431bSRobert Mustacchi usbgem_close_pipes(struct usbgem_dev *dp)
41746716431bSRobert Mustacchi {
41756716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
41766716431bSRobert Mustacchi 
41776716431bSRobert Mustacchi 	if (dp->intr_pipe) {
41786716431bSRobert Mustacchi 		usb_pipe_close(dp->dip,
41796716431bSRobert Mustacchi 		    dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0);
41806716431bSRobert Mustacchi 		dp->intr_pipe = NULL;
41816716431bSRobert Mustacchi 	}
41826716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: 1", dp->name, __func__));
41836716431bSRobert Mustacchi 
41846716431bSRobert Mustacchi 	ASSERT(dp->bulkin_pipe);
41856716431bSRobert Mustacchi 	usb_pipe_close(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
41866716431bSRobert Mustacchi 	dp->bulkin_pipe = NULL;
41876716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: 2", dp->name, __func__));
41886716431bSRobert Mustacchi 
41896716431bSRobert Mustacchi 	ASSERT(dp->bulkout_pipe);
41906716431bSRobert Mustacchi 	usb_pipe_close(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
41916716431bSRobert Mustacchi 	dp->bulkout_pipe = NULL;
41926716431bSRobert Mustacchi 	DPRINTF(1, (CE_CONT, "!%s: %s: 3", dp->name, __func__));
41936716431bSRobert Mustacchi 
41946716431bSRobert Mustacchi 	return (USB_SUCCESS);
41956716431bSRobert Mustacchi }
41966716431bSRobert Mustacchi 
41976716431bSRobert Mustacchi #define	FREEZE_GRACEFUL		(B_TRUE)
41986716431bSRobert Mustacchi #define	FREEZE_NO_GRACEFUL	(B_FALSE)
41996716431bSRobert Mustacchi static int
usbgem_freeze_device(struct usbgem_dev * dp,boolean_t graceful)42006716431bSRobert Mustacchi usbgem_freeze_device(struct usbgem_dev *dp, boolean_t graceful)
42016716431bSRobert Mustacchi {
42026716431bSRobert Mustacchi 	DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__));
42036716431bSRobert Mustacchi 
42046716431bSRobert Mustacchi 	/* stop nic activity */
42056716431bSRobert Mustacchi 	(void) usbgem_mac_stop(dp, MAC_STATE_DISCONNECTED, graceful);
42066716431bSRobert Mustacchi 
42076716431bSRobert Mustacchi 	/*
42086716431bSRobert Mustacchi 	 * Here we free all memory resource allocated, because it will
42096716431bSRobert Mustacchi 	 * cause to panic the system that we free usb_bulk_req objects
42106716431bSRobert Mustacchi 	 * during the usb device is disconnected.
42116716431bSRobert Mustacchi 	 */
42126716431bSRobert Mustacchi 	(void) usbgem_free_memory(dp);
42136716431bSRobert Mustacchi 
42146716431bSRobert Mustacchi 	return (USB_SUCCESS);
42156716431bSRobert Mustacchi }
42166716431bSRobert Mustacchi 
42176716431bSRobert Mustacchi static int
usbgem_disconnect_cb(dev_info_t * dip)42186716431bSRobert Mustacchi usbgem_disconnect_cb(dev_info_t *dip)
42196716431bSRobert Mustacchi {
42206716431bSRobert Mustacchi 	int	ret;
42216716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
42226716431bSRobert Mustacchi 
42236716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
42246716431bSRobert Mustacchi 
42256716431bSRobert Mustacchi 	cmn_err(CE_NOTE, "!%s: the usb device was disconnected (dp=%p)",
42266716431bSRobert Mustacchi 	    dp->name, (void *)dp);
42276716431bSRobert Mustacchi 
42286716431bSRobert Mustacchi 	/* start serialize */
42296716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
42306716431bSRobert Mustacchi 
42316716431bSRobert Mustacchi 	ret = usbgem_freeze_device(dp, 0);
42326716431bSRobert Mustacchi 
42336716431bSRobert Mustacchi 	/* end of serialize */
42346716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
42356716431bSRobert Mustacchi 
42366716431bSRobert Mustacchi 	return (ret);
42376716431bSRobert Mustacchi }
42386716431bSRobert Mustacchi 
42396716431bSRobert Mustacchi static int
usbgem_recover_device(struct usbgem_dev * dp)42406716431bSRobert Mustacchi usbgem_recover_device(struct usbgem_dev	*dp)
42416716431bSRobert Mustacchi {
42426716431bSRobert Mustacchi 	int	err;
42436716431bSRobert Mustacchi 
42446716431bSRobert Mustacchi 	DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__));
42456716431bSRobert Mustacchi 
42466716431bSRobert Mustacchi 	err = USB_SUCCESS;
42476716431bSRobert Mustacchi 
42486716431bSRobert Mustacchi 	/* reinitialize the usb connection */
42496716431bSRobert Mustacchi 	usbgem_close_pipes(dp);
42506716431bSRobert Mustacchi 	if ((err = usbgem_open_pipes(dp)) != USB_SUCCESS) {
42516716431bSRobert Mustacchi 		goto x;
42526716431bSRobert Mustacchi 	}
42536716431bSRobert Mustacchi 
42546716431bSRobert Mustacchi 	/* initialize nic state */
42556716431bSRobert Mustacchi 	dp->mac_state = MAC_STATE_STOPPED;
42566716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_UNKNOWN;
42576716431bSRobert Mustacchi 
42586716431bSRobert Mustacchi 	/* allocate memory resources again */
42596716431bSRobert Mustacchi 	if ((err = usbgem_alloc_memory(dp)) != USB_SUCCESS) {
42606716431bSRobert Mustacchi 		goto x;
42616716431bSRobert Mustacchi 	}
42626716431bSRobert Mustacchi 
42636716431bSRobert Mustacchi 	/* restart nic and recover state */
42646716431bSRobert Mustacchi 	(void) usbgem_restart_nic(dp);
42656716431bSRobert Mustacchi 
42666716431bSRobert Mustacchi 	usbgem_mii_init(dp);
42676716431bSRobert Mustacchi 
42686716431bSRobert Mustacchi 	/* kick potentially stopped house keeping thread */
42696716431bSRobert Mustacchi 	cv_signal(&dp->link_watcher_wait_cv);
42706716431bSRobert Mustacchi x:
42716716431bSRobert Mustacchi 	return (err);
42726716431bSRobert Mustacchi }
42736716431bSRobert Mustacchi 
42746716431bSRobert Mustacchi static int
usbgem_reconnect_cb(dev_info_t * dip)42756716431bSRobert Mustacchi usbgem_reconnect_cb(dev_info_t *dip)
42766716431bSRobert Mustacchi {
42776716431bSRobert Mustacchi 	int	err = USB_SUCCESS;
42786716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
42796716431bSRobert Mustacchi 
42806716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
42816716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: dp=%p", ddi_get_name(dip), dp));
42826716431bSRobert Mustacchi #ifdef notdef
42836716431bSRobert Mustacchi 	/* check device changes after disconnect */
42846716431bSRobert Mustacchi 	if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1,
42856716431bSRobert Mustacchi 	    USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
42866716431bSRobert Mustacchi 		cmn_err(CE_CONT,
42876716431bSRobert Mustacchi 		    "!%s: no or different device installed", dp->name);
42886716431bSRobert Mustacchi 		return (DDI_SUCCESS);
42896716431bSRobert Mustacchi 	}
42906716431bSRobert Mustacchi #endif
42916716431bSRobert Mustacchi 	cmn_err(CE_NOTE, "%s: the usb device was reconnected", dp->name);
42926716431bSRobert Mustacchi 
42936716431bSRobert Mustacchi 	/* start serialize */
42946716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
42956716431bSRobert Mustacchi 
42966716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
42976716431bSRobert Mustacchi 		err = usbgem_recover_device(dp);
42986716431bSRobert Mustacchi 	}
42996716431bSRobert Mustacchi 
43006716431bSRobert Mustacchi 	/* end of serialize */
43016716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
43026716431bSRobert Mustacchi 
43036716431bSRobert Mustacchi 	return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
43046716431bSRobert Mustacchi }
43056716431bSRobert Mustacchi 
43066716431bSRobert Mustacchi int
usbgem_suspend(dev_info_t * dip)43076716431bSRobert Mustacchi usbgem_suspend(dev_info_t *dip)
43086716431bSRobert Mustacchi {
43096716431bSRobert Mustacchi 	int	err = USB_SUCCESS;
43106716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
43116716431bSRobert Mustacchi 
43126716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
43136716431bSRobert Mustacchi 
43146716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__));
43156716431bSRobert Mustacchi 
43166716431bSRobert Mustacchi 	/* start serialize */
43176716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
43186716431bSRobert Mustacchi 
43196716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
43206716431bSRobert Mustacchi 		err = usbgem_freeze_device(dp, STOP_GRACEFUL);
43216716431bSRobert Mustacchi 	}
43226716431bSRobert Mustacchi 
43236716431bSRobert Mustacchi 	/* end of serialize */
43246716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
43256716431bSRobert Mustacchi 
43266716431bSRobert Mustacchi 	return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
43276716431bSRobert Mustacchi }
43286716431bSRobert Mustacchi 
43296716431bSRobert Mustacchi int
usbgem_resume(dev_info_t * dip)43306716431bSRobert Mustacchi usbgem_resume(dev_info_t *dip)
43316716431bSRobert Mustacchi {
43326716431bSRobert Mustacchi 	int	err = USB_SUCCESS;
43336716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
43346716431bSRobert Mustacchi 
43356716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
43366716431bSRobert Mustacchi 
43376716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__));
43386716431bSRobert Mustacchi #ifdef notdef
43396716431bSRobert Mustacchi 	/* check device changes after disconnect */
43406716431bSRobert Mustacchi 	if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1,
43416716431bSRobert Mustacchi 	    USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
43426716431bSRobert Mustacchi 		cmn_err(CE_CONT,
43436716431bSRobert Mustacchi 		    "!%s: no or different device installed", dp->name);
43446716431bSRobert Mustacchi 		return (DDI_SUCCESS);
43456716431bSRobert Mustacchi 	}
43466716431bSRobert Mustacchi #endif
43476716431bSRobert Mustacchi 	/* start serialize */
43486716431bSRobert Mustacchi 	rw_enter(&dp->dev_state_lock, RW_WRITER);
43496716431bSRobert Mustacchi 
43506716431bSRobert Mustacchi 	if (dp->mac_state == MAC_STATE_DISCONNECTED) {
43516716431bSRobert Mustacchi 		err = usbgem_recover_device(dp);
43526716431bSRobert Mustacchi 	}
43536716431bSRobert Mustacchi 
43546716431bSRobert Mustacchi 	/* end of serialize */
43556716431bSRobert Mustacchi 	rw_exit(&dp->dev_state_lock);
43566716431bSRobert Mustacchi 
43576716431bSRobert Mustacchi 	return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
43586716431bSRobert Mustacchi }
43596716431bSRobert Mustacchi 
43606716431bSRobert Mustacchi #define	USBGEM_LOCAL_DATA_SIZE(gc)	\
43616716431bSRobert Mustacchi 	(sizeof (struct usbgem_dev) + USBGEM_MCALLOC)
43626716431bSRobert Mustacchi 
43636716431bSRobert Mustacchi struct usbgem_dev *
usbgem_do_attach(dev_info_t * dip,struct usbgem_conf * gc,void * lp,int lmsize)43646716431bSRobert Mustacchi usbgem_do_attach(dev_info_t *dip,
4365720b1687SToomas Soome     struct usbgem_conf *gc, void *lp, int lmsize)
43666716431bSRobert Mustacchi {
43676716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
43686716431bSRobert Mustacchi 	int			i;
43696716431bSRobert Mustacchi 	mac_register_t		*macp = NULL;
43706716431bSRobert Mustacchi 	int			ret;
43716716431bSRobert Mustacchi 	int			unit;
43726716431bSRobert Mustacchi 	int			err;
43736716431bSRobert Mustacchi 
43746716431bSRobert Mustacchi 	unit = ddi_get_instance(dip);
43756716431bSRobert Mustacchi 
43766716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!usbgem%d: %s: called", unit, __func__));
43776716431bSRobert Mustacchi 
43786716431bSRobert Mustacchi 	/*
43796716431bSRobert Mustacchi 	 * Allocate soft data structure
43806716431bSRobert Mustacchi 	 */
43816716431bSRobert Mustacchi 	dp = kmem_zalloc(USBGEM_LOCAL_DATA_SIZE(gc), KM_SLEEP);
43826716431bSRobert Mustacchi 	if (dp == NULL) {
43836716431bSRobert Mustacchi 		return (NULL);
43846716431bSRobert Mustacchi 	}
4385ceb6b962SRobert Mustacchi 
43866716431bSRobert Mustacchi 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
43876716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!gem%d: %s: mac_alloc failed",
43886716431bSRobert Mustacchi 		    unit, __func__);
43896716431bSRobert Mustacchi 		return (NULL);
43906716431bSRobert Mustacchi 	}
43916716431bSRobert Mustacchi 
43926716431bSRobert Mustacchi 	/* link to private area */
43936716431bSRobert Mustacchi 	dp->private = lp;
43946716431bSRobert Mustacchi 	dp->priv_size = lmsize;
43956716431bSRobert Mustacchi 	dp->mc_list = (struct mcast_addr *)&dp[1];
43966716431bSRobert Mustacchi 
43976716431bSRobert Mustacchi 	dp->dip = dip;
43986716431bSRobert Mustacchi 	bcopy(gc->usbgc_name, dp->name, USBGEM_NAME_LEN);
43996716431bSRobert Mustacchi 
44006716431bSRobert Mustacchi 	/*
44016716431bSRobert Mustacchi 	 * register with usb service
44026716431bSRobert Mustacchi 	 */
44036716431bSRobert Mustacchi 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
44046716431bSRobert Mustacchi 		cmn_err(CE_WARN,
44056716431bSRobert Mustacchi 		    "%s: %s: usb_client_attach failed",
44066716431bSRobert Mustacchi 		    dp->name, __func__);
44076716431bSRobert Mustacchi 		goto err_free_private;
44086716431bSRobert Mustacchi 	}
44096716431bSRobert Mustacchi 
44106716431bSRobert Mustacchi 	if (usb_get_dev_data(dip, &dp->reg_data,
44116716431bSRobert Mustacchi 	    USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
44126716431bSRobert Mustacchi 		dp->reg_data = NULL;
44136716431bSRobert Mustacchi 		goto err_unregister_client;
44146716431bSRobert Mustacchi 	}
44156716431bSRobert Mustacchi #ifdef USBGEM_DEBUG_LEVEL
44166716431bSRobert Mustacchi 	usb_print_descr_tree(dp->dip, dp->reg_data);
44176716431bSRobert Mustacchi #endif
44186716431bSRobert Mustacchi 
44196716431bSRobert Mustacchi 	if (usbgem_open_pipes(dp) != USB_SUCCESS) {
44206716431bSRobert Mustacchi 		/* failed to open pipes */
44216716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: failed to open pipes",
44226716431bSRobert Mustacchi 		    dp->name, __func__);
44236716431bSRobert Mustacchi 		goto err_unregister_client;
44246716431bSRobert Mustacchi 	}
44256716431bSRobert Mustacchi 
44266716431bSRobert Mustacchi 	/*
44276716431bSRobert Mustacchi 	 * Initialize mutexs and condition variables
44286716431bSRobert Mustacchi 	 */
44296716431bSRobert Mustacchi 	mutex_init(&dp->rxlock, NULL, MUTEX_DRIVER, NULL);
44306716431bSRobert Mustacchi 	mutex_init(&dp->txlock, NULL, MUTEX_DRIVER, NULL);
44316716431bSRobert Mustacchi 	cv_init(&dp->rx_drain_cv, NULL, CV_DRIVER, NULL);
44326716431bSRobert Mustacchi 	cv_init(&dp->tx_drain_cv, NULL, CV_DRIVER, NULL);
44336716431bSRobert Mustacchi 	rw_init(&dp->dev_state_lock, NULL, RW_DRIVER, NULL);
44346716431bSRobert Mustacchi 	mutex_init(&dp->link_watcher_lock, NULL, MUTEX_DRIVER, NULL);
44356716431bSRobert Mustacchi 	cv_init(&dp->link_watcher_wait_cv, NULL, CV_DRIVER, NULL);
44366716431bSRobert Mustacchi 	sema_init(&dp->hal_op_lock, 1, NULL, SEMA_DRIVER, NULL);
44376716431bSRobert Mustacchi 	sema_init(&dp->rxfilter_lock, 1, NULL, SEMA_DRIVER, NULL);
44386716431bSRobert Mustacchi 
44396716431bSRobert Mustacchi 	/*
44406716431bSRobert Mustacchi 	 * Initialize configuration
44416716431bSRobert Mustacchi 	 */
44426716431bSRobert Mustacchi 	dp->ugc = *gc;
44436716431bSRobert Mustacchi 
44446716431bSRobert Mustacchi 	dp->mtu = ETHERMTU;
44456716431bSRobert Mustacchi 	dp->rxmode = 0;
44466716431bSRobert Mustacchi 	dp->speed = USBGEM_SPD_10;	/* default is 10Mbps */
44476716431bSRobert Mustacchi 	dp->full_duplex = B_FALSE;	/* default is half */
44486716431bSRobert Mustacchi 	dp->flow_control = FLOW_CONTROL_NONE;
44496716431bSRobert Mustacchi 
44506716431bSRobert Mustacchi 	dp->nic_state = NIC_STATE_STOPPED;
44516716431bSRobert Mustacchi 	dp->mac_state = MAC_STATE_STOPPED;
44526716431bSRobert Mustacchi 	dp->mii_state = MII_STATE_UNKNOWN;
44536716431bSRobert Mustacchi 
44546716431bSRobert Mustacchi 	/* performance tuning parameters */
44556716431bSRobert Mustacchi 	dp->txthr = ETHERMAX;		/* tx fifo threshoold */
44566716431bSRobert Mustacchi 	dp->txmaxdma = 16*4;		/* tx max dma burst size */
44576716431bSRobert Mustacchi 	dp->rxthr = 128;		/* rx fifo threshoold */
44586716431bSRobert Mustacchi 	dp->rxmaxdma = 16*4;		/* rx max dma burst size */
44596716431bSRobert Mustacchi 
44606716431bSRobert Mustacchi 	/*
44616716431bSRobert Mustacchi 	 * Get media mode infomation from .conf file
44626716431bSRobert Mustacchi 	 */
44636716431bSRobert Mustacchi 	usbgem_read_conf(dp);
44646716431bSRobert Mustacchi 
44656716431bSRobert Mustacchi 	/* rx_buf_len depend on MTU */
44666716431bSRobert Mustacchi 	dp->rx_buf_len = MAXPKTBUF(dp) + dp->ugc.usbgc_rx_header_len;
44676716431bSRobert Mustacchi 
44686716431bSRobert Mustacchi 	/*
44696716431bSRobert Mustacchi 	 * Reset the chip
44706716431bSRobert Mustacchi 	 */
44716716431bSRobert Mustacchi 	if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) {
44726716431bSRobert Mustacchi 		cmn_err(CE_WARN,
44736716431bSRobert Mustacchi 		    "!%s: %s: failed to reset the usb device",
44746716431bSRobert Mustacchi 		    dp->name, __func__);
44756716431bSRobert Mustacchi 		goto err_destroy_locks;
44766716431bSRobert Mustacchi 	}
44776716431bSRobert Mustacchi 
44786716431bSRobert Mustacchi 	/*
44796716431bSRobert Mustacchi 	 * HW dependant paremeter initialization
44806716431bSRobert Mustacchi 	 */
44816716431bSRobert Mustacchi 	if (usbgem_hal_attach_chip(dp) != USB_SUCCESS) {
44826716431bSRobert Mustacchi 		cmn_err(CE_WARN,
44836716431bSRobert Mustacchi 		    "!%s: %s: failed to attach the usb device",
44846716431bSRobert Mustacchi 		    dp->name, __func__);
44856716431bSRobert Mustacchi 		goto err_destroy_locks;
44866716431bSRobert Mustacchi 	}
44876716431bSRobert Mustacchi 
44886716431bSRobert Mustacchi 	/* allocate resources */
44896716431bSRobert Mustacchi 	if (usbgem_alloc_memory(dp) != USB_SUCCESS) {
44906716431bSRobert Mustacchi 		goto err_destroy_locks;
44916716431bSRobert Mustacchi 	}
44926716431bSRobert Mustacchi 
44936716431bSRobert Mustacchi 	DPRINTF(0, (CE_CONT,
44946716431bSRobert Mustacchi 	    "!%s: %02x:%02x:%02x:%02x:%02x:%02x",
44956716431bSRobert Mustacchi 	    dp->name,
44966716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[0],
44976716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[1],
44986716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[2],
44996716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[3],
45006716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[4],
45016716431bSRobert Mustacchi 	    dp->dev_addr.ether_addr_octet[5]));
45026716431bSRobert Mustacchi 
45036716431bSRobert Mustacchi 	/* copy mac address */
45046716431bSRobert Mustacchi 	dp->cur_addr = dp->dev_addr;
45056716431bSRobert Mustacchi 
45066716431bSRobert Mustacchi 	/* pre-calculated tx timeout in second for performance */
45076716431bSRobert Mustacchi 	dp->bulkout_timeout =
45086716431bSRobert Mustacchi 	    dp->ugc.usbgc_tx_timeout / drv_usectohz(1000*1000);
45096716431bSRobert Mustacchi 
45106716431bSRobert Mustacchi 	usbgem_gld3_init(dp, macp);
45116716431bSRobert Mustacchi 
45126716431bSRobert Mustacchi 	/* Probe MII phy (scan phy) */
45136716431bSRobert Mustacchi 	dp->mii_lpable = 0;
45146716431bSRobert Mustacchi 	dp->mii_advert = 0;
45156716431bSRobert Mustacchi 	dp->mii_exp = 0;
45166716431bSRobert Mustacchi 	dp->mii_ctl1000 = 0;
45176716431bSRobert Mustacchi 	dp->mii_stat1000 = 0;
45186716431bSRobert Mustacchi 
45196716431bSRobert Mustacchi 	dp->mii_status_ro = 0;
45206716431bSRobert Mustacchi 	dp->mii_xstatus_ro = 0;
45216716431bSRobert Mustacchi 
45226716431bSRobert Mustacchi 	if (usbgem_mii_probe(dp) != USB_SUCCESS) {
45236716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: mii_probe failed",
45246716431bSRobert Mustacchi 		    dp->name, __func__);
45256716431bSRobert Mustacchi 		goto err_free_memory;
45266716431bSRobert Mustacchi 	}
45276716431bSRobert Mustacchi 
45286716431bSRobert Mustacchi 	/* mask unsupported abilities */
45296716431bSRobert Mustacchi 	dp->anadv_autoneg &= BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
45306716431bSRobert Mustacchi 	dp->anadv_1000fdx &=
45316716431bSRobert Mustacchi 	    BOOLEAN(dp->mii_xstatus &
45326716431bSRobert Mustacchi 	    (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASET_FD));
45336716431bSRobert Mustacchi 	dp->anadv_1000hdx &=
45346716431bSRobert Mustacchi 	    BOOLEAN(dp->mii_xstatus &
45356716431bSRobert Mustacchi 	    (MII_XSTATUS_1000BASEX | MII_XSTATUS_1000BASET));
45366716431bSRobert Mustacchi 	dp->anadv_100t4 &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
45376716431bSRobert Mustacchi 	dp->anadv_100fdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
45386716431bSRobert Mustacchi 	dp->anadv_100hdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
45396716431bSRobert Mustacchi 	dp->anadv_10fdx &= BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
45406716431bSRobert Mustacchi 	dp->anadv_10hdx &= BOOLEAN(dp->mii_status & MII_STATUS_10);
45416716431bSRobert Mustacchi 
45426716431bSRobert Mustacchi 	if (usbgem_mii_init(dp) != USB_SUCCESS) {
45436716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: %s: mii_init failed",
45446716431bSRobert Mustacchi 		    dp->name, __func__);
45456716431bSRobert Mustacchi 		goto err_free_memory;
45466716431bSRobert Mustacchi 	}
45476716431bSRobert Mustacchi 
45486716431bSRobert Mustacchi 	/*
45496716431bSRobert Mustacchi 	 * Add interrupt to system.
45506716431bSRobert Mustacchi 	 */
45516716431bSRobert Mustacchi 	if (ret = mac_register(macp, &dp->mh)) {
45526716431bSRobert Mustacchi 		cmn_err(CE_WARN, "!%s: mac_register failed, error:%d",
45536716431bSRobert Mustacchi 		    dp->name, ret);
45546716431bSRobert Mustacchi 		goto err_release_stats;
45556716431bSRobert Mustacchi 	}
45566716431bSRobert Mustacchi 	mac_free(macp);
45576716431bSRobert Mustacchi 	macp = NULL;
4558ceb6b962SRobert Mustacchi 
45596716431bSRobert Mustacchi 	if (usb_register_hotplug_cbs(dip,
45606716431bSRobert Mustacchi 	    usbgem_suspend, usbgem_resume) != USB_SUCCESS) {
45616716431bSRobert Mustacchi 		cmn_err(CE_WARN,
45626716431bSRobert Mustacchi 		    "!%s: %s: failed to register hotplug cbs",
45636716431bSRobert Mustacchi 		    dp->name, __func__);
45646716431bSRobert Mustacchi 		goto err_unregister_gld;
45656716431bSRobert Mustacchi 	}
45666716431bSRobert Mustacchi 
45676716431bSRobert Mustacchi 	/* reset mii and start mii link watcher */
45686716431bSRobert Mustacchi 	if (usbgem_mii_start(dp) != USB_SUCCESS) {
45696716431bSRobert Mustacchi 		goto err_unregister_hotplug;
45706716431bSRobert Mustacchi 	}
45716716431bSRobert Mustacchi 
45726716431bSRobert Mustacchi 	/* start tx watchdow watcher */
45736716431bSRobert Mustacchi 	if (usbgem_tx_watcher_start(dp)) {
45746716431bSRobert Mustacchi 		goto err_usbgem_mii_stop;
45756716431bSRobert Mustacchi 	}
45766716431bSRobert Mustacchi 
45776716431bSRobert Mustacchi 	ddi_set_driver_private(dip, (caddr_t)dp);
45786716431bSRobert Mustacchi 
45796716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!%s: %s: return: success", dp->name, __func__));
45806716431bSRobert Mustacchi 
45816716431bSRobert Mustacchi 	return (dp);
45826716431bSRobert Mustacchi 
45836716431bSRobert Mustacchi err_usbgem_mii_stop:
45846716431bSRobert Mustacchi 	usbgem_mii_stop(dp);
45856716431bSRobert Mustacchi 
45866716431bSRobert Mustacchi err_unregister_hotplug:
45876716431bSRobert Mustacchi 	usb_unregister_hotplug_cbs(dip);
45886716431bSRobert Mustacchi 
45896716431bSRobert Mustacchi err_unregister_gld:
45906716431bSRobert Mustacchi 	mac_unregister(dp->mh);
45916716431bSRobert Mustacchi 
45926716431bSRobert Mustacchi err_release_stats:
45936716431bSRobert Mustacchi err_free_memory:
45946716431bSRobert Mustacchi 	usbgem_free_memory(dp);
45956716431bSRobert Mustacchi 
45966716431bSRobert Mustacchi err_destroy_locks:
45976716431bSRobert Mustacchi 	cv_destroy(&dp->tx_drain_cv);
45986716431bSRobert Mustacchi 	cv_destroy(&dp->rx_drain_cv);
45996716431bSRobert Mustacchi 	mutex_destroy(&dp->txlock);
46006716431bSRobert Mustacchi 	mutex_destroy(&dp->rxlock);
46016716431bSRobert Mustacchi 	rw_destroy(&dp->dev_state_lock);
46026716431bSRobert Mustacchi 	mutex_destroy(&dp->link_watcher_lock);
46036716431bSRobert Mustacchi 	cv_destroy(&dp->link_watcher_wait_cv);
46046716431bSRobert Mustacchi 	sema_destroy(&dp->hal_op_lock);
46056716431bSRobert Mustacchi 	sema_destroy(&dp->rxfilter_lock);
46066716431bSRobert Mustacchi 
46076716431bSRobert Mustacchi err_close_pipes:
46086716431bSRobert Mustacchi 	(void) usbgem_close_pipes(dp);
46096716431bSRobert Mustacchi 
46106716431bSRobert Mustacchi err_unregister_client:
46116716431bSRobert Mustacchi 	usb_client_detach(dp->dip, dp->reg_data);
46126716431bSRobert Mustacchi 
46136716431bSRobert Mustacchi err_free_private:
46146716431bSRobert Mustacchi 	if (macp) {
46156716431bSRobert Mustacchi 		mac_free(macp);
46166716431bSRobert Mustacchi 	}
4617ceb6b962SRobert Mustacchi 
46186716431bSRobert Mustacchi 	kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(gc));
46196716431bSRobert Mustacchi 
46206716431bSRobert Mustacchi 	return (NULL);
46216716431bSRobert Mustacchi }
46226716431bSRobert Mustacchi 
46236716431bSRobert Mustacchi int
usbgem_do_detach(dev_info_t * dip)46246716431bSRobert Mustacchi usbgem_do_detach(dev_info_t *dip)
46256716431bSRobert Mustacchi {
46266716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
46276716431bSRobert Mustacchi 
46286716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
46296716431bSRobert Mustacchi 
46306716431bSRobert Mustacchi 	/* unregister with gld v3 */
46316716431bSRobert Mustacchi 	if (mac_unregister(dp->mh) != DDI_SUCCESS) {
46326716431bSRobert Mustacchi 		return (DDI_FAILURE);
46336716431bSRobert Mustacchi 	}
4634ceb6b962SRobert Mustacchi 
46356716431bSRobert Mustacchi 	/* unregister with hotplug service */
46366716431bSRobert Mustacchi 	usb_unregister_hotplug_cbs(dip);
46376716431bSRobert Mustacchi 
46386716431bSRobert Mustacchi 	/* stop tx watchdog watcher */
46396716431bSRobert Mustacchi 	usbgem_tx_watcher_stop(dp);
46406716431bSRobert Mustacchi 
46416716431bSRobert Mustacchi 	/* stop the link manager */
46426716431bSRobert Mustacchi 	usbgem_mii_stop(dp);
46436716431bSRobert Mustacchi 
46446716431bSRobert Mustacchi 	/* unregister with usb service */
46456716431bSRobert Mustacchi 	(void) usbgem_free_memory(dp);
46466716431bSRobert Mustacchi 	(void) usbgem_close_pipes(dp);
46476716431bSRobert Mustacchi 	usb_client_detach(dp->dip, dp->reg_data);
46486716431bSRobert Mustacchi 	dp->reg_data = NULL;
46496716431bSRobert Mustacchi 
46506716431bSRobert Mustacchi 	/* release locks and condition variables */
46516716431bSRobert Mustacchi 	mutex_destroy(&dp->txlock);
46526716431bSRobert Mustacchi 	mutex_destroy(&dp->rxlock);
46536716431bSRobert Mustacchi 	cv_destroy(&dp->tx_drain_cv);
46546716431bSRobert Mustacchi 	cv_destroy(&dp->rx_drain_cv);
46556716431bSRobert Mustacchi 	rw_destroy(&dp->dev_state_lock);
46566716431bSRobert Mustacchi 	mutex_destroy(&dp->link_watcher_lock);
46576716431bSRobert Mustacchi 	cv_destroy(&dp->link_watcher_wait_cv);
46586716431bSRobert Mustacchi 	sema_destroy(&dp->hal_op_lock);
46596716431bSRobert Mustacchi 	sema_destroy(&dp->rxfilter_lock);
46606716431bSRobert Mustacchi 
46616716431bSRobert Mustacchi 	/* release basic memory resources */
46626716431bSRobert Mustacchi 	kmem_free((caddr_t)(dp->private), dp->priv_size);
46636716431bSRobert Mustacchi 	kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(&dp->ugc));
46646716431bSRobert Mustacchi 
46656716431bSRobert Mustacchi 	DPRINTF(2, (CE_CONT, "!%s: %s: return: success",
46666716431bSRobert Mustacchi 	    ddi_driver_name(dip), __func__));
46676716431bSRobert Mustacchi 
46686716431bSRobert Mustacchi 	return (DDI_SUCCESS);
46696716431bSRobert Mustacchi }
46706716431bSRobert Mustacchi 
46716716431bSRobert Mustacchi int
usbgem_mod_init(struct dev_ops * dop,char * name)46726716431bSRobert Mustacchi usbgem_mod_init(struct dev_ops *dop, char *name)
46736716431bSRobert Mustacchi {
46746716431bSRobert Mustacchi 	major_t	major;
46756716431bSRobert Mustacchi 	major = ddi_name_to_major(name);
46766716431bSRobert Mustacchi 	if (major == DDI_MAJOR_T_NONE) {
46776716431bSRobert Mustacchi 		return (DDI_FAILURE);
46786716431bSRobert Mustacchi 	}
46796716431bSRobert Mustacchi 	mac_init_ops(dop, name);
4680ceb6b962SRobert Mustacchi 
46816716431bSRobert Mustacchi 	return (DDI_SUCCESS);
46826716431bSRobert Mustacchi }
46836716431bSRobert Mustacchi 
46846716431bSRobert Mustacchi void
usbgem_mod_fini(struct dev_ops * dop)46856716431bSRobert Mustacchi usbgem_mod_fini(struct dev_ops *dop)
46866716431bSRobert Mustacchi {
46876716431bSRobert Mustacchi 	mac_fini_ops(dop);
46886716431bSRobert Mustacchi }
46896716431bSRobert Mustacchi 
46906716431bSRobert Mustacchi int
usbgem_quiesce(dev_info_t * dip)46916716431bSRobert Mustacchi usbgem_quiesce(dev_info_t *dip)
46926716431bSRobert Mustacchi {
46936716431bSRobert Mustacchi 	struct usbgem_dev	*dp;
46946716431bSRobert Mustacchi 
46956716431bSRobert Mustacchi 	dp = USBGEM_GET_DEV(dip);
46966716431bSRobert Mustacchi 
46976716431bSRobert Mustacchi 	ASSERT(dp != NULL);
46986716431bSRobert Mustacchi 
46996716431bSRobert Mustacchi 	if (dp->mac_state != MAC_STATE_DISCONNECTED &&
47006716431bSRobert Mustacchi 	    dp->mac_state != MAC_STATE_STOPPED) {
47016716431bSRobert Mustacchi 		if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) {
47026716431bSRobert Mustacchi 			(void) usbgem_hal_reset_chip(dp);
47036716431bSRobert Mustacchi 		}
47046716431bSRobert Mustacchi 	}
47056716431bSRobert Mustacchi 
47066716431bSRobert Mustacchi 	/* devo_quiesce() must return DDI_SUCCESS always */
47076716431bSRobert Mustacchi 	return (DDI_SUCCESS);
47086716431bSRobert Mustacchi }
4709