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