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