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 brcmf_pno_config_le pno_cfg; 186 struct cfg80211_ssid *ssid; 187 u16 chan; 188 int i, ret; 189 190 /* clean up everything */ 191 ret = brcmf_pno_clean(ifp); 192 if (ret < 0) { 193 brcmf_err("failed error=%d\n", ret); 194 return ret; 195 } 196 197 /* configure pno */ 198 ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0); 199 if (ret < 0) 200 return ret; 201 202 /* configure random mac */ 203 if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 204 ret = brcmf_pno_set_random(ifp, req->mac_addr, 205 req->mac_addr_mask); 206 if (ret < 0) 207 return ret; 208 } 209 210 /* configure channels to use */ 211 for (i = 0; i < req->n_channels; i++) { 212 chan = req->channels[i]->hw_value; 213 pno_cfg.channel_list[i] = cpu_to_le16(chan); 214 } 215 if (req->n_channels) { 216 pno_cfg.channel_num = cpu_to_le32(req->n_channels); 217 brcmf_pno_channel_config(ifp, &pno_cfg); 218 } 219 220 /* configure each match set */ 221 for (i = 0; i < req->n_match_sets; i++) { 222 ssid = &req->match_sets[i].ssid; 223 if (!ssid->ssid_len) { 224 brcmf_err("skip broadcast ssid\n"); 225 continue; 226 } 227 228 ret = brcmf_pno_add_ssid(ifp, ssid, 229 brcmf_is_ssid_active(ssid, req)); 230 if (ret < 0) 231 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", 232 ret == 0 ? "set" : "failed", ssid->ssid); 233 } 234 /* Enable the PNO */ 235 ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); 236 if (ret < 0) 237 brcmf_err("PNO enable failed!! ret=%d\n", ret); 238 239 return ret; 240 } 241 242