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 19 #include "core.h" 20 #include "debug.h" 21 #include "fwil.h" 22 #include "fwil_types.h" 23 #include "cfg80211.h" 24 #include "pno.h" 25 26 #define BRCMF_PNO_VERSION 2 27 #define BRCMF_PNO_REPEAT 4 28 #define BRCMF_PNO_FREQ_EXPO_MAX 3 29 #define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3 30 #define BRCMF_PNO_ENABLE_BD_SCAN_BIT 5 31 #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 32 #define BRCMF_PNO_REPORT_SEPARATELY_BIT 11 33 #define BRCMF_PNO_SCAN_INCOMPLETE 0 34 #define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF 35 #define BRCMF_PNO_HIDDEN_BIT 2 36 #define BRCMF_PNO_SCHED_SCAN_PERIOD 30 37 38 static int brcmf_pno_channel_config(struct brcmf_if *ifp, 39 struct brcmf_pno_config_le *cfg) 40 { 41 cfg->reporttype = 0; 42 cfg->flags = 0; 43 44 return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg)); 45 } 46 47 static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, 48 u32 mscan, u32 bestn) 49 { 50 struct brcmf_pno_param_le pfn_param; 51 u16 flags; 52 u32 pfnmem; 53 s32 err; 54 55 memset(&pfn_param, 0, sizeof(pfn_param)); 56 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); 57 58 /* set extra pno params */ 59 flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) | 60 BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) | 61 BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); 62 pfn_param.repeat = BRCMF_PNO_REPEAT; 63 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; 64 65 /* set up pno scan fr */ 66 if (scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) { 67 brcmf_dbg(SCAN, "scan period too small, using minimum\n"); 68 scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD; 69 } 70 pfn_param.scan_freq = cpu_to_le32(scan_freq); 71 72 if (mscan) { 73 pfnmem = bestn; 74 75 /* set bestn in firmware */ 76 err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem); 77 if (err < 0) { 78 brcmf_err("failed to set pfnmem\n"); 79 goto exit; 80 } 81 /* get max mscan which the firmware supports */ 82 err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem); 83 if (err < 0) { 84 brcmf_err("failed to get pfnmem\n"); 85 goto exit; 86 } 87 mscan = min_t(u32, mscan, pfnmem); 88 pfn_param.mscan = mscan; 89 pfn_param.bestn = bestn; 90 flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT); 91 brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn); 92 } 93 94 pfn_param.flags = cpu_to_le16(flags); 95 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, 96 sizeof(pfn_param)); 97 if (err) 98 brcmf_err("pfn_set failed, err=%d\n", err); 99 100 exit: 101 return err; 102 } 103 104 static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, 105 u8 *mac_mask) 106 { 107 struct brcmf_pno_macaddr_le pfn_mac; 108 int err, i; 109 110 pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; 111 pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; 112 113 memcpy(pfn_mac.mac, mac_addr, ETH_ALEN); 114 for (i = 0; i < ETH_ALEN; i++) { 115 pfn_mac.mac[i] &= mac_mask[i]; 116 pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); 117 } 118 /* Clear multi bit */ 119 pfn_mac.mac[0] &= 0xFE; 120 /* Set locally administered */ 121 pfn_mac.mac[0] |= 0x02; 122 123 err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, 124 sizeof(pfn_mac)); 125 if (err) 126 brcmf_err("pfn_macaddr failed, err=%d\n", err); 127 128 return err; 129 } 130 131 static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, 132 bool active) 133 { 134 struct brcmf_pno_net_param_le pfn; 135 136 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); 137 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); 138 pfn.wsec = cpu_to_le32(0); 139 pfn.infra = cpu_to_le32(1); 140 pfn.flags = 0; 141 if (active) 142 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); 143 pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len); 144 memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len); 145 return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn)); 146 } 147 148 static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, 149 struct cfg80211_sched_scan_request *req) 150 { 151 int i; 152 153 if (!ssid || !req->ssids || !req->n_ssids) 154 return false; 155 156 for (i = 0; i < req->n_ssids; i++) { 157 if (ssid->ssid_len == req->ssids[i].ssid_len) { 158 if (!strncmp(ssid->ssid, req->ssids[i].ssid, 159 ssid->ssid_len)) 160 return true; 161 } 162 } 163 return false; 164 } 165 166 int brcmf_pno_clean(struct brcmf_if *ifp) 167 { 168 int ret; 169 170 /* Disable pfn */ 171 ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); 172 if (ret == 0) { 173 /* clear pfn */ 174 ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); 175 } 176 if (ret < 0) 177 brcmf_err("failed code %d\n", ret); 178 179 return ret; 180 } 181 182 int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, 183 struct cfg80211_sched_scan_request *req) 184 { 185 struct brcmu_d11inf *d11inf; 186 struct brcmf_pno_config_le pno_cfg; 187 struct cfg80211_ssid *ssid; 188 u16 chan; 189 int i, ret; 190 191 /* clean up everything */ 192 ret = brcmf_pno_clean(ifp); 193 if (ret < 0) { 194 brcmf_err("failed error=%d\n", ret); 195 return ret; 196 } 197 198 /* configure pno */ 199 ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0); 200 if (ret < 0) 201 return ret; 202 203 /* configure random mac */ 204 if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 205 ret = brcmf_pno_set_random(ifp, req->mac_addr, 206 req->mac_addr_mask); 207 if (ret < 0) 208 return ret; 209 } 210 211 /* configure channels to use */ 212 d11inf = &ifp->drvr->config->d11inf; 213 for (i = 0; i < req->n_channels; i++) { 214 chan = req->channels[i]->hw_value; 215 pno_cfg.channel_list[i] = cpu_to_le16(chan); 216 } 217 if (req->n_channels) { 218 pno_cfg.channel_num = cpu_to_le32(req->n_channels); 219 brcmf_pno_channel_config(ifp, &pno_cfg); 220 } 221 222 /* configure each match set */ 223 for (i = 0; i < req->n_match_sets; i++) { 224 ssid = &req->match_sets[i].ssid; 225 if (!ssid->ssid_len) { 226 brcmf_err("skip broadcast ssid\n"); 227 continue; 228 } 229 230 ret = brcmf_pno_add_ssid(ifp, ssid, 231 brcmf_is_ssid_active(ssid, req)); 232 if (ret < 0) 233 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", 234 ret == 0 ? "set" : "failed", ssid->ssid); 235 } 236 /* Enable the PNO */ 237 ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); 238 if (ret < 0) 239 brcmf_err("PNO enable failed!! ret=%d\n", ret); 240 241 return ret; 242 } 243 244