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