dhd_cfg80211.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /*
  2. * Linux cfg80211 driver - Dongle Host Driver (DHD) related
  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: dhd_cfg80211.c 696903 2017-04-28 19:48:01Z $
  30. */
  31. #include <linux/vmalloc.h>
  32. #include <net/rtnetlink.h>
  33. #include <bcmutils.h>
  34. #include <wldev_common.h>
  35. #include <wl_cfg80211.h>
  36. #include <dhd_cfg80211.h>
  37. #ifdef PKT_FILTER_SUPPORT
  38. #include <dngl_stats.h>
  39. #include <dhd.h>
  40. #endif // endif
  41. #ifdef PKT_FILTER_SUPPORT
  42. extern uint dhd_pkt_filter_enable;
  43. extern uint dhd_master_mode;
  44. extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
  45. #endif // endif
  46. static int dhd_dongle_up = FALSE;
  47. #include <dngl_stats.h>
  48. #include <dhd.h>
  49. #include <dhdioctl.h>
  50. #include <wlioctl.h>
  51. #include <brcm_nl80211.h>
  52. #include <dhd_cfg80211.h>
  53. static s32 wl_dongle_up(struct net_device *ndev);
  54. static s32 wl_dongle_down(struct net_device *ndev);
  55. #ifndef OEM_ANDROID
  56. static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode);
  57. #ifdef BCMSDIO /* glomming is a sdio specific feature */
  58. static s32 wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align);
  59. #endif // endif
  60. static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, s32 scan_unassoc_time);
  61. static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol);
  62. static s32 wl_pattern_atoh(s8 *src, s8 *dst);
  63. static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode);
  64. #endif /* OEM_ANDROID */
  65. /**
  66. * Function implementations
  67. */
  68. s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
  69. {
  70. dhd_dongle_up = FALSE;
  71. return 0;
  72. }
  73. s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
  74. {
  75. dhd_dongle_up = FALSE;
  76. return 0;
  77. }
  78. s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
  79. {
  80. struct net_device *ndev;
  81. s32 err = 0;
  82. dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
  83. WL_TRACE(("In\n"));
  84. if ((!dhd_dongle_up) || (!dhd->up)) {
  85. WL_INFORM_MEM(("Dongle is already down\n"));
  86. err = 0;
  87. goto done;
  88. }
  89. ndev = bcmcfg_to_prmry_ndev(cfg);
  90. wl_dongle_down(ndev);
  91. done:
  92. dhd_dongle_up = FALSE;
  93. return err;
  94. }
  95. s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
  96. {
  97. dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
  98. dhd->op_mode |= val;
  99. WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
  100. #ifdef ARP_OFFLOAD_SUPPORT
  101. if (dhd->arp_version == 1) {
  102. /* IF P2P is enabled, disable arpoe */
  103. dhd_arp_offload_set(dhd, 0);
  104. dhd_arp_offload_enable(dhd, false);
  105. }
  106. #endif /* ARP_OFFLOAD_SUPPORT */
  107. return 0;
  108. }
  109. s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
  110. {
  111. dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
  112. dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
  113. WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
  114. #ifdef ARP_OFFLOAD_SUPPORT
  115. if (dhd->arp_version == 1) {
  116. /* IF P2P is disabled, enable arpoe back for STA mode. */
  117. dhd_arp_offload_set(dhd, dhd_arp_mode);
  118. dhd_arp_offload_enable(dhd, true);
  119. }
  120. #endif /* ARP_OFFLOAD_SUPPORT */
  121. return 0;
  122. }
  123. #ifdef WL_STATIC_IF
  124. int32
  125. wl_cfg80211_update_iflist_info(struct bcm_cfg80211 *cfg, struct net_device *ndev,
  126. int ifidx, uint8 *addr, int bssidx, char *name, int if_state)
  127. {
  128. return dhd_update_iflist_info(cfg->pub, ndev, ifidx, addr, bssidx, name, if_state);
  129. }
  130. #endif /* WL_STATIC_IF */
  131. struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name,
  132. uint8 *mac, uint8 bssidx, const char *dngl_name)
  133. {
  134. return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name);
  135. }
  136. int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg,
  137. int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
  138. {
  139. return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd);
  140. }
  141. int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg,
  142. int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
  143. {
  144. #ifdef DHD_PCIE_RUNTIMEPM
  145. dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
  146. #endif /* DHD_PCIE_RUNTIMEPM */
  147. return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd);
  148. }
  149. void wl_cfg80211_cleanup_if(struct net_device *net)
  150. {
  151. struct bcm_cfg80211 *cfg = wl_get_cfg(net);
  152. #ifdef DHD_PCIE_RUNTIMEPM
  153. dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
  154. #else
  155. BCM_REFERENCE(cfg);
  156. #endif /* DHD_PCIE_RUNTIMEPM */
  157. dhd_cleanup_if(net);
  158. }
  159. struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev)
  160. {
  161. struct bcm_cfg80211 *cfg;
  162. if (ndev) {
  163. cfg = wl_get_cfg(ndev);
  164. if (ndev->ieee80211_ptr) {
  165. MFREE(cfg->osh, ndev->ieee80211_ptr, sizeof(struct wireless_dev));
  166. ndev->ieee80211_ptr = NULL;
  167. }
  168. free_netdev(ndev);
  169. return NULL;
  170. }
  171. return ndev;
  172. }
  173. void dhd_netdev_free(struct net_device *ndev)
  174. {
  175. #ifdef WL_CFG80211
  176. ndev = dhd_cfg80211_netdev_free(ndev);
  177. #endif // endif
  178. if (ndev)
  179. free_netdev(ndev);
  180. }
  181. static s32
  182. wl_dongle_up(struct net_device *ndev)
  183. {
  184. s32 err = 0;
  185. u32 local_up = 0;
  186. err = wldev_ioctl_set(ndev, WLC_UP, &local_up, sizeof(local_up));
  187. if (unlikely(err)) {
  188. WL_ERR(("WLC_UP error (%d)\n", err));
  189. }
  190. return err;
  191. }
  192. static s32
  193. wl_dongle_down(struct net_device *ndev)
  194. {
  195. s32 err = 0;
  196. u32 local_down = 0;
  197. err = wldev_ioctl_set(ndev, WLC_DOWN, &local_down, sizeof(local_down));
  198. if (unlikely(err)) {
  199. WL_ERR(("WLC_DOWN error (%d)\n", err));
  200. }
  201. return err;
  202. }
  203. #ifndef OEM_ANDROID
  204. static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode)
  205. {
  206. s32 err = 0;
  207. WL_TRACE(("In\n"));
  208. err = wldev_ioctl_set(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode));
  209. if (unlikely(err)) {
  210. WL_ERR(("WLC_SET_PM error (%d)\n", err));
  211. }
  212. return err;
  213. }
  214. #ifdef BCMSDIO
  215. static s32
  216. wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align)
  217. {
  218. s32 err = 0;
  219. /* Match Host and Dongle rx alignment */
  220. err = wldev_iovar_setint(ndev, "bus:txglomalign", dongle_align);
  221. if (unlikely(err)) {
  222. WL_ERR(("txglomalign error (%d)\n", err));
  223. goto dongle_glom_out;
  224. }
  225. /* disable glom option per default */
  226. if (glom != DEFAULT_GLOM_VALUE) {
  227. err = wldev_iovar_setint(ndev, "bus:txglom", glom);
  228. if (unlikely(err)) {
  229. WL_ERR(("txglom error (%d)\n", err));
  230. goto dongle_glom_out;
  231. }
  232. }
  233. dongle_glom_out:
  234. return err;
  235. }
  236. #endif /* BCMSDIO */
  237. #endif /* OEM_ANDROID */
  238. s32
  239. wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
  240. {
  241. s32 err = 0;
  242. /* Setup timeout if Beacons are lost and roam is off to report link down */
  243. if (roamvar) {
  244. err = wldev_iovar_setint(ndev, "bcn_timeout", bcn_timeout);
  245. if (unlikely(err)) {
  246. WL_ERR(("bcn_timeout error (%d)\n", err));
  247. goto dongle_rom_out;
  248. }
  249. }
  250. /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */
  251. err = wldev_iovar_setint(ndev, "roam_off", roamvar);
  252. if (unlikely(err)) {
  253. WL_ERR(("roam_off error (%d)\n", err));
  254. goto dongle_rom_out;
  255. }
  256. dongle_rom_out:
  257. return err;
  258. }
  259. #ifndef OEM_ANDROID
  260. static s32
  261. wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
  262. s32 scan_unassoc_time)
  263. {
  264. s32 err = 0;
  265. err = wldev_ioctl_set(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time,
  266. sizeof(scan_assoc_time));
  267. if (err) {
  268. if (err == -EOPNOTSUPP) {
  269. WL_INFORM(("Scan assoc time is not supported\n"));
  270. } else {
  271. WL_ERR(("Scan assoc time error (%d)\n", err));
  272. }
  273. goto dongle_scantime_out;
  274. }
  275. err = wldev_ioctl_set(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time,
  276. sizeof(scan_unassoc_time));
  277. if (err) {
  278. if (err == -EOPNOTSUPP) {
  279. WL_INFORM(("Scan unassoc time is not supported\n"));
  280. } else {
  281. WL_ERR(("Scan unassoc time error (%d)\n", err));
  282. }
  283. goto dongle_scantime_out;
  284. }
  285. dongle_scantime_out:
  286. return err;
  287. }
  288. static s32
  289. wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol)
  290. {
  291. s8 iovbuf[WLC_IOCTL_SMLEN];
  292. s32 err = 0;
  293. s32 len;
  294. /* Set ARP offload */
  295. len = bcm_mkiovar("arpoe", (char *)&arpoe, sizeof(arpoe), iovbuf, sizeof(iovbuf));
  296. if (!len) {
  297. WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
  298. return BCME_BADARG;
  299. }
  300. err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
  301. if (err) {
  302. if (err == -EOPNOTSUPP)
  303. WL_INFORM(("arpoe is not supported\n"));
  304. else
  305. WL_ERR(("arpoe error (%d)\n", err));
  306. goto dongle_offload_out;
  307. }
  308. len = bcm_mkiovar("arp_ol", (char *)&arp_ol, sizeof(arp_ol), iovbuf, sizeof(iovbuf));
  309. if (!len) {
  310. WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
  311. return BCME_BADARG;
  312. }
  313. err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
  314. if (err) {
  315. if (err == -EOPNOTSUPP)
  316. WL_INFORM(("arp_ol is not supported\n"));
  317. else
  318. WL_ERR(("arp_ol error (%d)\n", err));
  319. goto dongle_offload_out;
  320. }
  321. dongle_offload_out:
  322. return err;
  323. }
  324. static s32 wl_pattern_atoh(s8 *src, s8 *dst)
  325. {
  326. int i;
  327. if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
  328. WL_ERR(("Mask invalid format. Needs to start with 0x\n"));
  329. return -1;
  330. }
  331. src = src + 2; /* Skip past 0x */
  332. if (strlen(src) % 2 != 0) {
  333. WL_ERR(("Mask invalid format. Needs to be of even length\n"));
  334. return -1;
  335. }
  336. for (i = 0; *src != '\0'; i++) {
  337. char num[3];
  338. strncpy(num, src, 2);
  339. num[2] = '\0';
  340. dst[i] = (u8) simple_strtoul(num, NULL, 16);
  341. src += 2;
  342. }
  343. return i;
  344. }
  345. static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode)
  346. {
  347. const s8 *str;
  348. struct wl_pkt_filter pkt_filter;
  349. struct wl_pkt_filter *pkt_filterp;
  350. s32 buf_len;
  351. s32 str_len;
  352. u32 mask_size;
  353. u32 pattern_size;
  354. s8 buf[64] = {0};
  355. s32 err = 0;
  356. /* add a default packet filter pattern */
  357. str = "pkt_filter_add";
  358. str_len = strlen(str);
  359. strncpy(buf, str, sizeof(buf) - 1);
  360. buf[ sizeof(buf) - 1 ] = '\0';
  361. buf_len = str_len + 1;
  362. pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1);
  363. /* Parse packet filter id. */
  364. pkt_filter.id = htod32(100);
  365. /* Parse filter polarity. */
  366. pkt_filter.negate_match = htod32(0);
  367. /* Parse filter type. */
  368. pkt_filter.type = htod32(0);
  369. /* Parse pattern filter offset. */
  370. pkt_filter.u.pattern.offset = htod32(0);
  371. /* Parse pattern filter mask. */
  372. mask_size = htod32(wl_pattern_atoh("0xff",
  373. (char *)pkt_filterp->u.pattern.
  374. mask_and_pattern));
  375. /* Parse pattern filter pattern. */
  376. pattern_size = htod32(wl_pattern_atoh("0x00",
  377. (char *)&pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
  378. if (mask_size != pattern_size) {
  379. WL_ERR(("Mask and pattern not the same size\n"));
  380. err = -EINVAL;
  381. goto dongle_filter_out;
  382. }
  383. pkt_filter.u.pattern.size_bytes = mask_size;
  384. buf_len += WL_PKT_FILTER_FIXED_LEN;
  385. buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
  386. /* Keep-alive attributes are set in local
  387. * variable (keep_alive_pkt), and
  388. * then memcpy'ed into buffer (keep_alive_pktp) since there is no
  389. * guarantee that the buffer is properly aligned.
  390. */
  391. memcpy((char *)pkt_filterp, &pkt_filter,
  392. WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
  393. err = wldev_ioctl_set(ndev, WLC_SET_VAR, buf, buf_len);
  394. if (err) {
  395. if (err == -EOPNOTSUPP) {
  396. WL_INFORM(("filter not supported\n"));
  397. } else {
  398. WL_ERR(("filter (%d)\n", err));
  399. }
  400. goto dongle_filter_out;
  401. }
  402. /* set mode to allow pattern */
  403. err = wldev_iovar_setint(ndev, "pkt_filter_mode", filter_mode);
  404. if (err) {
  405. if (err == -EOPNOTSUPP) {
  406. WL_INFORM(("filter_mode not supported\n"));
  407. } else {
  408. WL_ERR(("filter_mode (%d)\n", err));
  409. }
  410. goto dongle_filter_out;
  411. }
  412. dongle_filter_out:
  413. return err;
  414. }
  415. #endif /* OEM_ANDROID */
  416. s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
  417. {
  418. #ifndef DHD_SDALIGN
  419. #define DHD_SDALIGN 32
  420. #endif // endif
  421. struct net_device *ndev;
  422. s32 err = 0;
  423. #if !defined(OEM_ANDROID) && defined(BCMSDIO)
  424. s32 glom = CUSTOM_GLOM_SETTING;
  425. #endif // endif
  426. WL_TRACE(("In\n"));
  427. if (dhd_dongle_up) {
  428. WL_ERR(("Dongle is already up\n"));
  429. return err;
  430. }
  431. ndev = bcmcfg_to_prmry_ndev(cfg);
  432. err = wl_dongle_up(ndev);
  433. if (unlikely(err)) {
  434. WL_ERR(("wl_dongle_up failed\n"));
  435. goto default_conf_out;
  436. }
  437. #ifndef OEM_ANDROID
  438. err = wl_dongle_power(ndev, PM_FAST);
  439. if (unlikely(err)) {
  440. WL_ERR(("wl_dongle_power failed\n"));
  441. goto default_conf_out;
  442. }
  443. #ifdef BCMSDIO
  444. if (glom != DEFAULT_GLOM_VALUE) {
  445. err = wl_dongle_glom(ndev, glom, DHD_SDALIGN);
  446. } else {
  447. err = wl_dongle_glom(ndev, DEFAULT_GLOM_VALUE, DHD_SDALIGN);
  448. }
  449. if (unlikely(err)) {
  450. WL_ERR(("wl_dongle_glom failed\n"));
  451. goto default_conf_out;
  452. }
  453. #endif /* BCMSDIO */
  454. err = wl_dongle_roam(ndev, (cfg->roam_on ? 0 : 1), 3);
  455. if (unlikely(err)) {
  456. WL_ERR(("wl_dongle_roam failed\n"));
  457. goto default_conf_out;
  458. }
  459. wl_dongle_scantime(ndev, 40, 80);
  460. wl_dongle_offload(ndev, 1, 0xf);
  461. wl_dongle_filter(ndev, 1);
  462. #endif /* OEM_ANDROID */
  463. dhd_dongle_up = true;
  464. default_conf_out:
  465. return err;
  466. }
  467. int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
  468. const struct bcm_nlmsg_hdr *nlioc, void *buf)
  469. {
  470. struct net_device *ndev = NULL;
  471. dhd_pub_t *dhd;
  472. dhd_ioctl_t ioc = { 0, NULL, 0, 0, 0, 0, 0};
  473. int ret = 0;
  474. int8 index;
  475. WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
  476. dhd = cfg->pub;
  477. DHD_OS_WAKE_LOCK(dhd);
  478. ndev = wdev_to_wlc_ndev(wdev, cfg);
  479. index = dhd_net2idx(dhd->info, ndev);
  480. if (index == DHD_BAD_IF) {
  481. WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
  482. ret = BCME_ERROR;
  483. goto done;
  484. }
  485. ioc.cmd = nlioc->cmd;
  486. ioc.len = nlioc->len;
  487. ioc.set = nlioc->set;
  488. ioc.driver = nlioc->magic;
  489. ioc.buf = buf;
  490. ret = dhd_ioctl_process(dhd, index, &ioc, buf);
  491. if (ret) {
  492. WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
  493. ret = OSL_ERROR(ret);
  494. goto done;
  495. }
  496. done:
  497. DHD_OS_WAKE_UNLOCK(dhd);
  498. return ret;
  499. }