wl_roam.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * Linux roam cache
  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: wl_roam.c 798173 2019-01-07 09:23:21Z $
  30. */
  31. #include <typedefs.h>
  32. #include <osl.h>
  33. #include <bcmwifi_channels.h>
  34. #include <wlioctl.h>
  35. #include <bcmutils.h>
  36. #ifdef WL_CFG80211
  37. #include <wl_cfg80211.h>
  38. #endif // endif
  39. #include <wldev_common.h>
  40. #include <bcmstdlib_s.h>
  41. #ifdef ESCAN_CHANNEL_CACHE
  42. #define MAX_ROAM_CACHE 200
  43. #define MAX_SSID_BUFSIZE 36
  44. #define ROAMSCAN_MODE_NORMAL 0
  45. #define ROAMSCAN_MODE_WES 1
  46. typedef struct {
  47. chanspec_t chanspec;
  48. int ssid_len;
  49. char ssid[MAX_SSID_BUFSIZE];
  50. } roam_channel_cache;
  51. static int n_roam_cache = 0;
  52. static int roam_band = WLC_BAND_AUTO;
  53. static roam_channel_cache roam_cache[MAX_ROAM_CACHE];
  54. static uint band2G, band5G, band_bw;
  55. #ifdef WES_SUPPORT
  56. static int roamscan_mode = ROAMSCAN_MODE_NORMAL;
  57. #endif /* WES_SUPPORT */
  58. #ifdef ROAM_CHANNEL_CACHE
  59. int init_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
  60. {
  61. int err;
  62. struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
  63. s32 mode;
  64. /* Check support in firmware */
  65. err = wldev_iovar_getint(dev, "roamscan_mode", &mode);
  66. if (err && (err == BCME_UNSUPPORTED)) {
  67. /* If firmware doesn't support, return error. Else proceed */
  68. WL_ERR(("roamscan_mode iovar failed. %d\n", err));
  69. return err;
  70. }
  71. #ifdef D11AC_IOTYPES
  72. if (ioctl_ver == 1) {
  73. /* legacy chanspec */
  74. band2G = WL_LCHANSPEC_BAND_2G;
  75. band5G = WL_LCHANSPEC_BAND_5G;
  76. band_bw = WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE;
  77. } else {
  78. band2G = WL_CHANSPEC_BAND_2G;
  79. band5G = WL_CHANSPEC_BAND_5G;
  80. band_bw = WL_CHANSPEC_BW_20;
  81. }
  82. #else
  83. band2G = WL_CHANSPEC_BAND_2G;
  84. band5G = WL_CHANSPEC_BAND_5G;
  85. band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
  86. #endif /* D11AC_IOTYPES */
  87. n_roam_cache = 0;
  88. roam_band = WLC_BAND_AUTO;
  89. #ifdef WES_SUPPORT
  90. roamscan_mode = ROAMSCAN_MODE_NORMAL;
  91. #endif /* WES_SUPPORT */
  92. return 0;
  93. }
  94. #endif /* ROAM_CHANNEL_CACHE */
  95. #ifdef WES_SUPPORT
  96. int get_roamscan_mode(struct net_device *dev, int *mode)
  97. {
  98. *mode = roamscan_mode;
  99. return 0;
  100. }
  101. int set_roamscan_mode(struct net_device *dev, int mode)
  102. {
  103. int error = 0;
  104. roamscan_mode = mode;
  105. n_roam_cache = 0;
  106. error = wldev_iovar_setint(dev, "roamscan_mode", mode);
  107. if (error) {
  108. WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode, error));
  109. }
  110. return error;
  111. }
  112. int get_roamscan_channel_list(struct net_device *dev, unsigned char channels[],
  113. int n_channels)
  114. {
  115. int n = 0;
  116. int max_channel_number = MIN(n_channels, n_roam_cache);
  117. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  118. for (n = 0; n < max_channel_number; n++) {
  119. channels[n] = roam_cache[n].chanspec & WL_CHANSPEC_CHAN_MASK;
  120. WL_DBG(("channel[%d] - [%02d] \n", n, channels[n]));
  121. }
  122. }
  123. return n;
  124. }
  125. int set_roamscan_channel_list(struct net_device *dev,
  126. unsigned char n, unsigned char channels[], int ioctl_ver)
  127. {
  128. int i;
  129. int error;
  130. wl_roam_channel_list_t channel_list;
  131. char iobuf[WLC_IOCTL_SMLEN];
  132. roamscan_mode = ROAMSCAN_MODE_WES;
  133. if (n > MAX_ROAM_CHANNEL)
  134. n = MAX_ROAM_CHANNEL;
  135. for (i = 0; i < n; i++) {
  136. chanspec_t chanspec;
  137. if (channels[i] <= CH_MAX_2G_CHANNEL) {
  138. chanspec = band2G | band_bw | channels[i];
  139. } else {
  140. chanspec = band5G | band_bw | channels[i];
  141. }
  142. roam_cache[i].chanspec = chanspec;
  143. channel_list.channels[i] = chanspec;
  144. WL_DBG(("channel[%d] - [%02d] \n", i, channels[i]));
  145. }
  146. n_roam_cache = n;
  147. channel_list.n = n;
  148. /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
  149. * otherwise, it won't be updated
  150. */
  151. error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
  152. if (error) {
  153. WL_ERR(("Failed to set roamscan mode to %d, error = %d\n",
  154. ROAMSCAN_MODE_NORMAL, error));
  155. return error;
  156. }
  157. error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
  158. sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
  159. if (error) {
  160. WL_ERR(("Failed to set roamscan channels, error = %d\n", error));
  161. return error;
  162. }
  163. error = wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
  164. if (error) {
  165. WL_ERR(("Failed to set roamscan mode to %d, error = %d\n",
  166. ROAMSCAN_MODE_WES, error));
  167. }
  168. return error;
  169. }
  170. #endif /* WES_SUPPORT */
  171. #ifdef ESCAN_CHANNEL_CACHE
  172. void set_roam_band(int band)
  173. {
  174. roam_band = band;
  175. }
  176. void reset_roam_cache(struct bcm_cfg80211 *cfg)
  177. {
  178. if (!cfg->rcc_enabled) {
  179. return;
  180. }
  181. #ifdef WES_SUPPORT
  182. if (roamscan_mode == ROAMSCAN_MODE_WES)
  183. return;
  184. #endif /* WES_SUPPORT */
  185. n_roam_cache = 0;
  186. }
  187. void add_roam_cache(struct bcm_cfg80211 *cfg, wl_bss_info_t *bi)
  188. {
  189. int i;
  190. uint8 channel;
  191. char chanbuf[CHANSPEC_STR_LEN];
  192. if (!cfg->rcc_enabled) {
  193. return;
  194. }
  195. #ifdef WES_SUPPORT
  196. if (roamscan_mode == ROAMSCAN_MODE_WES)
  197. return;
  198. #endif /* WES_SUPPORT */
  199. if (n_roam_cache >= MAX_ROAM_CACHE)
  200. return;
  201. for (i = 0; i < n_roam_cache; i++) {
  202. if ((roam_cache[i].ssid_len == bi->SSID_len) &&
  203. (roam_cache[i].chanspec == bi->chanspec) &&
  204. (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) {
  205. /* identical one found, just return */
  206. return;
  207. }
  208. }
  209. roam_cache[n_roam_cache].ssid_len = bi->SSID_len;
  210. channel = wf_chspec_ctlchan(bi->chanspec);
  211. WL_DBG(("CHSPEC = %s, CTL %d\n", wf_chspec_ntoa_ex(bi->chanspec, chanbuf), channel));
  212. roam_cache[n_roam_cache].chanspec =
  213. (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel;
  214. (void)memcpy_s(roam_cache[n_roam_cache].ssid, bi->SSID_len, bi->SSID, bi->SSID_len);
  215. n_roam_cache++;
  216. }
  217. static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new)
  218. {
  219. int i;
  220. for (i = 0; i < n_channels; i++) {
  221. if (channels[i] == new)
  222. return TRUE;
  223. }
  224. return FALSE;
  225. }
  226. int get_roam_channel_list(int target_chan,
  227. chanspec_t *channels, int n_channels, const wlc_ssid_t *ssid, int ioctl_ver)
  228. {
  229. int i, n = 1;
  230. char chanbuf[CHANSPEC_STR_LEN];
  231. /* first index is filled with the given target channel */
  232. if (target_chan) {
  233. channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) |
  234. (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw;
  235. } else {
  236. /* If target channel is not provided, set the index to 0 */
  237. n = 0;
  238. }
  239. WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0]));
  240. #ifdef WES_SUPPORT
  241. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  242. for (i = 0; i < n_roam_cache; i++) {
  243. chanspec_t ch = roam_cache[i].chanspec;
  244. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  245. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  246. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  247. ((roam_band == WLC_BAND_2G) && is_2G) ||
  248. ((roam_band == WLC_BAND_5G) && is_5G));
  249. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  250. if (band_match && !is_duplicated_channel(channels, n, ch)) {
  251. WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__,
  252. wf_chspec_ntoa_ex(ch, chanbuf)));
  253. channels[n++] = ch;
  254. if (n >= n_channels) {
  255. WL_ERR(("Too many roam scan channels\n"));
  256. return n;
  257. }
  258. }
  259. }
  260. return n;
  261. }
  262. #endif /* WES_SUPPORT */
  263. for (i = 0; i < n_roam_cache; i++) {
  264. chanspec_t ch = roam_cache[i].chanspec;
  265. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  266. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  267. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  268. ((roam_band == WLC_BAND_2G) && is_2G) ||
  269. ((roam_band == WLC_BAND_5G) && is_5G));
  270. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  271. if ((roam_cache[i].ssid_len == ssid->SSID_len) &&
  272. band_match && !is_duplicated_channel(channels, n, ch) &&
  273. (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
  274. /* match found, add it */
  275. WL_DBG(("%s: Chanspec = %s\n", __FUNCTION__,
  276. wf_chspec_ntoa_ex(ch, chanbuf)));
  277. channels[n++] = ch;
  278. if (n >= n_channels) {
  279. WL_ERR(("Too many roam scan channels\n"));
  280. return n;
  281. }
  282. }
  283. }
  284. return n;
  285. }
  286. #endif /* ESCAN_CHANNEL_CACHE */
  287. #ifdef ROAM_CHANNEL_CACHE
  288. void print_roam_cache(struct bcm_cfg80211 *cfg)
  289. {
  290. int i;
  291. if (!cfg->rcc_enabled) {
  292. return;
  293. }
  294. WL_DBG((" %d cache\n", n_roam_cache));
  295. for (i = 0; i < n_roam_cache; i++) {
  296. roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
  297. WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
  298. roam_cache[i].ssid_len, roam_cache[i].ssid));
  299. }
  300. }
  301. static void add_roamcache_channel(wl_roam_channel_list_t *channels, chanspec_t ch)
  302. {
  303. int i;
  304. if (channels->n >= MAX_ROAM_CHANNEL) /* buffer full */
  305. return;
  306. for (i = 0; i < channels->n; i++) {
  307. if (channels->channels[i] == ch) /* already in the list */
  308. return;
  309. }
  310. channels->channels[i] = ch;
  311. channels->n++;
  312. WL_DBG((" RCC: %02d 0x%04X\n",
  313. ch & WL_CHANSPEC_CHAN_MASK, ch));
  314. }
  315. void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
  316. {
  317. int error, i, prev_channels;
  318. wl_roam_channel_list_t channel_list;
  319. char iobuf[WLC_IOCTL_SMLEN];
  320. struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
  321. wlc_ssid_t ssid;
  322. if (!cfg->rcc_enabled) {
  323. return;
  324. }
  325. #ifdef WES_SUPPORT
  326. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  327. /* no update when ROAMSCAN_MODE_WES */
  328. return;
  329. }
  330. #endif /* WES_SUPPORT */
  331. if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
  332. WL_DBG(("Not associated\n"));
  333. return;
  334. }
  335. /* need to read out the current cache list
  336. as the firmware may change dynamically
  337. */
  338. error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
  339. (void *)&channel_list, sizeof(channel_list), NULL);
  340. if (error) {
  341. WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
  342. return;
  343. }
  344. error = wldev_get_ssid(dev, &ssid);
  345. if (error) {
  346. WL_ERR(("Failed to get SSID, err=%d\n", error));
  347. return;
  348. }
  349. prev_channels = channel_list.n;
  350. for (i = 0; i < n_roam_cache; i++) {
  351. chanspec_t ch = roam_cache[i].chanspec;
  352. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  353. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  354. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  355. ((roam_band == WLC_BAND_2G) && is_2G) ||
  356. ((roam_band == WLC_BAND_5G) && is_5G));
  357. if ((roam_cache[i].ssid_len == ssid.SSID_len) &&
  358. band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
  359. /* match found, add it */
  360. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  361. add_roamcache_channel(&channel_list, ch);
  362. }
  363. }
  364. if (prev_channels != channel_list.n) {
  365. /* channel list updated */
  366. error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
  367. sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
  368. if (error) {
  369. WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
  370. }
  371. }
  372. WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error));
  373. }
  374. void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
  375. {
  376. int i, error, ioctl_ver, wes_mode;
  377. wl_roam_channel_list_t chanlist_before, chanlist_after;
  378. char iobuf[WLC_IOCTL_SMLEN];
  379. roam_band = band;
  380. error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode);
  381. if (error) {
  382. WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
  383. return;
  384. }
  385. ioctl_ver = wl_cfg80211_get_ioctl_version();
  386. /* in case of WES mode, update channel list by band based on the cache in DHD */
  387. if (wes_mode) {
  388. int n = 0;
  389. chanlist_before.n = n_roam_cache;
  390. for (n = 0; n < n_roam_cache; n++) {
  391. chanspec_t ch = roam_cache[n].chanspec;
  392. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  393. chanlist_before.channels[n] = CHSPEC_CHANNEL(ch) |
  394. (is_2G ? band2G : band5G) | band_bw;
  395. }
  396. } else {
  397. if (band == WLC_BAND_AUTO) {
  398. return;
  399. }
  400. error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
  401. (void *)&chanlist_before, sizeof(wl_roam_channel_list_t), NULL);
  402. if (error) {
  403. WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
  404. return;
  405. }
  406. }
  407. chanlist_after.n = 0;
  408. /* filtering by the given band */
  409. for (i = 0; i < chanlist_before.n; i++) {
  410. chanspec_t chspec = chanlist_before.channels[i];
  411. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec);
  412. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec);
  413. bool band_match = ((band == WLC_BAND_AUTO) ||
  414. ((band == WLC_BAND_2G) && is_2G) ||
  415. ((band == WLC_BAND_5G) && is_5G));
  416. if (band_match) {
  417. chanlist_after.channels[chanlist_after.n++] = chspec;
  418. }
  419. }
  420. if (wes_mode) {
  421. /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
  422. * otherwise, it won't be updated
  423. */
  424. wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
  425. error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
  426. sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
  427. if (error) {
  428. WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
  429. }
  430. wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
  431. } else {
  432. if (chanlist_before.n == chanlist_after.n) {
  433. return;
  434. }
  435. error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
  436. sizeof(wl_roam_channel_list_t), iobuf, sizeof(iobuf), NULL);
  437. if (error) {
  438. WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
  439. }
  440. }
  441. }
  442. #endif /* ROAM_CHANNEL_CACHE */
  443. #endif /* ESCAN_CHANNEL_CACHE */