wldev_common.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /*
  2. * Common function shared by Linux WEXT, cfg80211 and p2p drivers
  3. *
  4. * Copyright (C) 1999-2015, 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 585464 2015-09-10 12:47:43Z $
  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) "); \
  41. printk args; \
  42. } while (0)
  43. #define WLDEV_INFO(args) \
  44. do { \
  45. printk(KERN_INFO "WLDEV-INFO) "); \
  46. printk args; \
  47. } while (0)
  48. extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd);
  49. s32 wldev_ioctl(
  50. struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
  51. {
  52. s32 ret = 0;
  53. struct wl_ioctl ioc;
  54. memset(&ioc, 0, sizeof(ioc));
  55. ioc.cmd = cmd;
  56. ioc.buf = arg;
  57. ioc.len = len;
  58. ioc.set = set;
  59. ret = dhd_ioctl_entry_local(dev, &ioc, cmd);
  60. return ret;
  61. }
  62. /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
  63. * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
  64. * wl_iw, wl_cfg80211 and wl_cfgp2p
  65. */
  66. static s32 wldev_mkiovar(
  67. s8 *iovar_name, s8 *param, s32 paramlen,
  68. s8 *iovar_buf, u32 buflen)
  69. {
  70. s32 iolen = 0;
  71. iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
  72. return iolen;
  73. }
  74. s32 wldev_iovar_getbuf(
  75. struct net_device *dev, s8 *iovar_name,
  76. void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
  77. {
  78. s32 ret = 0;
  79. if (buf_sync) {
  80. mutex_lock(buf_sync);
  81. }
  82. wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
  83. ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE);
  84. if (buf_sync)
  85. mutex_unlock(buf_sync);
  86. return ret;
  87. }
  88. s32 wldev_iovar_setbuf(
  89. struct net_device *dev, s8 *iovar_name,
  90. void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
  91. {
  92. s32 ret = 0;
  93. s32 iovar_len;
  94. if (buf_sync) {
  95. mutex_lock(buf_sync);
  96. }
  97. iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
  98. if (iovar_len > 0)
  99. ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE);
  100. else
  101. ret = BCME_BUFTOOSHORT;
  102. if (buf_sync)
  103. mutex_unlock(buf_sync);
  104. return ret;
  105. }
  106. s32 wldev_iovar_setint(
  107. struct net_device *dev, s8 *iovar, s32 val)
  108. {
  109. s8 iovar_buf[WLC_IOCTL_SMLEN];
  110. val = htod32(val);
  111. memset(iovar_buf, 0, sizeof(iovar_buf));
  112. return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
  113. sizeof(iovar_buf), NULL);
  114. }
  115. s32 wldev_iovar_getint(
  116. struct net_device *dev, s8 *iovar, s32 *pval)
  117. {
  118. s8 iovar_buf[WLC_IOCTL_SMLEN];
  119. s32 err;
  120. memset(iovar_buf, 0, sizeof(iovar_buf));
  121. err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
  122. sizeof(iovar_buf), NULL);
  123. if (err == 0)
  124. {
  125. memcpy(pval, iovar_buf, sizeof(*pval));
  126. *pval = dtoh32(*pval);
  127. }
  128. return err;
  129. }
  130. /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
  131. * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
  132. * wl_iw, wl_cfg80211 and wl_cfgp2p
  133. */
  134. s32 wldev_mkiovar_bsscfg(
  135. const s8 *iovar_name, s8 *param, s32 paramlen,
  136. s8 *iovar_buf, s32 buflen, s32 bssidx)
  137. {
  138. const s8 *prefix = "bsscfg:";
  139. s8 *p;
  140. u32 prefixlen;
  141. u32 namelen;
  142. u32 iolen;
  143. if (bssidx == 0) {
  144. return wldev_mkiovar((s8*)iovar_name, (s8 *)param, paramlen,
  145. (s8 *) iovar_buf, buflen);
  146. }
  147. prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */
  148. namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */
  149. iolen = prefixlen + namelen + sizeof(u32) + paramlen;
  150. if (buflen < 0 || iolen > (u32)buflen)
  151. {
  152. WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__));
  153. return BCME_BUFTOOSHORT;
  154. }
  155. p = (s8 *)iovar_buf;
  156. /* copy prefix, no null */
  157. memcpy(p, prefix, prefixlen);
  158. p += prefixlen;
  159. /* copy iovar name including null */
  160. memcpy(p, iovar_name, namelen);
  161. p += namelen;
  162. /* bss config index as first param */
  163. bssidx = htod32(bssidx);
  164. memcpy(p, &bssidx, sizeof(u32));
  165. p += sizeof(u32);
  166. /* parameter buffer follows */
  167. if (paramlen)
  168. memcpy(p, param, paramlen);
  169. return iolen;
  170. }
  171. s32 wldev_iovar_getbuf_bsscfg(
  172. struct net_device *dev, s8 *iovar_name,
  173. void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
  174. {
  175. s32 ret = 0;
  176. if (buf_sync) {
  177. mutex_lock(buf_sync);
  178. }
  179. wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
  180. ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE);
  181. if (buf_sync) {
  182. mutex_unlock(buf_sync);
  183. }
  184. return ret;
  185. }
  186. s32 wldev_iovar_setbuf_bsscfg(
  187. struct net_device *dev, s8 *iovar_name,
  188. void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
  189. {
  190. s32 ret = 0;
  191. s32 iovar_len;
  192. if (buf_sync) {
  193. mutex_lock(buf_sync);
  194. }
  195. iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
  196. if (iovar_len > 0)
  197. ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE);
  198. else {
  199. ret = BCME_BUFTOOSHORT;
  200. }
  201. if (buf_sync) {
  202. mutex_unlock(buf_sync);
  203. }
  204. return ret;
  205. }
  206. s32 wldev_iovar_setint_bsscfg(
  207. struct net_device *dev, s8 *iovar, s32 val, s32 bssidx)
  208. {
  209. s8 iovar_buf[WLC_IOCTL_SMLEN];
  210. val = htod32(val);
  211. memset(iovar_buf, 0, sizeof(iovar_buf));
  212. return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
  213. sizeof(iovar_buf), bssidx, NULL);
  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. memset(iovar_buf, 0, sizeof(iovar_buf));
  221. err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
  222. sizeof(iovar_buf), bssidx, NULL);
  223. if (err == 0)
  224. {
  225. memcpy(pval, iovar_buf, sizeof(*pval));
  226. *pval = dtoh32(*pval);
  227. }
  228. return err;
  229. }
  230. int wldev_get_link_speed(
  231. struct net_device *dev, int *plink_speed)
  232. {
  233. int error;
  234. if (!plink_speed)
  235. return -ENOMEM;
  236. error = wldev_ioctl(dev, WLC_GET_RATE, plink_speed, sizeof(int), 0);
  237. if (unlikely(error))
  238. return error;
  239. /* Convert internal 500Kbps to Kbps */
  240. *plink_speed *= 500;
  241. return error;
  242. }
  243. int wldev_get_rssi(
  244. struct net_device *dev, int *prssi)
  245. {
  246. scb_val_t scb_val;
  247. int error;
  248. if (!prssi)
  249. return -ENOMEM;
  250. bzero(&scb_val, sizeof(scb_val_t));
  251. error = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t), 0);
  252. if (unlikely(error))
  253. return error;
  254. *prssi = dtoh32(scb_val.val);
  255. return error;
  256. }
  257. int wldev_get_ssid(
  258. struct net_device *dev, wlc_ssid_t *pssid)
  259. {
  260. int error;
  261. if (!pssid)
  262. return -ENOMEM;
  263. error = wldev_ioctl(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t), 0);
  264. if (unlikely(error))
  265. return error;
  266. pssid->SSID_len = dtoh32(pssid->SSID_len);
  267. return error;
  268. }
  269. int wldev_get_band(
  270. struct net_device *dev, uint *pband)
  271. {
  272. int error;
  273. error = wldev_ioctl(dev, WLC_GET_BAND, pband, sizeof(uint), 0);
  274. return error;
  275. }
  276. int wldev_set_band(
  277. struct net_device *dev, uint band)
  278. {
  279. int error = -1;
  280. if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
  281. error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true);
  282. if (!error)
  283. dhd_bus_band_set(dev, band);
  284. }
  285. return error;
  286. }
  287. int wldev_set_country(
  288. struct net_device *dev, char *country_code, bool notify, bool user_enforced)
  289. {
  290. int error = -1;
  291. wl_country_t cspec_orig = {{0}, 0, {0}};
  292. wl_country_t cspec_desired = {{0}, 0, {0}};
  293. scb_val_t scbval;
  294. char smbuf[WLC_IOCTL_SMLEN];
  295. if (!country_code)
  296. return error;
  297. bzero(&scbval, sizeof(scb_val_t));
  298. error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec_orig, sizeof(cspec_orig), NULL);
  299. if (error < 0) {
  300. WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error));
  301. return error;
  302. }
  303. memcpy(cspec_desired.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
  304. memcpy(cspec_desired.ccode, country_code, WLC_CNTRY_BUF_SZ);
  305. cspec_desired.rev = -1;
  306. dhd_get_customized_country_code(dev, (char *)&cspec_desired.country_abbrev, &cspec_desired);
  307. /* even if the ccode iso code is identical,
  308. * need to set the info when both revs are differ
  309. */
  310. if ((strncmp(cspec_desired.ccode, cspec_orig.ccode, WLC_CNTRY_BUF_SZ) != 0) ||
  311. (cspec_desired.rev != cspec_orig.rev)) {
  312. if (user_enforced) {
  313. bzero(&scbval, sizeof(scb_val_t));
  314. error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true);
  315. if (error < 0) {
  316. WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
  317. __FUNCTION__, error));
  318. return error;
  319. }
  320. }
  321. /* No match in the custom lookup table
  322. * host request get higher priority. set with default rev '0'
  323. */
  324. if (cspec_desired.rev == -1)
  325. cspec_desired.rev = 0;
  326. error = wldev_iovar_setbuf(dev, "country", &cspec_desired, sizeof(cspec_desired),
  327. smbuf, sizeof(smbuf), NULL);
  328. if (error < 0) {
  329. WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
  330. __FUNCTION__, country_code, cspec_desired.ccode, cspec_desired.rev));
  331. return error;
  332. }
  333. dhd_bus_country_set(dev, &cspec_desired, notify);
  334. WLDEV_INFO(("%s: set country for %s as %s rev %d\n",
  335. __FUNCTION__, country_code, cspec_desired.ccode, cspec_desired.rev));
  336. }
  337. return 0;
  338. }