wifi: renew patch drivers/net/wireless
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rkwifi / bcmdhd / wldev_common.c
1 /*
2  * Common function shared by Linux WEXT, cfg80211 and p2p drivers
3  *
4  * $Copyright Open Broadcom Corporation$
5  *
6  * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $
7  */
8
9 #include <osl.h>
10 #include <linux/kernel.h>
11 #include <linux/kthread.h>
12 #include <linux/netdevice.h>
13
14 #include <wldev_common.h>
15 #include <bcmutils.h>
16
17 #define htod32(i) i
18 #define htod16(i) i
19 #define dtoh32(i) i
20 #define dtoh16(i) i
21 #define htodchanspec(i) i
22 #define dtohchanspec(i) i
23
24 #define WLDEV_ERROR(args)                                               \
25         do {                                                                            \
26                 printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__);        \
27                 printk args;                                                    \
28         } while (0)
29
30 extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd);
31
32 s32 wldev_ioctl(
33         struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
34 {
35         s32 ret = 0;
36         struct wl_ioctl ioc;
37
38
39         memset(&ioc, 0, sizeof(ioc));
40         ioc.cmd = cmd;
41         ioc.buf = arg;
42         ioc.len = len;
43         ioc.set = set;
44
45         ret = dhd_ioctl_entry_local(dev, &ioc, cmd);
46
47         return ret;
48 }
49
50 /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
51  * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
52  * wl_iw, wl_cfg80211 and wl_cfgp2p
53  */
54 static s32 wldev_mkiovar(
55         s8 *iovar_name, s8 *param, s32 paramlen,
56         s8 *iovar_buf, u32 buflen)
57 {
58         s32 iolen = 0;
59
60         iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
61         return iolen;
62 }
63
64 s32 wldev_iovar_getbuf(
65         struct net_device *dev, s8 *iovar_name,
66         void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
67 {
68         s32 ret = 0;
69         if (buf_sync) {
70                 mutex_lock(buf_sync);
71         }
72         wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
73         ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE);
74         if (buf_sync)
75                 mutex_unlock(buf_sync);
76         return ret;
77 }
78
79
80 s32 wldev_iovar_setbuf(
81         struct net_device *dev, s8 *iovar_name,
82         void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
83 {
84         s32 ret = 0;
85         s32 iovar_len;
86         if (buf_sync) {
87                 mutex_lock(buf_sync);
88         }
89         iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
90         if (iovar_len > 0)
91                 ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE);
92         else
93                 ret = BCME_BUFTOOSHORT;
94
95         if (buf_sync)
96                 mutex_unlock(buf_sync);
97         return ret;
98 }
99
100 s32 wldev_iovar_setint(
101         struct net_device *dev, s8 *iovar, s32 val)
102 {
103         s8 iovar_buf[WLC_IOCTL_SMLEN];
104
105         val = htod32(val);
106         memset(iovar_buf, 0, sizeof(iovar_buf));
107         return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
108                 sizeof(iovar_buf), NULL);
109 }
110
111
112 s32 wldev_iovar_getint(
113         struct net_device *dev, s8 *iovar, s32 *pval)
114 {
115         s8 iovar_buf[WLC_IOCTL_SMLEN];
116         s32 err;
117
118         memset(iovar_buf, 0, sizeof(iovar_buf));
119         err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
120                 sizeof(iovar_buf), NULL);
121         if (err == 0)
122         {
123                 memcpy(pval, iovar_buf, sizeof(*pval));
124                 *pval = dtoh32(*pval);
125         }
126         return err;
127 }
128
129 /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
130  *  taken care of in dhd_ioctl_entry. Internal use only, not exposed to
131  *  wl_iw, wl_cfg80211 and wl_cfgp2p
132  */
133 s32 wldev_mkiovar_bsscfg(
134         const s8 *iovar_name, s8 *param, s32 paramlen,
135         s8 *iovar_buf, s32 buflen, s32 bssidx)
136 {
137         const s8 *prefix = "bsscfg:";
138         s8 *p;
139         u32 prefixlen;
140         u32 namelen;
141         u32 iolen;
142
143         if (bssidx == 0) {
144                 return wldev_mkiovar((s8*)iovar_name, (s8 *)param, paramlen,
145                         (s8 *) iovar_buf, buflen);
146         }
147
148         prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */
149         namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar  name + null */
150         iolen = prefixlen + namelen + sizeof(u32) + paramlen;
151
152         if (buflen < 0 || iolen > (u32)buflen)
153         {
154                 WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__));
155                 return BCME_BUFTOOSHORT;
156         }
157
158         p = (s8 *)iovar_buf;
159
160         /* copy prefix, no null */
161         memcpy(p, prefix, prefixlen);
162         p += prefixlen;
163
164         /* copy iovar name including null */
165         memcpy(p, iovar_name, namelen);
166         p += namelen;
167
168         /* bss config index as first param */
169         bssidx = htod32(bssidx);
170         memcpy(p, &bssidx, sizeof(u32));
171         p += sizeof(u32);
172
173         /* parameter buffer follows */
174         if (paramlen)
175                 memcpy(p, param, paramlen);
176
177         return iolen;
178
179 }
180
181 s32 wldev_iovar_getbuf_bsscfg(
182         struct net_device *dev, s8 *iovar_name,
183         void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
184 {
185         s32 ret = 0;
186         if (buf_sync) {
187                 mutex_lock(buf_sync);
188         }
189
190         wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
191         ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE);
192         if (buf_sync) {
193                 mutex_unlock(buf_sync);
194         }
195         return ret;
196
197 }
198
199 s32 wldev_iovar_setbuf_bsscfg(
200         struct net_device *dev, s8 *iovar_name,
201         void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
202 {
203         s32 ret = 0;
204         s32 iovar_len;
205         if (buf_sync) {
206                 mutex_lock(buf_sync);
207         }
208         iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
209         if (iovar_len > 0)
210                 ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE);
211         else {
212                 ret = BCME_BUFTOOSHORT;
213         }
214
215         if (buf_sync) {
216                 mutex_unlock(buf_sync);
217         }
218         return ret;
219 }
220
221 s32 wldev_iovar_setint_bsscfg(
222         struct net_device *dev, s8 *iovar, s32 val, s32 bssidx)
223 {
224         s8 iovar_buf[WLC_IOCTL_SMLEN];
225
226         val = htod32(val);
227         memset(iovar_buf, 0, sizeof(iovar_buf));
228         return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
229                 sizeof(iovar_buf), bssidx, NULL);
230 }
231
232
233 s32 wldev_iovar_getint_bsscfg(
234         struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx)
235 {
236         s8 iovar_buf[WLC_IOCTL_SMLEN];
237         s32 err;
238
239         memset(iovar_buf, 0, sizeof(iovar_buf));
240         err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
241                 sizeof(iovar_buf), bssidx, NULL);
242         if (err == 0)
243         {
244                 memcpy(pval, iovar_buf, sizeof(*pval));
245                 *pval = dtoh32(*pval);
246         }
247         return err;
248 }
249
250 int wldev_get_link_speed(
251         struct net_device *dev, int *plink_speed)
252 {
253         int error;
254
255         if (!plink_speed)
256                 return -ENOMEM;
257         error = wldev_ioctl(dev, WLC_GET_RATE, plink_speed, sizeof(int), 0);
258         if (unlikely(error))
259                 return error;
260
261         /* Convert internal 500Kbps to Kbps */
262         *plink_speed *= 500;
263         return error;
264 }
265
266 int wldev_get_rssi(
267         struct net_device *dev, int *prssi)
268 {
269         scb_val_t scb_val;
270         int error;
271
272         if (!prssi)
273                 return -ENOMEM;
274         bzero(&scb_val, sizeof(scb_val_t));
275
276         error = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t), 0);
277         if (unlikely(error))
278                 return error;
279
280         *prssi = dtoh32(scb_val.val);
281         return error;
282 }
283
284 int wldev_get_ssid(
285         struct net_device *dev, wlc_ssid_t *pssid)
286 {
287         int error;
288
289         if (!pssid)
290                 return -ENOMEM;
291         error = wldev_ioctl(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t), 0);
292         if (unlikely(error))
293                 return error;
294         pssid->SSID_len = dtoh32(pssid->SSID_len);
295         return error;
296 }
297
298 int wldev_get_band(
299         struct net_device *dev, uint *pband)
300 {
301         int error;
302
303         error = wldev_ioctl(dev, WLC_GET_BAND, pband, sizeof(uint), 0);
304         return error;
305 }
306
307 int wldev_set_band(
308         struct net_device *dev, uint band)
309 {
310         int error = -1;
311
312         if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
313                 error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true);
314                 if (!error)
315                         dhd_bus_band_set(dev, band);
316         }
317         return error;
318 }
319
320 int wldev_set_country(
321         struct net_device *dev, char *country_code, bool notify, bool user_enforced)
322 {
323         int error = -1;
324         wl_country_t cspec = {{0}, 0, {0}};
325         scb_val_t scbval;
326         char smbuf[WLC_IOCTL_SMLEN];
327
328         if (!country_code)
329                 return error;
330
331         bzero(&scbval, sizeof(scb_val_t));
332         error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec), NULL);
333         if (error < 0) {
334                 WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error));
335                 return error;
336         }
337
338         if ((error < 0) ||
339             (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) {
340
341                 if (user_enforced) {
342                         bzero(&scbval, sizeof(scb_val_t));
343                         error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true);
344                         if (error < 0) {
345                                 WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
346                                         __FUNCTION__, error));
347                                 return error;
348                         }
349                 }
350
351                 cspec.rev = -1;
352                 memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
353                 memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
354                 get_customized_country_code((char *)&cspec.country_abbrev, &cspec);
355                 error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec),
356                         smbuf, sizeof(smbuf), NULL);
357                 if (error < 0) {
358                         WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
359                                 __FUNCTION__, country_code, cspec.ccode, cspec.rev));
360                         return error;
361                 }
362                 dhd_bus_country_set(dev, &cspec, notify);
363                 WLDEV_ERROR(("%s: set country for %s as %s rev %d\n",
364                         __FUNCTION__, country_code, cspec.ccode, cspec.rev));
365         }
366         return 0;
367 }