PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/net/wireless/bcmdhd/wl_roam.c

https://gitlab.com/SerenityS/slteskt_kernel
C | 424 lines | 315 code | 69 blank | 40 comment | 97 complexity | 24e5e77f67aa4519929d103b138f6e5e MD5 | raw file
  1. /*
  2. * Linux roam cache
  3. *
  4. * Copyright (C) 1999-2014, 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: wl_roam.c 459591 2014-03-04 08:53:45Z $
  25. */
  26. #include <typedefs.h>
  27. #include <osl.h>
  28. #include <bcmwifi_channels.h>
  29. #include <wlioctl.h>
  30. #include <bcmutils.h>
  31. #ifdef WL_CFG80211
  32. #include <wl_cfg80211.h>
  33. #endif
  34. #include <wldev_common.h>
  35. #define MAX_ROAM_CACHE 100
  36. #define MAX_CHANNEL_LIST 20
  37. #define MAX_SSID_BUFSIZE 36
  38. #define ROAMSCAN_MODE_NORMAL 0
  39. #define ROAMSCAN_MODE_WES 1
  40. typedef struct {
  41. chanspec_t chanspec;
  42. int ssid_len;
  43. char ssid[MAX_SSID_BUFSIZE];
  44. } roam_channel_cache;
  45. typedef struct {
  46. int n;
  47. chanspec_t channels[MAX_CHANNEL_LIST];
  48. } channel_list_t;
  49. static int n_roam_cache = 0;
  50. static int roam_band = WLC_BAND_AUTO;
  51. static roam_channel_cache roam_cache[MAX_ROAM_CACHE];
  52. static uint band2G, band5G, band_bw;
  53. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  54. static int roamscan_mode = ROAMSCAN_MODE_NORMAL;
  55. #endif
  56. void init_roam(int ioctl_ver)
  57. {
  58. #ifdef D11AC_IOTYPES
  59. if (ioctl_ver == 1) {
  60. /* legacy chanspec */
  61. band2G = WL_LCHANSPEC_BAND_2G;
  62. band5G = WL_LCHANSPEC_BAND_5G;
  63. band_bw = WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE;
  64. } else {
  65. band2G = WL_CHANSPEC_BAND_2G;
  66. band5G = WL_CHANSPEC_BAND_5G;
  67. band_bw = WL_CHANSPEC_BW_20;
  68. }
  69. #else
  70. band2G = WL_CHANSPEC_BAND_2G;
  71. band5G = WL_CHANSPEC_BAND_5G;
  72. band_bw = WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
  73. #endif /* D11AC_IOTYPES */
  74. n_roam_cache = 0;
  75. roam_band = WLC_BAND_AUTO;
  76. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  77. roamscan_mode = ROAMSCAN_MODE_NORMAL;
  78. #endif
  79. }
  80. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  81. int get_roamscan_mode(struct net_device *dev, int *mode)
  82. {
  83. *mode = roamscan_mode;
  84. return 0;
  85. }
  86. int set_roamscan_mode(struct net_device *dev, int mode)
  87. {
  88. int error = 0;
  89. roamscan_mode = mode;
  90. n_roam_cache = 0;
  91. error = wldev_iovar_setint(dev, "roamscan_mode", mode);
  92. if (error) {
  93. WL_ERR(("Failed to set roamscan mode to %d, error = %d\n", mode, error));
  94. }
  95. return error;
  96. }
  97. int get_roamscan_channel_list(struct net_device *dev, unsigned char channels[])
  98. {
  99. int n = 0;
  100. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  101. for (n = 0; n < n_roam_cache; n++) {
  102. channels[n] = roam_cache[n].chanspec & WL_CHANSPEC_CHAN_MASK;
  103. WL_DBG(("channel[%d] - [%02d] \n", n, channels[n]));
  104. }
  105. }
  106. return n;
  107. }
  108. int set_roamscan_channel_list(struct net_device *dev,
  109. unsigned char n, unsigned char channels[], int ioctl_ver)
  110. {
  111. int i;
  112. int error;
  113. channel_list_t channel_list;
  114. char iobuf[WLC_IOCTL_SMLEN];
  115. roamscan_mode = ROAMSCAN_MODE_WES;
  116. if (n > MAX_CHANNEL_LIST)
  117. n = MAX_CHANNEL_LIST;
  118. for (i = 0; i < n; i++) {
  119. chanspec_t chanspec;
  120. if (channels[i] <= CH_MAX_2G_CHANNEL) {
  121. chanspec = band2G | band_bw | channels[i];
  122. } else {
  123. chanspec = band5G | band_bw | channels[i];
  124. }
  125. roam_cache[i].chanspec = chanspec;
  126. channel_list.channels[i] = chanspec;
  127. WL_DBG(("channel[%d] - [%02d] \n", i, channels[i]));
  128. }
  129. n_roam_cache = n;
  130. channel_list.n = n;
  131. /* need to set ROAMSCAN_MODE_NORMAL to update roamscan_channels,
  132. * otherwise, it won't be updated
  133. */
  134. wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_NORMAL);
  135. error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
  136. sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
  137. if (error) {
  138. WL_DBG(("Failed to set roamscan channels, error = %d\n", error));
  139. }
  140. wldev_iovar_setint(dev, "roamscan_mode", ROAMSCAN_MODE_WES);
  141. return error;
  142. }
  143. #endif /* WES_SUPPORT */
  144. void set_roam_band(int band)
  145. {
  146. roam_band = band;
  147. }
  148. void reset_roam_cache(void)
  149. {
  150. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  151. if (roamscan_mode == ROAMSCAN_MODE_WES)
  152. return;
  153. #endif
  154. n_roam_cache = 0;
  155. }
  156. void add_roam_cache(wl_bss_info_t *bi)
  157. {
  158. int i;
  159. uint8 channel;
  160. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  161. if (roamscan_mode == ROAMSCAN_MODE_WES)
  162. return;
  163. #endif
  164. if (n_roam_cache >= MAX_ROAM_CACHE)
  165. return;
  166. for (i = 0; i < n_roam_cache; i++) {
  167. if ((roam_cache[i].ssid_len == bi->SSID_len) &&
  168. (roam_cache[i].chanspec == bi->chanspec) &&
  169. (memcmp(roam_cache[i].ssid, bi->SSID, bi->SSID_len) == 0)) {
  170. /* identical one found, just return */
  171. return;
  172. }
  173. }
  174. roam_cache[n_roam_cache].ssid_len = bi->SSID_len;
  175. channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
  176. WL_DBG(("CHSPEC 0x%X %d, CTL %d\n",
  177. bi->chanspec, CHSPEC_CHANNEL(bi->chanspec), bi->ctl_ch));
  178. roam_cache[n_roam_cache].chanspec =
  179. (channel <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw | channel;
  180. memcpy(roam_cache[n_roam_cache].ssid, bi->SSID, bi->SSID_len);
  181. n_roam_cache++;
  182. }
  183. static bool is_duplicated_channel(const chanspec_t *channels, int n_channels, chanspec_t new)
  184. {
  185. int i;
  186. for (i = 0; i < n_channels; i++) {
  187. if (channels[i] == new)
  188. return TRUE;
  189. }
  190. return FALSE;
  191. }
  192. int get_roam_channel_list(int target_chan,
  193. chanspec_t *channels, const wlc_ssid_t *ssid, int ioctl_ver)
  194. {
  195. int i, n = 1;
  196. /* first index is filled with the given target channel */
  197. channels[0] = (target_chan & WL_CHANSPEC_CHAN_MASK) |
  198. (target_chan <= CH_MAX_2G_CHANNEL ? band2G : band5G) | band_bw;
  199. WL_DBG((" %s: %03d 0x%04X\n", __FUNCTION__, target_chan, channels[0]));
  200. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  201. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  202. for (i = 0; i < n_roam_cache; i++) {
  203. chanspec_t ch = roam_cache[i].chanspec;
  204. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  205. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  206. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  207. ((roam_band == WLC_BAND_2G) && is_2G) ||
  208. ((roam_band == WLC_BAND_5G) && is_5G));
  209. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  210. if (band_match && !is_duplicated_channel(channels, n, ch)) {
  211. WL_DBG((" %s: %03d(0x%X)\n", __FUNCTION__, CHSPEC_CHANNEL(ch), ch));
  212. channels[n++] = ch;
  213. }
  214. }
  215. return n;
  216. }
  217. #endif /* WES_SUPPORT */
  218. for (i = 0; i < n_roam_cache; i++) {
  219. chanspec_t ch = roam_cache[i].chanspec;
  220. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  221. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  222. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  223. ((roam_band == WLC_BAND_2G) && is_2G) ||
  224. ((roam_band == WLC_BAND_5G) && is_5G));
  225. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  226. if ((roam_cache[i].ssid_len == ssid->SSID_len) &&
  227. band_match && !is_duplicated_channel(channels, n, ch) &&
  228. (memcmp(roam_cache[i].ssid, ssid->SSID, ssid->SSID_len) == 0)) {
  229. /* match found, add it */
  230. WL_DBG((" %s: %03d(0x%04X)\n", __FUNCTION__,
  231. CHSPEC_CHANNEL(ch), ch));
  232. channels[n++] = ch;
  233. }
  234. }
  235. return n;
  236. }
  237. void print_roam_cache(void)
  238. {
  239. int i;
  240. WL_DBG((" %d cache\n", n_roam_cache));
  241. for (i = 0; i < n_roam_cache; i++) {
  242. roam_cache[i].ssid[roam_cache[i].ssid_len] = 0;
  243. WL_DBG(("0x%02X %02d %s\n", roam_cache[i].chanspec,
  244. roam_cache[i].ssid_len, roam_cache[i].ssid));
  245. }
  246. }
  247. static void add_roamcache_channel(channel_list_t *channels, chanspec_t ch)
  248. {
  249. int i;
  250. if (channels->n >= MAX_CHANNEL_LIST) /* buffer full */
  251. return;
  252. for (i = 0; i < channels->n; i++) {
  253. if (channels->channels[i] == ch) /* already in the list */
  254. return;
  255. }
  256. channels->channels[i] = ch;
  257. channels->n++;
  258. WL_DBG((" RCC: %02d 0x%04X\n",
  259. ch & WL_CHANSPEC_CHAN_MASK, ch));
  260. }
  261. void update_roam_cache(struct bcm_cfg80211 *cfg, int ioctl_ver)
  262. {
  263. int error, i, prev_channels;
  264. channel_list_t channel_list;
  265. char iobuf[WLC_IOCTL_SMLEN];
  266. struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
  267. wlc_ssid_t ssid;
  268. #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT)
  269. if (roamscan_mode == ROAMSCAN_MODE_WES) {
  270. /* no update when ROAMSCAN_MODE_WES */
  271. return;
  272. }
  273. #endif
  274. if (!wl_get_drv_status(cfg, CONNECTED, dev)) {
  275. WL_DBG(("Not associated\n"));
  276. return;
  277. }
  278. /* need to read out the current cache list
  279. as the firmware may change dynamically
  280. */
  281. error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
  282. (void *)&channel_list, sizeof(channel_list), NULL);
  283. WL_DBG(("%d AP, %d cache item(s), err=%d\n", n_roam_cache, channel_list.n, error));
  284. error = wldev_get_ssid(dev, &ssid);
  285. if (error) {
  286. WL_ERR(("Failed to get SSID, err=%d\n", error));
  287. return;
  288. }
  289. prev_channels = channel_list.n;
  290. for (i = 0; i < n_roam_cache; i++) {
  291. chanspec_t ch = roam_cache[i].chanspec;
  292. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(ch) : CHSPEC_IS2G(ch);
  293. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(ch) : CHSPEC_IS5G(ch);
  294. bool band_match = ((roam_band == WLC_BAND_AUTO) ||
  295. ((roam_band == WLC_BAND_2G) && is_2G) ||
  296. ((roam_band == WLC_BAND_5G) && is_5G));
  297. if ((roam_cache[i].ssid_len == ssid.SSID_len) &&
  298. band_match && (memcmp(roam_cache[i].ssid, ssid.SSID, ssid.SSID_len) == 0)) {
  299. /* match found, add it */
  300. ch = CHSPEC_CHANNEL(ch) | (is_2G ? band2G : band5G) | band_bw;
  301. add_roamcache_channel(&channel_list, ch);
  302. }
  303. }
  304. if (prev_channels != channel_list.n) {
  305. /* channel list updated */
  306. error = wldev_iovar_setbuf(dev, "roamscan_channels", &channel_list,
  307. sizeof(channel_list), iobuf, sizeof(iobuf), NULL);
  308. if (error) {
  309. WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
  310. }
  311. }
  312. }
  313. void wl_update_roamscan_cache_by_band(struct net_device *dev, int band)
  314. {
  315. int i, error, ioctl_ver, wes_mode;
  316. channel_list_t chanlist_before, chanlist_after;
  317. char iobuf[WLC_IOCTL_SMLEN];
  318. roam_band = band;
  319. if (band == WLC_BAND_AUTO)
  320. return;
  321. error = wldev_iovar_getint(dev, "roamscan_mode", &wes_mode);
  322. if (error) {
  323. WL_ERR(("Failed to get roamscan mode, error = %d\n", error));
  324. return;
  325. }
  326. /* in case of WES mode, then skip the update */
  327. if (wes_mode)
  328. return;
  329. error = wldev_iovar_getbuf(dev, "roamscan_channels", 0, 0,
  330. (void *)&chanlist_before, sizeof(channel_list_t), NULL);
  331. if (error) {
  332. WL_ERR(("Failed to get roamscan channels, error = %d\n", error));
  333. return;
  334. }
  335. ioctl_ver = wl_cfg80211_get_ioctl_version();
  336. chanlist_after.n = 0;
  337. /* filtering by the given band */
  338. for (i = 0; i < chanlist_before.n; i++) {
  339. chanspec_t chspec = chanlist_before.channels[i];
  340. bool is_2G = ioctl_ver == 1 ? LCHSPEC_IS2G(chspec) : CHSPEC_IS2G(chspec);
  341. bool is_5G = ioctl_ver == 1 ? LCHSPEC_IS5G(chspec) : CHSPEC_IS5G(chspec);
  342. bool band_match = ((band == WLC_BAND_2G) && is_2G) ||
  343. ((band == WLC_BAND_5G) && is_5G);
  344. if (band_match) {
  345. chanlist_after.channels[chanlist_after.n++] = chspec;
  346. }
  347. }
  348. if (chanlist_before.n == chanlist_after.n)
  349. return;
  350. error = wldev_iovar_setbuf(dev, "roamscan_channels", &chanlist_after,
  351. sizeof(channel_list_t), iobuf, sizeof(iobuf), NULL);
  352. if (error) {
  353. WL_ERR(("Failed to update roamscan channels, error = %d\n", error));
  354. }
  355. }