xref: /OK3568_Linux_fs/external/rkwifibt/drivers/infineon/dhd_cfg80211.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Linux cfg80211 driver - Dongle Host Driver (DHD) related
3  *
4  * Portions of this code are copyright (c) 2021 Cypress Semiconductor Corporation
5  *
6  * Copyright (C) 1999-2017, Broadcom Corporation
7  *
8  *      Unless you and Broadcom execute a separate written software license
9  * agreement governing use of this software, this software is licensed to you
10  * under the terms of the GNU General Public License version 2 (the "GPL"),
11  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12  * following added to such license:
13  *
14  *      As a special exception, the copyright holders of this software give you
15  * permission to link this software with independent modules, and to copy and
16  * distribute the resulting executable under terms of your choice, provided that
17  * you also meet, for each linked independent module, the terms and conditions of
18  * the license of that module.  An independent module is a module which is not
19  * derived from this software.  The special exception does not apply to any
20  * modifications of the software.
21  *
22  *      Notwithstanding the above, under no circumstances may you combine this
23  * software in any way with any other Broadcom software provided under a license
24  * other than the GPL, without Broadcom's express prior written consent.
25  *
26  *
27  * <<Broadcom-WL-IPTag/Open:>>
28  *
29  * $Id: dhd_cfg80211.c 696903 2017-04-28 19:48:01Z $
30  */
31 
32 #include <linux/vmalloc.h>
33 #include <net/rtnetlink.h>
34 
35 #include <bcmutils.h>
36 #include <wldev_common.h>
37 #include <wl_cfg80211.h>
38 #include <dhd_cfg80211.h>
39 
40 #ifdef PKT_FILTER_SUPPORT
41 #include <dngl_stats.h>
42 #include <dhd.h>
43 #endif // endif
44 
45 #ifdef PKT_FILTER_SUPPORT
46 extern uint dhd_pkt_filter_enable;
47 extern uint dhd_master_mode;
48 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
49 #endif // endif
50 
51 static int dhd_dongle_up = FALSE;
52 
53 #include <dngl_stats.h>
54 #include <dhd.h>
55 #include <dhdioctl.h>
56 #include <wlioctl.h>
57 #include <brcm_nl80211.h>
58 #include <dhd_cfg80211.h>
59 
60 static s32 wl_dongle_up(struct net_device *ndev);
61 static s32 wl_dongle_down(struct net_device *ndev);
62 #ifndef OEM_ANDROID
63 static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode);
64 #ifdef BCMSDIO /* glomming is a sdio specific feature */
65 static s32 wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align);
66 #endif // endif
67 static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, s32 scan_unassoc_time);
68 static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol);
69 static s32 wl_pattern_atoh(s8 *src, s8 *dst);
70 static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode);
71 #endif /* OEM_ANDROID */
72 
73 /**
74  * Function implementations
75  */
76 
dhd_cfg80211_init(struct bcm_cfg80211 * cfg)77 s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
78 {
79 	dhd_dongle_up = FALSE;
80 	return 0;
81 }
82 
dhd_cfg80211_deinit(struct bcm_cfg80211 * cfg)83 s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
84 {
85 	dhd_dongle_up = FALSE;
86 	return 0;
87 }
88 
dhd_cfg80211_down(struct bcm_cfg80211 * cfg)89 s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
90 {
91 	struct net_device *ndev;
92 	s32 err = 0;
93 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
94 
95 	WL_TRACE(("In\n"));
96 	if ((!dhd_dongle_up) || (!dhd->up)) {
97 		WL_INFORM_MEM(("Dongle is already down\n"));
98 		err = 0;
99 		goto done;
100 	}
101 	ndev = bcmcfg_to_prmry_ndev(cfg);
102 	wl_dongle_down(ndev);
103 done:
104 	dhd_dongle_up = FALSE;
105 	return err;
106 }
107 
dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 * cfg,int val)108 s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
109 {
110 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
111 	dhd->op_mode |= val;
112 	WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
113 #ifdef ARP_OFFLOAD_SUPPORT
114 	if (dhd->arp_version == 1) {
115 		/* IF P2P is enabled, disable arpoe */
116 		dhd_arp_offload_set(dhd, 0);
117 		dhd_arp_offload_enable(dhd, false);
118 	}
119 #endif /* ARP_OFFLOAD_SUPPORT */
120 
121 	return 0;
122 }
123 
dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 * cfg)124 s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
125 {
126 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
127 	dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
128 	WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
129 
130 #ifdef ARP_OFFLOAD_SUPPORT
131 	if (dhd->arp_version == 1) {
132 		/* IF P2P is disabled, enable arpoe back for STA mode. */
133 		dhd_arp_offload_set(dhd, dhd_arp_mode);
134 		dhd_arp_offload_enable(dhd, true);
135 	}
136 #endif /* ARP_OFFLOAD_SUPPORT */
137 
138 	return 0;
139 }
140 
141 #ifdef WL_STATIC_IF
142 int32
wl_cfg80211_update_iflist_info(struct bcm_cfg80211 * cfg,struct net_device * ndev,int ifidx,uint8 * addr,int bssidx,char * name,int if_state)143 wl_cfg80211_update_iflist_info(struct bcm_cfg80211 *cfg, struct net_device *ndev,
144 	int ifidx, uint8 *addr, int bssidx, char *name, int if_state)
145 {
146 	return dhd_update_iflist_info(cfg->pub, ndev, ifidx, addr, bssidx, name, if_state);
147 }
148 #endif /* WL_STATIC_IF */
149 
wl_cfg80211_allocate_if(struct bcm_cfg80211 * cfg,int ifidx,const char * name,uint8 * mac,uint8 bssidx,const char * dngl_name)150 struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name,
151 	uint8 *mac, uint8 bssidx, const char *dngl_name)
152 {
153 	return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name);
154 }
155 
wl_cfg80211_register_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)156 int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg,
157 	int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
158 {
159 	return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd);
160 }
161 
wl_cfg80211_remove_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)162 int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg,
163 	int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
164 {
165 #ifdef DHD_PCIE_RUNTIMEPM
166 	dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
167 #endif /* DHD_PCIE_RUNTIMEPM */
168 	return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd);
169 }
170 
wl_cfg80211_cleanup_if(struct net_device * net)171 void wl_cfg80211_cleanup_if(struct net_device *net)
172 {
173 	struct bcm_cfg80211 *cfg = wl_get_cfg(net);
174 #ifdef DHD_PCIE_RUNTIMEPM
175 	dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
176 #else
177 	BCM_REFERENCE(cfg);
178 #endif /* DHD_PCIE_RUNTIMEPM */
179 	dhd_cleanup_if(net);
180 }
181 
dhd_cfg80211_netdev_free(struct net_device * ndev)182 struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev)
183 {
184 	struct bcm_cfg80211 *cfg;
185 
186 	if (ndev) {
187 		cfg = wl_get_cfg(ndev);
188 		if (ndev->ieee80211_ptr) {
189 			MFREE(cfg->osh, ndev->ieee80211_ptr, sizeof(struct wireless_dev));
190 			ndev->ieee80211_ptr = NULL;
191 		}
192 		free_netdev(ndev);
193 		return NULL;
194 	}
195 
196 	return ndev;
197 }
198 
dhd_netdev_free(struct net_device * ndev)199 void dhd_netdev_free(struct net_device *ndev)
200 {
201 #ifdef WL_CFG80211
202 	ndev = dhd_cfg80211_netdev_free(ndev);
203 #endif // endif
204 	if (ndev)
205 		free_netdev(ndev);
206 }
207 
208 static s32
wl_dongle_up(struct net_device * ndev)209 wl_dongle_up(struct net_device *ndev)
210 {
211 	s32 err = 0;
212 	u32 local_up = 0;
213 
214 	err = wldev_ioctl_set(ndev, WLC_UP, &local_up, sizeof(local_up));
215 	if (unlikely(err)) {
216 		WL_ERR(("WLC_UP error (%d)\n", err));
217 	}
218 	return err;
219 }
220 
221 static s32
wl_dongle_down(struct net_device * ndev)222 wl_dongle_down(struct net_device *ndev)
223 {
224 	s32 err = 0;
225 	u32 local_down = 0;
226 
227 	err = wldev_ioctl_set(ndev, WLC_DOWN, &local_down, sizeof(local_down));
228 	if (unlikely(err)) {
229 		WL_ERR(("WLC_DOWN error (%d)\n", err));
230 	}
231 	return err;
232 }
233 
234 #ifndef OEM_ANDROID
wl_dongle_power(struct net_device * ndev,u32 power_mode)235 static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode)
236 {
237 	s32 err = 0;
238 
239 	WL_TRACE(("In\n"));
240 	err = wldev_ioctl_set(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode));
241 	if (unlikely(err)) {
242 		WL_ERR(("WLC_SET_PM error (%d)\n", err));
243 	}
244 	return err;
245 }
246 
247 #ifdef BCMSDIO
248 static s32
wl_dongle_glom(struct net_device * ndev,s32 glom,u32 dongle_align)249 wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align)
250 {
251 	s32 err = 0;
252 
253 	/* Match Host and Dongle rx alignment */
254 	err = wldev_iovar_setint(ndev, "bus:txglomalign", dongle_align);
255 	if (unlikely(err)) {
256 		WL_ERR(("txglomalign error (%d)\n", err));
257 		goto dongle_glom_out;
258 	}
259 	/* disable glom option per default */
260 	if (glom != DEFAULT_GLOM_VALUE) {
261 		err = wldev_iovar_setint(ndev, "bus:txglom", glom);
262 		if (unlikely(err)) {
263 			WL_ERR(("txglom error (%d)\n", err));
264 			goto dongle_glom_out;
265 		}
266 	}
267 dongle_glom_out:
268 	return err;
269 }
270 
271 #endif /* BCMSDIO */
272 #endif /* OEM_ANDROID */
273 
274 s32
wl_dongle_roam(struct net_device * ndev,u32 roamvar,u32 bcn_timeout)275 wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
276 {
277 	s32 err = 0;
278 
279 	/* Setup timeout if Beacons are lost and roam is off to report link down */
280 	if (roamvar) {
281 		err = wldev_iovar_setint(ndev, "bcn_timeout", bcn_timeout);
282 		if (unlikely(err)) {
283 			WL_ERR(("bcn_timeout error (%d)\n", err));
284 			goto dongle_rom_out;
285 		}
286 	}
287 	/* Enable/Disable built-in roaming to allow supplicant to take care of roaming */
288 	err = wldev_iovar_setint(ndev, "roam_off", roamvar);
289 	if (unlikely(err)) {
290 		WL_ERR(("roam_off error (%d)\n", err));
291 		goto dongle_rom_out;
292 	}
293 dongle_rom_out:
294 	return err;
295 }
296 
297 #ifndef OEM_ANDROID
298 static s32
wl_dongle_scantime(struct net_device * ndev,s32 scan_assoc_time,s32 scan_unassoc_time)299 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
300 	s32 scan_unassoc_time)
301 {
302 	s32 err = 0;
303 
304 	err = wldev_ioctl_set(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time,
305 		sizeof(scan_assoc_time));
306 	if (err) {
307 		if (err == -EOPNOTSUPP) {
308 			WL_INFORM(("Scan assoc time is not supported\n"));
309 		} else {
310 			WL_ERR(("Scan assoc time error (%d)\n", err));
311 		}
312 		goto dongle_scantime_out;
313 	}
314 	err = wldev_ioctl_set(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time,
315 		sizeof(scan_unassoc_time));
316 	if (err) {
317 		if (err == -EOPNOTSUPP) {
318 			WL_INFORM(("Scan unassoc time is not supported\n"));
319 		} else {
320 			WL_ERR(("Scan unassoc time error (%d)\n", err));
321 		}
322 		goto dongle_scantime_out;
323 	}
324 
325 dongle_scantime_out:
326 	return err;
327 }
328 
329 static s32
wl_dongle_offload(struct net_device * ndev,s32 arpoe,s32 arp_ol)330 wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol)
331 {
332 	s8 iovbuf[WLC_IOCTL_SMLEN];
333 	s32 err = 0;
334 	s32 len;
335 
336 	/* Set ARP offload */
337 	len = bcm_mkiovar("arpoe", (char *)&arpoe, sizeof(arpoe), iovbuf, sizeof(iovbuf));
338 	if (!len) {
339 		WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
340 		return BCME_BADARG;
341 	}
342 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
343 	if (err) {
344 		if (err == -EOPNOTSUPP)
345 			WL_INFORM(("arpoe is not supported\n"));
346 		else
347 			WL_ERR(("arpoe error (%d)\n", err));
348 
349 		goto dongle_offload_out;
350 	}
351 	len = bcm_mkiovar("arp_ol", (char *)&arp_ol, sizeof(arp_ol), iovbuf, sizeof(iovbuf));
352 	if (!len) {
353 		WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
354 		return BCME_BADARG;
355 	}
356 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
357 	if (err) {
358 		if (err == -EOPNOTSUPP)
359 			WL_INFORM(("arp_ol is not supported\n"));
360 		else
361 			WL_ERR(("arp_ol error (%d)\n", err));
362 
363 		goto dongle_offload_out;
364 	}
365 
366 dongle_offload_out:
367 	return err;
368 }
369 
wl_pattern_atoh(s8 * src,s8 * dst)370 static s32 wl_pattern_atoh(s8 *src, s8 *dst)
371 {
372 	int i;
373 	if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
374 		WL_ERR(("Mask invalid format. Needs to start with 0x\n"));
375 		return -1;
376 	}
377 	src = src + 2;		/* Skip past 0x */
378 	if (strlen(src) % 2 != 0) {
379 		WL_ERR(("Mask invalid format. Needs to be of even length\n"));
380 		return -1;
381 	}
382 	for (i = 0; *src != '\0'; i++) {
383 		char num[3];
384 		strncpy(num, src, 2);
385 		num[2] = '\0';
386 		dst[i] = (u8) simple_strtoul(num, NULL, 16);
387 		src += 2;
388 	}
389 	return i;
390 }
391 
wl_dongle_filter(struct net_device * ndev,u32 filter_mode)392 static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode)
393 {
394 	const s8 *str;
395 	struct wl_pkt_filter pkt_filter;
396 	struct wl_pkt_filter *pkt_filterp;
397 	s32 buf_len;
398 	s32 str_len;
399 	u32 mask_size;
400 	u32 pattern_size;
401 	s8 buf[64] = {0};
402 	s32 err = 0;
403 
404 	/* add a default packet filter pattern */
405 	str = "pkt_filter_add";
406 	str_len = strlen(str);
407 	strncpy(buf, str, sizeof(buf) - 1);
408 	buf[ sizeof(buf) - 1 ] = '\0';
409 	buf_len = str_len + 1;
410 
411 	pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1);
412 
413 	/* Parse packet filter id. */
414 	pkt_filter.id = htod32(100);
415 
416 	/* Parse filter polarity. */
417 	pkt_filter.negate_match = htod32(0);
418 
419 	/* Parse filter type. */
420 	pkt_filter.type = htod32(0);
421 
422 	/* Parse pattern filter offset. */
423 	pkt_filter.u.pattern.offset = htod32(0);
424 
425 	/* Parse pattern filter mask. */
426 	mask_size = htod32(wl_pattern_atoh("0xff",
427 		(char *)pkt_filterp->u.pattern.
428 		    mask_and_pattern));
429 
430 	/* Parse pattern filter pattern. */
431 	pattern_size = htod32(wl_pattern_atoh("0x00",
432 		(char *)&pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
433 
434 	if (mask_size != pattern_size) {
435 		WL_ERR(("Mask and pattern not the same size\n"));
436 		err = -EINVAL;
437 		goto dongle_filter_out;
438 	}
439 
440 	pkt_filter.u.pattern.size_bytes = mask_size;
441 	buf_len += WL_PKT_FILTER_FIXED_LEN;
442 	buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
443 
444 	/* Keep-alive attributes are set in local
445 	 * variable (keep_alive_pkt), and
446 	 * then memcpy'ed into buffer (keep_alive_pktp) since there is no
447 	 * guarantee that the buffer is properly aligned.
448 	 */
449 	memcpy((char *)pkt_filterp, &pkt_filter,
450 		WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
451 
452 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, buf, buf_len);
453 	if (err) {
454 		if (err == -EOPNOTSUPP) {
455 			WL_INFORM(("filter not supported\n"));
456 		} else {
457 			WL_ERR(("filter (%d)\n", err));
458 		}
459 		goto dongle_filter_out;
460 	}
461 
462 	/* set mode to allow pattern */
463 	err = wldev_iovar_setint(ndev, "pkt_filter_mode", filter_mode);
464 	if (err) {
465 		if (err == -EOPNOTSUPP) {
466 			WL_INFORM(("filter_mode not supported\n"));
467 		} else {
468 			WL_ERR(("filter_mode (%d)\n", err));
469 		}
470 		goto dongle_filter_out;
471 	}
472 
473 dongle_filter_out:
474 	return err;
475 }
476 #endif /* OEM_ANDROID */
477 
dhd_config_dongle(struct bcm_cfg80211 * cfg)478 s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
479 {
480 #ifndef DHD_SDALIGN
481 #define DHD_SDALIGN	32
482 #endif // endif
483 	struct net_device *ndev;
484 	s32 err = 0;
485 #if !defined(OEM_ANDROID) && defined(BCMSDIO)
486 	s32 glom = CUSTOM_GLOM_SETTING;
487 #endif // endif
488 
489 	WL_TRACE(("In\n"));
490 	if (dhd_dongle_up) {
491 		WL_ERR(("Dongle is already up\n"));
492 		return err;
493 	}
494 
495 	ndev = bcmcfg_to_prmry_ndev(cfg);
496 
497 	err = wl_dongle_up(ndev);
498 	if (unlikely(err)) {
499 		WL_ERR(("wl_dongle_up failed\n"));
500 		goto default_conf_out;
501 	}
502 #ifndef OEM_ANDROID
503 	err = wl_dongle_power(ndev, PM_FAST);
504 	if (unlikely(err)) {
505 		WL_ERR(("wl_dongle_power failed\n"));
506 		goto default_conf_out;
507 	}
508 #ifdef BCMSDIO
509 	if (glom != DEFAULT_GLOM_VALUE) {
510 		err = wl_dongle_glom(ndev, glom, DHD_SDALIGN);
511 	} else {
512 		err = wl_dongle_glom(ndev, DEFAULT_GLOM_VALUE, DHD_SDALIGN);
513 	}
514 	if (unlikely(err)) {
515 		WL_ERR(("wl_dongle_glom failed\n"));
516 		goto default_conf_out;
517 	}
518 #endif /* BCMSDIO */
519 	err = wl_dongle_roam(ndev, (cfg->roam_on ? 0 : 1), 3);
520 	if (unlikely(err)) {
521 		WL_ERR(("wl_dongle_roam failed\n"));
522 		goto default_conf_out;
523 	}
524 	wl_dongle_scantime(ndev, 40, 80);
525 	wl_dongle_offload(ndev, 1, 0xf);
526 	wl_dongle_filter(ndev, 1);
527 #endif /* OEM_ANDROID */
528 	dhd_dongle_up = true;
529 
530 default_conf_out:
531 
532 	return err;
533 
534 }
535 
dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 * cfg,struct wireless_dev * wdev,const struct bcm_nlmsg_hdr * nlioc,void * buf)536 int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
537 	const struct bcm_nlmsg_hdr *nlioc, void *buf)
538 {
539 	struct net_device *ndev = NULL;
540 	dhd_pub_t *dhd;
541 	dhd_ioctl_t ioc = { 0, NULL, 0, 0, 0, 0, 0};
542 	int ret = 0;
543 	int8 index;
544 
545 	WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
546 
547 	dhd = cfg->pub;
548 	DHD_OS_WAKE_LOCK(dhd);
549 
550 	ndev = wdev_to_wlc_ndev(wdev, cfg);
551 	index = dhd_net2idx(dhd->info, ndev);
552 	if (index == DHD_BAD_IF) {
553 		WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
554 		ret = BCME_ERROR;
555 		goto done;
556 	}
557 
558 	ioc.cmd = nlioc->cmd;
559 	ioc.len = nlioc->len;
560 	ioc.set = nlioc->set;
561 	ioc.driver = nlioc->magic;
562 	ioc.buf = buf;
563 	ret = dhd_ioctl_process(dhd, index, &ioc, buf);
564 	if (ret) {
565 		WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
566 		ret = OSL_ERROR(ret);
567 		goto done;
568 	}
569 
570 done:
571 	DHD_OS_WAKE_UNLOCK(dhd);
572 	return ret;
573 }
574