• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Common function shared by Linux WEXT, cfg80211 and p2p drivers
3  *
4  * Copyright (C) 1999-2019, 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
16  * of the license of that module.  An independent module is a module which is
17  * not derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  *
25  * <<Broadcom-WL-IPTag/Open:>>
26  *
27  * $Id: wldev_common.c 786015 2018-10-24 08:21:53Z $
28  */
29 
30 #include <osl.h>
31 #include <linux/kernel.h>
32 #include <linux/kthread.h>
33 #include <linux/netdevice.h>
34 
35 #include <wldev_common.h>
36 #include <bcmutils.h>
37 #ifdef WL_CFG80211
38 #include <wl_cfg80211.h>
39 #include <wl_cfgscan.h>
40 #endif /* WL_CFG80211 */
41 #include <dhd_config.h>
42 
43 #define htod32(i) (i)
44 #define htod16(i) (i)
45 #define dtoh32(i) (i)
46 #define dtoh16(i) (i)
47 #define htodchanspec(i) (i)
48 #define dtohchanspec(i) (i)
49 
50 #define WLDEV_ERROR_MSG(x, args...)                                            \
51     do {                                                                       \
52         printk(KERN_INFO DHD_LOG_PREFIXS "WLDEV-ERROR) " x, ##args);           \
53     } while (0)
54 #define WLDEV_ERROR(x) WLDEV_ERROR_MSG x
55 
56 #define WLDEV_INFO_MSG(x, args...)                                             \
57     do {                                                                       \
58         printk(KERN_INFO DHD_LOG_PREFIXS "WLDEV-INFO) " x, ##args);            \
59     } while (0)
60 #define WLDEV_INFO(x) WLDEV_INFO_MSG x
61 
62 extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc,
63                                  int cmd);
64 
wldev_ioctl(struct net_device * dev,u32 cmd,void * arg,u32 len,u32 set)65 s32 wldev_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
66 {
67     s32 ret = 0;
68     struct wl_ioctl ioc;
69 
70     memset(&ioc, 0, sizeof(ioc));
71     ioc.cmd = cmd;
72     ioc.buf = arg;
73     ioc.len = len;
74     ioc.set = set;
75     ret = dhd_ioctl_entry_local(dev, (wl_ioctl_t *)&ioc, cmd);
76 
77     return ret;
78 }
79 
80 /*
81 SET commands :
82 cast buffer to non-const  and call the GET function
83 */
84 
wldev_ioctl_set(struct net_device * dev,u32 cmd,const void * arg,u32 len)85 s32 wldev_ioctl_set(struct net_device *dev, u32 cmd, const void *arg, u32 len)
86 {
87 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
88 #pragma GCC diagnostic push
89 #pragma GCC diagnostic ignored "-Wcast-qual"
90 #endif // endif
91     return wldev_ioctl(dev, cmd, (void *)arg, len, 1);
92 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
93 #pragma GCC diagnostic pop
94 #endif // endif
95 }
96 
wldev_ioctl_get(struct net_device * dev,u32 cmd,void * arg,u32 len)97 s32 wldev_ioctl_get(struct net_device *dev, u32 cmd, void *arg, u32 len)
98 {
99     return wldev_ioctl(dev, cmd, (void *)arg, len, 0);
100 }
101 
102 /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
103  * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
104  * wl_iw, wl_cfg80211 and wl_cfgp2p
105  */
wldev_mkiovar(const s8 * iovar_name,const s8 * param,s32 paramlen,s8 * iovar_buf,u32 buflen)106 static s32 wldev_mkiovar(const s8 *iovar_name, const s8 *param, s32 paramlen,
107                          s8 *iovar_buf, u32 buflen)
108 {
109     s32 iolen = 0;
110 
111     iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
112     return iolen;
113 }
114 
wldev_iovar_getbuf(struct net_device * dev,s8 * iovar_name,const void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)115 s32 wldev_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
116                        const void *param, s32 paramlen, void *buf, s32 buflen,
117                        struct mutex *buf_sync)
118 {
119     s32 ret = 0;
120     if (buf_sync) {
121         mutex_lock(buf_sync);
122     }
123 
124     if (buf && (buflen > 0)) {
125         /* initialize the response buffer */
126         memset(buf, 0, buflen);
127     } else {
128         ret = BCME_BADARG;
129         goto exit;
130     }
131 
132     ret = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
133     if (!ret) {
134         ret = BCME_BUFTOOSHORT;
135         goto exit;
136     }
137     ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
138 exit:
139     if (buf_sync) {
140         mutex_unlock(buf_sync);
141     }
142     return ret;
143 }
144 
wldev_iovar_setbuf(struct net_device * dev,s8 * iovar_name,const void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)145 s32 wldev_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
146                        const void *param, s32 paramlen, void *buf, s32 buflen,
147                        struct mutex *buf_sync)
148 {
149     s32 ret = 0;
150     s32 iovar_len;
151     if (buf_sync) {
152         mutex_lock(buf_sync);
153     }
154     iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
155     if (iovar_len > 0) {
156         ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
157     } else {
158         ret = BCME_BUFTOOSHORT;
159     }
160 
161     if (buf_sync) {
162         mutex_unlock(buf_sync);
163     }
164     return ret;
165 }
166 
wldev_iovar_setint(struct net_device * dev,s8 * iovar,s32 val)167 s32 wldev_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
168 {
169     s8 iovar_buf[WLC_IOCTL_SMLEN];
170 
171     val = htod32(val);
172     memset(iovar_buf, 0, sizeof(iovar_buf));
173     return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
174                               sizeof(iovar_buf), NULL);
175 }
176 
wldev_iovar_getint(struct net_device * dev,s8 * iovar,s32 * pval)177 s32 wldev_iovar_getint(struct net_device *dev, s8 *iovar, s32 *pval)
178 {
179     s8 iovar_buf[WLC_IOCTL_SMLEN];
180     s32 err;
181 
182     memset(iovar_buf, 0, sizeof(iovar_buf));
183     err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
184                              sizeof(iovar_buf), NULL);
185     if (err == 0) {
186         memcpy(pval, iovar_buf, sizeof(*pval));
187         *pval = dtoh32(*pval);
188     }
189     return err;
190 }
191 
192 /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
193  *  taken care of in dhd_ioctl_entry. Internal use only, not exposed to
194  *  wl_iw, wl_cfg80211 and wl_cfgp2p
195  */
wldev_mkiovar_bsscfg(const s8 * iovar_name,const s8 * param,s32 paramlen,s8 * iovar_buf,s32 buflen,s32 bssidx)196 s32 wldev_mkiovar_bsscfg(const s8 *iovar_name, const s8 *param, s32 paramlen,
197                          s8 *iovar_buf, s32 buflen, s32 bssidx)
198 {
199     const s8 *prefix = "bsscfg:";
200     s8 *p;
201     u32 prefixlen;
202     u32 namelen;
203     u32 iolen;
204 
205     /* initialize buffer */
206     if (!iovar_buf || buflen <= 0) {
207         return BCME_BADARG;
208     }
209     memset(iovar_buf, 0, buflen);
210 
211     if (bssidx == 0) {
212         return wldev_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
213     }
214 
215     prefixlen = (u32)strlen(prefix);       /* lengh of bsscfg prefix */
216     namelen = (u32)strlen(iovar_name) + 1; /* lengh of iovar  name + null */
217     iolen = prefixlen + namelen + sizeof(u32) + paramlen;
218 
219     if (iolen > (u32)buflen) {
220         WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__));
221         return BCME_BUFTOOSHORT;
222     }
223 
224     p = (s8 *)iovar_buf;
225 
226     /* copy prefix, no null */
227     memcpy(p, prefix, prefixlen);
228     p += prefixlen;
229 
230     /* copy iovar name including null */
231     memcpy(p, iovar_name, namelen);
232     p += namelen;
233 
234     /* bss config index as first param */
235     bssidx = htod32(bssidx);
236     memcpy(p, &bssidx, sizeof(u32));
237     p += sizeof(u32);
238 
239     /* parameter buffer follows */
240     if (paramlen) {
241         memcpy(p, param, paramlen);
242     }
243 
244     return iolen;
245 }
246 
wldev_iovar_getbuf_bsscfg(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)247 s32 wldev_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
248                               void *param, s32 paramlen, void *buf, s32 buflen,
249                               s32 bsscfg_idx, struct mutex *buf_sync)
250 {
251     s32 ret = 0;
252     if (buf_sync) {
253         mutex_lock(buf_sync);
254     }
255 
256     wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
257     ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
258     if (buf_sync) {
259         mutex_unlock(buf_sync);
260     }
261     return ret;
262 }
263 
wldev_iovar_setbuf_bsscfg(struct net_device * dev,const s8 * iovar_name,const void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)264 s32 wldev_iovar_setbuf_bsscfg(struct net_device *dev, const s8 *iovar_name,
265                               const void *param, s32 paramlen, void *buf,
266                               s32 buflen, s32 bsscfg_idx,
267                               struct mutex *buf_sync)
268 {
269     s32 ret = 0;
270     s32 iovar_len;
271     if (buf_sync) {
272         mutex_lock(buf_sync);
273     }
274     iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen,
275                                      bsscfg_idx);
276     if (iovar_len > 0) {
277         ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
278     } else {
279         ret = BCME_BUFTOOSHORT;
280     }
281 
282     if (buf_sync) {
283         mutex_unlock(buf_sync);
284     }
285     return ret;
286 }
287 
wldev_iovar_setint_bsscfg(struct net_device * dev,s8 * iovar,s32 val,s32 bssidx)288 s32 wldev_iovar_setint_bsscfg(struct net_device *dev, s8 *iovar, s32 val,
289                               s32 bssidx)
290 {
291     s8 iovar_buf[WLC_IOCTL_SMLEN];
292 
293     val = htod32(val);
294     memset(iovar_buf, 0, sizeof(iovar_buf));
295     return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
296                                      sizeof(iovar_buf), bssidx, NULL);
297 }
298 
wldev_iovar_getint_bsscfg(struct net_device * dev,s8 * iovar,s32 * pval,s32 bssidx)299 s32 wldev_iovar_getint_bsscfg(struct net_device *dev, s8 *iovar, s32 *pval,
300                               s32 bssidx)
301 {
302     s8 iovar_buf[WLC_IOCTL_SMLEN];
303     s32 err;
304 
305     memset(iovar_buf, 0, sizeof(iovar_buf));
306     err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
307                                     sizeof(iovar_buf), bssidx, NULL);
308     if (err == 0) {
309         memcpy(pval, iovar_buf, sizeof(*pval));
310         *pval = dtoh32(*pval);
311     }
312     return err;
313 }
314 
wldev_get_link_speed(struct net_device * dev,int * plink_speed)315 int wldev_get_link_speed(struct net_device *dev, int *plink_speed)
316 {
317     int error;
318 
319     if (!plink_speed) {
320         return -ENOMEM;
321     }
322     *plink_speed = 0;
323     error = wldev_ioctl_get(dev, WLC_GET_RATE, plink_speed, sizeof(int));
324     if (unlikely(error)) {
325         return error;
326     }
327 
328     /* Convert internal 500Kbps to Kbps */
329     *plink_speed *= 500;
330     return error;
331 }
332 
wldev_get_rssi(struct net_device * dev,scb_val_t * scb_val)333 int wldev_get_rssi(struct net_device *dev, scb_val_t *scb_val)
334 {
335     int error;
336 
337     if (!scb_val) {
338         return -ENOMEM;
339     }
340     memset(scb_val, 0, sizeof(scb_val_t));
341     error = wldev_ioctl_get(dev, WLC_GET_RSSI, scb_val, sizeof(scb_val_t));
342     if (unlikely(error)) {
343         return error;
344     }
345 
346     return error;
347 }
348 
wldev_get_ssid(struct net_device * dev,wlc_ssid_t * pssid)349 int wldev_get_ssid(struct net_device *dev, wlc_ssid_t *pssid)
350 {
351     int error;
352 
353     if (!pssid) {
354         return -ENOMEM;
355     }
356     memset(pssid, 0, sizeof(wlc_ssid_t));
357     error = wldev_ioctl_get(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t));
358     if (unlikely(error)) {
359         return error;
360     }
361     pssid->SSID_len = dtoh32(pssid->SSID_len);
362     return error;
363 }
364 
wldev_get_band(struct net_device * dev,uint * pband)365 int wldev_get_band(struct net_device *dev, uint *pband)
366 {
367     int error;
368 
369     *pband = 0;
370     error = wldev_ioctl_get(dev, WLC_GET_BAND, pband, sizeof(uint));
371     return error;
372 }
373 
wldev_set_band(struct net_device * dev,uint band)374 int wldev_set_band(struct net_device *dev, uint band)
375 {
376     int error = -1;
377 
378     if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) ||
379         (band == WLC_BAND_2G)) {
380         error = wldev_ioctl_set(dev, WLC_SET_BAND, &band, sizeof(band));
381         if (!error) {
382             dhd_bus_band_set(dev, band);
383         }
384     }
385     return error;
386 }
wldev_get_datarate(struct net_device * dev,int * datarate)387 int wldev_get_datarate(struct net_device *dev, int *datarate)
388 {
389     int error = 0;
390 
391     error = wldev_ioctl_get(dev, WLC_GET_RATE, datarate, sizeof(int));
392     if (error) {
393         return -1;
394     } else {
395         *datarate = dtoh32(*datarate);
396     }
397 
398     return error;
399 }
400 
401 #ifdef WL_CFG80211
402 extern chanspec_t wl_chspec_driver_to_host(chanspec_t chanspec);
403 #define WL_EXTRA_BUF_MAX 2048
wldev_get_mode(struct net_device * dev,uint8 * cap,uint8 caplen)404 int wldev_get_mode(struct net_device *dev, uint8 *cap, uint8 caplen)
405 {
406     int error = 0;
407     int chanspec = 0;
408     uint16 band = 0;
409     uint16 bandwidth = 0;
410     wl_bss_info_t *bss = NULL;
411     char *buf = NULL;
412 
413     buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
414     if (!buf) {
415         WLDEV_ERROR(("%s:ENOMEM\n", __FUNCTION__));
416         return -ENOMEM;
417     }
418 
419     *(u32 *)buf = htod32(WL_EXTRA_BUF_MAX);
420     error =
421         wldev_ioctl_get(dev, WLC_GET_BSS_INFO, (void *)buf, WL_EXTRA_BUF_MAX);
422     if (error) {
423         WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__, error));
424         kfree(buf);
425         buf = NULL;
426         return error;
427     }
428     bss = (wl_bss_info_t *)(buf + 0x4);
429     chanspec = wl_chspec_driver_to_host(bss->chanspec);
430 
431     band = chanspec & WL_CHANSPEC_BAND_MASK;
432     bandwidth = chanspec & WL_CHANSPEC_BW_MASK;
433 
434     if (band == WL_CHANSPEC_BAND_2G) {
435         if (bss->n_cap) {
436             strncpy(cap, "n", caplen);
437         } else {
438             strncpy(cap, "bg", caplen);
439         }
440     } else if (band == WL_CHANSPEC_BAND_5G) {
441         if (bandwidth == WL_CHANSPEC_BW_80) {
442             strncpy(cap, "ac", caplen);
443         } else if ((bandwidth == WL_CHANSPEC_BW_40) ||
444                    (bandwidth == WL_CHANSPEC_BW_20)) {
445             if ((bss->nbss_cap & 0xf00) && (bss->n_cap)) {
446                 strncpy(cap, "n|ac", caplen);
447             } else if (bss->n_cap) {
448                 strncpy(cap, "n", caplen);
449             } else if (bss->vht_cap) {
450                 strncpy(cap, "ac", caplen);
451             } else {
452                 strncpy(cap, "a", caplen);
453             }
454         } else {
455             WLDEV_ERROR(("%s:Mode get failed\n", __FUNCTION__));
456             error = BCME_ERROR;
457         }
458     }
459     kfree(buf);
460     buf = NULL;
461     return error;
462 }
463 #endif
464 
wldev_set_country(struct net_device * dev,char * country_code,bool notify,bool user_enforced,int revinfo)465 int wldev_set_country(struct net_device *dev, char *country_code, bool notify,
466                       bool user_enforced, int revinfo)
467 {
468     int error = -1;
469     wl_country_t cspec = {{0}, 0, {0}};
470     scb_val_t scbval;
471 #ifdef WL_CFG80211
472     struct wireless_dev *wdev = ndev_to_wdev(dev);
473     struct wiphy *wiphy = wdev->wiphy;
474     struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
475 #endif /* WL_CFG80211 */
476 
477     if (!country_code) {
478         return error;
479     }
480 
481     bzero(&scbval, sizeof(scb_val_t));
482     error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec),
483                                NULL);
484     if (error < 0) {
485         WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error));
486         return error;
487     }
488 
489     if ((error < 0) || dhd_force_country_change(dev) ||
490         (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) {
491 #ifdef WL_CFG80211
492         if ((user_enforced) && (wl_get_drv_status(cfg, CONNECTED, dev)))
493 #else
494         if (user_enforced)
495 #endif /* WL_CFG80211 */
496         {
497             bzero(&scbval, sizeof(scb_val_t));
498             error =
499                 wldev_ioctl_set(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
500             if (error < 0) {
501                 WLDEV_ERROR(
502                     ("%s: set country failed due to Disassoc error %d\n",
503                      __FUNCTION__, error));
504                 return error;
505             }
506         }
507 
508 #ifdef WL_CFG80211
509         wl_cfg80211_scan_abort(cfg);
510 #endif
511 
512         cspec.rev = revinfo;
513         strlcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
514         strlcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
515         error = dhd_conf_map_country_list(dhd_get_pub(dev), &cspec);
516         if (error) {
517             dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev,
518                                             &cspec);
519         }
520         error = dhd_conf_set_country(dhd_get_pub(dev), &cspec);
521         if (error < 0) {
522             WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
523                          __FUNCTION__, country_code, cspec.ccode, cspec.rev));
524             return error;
525         }
526         dhd_conf_fix_country(dhd_get_pub(dev));
527         dhd_conf_get_country(dhd_get_pub(dev), &cspec);
528         dhd_bus_country_set(dev, &cspec, notify);
529         printf("%s: set country for %s as %s rev %d\n", __FUNCTION__,
530                country_code, cspec.ccode, cspec.rev);
531     }
532     return 0;
533 }
534