]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[linux.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