]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
drivers: add explicit interrupt.h includes
[mirror_ubuntu-artful-kernel.git] / drivers / net / wireless / broadcom / brcm80211 / brcmfmac / pno.c
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