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