wldev_common.c 10 KB

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