pno.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright (c) 2016 Broadcom
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  11. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  13. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <linux/netdevice.h>
  17. #include <net/cfg80211.h>
  18. #include "core.h"
  19. #include "debug.h"
  20. #include "fwil.h"
  21. #include "fwil_types.h"
  22. #include "cfg80211.h"
  23. #include "pno.h"
  24. #define BRCMF_PNO_VERSION 2
  25. #define BRCMF_PNO_REPEAT 4
  26. #define BRCMF_PNO_FREQ_EXPO_MAX 3
  27. #define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3
  28. #define BRCMF_PNO_ENABLE_BD_SCAN_BIT 5
  29. #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
  30. #define BRCMF_PNO_REPORT_SEPARATELY_BIT 11
  31. #define BRCMF_PNO_SCAN_INCOMPLETE 0
  32. #define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
  33. #define BRCMF_PNO_HIDDEN_BIT 2
  34. #define BRCMF_PNO_SCHED_SCAN_PERIOD 30
  35. static int brcmf_pno_channel_config(struct brcmf_if *ifp,
  36. struct brcmf_pno_config_le *cfg)
  37. {
  38. cfg->reporttype = 0;
  39. cfg->flags = 0;
  40. return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg));
  41. }
  42. static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
  43. u32 mscan, u32 bestn)
  44. {
  45. struct brcmf_pno_param_le pfn_param;
  46. u16 flags;
  47. u32 pfnmem;
  48. s32 err;
  49. memset(&pfn_param, 0, sizeof(pfn_param));
  50. pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
  51. /* set extra pno params */
  52. flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
  53. BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) |
  54. BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
  55. pfn_param.repeat = BRCMF_PNO_REPEAT;
  56. pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
  57. /* set up pno scan fr */
  58. if (scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) {
  59. brcmf_dbg(SCAN, "scan period too small, using minimum\n");
  60. scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD;
  61. }
  62. pfn_param.scan_freq = cpu_to_le32(scan_freq);
  63. if (mscan) {
  64. pfnmem = bestn;
  65. /* set bestn in firmware */
  66. err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem);
  67. if (err < 0) {
  68. brcmf_err("failed to set pfnmem\n");
  69. goto exit;
  70. }
  71. /* get max mscan which the firmware supports */
  72. err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem);
  73. if (err < 0) {
  74. brcmf_err("failed to get pfnmem\n");
  75. goto exit;
  76. }
  77. mscan = min_t(u32, mscan, pfnmem);
  78. pfn_param.mscan = mscan;
  79. pfn_param.bestn = bestn;
  80. flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT);
  81. brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn);
  82. }
  83. pfn_param.flags = cpu_to_le16(flags);
  84. err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
  85. sizeof(pfn_param));
  86. if (err)
  87. brcmf_err("pfn_set failed, err=%d\n", err);
  88. exit:
  89. return err;
  90. }
  91. static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr,
  92. u8 *mac_mask)
  93. {
  94. struct brcmf_pno_macaddr_le pfn_mac;
  95. int err, i;
  96. pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
  97. pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
  98. memcpy(pfn_mac.mac, mac_addr, ETH_ALEN);
  99. for (i = 0; i < ETH_ALEN; i++) {
  100. pfn_mac.mac[i] &= mac_mask[i];
  101. pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
  102. }
  103. /* Clear multi bit */
  104. pfn_mac.mac[0] &= 0xFE;
  105. /* Set locally administered */
  106. pfn_mac.mac[0] |= 0x02;
  107. err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
  108. sizeof(pfn_mac));
  109. if (err)
  110. brcmf_err("pfn_macaddr failed, err=%d\n", err);
  111. return err;
  112. }
  113. static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
  114. bool active)
  115. {
  116. struct brcmf_pno_net_param_le pfn;
  117. pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
  118. pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
  119. pfn.wsec = cpu_to_le32(0);
  120. pfn.infra = cpu_to_le32(1);
  121. pfn.flags = 0;
  122. if (active)
  123. pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
  124. pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len);
  125. memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len);
  126. return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn));
  127. }
  128. static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid,
  129. struct cfg80211_sched_scan_request *req)
  130. {
  131. int i;
  132. if (!ssid || !req->ssids || !req->n_ssids)
  133. return false;
  134. for (i = 0; i < req->n_ssids; i++) {
  135. if (ssid->ssid_len == req->ssids[i].ssid_len) {
  136. if (!strncmp(ssid->ssid, req->ssids[i].ssid,
  137. ssid->ssid_len))
  138. return true;
  139. }
  140. }
  141. return false;
  142. }
  143. int brcmf_pno_clean(struct brcmf_if *ifp)
  144. {
  145. int ret;
  146. /* Disable pfn */
  147. ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0);
  148. if (ret == 0) {
  149. /* clear pfn */
  150. ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0);
  151. }
  152. if (ret < 0)
  153. brcmf_err("failed code %d\n", ret);
  154. return ret;
  155. }
  156. int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
  157. struct cfg80211_sched_scan_request *req)
  158. {
  159. struct brcmu_d11inf *d11inf;
  160. struct brcmf_pno_config_le pno_cfg;
  161. struct cfg80211_ssid *ssid;
  162. u16 chan;
  163. int i, ret;
  164. /* clean up everything */
  165. ret = brcmf_pno_clean(ifp);
  166. if (ret < 0) {
  167. brcmf_err("failed error=%d\n", ret);
  168. return ret;
  169. }
  170. /* configure pno */
  171. ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0);
  172. if (ret < 0)
  173. return ret;
  174. /* configure random mac */
  175. if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
  176. ret = brcmf_pno_set_random(ifp, req->mac_addr,
  177. req->mac_addr_mask);
  178. if (ret < 0)
  179. return ret;
  180. }
  181. /* configure channels to use */
  182. d11inf = &ifp->drvr->config->d11inf;
  183. for (i = 0; i < req->n_channels; i++) {
  184. chan = req->channels[i]->hw_value;
  185. pno_cfg.channel_list[i] = cpu_to_le16(chan);
  186. }
  187. if (req->n_channels) {
  188. pno_cfg.channel_num = cpu_to_le32(req->n_channels);
  189. brcmf_pno_channel_config(ifp, &pno_cfg);
  190. }
  191. /* configure each match set */
  192. for (i = 0; i < req->n_match_sets; i++) {
  193. ssid = &req->match_sets[i].ssid;
  194. if (!ssid->ssid_len) {
  195. brcmf_err("skip broadcast ssid\n");
  196. continue;
  197. }
  198. ret = brcmf_pno_add_ssid(ifp, ssid,
  199. brcmf_is_ssid_active(ssid, req));
  200. if (ret < 0)
  201. brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
  202. ret == 0 ? "set" : "failed", ssid->ssid);
  203. }
  204. /* Enable the PNO */
  205. ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
  206. if (ret < 0)
  207. brcmf_err("PNO enable failed!! ret=%d\n", ret);
  208. return ret;
  209. }