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