wldev_common.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. * Common function shared by Linux WEXT, cfg80211 and p2p drivers
  3. *
  4. * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
  5. *
  6. * Copyright (C) 1999-2020, Broadcom Corporation
  7. *
  8. * Unless you and Broadcom execute a separate written software license
  9. * agreement governing use of this software, this software is licensed to you
  10. * under the terms of the GNU General Public License version 2 (the "GPL"),
  11. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  12. * following added to such license:
  13. *
  14. * As a special exception, the copyright holders of this software give you
  15. * permission to link this software with independent modules, and to copy and
  16. * distribute the resulting executable under terms of your choice, provided that
  17. * you also meet, for each linked independent module, the terms and conditions of
  18. * the license of that module. An independent module is a module which is not
  19. * derived from this software. The special exception does not apply to any
  20. * modifications of the software.
  21. *
  22. * Notwithstanding the above, under no circumstances may you combine this
  23. * software in any way with any other Broadcom software provided under a license
  24. * other than the GPL, without Broadcom's express prior written consent.
  25. *
  26. *
  27. * <<Broadcom-WL-IPTag/Open:>>
  28. *
  29. * $Id: wldev_common.c 698236 2017-05-08 19:41:09Z $
  30. */
  31. #include <osl.h>
  32. #include <linux/kernel.h>
  33. #include <linux/kthread.h>
  34. #include <linux/netdevice.h>
  35. #include <wldev_common.h>
  36. #include <bcmutils.h>
  37. #ifdef WL_CFG80211
  38. #include <wl_cfg80211.h>
  39. #include <wl_cfgscan.h>
  40. #endif /* WL_CFG80211 */
  41. #define htod32(i) (i)
  42. #define htod16(i) (i)
  43. #define dtoh32(i) (i)
  44. #define dtoh16(i) (i)
  45. #define htodchanspec(i) (i)
  46. #define dtohchanspec(i) (i)
  47. #define WLDEV_ERROR(args) \
  48. do { \
  49. printk(KERN_ERR "WLDEV-ERROR) "); \
  50. printk args; \
  51. } while (0)
  52. #define WLDEV_INFO(args) \
  53. do { \
  54. printk(KERN_INFO "WLDEV-INFO) "); \
  55. printk args; \
  56. } while (0)
  57. extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd);
  58. static s32 wldev_ioctl(
  59. struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
  60. {
  61. s32 ret = 0;
  62. struct wl_ioctl ioc;
  63. memset(&ioc, 0, sizeof(ioc));
  64. ioc.cmd = cmd;
  65. ioc.buf = arg;
  66. ioc.len = len;
  67. ioc.set = set;
  68. ret = dhd_ioctl_entry_local(dev, (wl_ioctl_t *)&ioc, cmd);
  69. return ret;
  70. }
  71. /*
  72. SET commands :
  73. cast buffer to non-const and call the GET function
  74. */
  75. s32 wldev_ioctl_set(
  76. struct net_device *dev, u32 cmd, const void *arg, u32 len)
  77. {
  78. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  79. #pragma GCC diagnostic push
  80. #pragma GCC diagnostic ignored "-Wcast-qual"
  81. #endif // endif
  82. return wldev_ioctl(dev, cmd, (void *)arg, len, 1);
  83. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  84. #pragma GCC diagnostic pop
  85. #endif // endif
  86. }
  87. s32 wldev_ioctl_get(
  88. struct net_device *dev, u32 cmd, void *arg, u32 len)
  89. {
  90. return wldev_ioctl(dev, cmd, (void *)arg, len, 0);
  91. }
  92. /* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be
  93. * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
  94. * wl_iw, wl_cfg80211 and wl_cfgp2p
  95. */
  96. static s32 wldev_mkiovar(
  97. const s8 *iovar_name, const s8 *param, s32 paramlen,
  98. s8 *iovar_buf, u32 buflen)
  99. {
  100. s32 iolen = 0;
  101. iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen);
  102. return iolen;
  103. }
  104. s32 wldev_iovar_getbuf(
  105. struct net_device *dev, s8 *iovar_name,
  106. const void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
  107. {
  108. s32 ret = 0;
  109. if (buf_sync) {
  110. mutex_lock(buf_sync);
  111. }
  112. if (buf && (buflen > 0)) {
  113. /* initialize the response buffer */
  114. memset(buf, 0, buflen);
  115. } else {
  116. ret = BCME_BADARG;
  117. goto exit;
  118. }
  119. ret = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
  120. if (!ret) {
  121. ret = BCME_BUFTOOSHORT;
  122. goto exit;
  123. }
  124. ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
  125. exit:
  126. if (buf_sync)
  127. mutex_unlock(buf_sync);
  128. return ret;
  129. }
  130. s32 wldev_iovar_setbuf(
  131. struct net_device *dev, s8 *iovar_name,
  132. const void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
  133. {
  134. s32 ret = 0;
  135. s32 iovar_len;
  136. if (buf_sync) {
  137. mutex_lock(buf_sync);
  138. }
  139. iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen);
  140. if (iovar_len > 0)
  141. ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
  142. else
  143. ret = BCME_BUFTOOSHORT;
  144. if (buf_sync)
  145. mutex_unlock(buf_sync);
  146. return ret;
  147. }
  148. s32 wldev_iovar_setint(
  149. struct net_device *dev, s8 *iovar, s32 val)
  150. {
  151. s8 iovar_buf[WLC_IOCTL_SMLEN];
  152. val = htod32(val);
  153. memset(iovar_buf, 0, sizeof(iovar_buf));
  154. return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf,
  155. sizeof(iovar_buf), NULL);
  156. }
  157. s32 wldev_iovar_getint(
  158. struct net_device *dev, s8 *iovar, s32 *pval)
  159. {
  160. s8 iovar_buf[WLC_IOCTL_SMLEN];
  161. s32 err;
  162. memset(iovar_buf, 0, sizeof(iovar_buf));
  163. err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf,
  164. sizeof(iovar_buf), NULL);
  165. if (err == 0)
  166. {
  167. memcpy(pval, iovar_buf, sizeof(*pval));
  168. *pval = dtoh32(*pval);
  169. }
  170. return err;
  171. }
  172. /** Format a bsscfg indexed iovar buffer. The bsscfg index will be
  173. * taken care of in dhd_ioctl_entry. Internal use only, not exposed to
  174. * wl_iw, wl_cfg80211 and wl_cfgp2p
  175. */
  176. s32 wldev_mkiovar_bsscfg(
  177. const s8 *iovar_name, const s8 *param, s32 paramlen,
  178. s8 *iovar_buf, s32 buflen, s32 bssidx)
  179. {
  180. const s8 *prefix = "bsscfg:";
  181. s8 *p;
  182. u32 prefixlen;
  183. u32 namelen;
  184. u32 iolen;
  185. /* initialize buffer */
  186. if (!iovar_buf || buflen <= 0)
  187. return BCME_BADARG;
  188. memset(iovar_buf, 0, buflen);
  189. if (bssidx == 0) {
  190. return wldev_mkiovar(iovar_name, param, paramlen,
  191. iovar_buf, buflen);
  192. }
  193. prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */
  194. namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */
  195. iolen = prefixlen + namelen + sizeof(u32) + paramlen;
  196. if (iolen > (u32)buflen) {
  197. WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__));
  198. return BCME_BUFTOOSHORT;
  199. }
  200. p = (s8 *)iovar_buf;
  201. /* copy prefix, no null */
  202. memcpy(p, prefix, prefixlen);
  203. p += prefixlen;
  204. /* copy iovar name including null */
  205. memcpy(p, iovar_name, namelen);
  206. p += namelen;
  207. /* bss config index as first param */
  208. bssidx = htod32(bssidx);
  209. memcpy(p, &bssidx, sizeof(u32));
  210. p += sizeof(u32);
  211. /* parameter buffer follows */
  212. if (paramlen)
  213. memcpy(p, param, paramlen);
  214. return iolen;
  215. }
  216. s32 wldev_iovar_getbuf_bsscfg(
  217. struct net_device *dev, s8 *iovar_name,
  218. void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
  219. {
  220. s32 ret = 0;
  221. if (buf_sync) {
  222. mutex_lock(buf_sync);
  223. }
  224. wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
  225. ret = wldev_ioctl_get(dev, WLC_GET_VAR, buf, buflen);
  226. if (buf_sync) {
  227. mutex_unlock(buf_sync);
  228. }
  229. return ret;
  230. }
  231. s32 wldev_iovar_setbuf_bsscfg(
  232. struct net_device *dev, const s8 *iovar_name,
  233. const void *param, s32 paramlen,
  234. void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
  235. {
  236. s32 ret = 0;
  237. s32 iovar_len;
  238. if (buf_sync) {
  239. mutex_lock(buf_sync);
  240. }
  241. iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx);
  242. if (iovar_len > 0)
  243. ret = wldev_ioctl_set(dev, WLC_SET_VAR, buf, iovar_len);
  244. else {
  245. ret = BCME_BUFTOOSHORT;
  246. }
  247. if (buf_sync) {
  248. mutex_unlock(buf_sync);
  249. }
  250. return ret;
  251. }
  252. s32 wldev_iovar_setint_bsscfg(
  253. struct net_device *dev, s8 *iovar, s32 val, s32 bssidx)
  254. {
  255. s8 iovar_buf[WLC_IOCTL_SMLEN];
  256. val = htod32(val);
  257. memset(iovar_buf, 0, sizeof(iovar_buf));
  258. return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf,
  259. sizeof(iovar_buf), bssidx, NULL);
  260. }
  261. s32 wldev_iovar_getint_bsscfg(
  262. struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx)
  263. {
  264. s8 iovar_buf[WLC_IOCTL_SMLEN];
  265. s32 err;
  266. memset(iovar_buf, 0, sizeof(iovar_buf));
  267. err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf,
  268. sizeof(iovar_buf), bssidx, NULL);
  269. if (err == 0)
  270. {
  271. memcpy(pval, iovar_buf, sizeof(*pval));
  272. *pval = dtoh32(*pval);
  273. }
  274. return err;
  275. }
  276. int wldev_get_link_speed(
  277. struct net_device *dev, int *plink_speed)
  278. {
  279. int error;
  280. if (!plink_speed)
  281. return -ENOMEM;
  282. *plink_speed = 0;
  283. error = wldev_ioctl_get(dev, WLC_GET_RATE, plink_speed, sizeof(int));
  284. if (unlikely(error))
  285. return error;
  286. /* Convert internal 500Kbps to Kbps */
  287. *plink_speed *= 500;
  288. return error;
  289. }
  290. int wldev_get_rssi(
  291. struct net_device *dev, scb_val_t *scb_val)
  292. {
  293. int error;
  294. if (!scb_val)
  295. return -ENOMEM;
  296. error = wldev_ioctl_get(dev, WLC_GET_RSSI, scb_val, sizeof(scb_val_t));
  297. if (unlikely(error))
  298. return error;
  299. return error;
  300. }
  301. int wldev_get_ssid(
  302. struct net_device *dev, wlc_ssid_t *pssid)
  303. {
  304. int error;
  305. if (!pssid)
  306. return -ENOMEM;
  307. memset(pssid, 0, sizeof(wlc_ssid_t));
  308. error = wldev_ioctl_get(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t));
  309. if (unlikely(error))
  310. return error;
  311. pssid->SSID_len = dtoh32(pssid->SSID_len);
  312. return error;
  313. }
  314. int wldev_get_band(
  315. struct net_device *dev, uint *pband)
  316. {
  317. int error;
  318. *pband = 0;
  319. error = wldev_ioctl_get(dev, WLC_GET_BAND, pband, sizeof(uint));
  320. return error;
  321. }
  322. int wldev_set_band(
  323. struct net_device *dev, uint band)
  324. {
  325. int error = -1;
  326. if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
  327. error = wldev_ioctl_set(dev, WLC_SET_BAND, &band, sizeof(band));
  328. if (!error)
  329. dhd_bus_band_set(dev, band);
  330. }
  331. return error;
  332. }
  333. int wldev_get_datarate(struct net_device *dev, int *datarate)
  334. {
  335. int error = 0;
  336. error = wldev_ioctl_get(dev, WLC_GET_RATE, datarate, sizeof(int));
  337. if (error) {
  338. return -1;
  339. } else {
  340. *datarate = dtoh32(*datarate);
  341. }
  342. return error;
  343. }
  344. extern chanspec_t
  345. wl_chspec_driver_to_host(chanspec_t chanspec);
  346. #define WL_EXTRA_BUF_MAX 2048
  347. int wldev_get_mode(
  348. struct net_device *dev, uint8 *cap, uint8 caplen)
  349. {
  350. int error = 0;
  351. int chanspec = 0;
  352. uint16 band = 0;
  353. uint16 bandwidth = 0;
  354. wl_bss_info_t *bss = NULL;
  355. char* buf = NULL;
  356. buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
  357. if (!buf) {
  358. WLDEV_ERROR(("%s:ENOMEM\n", __FUNCTION__));
  359. return -ENOMEM;
  360. }
  361. *(u32*) buf = htod32(WL_EXTRA_BUF_MAX);
  362. error = wldev_ioctl_get(dev, WLC_GET_BSS_INFO, (void*)buf, WL_EXTRA_BUF_MAX);
  363. if (error) {
  364. WLDEV_ERROR(("%s:failed:%d\n", __FUNCTION__, error));
  365. kfree(buf);
  366. buf = NULL;
  367. return error;
  368. }
  369. bss = (wl_bss_info_t*)(buf + 4);
  370. chanspec = wl_chspec_driver_to_host(bss->chanspec);
  371. band = chanspec & WL_CHANSPEC_BAND_MASK;
  372. bandwidth = chanspec & WL_CHANSPEC_BW_MASK;
  373. if (band == WL_CHANSPEC_BAND_2G) {
  374. if (bss->n_cap)
  375. strncpy(cap, "n", caplen);
  376. else
  377. strncpy(cap, "bg", caplen);
  378. } else if (band == WL_CHANSPEC_BAND_5G) {
  379. if (bandwidth == WL_CHANSPEC_BW_80)
  380. strncpy(cap, "ac", caplen);
  381. else if ((bandwidth == WL_CHANSPEC_BW_40) || (bandwidth == WL_CHANSPEC_BW_20)) {
  382. if ((bss->nbss_cap & 0xf00) && (bss->n_cap))
  383. strncpy(cap, "n|ac", caplen);
  384. else if (bss->n_cap)
  385. strncpy(cap, "n", caplen);
  386. else if (bss->vht_cap)
  387. strncpy(cap, "ac", caplen);
  388. else
  389. strncpy(cap, "a", caplen);
  390. } else {
  391. WLDEV_ERROR(("%s:Mode get failed\n", __FUNCTION__));
  392. error = BCME_ERROR;
  393. }
  394. }
  395. kfree(buf);
  396. buf = NULL;
  397. return error;
  398. }
  399. int wldev_set_country(
  400. struct net_device *dev, char *country_code, bool notify, bool user_enforced, int revinfo)
  401. {
  402. int error = -1;
  403. wl_country_t cspec = {{0}, 0, {0}};
  404. scb_val_t scbval;
  405. char smbuf[WLC_IOCTL_SMLEN];
  406. #ifdef WL_CFG80211
  407. struct wireless_dev *wdev = ndev_to_wdev(dev);
  408. struct wiphy *wiphy = wdev->wiphy;
  409. struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
  410. #endif /* WL_CFG80211 */
  411. if (!country_code)
  412. return error;
  413. bzero(&scbval, sizeof(scb_val_t));
  414. error = wldev_iovar_getbuf(dev, "country", NULL, 0, &cspec, sizeof(cspec), NULL);
  415. if (error < 0) {
  416. WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error));
  417. return error;
  418. }
  419. if ((error < 0) ||
  420. #ifdef OEM_ANDROID
  421. dhd_force_country_change(dev) ||
  422. #endif /* OEM_ANDROID */
  423. (strncmp(country_code, cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) {
  424. #ifdef WL_CFG80211
  425. if ((user_enforced) && (wl_get_drv_status(cfg, CONNECTED, dev))) {
  426. #else
  427. if (user_enforced) {
  428. #endif /* WL_CFG80211 */
  429. bzero(&scbval, sizeof(scb_val_t));
  430. error = wldev_ioctl_set(dev, WLC_DISASSOC,
  431. &scbval, sizeof(scb_val_t));
  432. if (error < 0) {
  433. WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n",
  434. __FUNCTION__, error));
  435. return error;
  436. }
  437. }
  438. wl_cfg80211_scan_abort(cfg);
  439. cspec.rev = revinfo;
  440. strlcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
  441. strlcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
  442. dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec);
  443. error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec),
  444. smbuf, sizeof(smbuf), NULL);
  445. if (error < 0) {
  446. WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n",
  447. __FUNCTION__, country_code, cspec.ccode, cspec.rev));
  448. return error;
  449. }
  450. dhd_bus_country_set(dev, &cspec, notify);
  451. WLDEV_INFO(("%s: set country for %s as %s rev %d\n",
  452. __FUNCTION__, country_code, cspec.ccode, cspec.rev));
  453. }
  454. return 0;
  455. }