]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - 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
CommitLineData
ac55136f
AVS
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"
ac55136f
AVS
21#include "fwil.h"
22#include "fwil_types.h"
331e7894
AVS
23#include "cfg80211.h"
24#include "pno.h"
ac55136f
AVS
25
26#define BRCMF_PNO_VERSION 2
ac55136f
AVS
27#define BRCMF_PNO_REPEAT 4
28#define BRCMF_PNO_FREQ_EXPO_MAX 3
fca6cb2f
AVS
29#define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3
30#define BRCMF_PNO_ENABLE_BD_SCAN_BIT 5
ac55136f 31#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
fca6cb2f 32#define BRCMF_PNO_REPORT_SEPARATELY_BIT 11
ac55136f 33#define BRCMF_PNO_SCAN_INCOMPLETE 0
3e2e86ab
AVS
34#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
35#define BRCMF_PNO_HIDDEN_BIT 2
3e48611d 36#define BRCMF_PNO_SCHED_SCAN_PERIOD 30
ac55136f 37
331e7894
AVS
38static 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
3e48611d
AVS
47static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
48 u32 mscan, u32 bestn)
ac55136f
AVS
49{
50 struct brcmf_pno_param_le pfn_param;
fca6cb2f
AVS
51 u16 flags;
52 u32 pfnmem;
ac55136f 53 s32 err;
ac55136f
AVS
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 */
fca6cb2f
AVS
59 flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
60 BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) |
61 BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
ac55136f
AVS
62 pfn_param.repeat = BRCMF_PNO_REPEAT;
63 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
64
65 /* set up pno scan fr */
c6989fd5
AVS
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 }
fca6cb2f
AVS
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 }
ac55136f 93
fca6cb2f 94 pfn_param.flags = cpu_to_le16(flags);
ac55136f
AVS
95 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
96 sizeof(pfn_param));
fca6cb2f 97 if (err)
ac55136f 98 brcmf_err("pfn_set failed, err=%d\n", err);
ac55136f 99
fca6cb2f
AVS
100exit:
101 return err;
102}
103
3e48611d
AVS
104static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr,
105 u8 *mac_mask)
fca6cb2f
AVS
106{
107 struct brcmf_pno_macaddr_le pfn_mac;
108 int err, i;
ac55136f
AVS
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
fca6cb2f 113 memcpy(pfn_mac.mac, mac_addr, ETH_ALEN);
ac55136f
AVS
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
3e48611d
AVS
131static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
132 bool active)
3e2e86ab
AVS
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);
2b66325d 140 pfn.flags = 0;
3e2e86ab
AVS
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
3e48611d
AVS
148static 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
166int 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
182int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
183 struct cfg80211_sched_scan_request *req)
184{
331e7894
AVS
185 struct brcmu_d11inf *d11inf;
186 struct brcmf_pno_config_le pno_cfg;
3e48611d 187 struct cfg80211_ssid *ssid;
331e7894 188 u16 chan;
3e48611d
AVS
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 */
c6989fd5 199 ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0);
3e48611d
AVS
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
331e7894
AVS
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
3e48611d
AVS
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