xref: /titanic_44/usr/src/lib/libdladm/common/libdlwlan.c (revision a399b7655a1d835aa8606c2b29e4e777baac8635)
1f595a68aSyz147064 /*
2f595a68aSyz147064  * CDDL HEADER START
3f595a68aSyz147064  *
4f595a68aSyz147064  * The contents of this file are subject to the terms of the
5f595a68aSyz147064  * Common Development and Distribution License (the "License").
6f595a68aSyz147064  * You may not use this file except in compliance with the License.
7f595a68aSyz147064  *
8f595a68aSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f595a68aSyz147064  * or http://www.opensolaris.org/os/licensing.
10f595a68aSyz147064  * See the License for the specific language governing permissions
11f595a68aSyz147064  * and limitations under the License.
12f595a68aSyz147064  *
13f595a68aSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14f595a68aSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f595a68aSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16f595a68aSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17f595a68aSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18f595a68aSyz147064  *
19f595a68aSyz147064  * CDDL HEADER END
20f595a68aSyz147064  */
21f595a68aSyz147064 /*
22f595a68aSyz147064  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23f595a68aSyz147064  * Use is subject to license terms.
24f595a68aSyz147064  */
25f595a68aSyz147064 
26f595a68aSyz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27f595a68aSyz147064 
28f595a68aSyz147064 #include <libintl.h>
29f595a68aSyz147064 #include <stdio.h>
30f595a68aSyz147064 #include <stdlib.h>
31f595a68aSyz147064 #include <stddef.h>
32f595a68aSyz147064 #include <unistd.h>
33f595a68aSyz147064 #include <fcntl.h>
34f595a68aSyz147064 #include <string.h>
35f595a68aSyz147064 #include <stropts.h>
36f595a68aSyz147064 #include <libdevinfo.h>
37f595a68aSyz147064 #include <net/if.h>
38f595a68aSyz147064 #include <net/if_dl.h>
39f595a68aSyz147064 #include <net/if_types.h>
40*a399b765Szf162725 #include <libscf.h>
41f595a68aSyz147064 #include <libdlwlan.h>
42f595a68aSyz147064 #include <libdlwlan_impl.h>
43*a399b765Szf162725 #include <net/wpa.h>
44f595a68aSyz147064 
45f595a68aSyz147064 typedef struct val_desc {
46f595a68aSyz147064 	char		*vd_name;
47f595a68aSyz147064 	uint_t		vd_val;
48f595a68aSyz147064 } val_desc_t;
49f595a68aSyz147064 
50f595a68aSyz147064 struct prop_desc;
51f595a68aSyz147064 
52f595a68aSyz147064 typedef dladm_status_t	wl_pd_getf_t(int, wldp_t *, char **, uint_t *);
53f595a68aSyz147064 typedef dladm_status_t	wl_pd_setf_t(int, wldp_t *, val_desc_t *, uint_t);
54f595a68aSyz147064 typedef dladm_status_t	wl_pd_checkf_t(int, wldp_t *, struct prop_desc *,
55f595a68aSyz147064 			    char **, uint_t, val_desc_t **);
56f595a68aSyz147064 typedef struct prop_desc {
57f595a68aSyz147064 	char		*pd_name;
58f595a68aSyz147064 	val_desc_t	pd_defval;
59f595a68aSyz147064 	val_desc_t	*pd_modval;
60f595a68aSyz147064 	uint_t		pd_nmodval;
61f595a68aSyz147064 	wl_pd_setf_t	*pd_set;
62f595a68aSyz147064 	wl_pd_getf_t	*pd_getmod;
63f595a68aSyz147064 	wl_pd_getf_t	*pd_get;
64f595a68aSyz147064 	wl_pd_checkf_t	*pd_check;
65f595a68aSyz147064 } prop_desc_t;
66f595a68aSyz147064 
67*a399b765Szf162725 static int	wpa_instance_create(const char *, void *);
68*a399b765Szf162725 static int	wpa_instance_delete(const char *);
69*a399b765Szf162725 
70f595a68aSyz147064 static int 	do_get_bsstype(int, wldp_t *);
71f595a68aSyz147064 static int 	do_get_essid(int, wldp_t *);
72f595a68aSyz147064 static int 	do_get_bssid(int, wldp_t *);
73f595a68aSyz147064 static int 	do_get_signal(int, wldp_t *);
74f595a68aSyz147064 static int 	do_get_encryption(int, wldp_t *);
75f595a68aSyz147064 static int 	do_get_authmode(int, wldp_t *);
76f595a68aSyz147064 static int 	do_get_linkstatus(int, wldp_t *);
77f595a68aSyz147064 static int	do_get_esslist(int, wldp_t *);
78f595a68aSyz147064 static int 	do_get_rate(int, wldp_t *);
79f595a68aSyz147064 static int	do_get_phyconf(int, wldp_t *);
80f595a68aSyz147064 static int	do_get_powermode(int, wldp_t *);
81f595a68aSyz147064 static int	do_get_radio(int, wldp_t *);
82f595a68aSyz147064 static int	do_get_mode(int, wldp_t *);
83*a399b765Szf162725 static int	do_get_capability(int, wldp_t *);
84*a399b765Szf162725 static int	do_get_wpamode(int, wldp_t *);
85f595a68aSyz147064 
86f595a68aSyz147064 static int	do_set_bsstype(int, wldp_t *, dladm_wlan_bsstype_t *);
87f595a68aSyz147064 static int	do_set_authmode(int, wldp_t *, dladm_wlan_auth_t *);
88f595a68aSyz147064 static int	do_set_encryption(int, wldp_t *, dladm_wlan_secmode_t *);
89f595a68aSyz147064 static int	do_set_essid(int, wldp_t *, dladm_wlan_essid_t *);
90f595a68aSyz147064 static int	do_set_createibss(int, wldp_t *, boolean_t *);
91*a399b765Szf162725 static int	do_set_key(int, wldp_t *, dladm_wlan_key_t *, uint_t);
92f595a68aSyz147064 static int	do_set_rate(int, wldp_t *, dladm_wlan_rates_t *);
93f595a68aSyz147064 static int	do_set_powermode(int, wldp_t *, dladm_wlan_powermode_t *);
94f595a68aSyz147064 static int	do_set_radio(int, wldp_t *, dladm_wlan_radio_t *);
95f595a68aSyz147064 static int	do_set_channel(int, wldp_t *, dladm_wlan_channel_t *);
96f595a68aSyz147064 
97f595a68aSyz147064 static int	open_link(const char *);
98f595a68aSyz147064 static int	do_scan(int, wldp_t *);
99*a399b765Szf162725 static int	do_disconnect(const char *, int, wldp_t *);
100f595a68aSyz147064 static boolean_t find_val_by_name(const char *, val_desc_t *, uint_t, uint_t *);
101f595a68aSyz147064 static boolean_t find_name_by_val(uint_t, val_desc_t *, uint_t, char **);
102f595a68aSyz147064 static void	generate_essid(dladm_wlan_essid_t *);
103f595a68aSyz147064 
104f595a68aSyz147064 static dladm_status_t	dladm_wlan_wlresult2status(wldp_t *);
105f595a68aSyz147064 
106f595a68aSyz147064 static wl_pd_getf_t	do_get_rate_mod, do_get_rate_prop, do_get_channel_prop,
107f595a68aSyz147064 			do_get_powermode_prop, do_get_radio_prop;
108f595a68aSyz147064 static wl_pd_setf_t 	do_set_rate_prop, do_set_powermode_prop,
109f595a68aSyz147064 			do_set_radio_prop;
110f595a68aSyz147064 static wl_pd_checkf_t	do_check_prop, do_check_rate;
111f595a68aSyz147064 
112f595a68aSyz147064 static val_desc_t	linkstatus_vals[] = {
113f595a68aSyz147064 	{ "disconnected", 	DLADM_WLAN_LINKSTATUS_DISCONNECTED	},
114f595a68aSyz147064 	{ "connected",		DLADM_WLAN_LINKSTATUS_CONNECTED	}
115f595a68aSyz147064 };
116f595a68aSyz147064 
117f595a68aSyz147064 static val_desc_t 	secmode_vals[] = {
118f595a68aSyz147064 	{ "none",	DLADM_WLAN_SECMODE_NONE		},
119*a399b765Szf162725 	{ "wep",	DLADM_WLAN_SECMODE_WEP		},
120*a399b765Szf162725 	{ "wpa",	DLADM_WLAN_SECMODE_WPA		}
121f595a68aSyz147064 };
122f595a68aSyz147064 
123f595a68aSyz147064 static val_desc_t 	strength_vals[] = {
124f595a68aSyz147064 	{ "very weak",	DLADM_WLAN_STRENGTH_VERY_WEAK 	},
125f595a68aSyz147064 	{ "weak",	DLADM_WLAN_STRENGTH_WEAK		},
126f595a68aSyz147064 	{ "good", 	DLADM_WLAN_STRENGTH_GOOD		},
127f595a68aSyz147064 	{ "very good",	DLADM_WLAN_STRENGTH_VERY_GOOD	},
128f595a68aSyz147064 	{ "excellent",	DLADM_WLAN_STRENGTH_EXCELLENT	}
129f595a68aSyz147064 };
130f595a68aSyz147064 
131f595a68aSyz147064 static val_desc_t	mode_vals[] = {
132f595a68aSyz147064 	{ "a",		DLADM_WLAN_MODE_80211A		},
133f595a68aSyz147064 	{ "b",		DLADM_WLAN_MODE_80211B		},
134f595a68aSyz147064 	{ "g",		DLADM_WLAN_MODE_80211G		},
135f595a68aSyz147064 };
136f595a68aSyz147064 
137f595a68aSyz147064 static val_desc_t	auth_vals[] = {
138f595a68aSyz147064 	{ "open",	DLADM_WLAN_AUTH_OPEN			},
139f595a68aSyz147064 	{ "shared",	DLADM_WLAN_AUTH_SHARED		}
140f595a68aSyz147064 };
141f595a68aSyz147064 
142f595a68aSyz147064 static val_desc_t	bsstype_vals[] = {
143f595a68aSyz147064 	{ "bss",	DLADM_WLAN_BSSTYPE_BSS		},
144f595a68aSyz147064 	{ "ibss",	DLADM_WLAN_BSSTYPE_IBSS		},
145f595a68aSyz147064 	{ "any",	DLADM_WLAN_BSSTYPE_ANY		}
146f595a68aSyz147064 };
147f595a68aSyz147064 
148f595a68aSyz147064 static val_desc_t	radio_vals[] = {
149f595a68aSyz147064 	{ "on",		DLADM_WLAN_RADIO_ON			},
150f595a68aSyz147064 	{ "off",	DLADM_WLAN_RADIO_OFF			}
151f595a68aSyz147064 };
152f595a68aSyz147064 
153f595a68aSyz147064 static val_desc_t	powermode_vals[] = {
154f595a68aSyz147064 	{ "off",	DLADM_WLAN_PM_OFF			},
155f595a68aSyz147064 	{ "fast",	DLADM_WLAN_PM_FAST			},
156f595a68aSyz147064 	{ "max",	DLADM_WLAN_PM_MAX			}
157f595a68aSyz147064 };
158f595a68aSyz147064 
159f595a68aSyz147064 #define	VALCNT(vals)	(sizeof ((vals)) / sizeof (val_desc_t))
160f595a68aSyz147064 static	prop_desc_t	prop_table[] = {
161f595a68aSyz147064 
162f595a68aSyz147064 	{ "channel",	{ NULL, 0 }, NULL, 0,
163f595a68aSyz147064 	    NULL, NULL, do_get_channel_prop, do_check_prop},
164f595a68aSyz147064 
165f595a68aSyz147064 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF }, powermode_vals,
166f595a68aSyz147064 	    VALCNT(powermode_vals),
167f595a68aSyz147064 	    do_set_powermode_prop, NULL,
168f595a68aSyz147064 	    do_get_powermode_prop, do_check_prop},
169f595a68aSyz147064 
170f595a68aSyz147064 	{ "radio", 	{ "on", DLADM_WLAN_RADIO_ON }, radio_vals,
171f595a68aSyz147064 	    VALCNT(radio_vals),
172f595a68aSyz147064 	    do_set_radio_prop, NULL,
173f595a68aSyz147064 	    do_get_radio_prop, do_check_prop},
174f595a68aSyz147064 
175f595a68aSyz147064 	{ "speed",	{ "", 0 }, NULL, 0,
176f595a68aSyz147064 	    do_set_rate_prop, do_get_rate_mod,
177f595a68aSyz147064 	    do_get_rate_prop, do_check_rate}
178f595a68aSyz147064 };
179f595a68aSyz147064 /*
180f595a68aSyz147064  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
181f595a68aSyz147064  * rates to be retrieved. However, we cannot increase it at this
182f595a68aSyz147064  * time because it will break binary comatibility with unbundled
183f595a68aSyz147064  * WiFi drivers and utilities. So for now we define an additional
184f595a68aSyz147064  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
185f595a68aSyz147064  */
186f595a68aSyz147064 #define	MAX_SUPPORT_RATES	64
187f595a68aSyz147064 #define	DLADM_WLAN_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
188f595a68aSyz147064 #define	IS_CONNECTED(gbuf) \
189f595a68aSyz147064 	((*(wl_linkstatus_t *)((gbuf)->wldp_buf) == WL_CONNECTED))
190f595a68aSyz147064 
191f595a68aSyz147064 static dladm_status_t
192f595a68aSyz147064 dladm_wlan_wlresult2status(wldp_t *gbuf)
193f595a68aSyz147064 {
194f595a68aSyz147064 	switch (gbuf->wldp_result) {
195f595a68aSyz147064 	case WL_SUCCESS:
196f595a68aSyz147064 		return (DLADM_STATUS_OK);
197f595a68aSyz147064 
198f595a68aSyz147064 	case WL_NOTSUPPORTED:
199f595a68aSyz147064 	case WL_LACK_FEATURE:
200f595a68aSyz147064 		return (DLADM_STATUS_NOTSUP);
201f595a68aSyz147064 
202f595a68aSyz147064 	case WL_READONLY:
203f595a68aSyz147064 		return (DLADM_STATUS_PROPRDONLY);
204f595a68aSyz147064 
205f595a68aSyz147064 	default:
206f595a68aSyz147064 		break;
207f595a68aSyz147064 	}
208f595a68aSyz147064 
209f595a68aSyz147064 	return (DLADM_STATUS_FAILED);
210f595a68aSyz147064 }
211f595a68aSyz147064 
212f595a68aSyz147064 static int
213f595a68aSyz147064 open_link(const char *link)
214f595a68aSyz147064 {
215f595a68aSyz147064 	char	linkname[MAXPATHLEN];
216f595a68aSyz147064 	wldp_t	*gbuf;
217f595a68aSyz147064 	int	fd;
218f595a68aSyz147064 
219f595a68aSyz147064 	if (link == NULL)
220f595a68aSyz147064 		return (-1);
221f595a68aSyz147064 
222f595a68aSyz147064 	(void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link);
223f595a68aSyz147064 	if ((fd = open(linkname, O_RDWR)) < 0)
224f595a68aSyz147064 		return (-1);
225f595a68aSyz147064 
226f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
227f595a68aSyz147064 		(void) close(fd);
228f595a68aSyz147064 		return (-1);
229f595a68aSyz147064 	}
230f595a68aSyz147064 
231f595a68aSyz147064 	/*
232f595a68aSyz147064 	 * Check to see if the link is wireless.
233f595a68aSyz147064 	 */
234f595a68aSyz147064 	if (do_get_bsstype(fd, gbuf) < 0) {
235f595a68aSyz147064 		free(gbuf);
236f595a68aSyz147064 		(void) close(fd);
237f595a68aSyz147064 		return (-1);
238f595a68aSyz147064 	}
239f595a68aSyz147064 
240f595a68aSyz147064 	free(gbuf);
241f595a68aSyz147064 	return (fd);
242f595a68aSyz147064 }
243f595a68aSyz147064 
244f595a68aSyz147064 static dladm_wlan_mode_t
245f595a68aSyz147064 do_convert_mode(wl_phy_conf_t *phyp)
246f595a68aSyz147064 {
247f595a68aSyz147064 	switch (phyp->wl_phy_fhss_conf.wl_fhss_subtype) {
248f595a68aSyz147064 	case WL_ERP:
249f595a68aSyz147064 		return (DLADM_WLAN_MODE_80211G);
250f595a68aSyz147064 	case WL_OFDM:
251f595a68aSyz147064 		return (DLADM_WLAN_MODE_80211A);
252f595a68aSyz147064 	case WL_DSSS:
253f595a68aSyz147064 	case WL_FHSS:
254f595a68aSyz147064 		return (DLADM_WLAN_MODE_80211B);
255f595a68aSyz147064 	default:
256f595a68aSyz147064 		break;
257f595a68aSyz147064 	}
258f595a68aSyz147064 
259f595a68aSyz147064 	return (DLADM_WLAN_MODE_NONE);
260f595a68aSyz147064 }
261f595a68aSyz147064 
262f595a68aSyz147064 static boolean_t
263f595a68aSyz147064 do_convert_chan(wl_phy_conf_t *phyp, uint32_t *channelp)
264f595a68aSyz147064 {
265f595a68aSyz147064 	wl_fhss_t *wlfp = &phyp->wl_phy_fhss_conf;
266f595a68aSyz147064 	wl_ofdm_t *wlop = &phyp->wl_phy_ofdm_conf;
267f595a68aSyz147064 
268f595a68aSyz147064 	switch (wlfp->wl_fhss_subtype) {
269f595a68aSyz147064 	case WL_FHSS:
270f595a68aSyz147064 	case WL_DSSS:
271f595a68aSyz147064 	case WL_IRBASE:
272f595a68aSyz147064 	case WL_HRDS:
273f595a68aSyz147064 	case WL_ERP:
274f595a68aSyz147064 		*channelp = wlfp->wl_fhss_channel;
275f595a68aSyz147064 		break;
276f595a68aSyz147064 	case WL_OFDM:
277f595a68aSyz147064 		*channelp = DLADM_WLAN_OFDM2CHAN(wlop->wl_ofdm_frequency);
278f595a68aSyz147064 		break;
279f595a68aSyz147064 	default:
280f595a68aSyz147064 		return (B_FALSE);
281f595a68aSyz147064 	}
282f595a68aSyz147064 	return (B_TRUE);
283f595a68aSyz147064 }
284f595a68aSyz147064 
285f595a68aSyz147064 #define	IEEE80211_RATE	0x7f
286f595a68aSyz147064 static void
287f595a68aSyz147064 fill_wlan_attr(wl_ess_conf_t *wlp, dladm_wlan_attr_t *attrp)
288f595a68aSyz147064 {
289f595a68aSyz147064 	int		i;
290f595a68aSyz147064 
291f595a68aSyz147064 	(void) memset(attrp, 0, sizeof (*attrp));
292f595a68aSyz147064 
293f595a68aSyz147064 	(void) snprintf(attrp->wa_essid.we_bytes, DLADM_WLAN_MAX_ESSID_LEN,
294f595a68aSyz147064 	    "%s", wlp->wl_ess_conf_essid.wl_essid_essid);
295f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_ESSID;
296f595a68aSyz147064 
297f595a68aSyz147064 	(void) memcpy(attrp->wa_bssid.wb_bytes, wlp->wl_ess_conf_bssid,
298f595a68aSyz147064 	    DLADM_WLAN_BSSID_LEN);
299f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
300f595a68aSyz147064 
301f595a68aSyz147064 	attrp->wa_secmode = (wlp->wl_ess_conf_wepenabled ==
302f595a68aSyz147064 	    WL_ENC_WEP ? DLADM_WLAN_SECMODE_WEP : DLADM_WLAN_SECMODE_NONE);
303*a399b765Szf162725 	if (wlp->wl_ess_conf_reserved[0] > 0)
304*a399b765Szf162725 		attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
305f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
306f595a68aSyz147064 
307f595a68aSyz147064 	attrp->wa_bsstype = (wlp->wl_ess_conf_bsstype == WL_BSS_BSS ?
308f595a68aSyz147064 	    DLADM_WLAN_BSSTYPE_BSS : DLADM_WLAN_BSSTYPE_IBSS);
309f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
310f595a68aSyz147064 
311f595a68aSyz147064 	attrp->wa_auth = (wlp->wl_ess_conf_authmode == 0 ?
312f595a68aSyz147064 	    DLADM_WLAN_AUTH_OPEN : DLADM_WLAN_AUTH_SHARED);
313f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
314f595a68aSyz147064 
315f595a68aSyz147064 	attrp->wa_strength = DLADM_WLAN_SIGNAL2STRENGTH(wlp->wl_ess_conf_sl);
316f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_STRENGTH;
317f595a68aSyz147064 
318f595a68aSyz147064 	attrp->wa_mode = do_convert_mode((wl_phy_conf_t *)&wlp->wl_phy_conf);
319f595a68aSyz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
320f595a68aSyz147064 
321f595a68aSyz147064 	for (i = 0; i < MAX_SCAN_SUPPORT_RATES; i++) {
322f595a68aSyz147064 		wlp->wl_supported_rates[i] &= IEEE80211_RATE;
323f595a68aSyz147064 		if (wlp->wl_supported_rates[i] > attrp->wa_speed)
324f595a68aSyz147064 			attrp->wa_speed = wlp->wl_supported_rates[i];
325f595a68aSyz147064 	}
326f595a68aSyz147064 	if (attrp->wa_speed > 0)
327f595a68aSyz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_SPEED;
328f595a68aSyz147064 
329f595a68aSyz147064 	if (do_convert_chan((wl_phy_conf_t *)&wlp->wl_phy_conf,
330f595a68aSyz147064 	    &attrp->wa_channel))
331f595a68aSyz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_CHANNEL;
332f595a68aSyz147064 }
333f595a68aSyz147064 
334f595a68aSyz147064 dladm_status_t
335f595a68aSyz147064 dladm_wlan_scan(const char *link, void *arg,
336f595a68aSyz147064     boolean_t (*func)(void *, dladm_wlan_attr_t *))
337f595a68aSyz147064 {
338f595a68aSyz147064 	int			fd, i;
339f595a68aSyz147064 	uint32_t		count;
340f595a68aSyz147064 	wl_ess_conf_t		*wlp;
341f595a68aSyz147064 	wldp_t 			*gbuf;
342f595a68aSyz147064 	dladm_wlan_attr_t	wlattr;
343f595a68aSyz147064 	dladm_status_t		status;
344f595a68aSyz147064 	boolean_t		connected;
345f595a68aSyz147064 
346f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
347f595a68aSyz147064 		return (DLADM_STATUS_LINKINVAL);
348f595a68aSyz147064 
349f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
350f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
351f595a68aSyz147064 		goto done;
352f595a68aSyz147064 	}
353f595a68aSyz147064 
354f595a68aSyz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
355f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
356f595a68aSyz147064 		goto done;
357f595a68aSyz147064 	}
358f595a68aSyz147064 	connected = IS_CONNECTED(gbuf);
359f595a68aSyz147064 
360f595a68aSyz147064 	if (do_scan(fd, gbuf) < 0) {
361f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
362f595a68aSyz147064 		goto done;
363f595a68aSyz147064 	}
364f595a68aSyz147064 
365*a399b765Szf162725 	if (func == NULL) {
366*a399b765Szf162725 		status = DLADM_STATUS_OK;
367*a399b765Szf162725 		goto done;
368*a399b765Szf162725 	}
369*a399b765Szf162725 
370f595a68aSyz147064 	if (do_get_esslist(fd, gbuf) < 0) {
371f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
372f595a68aSyz147064 		goto done;
373f595a68aSyz147064 	}
374f595a68aSyz147064 
375f595a68aSyz147064 	wlp = ((wl_ess_list_t *)gbuf->wldp_buf)->wl_ess_list_ess;
376f595a68aSyz147064 	count = ((wl_ess_list_t *)(gbuf->wldp_buf))->wl_ess_list_num;
377f595a68aSyz147064 
378f595a68aSyz147064 	for (i = 0; i < count; i++, wlp++) {
379f595a68aSyz147064 		fill_wlan_attr(wlp, &wlattr);
380f595a68aSyz147064 		if (!func(arg, &wlattr))
381f595a68aSyz147064 			break;
382f595a68aSyz147064 	}
383f595a68aSyz147064 
384f595a68aSyz147064 	if (!connected) {
385f595a68aSyz147064 		if (do_get_linkstatus(fd, gbuf) < 0) {
386f595a68aSyz147064 			status = DLADM_STATUS_FAILED;
387f595a68aSyz147064 			goto done;
388f595a68aSyz147064 		}
389f595a68aSyz147064 		if (IS_CONNECTED(gbuf))
390*a399b765Szf162725 			(void) do_disconnect(link, fd, gbuf);
391f595a68aSyz147064 	}
392f595a68aSyz147064 
393f595a68aSyz147064 	status = DLADM_STATUS_OK;
394f595a68aSyz147064 done:
395f595a68aSyz147064 	free(gbuf);
396f595a68aSyz147064 	(void) close(fd);
397f595a68aSyz147064 	return (status);
398f595a68aSyz147064 }
399f595a68aSyz147064 
400f595a68aSyz147064 /*
401f595a68aSyz147064  * Structures used in building the list of eligible WLANs to connect to.
402f595a68aSyz147064  * Specifically, `connect_state' has the WLAN attributes that must be matched
403f595a68aSyz147064  * (in `cs_attr') and a growing list of WLANs that matched those attributes
404f595a68aSyz147064  * chained through `cs_list'.  Each element in the list is of type `attr_node'
405f595a68aSyz147064  * and has the matching WLAN's attributes and a pointer to the next element.
406f595a68aSyz147064  * For convenience, `cs_count' tracks the number of elements in the list.
407f595a68aSyz147064  */
408f595a68aSyz147064 typedef struct attr_node {
409f595a68aSyz147064 	dladm_wlan_attr_t	an_attr;
410f595a68aSyz147064 	struct attr_node	*an_next;
411f595a68aSyz147064 } attr_node_t;
412f595a68aSyz147064 
413f595a68aSyz147064 typedef struct connect_state {
414f595a68aSyz147064 	dladm_wlan_attr_t	*cs_attr;
415f595a68aSyz147064 	uint_t			cs_count;
416f595a68aSyz147064 	attr_node_t		*cs_list;
417f595a68aSyz147064 } connect_state_t;
418f595a68aSyz147064 
419f595a68aSyz147064 /*
420f595a68aSyz147064  * Compare two sets of WLAN attributes.  For now, we only consider strength
421f595a68aSyz147064  * and speed (in that order), which matches the documented default policy for
422f595a68aSyz147064  * dladm_wlan_connect().
423f595a68aSyz147064  */
424f595a68aSyz147064 static int
425f595a68aSyz147064 attr_compare(const void *p1, const void *p2)
426f595a68aSyz147064 {
427f595a68aSyz147064 	dladm_wlan_attr_t *attrp1, *attrp2;
428f595a68aSyz147064 
429f595a68aSyz147064 	attrp1 = (*(dladm_wlan_attr_t **)p1);
430f595a68aSyz147064 	attrp2 = (*(dladm_wlan_attr_t **)p2);
431f595a68aSyz147064 
432f595a68aSyz147064 	if (attrp1->wa_strength < attrp2->wa_strength)
433f595a68aSyz147064 		return (1);
434f595a68aSyz147064 
435f595a68aSyz147064 	if (attrp1->wa_strength > attrp2->wa_strength)
436f595a68aSyz147064 		return (-1);
437f595a68aSyz147064 
438f595a68aSyz147064 	return (attrp2->wa_speed - attrp1->wa_speed);
439f595a68aSyz147064 }
440f595a68aSyz147064 
441f595a68aSyz147064 /*
442f595a68aSyz147064  * Callback function used by dladm_wlan_connect() to filter out unwanted
443f595a68aSyz147064  * WLANs when scanning for available WLANs.  Always returns B_TRUE to
444f595a68aSyz147064  * continue the scan.
445f595a68aSyz147064  */
446f595a68aSyz147064 static boolean_t
447f595a68aSyz147064 connect_cb(void *arg, dladm_wlan_attr_t *attrp)
448f595a68aSyz147064 {
449f595a68aSyz147064 	attr_node_t		*nodep;
450f595a68aSyz147064 	dladm_wlan_attr_t	*fattrp;
451f595a68aSyz147064 	connect_state_t		*statep = (connect_state_t *)arg;
452f595a68aSyz147064 
453f595a68aSyz147064 	fattrp = statep->cs_attr;
454f595a68aSyz147064 	if (fattrp == NULL)
455f595a68aSyz147064 		goto append;
456f595a68aSyz147064 
457f595a68aSyz147064 	if ((fattrp->wa_valid & attrp->wa_valid) != fattrp->wa_valid)
458f595a68aSyz147064 		return (B_TRUE);
459f595a68aSyz147064 
460f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_ESSID) != 0 &&
461f595a68aSyz147064 	    strncmp(fattrp->wa_essid.we_bytes, attrp->wa_essid.we_bytes,
462f595a68aSyz147064 	    DLADM_WLAN_MAX_ESSID_LEN) != 0)
463f595a68aSyz147064 		return (B_TRUE);
464f595a68aSyz147064 
465f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
466f595a68aSyz147064 	    fattrp->wa_secmode != attrp->wa_secmode)
467f595a68aSyz147064 		return (B_TRUE);
468f595a68aSyz147064 
469f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_MODE) != 0 &&
470f595a68aSyz147064 	    fattrp->wa_mode != attrp->wa_mode)
471f595a68aSyz147064 		return (B_TRUE);
472f595a68aSyz147064 
473f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_STRENGTH) != 0 &&
474f595a68aSyz147064 	    fattrp->wa_strength != attrp->wa_strength)
475f595a68aSyz147064 		return (B_TRUE);
476f595a68aSyz147064 
477f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_SPEED) != 0 &&
478f595a68aSyz147064 	    fattrp->wa_speed != attrp->wa_speed)
479f595a68aSyz147064 		return (B_TRUE);
480f595a68aSyz147064 
481f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_AUTH) != 0) {
482f595a68aSyz147064 		attrp->wa_auth = fattrp->wa_auth;
483f595a68aSyz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
484f595a68aSyz147064 	}
485f595a68aSyz147064 
486f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0 &&
487f595a68aSyz147064 	    fattrp->wa_bsstype != attrp->wa_bsstype)
488f595a68aSyz147064 		return (B_TRUE);
489f595a68aSyz147064 
490f595a68aSyz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_BSSID) != 0 &&
491f595a68aSyz147064 	    memcmp(fattrp->wa_bssid.wb_bytes, attrp->wa_bssid.wb_bytes,
492f595a68aSyz147064 	    DLADM_WLAN_BSSID_LEN) != 0)
493f595a68aSyz147064 		return (B_TRUE);
494f595a68aSyz147064 append:
495f595a68aSyz147064 	nodep = malloc(sizeof (attr_node_t));
496f595a68aSyz147064 	if (nodep == NULL)
497f595a68aSyz147064 		return (B_TRUE);
498f595a68aSyz147064 
499f595a68aSyz147064 	(void) memcpy(&nodep->an_attr, attrp, sizeof (dladm_wlan_attr_t));
500f595a68aSyz147064 	nodep->an_next = statep->cs_list;
501f595a68aSyz147064 	statep->cs_list = nodep;
502f595a68aSyz147064 	statep->cs_count++;
503f595a68aSyz147064 
504f595a68aSyz147064 	return (B_TRUE);
505f595a68aSyz147064 }
506f595a68aSyz147064 
507*a399b765Szf162725 #define	IEEE80211_C_WPA		0x01800000
508*a399b765Szf162725 
509f595a68aSyz147064 static dladm_status_t
510*a399b765Szf162725 do_connect(const char *link, int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
511f595a68aSyz147064     boolean_t create_ibss, void *keys, uint_t key_count, int timeout)
512f595a68aSyz147064 {
513f595a68aSyz147064 	dladm_wlan_secmode_t		secmode;
514f595a68aSyz147064 	dladm_wlan_auth_t		authmode;
515f595a68aSyz147064 	dladm_wlan_bsstype_t		bsstype;
516f595a68aSyz147064 	dladm_wlan_essid_t		essid;
517f595a68aSyz147064 	boolean_t			essid_valid = B_FALSE;
518f595a68aSyz147064 	dladm_wlan_channel_t		channel;
519f595a68aSyz147064 	hrtime_t			start;
520*a399b765Szf162725 	wl_capability_t			*caps;
521f595a68aSyz147064 
522f595a68aSyz147064 	if ((attrp->wa_valid & DLADM_WLAN_ATTR_CHANNEL) != 0) {
523f595a68aSyz147064 		channel = attrp->wa_channel;
524f595a68aSyz147064 		if (do_set_channel(fd, gbuf, &channel) < 0)
525f595a68aSyz147064 			goto fail;
526f595a68aSyz147064 	}
527f595a68aSyz147064 
528f595a68aSyz147064 	secmode = ((attrp->wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) ?
529f595a68aSyz147064 	    attrp->wa_secmode : DLADM_WLAN_SECMODE_NONE;
530f595a68aSyz147064 
531f595a68aSyz147064 	if (do_set_encryption(fd, gbuf, &secmode) < 0)
532f595a68aSyz147064 		goto fail;
533f595a68aSyz147064 
534f595a68aSyz147064 	authmode = ((attrp->wa_valid & DLADM_WLAN_ATTR_AUTH) != 0) ?
535f595a68aSyz147064 	    attrp->wa_auth : DLADM_WLAN_AUTH_OPEN;
536f595a68aSyz147064 
537f595a68aSyz147064 	if (do_set_authmode(fd, gbuf, &authmode) < 0)
538f595a68aSyz147064 		goto fail;
539f595a68aSyz147064 
540f595a68aSyz147064 	bsstype = ((attrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0) ?
541f595a68aSyz147064 	    attrp->wa_bsstype : DLADM_WLAN_BSSTYPE_BSS;
542f595a68aSyz147064 
543f595a68aSyz147064 	if (do_set_bsstype(fd, gbuf, &bsstype) < 0)
544f595a68aSyz147064 		goto fail;
545f595a68aSyz147064 
546f595a68aSyz147064 	if (secmode == DLADM_WLAN_SECMODE_WEP) {
547f595a68aSyz147064 		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
548f595a68aSyz147064 			return (DLADM_STATUS_BADARG);
549*a399b765Szf162725 		if (do_set_key(fd, gbuf, keys, key_count) < 0)
550f595a68aSyz147064 			goto fail;
551*a399b765Szf162725 	} else if (secmode == DLADM_WLAN_SECMODE_WPA) {
552*a399b765Szf162725 		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
553*a399b765Szf162725 			return (DLADM_STATUS_BADARG);
554*a399b765Szf162725 		if (do_get_capability(fd, gbuf) < 0)
555*a399b765Szf162725 			goto fail;
556*a399b765Szf162725 		caps = (wl_capability_t *)(gbuf->wldp_buf);
557*a399b765Szf162725 		if ((caps->caps & IEEE80211_C_WPA) == 0)
558*a399b765Szf162725 			return (DLADM_STATUS_NOTSUP);
559f595a68aSyz147064 	}
560f595a68aSyz147064 
561f595a68aSyz147064 	if (create_ibss) {
562f595a68aSyz147064 		if (do_set_channel(fd, gbuf, &channel) < 0)
563f595a68aSyz147064 			goto fail;
564f595a68aSyz147064 
565f595a68aSyz147064 		if (do_set_createibss(fd, gbuf, &create_ibss) < 0)
566f595a68aSyz147064 			goto fail;
567f595a68aSyz147064 
568f595a68aSyz147064 		if ((attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0) {
569f595a68aSyz147064 			generate_essid(&essid);
570f595a68aSyz147064 			essid_valid = B_TRUE;
571f595a68aSyz147064 		}
572f595a68aSyz147064 	}
573f595a68aSyz147064 
574f595a68aSyz147064 	if ((attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) != 0) {
575f595a68aSyz147064 		essid = attrp->wa_essid;
576f595a68aSyz147064 		essid_valid = B_TRUE;
577f595a68aSyz147064 	}
578f595a68aSyz147064 
579f595a68aSyz147064 	if (!essid_valid)
580f595a68aSyz147064 		return (DLADM_STATUS_FAILED);
581f595a68aSyz147064 	if (do_set_essid(fd, gbuf, &essid) < 0)
582f595a68aSyz147064 		goto fail;
583f595a68aSyz147064 
584*a399b765Szf162725 	/*
585*a399b765Szf162725 	 * Because wpa daemon needs getting essid from driver,
586*a399b765Szf162725 	 * we need call do_set_essid() first, then call wpa_instance_create().
587*a399b765Szf162725 	 */
588*a399b765Szf162725 	if (secmode == DLADM_WLAN_SECMODE_WPA && keys != NULL)
589*a399b765Szf162725 		(void) wpa_instance_create(link, keys);
590*a399b765Szf162725 
591f595a68aSyz147064 	start = gethrtime();
592f595a68aSyz147064 	for (;;) {
593f595a68aSyz147064 		if (do_get_linkstatus(fd, gbuf) < 0)
594f595a68aSyz147064 			goto fail;
595f595a68aSyz147064 
596f595a68aSyz147064 		if (IS_CONNECTED(gbuf))
597f595a68aSyz147064 			break;
598f595a68aSyz147064 
599f595a68aSyz147064 		(void) poll(NULL, 0, DLADM_WLAN_CONNECT_POLLRATE);
600f595a68aSyz147064 		if ((timeout >= 0) && (gethrtime() - start) /
601f595a68aSyz147064 		    NANOSEC >= timeout)
602f595a68aSyz147064 			return (DLADM_STATUS_TIMEDOUT);
603f595a68aSyz147064 	}
604f595a68aSyz147064 	return (DLADM_STATUS_OK);
605f595a68aSyz147064 fail:
606f595a68aSyz147064 	return (dladm_wlan_wlresult2status(gbuf));
607f595a68aSyz147064 }
608f595a68aSyz147064 
609f595a68aSyz147064 dladm_status_t
610f595a68aSyz147064 dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
611f595a68aSyz147064     int timeout, void *keys, uint_t key_count, uint_t flags)
612f595a68aSyz147064 {
613f595a68aSyz147064 	int			fd, i;
614f595a68aSyz147064 	wldp_t 			*gbuf = NULL;
615f595a68aSyz147064 	connect_state_t		state = {0, NULL, NULL};
616f595a68aSyz147064 	attr_node_t		*nodep = NULL;
617f595a68aSyz147064 	boolean_t		create_ibss, set_authmode;
618f595a68aSyz147064 	dladm_wlan_attr_t	**wl_list = NULL;
619f595a68aSyz147064 	dladm_status_t		status = DLADM_STATUS_FAILED;
620f595a68aSyz147064 
621f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
622f595a68aSyz147064 		return (DLADM_STATUS_LINKINVAL);
623f595a68aSyz147064 
624f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
625f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
626f595a68aSyz147064 		goto done;
627f595a68aSyz147064 	}
628f595a68aSyz147064 
629f595a68aSyz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
630f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
631f595a68aSyz147064 		goto done;
632f595a68aSyz147064 	}
633f595a68aSyz147064 
634f595a68aSyz147064 	if (IS_CONNECTED(gbuf)) {
635f595a68aSyz147064 		status = DLADM_STATUS_ISCONN;
636f595a68aSyz147064 		goto done;
637f595a68aSyz147064 	}
638f595a68aSyz147064 
639f595a68aSyz147064 	set_authmode = ((attrp != NULL) &&
640f595a68aSyz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_MODE) != 0);
641f595a68aSyz147064 	create_ibss = ((flags & DLADM_WLAN_CONNECT_CREATEIBSS) != 0 &&
642f595a68aSyz147064 	    attrp != NULL &&
643f595a68aSyz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0 &&
644f595a68aSyz147064 	    attrp->wa_bsstype == DLADM_WLAN_BSSTYPE_IBSS);
645f595a68aSyz147064 
646f595a68aSyz147064 	if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0 ||
647f595a68aSyz147064 	    (create_ibss && attrp != NULL &&
648f595a68aSyz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)) {
649*a399b765Szf162725 		status = do_connect(link, fd, gbuf, attrp,
650f595a68aSyz147064 		    create_ibss, keys, key_count, timeout);
651f595a68aSyz147064 		goto done;
652f595a68aSyz147064 	}
653f595a68aSyz147064 
654f595a68aSyz147064 	state.cs_attr = attrp;
655f595a68aSyz147064 	state.cs_list = NULL;
656f595a68aSyz147064 	state.cs_count = 0;
657f595a68aSyz147064 
658f595a68aSyz147064 	status = dladm_wlan_scan(link, &state, connect_cb);
659f595a68aSyz147064 	if (status != DLADM_STATUS_OK)
660f595a68aSyz147064 		goto done;
661f595a68aSyz147064 
662f595a68aSyz147064 	if (state.cs_count == 0) {
663f595a68aSyz147064 		if (!create_ibss) {
664f595a68aSyz147064 			status = DLADM_STATUS_NOTFOUND;
665f595a68aSyz147064 			goto done;
666f595a68aSyz147064 		}
667*a399b765Szf162725 		status = do_connect(link, fd, gbuf, attrp, create_ibss,
668f595a68aSyz147064 		    keys, key_count, timeout);
669f595a68aSyz147064 		goto done;
670f595a68aSyz147064 	}
671f595a68aSyz147064 
672f595a68aSyz147064 	wl_list = malloc(state.cs_count * sizeof (dladm_wlan_attr_t *));
673f595a68aSyz147064 	if (wl_list == NULL) {
674f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
675f595a68aSyz147064 		goto done;
676f595a68aSyz147064 	}
677f595a68aSyz147064 
678f595a68aSyz147064 	nodep = state.cs_list;
679f595a68aSyz147064 	for (i = 0; i < state.cs_count; i++) {
680f595a68aSyz147064 		wl_list[i] = &nodep->an_attr;
681f595a68aSyz147064 		nodep = nodep->an_next;
682f595a68aSyz147064 	}
683f595a68aSyz147064 	qsort(wl_list, state.cs_count, sizeof (dladm_wlan_attr_t *),
684f595a68aSyz147064 	    attr_compare);
685f595a68aSyz147064 
686f595a68aSyz147064 	for (i = 0; i < state.cs_count; i++) {
687f595a68aSyz147064 		dladm_wlan_attr_t	*ap = wl_list[i];
688f595a68aSyz147064 
689*a399b765Szf162725 		status = do_connect(link, fd, gbuf, ap, create_ibss, keys,
690f595a68aSyz147064 		    key_count, timeout);
691f595a68aSyz147064 		if (status == DLADM_STATUS_OK)
692f595a68aSyz147064 			break;
693f595a68aSyz147064 
694f595a68aSyz147064 		if (!set_authmode) {
695f595a68aSyz147064 			ap->wa_auth = DLADM_WLAN_AUTH_SHARED;
696f595a68aSyz147064 			ap->wa_valid |= DLADM_WLAN_ATTR_AUTH;
697*a399b765Szf162725 			status = do_connect(link, fd, gbuf, ap, create_ibss,
698*a399b765Szf162725 			    keys, key_count, timeout);
699f595a68aSyz147064 			if (status == DLADM_STATUS_OK)
700f595a68aSyz147064 				break;
701f595a68aSyz147064 		}
702f595a68aSyz147064 	}
703f595a68aSyz147064 done:
704f595a68aSyz147064 	if ((status != DLADM_STATUS_OK) && (status != DLADM_STATUS_ISCONN))
705*a399b765Szf162725 		(void) do_disconnect(link, fd, gbuf);
706f595a68aSyz147064 
707f595a68aSyz147064 	while (state.cs_list != NULL) {
708f595a68aSyz147064 		nodep = state.cs_list;
709f595a68aSyz147064 		state.cs_list = nodep->an_next;
710f595a68aSyz147064 		free(nodep);
711f595a68aSyz147064 	}
712f595a68aSyz147064 	free(gbuf);
713f595a68aSyz147064 	free(wl_list);
714f595a68aSyz147064 	(void) close(fd);
715f595a68aSyz147064 	return (status);
716f595a68aSyz147064 }
717f595a68aSyz147064 
718f595a68aSyz147064 dladm_status_t
719f595a68aSyz147064 dladm_wlan_disconnect(const char *link)
720f595a68aSyz147064 {
721f595a68aSyz147064 	int		fd;
722f595a68aSyz147064 	wldp_t		*gbuf;
723f595a68aSyz147064 	dladm_status_t	status;
724f595a68aSyz147064 
725f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
726f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
727f595a68aSyz147064 
728f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
729f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
730f595a68aSyz147064 		goto done;
731f595a68aSyz147064 	}
732f595a68aSyz147064 
733f595a68aSyz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
734f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
735f595a68aSyz147064 		goto done;
736f595a68aSyz147064 	}
737f595a68aSyz147064 
738f595a68aSyz147064 	if (!IS_CONNECTED(gbuf)) {
739f595a68aSyz147064 		status = DLADM_STATUS_NOTCONN;
740f595a68aSyz147064 		goto done;
741f595a68aSyz147064 	}
742f595a68aSyz147064 
743*a399b765Szf162725 	if (do_disconnect(link, fd, gbuf) < 0) {
744f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
745f595a68aSyz147064 		goto done;
746f595a68aSyz147064 	}
747f595a68aSyz147064 
748f595a68aSyz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
749f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
750f595a68aSyz147064 		goto done;
751f595a68aSyz147064 	}
752f595a68aSyz147064 
753f595a68aSyz147064 	if (IS_CONNECTED(gbuf)) {
754f595a68aSyz147064 		status = DLADM_STATUS_FAILED;
755f595a68aSyz147064 		goto done;
756f595a68aSyz147064 	}
757f595a68aSyz147064 
758f595a68aSyz147064 	status = DLADM_STATUS_OK;
759f595a68aSyz147064 done:
760f595a68aSyz147064 	free(gbuf);
761f595a68aSyz147064 	(void) close(fd);
762f595a68aSyz147064 	return (status);
763f595a68aSyz147064 }
764f595a68aSyz147064 
765f595a68aSyz147064 typedef struct dladm_wlan_linkname {
766f595a68aSyz147064 	char			wl_name[MAXNAMELEN];
767f595a68aSyz147064 	struct dladm_wlan_linkname	*wl_next;
768f595a68aSyz147064 } dladm_wlan_linkname_t;
769f595a68aSyz147064 
770f595a68aSyz147064 typedef struct dladm_wlan_walk {
771f595a68aSyz147064 	dladm_wlan_linkname_t	*ww_list;
772f595a68aSyz147064 	dladm_status_t		ww_status;
773f595a68aSyz147064 } dladm_wlan_walk_t;
774f595a68aSyz147064 
775f595a68aSyz147064 /* ARGSUSED */
776f595a68aSyz147064 static int
777f595a68aSyz147064 append_linkname(di_node_t node, di_minor_t minor, void *arg)
778f595a68aSyz147064 {
779f595a68aSyz147064 	dladm_wlan_walk_t		*statep = arg;
780f595a68aSyz147064 	dladm_wlan_linkname_t	**lastp = &statep->ww_list;
781f595a68aSyz147064 	dladm_wlan_linkname_t	*wlp = *lastp;
782f595a68aSyz147064 	char			name[MAXNAMELEN];
783f595a68aSyz147064 
784f595a68aSyz147064 	(void) snprintf(name, MAXNAMELEN, "%s%d",
785f595a68aSyz147064 	    di_driver_name(node), di_instance(node));
786f595a68aSyz147064 
787f595a68aSyz147064 	while (wlp != NULL) {
788f595a68aSyz147064 		if (strcmp(wlp->wl_name, name) == 0)
789f595a68aSyz147064 			return (DI_WALK_CONTINUE);
790f595a68aSyz147064 
791f595a68aSyz147064 		lastp = &wlp->wl_next;
792f595a68aSyz147064 		wlp = wlp->wl_next;
793f595a68aSyz147064 	}
794f595a68aSyz147064 	if ((wlp = malloc(sizeof (*wlp))) == NULL) {
795f595a68aSyz147064 		statep->ww_status = DLADM_STATUS_NOMEM;
796f595a68aSyz147064 		return (DI_WALK_CONTINUE);
797f595a68aSyz147064 	}
798f595a68aSyz147064 
799f595a68aSyz147064 	(void) strlcpy(wlp->wl_name, name, MAXNAMELEN);
800f595a68aSyz147064 	wlp->wl_next = NULL;
801f595a68aSyz147064 	*lastp = wlp;
802f595a68aSyz147064 
803f595a68aSyz147064 	return (DI_WALK_CONTINUE);
804f595a68aSyz147064 }
805f595a68aSyz147064 
806f595a68aSyz147064 dladm_status_t
807f595a68aSyz147064 dladm_wlan_walk(void *arg, boolean_t (*func)(void *, const char *))
808f595a68aSyz147064 {
809f595a68aSyz147064 	di_node_t		root;
810f595a68aSyz147064 	dladm_wlan_walk_t		state;
811f595a68aSyz147064 	dladm_wlan_linkname_t	*wlp, *wlp_next;
812f595a68aSyz147064 	boolean_t		cont = B_TRUE;
813f595a68aSyz147064 
814f595a68aSyz147064 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
815f595a68aSyz147064 		return (DLADM_STATUS_FAILED);
816f595a68aSyz147064 
817f595a68aSyz147064 	state.ww_list = NULL;
818f595a68aSyz147064 	state.ww_status = DLADM_STATUS_OK;
819f595a68aSyz147064 	(void) di_walk_minor(root, DDI_NT_NET_WIFI, DI_CHECK_ALIAS,
820f595a68aSyz147064 	    &state, append_linkname);
821f595a68aSyz147064 	di_fini(root);
822f595a68aSyz147064 
823f595a68aSyz147064 	for (wlp = state.ww_list; wlp != NULL; wlp = wlp_next) {
824f595a68aSyz147064 		/*
825f595a68aSyz147064 		 * NOTE: even if (*func)() returns B_FALSE, the loop continues
826f595a68aSyz147064 		 * since all memory must be freed.
827f595a68aSyz147064 		 */
828f595a68aSyz147064 		if (cont)
829f595a68aSyz147064 			cont = (*func)(arg, wlp->wl_name);
830f595a68aSyz147064 		wlp_next = wlp->wl_next;
831f595a68aSyz147064 		free(wlp);
832f595a68aSyz147064 	}
833f595a68aSyz147064 	return (state.ww_status);
834f595a68aSyz147064 }
835f595a68aSyz147064 
836f595a68aSyz147064 dladm_status_t
837f595a68aSyz147064 dladm_wlan_get_linkattr(const char *link, dladm_wlan_linkattr_t *attrp)
838f595a68aSyz147064 {
839f595a68aSyz147064 	int			fd;
840f595a68aSyz147064 	wldp_t			*gbuf;
841f595a68aSyz147064 	wl_rssi_t		signal;
842f595a68aSyz147064 	wl_bss_type_t		bsstype;
843f595a68aSyz147064 	wl_authmode_t		authmode;
844f595a68aSyz147064 	wl_encryption_t		encryption;
845f595a68aSyz147064 	wl_rates_t		*ratesp;
846f595a68aSyz147064 	dladm_wlan_attr_t	*wl_attrp;
847f595a68aSyz147064 	dladm_status_t		status = DLADM_STATUS_FAILED;
848f595a68aSyz147064 
849f595a68aSyz147064 	if (attrp == NULL)
850f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
851f595a68aSyz147064 
852f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
853f595a68aSyz147064 		return (DLADM_STATUS_LINKINVAL);
854f595a68aSyz147064 
855f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
856f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
857f595a68aSyz147064 		goto done;
858f595a68aSyz147064 	}
859f595a68aSyz147064 
860f595a68aSyz147064 	(void) memset(attrp, 0, sizeof (*attrp));
861f595a68aSyz147064 	wl_attrp = &attrp->la_wlan_attr;
862f595a68aSyz147064 
863f595a68aSyz147064 	if (do_get_linkstatus(fd, gbuf) < 0)
864f595a68aSyz147064 		goto done;
865f595a68aSyz147064 
866f595a68aSyz147064 	attrp->la_valid |= DLADM_WLAN_LINKATTR_STATUS;
867f595a68aSyz147064 	if (!IS_CONNECTED(gbuf)) {
868f595a68aSyz147064 		attrp->la_status = DLADM_WLAN_LINKSTATUS_DISCONNECTED;
869*a399b765Szf162725 	} else {
870f595a68aSyz147064 		attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
871*a399b765Szf162725 	}
872f595a68aSyz147064 
873f595a68aSyz147064 	if (do_get_essid(fd, gbuf) < 0)
874f595a68aSyz147064 		goto done;
875f595a68aSyz147064 
876f595a68aSyz147064 	(void) strlcpy(wl_attrp->wa_essid.we_bytes,
877f595a68aSyz147064 	    ((wl_essid_t *)(gbuf->wldp_buf))->wl_essid_essid,
878f595a68aSyz147064 	    DLADM_WLAN_MAX_ESSID_LEN);
879f595a68aSyz147064 
880f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_ESSID;
881f595a68aSyz147064 
882f595a68aSyz147064 	if (do_get_bssid(fd, gbuf) < 0)
883f595a68aSyz147064 		goto done;
884f595a68aSyz147064 
885f595a68aSyz147064 	(void) memcpy(wl_attrp->wa_bssid.wb_bytes, gbuf->wldp_buf,
886f595a68aSyz147064 	    DLADM_WLAN_BSSID_LEN);
887f595a68aSyz147064 
888f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
889f595a68aSyz147064 
890*a399b765Szf162725 	if (attrp->la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED) {
891*a399b765Szf162725 		attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
892*a399b765Szf162725 		status = DLADM_STATUS_OK;
893*a399b765Szf162725 		goto done;
894*a399b765Szf162725 	}
895*a399b765Szf162725 
896f595a68aSyz147064 	if (do_get_encryption(fd, gbuf) < 0)
897f595a68aSyz147064 		goto done;
898f595a68aSyz147064 
899f595a68aSyz147064 	encryption = *(wl_encryption_t *)(gbuf->wldp_buf);
900f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
901f595a68aSyz147064 
902f595a68aSyz147064 	switch (encryption) {
903f595a68aSyz147064 	case WL_NOENCRYPTION:
904f595a68aSyz147064 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_NONE;
905f595a68aSyz147064 		break;
906f595a68aSyz147064 	case WL_ENC_WEP:
907f595a68aSyz147064 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WEP;
908f595a68aSyz147064 		break;
909*a399b765Szf162725 	case WL_ENC_WPA:
910*a399b765Szf162725 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
911*a399b765Szf162725 		break;
912f595a68aSyz147064 	default:
913f595a68aSyz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_SECMODE;
914f595a68aSyz147064 		break;
915f595a68aSyz147064 	}
916f595a68aSyz147064 
917f595a68aSyz147064 	if (do_get_signal(fd, gbuf) < 0)
918f595a68aSyz147064 		goto done;
919f595a68aSyz147064 
920f595a68aSyz147064 	signal = *(wl_rssi_t *)(gbuf->wldp_buf);
921f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_STRENGTH;
922f595a68aSyz147064 	wl_attrp->wa_strength = DLADM_WLAN_SIGNAL2STRENGTH(signal);
923f595a68aSyz147064 
924f595a68aSyz147064 	if (do_get_rate(fd, gbuf) < 0)
925f595a68aSyz147064 		goto done;
926f595a68aSyz147064 
927f595a68aSyz147064 	ratesp = (wl_rates_t *)(gbuf->wldp_buf);
928f595a68aSyz147064 	if (ratesp->wl_rates_num > 0) {
929f595a68aSyz147064 		uint_t	i, r = 0;
930f595a68aSyz147064 
931f595a68aSyz147064 		for (i = 0; i < ratesp->wl_rates_num; i++) {
932f595a68aSyz147064 			if (ratesp->wl_rates_rates[i] > r)
933f595a68aSyz147064 				r = ratesp->wl_rates_rates[i];
934f595a68aSyz147064 		}
935f595a68aSyz147064 		wl_attrp->wa_speed = r;
936f595a68aSyz147064 		wl_attrp->wa_valid |= DLADM_WLAN_ATTR_SPEED;
937f595a68aSyz147064 	}
938f595a68aSyz147064 
939f595a68aSyz147064 	if (do_get_authmode(fd, gbuf) < 0)
940f595a68aSyz147064 		goto done;
941f595a68aSyz147064 
942f595a68aSyz147064 	authmode = *(wl_authmode_t *)(gbuf->wldp_buf);
943f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
944f595a68aSyz147064 
945f595a68aSyz147064 	switch (authmode) {
946f595a68aSyz147064 	case WL_OPENSYSTEM:
947f595a68aSyz147064 		wl_attrp->wa_auth = DLADM_WLAN_AUTH_OPEN;
948f595a68aSyz147064 		break;
949f595a68aSyz147064 	case WL_SHAREDKEY:
950f595a68aSyz147064 		wl_attrp->wa_auth = DLADM_WLAN_AUTH_SHARED;
951f595a68aSyz147064 		break;
952f595a68aSyz147064 	default:
953f595a68aSyz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_AUTH;
954f595a68aSyz147064 		break;
955f595a68aSyz147064 	}
956f595a68aSyz147064 
957f595a68aSyz147064 	if (do_get_bsstype(fd, gbuf) < 0)
958f595a68aSyz147064 		goto done;
959f595a68aSyz147064 
960f595a68aSyz147064 	bsstype = *(wl_bss_type_t *)(gbuf->wldp_buf);
961f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
962f595a68aSyz147064 
963f595a68aSyz147064 	switch (bsstype) {
964f595a68aSyz147064 	case WL_BSS_BSS:
965f595a68aSyz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_BSS;
966f595a68aSyz147064 		break;
967f595a68aSyz147064 	case WL_BSS_IBSS:
968f595a68aSyz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_IBSS;
969f595a68aSyz147064 		break;
970f595a68aSyz147064 	case WL_BSS_ANY:
971f595a68aSyz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_ANY;
972f595a68aSyz147064 		break;
973f595a68aSyz147064 	default:
974f595a68aSyz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_BSSTYPE;
975f595a68aSyz147064 		break;
976f595a68aSyz147064 	}
977f595a68aSyz147064 
978f595a68aSyz147064 	if (do_get_mode(fd, gbuf) < 0)
979f595a68aSyz147064 		goto done;
980f595a68aSyz147064 
981f595a68aSyz147064 	wl_attrp->wa_mode = do_convert_mode((wl_phy_conf_t *)(gbuf->wldp_buf));
982f595a68aSyz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
983f595a68aSyz147064 	if (wl_attrp->wa_mode != DLADM_WLAN_MODE_NONE)
984f595a68aSyz147064 		wl_attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
985f595a68aSyz147064 
986f595a68aSyz147064 	attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
987f595a68aSyz147064 	status = DLADM_STATUS_OK;
988f595a68aSyz147064 
989f595a68aSyz147064 done:
990f595a68aSyz147064 	free(gbuf);
991f595a68aSyz147064 	(void) close(fd);
992f595a68aSyz147064 	return (status);
993f595a68aSyz147064 }
994f595a68aSyz147064 
995f595a68aSyz147064 boolean_t
996f595a68aSyz147064 dladm_wlan_is_valid(const char *link)
997f595a68aSyz147064 {
998f595a68aSyz147064 	int fd = open_link(link);
999f595a68aSyz147064 
1000f595a68aSyz147064 	if (fd < 0)
1001f595a68aSyz147064 		return (B_FALSE);
1002f595a68aSyz147064 
1003f595a68aSyz147064 	(void) close(fd);
1004f595a68aSyz147064 	return (B_TRUE);
1005f595a68aSyz147064 }
1006f595a68aSyz147064 
1007f595a68aSyz147064 /* ARGSUSED */
1008f595a68aSyz147064 static dladm_status_t
1009f595a68aSyz147064 do_check_prop(int fd, wldp_t *guf, prop_desc_t *pdp, char **prop_val,
1010f595a68aSyz147064     uint_t val_cnt, val_desc_t **vdpp)
1011f595a68aSyz147064 {
1012f595a68aSyz147064 	int		i;
1013f595a68aSyz147064 	val_desc_t	*vdp;
1014f595a68aSyz147064 
1015f595a68aSyz147064 	if (pdp->pd_nmodval == 0)
1016f595a68aSyz147064 		return (DLADM_STATUS_PROPRDONLY);
1017f595a68aSyz147064 
1018f595a68aSyz147064 	if (val_cnt != 1)
1019f595a68aSyz147064 		return (DLADM_STATUS_BADVALCNT);
1020f595a68aSyz147064 
1021f595a68aSyz147064 	for (i = 0; i < pdp->pd_nmodval; i++)
1022f595a68aSyz147064 		if (strcasecmp(*prop_val, pdp->pd_modval[i].vd_name) == 0)
1023f595a68aSyz147064 			break;
1024f595a68aSyz147064 
1025f595a68aSyz147064 	if (i == pdp->pd_nmodval)
1026f595a68aSyz147064 		return (DLADM_STATUS_BADVAL);
1027f595a68aSyz147064 
1028f595a68aSyz147064 	vdp = malloc(sizeof (val_desc_t));
1029f595a68aSyz147064 	if (vdp == NULL)
1030f595a68aSyz147064 		return (DLADM_STATUS_NOMEM);
1031f595a68aSyz147064 
1032f595a68aSyz147064 	(void) memcpy(vdp, &pdp->pd_modval[i], sizeof (val_desc_t));
1033f595a68aSyz147064 	*vdpp = vdp;
1034f595a68aSyz147064 	return (DLADM_STATUS_OK);
1035f595a68aSyz147064 }
1036f595a68aSyz147064 
1037f595a68aSyz147064 static dladm_status_t
1038f595a68aSyz147064 do_set_prop(int fd, wldp_t *gbuf, prop_desc_t *pdp,
1039f595a68aSyz147064     char **prop_val, uint_t val_cnt)
1040f595a68aSyz147064 {
1041f595a68aSyz147064 	dladm_status_t	status;
1042f595a68aSyz147064 	val_desc_t	*vdp = NULL;
1043f595a68aSyz147064 	uint_t		cnt;
1044f595a68aSyz147064 
1045f595a68aSyz147064 	if (pdp->pd_set == NULL)
1046f595a68aSyz147064 		return (DLADM_STATUS_PROPRDONLY);
1047f595a68aSyz147064 
1048f595a68aSyz147064 	if (prop_val != NULL) {
1049f595a68aSyz147064 		status = pdp->pd_check(fd, gbuf, pdp, prop_val,
1050f595a68aSyz147064 		    val_cnt, &vdp);
1051f595a68aSyz147064 
1052f595a68aSyz147064 		if (status != DLADM_STATUS_OK)
1053f595a68aSyz147064 			return (status);
1054f595a68aSyz147064 
1055f595a68aSyz147064 		cnt = val_cnt;
1056f595a68aSyz147064 	} else {
1057f595a68aSyz147064 		if (pdp->pd_defval.vd_name == NULL)
1058f595a68aSyz147064 			return (DLADM_STATUS_NOTSUP);
1059f595a68aSyz147064 
1060f595a68aSyz147064 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
1061f595a68aSyz147064 			return (DLADM_STATUS_NOMEM);
1062f595a68aSyz147064 
1063f595a68aSyz147064 		*vdp = pdp->pd_defval;
1064f595a68aSyz147064 		cnt = 1;
1065f595a68aSyz147064 	}
1066f595a68aSyz147064 	status = pdp->pd_set(fd, gbuf, vdp, cnt);
1067f595a68aSyz147064 	if (status == DLADM_STATUS_OK) {
1068f595a68aSyz147064 		/*
1069f595a68aSyz147064 		 * Some ioctls return 0 but store error code in
1070f595a68aSyz147064 		 * wldp_result. Need to fix them.
1071f595a68aSyz147064 		 */
1072f595a68aSyz147064 		if (gbuf->wldp_result != WL_SUCCESS)
1073f595a68aSyz147064 			status = dladm_wlan_wlresult2status(gbuf);
1074f595a68aSyz147064 	}
1075f595a68aSyz147064 	free(vdp);
1076f595a68aSyz147064 	return (status);
1077f595a68aSyz147064 }
1078f595a68aSyz147064 
1079f595a68aSyz147064 dladm_status_t
1080f595a68aSyz147064 dladm_wlan_set_prop(const char *link, const char *prop_name,
1081f595a68aSyz147064     char **prop_val, uint_t val_cnt, char **errprop)
1082f595a68aSyz147064 {
1083f595a68aSyz147064 	int		fd, i;
1084f595a68aSyz147064 	wldp_t		*gbuf = NULL;
1085f595a68aSyz147064 	boolean_t	found = B_FALSE;
1086f595a68aSyz147064 	dladm_status_t	status = DLADM_STATUS_OK;
1087f595a68aSyz147064 
1088f595a68aSyz147064 	if ((prop_name == NULL && prop_val != NULL) ||
1089f595a68aSyz147064 	    (prop_val != NULL && val_cnt == 0))
1090f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1091f595a68aSyz147064 
1092f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
1093f595a68aSyz147064 		return (DLADM_STATUS_LINKINVAL);
1094f595a68aSyz147064 
1095f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
1096f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
1097f595a68aSyz147064 		goto done;
1098f595a68aSyz147064 	}
1099f595a68aSyz147064 
1100f595a68aSyz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++) {
1101f595a68aSyz147064 		prop_desc_t	*pdp = &prop_table[i];
1102f595a68aSyz147064 		dladm_status_t	s;
1103f595a68aSyz147064 
1104f595a68aSyz147064 		if (prop_name != NULL &&
1105f595a68aSyz147064 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
1106f595a68aSyz147064 			continue;
1107f595a68aSyz147064 
1108f595a68aSyz147064 		found = B_TRUE;
1109f595a68aSyz147064 		s = do_set_prop(fd, gbuf, pdp, prop_val, val_cnt);
1110f595a68aSyz147064 
1111f595a68aSyz147064 		if (prop_name != NULL) {
1112f595a68aSyz147064 			status = s;
1113f595a68aSyz147064 			break;
1114f595a68aSyz147064 		} else {
1115f595a68aSyz147064 			if (s != DLADM_STATUS_OK &&
1116f595a68aSyz147064 			    s != DLADM_STATUS_NOTSUP) {
1117f595a68aSyz147064 				if (errprop != NULL)
1118f595a68aSyz147064 					*errprop = pdp->pd_name;
1119f595a68aSyz147064 				status = s;
1120f595a68aSyz147064 				break;
1121f595a68aSyz147064 			}
1122f595a68aSyz147064 		}
1123f595a68aSyz147064 	}
1124f595a68aSyz147064 	if (!found)
1125f595a68aSyz147064 		status = DLADM_STATUS_NOTFOUND;
1126f595a68aSyz147064 done:
1127f595a68aSyz147064 	free(gbuf);
1128f595a68aSyz147064 	(void) close(fd);
1129f595a68aSyz147064 	return (status);
1130f595a68aSyz147064 }
1131f595a68aSyz147064 
1132f595a68aSyz147064 /* ARGSUSED */
1133f595a68aSyz147064 dladm_status_t
1134f595a68aSyz147064 dladm_wlan_walk_prop(const char *link, void *arg,
1135f595a68aSyz147064     boolean_t (*func)(void *, const char *))
1136f595a68aSyz147064 {
1137f595a68aSyz147064 	int	i;
1138f595a68aSyz147064 
1139f595a68aSyz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++) {
1140f595a68aSyz147064 		if (!func(arg, prop_table[i].pd_name))
1141f595a68aSyz147064 			break;
1142f595a68aSyz147064 	}
1143f595a68aSyz147064 	return (DLADM_STATUS_OK);
1144f595a68aSyz147064 }
1145f595a68aSyz147064 
1146f595a68aSyz147064 dladm_status_t
1147f595a68aSyz147064 dladm_wlan_get_prop(const char *link, dladm_prop_type_t type,
1148f595a68aSyz147064     const char *prop_name, char **prop_val, uint_t *val_cnt)
1149f595a68aSyz147064 {
1150f595a68aSyz147064 	int		fd;
1151f595a68aSyz147064 	int		i;
1152f595a68aSyz147064 	wldp_t		*gbuf;
1153f595a68aSyz147064 	dladm_status_t	status;
1154f595a68aSyz147064 	uint_t		cnt;
1155f595a68aSyz147064 	prop_desc_t	*pdp;
1156f595a68aSyz147064 
1157f595a68aSyz147064 	if (prop_val == NULL || val_cnt == NULL || *val_cnt == 0)
1158f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1159f595a68aSyz147064 
1160f595a68aSyz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++)
1161f595a68aSyz147064 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
1162f595a68aSyz147064 			break;
1163f595a68aSyz147064 
1164f595a68aSyz147064 	if (i == DLADM_WLAN_MAX_PROPS)
1165f595a68aSyz147064 		return (DLADM_STATUS_NOTFOUND);
1166f595a68aSyz147064 
1167f595a68aSyz147064 	if ((fd = open_link(link)) < 0)
1168f595a68aSyz147064 		return (DLADM_STATUS_LINKINVAL);
1169f595a68aSyz147064 
1170f595a68aSyz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
1171f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
1172f595a68aSyz147064 		goto done;
1173f595a68aSyz147064 	}
1174f595a68aSyz147064 	pdp = &prop_table[i];
1175f595a68aSyz147064 	status = DLADM_STATUS_OK;
1176f595a68aSyz147064 
1177f595a68aSyz147064 	switch (type) {
1178f595a68aSyz147064 	case DLADM_PROP_VAL_CURRENT:
1179f595a68aSyz147064 		status = pdp->pd_get(fd, gbuf, prop_val, val_cnt);
1180f595a68aSyz147064 		break;
1181f595a68aSyz147064 
1182f595a68aSyz147064 	case DLADM_PROP_VAL_DEFAULT:
1183f595a68aSyz147064 		if (pdp->pd_defval.vd_name == NULL) {
1184f595a68aSyz147064 			status = DLADM_STATUS_NOTSUP;
1185f595a68aSyz147064 			break;
1186f595a68aSyz147064 		}
1187f595a68aSyz147064 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1188f595a68aSyz147064 		*val_cnt = 1;
1189f595a68aSyz147064 		break;
1190f595a68aSyz147064 
1191f595a68aSyz147064 	case DLADM_PROP_VAL_MODIFIABLE:
1192f595a68aSyz147064 		if (pdp->pd_getmod != NULL) {
1193f595a68aSyz147064 			status = pdp->pd_getmod(fd, gbuf, prop_val, val_cnt);
1194f595a68aSyz147064 			break;
1195f595a68aSyz147064 		}
1196f595a68aSyz147064 		cnt = pdp->pd_nmodval;
1197f595a68aSyz147064 		if (cnt == 0) {
1198f595a68aSyz147064 			status = DLADM_STATUS_NOTSUP;
1199f595a68aSyz147064 		} else if (cnt > *val_cnt) {
1200f595a68aSyz147064 			status = DLADM_STATUS_TOOSMALL;
1201f595a68aSyz147064 		} else {
1202f595a68aSyz147064 			for (i = 0; i < cnt; i++) {
1203f595a68aSyz147064 				(void) strcpy(prop_val[i],
1204f595a68aSyz147064 				    pdp->pd_modval[i].vd_name);
1205f595a68aSyz147064 			}
1206f595a68aSyz147064 			*val_cnt = cnt;
1207f595a68aSyz147064 		}
1208f595a68aSyz147064 		break;
1209f595a68aSyz147064 	default:
1210f595a68aSyz147064 		status = DLADM_STATUS_BADARG;
1211f595a68aSyz147064 		break;
1212f595a68aSyz147064 	}
1213f595a68aSyz147064 done:
1214f595a68aSyz147064 	free(gbuf);
1215f595a68aSyz147064 	(void) close(fd);
1216f595a68aSyz147064 	return (status);
1217f595a68aSyz147064 }
1218f595a68aSyz147064 
1219f595a68aSyz147064 static boolean_t
1220f595a68aSyz147064 find_val_by_name(const char *str, val_desc_t *vdp, uint_t cnt, uint_t *valp)
1221f595a68aSyz147064 {
1222f595a68aSyz147064 	int	i;
1223f595a68aSyz147064 
1224f595a68aSyz147064 	for (i = 0; i < cnt; i++) {
1225f595a68aSyz147064 		if (strcasecmp(str, vdp[i].vd_name) == 0) {
1226f595a68aSyz147064 			*valp = vdp[i].vd_val;
1227f595a68aSyz147064 			return (B_TRUE);
1228f595a68aSyz147064 		}
1229f595a68aSyz147064 	}
1230f595a68aSyz147064 	return (B_FALSE);
1231f595a68aSyz147064 }
1232f595a68aSyz147064 
1233f595a68aSyz147064 static boolean_t
1234f595a68aSyz147064 find_name_by_val(uint_t val, val_desc_t *vdp, uint_t cnt, char **strp)
1235f595a68aSyz147064 {
1236f595a68aSyz147064 	int	i;
1237f595a68aSyz147064 
1238f595a68aSyz147064 	for (i = 0; i < cnt; i++) {
1239f595a68aSyz147064 		if (val == vdp[i].vd_val) {
1240f595a68aSyz147064 			*strp = vdp[i].vd_name;
1241f595a68aSyz147064 			return (B_TRUE);
1242f595a68aSyz147064 		}
1243f595a68aSyz147064 	}
1244f595a68aSyz147064 	return (B_FALSE);
1245f595a68aSyz147064 }
1246f595a68aSyz147064 
1247f595a68aSyz147064 const char *
1248f595a68aSyz147064 dladm_wlan_essid2str(dladm_wlan_essid_t *essid, char *buf)
1249f595a68aSyz147064 {
1250f595a68aSyz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", essid->we_bytes);
1251f595a68aSyz147064 	return (buf);
1252f595a68aSyz147064 }
1253f595a68aSyz147064 
1254f595a68aSyz147064 const char *
1255f595a68aSyz147064 dladm_wlan_bssid2str(dladm_wlan_bssid_t *bssid, char *buf)
1256f595a68aSyz147064 {
1257f595a68aSyz147064 	return (_link_ntoa(bssid->wb_bytes, buf, DLADM_WLAN_BSSID_LEN,
1258f595a68aSyz147064 	    IFT_OTHER));
1259f595a68aSyz147064 }
1260f595a68aSyz147064 
1261f595a68aSyz147064 static const char *
1262f595a68aSyz147064 dladm_wlan_val2str(uint_t val, val_desc_t *vdp, uint_t cnt, char *buf)
1263f595a68aSyz147064 {
1264f595a68aSyz147064 	char	*s;
1265f595a68aSyz147064 
1266f595a68aSyz147064 	if (!find_name_by_val(val, vdp, cnt, &s))
1267f595a68aSyz147064 		s = "";
1268f595a68aSyz147064 
1269f595a68aSyz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
1270f595a68aSyz147064 	return (buf);
1271f595a68aSyz147064 }
1272f595a68aSyz147064 
1273f595a68aSyz147064 const char *
1274f595a68aSyz147064 dladm_wlan_secmode2str(dladm_wlan_secmode_t *secmode, char *buf)
1275f595a68aSyz147064 {
1276f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*secmode, secmode_vals,
1277f595a68aSyz147064 	    VALCNT(secmode_vals), buf));
1278f595a68aSyz147064 }
1279f595a68aSyz147064 
1280f595a68aSyz147064 const char *
1281f595a68aSyz147064 dladm_wlan_strength2str(dladm_wlan_strength_t *strength, char *buf)
1282f595a68aSyz147064 {
1283f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*strength, strength_vals,
1284f595a68aSyz147064 	    VALCNT(strength_vals), buf));
1285f595a68aSyz147064 }
1286f595a68aSyz147064 
1287f595a68aSyz147064 const char *
1288f595a68aSyz147064 dladm_wlan_mode2str(dladm_wlan_mode_t *mode, char *buf)
1289f595a68aSyz147064 {
1290f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*mode, mode_vals,
1291f595a68aSyz147064 	    VALCNT(mode_vals), buf));
1292f595a68aSyz147064 }
1293f595a68aSyz147064 
1294f595a68aSyz147064 const char *
1295f595a68aSyz147064 dladm_wlan_speed2str(dladm_wlan_speed_t *speed, char *buf)
1296f595a68aSyz147064 {
1297f595a68aSyz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%.*f", *speed % 2,
1298f595a68aSyz147064 	    (float)(*speed) / 2);
1299f595a68aSyz147064 	return (buf);
1300f595a68aSyz147064 }
1301f595a68aSyz147064 
1302f595a68aSyz147064 const char *
1303f595a68aSyz147064 dladm_wlan_auth2str(dladm_wlan_auth_t *auth, char *buf)
1304f595a68aSyz147064 {
1305f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*auth, auth_vals,
1306f595a68aSyz147064 	    VALCNT(auth_vals), buf));
1307f595a68aSyz147064 }
1308f595a68aSyz147064 
1309f595a68aSyz147064 const char *
1310f595a68aSyz147064 dladm_wlan_bsstype2str(dladm_wlan_bsstype_t *bsstype, char *buf)
1311f595a68aSyz147064 {
1312f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*bsstype, bsstype_vals,
1313f595a68aSyz147064 	    VALCNT(bsstype_vals), buf));
1314f595a68aSyz147064 }
1315f595a68aSyz147064 
1316f595a68aSyz147064 const char *
1317f595a68aSyz147064 dladm_wlan_linkstatus2str(dladm_wlan_linkstatus_t *linkstatus, char *buf)
1318f595a68aSyz147064 {
1319f595a68aSyz147064 	return (dladm_wlan_val2str((uint_t)*linkstatus, linkstatus_vals,
1320f595a68aSyz147064 	    VALCNT(linkstatus_vals), buf));
1321f595a68aSyz147064 }
1322f595a68aSyz147064 
1323f595a68aSyz147064 dladm_status_t
1324f595a68aSyz147064 dladm_wlan_str2essid(const char *str, dladm_wlan_essid_t *essid)
1325f595a68aSyz147064 {
1326f595a68aSyz147064 	if (str[0] == '\0')
1327f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1328f595a68aSyz147064 
1329f595a68aSyz147064 	(void) strlcpy(essid->we_bytes, str, DLADM_WLAN_MAX_ESSID_LEN);
1330f595a68aSyz147064 	return (DLADM_STATUS_OK);
1331f595a68aSyz147064 }
1332f595a68aSyz147064 
1333f595a68aSyz147064 dladm_status_t
1334f595a68aSyz147064 dladm_wlan_str2bssid(const char *str, dladm_wlan_bssid_t *bssid)
1335f595a68aSyz147064 {
1336f595a68aSyz147064 	int	len;
1337f595a68aSyz147064 	uchar_t	*buf;
1338f595a68aSyz147064 
1339f595a68aSyz147064 	buf = _link_aton(str, &len);
1340f595a68aSyz147064 	if (buf == NULL)
1341f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1342f595a68aSyz147064 
1343f595a68aSyz147064 	if (len != DLADM_WLAN_BSSID_LEN) {
1344f595a68aSyz147064 		free(buf);
1345f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1346f595a68aSyz147064 	}
1347f595a68aSyz147064 
1348f595a68aSyz147064 	(void) memcpy(bssid->wb_bytes, buf, len);
1349f595a68aSyz147064 	free(buf);
1350f595a68aSyz147064 	return (DLADM_STATUS_OK);
1351f595a68aSyz147064 }
1352f595a68aSyz147064 
1353f595a68aSyz147064 dladm_status_t
1354f595a68aSyz147064 dladm_wlan_str2secmode(const char *str, dladm_wlan_secmode_t *secmode)
1355f595a68aSyz147064 {
1356f595a68aSyz147064 	uint_t	val;
1357f595a68aSyz147064 
1358f595a68aSyz147064 	if (!find_val_by_name(str, secmode_vals, VALCNT(secmode_vals), &val))
1359f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1360f595a68aSyz147064 
1361f595a68aSyz147064 	*secmode = (dladm_wlan_secmode_t)val;
1362f595a68aSyz147064 	return (DLADM_STATUS_OK);
1363f595a68aSyz147064 }
1364f595a68aSyz147064 
1365f595a68aSyz147064 dladm_status_t
1366f595a68aSyz147064 dladm_wlan_str2strength(const char *str, dladm_wlan_strength_t *strength)
1367f595a68aSyz147064 {
1368f595a68aSyz147064 	uint_t	val;
1369f595a68aSyz147064 
1370f595a68aSyz147064 	if (!find_val_by_name(str, strength_vals, VALCNT(strength_vals), &val))
1371f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1372f595a68aSyz147064 
1373f595a68aSyz147064 	*strength = (dladm_wlan_strength_t)val;
1374f595a68aSyz147064 	return (DLADM_STATUS_OK);
1375f595a68aSyz147064 }
1376f595a68aSyz147064 
1377f595a68aSyz147064 dladm_status_t
1378f595a68aSyz147064 dladm_wlan_str2mode(const char *str, dladm_wlan_mode_t *mode)
1379f595a68aSyz147064 {
1380f595a68aSyz147064 	uint_t	val;
1381f595a68aSyz147064 
1382f595a68aSyz147064 	if (!find_val_by_name(str, mode_vals, VALCNT(mode_vals), &val))
1383f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1384f595a68aSyz147064 
1385f595a68aSyz147064 	*mode = (dladm_wlan_mode_t)val;
1386f595a68aSyz147064 	return (DLADM_STATUS_OK);
1387f595a68aSyz147064 }
1388f595a68aSyz147064 
1389f595a68aSyz147064 dladm_status_t
1390f595a68aSyz147064 dladm_wlan_str2speed(const char *str, dladm_wlan_speed_t *speed)
1391f595a68aSyz147064 {
1392f595a68aSyz147064 	*speed = (dladm_wlan_speed_t)(atof(str) * 2);
1393f595a68aSyz147064 	return (DLADM_STATUS_OK);
1394f595a68aSyz147064 }
1395f595a68aSyz147064 
1396f595a68aSyz147064 dladm_status_t
1397f595a68aSyz147064 dladm_wlan_str2auth(const char *str, dladm_wlan_auth_t *auth)
1398f595a68aSyz147064 {
1399f595a68aSyz147064 	uint_t	val;
1400f595a68aSyz147064 
1401f595a68aSyz147064 	if (!find_val_by_name(str, auth_vals, VALCNT(auth_vals), &val))
1402f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1403f595a68aSyz147064 
1404f595a68aSyz147064 	*auth = (dladm_wlan_auth_t)val;
1405f595a68aSyz147064 	return (DLADM_STATUS_OK);
1406f595a68aSyz147064 }
1407f595a68aSyz147064 
1408f595a68aSyz147064 dladm_status_t
1409f595a68aSyz147064 dladm_wlan_str2bsstype(const char *str, dladm_wlan_bsstype_t *bsstype)
1410f595a68aSyz147064 {
1411f595a68aSyz147064 	uint_t	val;
1412f595a68aSyz147064 
1413f595a68aSyz147064 	if (!find_val_by_name(str, bsstype_vals, VALCNT(bsstype_vals), &val))
1414f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1415f595a68aSyz147064 
1416f595a68aSyz147064 	*bsstype = (dladm_wlan_bsstype_t)val;
1417f595a68aSyz147064 	return (DLADM_STATUS_OK);
1418f595a68aSyz147064 }
1419f595a68aSyz147064 
1420f595a68aSyz147064 dladm_status_t
1421f595a68aSyz147064 dladm_wlan_str2linkstatus(const char *str, dladm_wlan_linkstatus_t *linkstatus)
1422f595a68aSyz147064 {
1423f595a68aSyz147064 	uint_t	val;
1424f595a68aSyz147064 
1425f595a68aSyz147064 	if (!find_val_by_name(str, linkstatus_vals, VALCNT(linkstatus_vals),
1426f595a68aSyz147064 	    &val))
1427f595a68aSyz147064 		return (DLADM_STATUS_BADARG);
1428f595a68aSyz147064 
1429f595a68aSyz147064 	*linkstatus = (dladm_wlan_linkstatus_t)val;
1430f595a68aSyz147064 	return (DLADM_STATUS_OK);
1431f595a68aSyz147064 }
1432f595a68aSyz147064 
1433f595a68aSyz147064 static int
1434f595a68aSyz147064 do_ioctl(int fd, wldp_t *gbuf, uint_t id, size_t len, uint_t cmd, size_t cmdlen)
1435f595a68aSyz147064 {
1436f595a68aSyz147064 	int			rc;
1437f595a68aSyz147064 	struct	strioctl	stri;
1438f595a68aSyz147064 
1439f595a68aSyz147064 	gbuf->wldp_type = NET_802_11;
1440f595a68aSyz147064 	gbuf->wldp_id	= id;
1441f595a68aSyz147064 	gbuf->wldp_length = len;
1442f595a68aSyz147064 
1443f595a68aSyz147064 	stri.ic_timout	= 0;
1444f595a68aSyz147064 	stri.ic_dp	= (char *)gbuf;
1445f595a68aSyz147064 	stri.ic_cmd	= cmd;
1446f595a68aSyz147064 	stri.ic_len	= cmdlen;
1447f595a68aSyz147064 
1448f595a68aSyz147064 	if ((rc = ioctl(fd, I_STR, &stri)) != 0) {
1449f595a68aSyz147064 		if (rc > 0)
1450f595a68aSyz147064 			errno = rc;
1451f595a68aSyz147064 		return (-1);
1452f595a68aSyz147064 	}
1453f595a68aSyz147064 	return (0);
1454f595a68aSyz147064 }
1455f595a68aSyz147064 
1456f595a68aSyz147064 static int
1457f595a68aSyz147064 do_get_ioctl(int fd, wldp_t *gbuf, uint_t id)
1458f595a68aSyz147064 {
1459f595a68aSyz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1460f595a68aSyz147064 	return (do_ioctl(fd, gbuf, id, MAX_BUF_LEN, WLAN_GET_PARAM,
1461f595a68aSyz147064 	    MAX_BUF_LEN));
1462f595a68aSyz147064 }
1463f595a68aSyz147064 
1464f595a68aSyz147064 static int
1465f595a68aSyz147064 do_set_ioctl(int fd, wldp_t *gbuf, uint_t id, void *buf, uint_t buflen)
1466f595a68aSyz147064 {
1467f595a68aSyz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1468f595a68aSyz147064 	(void) memcpy(gbuf->wldp_buf, buf, buflen);
1469f595a68aSyz147064 	buflen += WIFI_BUF_OFFSET;
1470f595a68aSyz147064 	return (do_ioctl(fd, gbuf, id, buflen, WLAN_SET_PARAM, buflen));
1471f595a68aSyz147064 }
1472f595a68aSyz147064 
1473f595a68aSyz147064 static int
1474f595a68aSyz147064 do_cmd_ioctl(int fd, wldp_t *gbuf, uint_t cmd)
1475f595a68aSyz147064 {
1476f595a68aSyz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1477f595a68aSyz147064 	return (do_ioctl(fd, gbuf, cmd, sizeof (wldp_t), WLAN_COMMAND,
1478f595a68aSyz147064 	    sizeof (wldp_t)));
1479f595a68aSyz147064 }
1480f595a68aSyz147064 
1481f595a68aSyz147064 static int
1482f595a68aSyz147064 do_scan(int fd, wldp_t *gbuf)
1483f595a68aSyz147064 {
1484f595a68aSyz147064 	return (do_cmd_ioctl(fd, gbuf, WL_SCAN));
1485f595a68aSyz147064 }
1486f595a68aSyz147064 
1487f595a68aSyz147064 static int
1488*a399b765Szf162725 do_disconnect(const char *link, int fd, wldp_t *gbuf)
1489f595a68aSyz147064 {
1490*a399b765Szf162725 	if (do_get_wpamode(fd, gbuf) == 0 && ((wl_wpa_t *)(gbuf->
1491*a399b765Szf162725 	    wldp_buf))->wpa_flag > 0)
1492*a399b765Szf162725 		(void) wpa_instance_delete(link);
1493*a399b765Szf162725 
1494f595a68aSyz147064 	return (do_cmd_ioctl(fd, gbuf, WL_DISASSOCIATE));
1495f595a68aSyz147064 }
1496f595a68aSyz147064 
1497f595a68aSyz147064 static int
1498f595a68aSyz147064 do_get_esslist(int fd, wldp_t *gbuf)
1499f595a68aSyz147064 {
1500f595a68aSyz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1501f595a68aSyz147064 	return (do_ioctl(fd, gbuf, WL_ESS_LIST, MAX_BUF_LEN,
1502f595a68aSyz147064 	    WLAN_GET_PARAM, sizeof (wldp_t)));
1503f595a68aSyz147064 }
1504f595a68aSyz147064 
1505f595a68aSyz147064 static int
1506f595a68aSyz147064 do_get_bssid(int fd, wldp_t *gbuf)
1507f595a68aSyz147064 {
1508f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_BSSID));
1509f595a68aSyz147064 }
1510f595a68aSyz147064 
1511f595a68aSyz147064 static int
1512f595a68aSyz147064 do_get_essid(int fd, wldp_t *gbuf)
1513f595a68aSyz147064 {
1514f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_ESSID));
1515f595a68aSyz147064 }
1516f595a68aSyz147064 
1517f595a68aSyz147064 static int
1518f595a68aSyz147064 do_get_bsstype(int fd, wldp_t *gbuf)
1519f595a68aSyz147064 {
1520f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_BSS_TYPE));
1521f595a68aSyz147064 }
1522f595a68aSyz147064 
1523f595a68aSyz147064 static int
1524f595a68aSyz147064 do_get_linkstatus(int fd, wldp_t *gbuf)
1525f595a68aSyz147064 {
1526f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_LINKSTATUS));
1527f595a68aSyz147064 }
1528f595a68aSyz147064 
1529f595a68aSyz147064 static int
1530f595a68aSyz147064 do_get_rate(int fd, wldp_t *gbuf)
1531f595a68aSyz147064 {
1532f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_DESIRED_RATES));
1533f595a68aSyz147064 }
1534f595a68aSyz147064 
1535f595a68aSyz147064 static int
1536f595a68aSyz147064 do_get_phyconf(int fd, wldp_t *gbuf)
1537f595a68aSyz147064 {
1538f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_PHY_CONFIG));
1539f595a68aSyz147064 }
1540f595a68aSyz147064 
1541f595a68aSyz147064 static int
1542f595a68aSyz147064 do_get_powermode(int fd, wldp_t *gbuf)
1543f595a68aSyz147064 {
1544f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_POWER_MODE));
1545f595a68aSyz147064 }
1546f595a68aSyz147064 
1547f595a68aSyz147064 static int
1548f595a68aSyz147064 do_get_radio(int fd, wldp_t *gbuf)
1549f595a68aSyz147064 {
1550f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_RADIO));
1551f595a68aSyz147064 }
1552f595a68aSyz147064 
1553f595a68aSyz147064 static int
1554f595a68aSyz147064 do_get_authmode(int fd, wldp_t *gbuf)
1555f595a68aSyz147064 {
1556f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_AUTH_MODE));
1557f595a68aSyz147064 }
1558f595a68aSyz147064 
1559f595a68aSyz147064 static int
1560f595a68aSyz147064 do_get_encryption(int fd, wldp_t *gbuf)
1561f595a68aSyz147064 {
1562f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_ENCRYPTION));
1563f595a68aSyz147064 }
1564f595a68aSyz147064 
1565f595a68aSyz147064 static int
1566f595a68aSyz147064 do_get_signal(int fd, wldp_t *gbuf)
1567f595a68aSyz147064 {
1568f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_RSSI));
1569f595a68aSyz147064 }
1570f595a68aSyz147064 
1571f595a68aSyz147064 static int
1572f595a68aSyz147064 do_get_mode(int fd, wldp_t *gbuf)
1573f595a68aSyz147064 {
1574f595a68aSyz147064 	return (do_get_ioctl(fd, gbuf, WL_PHY_CONFIG));
1575f595a68aSyz147064 }
1576f595a68aSyz147064 
1577f595a68aSyz147064 static dladm_status_t
1578f595a68aSyz147064 do_get_rate_common(wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1579f595a68aSyz147064 {
1580f595a68aSyz147064 	wl_rates_t	*wrp = (wl_rates_t *)gbuf->wldp_buf;
1581f595a68aSyz147064 	uint_t		cnt = wrp->wl_rates_num;
1582f595a68aSyz147064 	uint_t		i;
1583f595a68aSyz147064 
1584f595a68aSyz147064 	if (cnt > *val_cnt)
1585f595a68aSyz147064 		return (DLADM_STATUS_TOOSMALL);
1586f595a68aSyz147064 	if (wrp->wl_rates_rates[0] == 0) {
1587f595a68aSyz147064 		prop_val[0][0] = '\0';
1588f595a68aSyz147064 		*val_cnt = 1;
1589f595a68aSyz147064 		return (DLADM_STATUS_OK);
1590f595a68aSyz147064 	}
1591f595a68aSyz147064 
1592f595a68aSyz147064 	for (i = 0; i < cnt; i++) {
1593f595a68aSyz147064 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
1594f595a68aSyz147064 		    wrp->wl_rates_rates[i] % 2,
1595f595a68aSyz147064 		    (float)wrp->wl_rates_rates[i] / 2);
1596f595a68aSyz147064 	}
1597f595a68aSyz147064 	*val_cnt = cnt;
1598f595a68aSyz147064 	return (DLADM_STATUS_OK);
1599f595a68aSyz147064 }
1600f595a68aSyz147064 
1601f595a68aSyz147064 static dladm_status_t
1602f595a68aSyz147064 do_get_rate_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1603f595a68aSyz147064 {
1604f595a68aSyz147064 	if (do_get_rate(fd, gbuf) < 0)
1605f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1606f595a68aSyz147064 
1607f595a68aSyz147064 	return (do_get_rate_common(gbuf, prop_val, val_cnt));
1608f595a68aSyz147064 }
1609f595a68aSyz147064 
1610f595a68aSyz147064 static dladm_status_t
1611f595a68aSyz147064 do_get_rate_mod(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1612f595a68aSyz147064 {
1613f595a68aSyz147064 	if (do_get_ioctl(fd, gbuf, WL_SUPPORTED_RATES) < 0)
1614f595a68aSyz147064 		return (DLADM_STATUS_FAILED);
1615f595a68aSyz147064 
1616f595a68aSyz147064 	return (do_get_rate_common(gbuf, prop_val, val_cnt));
1617f595a68aSyz147064 }
1618f595a68aSyz147064 
1619f595a68aSyz147064 static dladm_status_t
1620f595a68aSyz147064 do_get_channel_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1621f595a68aSyz147064 {
1622f595a68aSyz147064 	uint32_t	channel;
1623f595a68aSyz147064 
1624f595a68aSyz147064 	if (do_get_phyconf(fd, gbuf) < 0)
1625f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1626f595a68aSyz147064 
1627f595a68aSyz147064 	if (!do_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf, &channel))
1628f595a68aSyz147064 		return (DLADM_STATUS_NOTFOUND);
1629f595a68aSyz147064 
1630f595a68aSyz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
1631f595a68aSyz147064 	*val_cnt = 1;
1632f595a68aSyz147064 
1633f595a68aSyz147064 	return (DLADM_STATUS_OK);
1634f595a68aSyz147064 }
1635f595a68aSyz147064 
1636f595a68aSyz147064 static dladm_status_t
1637f595a68aSyz147064 do_get_powermode_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1638f595a68aSyz147064 {
1639f595a68aSyz147064 	wl_ps_mode_t	*mode;
1640f595a68aSyz147064 	const char	*s;
1641f595a68aSyz147064 
1642f595a68aSyz147064 	if (do_get_powermode(fd, gbuf) < 0)
1643f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1644f595a68aSyz147064 
1645f595a68aSyz147064 	mode = (wl_ps_mode_t *)(gbuf->wldp_buf);
1646f595a68aSyz147064 	switch (mode->wl_ps_mode) {
1647f595a68aSyz147064 	case WL_PM_AM:
1648f595a68aSyz147064 		s = "off";
1649f595a68aSyz147064 		break;
1650f595a68aSyz147064 	case WL_PM_MPS:
1651f595a68aSyz147064 		s = "max";
1652f595a68aSyz147064 		break;
1653f595a68aSyz147064 	case WL_PM_FAST:
1654f595a68aSyz147064 		s = "fast";
1655f595a68aSyz147064 		break;
1656f595a68aSyz147064 	default:
1657f595a68aSyz147064 		return (DLADM_STATUS_NOTFOUND);
1658f595a68aSyz147064 	}
1659f595a68aSyz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1660f595a68aSyz147064 	*val_cnt = 1;
1661f595a68aSyz147064 
1662f595a68aSyz147064 	return (DLADM_STATUS_OK);
1663f595a68aSyz147064 }
1664f595a68aSyz147064 
1665f595a68aSyz147064 static dladm_status_t
1666f595a68aSyz147064 do_get_radio_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
1667f595a68aSyz147064 {
1668f595a68aSyz147064 	wl_radio_t	radio;
1669f595a68aSyz147064 	const char	*s;
1670f595a68aSyz147064 
1671f595a68aSyz147064 	if (do_get_radio(fd, gbuf) < 0)
1672f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1673f595a68aSyz147064 
1674f595a68aSyz147064 	radio = *(wl_radio_t *)(gbuf->wldp_buf);
1675f595a68aSyz147064 	switch (radio) {
1676f595a68aSyz147064 	case B_TRUE:
1677f595a68aSyz147064 		s = "on";
1678f595a68aSyz147064 		break;
1679f595a68aSyz147064 	case B_FALSE:
1680f595a68aSyz147064 		s = "off";
1681f595a68aSyz147064 		break;
1682f595a68aSyz147064 	default:
1683f595a68aSyz147064 		return (DLADM_STATUS_NOTFOUND);
1684f595a68aSyz147064 	}
1685f595a68aSyz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1686f595a68aSyz147064 	*val_cnt = 1;
1687f595a68aSyz147064 
1688f595a68aSyz147064 	return (DLADM_STATUS_OK);
1689f595a68aSyz147064 }
1690f595a68aSyz147064 
1691f595a68aSyz147064 static int
1692f595a68aSyz147064 do_set_bsstype(int fd, wldp_t *gbuf, dladm_wlan_bsstype_t *bsstype)
1693f595a68aSyz147064 {
1694f595a68aSyz147064 	wl_bss_type_t	ibsstype;
1695f595a68aSyz147064 
1696f595a68aSyz147064 	switch (*bsstype) {
1697f595a68aSyz147064 	case DLADM_WLAN_BSSTYPE_BSS:
1698f595a68aSyz147064 		ibsstype = WL_BSS_BSS;
1699f595a68aSyz147064 		break;
1700f595a68aSyz147064 	case DLADM_WLAN_BSSTYPE_IBSS:
1701f595a68aSyz147064 		ibsstype = WL_BSS_IBSS;
1702f595a68aSyz147064 		break;
1703f595a68aSyz147064 	default:
1704f595a68aSyz147064 		ibsstype = WL_BSS_ANY;
1705f595a68aSyz147064 		break;
1706f595a68aSyz147064 	}
1707f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_BSS_TYPE, &ibsstype,
1708f595a68aSyz147064 	    sizeof (ibsstype)));
1709f595a68aSyz147064 }
1710f595a68aSyz147064 
1711f595a68aSyz147064 static int
1712f595a68aSyz147064 do_set_authmode(int fd, wldp_t *gbuf, dladm_wlan_auth_t *auth)
1713f595a68aSyz147064 {
1714f595a68aSyz147064 	wl_authmode_t	auth_mode;
1715f595a68aSyz147064 
1716f595a68aSyz147064 	switch (*auth) {
1717f595a68aSyz147064 	case DLADM_WLAN_AUTH_OPEN:
1718f595a68aSyz147064 		auth_mode = WL_OPENSYSTEM;
1719f595a68aSyz147064 		break;
1720f595a68aSyz147064 	case DLADM_WLAN_AUTH_SHARED:
1721f595a68aSyz147064 		auth_mode = WL_SHAREDKEY;
1722f595a68aSyz147064 		break;
1723f595a68aSyz147064 	default:
1724f595a68aSyz147064 		return (-1);
1725f595a68aSyz147064 	}
1726f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_AUTH_MODE, &auth_mode,
1727f595a68aSyz147064 	    sizeof (auth_mode)));
1728f595a68aSyz147064 }
1729f595a68aSyz147064 
1730f595a68aSyz147064 static int
1731f595a68aSyz147064 do_set_encryption(int fd, wldp_t *gbuf, dladm_wlan_secmode_t *secmode)
1732f595a68aSyz147064 {
1733f595a68aSyz147064 	wl_encryption_t	encryption;
1734f595a68aSyz147064 
1735f595a68aSyz147064 	switch (*secmode) {
1736f595a68aSyz147064 	case DLADM_WLAN_SECMODE_NONE:
1737f595a68aSyz147064 		encryption = WL_NOENCRYPTION;
1738f595a68aSyz147064 		break;
1739f595a68aSyz147064 	case DLADM_WLAN_SECMODE_WEP:
1740f595a68aSyz147064 		encryption = WL_ENC_WEP;
1741f595a68aSyz147064 		break;
1742*a399b765Szf162725 	case DLADM_WLAN_SECMODE_WPA:
1743*a399b765Szf162725 		return (0);
1744f595a68aSyz147064 	default:
1745f595a68aSyz147064 		return (-1);
1746f595a68aSyz147064 	}
1747f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_ENCRYPTION, &encryption,
1748f595a68aSyz147064 	    sizeof (encryption)));
1749f595a68aSyz147064 }
1750f595a68aSyz147064 
1751f595a68aSyz147064 static int
1752*a399b765Szf162725 do_set_key(int fd, wldp_t *gbuf, dladm_wlan_key_t *keys,
1753f595a68aSyz147064     uint_t key_count)
1754f595a68aSyz147064 {
1755f595a68aSyz147064 	int			i;
1756f595a68aSyz147064 	wl_wep_key_t		*wkp;
1757f595a68aSyz147064 	wl_wep_key_tab_t	wepkey_tab;
1758*a399b765Szf162725 	dladm_wlan_key_t	*kp;
1759f595a68aSyz147064 
1760f595a68aSyz147064 	if (key_count == 0 || key_count > MAX_NWEPKEYS || keys == NULL)
1761f595a68aSyz147064 		return (-1);
1762f595a68aSyz147064 
1763f595a68aSyz147064 	(void) memset(wepkey_tab, 0, sizeof (wepkey_tab));
1764f595a68aSyz147064 	for (i = 0; i < MAX_NWEPKEYS; i++)
1765f595a68aSyz147064 		wepkey_tab[i].wl_wep_operation = WL_NUL;
1766f595a68aSyz147064 
1767f595a68aSyz147064 	for (i = 0; i < key_count; i++) {
1768f595a68aSyz147064 		kp = &keys[i];
1769f595a68aSyz147064 		if (kp->wk_idx == 0 || kp->wk_idx > MAX_NWEPKEYS)
1770f595a68aSyz147064 			return (-1);
1771f595a68aSyz147064 		if (kp->wk_len != DLADM_WLAN_WEPKEY64_LEN &&
1772f595a68aSyz147064 		    kp->wk_len != DLADM_WLAN_WEPKEY128_LEN)
1773f595a68aSyz147064 			return (-1);
1774f595a68aSyz147064 
1775f595a68aSyz147064 		wkp = &wepkey_tab[kp->wk_idx - 1];
1776f595a68aSyz147064 		wkp->wl_wep_operation = WL_ADD;
1777f595a68aSyz147064 		wkp->wl_wep_length = kp->wk_len;
1778f595a68aSyz147064 		(void) memcpy(wkp->wl_wep_key, kp->wk_val, kp->wk_len);
1779f595a68aSyz147064 	}
1780f595a68aSyz147064 
1781f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_WEP_KEY_TAB, &wepkey_tab,
1782f595a68aSyz147064 	    sizeof (wepkey_tab)));
1783f595a68aSyz147064 }
1784f595a68aSyz147064 
1785f595a68aSyz147064 static int
1786f595a68aSyz147064 do_set_essid(int fd, wldp_t *gbuf, dladm_wlan_essid_t *essid)
1787f595a68aSyz147064 {
1788f595a68aSyz147064 	wl_essid_t	iessid;
1789f595a68aSyz147064 
1790f595a68aSyz147064 	(void) memset(&iessid, 0, sizeof (essid));
1791f595a68aSyz147064 
1792f595a68aSyz147064 	if (essid != NULL && essid->we_bytes[0] != '\0') {
1793f595a68aSyz147064 		iessid.wl_essid_length = strlen(essid->we_bytes);
1794f595a68aSyz147064 		(void) strlcpy(iessid.wl_essid_essid, essid->we_bytes,
1795f595a68aSyz147064 		    sizeof (iessid.wl_essid_essid));
1796f595a68aSyz147064 	} else {
1797f595a68aSyz147064 		return (-1);
1798f595a68aSyz147064 	}
1799f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_ESSID, &iessid, sizeof (iessid)));
1800f595a68aSyz147064 }
1801f595a68aSyz147064 
1802f595a68aSyz147064 /* ARGSUSED */
1803f595a68aSyz147064 static dladm_status_t
1804f595a68aSyz147064 do_check_rate(int fd, wldp_t *gbuf, prop_desc_t *pdp, char **prop_val,
1805f595a68aSyz147064     uint_t val_cnt, val_desc_t **vdpp)
1806f595a68aSyz147064 {
1807f595a68aSyz147064 	int		i;
1808f595a68aSyz147064 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
1809f595a68aSyz147064 	char		*buf, **modval;
1810f595a68aSyz147064 	dladm_status_t	status;
1811f595a68aSyz147064 	val_desc_t	*vdp = NULL;
1812f595a68aSyz147064 
1813f595a68aSyz147064 	if (val_cnt != 1)
1814f595a68aSyz147064 		return (DLADM_STATUS_BADVALCNT);
1815f595a68aSyz147064 
1816f595a68aSyz147064 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) * MAX_SUPPORT_RATES);
1817f595a68aSyz147064 	if (buf == NULL)
1818f595a68aSyz147064 		goto done;
1819f595a68aSyz147064 
1820f595a68aSyz147064 	modval = (char **)(void *)buf;
1821f595a68aSyz147064 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
1822f595a68aSyz147064 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
1823f595a68aSyz147064 		    i * DLADM_STRSIZE;
1824f595a68aSyz147064 	}
1825f595a68aSyz147064 
1826f595a68aSyz147064 	status = do_get_rate_mod(fd, gbuf, modval, &modval_cnt);
1827f595a68aSyz147064 	if (status != DLADM_STATUS_OK)
1828f595a68aSyz147064 		goto done;
1829f595a68aSyz147064 
1830f595a68aSyz147064 	vdp = malloc(sizeof (val_desc_t));
1831f595a68aSyz147064 	if (vdp == NULL) {
1832f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
1833f595a68aSyz147064 		goto done;
1834f595a68aSyz147064 	}
1835f595a68aSyz147064 
1836f595a68aSyz147064 	for (i = 0; i < modval_cnt; i++) {
1837f595a68aSyz147064 		if (strcasecmp(*prop_val, modval[i]) == 0) {
1838f595a68aSyz147064 			vdp->vd_val = (uint_t)(atof(*prop_val) * 2);
1839f595a68aSyz147064 			status = DLADM_STATUS_OK;
1840f595a68aSyz147064 			*vdpp = vdp;
1841f595a68aSyz147064 			vdp = NULL;
1842f595a68aSyz147064 			break;
1843f595a68aSyz147064 		}
1844f595a68aSyz147064 	}
1845f595a68aSyz147064 	if (i == modval_cnt)
1846f595a68aSyz147064 		status = DLADM_STATUS_BADVAL;
1847f595a68aSyz147064 done:
1848f595a68aSyz147064 	free(buf);
1849f595a68aSyz147064 	free(vdp);
1850f595a68aSyz147064 	return (status);
1851f595a68aSyz147064 }
1852f595a68aSyz147064 
1853f595a68aSyz147064 static dladm_status_t
1854f595a68aSyz147064 do_set_rate_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
1855f595a68aSyz147064 {
1856f595a68aSyz147064 	dladm_wlan_rates_t	rates;
1857f595a68aSyz147064 
1858f595a68aSyz147064 	if (val_cnt != 1)
1859f595a68aSyz147064 		return (DLADM_STATUS_BADVALCNT);
1860f595a68aSyz147064 
1861f595a68aSyz147064 	rates.wr_cnt = 1;
1862f595a68aSyz147064 	rates.wr_rates[0] = vdp[0].vd_val;
1863f595a68aSyz147064 
1864f595a68aSyz147064 	if (do_set_rate(fd, gbuf, &rates) < 0)
1865f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1866f595a68aSyz147064 
1867f595a68aSyz147064 	return (DLADM_STATUS_OK);
1868f595a68aSyz147064 }
1869f595a68aSyz147064 
1870f595a68aSyz147064 static int
1871f595a68aSyz147064 do_set_rate(int fd, wldp_t *gbuf, dladm_wlan_rates_t *rates)
1872f595a68aSyz147064 {
1873f595a68aSyz147064 	int		i;
1874f595a68aSyz147064 	uint_t		len;
1875f595a68aSyz147064 	wl_rates_t	*wrp = (wl_rates_t *)gbuf->wldp_buf;
1876f595a68aSyz147064 
1877f595a68aSyz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1878f595a68aSyz147064 
1879f595a68aSyz147064 	for (i = 0; i < rates->wr_cnt; i++)
1880f595a68aSyz147064 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
1881f595a68aSyz147064 	wrp->wl_rates_num = rates->wr_cnt;
1882f595a68aSyz147064 
1883f595a68aSyz147064 	len = offsetof(wl_rates_t, wl_rates_rates) +
1884f595a68aSyz147064 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
1885f595a68aSyz147064 	return (do_ioctl(fd, gbuf, WL_DESIRED_RATES, len, WLAN_SET_PARAM, len));
1886f595a68aSyz147064 }
1887f595a68aSyz147064 
1888f595a68aSyz147064 /* ARGSUSED */
1889f595a68aSyz147064 static dladm_status_t
1890f595a68aSyz147064 do_set_powermode_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
1891f595a68aSyz147064 {
1892f595a68aSyz147064 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
1893f595a68aSyz147064 
1894f595a68aSyz147064 	if (do_set_powermode(fd, gbuf, &powermode) < 0)
1895f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1896f595a68aSyz147064 
1897f595a68aSyz147064 	return (DLADM_STATUS_OK);
1898f595a68aSyz147064 }
1899f595a68aSyz147064 
1900f595a68aSyz147064 static int
1901f595a68aSyz147064 do_set_powermode(int fd, wldp_t *gbuf, dladm_wlan_powermode_t *pm)
1902f595a68aSyz147064 {
1903f595a68aSyz147064 	wl_ps_mode_t	ps_mode;
1904f595a68aSyz147064 
1905f595a68aSyz147064 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
1906f595a68aSyz147064 
1907f595a68aSyz147064 	switch (*pm) {
1908f595a68aSyz147064 	case DLADM_WLAN_PM_OFF:
1909f595a68aSyz147064 		ps_mode.wl_ps_mode = WL_PM_AM;
1910f595a68aSyz147064 		break;
1911f595a68aSyz147064 	case DLADM_WLAN_PM_MAX:
1912f595a68aSyz147064 		ps_mode.wl_ps_mode = WL_PM_MPS;
1913f595a68aSyz147064 		break;
1914f595a68aSyz147064 	case DLADM_WLAN_PM_FAST:
1915f595a68aSyz147064 		ps_mode.wl_ps_mode = WL_PM_FAST;
1916f595a68aSyz147064 		break;
1917f595a68aSyz147064 	default:
1918f595a68aSyz147064 		return (-1);
1919f595a68aSyz147064 	}
1920f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_POWER_MODE, &ps_mode,
1921f595a68aSyz147064 	    sizeof (ps_mode)));
1922f595a68aSyz147064 }
1923f595a68aSyz147064 
1924f595a68aSyz147064 /* ARGSUSED */
1925f595a68aSyz147064 static dladm_status_t
1926f595a68aSyz147064 do_set_radio_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
1927f595a68aSyz147064 {
1928f595a68aSyz147064 	dladm_wlan_radio_t	radio = (dladm_wlan_radio_t)vdp->vd_val;
1929f595a68aSyz147064 
1930f595a68aSyz147064 	if (do_set_radio(fd, gbuf, &radio) < 0)
1931f595a68aSyz147064 		return (dladm_wlan_wlresult2status(gbuf));
1932f595a68aSyz147064 
1933f595a68aSyz147064 	return (DLADM_STATUS_OK);
1934f595a68aSyz147064 }
1935f595a68aSyz147064 
1936f595a68aSyz147064 static int
1937f595a68aSyz147064 do_set_radio(int fd, wldp_t *gbuf, dladm_wlan_radio_t *radio)
1938f595a68aSyz147064 {
1939f595a68aSyz147064 	wl_radio_t r;
1940f595a68aSyz147064 
1941f595a68aSyz147064 	switch (*radio) {
1942f595a68aSyz147064 	case DLADM_WLAN_RADIO_ON:
1943f595a68aSyz147064 		r = B_TRUE;
1944f595a68aSyz147064 		break;
1945f595a68aSyz147064 	case DLADM_WLAN_RADIO_OFF:
1946f595a68aSyz147064 		r = B_FALSE;
1947f595a68aSyz147064 		break;
1948f595a68aSyz147064 	default:
1949f595a68aSyz147064 		return (-1);
1950f595a68aSyz147064 	}
1951f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_RADIO, &r, sizeof (r)));
1952f595a68aSyz147064 }
1953f595a68aSyz147064 
1954f595a68aSyz147064 static int
1955f595a68aSyz147064 do_set_channel(int fd, wldp_t *gbuf, dladm_wlan_channel_t *channel)
1956f595a68aSyz147064 {
1957f595a68aSyz147064 	wl_phy_conf_t phy_conf;
1958f595a68aSyz147064 
1959f595a68aSyz147064 	if (*channel > MAX_CHANNEL_NUM)
1960f595a68aSyz147064 		return (-1);
1961f595a68aSyz147064 
1962f595a68aSyz147064 	(void) memset(&phy_conf, 0xff, sizeof (phy_conf));
1963f595a68aSyz147064 	phy_conf.wl_phy_dsss_conf.wl_dsss_channel = *channel;
1964f595a68aSyz147064 
1965f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_PHY_CONFIG, &phy_conf,
1966f595a68aSyz147064 	    sizeof (phy_conf)));
1967f595a68aSyz147064 }
1968f595a68aSyz147064 
1969f595a68aSyz147064 static int
1970f595a68aSyz147064 do_set_createibss(int fd, wldp_t *gbuf, boolean_t *create_ibss)
1971f595a68aSyz147064 {
1972f595a68aSyz147064 	wl_create_ibss_t cr = (wl_create_ibss_t)(*create_ibss);
1973f595a68aSyz147064 
1974f595a68aSyz147064 	return (do_set_ioctl(fd, gbuf, WL_CREATE_IBSS, &cr, sizeof (cr)));
1975f595a68aSyz147064 }
1976f595a68aSyz147064 
1977f595a68aSyz147064 static void
1978f595a68aSyz147064 generate_essid(dladm_wlan_essid_t *essid)
1979f595a68aSyz147064 {
1980f595a68aSyz147064 	srandom(gethrtime());
1981f595a68aSyz147064 	(void) snprintf(essid->we_bytes, DLADM_WLAN_MAX_ESSID_LEN, "%d",
1982f595a68aSyz147064 	    random());
1983f595a68aSyz147064 }
1984*a399b765Szf162725 
1985*a399b765Szf162725 static int
1986*a399b765Szf162725 do_get_capability(int fd, wldp_t *gbuf)
1987*a399b765Szf162725 {
1988*a399b765Szf162725 	return (do_get_ioctl(fd, gbuf, WL_CAPABILITY));
1989*a399b765Szf162725 }
1990*a399b765Szf162725 
1991*a399b765Szf162725 static int
1992*a399b765Szf162725 do_get_wpamode(int fd, wldp_t *gbuf)
1993*a399b765Szf162725 {
1994*a399b765Szf162725 	return (do_get_ioctl(fd, gbuf, WL_WPA));
1995*a399b765Szf162725 }
1996*a399b765Szf162725 
1997*a399b765Szf162725 static dladm_status_t
1998*a399b765Szf162725 ioctl_get(const char *link, int id, void *gbuf)
1999*a399b765Szf162725 {
2000*a399b765Szf162725 	int		fd;
2001*a399b765Szf162725 
2002*a399b765Szf162725 	if ((fd = open_link(link)) < 0)
2003*a399b765Szf162725 		return (DLADM_STATUS_LINKINVAL);
2004*a399b765Szf162725 	(void) do_get_ioctl(fd, gbuf, id);
2005*a399b765Szf162725 
2006*a399b765Szf162725 	(void) close(fd);
2007*a399b765Szf162725 	return (dladm_wlan_wlresult2status(gbuf));
2008*a399b765Szf162725 }
2009*a399b765Szf162725 
2010*a399b765Szf162725 static dladm_status_t
2011*a399b765Szf162725 ioctl_set(const char *link, int id, void *buf, uint_t buflen)
2012*a399b765Szf162725 {
2013*a399b765Szf162725 	int		fd;
2014*a399b765Szf162725 	wldp_t 		*gbuf;
2015*a399b765Szf162725 	dladm_status_t	status;
2016*a399b765Szf162725 
2017*a399b765Szf162725 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2018*a399b765Szf162725 		return (DLADM_STATUS_NOMEM);
2019*a399b765Szf162725 
2020*a399b765Szf162725 	if ((fd = open_link(link)) < 0)
2021*a399b765Szf162725 		return (DLADM_STATUS_LINKINVAL);
2022*a399b765Szf162725 	(void) do_set_ioctl(fd, gbuf, id, buf, buflen);
2023*a399b765Szf162725 
2024*a399b765Szf162725 	(void) close(fd);
2025*a399b765Szf162725 	status = dladm_wlan_wlresult2status(gbuf);
2026*a399b765Szf162725 	free(gbuf);
2027*a399b765Szf162725 
2028*a399b765Szf162725 	return (status);
2029*a399b765Szf162725 }
2030*a399b765Szf162725 
2031*a399b765Szf162725 dladm_status_t
2032*a399b765Szf162725 dladm_wlan_wpa_get_sr(const char *link, dladm_wlan_ess_t *sr, uint_t escnt,
2033*a399b765Szf162725     uint_t *estot)
2034*a399b765Szf162725 {
2035*a399b765Szf162725 	int		i, n;
2036*a399b765Szf162725 	wldp_t 		*gbuf;
2037*a399b765Szf162725 	wl_wpa_ess_t	*es;
2038*a399b765Szf162725 	dladm_status_t	status;
2039*a399b765Szf162725 
2040*a399b765Szf162725 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2041*a399b765Szf162725 		return (DLADM_STATUS_NOMEM);
2042*a399b765Szf162725 
2043*a399b765Szf162725 	status = ioctl_get(link, WL_SCANRESULTS, gbuf);
2044*a399b765Szf162725 
2045*a399b765Szf162725 	if (status == DLADM_STATUS_OK) {
2046*a399b765Szf162725 		es = (wl_wpa_ess_t *)(gbuf->wldp_buf);
2047*a399b765Szf162725 		n = (es->count > escnt) ? escnt : es->count;
2048*a399b765Szf162725 		for (i = 0; i < n; i ++) {
2049*a399b765Szf162725 			(void) memcpy(sr[i].we_bssid.wb_bytes, es->ess[i].bssid,
2050*a399b765Szf162725 			    DLADM_WLAN_BSSID_LEN);
2051*a399b765Szf162725 			sr[i].we_ssid_len = es->ess[i].ssid_len;
2052*a399b765Szf162725 			(void) memcpy(sr[i].we_ssid.we_bytes, es->ess[i].ssid,
2053*a399b765Szf162725 			    es->ess[i].ssid_len);
2054*a399b765Szf162725 			sr[i].we_wpa_ie_len = es->ess[i].wpa_ie_len;
2055*a399b765Szf162725 			(void) memcpy(sr[i].we_wpa_ie, es->ess[i].wpa_ie,
2056*a399b765Szf162725 			    es->ess[i].wpa_ie_len);
2057*a399b765Szf162725 			sr[i].we_freq = es->ess[i].freq;
2058*a399b765Szf162725 		}
2059*a399b765Szf162725 		*estot = n;
2060*a399b765Szf162725 	}
2061*a399b765Szf162725 
2062*a399b765Szf162725 	free(gbuf);
2063*a399b765Szf162725 	return (status);
2064*a399b765Szf162725 }
2065*a399b765Szf162725 
2066*a399b765Szf162725 dladm_status_t
2067*a399b765Szf162725 dladm_wlan_wpa_set_ie(const char *link, uint8_t *wpa_ie,
2068*a399b765Szf162725     uint_t wpa_ie_len)
2069*a399b765Szf162725 {
2070*a399b765Szf162725 	wl_wpa_ie_t *ie;
2071*a399b765Szf162725 	uint_t len;
2072*a399b765Szf162725 	dladm_status_t	status;
2073*a399b765Szf162725 
2074*a399b765Szf162725 	if (wpa_ie_len > DLADM_WLAN_MAX_WPA_IE_LEN)
2075*a399b765Szf162725 		return (DLADM_STATUS_BADARG);
2076*a399b765Szf162725 	len = sizeof (wl_wpa_ie_t) + wpa_ie_len;
2077*a399b765Szf162725 	ie = malloc(len);
2078*a399b765Szf162725 	if (ie == NULL)
2079*a399b765Szf162725 		return (DLADM_STATUS_NOMEM);
2080*a399b765Szf162725 
2081*a399b765Szf162725 	(void) memset(ie, 0, len);
2082*a399b765Szf162725 	ie->wpa_ie_len = wpa_ie_len;
2083*a399b765Szf162725 	(void) memcpy(ie->wpa_ie, wpa_ie, wpa_ie_len);
2084*a399b765Szf162725 
2085*a399b765Szf162725 	status = ioctl_set(link, WL_SETOPTIE, ie, len);
2086*a399b765Szf162725 	free(ie);
2087*a399b765Szf162725 
2088*a399b765Szf162725 	return (status);
2089*a399b765Szf162725 }
2090*a399b765Szf162725 
2091*a399b765Szf162725 dladm_status_t
2092*a399b765Szf162725 dladm_wlan_wpa_set_wpa(const char *link, boolean_t flag)
2093*a399b765Szf162725 {
2094*a399b765Szf162725 	wl_wpa_t wpa;
2095*a399b765Szf162725 
2096*a399b765Szf162725 	wpa.wpa_flag = flag;
2097*a399b765Szf162725 	return (ioctl_set(link, WL_WPA, &wpa, sizeof (wl_wpa_t)));
2098*a399b765Szf162725 }
2099*a399b765Szf162725 
2100*a399b765Szf162725 dladm_status_t
2101*a399b765Szf162725 dladm_wlan_wpa_del_key(const char *link, uint_t key_idx,
2102*a399b765Szf162725     const dladm_wlan_bssid_t *addr)
2103*a399b765Szf162725 {
2104*a399b765Szf162725 	wl_del_key_t wk;
2105*a399b765Szf162725 
2106*a399b765Szf162725 	wk.idk_keyix = key_idx;
2107*a399b765Szf162725 	if (addr != NULL)
2108*a399b765Szf162725 		(void) memcpy((char *)wk.idk_macaddr, addr->wb_bytes,
2109*a399b765Szf162725 		    DLADM_WLAN_BSSID_LEN);
2110*a399b765Szf162725 
2111*a399b765Szf162725 	return (ioctl_set(link, WL_DELKEY, &wk, sizeof (wl_del_key_t)));
2112*a399b765Szf162725 }
2113*a399b765Szf162725 
2114*a399b765Szf162725 dladm_status_t
2115*a399b765Szf162725 dladm_wlan_wpa_set_key(const char *link, dladm_wlan_cipher_t cipher,
2116*a399b765Szf162725     const dladm_wlan_bssid_t *addr, boolean_t set_tx, uint64_t seq,
2117*a399b765Szf162725     uint_t key_idx, uint8_t *key, uint_t key_len)
2118*a399b765Szf162725 {
2119*a399b765Szf162725 	wl_key_t wk;
2120*a399b765Szf162725 
2121*a399b765Szf162725 	(void) memset(&wk, 0, sizeof (wl_key_t));
2122*a399b765Szf162725 	switch (cipher) {
2123*a399b765Szf162725 	case DLADM_WLAN_CIPHER_WEP:
2124*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_WEP;
2125*a399b765Szf162725 		break;
2126*a399b765Szf162725 	case DLADM_WLAN_CIPHER_TKIP:
2127*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_TKIP;
2128*a399b765Szf162725 		break;
2129*a399b765Szf162725 	case DLADM_WLAN_CIPHER_AES_OCB:
2130*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_AES_OCB;
2131*a399b765Szf162725 		break;
2132*a399b765Szf162725 	case DLADM_WLAN_CIPHER_AES_CCM:
2133*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_AES_CCM;
2134*a399b765Szf162725 		break;
2135*a399b765Szf162725 	case DLADM_WLAN_CIPHER_CKIP:
2136*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_CKIP;
2137*a399b765Szf162725 		break;
2138*a399b765Szf162725 	case DLADM_WLAN_CIPHER_NONE:
2139*a399b765Szf162725 		wk.ik_type = IEEE80211_CIPHER_NONE;
2140*a399b765Szf162725 		break;
2141*a399b765Szf162725 	default:
2142*a399b765Szf162725 		return (DLADM_STATUS_BADARG);
2143*a399b765Szf162725 	}
2144*a399b765Szf162725 	wk.ik_flags = IEEE80211_KEY_RECV;
2145*a399b765Szf162725 	if (set_tx) {
2146*a399b765Szf162725 		wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
2147*a399b765Szf162725 		(void) memcpy(wk.ik_macaddr, addr->wb_bytes,
2148*a399b765Szf162725 		    DLADM_WLAN_BSSID_LEN);
2149*a399b765Szf162725 	} else
2150*a399b765Szf162725 		(void) memset(wk.ik_macaddr, 0, DLADM_WLAN_BSSID_LEN);
2151*a399b765Szf162725 	wk.ik_keyix = key_idx;
2152*a399b765Szf162725 	wk.ik_keylen = key_len;
2153*a399b765Szf162725 	(void) memcpy(&wk.ik_keyrsc, &seq, 6);	/* only use 48-bit of seq */
2154*a399b765Szf162725 	(void) memcpy(wk.ik_keydata, key, key_len);
2155*a399b765Szf162725 
2156*a399b765Szf162725 	return (ioctl_set(link, WL_KEY, &wk, sizeof (wl_key_t)));
2157*a399b765Szf162725 }
2158*a399b765Szf162725 
2159*a399b765Szf162725 dladm_status_t
2160*a399b765Szf162725 dladm_wlan_wpa_set_mlme(const char *link, dladm_wlan_mlme_op_t op,
2161*a399b765Szf162725     dladm_wlan_reason_t reason, dladm_wlan_bssid_t *bssid)
2162*a399b765Szf162725 {
2163*a399b765Szf162725 	wl_mlme_t mlme;
2164*a399b765Szf162725 
2165*a399b765Szf162725 	(void) memset(&mlme, 0, sizeof (wl_mlme_t));
2166*a399b765Szf162725 	switch (op) {
2167*a399b765Szf162725 	case DLADM_WLAN_MLME_ASSOC:
2168*a399b765Szf162725 		mlme.im_op = IEEE80211_MLME_ASSOC;
2169*a399b765Szf162725 		break;
2170*a399b765Szf162725 	case DLADM_WLAN_MLME_DISASSOC:
2171*a399b765Szf162725 		mlme.im_op = IEEE80211_MLME_DISASSOC;
2172*a399b765Szf162725 		break;
2173*a399b765Szf162725 	default:
2174*a399b765Szf162725 		return (DLADM_STATUS_BADARG);
2175*a399b765Szf162725 	}
2176*a399b765Szf162725 	mlme.im_reason = reason;
2177*a399b765Szf162725 	if (bssid != NULL)
2178*a399b765Szf162725 		(void) memcpy(mlme.im_macaddr, bssid->wb_bytes,
2179*a399b765Szf162725 		    DLADM_WLAN_BSSID_LEN);
2180*a399b765Szf162725 
2181*a399b765Szf162725 	return (ioctl_set(link, WL_MLME, &mlme, sizeof (wl_mlme_t)));
2182*a399b765Szf162725 }
2183*a399b765Szf162725 
2184*a399b765Szf162725 /*
2185*a399b765Szf162725  * routines of create instance
2186*a399b765Szf162725  */
2187*a399b765Szf162725 static scf_propertygroup_t *
2188*a399b765Szf162725 add_property_group_to_instance(scf_handle_t *handle, scf_instance_t *instance,
2189*a399b765Szf162725     const char *pg_name, const char *pg_type)
2190*a399b765Szf162725 {
2191*a399b765Szf162725 	scf_propertygroup_t *pg;
2192*a399b765Szf162725 
2193*a399b765Szf162725 	pg = scf_pg_create(handle);
2194*a399b765Szf162725 	if (pg == NULL)
2195*a399b765Szf162725 		return (NULL);
2196*a399b765Szf162725 
2197*a399b765Szf162725 	if (scf_instance_add_pg(instance, pg_name, pg_type, 0, pg) != 0) {
2198*a399b765Szf162725 		scf_pg_destroy(pg);
2199*a399b765Szf162725 		return (NULL);
2200*a399b765Szf162725 	}
2201*a399b765Szf162725 
2202*a399b765Szf162725 	return (pg);
2203*a399b765Szf162725 }
2204*a399b765Szf162725 
2205*a399b765Szf162725 static int
2206*a399b765Szf162725 add_new_property(scf_handle_t *handle, const char *prop_name,
2207*a399b765Szf162725     scf_type_t type, const char *val, scf_transaction_t *tx)
2208*a399b765Szf162725 {
2209*a399b765Szf162725 	scf_value_t *value = NULL;
2210*a399b765Szf162725 	scf_transaction_entry_t *entry = NULL;
2211*a399b765Szf162725 
2212*a399b765Szf162725 	entry = scf_entry_create(handle);
2213*a399b765Szf162725 	if (entry == NULL)
2214*a399b765Szf162725 		goto out;
2215*a399b765Szf162725 
2216*a399b765Szf162725 	value = scf_value_create(handle);
2217*a399b765Szf162725 	if (value == NULL)
2218*a399b765Szf162725 		goto out;
2219*a399b765Szf162725 
2220*a399b765Szf162725 	if (scf_transaction_property_new(tx, entry, prop_name, type) != 0)
2221*a399b765Szf162725 		goto out;
2222*a399b765Szf162725 
2223*a399b765Szf162725 	if (scf_value_set_from_string(value, type, val) != 0)
2224*a399b765Szf162725 		goto out;
2225*a399b765Szf162725 
2226*a399b765Szf162725 	if (scf_entry_add_value(entry, value) != 0)
2227*a399b765Szf162725 		goto out;
2228*a399b765Szf162725 
2229*a399b765Szf162725 	return (DLADM_WLAN_SVC_SUCCESS);
2230*a399b765Szf162725 
2231*a399b765Szf162725 out:
2232*a399b765Szf162725 	if (value != NULL)
2233*a399b765Szf162725 		scf_value_destroy(value);
2234*a399b765Szf162725 	if (entry != NULL)
2235*a399b765Szf162725 		scf_entry_destroy(entry);
2236*a399b765Szf162725 
2237*a399b765Szf162725 	return (DLADM_WLAN_SVC_FAILURE);
2238*a399b765Szf162725 }
2239*a399b765Szf162725 
2240*a399b765Szf162725 /*
2241*a399b765Szf162725  * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
2242*a399b765Szf162725  */
2243*a399b765Szf162725 static int
2244*a399b765Szf162725 add_pg_method(scf_handle_t *handle, scf_instance_t *instance,
2245*a399b765Szf162725     const char *pg_name, const char *flags)
2246*a399b765Szf162725 {
2247*a399b765Szf162725 	int			rv, size;
2248*a399b765Szf162725 	int			status = DLADM_WLAN_SVC_FAILURE;
2249*a399b765Szf162725 	char			*command = NULL;
2250*a399b765Szf162725 	scf_transaction_t	*tran = NULL;
2251*a399b765Szf162725 	scf_propertygroup_t	*pg;
2252*a399b765Szf162725 
2253*a399b765Szf162725 	pg = add_property_group_to_instance(handle, instance,
2254*a399b765Szf162725 	    pg_name, SCF_GROUP_METHOD);
2255*a399b765Szf162725 	if (pg == NULL)
2256*a399b765Szf162725 		goto out;
2257*a399b765Szf162725 
2258*a399b765Szf162725 	tran = scf_transaction_create(handle);
2259*a399b765Szf162725 	if (tran == NULL)
2260*a399b765Szf162725 		goto out;
2261*a399b765Szf162725 
2262*a399b765Szf162725 	size = strlen(SVC_METHOD) + strlen("  ") + strlen(flags) + 1;
2263*a399b765Szf162725 	command = malloc(size);
2264*a399b765Szf162725 	if (command == NULL) {
2265*a399b765Szf162725 		status = DLADM_WLAN_SVC_APP_FAILURE;
2266*a399b765Szf162725 		goto out;
2267*a399b765Szf162725 	}
2268*a399b765Szf162725 	(void) snprintf(command, size, "%s %s", SVC_METHOD, flags);
2269*a399b765Szf162725 
2270*a399b765Szf162725 	do {
2271*a399b765Szf162725 		if (scf_transaction_start(tran, pg) != 0)
2272*a399b765Szf162725 			goto out;
2273*a399b765Szf162725 
2274*a399b765Szf162725 		if (add_new_property(handle, SCF_PROPERTY_EXEC,
2275*a399b765Szf162725 		    SCF_TYPE_ASTRING, command, tran) !=
2276*a399b765Szf162725 		    DLADM_WLAN_SVC_SUCCESS) {
2277*a399b765Szf162725 			goto out;
2278*a399b765Szf162725 		}
2279*a399b765Szf162725 
2280*a399b765Szf162725 		rv = scf_transaction_commit(tran);
2281*a399b765Szf162725 		switch (rv) {
2282*a399b765Szf162725 		case 1:
2283*a399b765Szf162725 			status = DLADM_WLAN_SVC_SUCCESS;
2284*a399b765Szf162725 			goto out;
2285*a399b765Szf162725 		case 0:
2286*a399b765Szf162725 			scf_transaction_destroy_children(tran);
2287*a399b765Szf162725 			if (scf_pg_update(pg) == -1) {
2288*a399b765Szf162725 				goto out;
2289*a399b765Szf162725 			}
2290*a399b765Szf162725 			break;
2291*a399b765Szf162725 		case -1:
2292*a399b765Szf162725 		default:
2293*a399b765Szf162725 			goto out;
2294*a399b765Szf162725 		}
2295*a399b765Szf162725 	} while (rv == 0);
2296*a399b765Szf162725 
2297*a399b765Szf162725 out:
2298*a399b765Szf162725 	if (tran != NULL) {
2299*a399b765Szf162725 		scf_transaction_destroy_children(tran);
2300*a399b765Szf162725 		scf_transaction_destroy(tran);
2301*a399b765Szf162725 	}
2302*a399b765Szf162725 
2303*a399b765Szf162725 	if (pg != NULL)
2304*a399b765Szf162725 		scf_pg_destroy(pg);
2305*a399b765Szf162725 
2306*a399b765Szf162725 	if (command != NULL)
2307*a399b765Szf162725 		free(command);
2308*a399b765Szf162725 
2309*a399b765Szf162725 	return (status);
2310*a399b765Szf162725 }
2311*a399b765Szf162725 
2312*a399b765Szf162725 static int
2313*a399b765Szf162725 do_create_instance(scf_handle_t *handle, scf_service_t *svc,
2314*a399b765Szf162725     const char *instance_name, const char *command)
2315*a399b765Szf162725 {
2316*a399b765Szf162725 	int status = DLADM_WLAN_SVC_FAILURE;
2317*a399b765Szf162725 	char *buf;
2318*a399b765Szf162725 	ssize_t max_fmri_len;
2319*a399b765Szf162725 	scf_instance_t *instance;
2320*a399b765Szf162725 
2321*a399b765Szf162725 	instance = scf_instance_create(handle);
2322*a399b765Szf162725 	if (instance == NULL)
2323*a399b765Szf162725 		goto out;
2324*a399b765Szf162725 
2325*a399b765Szf162725 	if (scf_service_add_instance(svc, instance_name, instance) != 0) {
2326*a399b765Szf162725 		if (scf_error() == SCF_ERROR_EXISTS)
2327*a399b765Szf162725 			/* Let the caller deal with the duplicate instance */
2328*a399b765Szf162725 			status = DLADM_WLAN_SVC_INSTANCE_EXISTS;
2329*a399b765Szf162725 		goto out;
2330*a399b765Szf162725 	}
2331*a399b765Szf162725 
2332*a399b765Szf162725 	if (add_pg_method(handle, instance, "start",
2333*a399b765Szf162725 	    command) != DLADM_WLAN_SVC_SUCCESS) {
2334*a399b765Szf162725 		goto out;
2335*a399b765Szf162725 	}
2336*a399b765Szf162725 
2337*a399b765Szf162725 	/* enabling the instance */
2338*a399b765Szf162725 	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2339*a399b765Szf162725 	if ((buf = malloc(max_fmri_len + 1)) == NULL)
2340*a399b765Szf162725 		goto out;
2341*a399b765Szf162725 
2342*a399b765Szf162725 	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
2343*a399b765Szf162725 		if ((smf_disable_instance(buf, 0) != 0) ||
2344*a399b765Szf162725 		    (smf_enable_instance(buf, SMF_TEMPORARY) != 0)) {
2345*a399b765Szf162725 			goto out;
2346*a399b765Szf162725 		}
2347*a399b765Szf162725 		status = DLADM_WLAN_SVC_SUCCESS;
2348*a399b765Szf162725 	}
2349*a399b765Szf162725 
2350*a399b765Szf162725 out:
2351*a399b765Szf162725 	if (instance != NULL)
2352*a399b765Szf162725 		scf_instance_destroy(instance);
2353*a399b765Szf162725 	return (status);
2354*a399b765Szf162725 }
2355*a399b765Szf162725 
2356*a399b765Szf162725 static int
2357*a399b765Szf162725 create_instance(const char *instance_name, const char *command)
2358*a399b765Szf162725 {
2359*a399b765Szf162725 	int status = DLADM_WLAN_SVC_FAILURE;
2360*a399b765Szf162725 	scf_service_t *svc = NULL;
2361*a399b765Szf162725 	scf_handle_t *handle = NULL;
2362*a399b765Szf162725 
2363*a399b765Szf162725 	handle = scf_handle_create(SCF_VERSION);
2364*a399b765Szf162725 	if (handle == NULL)
2365*a399b765Szf162725 		goto out;
2366*a399b765Szf162725 
2367*a399b765Szf162725 	if (scf_handle_bind(handle) == -1)
2368*a399b765Szf162725 		goto out;
2369*a399b765Szf162725 
2370*a399b765Szf162725 	if ((svc = scf_service_create(handle)) == NULL)
2371*a399b765Szf162725 		goto out;
2372*a399b765Szf162725 
2373*a399b765Szf162725 	if (scf_handle_decode_fmri(handle, SERVICE_NAME, NULL, svc,
2374*a399b765Szf162725 	    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
2375*a399b765Szf162725 		goto out;
2376*a399b765Szf162725 
2377*a399b765Szf162725 	status = do_create_instance(handle, svc, instance_name, command);
2378*a399b765Szf162725 
2379*a399b765Szf162725 out:
2380*a399b765Szf162725 	if (svc != NULL)
2381*a399b765Szf162725 		scf_service_destroy(svc);
2382*a399b765Szf162725 
2383*a399b765Szf162725 	if (handle != NULL) {
2384*a399b765Szf162725 		(void) scf_handle_unbind(handle);
2385*a399b765Szf162725 		scf_handle_destroy(handle);
2386*a399b765Szf162725 	}
2387*a399b765Szf162725 
2388*a399b765Szf162725 	return (status);
2389*a399b765Szf162725 }
2390*a399b765Szf162725 
2391*a399b765Szf162725 /*
2392*a399b765Szf162725  * routines of delete instance
2393*a399b765Szf162725  */
2394*a399b765Szf162725 #define	DEFAULT_TIMEOUT	60000000
2395*a399b765Szf162725 #define	INIT_WAIT_USECS	50000
2396*a399b765Szf162725 
2397*a399b765Szf162725 static void
2398*a399b765Szf162725 wait_until_disabled(scf_handle_t *handle, char *fmri)
2399*a399b765Szf162725 {
2400*a399b765Szf162725 	char		*state;
2401*a399b765Szf162725 	useconds_t	max;
2402*a399b765Szf162725 	useconds_t	usecs;
2403*a399b765Szf162725 	uint64_t	*cp = NULL;
2404*a399b765Szf162725 	scf_simple_prop_t *sp = NULL;
2405*a399b765Szf162725 
2406*a399b765Szf162725 	max = DEFAULT_TIMEOUT;
2407*a399b765Szf162725 
2408*a399b765Szf162725 	if (((sp = scf_simple_prop_get(handle, fmri, "stop",
2409*a399b765Szf162725 	    SCF_PROPERTY_TIMEOUT)) != NULL) &&
2410*a399b765Szf162725 	    ((cp = scf_simple_prop_next_count(sp)) != NULL) && (*cp != 0))
2411*a399b765Szf162725 		max = (*cp) * 1000000;	/* convert to usecs */
2412*a399b765Szf162725 
2413*a399b765Szf162725 	if (sp != NULL)
2414*a399b765Szf162725 		scf_simple_prop_free(sp);
2415*a399b765Szf162725 
2416*a399b765Szf162725 	for (usecs = INIT_WAIT_USECS; max > 0; max -= usecs) {
2417*a399b765Szf162725 		/* incremental wait */
2418*a399b765Szf162725 		usecs *= 2;
2419*a399b765Szf162725 		usecs = (usecs > max) ? max : usecs;
2420*a399b765Szf162725 
2421*a399b765Szf162725 		(void) usleep(usecs);
2422*a399b765Szf162725 
2423*a399b765Szf162725 		/* Check state after the wait */
2424*a399b765Szf162725 		if ((state = smf_get_state(fmri)) != NULL) {
2425*a399b765Szf162725 			if (strcmp(state, "disabled") == 0)
2426*a399b765Szf162725 				return;
2427*a399b765Szf162725 		}
2428*a399b765Szf162725 	}
2429*a399b765Szf162725 }
2430*a399b765Szf162725 
2431*a399b765Szf162725 static int
2432*a399b765Szf162725 delete_instance(const char *instance_name)
2433*a399b765Szf162725 {
2434*a399b765Szf162725 	int		status = DLADM_WLAN_SVC_FAILURE;
2435*a399b765Szf162725 	char		*buf;
2436*a399b765Szf162725 	ssize_t		max_fmri_len;
2437*a399b765Szf162725 	scf_scope_t	*scope = NULL;
2438*a399b765Szf162725 	scf_service_t	*svc = NULL;
2439*a399b765Szf162725 	scf_handle_t	*handle = NULL;
2440*a399b765Szf162725 	scf_instance_t	*instance;
2441*a399b765Szf162725 
2442*a399b765Szf162725 	handle = scf_handle_create(SCF_VERSION);
2443*a399b765Szf162725 	if (handle == NULL)
2444*a399b765Szf162725 		goto out;
2445*a399b765Szf162725 
2446*a399b765Szf162725 	if (scf_handle_bind(handle) == -1)
2447*a399b765Szf162725 		goto out;
2448*a399b765Szf162725 
2449*a399b765Szf162725 	if ((scope = scf_scope_create(handle)) == NULL)
2450*a399b765Szf162725 		goto out;
2451*a399b765Szf162725 
2452*a399b765Szf162725 	if ((svc = scf_service_create(handle)) == NULL)
2453*a399b765Szf162725 		goto out;
2454*a399b765Szf162725 
2455*a399b765Szf162725 	if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) == -1)
2456*a399b765Szf162725 		goto out;
2457*a399b765Szf162725 
2458*a399b765Szf162725 	if (scf_scope_get_service(scope, SERVICE_NAME, svc) < 0)
2459*a399b765Szf162725 		goto out;
2460*a399b765Szf162725 
2461*a399b765Szf162725 	instance = scf_instance_create(handle);
2462*a399b765Szf162725 	if (instance == NULL)
2463*a399b765Szf162725 		goto out;
2464*a399b765Szf162725 
2465*a399b765Szf162725 	if (scf_service_get_instance(svc, instance_name, instance) != 0) {
2466*a399b765Szf162725 		scf_error_t scf_errnum = scf_error();
2467*a399b765Szf162725 
2468*a399b765Szf162725 		if (scf_errnum == SCF_ERROR_NOT_FOUND)
2469*a399b765Szf162725 			status = DLADM_WLAN_SVC_SUCCESS;
2470*a399b765Szf162725 
2471*a399b765Szf162725 		scf_instance_destroy(instance);
2472*a399b765Szf162725 		goto out;
2473*a399b765Szf162725 	}
2474*a399b765Szf162725 
2475*a399b765Szf162725 	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2476*a399b765Szf162725 	if ((buf = malloc(max_fmri_len + 1)) == NULL) {
2477*a399b765Szf162725 		scf_instance_destroy(instance);
2478*a399b765Szf162725 		goto out;
2479*a399b765Szf162725 	}
2480*a399b765Szf162725 
2481*a399b765Szf162725 	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
2482*a399b765Szf162725 		char *state;
2483*a399b765Szf162725 
2484*a399b765Szf162725 		state = smf_get_state(buf);
2485*a399b765Szf162725 		if (state && (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
2486*a399b765Szf162725 		    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)) {
2487*a399b765Szf162725 			if (smf_disable_instance(buf, 0) == 0) {
2488*a399b765Szf162725 				/*
2489*a399b765Szf162725 				 * Wait for some time till timeout to avoid
2490*a399b765Szf162725 				 * a race with scf_instance_delete() below.
2491*a399b765Szf162725 				 */
2492*a399b765Szf162725 				wait_until_disabled(handle, buf);
2493*a399b765Szf162725 			}
2494*a399b765Szf162725 		}
2495*a399b765Szf162725 	}
2496*a399b765Szf162725 
2497*a399b765Szf162725 	if (scf_instance_delete(instance) != 0) {
2498*a399b765Szf162725 		scf_instance_destroy(instance);
2499*a399b765Szf162725 		goto out;
2500*a399b765Szf162725 	}
2501*a399b765Szf162725 
2502*a399b765Szf162725 	scf_instance_destroy(instance);
2503*a399b765Szf162725 
2504*a399b765Szf162725 	status = DLADM_WLAN_SVC_SUCCESS;
2505*a399b765Szf162725 
2506*a399b765Szf162725 out:
2507*a399b765Szf162725 	if (svc != NULL)
2508*a399b765Szf162725 		scf_service_destroy(svc);
2509*a399b765Szf162725 
2510*a399b765Szf162725 	if (scope != NULL)
2511*a399b765Szf162725 		scf_scope_destroy(scope);
2512*a399b765Szf162725 
2513*a399b765Szf162725 	if (handle != NULL) {
2514*a399b765Szf162725 		(void) scf_handle_unbind(handle);
2515*a399b765Szf162725 		scf_handle_destroy(handle);
2516*a399b765Szf162725 	}
2517*a399b765Szf162725 
2518*a399b765Szf162725 	return (status);
2519*a399b765Szf162725 }
2520*a399b765Szf162725 
2521*a399b765Szf162725 /*
2522*a399b765Szf162725  * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
2523*a399b765Szf162725  */
2524*a399b765Szf162725 static int
2525*a399b765Szf162725 wpa_instance_create(const char *instance_name, void *key)
2526*a399b765Szf162725 {
2527*a399b765Szf162725 	int		status = DLADM_WLAN_SVC_FAILURE;
2528*a399b765Szf162725 	char		*command = NULL;
2529*a399b765Szf162725 	char		*wk_name = ((dladm_wlan_key_t *)key)->wk_name;
2530*a399b765Szf162725 	int		size;
2531*a399b765Szf162725 
2532*a399b765Szf162725 	size = strlen(instance_name) + strlen(" -i  -k ") + strlen(wk_name) + 1;
2533*a399b765Szf162725 	command = malloc(size);
2534*a399b765Szf162725 	if (command == NULL) {
2535*a399b765Szf162725 		status = DLADM_WLAN_SVC_APP_FAILURE;
2536*a399b765Szf162725 		goto out;
2537*a399b765Szf162725 	}
2538*a399b765Szf162725 	(void) snprintf(command, size, "-i %s -k %s", instance_name, wk_name);
2539*a399b765Szf162725 
2540*a399b765Szf162725 	status = create_instance(instance_name, command);
2541*a399b765Szf162725 	if (status == DLADM_WLAN_SVC_INSTANCE_EXISTS) {
2542*a399b765Szf162725 		/*
2543*a399b765Szf162725 		 * Delete the existing instance and create a new instance
2544*a399b765Szf162725 		 * with the supplied arguments.
2545*a399b765Szf162725 		 */
2546*a399b765Szf162725 		if ((status = delete_instance(instance_name)) ==
2547*a399b765Szf162725 		    DLADM_WLAN_SVC_SUCCESS) {
2548*a399b765Szf162725 			status = create_instance(instance_name, command);
2549*a399b765Szf162725 		}
2550*a399b765Szf162725 	}
2551*a399b765Szf162725 
2552*a399b765Szf162725 out:
2553*a399b765Szf162725 	if (command != NULL)
2554*a399b765Szf162725 		free(command);
2555*a399b765Szf162725 
2556*a399b765Szf162725 	return (status);
2557*a399b765Szf162725 }
2558*a399b765Szf162725 
2559*a399b765Szf162725 static int
2560*a399b765Szf162725 wpa_instance_delete(const char *instance_name)
2561*a399b765Szf162725 {
2562*a399b765Szf162725 	int status;
2563*a399b765Szf162725 
2564*a399b765Szf162725 	status = delete_instance(instance_name);
2565*a399b765Szf162725 
2566*a399b765Szf162725 	return (status);
2567*a399b765Szf162725 }
2568