2 * Copyright (c) 2016 Broadcom
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.
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.
16 #include <linux/netdevice.h>
17 #include <net/cfg80211.h>
22 #include "fwil_types.h"
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
38 static int brcmf_pno_channel_config(struct brcmf_if
*ifp
,
39 struct brcmf_pno_config_le
*cfg
)
44 return brcmf_fil_iovar_data_set(ifp
, "pfn_cfg", cfg
, sizeof(*cfg
));
47 static int brcmf_pno_config(struct brcmf_if
*ifp
, u32 scan_freq
,
50 struct brcmf_pno_param_le pfn_param
;
55 memset(&pfn_param
, 0, sizeof(pfn_param
));
56 pfn_param
.version
= cpu_to_le32(BRCMF_PNO_VERSION
);
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
;
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
;
70 pfn_param
.scan_freq
= cpu_to_le32(scan_freq
);
75 /* set bestn in firmware */
76 err
= brcmf_fil_iovar_int_set(ifp
, "pfnmem", pfnmem
);
78 brcmf_err("failed to set pfnmem\n");
81 /* get max mscan which the firmware supports */
82 err
= brcmf_fil_iovar_int_get(ifp
, "pfnmem", &pfnmem
);
84 brcmf_err("failed to get pfnmem\n");
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
);
94 pfn_param
.flags
= cpu_to_le16(flags
);
95 err
= brcmf_fil_iovar_data_set(ifp
, "pfn_set", &pfn_param
,
98 brcmf_err("pfn_set failed, err=%d\n", err
);
104 static int brcmf_pno_set_random(struct brcmf_if
*ifp
, u8
*mac_addr
,
107 struct brcmf_pno_macaddr_le pfn_mac
;
110 pfn_mac
.version
= BRCMF_PFN_MACADDR_CFG_VER
;
111 pfn_mac
.flags
= BRCMF_PFN_MAC_OUI_ONLY
| BRCMF_PFN_SET_MAC_UNASSOC
;
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
]);
118 /* Clear multi bit */
119 pfn_mac
.mac
[0] &= 0xFE;
120 /* Set locally administered */
121 pfn_mac
.mac
[0] |= 0x02;
123 err
= brcmf_fil_iovar_data_set(ifp
, "pfn_macaddr", &pfn_mac
,
126 brcmf_err("pfn_macaddr failed, err=%d\n", err
);
131 static int brcmf_pno_add_ssid(struct brcmf_if
*ifp
, struct cfg80211_ssid
*ssid
,
134 struct brcmf_pno_net_param_le pfn
;
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);
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
));
148 static bool brcmf_is_ssid_active(struct cfg80211_ssid
*ssid
,
149 struct cfg80211_sched_scan_request
*req
)
153 if (!ssid
|| !req
->ssids
|| !req
->n_ssids
)
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
,
166 int brcmf_pno_clean(struct brcmf_if
*ifp
)
171 ret
= brcmf_fil_iovar_int_set(ifp
, "pfn", 0);
174 ret
= brcmf_fil_iovar_data_set(ifp
, "pfnclear", NULL
, 0);
177 brcmf_err("failed code %d\n", ret
);
182 int brcmf_pno_start_sched_scan(struct brcmf_if
*ifp
,
183 struct cfg80211_sched_scan_request
*req
)
185 struct brcmu_d11inf
*d11inf
;
186 struct brcmf_pno_config_le pno_cfg
;
187 struct cfg80211_ssid
*ssid
;
191 /* clean up everything */
192 ret
= brcmf_pno_clean(ifp
);
194 brcmf_err("failed error=%d\n", ret
);
199 ret
= brcmf_pno_config(ifp
, req
->scan_plans
[0].interval
, 0, 0);
203 /* configure random mac */
204 if (req
->flags
& NL80211_SCAN_FLAG_RANDOM_ADDR
) {
205 ret
= brcmf_pno_set_random(ifp
, req
->mac_addr
,
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
);
217 if (req
->n_channels
) {
218 pno_cfg
.channel_num
= cpu_to_le32(req
->n_channels
);
219 brcmf_pno_channel_config(ifp
, &pno_cfg
);
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");
230 ret
= brcmf_pno_add_ssid(ifp
, ssid
,
231 brcmf_is_ssid_active(ssid
, req
));
233 brcmf_dbg(SCAN
, ">>> PNO filter %s for ssid (%s)\n",
234 ret
== 0 ? "set" : "failed", ssid
->ssid
);
237 ret
= brcmf_fil_iovar_int_set(ifp
, "pfn", 1);
239 brcmf_err("PNO enable failed!! ret=%d\n", ret
);