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