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