xref: /onnv-gate/usr/src/uts/common/io/simnet/simnet.c (revision 10416:518cc966c6ed)
19815SRishi.Srivatsavai@Sun.COM /*
29815SRishi.Srivatsavai@Sun.COM  * CDDL HEADER START
39815SRishi.Srivatsavai@Sun.COM  *
49815SRishi.Srivatsavai@Sun.COM  * The contents of this file are subject to the terms of the
59815SRishi.Srivatsavai@Sun.COM  * Common Development and Distribution License (the "License").
69815SRishi.Srivatsavai@Sun.COM  * You may not use this file except in compliance with the License.
79815SRishi.Srivatsavai@Sun.COM  *
89815SRishi.Srivatsavai@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99815SRishi.Srivatsavai@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109815SRishi.Srivatsavai@Sun.COM  * See the License for the specific language governing permissions
119815SRishi.Srivatsavai@Sun.COM  * and limitations under the License.
129815SRishi.Srivatsavai@Sun.COM  *
139815SRishi.Srivatsavai@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149815SRishi.Srivatsavai@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159815SRishi.Srivatsavai@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169815SRishi.Srivatsavai@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179815SRishi.Srivatsavai@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189815SRishi.Srivatsavai@Sun.COM  *
199815SRishi.Srivatsavai@Sun.COM  * CDDL HEADER END
209815SRishi.Srivatsavai@Sun.COM  */
219815SRishi.Srivatsavai@Sun.COM /*
229815SRishi.Srivatsavai@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
239815SRishi.Srivatsavai@Sun.COM  * Use is subject to license terms.
249815SRishi.Srivatsavai@Sun.COM  */
259815SRishi.Srivatsavai@Sun.COM 
269815SRishi.Srivatsavai@Sun.COM /*
279815SRishi.Srivatsavai@Sun.COM  * Simulated network device (simnet) driver: simulates a pseudo GLDv3 network
289815SRishi.Srivatsavai@Sun.COM  * device. Can simulate an Ethernet or WiFi network device. In addition, another
299815SRishi.Srivatsavai@Sun.COM  * simnet instance can be attached as a peer to create a point-to-point link on
309815SRishi.Srivatsavai@Sun.COM  * the same system.
319815SRishi.Srivatsavai@Sun.COM  */
329815SRishi.Srivatsavai@Sun.COM 
339815SRishi.Srivatsavai@Sun.COM #include <sys/policy.h>
349815SRishi.Srivatsavai@Sun.COM #include <sys/conf.h>
359815SRishi.Srivatsavai@Sun.COM #include <sys/modctl.h>
369815SRishi.Srivatsavai@Sun.COM #include <sys/priv_names.h>
379815SRishi.Srivatsavai@Sun.COM #include <sys/dlpi.h>
389815SRishi.Srivatsavai@Sun.COM #include <net/simnet.h>
399815SRishi.Srivatsavai@Sun.COM #include <sys/ethernet.h>
409815SRishi.Srivatsavai@Sun.COM #include <sys/mac.h>
419815SRishi.Srivatsavai@Sun.COM #include <sys/dls.h>
429815SRishi.Srivatsavai@Sun.COM #include <sys/mac_ether.h>
439815SRishi.Srivatsavai@Sun.COM #include <sys/mac_provider.h>
449815SRishi.Srivatsavai@Sun.COM #include <sys/mac_client_priv.h>
459815SRishi.Srivatsavai@Sun.COM #include <sys/vlan.h>
469815SRishi.Srivatsavai@Sun.COM #include <sys/random.h>
479815SRishi.Srivatsavai@Sun.COM #include <sys/sysmacros.h>
489815SRishi.Srivatsavai@Sun.COM #include <sys/list.h>
499815SRishi.Srivatsavai@Sun.COM #include <sys/strsubr.h>
509815SRishi.Srivatsavai@Sun.COM #include <sys/strsun.h>
519815SRishi.Srivatsavai@Sun.COM #include <sys/atomic.h>
529815SRishi.Srivatsavai@Sun.COM #include <sys/mac_wifi.h>
539815SRishi.Srivatsavai@Sun.COM #include <sys/mac_impl.h>
549815SRishi.Srivatsavai@Sun.COM #include <inet/wifi_ioctl.h>
559815SRishi.Srivatsavai@Sun.COM #include <sys/thread.h>
569815SRishi.Srivatsavai@Sun.COM #include <sys/synch.h>
57*10416SRishi.Srivatsavai@Sun.COM #include <sys/sunddi.h>
589815SRishi.Srivatsavai@Sun.COM 
599815SRishi.Srivatsavai@Sun.COM #include "simnet_impl.h"
609815SRishi.Srivatsavai@Sun.COM 
619815SRishi.Srivatsavai@Sun.COM #define	SIMNETINFO		"Simulated Network Driver"
629815SRishi.Srivatsavai@Sun.COM 
639815SRishi.Srivatsavai@Sun.COM static dev_info_t *simnet_dip;
64*10416SRishi.Srivatsavai@Sun.COM static ddi_taskq_t *simnet_rxq;
659815SRishi.Srivatsavai@Sun.COM 
669815SRishi.Srivatsavai@Sun.COM static int simnet_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
679815SRishi.Srivatsavai@Sun.COM static int simnet_attach(dev_info_t *, ddi_attach_cmd_t);
689815SRishi.Srivatsavai@Sun.COM static int simnet_detach(dev_info_t *, ddi_detach_cmd_t);
699815SRishi.Srivatsavai@Sun.COM static int simnet_ioc_create(void *, intptr_t, int, cred_t *, int *);
709815SRishi.Srivatsavai@Sun.COM static int simnet_ioc_delete(void *, intptr_t, int, cred_t *, int *);
719815SRishi.Srivatsavai@Sun.COM static int simnet_ioc_info(void *, intptr_t, int, cred_t *, int *);
729815SRishi.Srivatsavai@Sun.COM static int simnet_ioc_modify(void *, intptr_t, int, cred_t *, int *);
739815SRishi.Srivatsavai@Sun.COM static uint8_t *mcastaddr_lookup(simnet_dev_t *, const uint8_t *);
749815SRishi.Srivatsavai@Sun.COM 
759815SRishi.Srivatsavai@Sun.COM static dld_ioc_info_t simnet_ioc_list[] = {
769815SRishi.Srivatsavai@Sun.COM 	{SIMNET_IOC_CREATE, DLDCOPYINOUT, sizeof (simnet_ioc_create_t),
779815SRishi.Srivatsavai@Sun.COM 	    simnet_ioc_create, {PRIV_SYS_DL_CONFIG}},
789815SRishi.Srivatsavai@Sun.COM 	{SIMNET_IOC_DELETE, DLDCOPYIN, sizeof (simnet_ioc_delete_t),
799815SRishi.Srivatsavai@Sun.COM 	    simnet_ioc_delete, {PRIV_SYS_DL_CONFIG}},
809815SRishi.Srivatsavai@Sun.COM 	{SIMNET_IOC_INFO, DLDCOPYINOUT, sizeof (simnet_ioc_info_t),
819815SRishi.Srivatsavai@Sun.COM 	    simnet_ioc_info, {NULL}},
829815SRishi.Srivatsavai@Sun.COM 	{SIMNET_IOC_MODIFY, DLDCOPYIN, sizeof (simnet_ioc_modify_t),
839815SRishi.Srivatsavai@Sun.COM 	    simnet_ioc_modify, {PRIV_SYS_DL_CONFIG}},
849815SRishi.Srivatsavai@Sun.COM };
859815SRishi.Srivatsavai@Sun.COM 
869815SRishi.Srivatsavai@Sun.COM DDI_DEFINE_STREAM_OPS(simnet_dev_ops, nulldev, nulldev, simnet_attach,
879815SRishi.Srivatsavai@Sun.COM     simnet_detach, nodev, simnet_getinfo, D_MP, NULL,
889815SRishi.Srivatsavai@Sun.COM     ddi_quiesce_not_supported);
899815SRishi.Srivatsavai@Sun.COM 
909815SRishi.Srivatsavai@Sun.COM static struct modldrv simnet_modldrv = {
919815SRishi.Srivatsavai@Sun.COM 	&mod_driverops,		/* Type of module.  This one is a driver */
929815SRishi.Srivatsavai@Sun.COM 	SIMNETINFO,		/* short description */
939815SRishi.Srivatsavai@Sun.COM 	&simnet_dev_ops		/* driver specific ops */
949815SRishi.Srivatsavai@Sun.COM };
959815SRishi.Srivatsavai@Sun.COM 
969815SRishi.Srivatsavai@Sun.COM static struct modlinkage modlinkage = {
979815SRishi.Srivatsavai@Sun.COM 	MODREV_1, &simnet_modldrv, NULL
989815SRishi.Srivatsavai@Sun.COM };
999815SRishi.Srivatsavai@Sun.COM 
1009815SRishi.Srivatsavai@Sun.COM /* MAC callback function declarations */
1019815SRishi.Srivatsavai@Sun.COM static int simnet_m_start(void *);
1029815SRishi.Srivatsavai@Sun.COM static void simnet_m_stop(void *);
1039815SRishi.Srivatsavai@Sun.COM static int simnet_m_promisc(void *, boolean_t);
1049815SRishi.Srivatsavai@Sun.COM static int simnet_m_multicst(void *, boolean_t, const uint8_t *);
1059815SRishi.Srivatsavai@Sun.COM static int simnet_m_unicst(void *, const uint8_t *);
1069815SRishi.Srivatsavai@Sun.COM static int simnet_m_stat(void *, uint_t, uint64_t *);
1079815SRishi.Srivatsavai@Sun.COM static void simnet_m_ioctl(void *, queue_t *, mblk_t *);
1089815SRishi.Srivatsavai@Sun.COM static mblk_t *simnet_m_tx(void *, mblk_t *);
1099815SRishi.Srivatsavai@Sun.COM static int simnet_m_setprop(void *, const char *, mac_prop_id_t,
1109815SRishi.Srivatsavai@Sun.COM     uint_t, const void *);
1119815SRishi.Srivatsavai@Sun.COM static int simnet_m_getprop(void *, const char *, mac_prop_id_t,
1129815SRishi.Srivatsavai@Sun.COM     uint_t, uint_t, void *, uint_t *);
1139815SRishi.Srivatsavai@Sun.COM 
1149815SRishi.Srivatsavai@Sun.COM static mac_callbacks_t simnet_m_callbacks = {
1159815SRishi.Srivatsavai@Sun.COM 	(MC_IOCTL | MC_SETPROP | MC_GETPROP),
1169815SRishi.Srivatsavai@Sun.COM 	simnet_m_stat,
1179815SRishi.Srivatsavai@Sun.COM 	simnet_m_start,
1189815SRishi.Srivatsavai@Sun.COM 	simnet_m_stop,
1199815SRishi.Srivatsavai@Sun.COM 	simnet_m_promisc,
1209815SRishi.Srivatsavai@Sun.COM 	simnet_m_multicst,
1219815SRishi.Srivatsavai@Sun.COM 	simnet_m_unicst,
1229815SRishi.Srivatsavai@Sun.COM 	simnet_m_tx,
1239815SRishi.Srivatsavai@Sun.COM 	simnet_m_ioctl,
1249815SRishi.Srivatsavai@Sun.COM 	NULL,
1259815SRishi.Srivatsavai@Sun.COM 	NULL,
1269815SRishi.Srivatsavai@Sun.COM 	NULL,
1279815SRishi.Srivatsavai@Sun.COM 	simnet_m_setprop,
1289815SRishi.Srivatsavai@Sun.COM 	simnet_m_getprop
1299815SRishi.Srivatsavai@Sun.COM };
1309815SRishi.Srivatsavai@Sun.COM 
1319815SRishi.Srivatsavai@Sun.COM /*
1329815SRishi.Srivatsavai@Sun.COM  * simnet_dev_lock protects the simnet device list.
1339815SRishi.Srivatsavai@Sun.COM  * sd_instlock in each simnet_dev_t protects access to
1349815SRishi.Srivatsavai@Sun.COM  * a single simnet_dev_t.
1359815SRishi.Srivatsavai@Sun.COM  */
1369815SRishi.Srivatsavai@Sun.COM static krwlock_t	simnet_dev_lock;
1379815SRishi.Srivatsavai@Sun.COM static list_t		simnet_dev_list;
1389815SRishi.Srivatsavai@Sun.COM static int		simnet_count; /* Num of simnet instances */
1399815SRishi.Srivatsavai@Sun.COM 
1409815SRishi.Srivatsavai@Sun.COM int
1419815SRishi.Srivatsavai@Sun.COM _init(void)
1429815SRishi.Srivatsavai@Sun.COM {
1439815SRishi.Srivatsavai@Sun.COM 	int	status;
1449815SRishi.Srivatsavai@Sun.COM 
1459815SRishi.Srivatsavai@Sun.COM 	mac_init_ops(&simnet_dev_ops, "simnet");
1469815SRishi.Srivatsavai@Sun.COM 	status = mod_install(&modlinkage);
1479815SRishi.Srivatsavai@Sun.COM 	if (status != DDI_SUCCESS)
1489815SRishi.Srivatsavai@Sun.COM 		mac_fini_ops(&simnet_dev_ops);
1499815SRishi.Srivatsavai@Sun.COM 
1509815SRishi.Srivatsavai@Sun.COM 	return (status);
1519815SRishi.Srivatsavai@Sun.COM }
1529815SRishi.Srivatsavai@Sun.COM 
1539815SRishi.Srivatsavai@Sun.COM int
1549815SRishi.Srivatsavai@Sun.COM _fini(void)
1559815SRishi.Srivatsavai@Sun.COM {
1569815SRishi.Srivatsavai@Sun.COM 	int	status;
1579815SRishi.Srivatsavai@Sun.COM 
1589815SRishi.Srivatsavai@Sun.COM 	status = mod_remove(&modlinkage);
1599815SRishi.Srivatsavai@Sun.COM 	if (status == DDI_SUCCESS)
1609815SRishi.Srivatsavai@Sun.COM 		mac_fini_ops(&simnet_dev_ops);
1619815SRishi.Srivatsavai@Sun.COM 
1629815SRishi.Srivatsavai@Sun.COM 	return (status);
1639815SRishi.Srivatsavai@Sun.COM }
1649815SRishi.Srivatsavai@Sun.COM 
1659815SRishi.Srivatsavai@Sun.COM int
1669815SRishi.Srivatsavai@Sun.COM _info(struct modinfo *modinfop)
1679815SRishi.Srivatsavai@Sun.COM {
1689815SRishi.Srivatsavai@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1699815SRishi.Srivatsavai@Sun.COM }
1709815SRishi.Srivatsavai@Sun.COM 
171*10416SRishi.Srivatsavai@Sun.COM static boolean_t
1729815SRishi.Srivatsavai@Sun.COM simnet_init(void)
1739815SRishi.Srivatsavai@Sun.COM {
174*10416SRishi.Srivatsavai@Sun.COM 	if ((simnet_rxq = ddi_taskq_create(simnet_dip, "simnet", 1,
175*10416SRishi.Srivatsavai@Sun.COM 	    TASKQ_DEFAULTPRI, 0)) == NULL)
176*10416SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
1779815SRishi.Srivatsavai@Sun.COM 	rw_init(&simnet_dev_lock, NULL, RW_DEFAULT, NULL);
1789815SRishi.Srivatsavai@Sun.COM 	list_create(&simnet_dev_list, sizeof (simnet_dev_t),
1799815SRishi.Srivatsavai@Sun.COM 	    offsetof(simnet_dev_t, sd_listnode));
180*10416SRishi.Srivatsavai@Sun.COM 	return (B_TRUE);
1819815SRishi.Srivatsavai@Sun.COM }
1829815SRishi.Srivatsavai@Sun.COM 
1839815SRishi.Srivatsavai@Sun.COM static void
1849815SRishi.Srivatsavai@Sun.COM simnet_fini(void)
1859815SRishi.Srivatsavai@Sun.COM {
1869815SRishi.Srivatsavai@Sun.COM 	ASSERT(simnet_count == 0);
1879815SRishi.Srivatsavai@Sun.COM 	rw_destroy(&simnet_dev_lock);
1889815SRishi.Srivatsavai@Sun.COM 	list_destroy(&simnet_dev_list);
189*10416SRishi.Srivatsavai@Sun.COM 	ddi_taskq_destroy(simnet_rxq);
1909815SRishi.Srivatsavai@Sun.COM }
1919815SRishi.Srivatsavai@Sun.COM 
1929815SRishi.Srivatsavai@Sun.COM /*ARGSUSED*/
1939815SRishi.Srivatsavai@Sun.COM static int
1949815SRishi.Srivatsavai@Sun.COM simnet_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1959815SRishi.Srivatsavai@Sun.COM     void **result)
1969815SRishi.Srivatsavai@Sun.COM {
1979815SRishi.Srivatsavai@Sun.COM 	switch (infocmd) {
1989815SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
1999815SRishi.Srivatsavai@Sun.COM 		*result = simnet_dip;
2009815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2019815SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
2029815SRishi.Srivatsavai@Sun.COM 		*result = NULL;
2039815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2049815SRishi.Srivatsavai@Sun.COM 	}
2059815SRishi.Srivatsavai@Sun.COM 	return (DDI_FAILURE);
2069815SRishi.Srivatsavai@Sun.COM }
2079815SRishi.Srivatsavai@Sun.COM 
2089815SRishi.Srivatsavai@Sun.COM static int
2099815SRishi.Srivatsavai@Sun.COM simnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2109815SRishi.Srivatsavai@Sun.COM {
2119815SRishi.Srivatsavai@Sun.COM 	switch (cmd) {
2129815SRishi.Srivatsavai@Sun.COM 	case DDI_ATTACH:
2139815SRishi.Srivatsavai@Sun.COM 		if (ddi_get_instance(dip) != 0) {
2149815SRishi.Srivatsavai@Sun.COM 			/* we only allow instance 0 to attach */
2159815SRishi.Srivatsavai@Sun.COM 			return (DDI_FAILURE);
2169815SRishi.Srivatsavai@Sun.COM 		}
217*10416SRishi.Srivatsavai@Sun.COM 
2189815SRishi.Srivatsavai@Sun.COM 		if (dld_ioc_register(SIMNET_IOC, simnet_ioc_list,
2199815SRishi.Srivatsavai@Sun.COM 		    DLDIOCCNT(simnet_ioc_list)) != 0)
2209815SRishi.Srivatsavai@Sun.COM 			return (DDI_FAILURE);
2219815SRishi.Srivatsavai@Sun.COM 
2229815SRishi.Srivatsavai@Sun.COM 		simnet_dip = dip;
223*10416SRishi.Srivatsavai@Sun.COM 		if (!simnet_init())
224*10416SRishi.Srivatsavai@Sun.COM 			return (DDI_FAILURE);
2259815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2269815SRishi.Srivatsavai@Sun.COM 
2279815SRishi.Srivatsavai@Sun.COM 	case DDI_RESUME:
2289815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2299815SRishi.Srivatsavai@Sun.COM 
2309815SRishi.Srivatsavai@Sun.COM 	default:
2319815SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
2329815SRishi.Srivatsavai@Sun.COM 	}
2339815SRishi.Srivatsavai@Sun.COM }
2349815SRishi.Srivatsavai@Sun.COM 
2359815SRishi.Srivatsavai@Sun.COM /*ARGSUSED*/
2369815SRishi.Srivatsavai@Sun.COM static int
2379815SRishi.Srivatsavai@Sun.COM simnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2389815SRishi.Srivatsavai@Sun.COM {
2399815SRishi.Srivatsavai@Sun.COM 	switch (cmd) {
2409815SRishi.Srivatsavai@Sun.COM 	case DDI_DETACH:
2419815SRishi.Srivatsavai@Sun.COM 		/*
2429815SRishi.Srivatsavai@Sun.COM 		 * Allow the simnet instance to be detached only if there
2439815SRishi.Srivatsavai@Sun.COM 		 * are no simnets configured.
2449815SRishi.Srivatsavai@Sun.COM 		 */
2459815SRishi.Srivatsavai@Sun.COM 		if (simnet_count > 0)
2469815SRishi.Srivatsavai@Sun.COM 			return (DDI_FAILURE);
2479815SRishi.Srivatsavai@Sun.COM 
248*10416SRishi.Srivatsavai@Sun.COM 		dld_ioc_unregister(SIMNET_IOC);
2499815SRishi.Srivatsavai@Sun.COM 		simnet_fini();
250*10416SRishi.Srivatsavai@Sun.COM 		simnet_dip = NULL;
2519815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2529815SRishi.Srivatsavai@Sun.COM 
2539815SRishi.Srivatsavai@Sun.COM 	case DDI_SUSPEND:
2549815SRishi.Srivatsavai@Sun.COM 		return (DDI_SUCCESS);
2559815SRishi.Srivatsavai@Sun.COM 
2569815SRishi.Srivatsavai@Sun.COM 	default:
2579815SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
2589815SRishi.Srivatsavai@Sun.COM 	}
2599815SRishi.Srivatsavai@Sun.COM }
2609815SRishi.Srivatsavai@Sun.COM 
2619815SRishi.Srivatsavai@Sun.COM /* Caller must hold simnet_dev_lock */
2629815SRishi.Srivatsavai@Sun.COM static simnet_dev_t *
2639815SRishi.Srivatsavai@Sun.COM simnet_dev_lookup(datalink_id_t link_id)
2649815SRishi.Srivatsavai@Sun.COM {
2659815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
2669815SRishi.Srivatsavai@Sun.COM 
267*10416SRishi.Srivatsavai@Sun.COM 	ASSERT(RW_LOCK_HELD(&simnet_dev_lock));
2689815SRishi.Srivatsavai@Sun.COM 	for (sdev = list_head(&simnet_dev_list); sdev != NULL;
2699815SRishi.Srivatsavai@Sun.COM 	    sdev = list_next(&simnet_dev_list, sdev)) {
2709815SRishi.Srivatsavai@Sun.COM 		if (!(sdev->sd_flags & SDF_SHUTDOWN) &&
2719815SRishi.Srivatsavai@Sun.COM 		    (sdev->sd_link_id == link_id)) {
2729815SRishi.Srivatsavai@Sun.COM 			atomic_inc_32(&sdev->sd_refcount);
2739815SRishi.Srivatsavai@Sun.COM 			return (sdev);
2749815SRishi.Srivatsavai@Sun.COM 		}
2759815SRishi.Srivatsavai@Sun.COM 	}
2769815SRishi.Srivatsavai@Sun.COM 
2779815SRishi.Srivatsavai@Sun.COM 	return (NULL);
2789815SRishi.Srivatsavai@Sun.COM }
2799815SRishi.Srivatsavai@Sun.COM 
2809815SRishi.Srivatsavai@Sun.COM static void
2819815SRishi.Srivatsavai@Sun.COM simnet_wifidev_free(simnet_dev_t *sdev)
2829815SRishi.Srivatsavai@Sun.COM {
2839815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
2849815SRishi.Srivatsavai@Sun.COM 	int i;
2859815SRishi.Srivatsavai@Sun.COM 
2869815SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < wdev->swd_esslist_num; i++) {
2879815SRishi.Srivatsavai@Sun.COM 		kmem_free(wdev->swd_esslist[i],
2889815SRishi.Srivatsavai@Sun.COM 		    sizeof (wl_ess_conf_t));
2899815SRishi.Srivatsavai@Sun.COM 	}
2909815SRishi.Srivatsavai@Sun.COM 	kmem_free(wdev, sizeof (simnet_wifidev_t));
2919815SRishi.Srivatsavai@Sun.COM }
2929815SRishi.Srivatsavai@Sun.COM 
2939815SRishi.Srivatsavai@Sun.COM static void
2949815SRishi.Srivatsavai@Sun.COM simnet_dev_unref(simnet_dev_t *sdev)
2959815SRishi.Srivatsavai@Sun.COM {
2969815SRishi.Srivatsavai@Sun.COM 
2979815SRishi.Srivatsavai@Sun.COM 	ASSERT(sdev->sd_refcount > 0);
2989815SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_32_nv(&sdev->sd_refcount) != 0)
2999815SRishi.Srivatsavai@Sun.COM 		return;
3009815SRishi.Srivatsavai@Sun.COM 
3019815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_mh != NULL)
3029815SRishi.Srivatsavai@Sun.COM 		(void) mac_unregister(sdev->sd_mh);
3039815SRishi.Srivatsavai@Sun.COM 
3049815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_wifidev != NULL) {
3059815SRishi.Srivatsavai@Sun.COM 		ASSERT(sdev->sd_type == DL_WIFI);
3069815SRishi.Srivatsavai@Sun.COM 		simnet_wifidev_free(sdev);
3079815SRishi.Srivatsavai@Sun.COM 	}
3089815SRishi.Srivatsavai@Sun.COM 
3099815SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&sdev->sd_instlock);
3109815SRishi.Srivatsavai@Sun.COM 	cv_destroy(&sdev->sd_threadwait);
3119815SRishi.Srivatsavai@Sun.COM 	kmem_free(sdev->sd_mcastaddrs, ETHERADDRL * sdev->sd_mcastaddr_count);
3129815SRishi.Srivatsavai@Sun.COM 	kmem_free(sdev, sizeof (*sdev));
3139815SRishi.Srivatsavai@Sun.COM 	simnet_count--;
3149815SRishi.Srivatsavai@Sun.COM }
3159815SRishi.Srivatsavai@Sun.COM 
3169815SRishi.Srivatsavai@Sun.COM static int
3179815SRishi.Srivatsavai@Sun.COM simnet_init_wifi(simnet_dev_t *sdev, mac_register_t *mac)
3189815SRishi.Srivatsavai@Sun.COM {
3199815SRishi.Srivatsavai@Sun.COM 	wifi_data_t		wd = { 0 };
3209815SRishi.Srivatsavai@Sun.COM 	int err;
3219815SRishi.Srivatsavai@Sun.COM 
3229815SRishi.Srivatsavai@Sun.COM 	sdev->sd_wifidev = kmem_zalloc(sizeof (simnet_wifidev_t), KM_NOSLEEP);
3239815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_wifidev == NULL)
3249815SRishi.Srivatsavai@Sun.COM 		return (ENOMEM);
3259815SRishi.Srivatsavai@Sun.COM 
3269815SRishi.Srivatsavai@Sun.COM 	sdev->sd_wifidev->swd_sdev = sdev;
3279815SRishi.Srivatsavai@Sun.COM 	sdev->sd_wifidev->swd_linkstatus = WL_NOTCONNECTED;
3289815SRishi.Srivatsavai@Sun.COM 	wd.wd_secalloc = WIFI_SEC_NONE;
3299815SRishi.Srivatsavai@Sun.COM 	wd.wd_opmode = IEEE80211_M_STA;
3309815SRishi.Srivatsavai@Sun.COM 	mac->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
3319815SRishi.Srivatsavai@Sun.COM 	mac->m_max_sdu = IEEE80211_MTU;
3329815SRishi.Srivatsavai@Sun.COM 	mac->m_pdata = &wd;
3339815SRishi.Srivatsavai@Sun.COM 	mac->m_pdata_size = sizeof (wd);
3349815SRishi.Srivatsavai@Sun.COM 	err = mac_register(mac, &sdev->sd_mh);
3359815SRishi.Srivatsavai@Sun.COM 	return (err);
3369815SRishi.Srivatsavai@Sun.COM }
3379815SRishi.Srivatsavai@Sun.COM 
3389815SRishi.Srivatsavai@Sun.COM static int
3399815SRishi.Srivatsavai@Sun.COM simnet_init_ether(simnet_dev_t *sdev, mac_register_t *mac)
3409815SRishi.Srivatsavai@Sun.COM {
3419815SRishi.Srivatsavai@Sun.COM 	int err;
3429815SRishi.Srivatsavai@Sun.COM 
3439815SRishi.Srivatsavai@Sun.COM 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
3449815SRishi.Srivatsavai@Sun.COM 	mac->m_max_sdu = SIMNET_MAX_MTU;
3459815SRishi.Srivatsavai@Sun.COM 	mac->m_margin = VLAN_TAGSZ;
3469815SRishi.Srivatsavai@Sun.COM 	err = mac_register(mac, &sdev->sd_mh);
3479815SRishi.Srivatsavai@Sun.COM 	return (err);
3489815SRishi.Srivatsavai@Sun.COM }
3499815SRishi.Srivatsavai@Sun.COM 
3509815SRishi.Srivatsavai@Sun.COM static int
3519815SRishi.Srivatsavai@Sun.COM simnet_init_mac(simnet_dev_t *sdev)
3529815SRishi.Srivatsavai@Sun.COM {
3539815SRishi.Srivatsavai@Sun.COM 	mac_register_t *mac;
3549815SRishi.Srivatsavai@Sun.COM 	int err;
3559815SRishi.Srivatsavai@Sun.COM 
3569815SRishi.Srivatsavai@Sun.COM 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
3579815SRishi.Srivatsavai@Sun.COM 		return (ENOMEM);
3589815SRishi.Srivatsavai@Sun.COM 
3599815SRishi.Srivatsavai@Sun.COM 	mac->m_driver = sdev;
3609815SRishi.Srivatsavai@Sun.COM 	mac->m_dip = simnet_dip;
3619815SRishi.Srivatsavai@Sun.COM 	mac->m_instance = (uint_t)-1;
3629815SRishi.Srivatsavai@Sun.COM 	mac->m_src_addr = sdev->sd_mac_addr;
3639815SRishi.Srivatsavai@Sun.COM 	mac->m_callbacks = &simnet_m_callbacks;
3649815SRishi.Srivatsavai@Sun.COM 	mac->m_min_sdu = 0;
3659815SRishi.Srivatsavai@Sun.COM 
3669815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_type == DL_ETHER)
3679815SRishi.Srivatsavai@Sun.COM 		err = simnet_init_ether(sdev, mac);
3689815SRishi.Srivatsavai@Sun.COM 	else if (sdev->sd_type == DL_WIFI)
3699815SRishi.Srivatsavai@Sun.COM 		err = simnet_init_wifi(sdev, mac);
3709815SRishi.Srivatsavai@Sun.COM 	else
3719815SRishi.Srivatsavai@Sun.COM 		err = EINVAL;
3729815SRishi.Srivatsavai@Sun.COM 
3739815SRishi.Srivatsavai@Sun.COM 	mac_free(mac);
3749815SRishi.Srivatsavai@Sun.COM 	return (err);
3759815SRishi.Srivatsavai@Sun.COM }
3769815SRishi.Srivatsavai@Sun.COM 
3779815SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
3789815SRishi.Srivatsavai@Sun.COM static int
3799815SRishi.Srivatsavai@Sun.COM simnet_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
3809815SRishi.Srivatsavai@Sun.COM {
3819815SRishi.Srivatsavai@Sun.COM 	simnet_ioc_create_t *create_arg = karg;
3829815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
3839815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev_tmp;
3849815SRishi.Srivatsavai@Sun.COM 	int err = 0;
3859815SRishi.Srivatsavai@Sun.COM 
3869815SRishi.Srivatsavai@Sun.COM 	sdev = kmem_zalloc(sizeof (*sdev), KM_NOSLEEP);
3879815SRishi.Srivatsavai@Sun.COM 	if (sdev == NULL)
3889815SRishi.Srivatsavai@Sun.COM 		return (ENOMEM);
3899815SRishi.Srivatsavai@Sun.COM 
3909815SRishi.Srivatsavai@Sun.COM 	rw_enter(&simnet_dev_lock, RW_WRITER);
3919815SRishi.Srivatsavai@Sun.COM 	if ((sdev_tmp = simnet_dev_lookup(create_arg->sic_link_id)) != NULL) {
3929815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev_tmp);
3939815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
3949815SRishi.Srivatsavai@Sun.COM 		kmem_free(sdev, sizeof (*sdev));
3959815SRishi.Srivatsavai@Sun.COM 		return (EEXIST);
3969815SRishi.Srivatsavai@Sun.COM 	}
3979815SRishi.Srivatsavai@Sun.COM 
3989815SRishi.Srivatsavai@Sun.COM 	sdev->sd_type = create_arg->sic_type;
3999815SRishi.Srivatsavai@Sun.COM 	sdev->sd_link_id = create_arg->sic_link_id;
4009815SRishi.Srivatsavai@Sun.COM 	sdev->sd_refcount++;
4019815SRishi.Srivatsavai@Sun.COM 	mutex_init(&sdev->sd_instlock, NULL, MUTEX_DRIVER, NULL);
4029815SRishi.Srivatsavai@Sun.COM 	cv_init(&sdev->sd_threadwait, NULL, CV_DRIVER, NULL);
4039815SRishi.Srivatsavai@Sun.COM 	simnet_count++;
4049815SRishi.Srivatsavai@Sun.COM 
4059815SRishi.Srivatsavai@Sun.COM 	/* Simnets created from configuration on boot pass saved MAC address */
4069815SRishi.Srivatsavai@Sun.COM 	if (create_arg->sic_mac_len == 0) {
4079815SRishi.Srivatsavai@Sun.COM 		/* Generate random MAC address */
4089815SRishi.Srivatsavai@Sun.COM 		(void) random_get_pseudo_bytes(sdev->sd_mac_addr, ETHERADDRL);
4099815SRishi.Srivatsavai@Sun.COM 		/* Ensure MAC address is not multicast and is local */
4109815SRishi.Srivatsavai@Sun.COM 		sdev->sd_mac_addr[0] = (sdev->sd_mac_addr[0] & ~1) | 2;
4119815SRishi.Srivatsavai@Sun.COM 		sdev->sd_mac_len = ETHERADDRL;
4129815SRishi.Srivatsavai@Sun.COM 	} else {
4139815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(sdev->sd_mac_addr, create_arg->sic_mac_addr,
4149815SRishi.Srivatsavai@Sun.COM 		    create_arg->sic_mac_len);
4159815SRishi.Srivatsavai@Sun.COM 		sdev->sd_mac_len = create_arg->sic_mac_len;
4169815SRishi.Srivatsavai@Sun.COM 	}
4179815SRishi.Srivatsavai@Sun.COM 
4189815SRishi.Srivatsavai@Sun.COM 	if ((err = simnet_init_mac(sdev)) != 0) {
4199815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4209815SRishi.Srivatsavai@Sun.COM 		goto exit;
4219815SRishi.Srivatsavai@Sun.COM 	}
4229815SRishi.Srivatsavai@Sun.COM 
4239815SRishi.Srivatsavai@Sun.COM 	if ((err = dls_devnet_create(sdev->sd_mh, sdev->sd_link_id)) != 0) {
4249815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4259815SRishi.Srivatsavai@Sun.COM 		goto exit;
4269815SRishi.Srivatsavai@Sun.COM 	}
4279815SRishi.Srivatsavai@Sun.COM 
4289815SRishi.Srivatsavai@Sun.COM 	mac_link_update(sdev->sd_mh, LINK_STATE_UP);
4299815SRishi.Srivatsavai@Sun.COM 	mac_tx_update(sdev->sd_mh);
4309815SRishi.Srivatsavai@Sun.COM 	list_insert_tail(&simnet_dev_list, sdev);
4319815SRishi.Srivatsavai@Sun.COM 
4329815SRishi.Srivatsavai@Sun.COM 	/* Always return MAC address back to caller */
4339815SRishi.Srivatsavai@Sun.COM 	(void) memcpy(create_arg->sic_mac_addr, sdev->sd_mac_addr,
4349815SRishi.Srivatsavai@Sun.COM 	    sdev->sd_mac_len);
4359815SRishi.Srivatsavai@Sun.COM 	create_arg->sic_mac_len = sdev->sd_mac_len;
4369815SRishi.Srivatsavai@Sun.COM exit:
4379815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
4389815SRishi.Srivatsavai@Sun.COM 	return (err);
4399815SRishi.Srivatsavai@Sun.COM }
4409815SRishi.Srivatsavai@Sun.COM 
4419815SRishi.Srivatsavai@Sun.COM /* Caller must hold writer simnet_dev_lock */
4429815SRishi.Srivatsavai@Sun.COM static datalink_id_t
4439815SRishi.Srivatsavai@Sun.COM simnet_remove_peer(simnet_dev_t *sdev)
4449815SRishi.Srivatsavai@Sun.COM {
4459815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev_peer;
4469815SRishi.Srivatsavai@Sun.COM 	datalink_id_t peer_link_id = DATALINK_INVALID_LINKID;
4479815SRishi.Srivatsavai@Sun.COM 
448*10416SRishi.Srivatsavai@Sun.COM 	ASSERT(RW_WRITE_HELD(&simnet_dev_lock));
4499815SRishi.Srivatsavai@Sun.COM 	if ((sdev_peer = sdev->sd_peer_dev) != NULL) {
4509815SRishi.Srivatsavai@Sun.COM 		ASSERT(sdev == sdev_peer->sd_peer_dev);
4519815SRishi.Srivatsavai@Sun.COM 		sdev_peer->sd_peer_dev = NULL;
4529815SRishi.Srivatsavai@Sun.COM 		sdev->sd_peer_dev = NULL;
4539815SRishi.Srivatsavai@Sun.COM 		peer_link_id = sdev_peer->sd_link_id;
4549815SRishi.Srivatsavai@Sun.COM 		/* Release previous references held on both simnets */
4559815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev_peer);
4569815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4579815SRishi.Srivatsavai@Sun.COM 	}
4589815SRishi.Srivatsavai@Sun.COM 
4599815SRishi.Srivatsavai@Sun.COM 	return (peer_link_id);
4609815SRishi.Srivatsavai@Sun.COM }
4619815SRishi.Srivatsavai@Sun.COM 
4629815SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
4639815SRishi.Srivatsavai@Sun.COM static int
4649815SRishi.Srivatsavai@Sun.COM simnet_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
4659815SRishi.Srivatsavai@Sun.COM {
4669815SRishi.Srivatsavai@Sun.COM 	simnet_ioc_modify_t *modify_arg = karg;
4679815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
4689815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev_peer = NULL;
4699815SRishi.Srivatsavai@Sun.COM 
4709815SRishi.Srivatsavai@Sun.COM 	rw_enter(&simnet_dev_lock, RW_WRITER);
4719815SRishi.Srivatsavai@Sun.COM 	if ((sdev = simnet_dev_lookup(modify_arg->sim_link_id)) == NULL) {
4729815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
4739815SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
4749815SRishi.Srivatsavai@Sun.COM 	}
4759815SRishi.Srivatsavai@Sun.COM 
4769815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_link_id == modify_arg->sim_peer_link_id) {
4779815SRishi.Srivatsavai@Sun.COM 		/* Cannot peer with self */
4789815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
4799815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4809815SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
4819815SRishi.Srivatsavai@Sun.COM 	}
4829815SRishi.Srivatsavai@Sun.COM 
4839815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_peer_dev != NULL && sdev->sd_peer_dev->sd_link_id ==
4849815SRishi.Srivatsavai@Sun.COM 	    modify_arg->sim_peer_link_id) {
4859815SRishi.Srivatsavai@Sun.COM 		/* Nothing to modify */
4869815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
4879815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4889815SRishi.Srivatsavai@Sun.COM 		return (0);
4899815SRishi.Srivatsavai@Sun.COM 	}
4909815SRishi.Srivatsavai@Sun.COM 
4919815SRishi.Srivatsavai@Sun.COM 	if (modify_arg->sim_peer_link_id != DATALINK_INVALID_LINKID &&
4929815SRishi.Srivatsavai@Sun.COM 	    (sdev_peer = simnet_dev_lookup(modify_arg->sim_peer_link_id)) ==
4939815SRishi.Srivatsavai@Sun.COM 	    NULL) {
4949815SRishi.Srivatsavai@Sun.COM 		/* Peer simnet device not available */
4959815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
4969815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
4979815SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
4989815SRishi.Srivatsavai@Sun.COM 	}
4999815SRishi.Srivatsavai@Sun.COM 
5009815SRishi.Srivatsavai@Sun.COM 	/* First remove any previous peer */
5019815SRishi.Srivatsavai@Sun.COM 	(void) simnet_remove_peer(sdev);
5029815SRishi.Srivatsavai@Sun.COM 
5039815SRishi.Srivatsavai@Sun.COM 	if (sdev_peer != NULL) {
5049815SRishi.Srivatsavai@Sun.COM 		/* Remove any previous peer of sdev_peer */
5059815SRishi.Srivatsavai@Sun.COM 		(void) simnet_remove_peer(sdev_peer);
5069815SRishi.Srivatsavai@Sun.COM 		/* Update both devices with the new peer */
5079815SRishi.Srivatsavai@Sun.COM 		sdev_peer->sd_peer_dev = sdev;
5089815SRishi.Srivatsavai@Sun.COM 		sdev->sd_peer_dev = sdev_peer;
5099815SRishi.Srivatsavai@Sun.COM 		/* Hold references on both devices */
5109815SRishi.Srivatsavai@Sun.COM 	} else {
5119815SRishi.Srivatsavai@Sun.COM 		/* Release sdev lookup reference */
5129815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
5139815SRishi.Srivatsavai@Sun.COM 	}
5149815SRishi.Srivatsavai@Sun.COM 
5159815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
5169815SRishi.Srivatsavai@Sun.COM 	return (0);
5179815SRishi.Srivatsavai@Sun.COM }
5189815SRishi.Srivatsavai@Sun.COM 
5199815SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
5209815SRishi.Srivatsavai@Sun.COM static int
5219815SRishi.Srivatsavai@Sun.COM simnet_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
5229815SRishi.Srivatsavai@Sun.COM {
5239815SRishi.Srivatsavai@Sun.COM 	int err;
5249815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
5259815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev_peer;
5269815SRishi.Srivatsavai@Sun.COM 	simnet_ioc_delete_t *delete_arg = karg;
5279815SRishi.Srivatsavai@Sun.COM 	datalink_id_t tmpid;
5289815SRishi.Srivatsavai@Sun.COM 	datalink_id_t peerid;
5299815SRishi.Srivatsavai@Sun.COM 
5309815SRishi.Srivatsavai@Sun.COM 	rw_enter(&simnet_dev_lock, RW_WRITER);
5319815SRishi.Srivatsavai@Sun.COM 	if ((sdev = simnet_dev_lookup(delete_arg->sid_link_id)) == NULL) {
5329815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
5339815SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
5349815SRishi.Srivatsavai@Sun.COM 	}
5359815SRishi.Srivatsavai@Sun.COM 
5369815SRishi.Srivatsavai@Sun.COM 	if ((err = dls_devnet_destroy(sdev->sd_mh, &tmpid, B_TRUE)) != 0) {
5379815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
5389815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
5399815SRishi.Srivatsavai@Sun.COM 		return (err);
5409815SRishi.Srivatsavai@Sun.COM 	}
5419815SRishi.Srivatsavai@Sun.COM 
5429815SRishi.Srivatsavai@Sun.COM 	ASSERT(sdev->sd_link_id == tmpid);
5439815SRishi.Srivatsavai@Sun.COM 	/* Remove any attached peer link */
5449815SRishi.Srivatsavai@Sun.COM 	peerid = simnet_remove_peer(sdev);
5459815SRishi.Srivatsavai@Sun.COM 
5469815SRishi.Srivatsavai@Sun.COM 	/* Prevent new threads from using the instance */
5479815SRishi.Srivatsavai@Sun.COM 	mutex_enter(&sdev->sd_instlock);
5489815SRishi.Srivatsavai@Sun.COM 	sdev->sd_flags |= SDF_SHUTDOWN;
5499815SRishi.Srivatsavai@Sun.COM 	/* Wait until all active threads using the instance exit */
5509815SRishi.Srivatsavai@Sun.COM 	while (sdev->sd_threadcount > 0) {
5519815SRishi.Srivatsavai@Sun.COM 		if (cv_wait_sig(&sdev->sd_threadwait,
5529815SRishi.Srivatsavai@Sun.COM 		    &sdev->sd_instlock) == 0)  {
5539815SRishi.Srivatsavai@Sun.COM 			/* Signaled */
5549815SRishi.Srivatsavai@Sun.COM 			mutex_exit(&sdev->sd_instlock);
5559815SRishi.Srivatsavai@Sun.COM 			err = EINTR;
5569815SRishi.Srivatsavai@Sun.COM 			goto fail;
5579815SRishi.Srivatsavai@Sun.COM 		}
5589815SRishi.Srivatsavai@Sun.COM 	}
5599815SRishi.Srivatsavai@Sun.COM 	mutex_exit(&sdev->sd_instlock);
5609815SRishi.Srivatsavai@Sun.COM 
5619815SRishi.Srivatsavai@Sun.COM 	/* Try disabling the MAC */
5629815SRishi.Srivatsavai@Sun.COM 	if ((err = mac_disable(sdev->sd_mh)) != 0)
5639815SRishi.Srivatsavai@Sun.COM 		goto fail;
5649815SRishi.Srivatsavai@Sun.COM 
5659815SRishi.Srivatsavai@Sun.COM 	list_remove(&simnet_dev_list, sdev);
5669815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
5679815SRishi.Srivatsavai@Sun.COM 	simnet_dev_unref(sdev); /* Release lookup ref */
5689815SRishi.Srivatsavai@Sun.COM 	/* Releasing the last ref performs sdev/mem free */
5699815SRishi.Srivatsavai@Sun.COM 	simnet_dev_unref(sdev);
5709815SRishi.Srivatsavai@Sun.COM 	return (err);
5719815SRishi.Srivatsavai@Sun.COM fail:
5729815SRishi.Srivatsavai@Sun.COM 	/* Re-create simnet instance and add any previous peer */
5739815SRishi.Srivatsavai@Sun.COM 	(void) dls_devnet_create(sdev->sd_mh, sdev->sd_link_id);
5749815SRishi.Srivatsavai@Sun.COM 	sdev->sd_flags &= ~SDF_SHUTDOWN;
5759815SRishi.Srivatsavai@Sun.COM 
5769815SRishi.Srivatsavai@Sun.COM 	ASSERT(sdev->sd_peer_dev == NULL);
5779815SRishi.Srivatsavai@Sun.COM 	if (peerid != DATALINK_INVALID_LINKID &&
5789815SRishi.Srivatsavai@Sun.COM 	    ((sdev_peer = simnet_dev_lookup(peerid)) != NULL)) {
5799815SRishi.Srivatsavai@Sun.COM 		/* Attach peer device back */
5809815SRishi.Srivatsavai@Sun.COM 		ASSERT(sdev_peer->sd_peer_dev == NULL);
5819815SRishi.Srivatsavai@Sun.COM 		sdev_peer->sd_peer_dev = sdev;
5829815SRishi.Srivatsavai@Sun.COM 		sdev->sd_peer_dev = sdev_peer;
5839815SRishi.Srivatsavai@Sun.COM 		/* Hold reference on both devices */
5849815SRishi.Srivatsavai@Sun.COM 	} else {
5859815SRishi.Srivatsavai@Sun.COM 		/*
5869815SRishi.Srivatsavai@Sun.COM 		 * No previous peer or previous peer no longer
5879815SRishi.Srivatsavai@Sun.COM 		 * available so release lookup reference.
5889815SRishi.Srivatsavai@Sun.COM 		 */
5899815SRishi.Srivatsavai@Sun.COM 		simnet_dev_unref(sdev);
5909815SRishi.Srivatsavai@Sun.COM 	}
5919815SRishi.Srivatsavai@Sun.COM 
5929815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
5939815SRishi.Srivatsavai@Sun.COM 	return (err);
5949815SRishi.Srivatsavai@Sun.COM }
5959815SRishi.Srivatsavai@Sun.COM 
5969815SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
5979815SRishi.Srivatsavai@Sun.COM static int
5989815SRishi.Srivatsavai@Sun.COM simnet_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
5999815SRishi.Srivatsavai@Sun.COM {
6009815SRishi.Srivatsavai@Sun.COM 	simnet_ioc_info_t *info_arg = karg;
6019815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
6029815SRishi.Srivatsavai@Sun.COM 
6039815SRishi.Srivatsavai@Sun.COM 	rw_enter(&simnet_dev_lock, RW_READER);
6049815SRishi.Srivatsavai@Sun.COM 	if ((sdev = simnet_dev_lookup(info_arg->sii_link_id)) == NULL) {
6059815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
6069815SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
6079815SRishi.Srivatsavai@Sun.COM 	}
6089815SRishi.Srivatsavai@Sun.COM 
6099815SRishi.Srivatsavai@Sun.COM 	(void) memcpy(info_arg->sii_mac_addr, sdev->sd_mac_addr,
6109815SRishi.Srivatsavai@Sun.COM 	    sdev->sd_mac_len);
6119815SRishi.Srivatsavai@Sun.COM 	info_arg->sii_mac_len = sdev->sd_mac_len;
6129815SRishi.Srivatsavai@Sun.COM 	info_arg->sii_type = sdev->sd_type;
6139815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_peer_dev != NULL)
6149815SRishi.Srivatsavai@Sun.COM 		info_arg->sii_peer_link_id = sdev->sd_peer_dev->sd_link_id;
6159815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
6169815SRishi.Srivatsavai@Sun.COM 	simnet_dev_unref(sdev);
6179815SRishi.Srivatsavai@Sun.COM 	return (0);
6189815SRishi.Srivatsavai@Sun.COM }
6199815SRishi.Srivatsavai@Sun.COM 
620*10416SRishi.Srivatsavai@Sun.COM static boolean_t
621*10416SRishi.Srivatsavai@Sun.COM simnet_thread_ref(simnet_dev_t *sdev)
622*10416SRishi.Srivatsavai@Sun.COM {
623*10416SRishi.Srivatsavai@Sun.COM 	mutex_enter(&sdev->sd_instlock);
624*10416SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_flags & SDF_SHUTDOWN ||
625*10416SRishi.Srivatsavai@Sun.COM 	    !(sdev->sd_flags & SDF_STARTED)) {
626*10416SRishi.Srivatsavai@Sun.COM 		mutex_exit(&sdev->sd_instlock);
627*10416SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
628*10416SRishi.Srivatsavai@Sun.COM 	}
629*10416SRishi.Srivatsavai@Sun.COM 	sdev->sd_threadcount++;
630*10416SRishi.Srivatsavai@Sun.COM 	mutex_exit(&sdev->sd_instlock);
631*10416SRishi.Srivatsavai@Sun.COM 	return (B_TRUE);
632*10416SRishi.Srivatsavai@Sun.COM }
633*10416SRishi.Srivatsavai@Sun.COM 
6349815SRishi.Srivatsavai@Sun.COM static void
635*10416SRishi.Srivatsavai@Sun.COM simnet_thread_unref(simnet_dev_t *sdev)
6369815SRishi.Srivatsavai@Sun.COM {
637*10416SRishi.Srivatsavai@Sun.COM 	mutex_enter(&sdev->sd_instlock);
638*10416SRishi.Srivatsavai@Sun.COM 	if (--sdev->sd_threadcount == 0)
639*10416SRishi.Srivatsavai@Sun.COM 		cv_broadcast(&sdev->sd_threadwait);
640*10416SRishi.Srivatsavai@Sun.COM 	mutex_exit(&sdev->sd_instlock);
641*10416SRishi.Srivatsavai@Sun.COM }
642*10416SRishi.Srivatsavai@Sun.COM 
643*10416SRishi.Srivatsavai@Sun.COM static void
644*10416SRishi.Srivatsavai@Sun.COM simnet_rx(void *arg)
645*10416SRishi.Srivatsavai@Sun.COM {
646*10416SRishi.Srivatsavai@Sun.COM 	mblk_t *mp = arg;
6479815SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
648*10416SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev;
649*10416SRishi.Srivatsavai@Sun.COM 
650*10416SRishi.Srivatsavai@Sun.COM 	sdev = (simnet_dev_t *)mp->b_next;
651*10416SRishi.Srivatsavai@Sun.COM 	mp->b_next = NULL;
6529815SRishi.Srivatsavai@Sun.COM 
6539815SRishi.Srivatsavai@Sun.COM 	/* Check for valid packet header */
6549815SRishi.Srivatsavai@Sun.COM 	if (mac_header_info(sdev->sd_mh, mp, &hdr_info) != 0) {
6559815SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
6569815SRishi.Srivatsavai@Sun.COM 		sdev->sd_stats.recv_errors++;
657*10416SRishi.Srivatsavai@Sun.COM 		goto rx_done;
6589815SRishi.Srivatsavai@Sun.COM 	}
6599815SRishi.Srivatsavai@Sun.COM 
6609815SRishi.Srivatsavai@Sun.COM 	/*
6619815SRishi.Srivatsavai@Sun.COM 	 * When we are NOT in promiscuous mode we only receive
6629815SRishi.Srivatsavai@Sun.COM 	 * unicast packets addressed to us and multicast packets that
6639815SRishi.Srivatsavai@Sun.COM 	 * MAC clients have requested.
6649815SRishi.Srivatsavai@Sun.COM 	 */
6659815SRishi.Srivatsavai@Sun.COM 	if (!sdev->sd_promisc &&
6669815SRishi.Srivatsavai@Sun.COM 	    hdr_info.mhi_dsttype != MAC_ADDRTYPE_BROADCAST) {
6679815SRishi.Srivatsavai@Sun.COM 		if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_UNICAST &&
6689815SRishi.Srivatsavai@Sun.COM 		    bcmp(hdr_info.mhi_daddr, sdev->sd_mac_addr,
6699815SRishi.Srivatsavai@Sun.COM 		    ETHERADDRL) != 0) {
6709815SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
671*10416SRishi.Srivatsavai@Sun.COM 			goto rx_done;
6729815SRishi.Srivatsavai@Sun.COM 		} else if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) {
6739815SRishi.Srivatsavai@Sun.COM 			mutex_enter(&sdev->sd_instlock);
6749815SRishi.Srivatsavai@Sun.COM 			if (mcastaddr_lookup(sdev, hdr_info.mhi_daddr) ==
6759815SRishi.Srivatsavai@Sun.COM 			    NULL) {
6769815SRishi.Srivatsavai@Sun.COM 				mutex_exit(&sdev->sd_instlock);
6779815SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
678*10416SRishi.Srivatsavai@Sun.COM 				goto rx_done;
6799815SRishi.Srivatsavai@Sun.COM 			}
6809815SRishi.Srivatsavai@Sun.COM 			mutex_exit(&sdev->sd_instlock);
6819815SRishi.Srivatsavai@Sun.COM 		}
6829815SRishi.Srivatsavai@Sun.COM 	}
6839815SRishi.Srivatsavai@Sun.COM 
6849815SRishi.Srivatsavai@Sun.COM 	sdev->sd_stats.recv_count++;
6859815SRishi.Srivatsavai@Sun.COM 	sdev->sd_stats.rbytes += msgdsize(mp);
6869815SRishi.Srivatsavai@Sun.COM 	mac_rx(sdev->sd_mh, NULL, mp);
687*10416SRishi.Srivatsavai@Sun.COM rx_done:
688*10416SRishi.Srivatsavai@Sun.COM 	simnet_thread_unref(sdev);
6899815SRishi.Srivatsavai@Sun.COM }
6909815SRishi.Srivatsavai@Sun.COM 
6919815SRishi.Srivatsavai@Sun.COM static mblk_t *
6929815SRishi.Srivatsavai@Sun.COM simnet_m_tx(void *arg, mblk_t *mp_chain)
6939815SRishi.Srivatsavai@Sun.COM {
6949815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
6959815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev_rx;
6969815SRishi.Srivatsavai@Sun.COM 	mblk_t *mpnext = mp_chain;
6979815SRishi.Srivatsavai@Sun.COM 	mblk_t *mp;
6989815SRishi.Srivatsavai@Sun.COM 
6999815SRishi.Srivatsavai@Sun.COM 	rw_enter(&simnet_dev_lock, RW_READER);
7009815SRishi.Srivatsavai@Sun.COM 	if ((sdev_rx = sdev->sd_peer_dev) == NULL) {
7019815SRishi.Srivatsavai@Sun.COM 		/* Discard packets when no peer exists */
7029815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
7039815SRishi.Srivatsavai@Sun.COM 		freemsgchain(mp_chain);
7049815SRishi.Srivatsavai@Sun.COM 		return (NULL);
7059815SRishi.Srivatsavai@Sun.COM 	}
7069815SRishi.Srivatsavai@Sun.COM 
7079815SRishi.Srivatsavai@Sun.COM 	/*
7089815SRishi.Srivatsavai@Sun.COM 	 * Discard packets when either device is shutting down or not ready.
7099815SRishi.Srivatsavai@Sun.COM 	 * Though MAC layer ensures a reference is held on the MAC while we
7109815SRishi.Srivatsavai@Sun.COM 	 * process the packet chain, there is no guarantee the peer MAC will
7119815SRishi.Srivatsavai@Sun.COM 	 * remain enabled. So we increment per-instance threadcount to ensure
7129815SRishi.Srivatsavai@Sun.COM 	 * either MAC instance is not disabled while we handle the chain of
7139815SRishi.Srivatsavai@Sun.COM 	 * packets. It is okay if the peer device is disconnected while we are
7149815SRishi.Srivatsavai@Sun.COM 	 * here since we lookup the peer device while holding simnet_dev_lock
7159815SRishi.Srivatsavai@Sun.COM 	 * (reader lock) and increment the threadcount of the peer, the peer
7169815SRishi.Srivatsavai@Sun.COM 	 * MAC cannot be disabled in simnet_ioc_delete.
7179815SRishi.Srivatsavai@Sun.COM 	 */
718*10416SRishi.Srivatsavai@Sun.COM 	if (!simnet_thread_ref(sdev_rx)) {
7199815SRishi.Srivatsavai@Sun.COM 		rw_exit(&simnet_dev_lock);
7209815SRishi.Srivatsavai@Sun.COM 		freemsgchain(mp_chain);
7219815SRishi.Srivatsavai@Sun.COM 		return (NULL);
7229815SRishi.Srivatsavai@Sun.COM 	}
7239815SRishi.Srivatsavai@Sun.COM 	rw_exit(&simnet_dev_lock);
7249815SRishi.Srivatsavai@Sun.COM 
725*10416SRishi.Srivatsavai@Sun.COM 	if (!simnet_thread_ref(sdev)) {
726*10416SRishi.Srivatsavai@Sun.COM 		simnet_thread_unref(sdev_rx);
7279815SRishi.Srivatsavai@Sun.COM 		freemsgchain(mp_chain);
7289815SRishi.Srivatsavai@Sun.COM 		return (NULL);
7299815SRishi.Srivatsavai@Sun.COM 	}
7309815SRishi.Srivatsavai@Sun.COM 
7319815SRishi.Srivatsavai@Sun.COM 	while ((mp = mpnext) != NULL) {
7329815SRishi.Srivatsavai@Sun.COM 		int len;
7339815SRishi.Srivatsavai@Sun.COM 		int size;
7349815SRishi.Srivatsavai@Sun.COM 		mblk_t *mp_new;
7359815SRishi.Srivatsavai@Sun.COM 		mblk_t *mp_tmp;
7369815SRishi.Srivatsavai@Sun.COM 
7379815SRishi.Srivatsavai@Sun.COM 		mpnext = mp->b_next;
7389815SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
7399815SRishi.Srivatsavai@Sun.COM 		len = msgdsize(mp);
7409815SRishi.Srivatsavai@Sun.COM 
7419815SRishi.Srivatsavai@Sun.COM 		/* Pad packet to minimum Ethernet frame size */
7429815SRishi.Srivatsavai@Sun.COM 		if (len < ETHERMIN) {
7439815SRishi.Srivatsavai@Sun.COM 			size = ETHERMIN - len;
7449815SRishi.Srivatsavai@Sun.COM 			mp_new = allocb(size, BPRI_HI);
7459815SRishi.Srivatsavai@Sun.COM 			if (mp_new == NULL) {
7469815SRishi.Srivatsavai@Sun.COM 				sdev->sd_stats.xmit_errors++;
7479815SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
7489815SRishi.Srivatsavai@Sun.COM 				continue;
7499815SRishi.Srivatsavai@Sun.COM 			}
7509815SRishi.Srivatsavai@Sun.COM 			bzero(mp_new->b_wptr, size);
7519815SRishi.Srivatsavai@Sun.COM 			mp_new->b_wptr += size;
7529815SRishi.Srivatsavai@Sun.COM 
7539815SRishi.Srivatsavai@Sun.COM 			mp_tmp = mp;
7549815SRishi.Srivatsavai@Sun.COM 			while (mp_tmp->b_cont != NULL)
7559815SRishi.Srivatsavai@Sun.COM 				mp_tmp = mp_tmp->b_cont;
7569815SRishi.Srivatsavai@Sun.COM 			mp_tmp->b_cont = mp_new;
7579815SRishi.Srivatsavai@Sun.COM 			len += size;
7589815SRishi.Srivatsavai@Sun.COM 		}
7599815SRishi.Srivatsavai@Sun.COM 
7609815SRishi.Srivatsavai@Sun.COM 		/* Pullup packet into a single mblk */
7619815SRishi.Srivatsavai@Sun.COM 		if (!pullupmsg(mp, -1)) {
7629815SRishi.Srivatsavai@Sun.COM 			sdev->sd_stats.xmit_errors++;
7639815SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
7649815SRishi.Srivatsavai@Sun.COM 			continue;
7659815SRishi.Srivatsavai@Sun.COM 		}
7669815SRishi.Srivatsavai@Sun.COM 
7679815SRishi.Srivatsavai@Sun.COM 		/* Fix mblk checksum as the pkt dest is local */
7689815SRishi.Srivatsavai@Sun.COM 		if ((mp = mac_fix_cksum(mp)) == NULL) {
7699815SRishi.Srivatsavai@Sun.COM 			sdev->sd_stats.xmit_errors++;
7709815SRishi.Srivatsavai@Sun.COM 			continue;
7719815SRishi.Srivatsavai@Sun.COM 		}
7729815SRishi.Srivatsavai@Sun.COM 
773*10416SRishi.Srivatsavai@Sun.COM 		/* Hold reference for taskq receive processing per-pkt */
774*10416SRishi.Srivatsavai@Sun.COM 		if (!simnet_thread_ref(sdev_rx)) {
775*10416SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
776*10416SRishi.Srivatsavai@Sun.COM 			freemsgchain(mpnext);
777*10416SRishi.Srivatsavai@Sun.COM 			break;
778*10416SRishi.Srivatsavai@Sun.COM 		}
779*10416SRishi.Srivatsavai@Sun.COM 
780*10416SRishi.Srivatsavai@Sun.COM 		/* Use taskq for pkt receive to avoid kernel stack explosion */
781*10416SRishi.Srivatsavai@Sun.COM 		mp->b_next = (mblk_t *)sdev_rx;
782*10416SRishi.Srivatsavai@Sun.COM 		if (ddi_taskq_dispatch(simnet_rxq, simnet_rx, mp,
783*10416SRishi.Srivatsavai@Sun.COM 		    DDI_NOSLEEP) == DDI_SUCCESS) {
784*10416SRishi.Srivatsavai@Sun.COM 			sdev->sd_stats.xmit_count++;
785*10416SRishi.Srivatsavai@Sun.COM 			sdev->sd_stats.obytes += len;
786*10416SRishi.Srivatsavai@Sun.COM 		} else {
787*10416SRishi.Srivatsavai@Sun.COM 			simnet_thread_unref(sdev_rx);
788*10416SRishi.Srivatsavai@Sun.COM 			mp->b_next = NULL;
789*10416SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
790*10416SRishi.Srivatsavai@Sun.COM 			sdev_rx->sd_stats.recv_errors++;
791*10416SRishi.Srivatsavai@Sun.COM 		}
7929815SRishi.Srivatsavai@Sun.COM 	}
7939815SRishi.Srivatsavai@Sun.COM 
794*10416SRishi.Srivatsavai@Sun.COM 	simnet_thread_unref(sdev);
795*10416SRishi.Srivatsavai@Sun.COM 	simnet_thread_unref(sdev_rx);
7969815SRishi.Srivatsavai@Sun.COM 	return (NULL);
7979815SRishi.Srivatsavai@Sun.COM }
7989815SRishi.Srivatsavai@Sun.COM 
7999815SRishi.Srivatsavai@Sun.COM static int
8009815SRishi.Srivatsavai@Sun.COM simnet_wifi_ioctl(simnet_dev_t *sdev, mblk_t *mp)
8019815SRishi.Srivatsavai@Sun.COM {
8029815SRishi.Srivatsavai@Sun.COM 	int rc = WL_SUCCESS;
8039815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
8049815SRishi.Srivatsavai@Sun.COM 
8059815SRishi.Srivatsavai@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
8069815SRishi.Srivatsavai@Sun.COM 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
8079815SRishi.Srivatsavai@Sun.COM 	case WL_DISASSOCIATE:
8089815SRishi.Srivatsavai@Sun.COM 		wdev->swd_linkstatus = WL_NOTCONNECTED;
8099815SRishi.Srivatsavai@Sun.COM 		break;
8109815SRishi.Srivatsavai@Sun.COM 	default:
8119815SRishi.Srivatsavai@Sun.COM 		break;
8129815SRishi.Srivatsavai@Sun.COM 	}
8139815SRishi.Srivatsavai@Sun.COM 	return (rc);
8149815SRishi.Srivatsavai@Sun.COM }
8159815SRishi.Srivatsavai@Sun.COM 
8169815SRishi.Srivatsavai@Sun.COM static void
8179815SRishi.Srivatsavai@Sun.COM simnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
8189815SRishi.Srivatsavai@Sun.COM {
8199815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
8209815SRishi.Srivatsavai@Sun.COM 	struct	iocblk	*iocp;
8219815SRishi.Srivatsavai@Sun.COM 	mblk_t	*mp1;
8229815SRishi.Srivatsavai@Sun.COM 	uint32_t cmd;
8239815SRishi.Srivatsavai@Sun.COM 	int rc;
8249815SRishi.Srivatsavai@Sun.COM 
8259815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_type != DL_WIFI) {
8269815SRishi.Srivatsavai@Sun.COM 		miocnak(q, mp, 0, ENOTSUP);
8279815SRishi.Srivatsavai@Sun.COM 		return;
8289815SRishi.Srivatsavai@Sun.COM 	}
8299815SRishi.Srivatsavai@Sun.COM 
8309815SRishi.Srivatsavai@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
8319815SRishi.Srivatsavai@Sun.COM 	iocp = (struct iocblk *)mp->b_rptr;
8329815SRishi.Srivatsavai@Sun.COM 	if (iocp->ioc_count == 0) {
8339815SRishi.Srivatsavai@Sun.COM 		miocnak(q, mp, 0, EINVAL);
8349815SRishi.Srivatsavai@Sun.COM 		return;
8359815SRishi.Srivatsavai@Sun.COM 	}
8369815SRishi.Srivatsavai@Sun.COM 
8379815SRishi.Srivatsavai@Sun.COM 	/* We only claim support for WiFi operation commands */
8389815SRishi.Srivatsavai@Sun.COM 	cmd = iocp->ioc_cmd;
8399815SRishi.Srivatsavai@Sun.COM 	switch (cmd) {
8409815SRishi.Srivatsavai@Sun.COM 	default:
8419815SRishi.Srivatsavai@Sun.COM 		miocnak(q, mp, 0, EINVAL);
8429815SRishi.Srivatsavai@Sun.COM 		return;
8439815SRishi.Srivatsavai@Sun.COM 	case WLAN_GET_PARAM:
8449815SRishi.Srivatsavai@Sun.COM 	case WLAN_SET_PARAM:
8459815SRishi.Srivatsavai@Sun.COM 	case WLAN_COMMAND:
8469815SRishi.Srivatsavai@Sun.COM 		break;
8479815SRishi.Srivatsavai@Sun.COM 	}
8489815SRishi.Srivatsavai@Sun.COM 
8499815SRishi.Srivatsavai@Sun.COM 	mp1 = mp->b_cont;
8509815SRishi.Srivatsavai@Sun.COM 	freemsg(mp1->b_cont);
8519815SRishi.Srivatsavai@Sun.COM 	mp1->b_cont = NULL;
8529815SRishi.Srivatsavai@Sun.COM 	/* overwrite everything */
8539815SRishi.Srivatsavai@Sun.COM 	mp1->b_wptr = mp1->b_rptr;
8549815SRishi.Srivatsavai@Sun.COM 	rc = simnet_wifi_ioctl(sdev, mp1);
8559815SRishi.Srivatsavai@Sun.COM 	miocack(q, mp, msgdsize(mp1), rc);
8569815SRishi.Srivatsavai@Sun.COM }
8579815SRishi.Srivatsavai@Sun.COM 
8589815SRishi.Srivatsavai@Sun.COM static int
8599815SRishi.Srivatsavai@Sun.COM simnet_m_stat(void *arg, uint_t stat, uint64_t *val)
8609815SRishi.Srivatsavai@Sun.COM {
8619815SRishi.Srivatsavai@Sun.COM 	int rval = 0;
8629815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
8639815SRishi.Srivatsavai@Sun.COM 
8649815SRishi.Srivatsavai@Sun.COM 	ASSERT(sdev->sd_mh != NULL);
8659815SRishi.Srivatsavai@Sun.COM 
8669815SRishi.Srivatsavai@Sun.COM 	switch (stat) {
8679815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_IFSPEED:
8689815SRishi.Srivatsavai@Sun.COM 		*val = 100 * 1000000ull; /* 100 Mbps */
8699815SRishi.Srivatsavai@Sun.COM 		break;
8709815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_LINK_STATE:
8719815SRishi.Srivatsavai@Sun.COM 		*val = LINK_DUPLEX_FULL;
8729815SRishi.Srivatsavai@Sun.COM 		break;
8739815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_LINK_UP:
8749815SRishi.Srivatsavai@Sun.COM 		if (sdev->sd_flags & SDF_STARTED)
8759815SRishi.Srivatsavai@Sun.COM 			*val = LINK_STATE_UP;
8769815SRishi.Srivatsavai@Sun.COM 		else
8779815SRishi.Srivatsavai@Sun.COM 			*val = LINK_STATE_DOWN;
8789815SRishi.Srivatsavai@Sun.COM 		break;
8799815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_PROMISC:
8809815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_MULTIRCV:
8819815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_MULTIXMT:
8829815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_BRDCSTRCV:
8839815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_BRDCSTXMT:
8849815SRishi.Srivatsavai@Sun.COM 		rval = ENOTSUP;
8859815SRishi.Srivatsavai@Sun.COM 		break;
8869815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_OPACKETS:
8879815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.xmit_count;
8889815SRishi.Srivatsavai@Sun.COM 		break;
8899815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_OBYTES:
8909815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.obytes;
8919815SRishi.Srivatsavai@Sun.COM 		break;
8929815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_IERRORS:
8939815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.recv_errors;
8949815SRishi.Srivatsavai@Sun.COM 		break;
8959815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_OERRORS:
8969815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.xmit_errors;
8979815SRishi.Srivatsavai@Sun.COM 		break;
8989815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_RBYTES:
8999815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.rbytes;
9009815SRishi.Srivatsavai@Sun.COM 		break;
9019815SRishi.Srivatsavai@Sun.COM 	case MAC_STAT_IPACKETS:
9029815SRishi.Srivatsavai@Sun.COM 		*val = sdev->sd_stats.recv_count;
9039815SRishi.Srivatsavai@Sun.COM 		break;
9049815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_FCS_ERRORS:
9059815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_WEP_ERRORS:
9069815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_TX_FRAGS:
9079815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_MCAST_TX:
9089815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_RTS_SUCCESS:
9099815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_RTS_FAILURE:
9109815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_ACK_FAILURE:
9119815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_RX_FRAGS:
9129815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_MCAST_RX:
9139815SRishi.Srivatsavai@Sun.COM 	case WIFI_STAT_RX_DUPS:
9149815SRishi.Srivatsavai@Sun.COM 		rval = ENOTSUP;
9159815SRishi.Srivatsavai@Sun.COM 		break;
9169815SRishi.Srivatsavai@Sun.COM 	default:
9179815SRishi.Srivatsavai@Sun.COM 		rval = ENOTSUP;
9189815SRishi.Srivatsavai@Sun.COM 		break;
9199815SRishi.Srivatsavai@Sun.COM 	}
9209815SRishi.Srivatsavai@Sun.COM 
9219815SRishi.Srivatsavai@Sun.COM 	return (rval);
9229815SRishi.Srivatsavai@Sun.COM }
9239815SRishi.Srivatsavai@Sun.COM 
9249815SRishi.Srivatsavai@Sun.COM static int
9259815SRishi.Srivatsavai@Sun.COM simnet_m_start(void *arg)
9269815SRishi.Srivatsavai@Sun.COM {
9279815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
9289815SRishi.Srivatsavai@Sun.COM 
9299815SRishi.Srivatsavai@Sun.COM 	sdev->sd_flags |= SDF_STARTED;
9309815SRishi.Srivatsavai@Sun.COM 	return (0);
9319815SRishi.Srivatsavai@Sun.COM }
9329815SRishi.Srivatsavai@Sun.COM 
9339815SRishi.Srivatsavai@Sun.COM static void
9349815SRishi.Srivatsavai@Sun.COM simnet_m_stop(void *arg)
9359815SRishi.Srivatsavai@Sun.COM {
9369815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
9379815SRishi.Srivatsavai@Sun.COM 
9389815SRishi.Srivatsavai@Sun.COM 	sdev->sd_flags &= ~SDF_STARTED;
9399815SRishi.Srivatsavai@Sun.COM }
9409815SRishi.Srivatsavai@Sun.COM 
9419815SRishi.Srivatsavai@Sun.COM static int
9429815SRishi.Srivatsavai@Sun.COM simnet_m_promisc(void *arg, boolean_t on)
9439815SRishi.Srivatsavai@Sun.COM {
9449815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
9459815SRishi.Srivatsavai@Sun.COM 
9469815SRishi.Srivatsavai@Sun.COM 	sdev->sd_promisc = on;
9479815SRishi.Srivatsavai@Sun.COM 	return (0);
9489815SRishi.Srivatsavai@Sun.COM }
9499815SRishi.Srivatsavai@Sun.COM 
9509815SRishi.Srivatsavai@Sun.COM /*
9519815SRishi.Srivatsavai@Sun.COM  * Returns matching multicast address enabled on the simnet instance.
9529815SRishi.Srivatsavai@Sun.COM  * Assumes simnet instance mutex lock is held.
9539815SRishi.Srivatsavai@Sun.COM  */
9549815SRishi.Srivatsavai@Sun.COM static uint8_t *
9559815SRishi.Srivatsavai@Sun.COM mcastaddr_lookup(simnet_dev_t *sdev, const uint8_t *addrp)
9569815SRishi.Srivatsavai@Sun.COM {
9579815SRishi.Srivatsavai@Sun.COM 	int idx;
9589815SRishi.Srivatsavai@Sun.COM 	uint8_t *maddrptr;
9599815SRishi.Srivatsavai@Sun.COM 
960*10416SRishi.Srivatsavai@Sun.COM 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
9619815SRishi.Srivatsavai@Sun.COM 	maddrptr = sdev->sd_mcastaddrs;
9629815SRishi.Srivatsavai@Sun.COM 	for (idx = 0; idx < sdev->sd_mcastaddr_count; idx++) {
9639815SRishi.Srivatsavai@Sun.COM 		if (bcmp(maddrptr, addrp, ETHERADDRL) == 0)
9649815SRishi.Srivatsavai@Sun.COM 			return (maddrptr);
9659815SRishi.Srivatsavai@Sun.COM 		maddrptr += ETHERADDRL;
9669815SRishi.Srivatsavai@Sun.COM 	}
9679815SRishi.Srivatsavai@Sun.COM 
9689815SRishi.Srivatsavai@Sun.COM 	return (NULL);
9699815SRishi.Srivatsavai@Sun.COM }
9709815SRishi.Srivatsavai@Sun.COM 
9719815SRishi.Srivatsavai@Sun.COM /* Add or remove Multicast addresses on simnet instance */
9729815SRishi.Srivatsavai@Sun.COM static int
9739815SRishi.Srivatsavai@Sun.COM simnet_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
9749815SRishi.Srivatsavai@Sun.COM {
9759815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
9769815SRishi.Srivatsavai@Sun.COM 	uint8_t *maddrptr;
9779815SRishi.Srivatsavai@Sun.COM 	uint8_t *newbuf;
9789815SRishi.Srivatsavai@Sun.COM 	size_t prevsize;
9799815SRishi.Srivatsavai@Sun.COM 	size_t newsize;
9809815SRishi.Srivatsavai@Sun.COM 	ptrdiff_t len;
9819815SRishi.Srivatsavai@Sun.COM 	ptrdiff_t len2;
9829815SRishi.Srivatsavai@Sun.COM 
9839815SRishi.Srivatsavai@Sun.COM alloc_retry:
9849815SRishi.Srivatsavai@Sun.COM 	prevsize = sdev->sd_mcastaddr_count * ETHERADDRL;
9859815SRishi.Srivatsavai@Sun.COM 	newsize = prevsize + (add ? ETHERADDRL:-ETHERADDRL);
9869815SRishi.Srivatsavai@Sun.COM 	newbuf = kmem_alloc(newsize, KM_SLEEP);
9879815SRishi.Srivatsavai@Sun.COM 
9889815SRishi.Srivatsavai@Sun.COM 	mutex_enter(&sdev->sd_instlock);
9899815SRishi.Srivatsavai@Sun.COM 	if (prevsize != (sdev->sd_mcastaddr_count * ETHERADDRL)) {
9909815SRishi.Srivatsavai@Sun.COM 		mutex_exit(&sdev->sd_instlock);
9919815SRishi.Srivatsavai@Sun.COM 		kmem_free(newbuf, newsize);
9929815SRishi.Srivatsavai@Sun.COM 		goto alloc_retry;
9939815SRishi.Srivatsavai@Sun.COM 	}
9949815SRishi.Srivatsavai@Sun.COM 
9959815SRishi.Srivatsavai@Sun.COM 	maddrptr = mcastaddr_lookup(sdev, addrp);
9969815SRishi.Srivatsavai@Sun.COM 	if (!add && maddrptr != NULL) {
9979815SRishi.Srivatsavai@Sun.COM 		/* Removing a Multicast address */
9989815SRishi.Srivatsavai@Sun.COM 		if (newbuf != NULL) {
9999815SRishi.Srivatsavai@Sun.COM 			/* LINTED: E_PTRDIFF_OVERFLOW */
10009815SRishi.Srivatsavai@Sun.COM 			len = maddrptr - sdev->sd_mcastaddrs;
10019815SRishi.Srivatsavai@Sun.COM 			(void) memcpy(newbuf, sdev->sd_mcastaddrs, len);
10029815SRishi.Srivatsavai@Sun.COM 			len2 = prevsize - len - ETHERADDRL;
10039815SRishi.Srivatsavai@Sun.COM 			(void) memcpy(newbuf + len,
10049815SRishi.Srivatsavai@Sun.COM 			    maddrptr + ETHERADDRL, len2);
10059815SRishi.Srivatsavai@Sun.COM 		}
10069815SRishi.Srivatsavai@Sun.COM 		sdev->sd_mcastaddr_count--;
10079815SRishi.Srivatsavai@Sun.COM 	} else if (add && maddrptr == NULL) {
10089815SRishi.Srivatsavai@Sun.COM 		/* Adding a new Multicast address */
10099815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(newbuf, sdev->sd_mcastaddrs, prevsize);
10109815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(newbuf + prevsize, addrp, ETHERADDRL);
10119815SRishi.Srivatsavai@Sun.COM 		sdev->sd_mcastaddr_count++;
10129815SRishi.Srivatsavai@Sun.COM 	} else {
10139815SRishi.Srivatsavai@Sun.COM 		/* Error: removing a non-existing Multicast address */
10149815SRishi.Srivatsavai@Sun.COM 		mutex_exit(&sdev->sd_instlock);
10159815SRishi.Srivatsavai@Sun.COM 		kmem_free(newbuf, newsize);
10169815SRishi.Srivatsavai@Sun.COM 		cmn_err(CE_WARN, "simnet: MAC call to remove a "
10179815SRishi.Srivatsavai@Sun.COM 		    "Multicast address failed");
10189815SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
10199815SRishi.Srivatsavai@Sun.COM 	}
10209815SRishi.Srivatsavai@Sun.COM 
10219815SRishi.Srivatsavai@Sun.COM 	kmem_free(sdev->sd_mcastaddrs, prevsize);
10229815SRishi.Srivatsavai@Sun.COM 	sdev->sd_mcastaddrs = newbuf;
10239815SRishi.Srivatsavai@Sun.COM 	mutex_exit(&sdev->sd_instlock);
10249815SRishi.Srivatsavai@Sun.COM 	return (0);
10259815SRishi.Srivatsavai@Sun.COM }
10269815SRishi.Srivatsavai@Sun.COM 
10279815SRishi.Srivatsavai@Sun.COM static int
10289815SRishi.Srivatsavai@Sun.COM simnet_m_unicst(void *arg, const uint8_t *macaddr)
10299815SRishi.Srivatsavai@Sun.COM {
10309815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
10319815SRishi.Srivatsavai@Sun.COM 
10329815SRishi.Srivatsavai@Sun.COM 	(void) memcpy(sdev->sd_mac_addr, macaddr, ETHERADDRL);
10339815SRishi.Srivatsavai@Sun.COM 	return (0);
10349815SRishi.Srivatsavai@Sun.COM }
10359815SRishi.Srivatsavai@Sun.COM 
10369815SRishi.Srivatsavai@Sun.COM /* Parse WiFi scan list entry arguments and return the arg count */
10379815SRishi.Srivatsavai@Sun.COM static int
10389815SRishi.Srivatsavai@Sun.COM parse_esslist_args(const void *pr_val, uint_t pr_valsize,
10399815SRishi.Srivatsavai@Sun.COM     char args[][MAX_ESSLIST_ARGLEN])
10409815SRishi.Srivatsavai@Sun.COM {
10419815SRishi.Srivatsavai@Sun.COM 	char *sep;
10429815SRishi.Srivatsavai@Sun.COM 	ptrdiff_t len = pr_valsize;
10439815SRishi.Srivatsavai@Sun.COM 	const char *piece = pr_val;
10449815SRishi.Srivatsavai@Sun.COM 	const char *end = (const char *)pr_val + pr_valsize - 1;
10459815SRishi.Srivatsavai@Sun.COM 	int arg = 0;
10469815SRishi.Srivatsavai@Sun.COM 
10479815SRishi.Srivatsavai@Sun.COM 	while (piece < end && (arg < MAX_ESSLIST_ARGS)) {
10489815SRishi.Srivatsavai@Sun.COM 		sep = strchr(piece, ',');
10499815SRishi.Srivatsavai@Sun.COM 		if (sep == NULL)
10509815SRishi.Srivatsavai@Sun.COM 			sep = (char *)end;
10519815SRishi.Srivatsavai@Sun.COM 		/* LINTED E_PTRDIFF_OVERFLOW */
10529815SRishi.Srivatsavai@Sun.COM 		len = sep - piece;
10539815SRishi.Srivatsavai@Sun.COM 		/* If first arg is zero then return none to delete all */
10549815SRishi.Srivatsavai@Sun.COM 		if (arg == 0 && strnlen(piece, len) == 1 && piece[0] == '0')
10559815SRishi.Srivatsavai@Sun.COM 			return (0);
10569815SRishi.Srivatsavai@Sun.COM 		if (len > MAX_ESSLIST_ARGLEN)
10579815SRishi.Srivatsavai@Sun.COM 			len = MAX_ESSLIST_ARGLEN - 1;
10589815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(&args[arg][0], piece, len);
10599815SRishi.Srivatsavai@Sun.COM 		args[arg][len] = '\0';
10609815SRishi.Srivatsavai@Sun.COM 		piece = sep + 1;
10619815SRishi.Srivatsavai@Sun.COM 		arg++;
10629815SRishi.Srivatsavai@Sun.COM 	}
10639815SRishi.Srivatsavai@Sun.COM 
10649815SRishi.Srivatsavai@Sun.COM 	return (arg);
10659815SRishi.Srivatsavai@Sun.COM }
10669815SRishi.Srivatsavai@Sun.COM 
10679815SRishi.Srivatsavai@Sun.COM /* Set WiFi scan list entry from private property _wl_esslist */
10689815SRishi.Srivatsavai@Sun.COM static int
10699815SRishi.Srivatsavai@Sun.COM set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize,
10709815SRishi.Srivatsavai@Sun.COM     const void *pr_val)
10719815SRishi.Srivatsavai@Sun.COM {
10729815SRishi.Srivatsavai@Sun.COM 	char essargs[MAX_ESSLIST_ARGS][MAX_ESSLIST_ARGLEN];
10739815SRishi.Srivatsavai@Sun.COM 	wl_ess_conf_t *wls;
10749815SRishi.Srivatsavai@Sun.COM 	long result;
10759815SRishi.Srivatsavai@Sun.COM 	int i;
10769815SRishi.Srivatsavai@Sun.COM 
10779815SRishi.Srivatsavai@Sun.COM 	bzero(essargs, sizeof (essargs));
10789815SRishi.Srivatsavai@Sun.COM 	if (parse_esslist_args(pr_val, pr_valsize, essargs) == 0) {
10799815SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < wdev->swd_esslist_num; i++) {
10809815SRishi.Srivatsavai@Sun.COM 			kmem_free(wdev->swd_esslist[i], sizeof (wl_ess_conf_t));
10819815SRishi.Srivatsavai@Sun.COM 			wdev->swd_esslist[i] = NULL;
10829815SRishi.Srivatsavai@Sun.COM 		}
10839815SRishi.Srivatsavai@Sun.COM 		wdev->swd_esslist_num = 0;
10849815SRishi.Srivatsavai@Sun.COM 		return (0);
10859815SRishi.Srivatsavai@Sun.COM 	}
10869815SRishi.Srivatsavai@Sun.COM 
10879815SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < wdev->swd_esslist_num; i++) {
10889815SRishi.Srivatsavai@Sun.COM 		wls = wdev->swd_esslist[i];
10899815SRishi.Srivatsavai@Sun.COM 		if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
10909815SRishi.Srivatsavai@Sun.COM 		    essargs[0]) == 0)
10919815SRishi.Srivatsavai@Sun.COM 			return (EEXIST);
10929815SRishi.Srivatsavai@Sun.COM 	}
10939815SRishi.Srivatsavai@Sun.COM 
10949815SRishi.Srivatsavai@Sun.COM 	if (wdev->swd_esslist_num >= MAX_SIMNET_ESSCONF)
10959815SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
10969815SRishi.Srivatsavai@Sun.COM 
10979815SRishi.Srivatsavai@Sun.COM 	wls = kmem_zalloc(sizeof (wl_ess_conf_t), KM_SLEEP);
10989815SRishi.Srivatsavai@Sun.COM 	(void) strlcpy(wls->wl_ess_conf_essid.wl_essid_essid,
10999815SRishi.Srivatsavai@Sun.COM 	    essargs[0], sizeof (wls->wl_ess_conf_essid.wl_essid_essid));
11009815SRishi.Srivatsavai@Sun.COM 	wls->wl_ess_conf_essid.wl_essid_length =
11019815SRishi.Srivatsavai@Sun.COM 	    strlen(wls->wl_ess_conf_essid.wl_essid_essid);
11029815SRishi.Srivatsavai@Sun.COM 	(void) random_get_pseudo_bytes((uint8_t *)
11039815SRishi.Srivatsavai@Sun.COM 	    &wls->wl_ess_conf_bssid, sizeof (wl_bssid_t));
11049815SRishi.Srivatsavai@Sun.COM 	(void) ddi_strtol(essargs[1], (char **)NULL, 0, &result);
11059815SRishi.Srivatsavai@Sun.COM 	wls->wl_ess_conf_sl = (wl_rssi_t)
11069815SRishi.Srivatsavai@Sun.COM 	    ((result > MAX_RSSI || result < 0) ? 0:result);
11079815SRishi.Srivatsavai@Sun.COM 	wdev->swd_esslist[wdev->swd_esslist_num] = wls;
11089815SRishi.Srivatsavai@Sun.COM 	wdev->swd_esslist_num++;
11099815SRishi.Srivatsavai@Sun.COM 
11109815SRishi.Srivatsavai@Sun.COM 	return (0);
11119815SRishi.Srivatsavai@Sun.COM }
11129815SRishi.Srivatsavai@Sun.COM 
11139815SRishi.Srivatsavai@Sun.COM static int
11149815SRishi.Srivatsavai@Sun.COM simnet_set_priv_prop(simnet_dev_t *sdev, const char *pr_name,
11159815SRishi.Srivatsavai@Sun.COM     uint_t pr_valsize, const void *pr_val)
11169815SRishi.Srivatsavai@Sun.COM {
11179815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
11189815SRishi.Srivatsavai@Sun.COM 	long result;
11199815SRishi.Srivatsavai@Sun.COM 
11209815SRishi.Srivatsavai@Sun.COM 	if (strcmp(pr_name, "_wl_esslist") == 0) {
11219815SRishi.Srivatsavai@Sun.COM 		if (pr_val == NULL)
11229815SRishi.Srivatsavai@Sun.COM 			return (EINVAL);
11239815SRishi.Srivatsavai@Sun.COM 		return (set_wl_esslist_priv_prop(wdev, pr_valsize, pr_val));
11249815SRishi.Srivatsavai@Sun.COM 	} else if (strcmp(pr_name, "_wl_connected") == 0) {
11259815SRishi.Srivatsavai@Sun.COM 		if (pr_val == NULL)
11269815SRishi.Srivatsavai@Sun.COM 			return (EINVAL);
11279815SRishi.Srivatsavai@Sun.COM 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
11289815SRishi.Srivatsavai@Sun.COM 		wdev->swd_linkstatus = ((result == 1) ?
11299815SRishi.Srivatsavai@Sun.COM 		    WL_CONNECTED:WL_NOTCONNECTED);
11309815SRishi.Srivatsavai@Sun.COM 		return (0);
11319815SRishi.Srivatsavai@Sun.COM 	}
11329815SRishi.Srivatsavai@Sun.COM 
11339815SRishi.Srivatsavai@Sun.COM 	return (EINVAL);
11349815SRishi.Srivatsavai@Sun.COM }
11359815SRishi.Srivatsavai@Sun.COM 
11369815SRishi.Srivatsavai@Sun.COM static int
11379815SRishi.Srivatsavai@Sun.COM simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
11389815SRishi.Srivatsavai@Sun.COM     uint_t wldp_length, const void *wldp_buf)
11399815SRishi.Srivatsavai@Sun.COM {
11409815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
11419815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
11429815SRishi.Srivatsavai@Sun.COM 	int err = 0;
11439815SRishi.Srivatsavai@Sun.COM 	uint32_t mtu;
11449815SRishi.Srivatsavai@Sun.COM 
11459815SRishi.Srivatsavai@Sun.COM 	switch (wldp_pr_num) {
11469815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_MTU:
11479815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(&mtu, wldp_buf, sizeof (mtu));
11489815SRishi.Srivatsavai@Sun.COM 		if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU)
11499815SRishi.Srivatsavai@Sun.COM 			return (mac_maxsdu_update(sdev->sd_mh, mtu));
11509815SRishi.Srivatsavai@Sun.COM 		else
11519815SRishi.Srivatsavai@Sun.COM 			return (EINVAL);
11529815SRishi.Srivatsavai@Sun.COM 	default:
11539815SRishi.Srivatsavai@Sun.COM 		break;
11549815SRishi.Srivatsavai@Sun.COM 	}
11559815SRishi.Srivatsavai@Sun.COM 
11569815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_type == DL_ETHER)
11579815SRishi.Srivatsavai@Sun.COM 		return (ENOTSUP);
11589815SRishi.Srivatsavai@Sun.COM 
11599815SRishi.Srivatsavai@Sun.COM 	/* mac_prop_id */
11609815SRishi.Srivatsavai@Sun.COM 	switch (wldp_pr_num) {
11619815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_ESSID: {
11629815SRishi.Srivatsavai@Sun.COM 		int i;
11639815SRishi.Srivatsavai@Sun.COM 		wl_ess_conf_t *wls;
11649815SRishi.Srivatsavai@Sun.COM 
11659815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(&wdev->swd_essid, wldp_buf,
11669815SRishi.Srivatsavai@Sun.COM 		    sizeof (wl_essid_t));
11679815SRishi.Srivatsavai@Sun.COM 		wdev->swd_linkstatus = WL_CONNECTED;
11689815SRishi.Srivatsavai@Sun.COM 
11699815SRishi.Srivatsavai@Sun.COM 		/* Lookup the signal strength of the connected ESSID */
11709815SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < wdev->swd_esslist_num; i++) {
11719815SRishi.Srivatsavai@Sun.COM 			wls = wdev->swd_esslist[i];
11729815SRishi.Srivatsavai@Sun.COM 			if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
11739815SRishi.Srivatsavai@Sun.COM 			    wdev->swd_essid.wl_essid_essid) == 0) {
11749815SRishi.Srivatsavai@Sun.COM 				wdev->swd_rssi = wls->wl_ess_conf_sl;
11759815SRishi.Srivatsavai@Sun.COM 				break;
11769815SRishi.Srivatsavai@Sun.COM 			}
11779815SRishi.Srivatsavai@Sun.COM 		}
11789815SRishi.Srivatsavai@Sun.COM 		break;
11799815SRishi.Srivatsavai@Sun.COM 	}
11809815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_BSSID: {
11819815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(&wdev->swd_bssid, wldp_buf,
11829815SRishi.Srivatsavai@Sun.COM 		    sizeof (wl_bssid_t));
11839815SRishi.Srivatsavai@Sun.COM 		break;
11849815SRishi.Srivatsavai@Sun.COM 	}
11859815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_PHY_CONFIG:
11869815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_KEY_TAB:
11879815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_AUTH_MODE:
11889815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_ENCRYPTION:
11899815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_BSSTYPE:
11909815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
11919815SRishi.Srivatsavai@Sun.COM 		break;
11929815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_PRIVATE:
11939815SRishi.Srivatsavai@Sun.COM 		err = simnet_set_priv_prop(sdev, pr_name,
11949815SRishi.Srivatsavai@Sun.COM 		    wldp_length, wldp_buf);
11959815SRishi.Srivatsavai@Sun.COM 		break;
11969815SRishi.Srivatsavai@Sun.COM 	default:
11979815SRishi.Srivatsavai@Sun.COM 		break;
11989815SRishi.Srivatsavai@Sun.COM 	}
11999815SRishi.Srivatsavai@Sun.COM 
12009815SRishi.Srivatsavai@Sun.COM 	return (err);
12019815SRishi.Srivatsavai@Sun.COM }
12029815SRishi.Srivatsavai@Sun.COM 
12039815SRishi.Srivatsavai@Sun.COM static int
12049815SRishi.Srivatsavai@Sun.COM simnet_get_priv_prop(simnet_dev_t *sdev, const char *pr_name, uint_t pr_flags,
12059815SRishi.Srivatsavai@Sun.COM     uint_t pr_valsize, void *pr_val)
12069815SRishi.Srivatsavai@Sun.COM {
12079815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
12089815SRishi.Srivatsavai@Sun.COM 	boolean_t is_default = ((pr_flags & MAC_PROP_DEFAULT) != 0);
12099815SRishi.Srivatsavai@Sun.COM 	int err = 0;
12109815SRishi.Srivatsavai@Sun.COM 	int value;
12119815SRishi.Srivatsavai@Sun.COM 
12129815SRishi.Srivatsavai@Sun.COM 	if (strcmp(pr_name, "_wl_esslist") == 0) {
12139815SRishi.Srivatsavai@Sun.COM 		/* Returns num of _wl_ess_conf_t that have been set */
12149815SRishi.Srivatsavai@Sun.COM 		value = (is_default ? 0:wdev->swd_esslist_num);
12159815SRishi.Srivatsavai@Sun.COM 	} else if (strcmp(pr_name, "_wl_connected") == 0) {
12169815SRishi.Srivatsavai@Sun.COM 		value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0);
12179815SRishi.Srivatsavai@Sun.COM 	} else {
12189815SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
12199815SRishi.Srivatsavai@Sun.COM 	}
12209815SRishi.Srivatsavai@Sun.COM 
12219815SRishi.Srivatsavai@Sun.COM 	if (err == 0)
12229815SRishi.Srivatsavai@Sun.COM 		(void) snprintf(pr_val, pr_valsize, "%d", value);
12239815SRishi.Srivatsavai@Sun.COM 	return (err);
12249815SRishi.Srivatsavai@Sun.COM }
12259815SRishi.Srivatsavai@Sun.COM 
12269815SRishi.Srivatsavai@Sun.COM static int
12279815SRishi.Srivatsavai@Sun.COM simnet_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
12289815SRishi.Srivatsavai@Sun.COM     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
12299815SRishi.Srivatsavai@Sun.COM {
12309815SRishi.Srivatsavai@Sun.COM 	simnet_dev_t *sdev = arg;
12319815SRishi.Srivatsavai@Sun.COM 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
12329815SRishi.Srivatsavai@Sun.COM 	int err = 0;
12339815SRishi.Srivatsavai@Sun.COM 	int i;
12349815SRishi.Srivatsavai@Sun.COM 
12359815SRishi.Srivatsavai@Sun.COM 	if (sdev->sd_type == DL_ETHER)
12369815SRishi.Srivatsavai@Sun.COM 		return (ENOTSUP);
12379815SRishi.Srivatsavai@Sun.COM 
12389815SRishi.Srivatsavai@Sun.COM 	/* mac_prop_id */
12399815SRishi.Srivatsavai@Sun.COM 	switch (wldp_pr_num) {
12409815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_ESSID:
12419815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(wldp_buf, &wdev->swd_essid,
12429815SRishi.Srivatsavai@Sun.COM 		    sizeof (wl_essid_t));
12439815SRishi.Srivatsavai@Sun.COM 		break;
12449815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_BSSID:
12459815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(wldp_buf, &wdev->swd_bssid,
12469815SRishi.Srivatsavai@Sun.COM 		    sizeof (wl_bssid_t));
12479815SRishi.Srivatsavai@Sun.COM 		break;
12489815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_PHY_CONFIG:
12499815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_AUTH_MODE:
12509815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_ENCRYPTION:
12519815SRishi.Srivatsavai@Sun.COM 		break;
12529815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_BSSTYPE:
12539815SRishi.Srivatsavai@Sun.COM 		*perm = MAC_PROP_PERM_READ;
12549815SRishi.Srivatsavai@Sun.COM 		break;
12559815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_LINKSTATUS:
12569815SRishi.Srivatsavai@Sun.COM 		(void) memcpy(wldp_buf, &wdev->swd_linkstatus,
12579815SRishi.Srivatsavai@Sun.COM 		    sizeof (wdev->swd_linkstatus));
12589815SRishi.Srivatsavai@Sun.COM 		break;
12599815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_ESS_LIST: {
12609815SRishi.Srivatsavai@Sun.COM 		wl_ess_conf_t *w_ess_conf;
12619815SRishi.Srivatsavai@Sun.COM 
12629815SRishi.Srivatsavai@Sun.COM 		*perm = MAC_PROP_PERM_READ;
12639815SRishi.Srivatsavai@Sun.COM 		((wl_ess_list_t *)wldp_buf)->wl_ess_list_num =
12649815SRishi.Srivatsavai@Sun.COM 		    wdev->swd_esslist_num;
12659815SRishi.Srivatsavai@Sun.COM 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12669815SRishi.Srivatsavai@Sun.COM 		w_ess_conf = (wl_ess_conf_t *)((char *)wldp_buf +
12679815SRishi.Srivatsavai@Sun.COM 		    offsetof(wl_ess_list_t, wl_ess_list_ess));
12689815SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < wdev->swd_esslist_num; i++) {
12699815SRishi.Srivatsavai@Sun.COM 			(void) memcpy(w_ess_conf, wdev->swd_esslist[i],
12709815SRishi.Srivatsavai@Sun.COM 			    sizeof (wl_ess_conf_t));
12719815SRishi.Srivatsavai@Sun.COM 			w_ess_conf++;
12729815SRishi.Srivatsavai@Sun.COM 		}
12739815SRishi.Srivatsavai@Sun.COM 		break;
12749815SRishi.Srivatsavai@Sun.COM 	}
12759815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_SUPPORTED_RATES:
12769815SRishi.Srivatsavai@Sun.COM 		*perm = MAC_PROP_PERM_READ;
12779815SRishi.Srivatsavai@Sun.COM 		break;
12789815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_RSSI:
12799815SRishi.Srivatsavai@Sun.COM 		*perm = MAC_PROP_PERM_READ;
12809815SRishi.Srivatsavai@Sun.COM 		*(wl_rssi_t *)wldp_buf = wdev->swd_rssi;
12819815SRishi.Srivatsavai@Sun.COM 		break;
12829815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_RADIO:
12839815SRishi.Srivatsavai@Sun.COM 		*(wl_radio_t *)wldp_buf = B_TRUE;
12849815SRishi.Srivatsavai@Sun.COM 		break;
12859815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_POWER_MODE:
12869815SRishi.Srivatsavai@Sun.COM 		break;
12879815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
12889815SRishi.Srivatsavai@Sun.COM 		break;
12899815SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_PRIVATE:
12909815SRishi.Srivatsavai@Sun.COM 		err = simnet_get_priv_prop(sdev, pr_name, pr_flags,
12919815SRishi.Srivatsavai@Sun.COM 		    wldp_length, wldp_buf);
12929815SRishi.Srivatsavai@Sun.COM 		break;
12939815SRishi.Srivatsavai@Sun.COM 	default:
12949815SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
12959815SRishi.Srivatsavai@Sun.COM 		break;
12969815SRishi.Srivatsavai@Sun.COM 	}
12979815SRishi.Srivatsavai@Sun.COM 
12989815SRishi.Srivatsavai@Sun.COM 	return (err);
12999815SRishi.Srivatsavai@Sun.COM }
1300