Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /** |
2 | * Functions implementing wlan scan IOCTL and firmware command APIs | |
3 | * | |
4 | * IOCTL handlers as well as command preperation and response routines | |
5 | * for sending scan commands to the firmware. | |
6 | */ | |
7 | #include <linux/ctype.h> | |
8 | #include <linux/if.h> | |
9 | #include <linux/netdevice.h> | |
10 | #include <linux/wireless.h> | |
fcdb53db | 11 | #include <linux/etherdevice.h> |
876c9d3a MT |
12 | |
13 | #include <net/ieee80211.h> | |
14 | #include <net/iw_handler.h> | |
15 | ||
16 | #include "host.h" | |
17 | #include "decl.h" | |
18 | #include "dev.h" | |
19 | #include "scan.h" | |
8c512765 | 20 | #include "join.h" |
876c9d3a MT |
21 | |
22 | //! Approximate amount of data needed to pass a scan result back to iwlist | |
23 | #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ | |
24 | + IW_ESSID_MAX_SIZE \ | |
25 | + IW_EV_UINT_LEN \ | |
26 | + IW_EV_FREQ_LEN \ | |
27 | + IW_EV_QUAL_LEN \ | |
28 | + IW_ESSID_MAX_SIZE \ | |
29 | + IW_EV_PARAM_LEN \ | |
30 | + 40) /* 40 for WPAIE */ | |
31 | ||
32 | //! Memory needed to store a max sized channel List TLV for a firmware scan | |
33 | #define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ | |
34 | + (MRVDRV_MAX_CHANNELS_PER_SCAN \ | |
35 | * sizeof(struct chanscanparamset))) | |
36 | ||
37 | //! Memory needed to store a max number/size SSID TLV for a firmware scan | |
38 | #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) | |
39 | ||
40 | //! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max | |
41 | #define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ | |
42 | + sizeof(struct mrvlietypes_numprobes) \ | |
43 | + CHAN_TLV_MAX_SIZE \ | |
44 | + SSID_TLV_MAX_SIZE) | |
45 | ||
46 | //! The maximum number of channels the firmware can scan per command | |
47 | #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 | |
48 | ||
49 | /** | |
50 | * @brief Number of channels to scan per firmware scan command issuance. | |
51 | * | |
52 | * Number restricted to prevent hitting the limit on the amount of scan data | |
53 | * returned in a single firmware scan command. | |
54 | */ | |
55 | #define MRVDRV_CHANNELS_PER_SCAN_CMD 4 | |
56 | ||
57 | //! Scan time specified in the channel TLV for each channel for passive scans | |
58 | #define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 | |
59 | ||
60 | //! Scan time specified in the channel TLV for each channel for active scans | |
61 | #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 | |
62 | ||
123e0e04 DW |
63 | static const u8 zeromac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
64 | static const u8 bcastmac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
eb8f7330 | 65 | |
fcdb53db DW |
66 | static inline void clear_bss_descriptor (struct bss_descriptor * bss) |
67 | { | |
68 | /* Don't blow away ->list, just BSS data */ | |
69 | memset(bss, 0, offsetof(struct bss_descriptor, list)); | |
70 | } | |
71 | ||
72 | static inline int match_bss_no_security(struct wlan_802_11_security * secinfo, | |
73 | struct bss_descriptor * match_bss) | |
74 | { | |
75 | if ( !secinfo->wep_enabled | |
76 | && !secinfo->WPAenabled | |
77 | && !secinfo->WPA2enabled | |
ab617971 DW |
78 | && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC |
79 | && match_bss->rsn_ie[0] != MFIE_TYPE_RSN | |
0c9ca690 | 80 | && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { |
fcdb53db DW |
81 | return 1; |
82 | } | |
83 | return 0; | |
84 | } | |
85 | ||
86 | static inline int match_bss_static_wep(struct wlan_802_11_security * secinfo, | |
87 | struct bss_descriptor * match_bss) | |
88 | { | |
89 | if ( secinfo->wep_enabled | |
90 | && !secinfo->WPAenabled | |
91 | && !secinfo->WPA2enabled | |
0c9ca690 | 92 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { |
fcdb53db DW |
93 | return 1; |
94 | } | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static inline int match_bss_wpa(struct wlan_802_11_security * secinfo, | |
99 | struct bss_descriptor * match_bss) | |
100 | { | |
101 | if ( !secinfo->wep_enabled | |
102 | && secinfo->WPAenabled | |
ab617971 | 103 | && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC) |
fcdb53db | 104 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G |
0c9ca690 DW |
105 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { |
106 | */ | |
fcdb53db DW |
107 | ) { |
108 | return 1; | |
109 | } | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static inline int match_bss_wpa2(struct wlan_802_11_security * secinfo, | |
114 | struct bss_descriptor * match_bss) | |
115 | { | |
116 | if ( !secinfo->wep_enabled | |
fcdb53db | 117 | && secinfo->WPA2enabled |
ab617971 | 118 | && (match_bss->rsn_ie[0] == MFIE_TYPE_RSN) |
fcdb53db | 119 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G |
0c9ca690 DW |
120 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { |
121 | */ | |
fcdb53db DW |
122 | ) { |
123 | return 1; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static inline int match_bss_dynamic_wep(struct wlan_802_11_security * secinfo, | |
129 | struct bss_descriptor * match_bss) | |
130 | { | |
131 | if ( !secinfo->wep_enabled | |
132 | && !secinfo->WPAenabled | |
133 | && !secinfo->WPA2enabled | |
ab617971 DW |
134 | && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC) |
135 | && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN) | |
0c9ca690 | 136 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { |
fcdb53db DW |
137 | return 1; |
138 | } | |
139 | return 0; | |
140 | } | |
876c9d3a MT |
141 | |
142 | /** | |
143 | * @brief Check if a scanned network compatible with the driver settings | |
144 | * | |
145 | * WEP WPA WPA2 ad-hoc encrypt Network | |
146 | * enabled enabled enabled AES mode privacy WPA WPA2 Compatible | |
147 | * 0 0 0 0 NONE 0 0 0 yes No security | |
148 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | |
149 | * 0 1 0 0 x 1x 1 x yes WPA | |
150 | * 0 0 1 0 x 1x x 1 yes WPA2 | |
151 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | |
152 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | |
153 | * | |
154 | * | |
155 | * @param adapter A pointer to wlan_adapter | |
156 | * @param index Index in scantable to check against current driver settings | |
157 | * @param mode Network mode: Infrastructure or IBSS | |
158 | * | |
159 | * @return Index in scantable, or error code if negative | |
160 | */ | |
fcdb53db DW |
161 | static int is_network_compatible(wlan_adapter * adapter, |
162 | struct bss_descriptor * bss, u8 mode) | |
876c9d3a | 163 | { |
fcdb53db DW |
164 | int matched = 0; |
165 | ||
9012b28a | 166 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 167 | |
fcdb53db DW |
168 | if (bss->mode != mode) |
169 | goto done; | |
876c9d3a | 170 | |
fcdb53db DW |
171 | if ((matched = match_bss_no_security(&adapter->secinfo, bss))) { |
172 | goto done; | |
173 | } else if ((matched = match_bss_static_wep(&adapter->secinfo, bss))) { | |
174 | goto done; | |
175 | } else if ((matched = match_bss_wpa(&adapter->secinfo, bss))) { | |
176 | lbs_deb_scan( | |
177 | "is_network_compatible() WPA: wpa_ie=%#x " | |
178 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | |
179 | "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], | |
889c05bd DW |
180 | adapter->secinfo.wep_enabled ? "e" : "d", |
181 | adapter->secinfo.WPAenabled ? "e" : "d", | |
182 | adapter->secinfo.WPA2enabled ? "e" : "d", | |
0c9ca690 | 183 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); |
fcdb53db DW |
184 | goto done; |
185 | } else if ((matched = match_bss_wpa2(&adapter->secinfo, bss))) { | |
186 | lbs_deb_scan( | |
187 | "is_network_compatible() WPA2: wpa_ie=%#x " | |
188 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | |
189 | "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], | |
190 | adapter->secinfo.wep_enabled ? "e" : "d", | |
191 | adapter->secinfo.WPAenabled ? "e" : "d", | |
192 | adapter->secinfo.WPA2enabled ? "e" : "d", | |
0c9ca690 | 193 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); |
fcdb53db DW |
194 | goto done; |
195 | } else if ((matched = match_bss_dynamic_wep(&adapter->secinfo, bss))) { | |
196 | lbs_deb_scan( | |
197 | "is_network_compatible() dynamic WEP: " | |
198 | "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n", | |
0c9ca690 DW |
199 | bss->wpa_ie[0], bss->rsn_ie[0], |
200 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | |
9012b28a | 201 | goto done; |
876c9d3a MT |
202 | } |
203 | ||
fcdb53db DW |
204 | /* bss security settings don't match those configured on card */ |
205 | lbs_deb_scan( | |
206 | "is_network_compatible() FAILED: wpa_ie=%#x " | |
207 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n", | |
208 | bss->wpa_ie[0], bss->rsn_ie[0], | |
209 | adapter->secinfo.wep_enabled ? "e" : "d", | |
210 | adapter->secinfo.WPAenabled ? "e" : "d", | |
211 | adapter->secinfo.WPA2enabled ? "e" : "d", | |
0c9ca690 | 212 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); |
9012b28a HS |
213 | |
214 | done: | |
fcdb53db DW |
215 | lbs_deb_leave(LBS_DEB_SCAN); |
216 | return matched; | |
876c9d3a MT |
217 | } |
218 | ||
876c9d3a MT |
219 | /** |
220 | * @brief Create a channel list for the driver to scan based on region info | |
221 | * | |
222 | * Use the driver region/band information to construct a comprehensive list | |
223 | * of channels to scan. This routine is used for any scan that is not | |
224 | * provided a specific channel list to scan. | |
225 | * | |
226 | * @param priv A pointer to wlan_private structure | |
227 | * @param scanchanlist Output parameter: resulting channel list to scan | |
228 | * @param filteredscan Flag indicating whether or not a BSSID or SSID filter | |
229 | * is being sent in the command to firmware. Used to | |
230 | * increase the number of channels sent in a scan | |
231 | * command and to disable the firmware channel scan | |
232 | * filter. | |
233 | * | |
234 | * @return void | |
235 | */ | |
236 | static void wlan_scan_create_channel_list(wlan_private * priv, | |
237 | struct chanscanparamset * scanchanlist, | |
238 | u8 filteredscan) | |
239 | { | |
240 | ||
241 | wlan_adapter *adapter = priv->adapter; | |
242 | struct region_channel *scanregion; | |
243 | struct chan_freq_power *cfp; | |
244 | int rgnidx; | |
245 | int chanidx; | |
246 | int nextchan; | |
247 | u8 scantype; | |
248 | ||
249 | chanidx = 0; | |
250 | ||
251 | /* Set the default scan type to the user specified type, will later | |
252 | * be changed to passive on a per channel basis if restricted by | |
253 | * regulatory requirements (11d or 11h) | |
254 | */ | |
4f2fdaaf | 255 | scantype = CMD_SCAN_TYPE_ACTIVE; |
876c9d3a MT |
256 | |
257 | for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { | |
258 | if (priv->adapter->enable11d && | |
0aef64d7 | 259 | adapter->connect_status != LIBERTAS_CONNECTED) { |
876c9d3a MT |
260 | /* Scan all the supported chan for the first scan */ |
261 | if (!adapter->universal_channel[rgnidx].valid) | |
262 | continue; | |
263 | scanregion = &adapter->universal_channel[rgnidx]; | |
264 | ||
265 | /* clear the parsed_region_chan for the first scan */ | |
266 | memset(&adapter->parsed_region_chan, 0x00, | |
267 | sizeof(adapter->parsed_region_chan)); | |
268 | } else { | |
269 | if (!adapter->region_channel[rgnidx].valid) | |
270 | continue; | |
271 | scanregion = &adapter->region_channel[rgnidx]; | |
272 | } | |
273 | ||
274 | for (nextchan = 0; | |
275 | nextchan < scanregion->nrcfp; nextchan++, chanidx++) { | |
276 | ||
277 | cfp = scanregion->CFP + nextchan; | |
278 | ||
279 | if (priv->adapter->enable11d) { | |
280 | scantype = | |
281 | libertas_get_scan_type_11d(cfp->channel, | |
282 | &adapter-> | |
283 | parsed_region_chan); | |
284 | } | |
285 | ||
286 | switch (scanregion->band) { | |
287 | case BAND_B: | |
288 | case BAND_G: | |
289 | default: | |
290 | scanchanlist[chanidx].radiotype = | |
0aef64d7 | 291 | CMD_SCAN_RADIO_TYPE_BG; |
876c9d3a MT |
292 | break; |
293 | } | |
294 | ||
0aef64d7 | 295 | if (scantype == CMD_SCAN_TYPE_PASSIVE) { |
876c9d3a | 296 | scanchanlist[chanidx].maxscantime = |
981f187b | 297 | cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME); |
876c9d3a MT |
298 | scanchanlist[chanidx].chanscanmode.passivescan = |
299 | 1; | |
300 | } else { | |
301 | scanchanlist[chanidx].maxscantime = | |
981f187b | 302 | cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME); |
876c9d3a MT |
303 | scanchanlist[chanidx].chanscanmode.passivescan = |
304 | 0; | |
305 | } | |
306 | ||
307 | scanchanlist[chanidx].channumber = cfp->channel; | |
308 | ||
309 | if (filteredscan) { | |
310 | scanchanlist[chanidx].chanscanmode. | |
311 | disablechanfilt = 1; | |
312 | } | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
2afc0c5d DW |
317 | |
318 | /* Delayed partial scan worker */ | |
319 | void libertas_scan_worker(struct work_struct *work) | |
320 | { | |
321 | wlan_private *priv = container_of(work, wlan_private, scan_work.work); | |
322 | ||
323 | wlan_scan_networks(priv, NULL, 0); | |
324 | } | |
325 | ||
326 | ||
876c9d3a MT |
327 | /** |
328 | * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds | |
329 | * | |
330 | * Application layer or other functions can invoke wlan_scan_networks | |
331 | * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. | |
332 | * This structure is used as the basis of one or many wlan_scan_cmd_config | |
333 | * commands that are sent to the command processing module and sent to | |
334 | * firmware. | |
335 | * | |
336 | * Create a wlan_scan_cmd_config based on the following user supplied | |
337 | * parameters (if present): | |
338 | * - SSID filter | |
339 | * - BSSID filter | |
340 | * - Number of Probes to be sent | |
341 | * - channel list | |
342 | * | |
343 | * If the SSID or BSSID filter is not present, disable/clear the filter. | |
344 | * If the number of probes is not set, use the adapter default setting | |
345 | * Qualify the channel | |
346 | * | |
347 | * @param priv A pointer to wlan_private structure | |
348 | * @param puserscanin NULL or pointer to scan configuration parameters | |
349 | * @param ppchantlvout Output parameter: Pointer to the start of the | |
350 | * channel TLV portion of the output scan config | |
351 | * @param pscanchanlist Output parameter: Pointer to the resulting channel | |
352 | * list to scan | |
353 | * @param pmaxchanperscan Output parameter: Number of channels to scan for | |
354 | * each issuance of the firmware scan command | |
355 | * @param pfilteredscan Output parameter: Flag indicating whether or not | |
356 | * a BSSID or SSID filter is being sent in the | |
357 | * command to firmware. Used to increase the number | |
358 | * of channels sent in a scan command and to | |
359 | * disable the firmware channel scan filter. | |
360 | * @param pscancurrentonly Output parameter: Flag indicating whether or not | |
361 | * we are only scanning our current active channel | |
362 | * | |
363 | * @return resulting scan configuration | |
364 | */ | |
365 | static struct wlan_scan_cmd_config * | |
366 | wlan_scan_setup_scan_config(wlan_private * priv, | |
367 | const struct wlan_ioctl_user_scan_cfg * puserscanin, | |
368 | struct mrvlietypes_chanlistparamset ** ppchantlvout, | |
369 | struct chanscanparamset * pscanchanlist, | |
370 | int *pmaxchanperscan, | |
371 | u8 * pfilteredscan, | |
372 | u8 * pscancurrentonly) | |
373 | { | |
876c9d3a MT |
374 | struct mrvlietypes_numprobes *pnumprobestlv; |
375 | struct mrvlietypes_ssidparamset *pssidtlv; | |
376 | struct wlan_scan_cmd_config * pscancfgout = NULL; | |
377 | u8 *ptlvpos; | |
378 | u16 numprobes; | |
876c9d3a MT |
379 | int chanidx; |
380 | int scantype; | |
381 | int scandur; | |
382 | int channel; | |
383 | int radiotype; | |
384 | ||
385 | pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); | |
386 | if (pscancfgout == NULL) | |
387 | goto out; | |
388 | ||
389 | /* The tlvbufferlen is calculated for each scan command. The TLVs added | |
390 | * in this routine will be preserved since the routine that sends | |
391 | * the command will append channelTLVs at *ppchantlvout. The difference | |
392 | * between the *ppchantlvout and the tlvbuffer start will be used | |
393 | * to calculate the size of anything we add in this routine. | |
394 | */ | |
395 | pscancfgout->tlvbufferlen = 0; | |
396 | ||
397 | /* Running tlv pointer. Assigned to ppchantlvout at end of function | |
398 | * so later routines know where channels can be added to the command buf | |
399 | */ | |
400 | ptlvpos = pscancfgout->tlvbuffer; | |
401 | ||
402 | /* | |
403 | * Set the initial scan paramters for progressive scanning. If a specific | |
404 | * BSSID or SSID is used, the number of channels in the scan command | |
405 | * will be increased to the absolute maximum | |
406 | */ | |
407 | *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; | |
408 | ||
409 | /* Initialize the scan as un-filtered by firmware, set to TRUE below if | |
410 | * a SSID or BSSID filter is sent in the command | |
411 | */ | |
412 | *pfilteredscan = 0; | |
413 | ||
414 | /* Initialize the scan as not being only on the current channel. If | |
415 | * the channel list is customized, only contains one channel, and | |
416 | * is the active channel, this is set true and data flow is not halted. | |
417 | */ | |
418 | *pscancurrentonly = 0; | |
419 | ||
420 | if (puserscanin) { | |
876c9d3a MT |
421 | /* Set the bss type scan filter, use adapter setting if unset */ |
422 | pscancfgout->bsstype = | |
d65ead88 | 423 | puserscanin->bsstype ? puserscanin->bsstype : CMD_BSS_TYPE_ANY; |
876c9d3a MT |
424 | |
425 | /* Set the number of probes to send, use adapter setting if unset */ | |
e2aa334b | 426 | numprobes = puserscanin->numprobes ? puserscanin->numprobes : 0; |
876c9d3a MT |
427 | |
428 | /* | |
429 | * Set the BSSID filter to the incoming configuration, | |
430 | * if non-zero. If not set, it will remain disabled (all zeros). | |
431 | */ | |
eb8f7330 DW |
432 | memcpy(pscancfgout->bssid, puserscanin->bssid, |
433 | sizeof(pscancfgout->bssid)); | |
876c9d3a | 434 | |
eb8f7330 | 435 | if (puserscanin->ssid_len) { |
876c9d3a MT |
436 | pssidtlv = |
437 | (struct mrvlietypes_ssidparamset *) pscancfgout-> | |
438 | tlvbuffer; | |
439 | pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); | |
eb8f7330 DW |
440 | pssidtlv->header.len = cpu_to_le16(puserscanin->ssid_len); |
441 | memcpy(pssidtlv->ssid, puserscanin->ssid, | |
442 | puserscanin->ssid_len); | |
443 | ptlvpos += sizeof(pssidtlv->header) + puserscanin->ssid_len; | |
876c9d3a MT |
444 | } |
445 | ||
446 | /* | |
447 | * The default number of channels sent in the command is low to | |
448 | * ensure the response buffer from the firmware does not truncate | |
449 | * scan results. That is not an issue with an SSID or BSSID | |
450 | * filter applied to the scan results in the firmware. | |
451 | */ | |
eb8f7330 DW |
452 | if ( puserscanin->ssid_len |
453 | || (compare_ether_addr(pscancfgout->bssid, &zeromac[0]) != 0)) { | |
876c9d3a MT |
454 | *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; |
455 | *pfilteredscan = 1; | |
456 | } | |
457 | } else { | |
d65ead88 | 458 | pscancfgout->bsstype = CMD_BSS_TYPE_ANY; |
e2aa334b | 459 | numprobes = 0; |
876c9d3a MT |
460 | } |
461 | ||
462 | /* If the input config or adapter has the number of Probes set, add tlv */ | |
463 | if (numprobes) { | |
464 | pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; | |
981f187b DW |
465 | pnumprobestlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); |
466 | pnumprobestlv->header.len = cpu_to_le16(2); | |
876c9d3a MT |
467 | pnumprobestlv->numprobes = cpu_to_le16(numprobes); |
468 | ||
981f187b | 469 | ptlvpos += sizeof(*pnumprobestlv); |
876c9d3a MT |
470 | } |
471 | ||
472 | /* | |
473 | * Set the output for the channel TLV to the address in the tlv buffer | |
474 | * past any TLVs that were added in this fuction (SSID, numprobes). | |
475 | * channel TLVs will be added past this for each scan command, preserving | |
476 | * the TLVs that were previously added. | |
477 | */ | |
478 | *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; | |
479 | ||
2afc0c5d DW |
480 | if (!puserscanin || !puserscanin->chanlist[0].channumber) { |
481 | /* Create a default channel scan list */ | |
482 | lbs_deb_scan("Scan: Creating full region channel list\n"); | |
483 | wlan_scan_create_channel_list(priv, pscanchanlist, | |
484 | *pfilteredscan); | |
485 | goto out; | |
486 | } | |
876c9d3a | 487 | |
2afc0c5d DW |
488 | lbs_deb_scan("Scan: Using supplied channel list\n"); |
489 | for (chanidx = 0; | |
490 | chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX | |
491 | && puserscanin->chanlist[chanidx].channumber; chanidx++) { | |
876c9d3a | 492 | |
2afc0c5d DW |
493 | channel = puserscanin->chanlist[chanidx].channumber; |
494 | (pscanchanlist + chanidx)->channumber = channel; | |
876c9d3a | 495 | |
2afc0c5d DW |
496 | radiotype = puserscanin->chanlist[chanidx].radiotype; |
497 | (pscanchanlist + chanidx)->radiotype = radiotype; | |
876c9d3a | 498 | |
2afc0c5d | 499 | scantype = puserscanin->chanlist[chanidx].scantype; |
876c9d3a | 500 | |
2afc0c5d DW |
501 | if (scantype == CMD_SCAN_TYPE_PASSIVE) { |
502 | (pscanchanlist + | |
503 | chanidx)->chanscanmode.passivescan = 1; | |
504 | } else { | |
505 | (pscanchanlist + | |
506 | chanidx)->chanscanmode.passivescan = 0; | |
507 | } | |
876c9d3a | 508 | |
2afc0c5d DW |
509 | if (puserscanin->chanlist[chanidx].scantime) { |
510 | scandur = puserscanin->chanlist[chanidx].scantime; | |
511 | } else { | |
0aef64d7 | 512 | if (scantype == CMD_SCAN_TYPE_PASSIVE) { |
2afc0c5d | 513 | scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; |
876c9d3a | 514 | } else { |
2afc0c5d | 515 | scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; |
876c9d3a | 516 | } |
876c9d3a MT |
517 | } |
518 | ||
2afc0c5d DW |
519 | (pscanchanlist + chanidx)->minscantime = |
520 | cpu_to_le16(scandur); | |
521 | (pscanchanlist + chanidx)->maxscantime = | |
522 | cpu_to_le16(scandur); | |
523 | } | |
876c9d3a | 524 | |
2afc0c5d DW |
525 | /* Check if we are only scanning the current channel */ |
526 | if ((chanidx == 1) && | |
527 | (puserscanin->chanlist[0].channumber == | |
528 | priv->adapter->curbssparams.channel)) { | |
529 | *pscancurrentonly = 1; | |
530 | lbs_deb_scan("Scan: Scanning current channel only"); | |
876c9d3a MT |
531 | } |
532 | ||
533 | out: | |
534 | return pscancfgout; | |
535 | } | |
536 | ||
537 | /** | |
538 | * @brief Construct and send multiple scan config commands to the firmware | |
539 | * | |
540 | * Previous routines have created a wlan_scan_cmd_config with any requested | |
541 | * TLVs. This function splits the channel TLV into maxchanperscan lists | |
542 | * and sends the portion of the channel TLV along with the other TLVs | |
543 | * to the wlan_cmd routines for execution in the firmware. | |
544 | * | |
545 | * @param priv A pointer to wlan_private structure | |
546 | * @param maxchanperscan Maximum number channels to be included in each | |
547 | * scan command sent to firmware | |
548 | * @param filteredscan Flag indicating whether or not a BSSID or SSID | |
549 | * filter is being used for the firmware command | |
550 | * scan command sent to firmware | |
551 | * @param pscancfgout Scan configuration used for this scan. | |
552 | * @param pchantlvout Pointer in the pscancfgout where the channel TLV | |
553 | * should start. This is past any other TLVs that | |
554 | * must be sent down in each firmware command. | |
555 | * @param pscanchanlist List of channels to scan in maxchanperscan segments | |
556 | * | |
557 | * @return 0 or error return otherwise | |
558 | */ | |
559 | static int wlan_scan_channel_list(wlan_private * priv, | |
560 | int maxchanperscan, | |
561 | u8 filteredscan, | |
562 | struct wlan_scan_cmd_config * pscancfgout, | |
563 | struct mrvlietypes_chanlistparamset * pchantlvout, | |
064827ed | 564 | struct chanscanparamset * pscanchanlist, |
2be92196 MT |
565 | const struct wlan_ioctl_user_scan_cfg * puserscanin, |
566 | int full_scan) | |
876c9d3a MT |
567 | { |
568 | struct chanscanparamset *ptmpchan; | |
569 | struct chanscanparamset *pstartchan; | |
570 | u8 scanband; | |
571 | int doneearly; | |
572 | int tlvidx; | |
573 | int ret = 0; | |
064827ed MT |
574 | int scanned = 0; |
575 | union iwreq_data wrqu; | |
876c9d3a | 576 | |
9012b28a | 577 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
578 | |
579 | if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { | |
9012b28a | 580 | lbs_deb_scan("Scan: Null detect: %p, %p, %p\n", |
876c9d3a MT |
581 | pscancfgout, pchantlvout, pscanchanlist); |
582 | return -1; | |
583 | } | |
584 | ||
585 | pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | |
586 | ||
587 | /* Set the temp channel struct pointer to the start of the desired list */ | |
588 | ptmpchan = pscanchanlist; | |
589 | ||
064827ed MT |
590 | if (priv->adapter->last_scanned_channel && !puserscanin) |
591 | ptmpchan += priv->adapter->last_scanned_channel; | |
592 | ||
876c9d3a MT |
593 | /* Loop through the desired channel list, sending a new firmware scan |
594 | * commands for each maxchanperscan channels (or for 1,6,11 individually | |
595 | * if configured accordingly) | |
596 | */ | |
597 | while (ptmpchan->channumber) { | |
598 | ||
599 | tlvidx = 0; | |
600 | pchantlvout->header.len = 0; | |
601 | scanband = ptmpchan->radiotype; | |
602 | pstartchan = ptmpchan; | |
603 | doneearly = 0; | |
604 | ||
605 | /* Construct the channel TLV for the scan command. Continue to | |
606 | * insert channel TLVs until: | |
607 | * - the tlvidx hits the maximum configured per scan command | |
608 | * - the next channel to insert is 0 (end of desired channel list) | |
609 | * - doneearly is set (controlling individual scanning of 1,6,11) | |
610 | */ | |
611 | while (tlvidx < maxchanperscan && ptmpchan->channumber | |
064827ed | 612 | && !doneearly && scanned < 2) { |
876c9d3a | 613 | |
2afc0c5d DW |
614 | lbs_deb_scan("Scan: Chan(%3d), Radio(%d), mode(%d,%d), " |
615 | "Dur(%d)\n", | |
616 | ptmpchan->channumber, ptmpchan->radiotype, | |
617 | ptmpchan->chanscanmode.passivescan, | |
618 | ptmpchan->chanscanmode.disablechanfilt, | |
619 | ptmpchan->maxscantime); | |
876c9d3a MT |
620 | |
621 | /* Copy the current channel TLV to the command being prepared */ | |
622 | memcpy(pchantlvout->chanscanparam + tlvidx, | |
623 | ptmpchan, sizeof(pchantlvout->chanscanparam)); | |
624 | ||
625 | /* Increment the TLV header length by the size appended */ | |
981f187b DW |
626 | /* Ew, it would be _so_ nice if we could just declare the |
627 | variable little-endian and let GCC handle it for us */ | |
628 | pchantlvout->header.len = | |
629 | cpu_to_le16(le16_to_cpu(pchantlvout->header.len) + | |
630 | sizeof(pchantlvout->chanscanparam)); | |
876c9d3a MT |
631 | |
632 | /* | |
633 | * The tlv buffer length is set to the number of bytes of the | |
634 | * between the channel tlv pointer and the start of the | |
635 | * tlv buffer. This compensates for any TLVs that were appended | |
636 | * before the channel list. | |
637 | */ | |
638 | pscancfgout->tlvbufferlen = ((u8 *) pchantlvout | |
639 | - pscancfgout->tlvbuffer); | |
640 | ||
641 | /* Add the size of the channel tlv header and the data length */ | |
642 | pscancfgout->tlvbufferlen += | |
643 | (sizeof(pchantlvout->header) | |
981f187b | 644 | + le16_to_cpu(pchantlvout->header.len)); |
876c9d3a MT |
645 | |
646 | /* Increment the index to the channel tlv we are constructing */ | |
647 | tlvidx++; | |
648 | ||
649 | doneearly = 0; | |
650 | ||
651 | /* Stop the loop if the *current* channel is in the 1,6,11 set | |
652 | * and we are not filtering on a BSSID or SSID. | |
653 | */ | |
654 | if (!filteredscan && (ptmpchan->channumber == 1 | |
655 | || ptmpchan->channumber == 6 | |
656 | || ptmpchan->channumber == 11)) { | |
657 | doneearly = 1; | |
658 | } | |
659 | ||
660 | /* Increment the tmp pointer to the next channel to be scanned */ | |
661 | ptmpchan++; | |
064827ed | 662 | scanned++; |
876c9d3a MT |
663 | |
664 | /* Stop the loop if the *next* channel is in the 1,6,11 set. | |
665 | * This will cause it to be the only channel scanned on the next | |
666 | * interation | |
667 | */ | |
668 | if (!filteredscan && (ptmpchan->channumber == 1 | |
669 | || ptmpchan->channumber == 6 | |
670 | || ptmpchan->channumber == 11)) { | |
671 | doneearly = 1; | |
672 | } | |
673 | } | |
674 | ||
675 | /* Send the scan command to the firmware with the specified cfg */ | |
0aef64d7 | 676 | ret = libertas_prepare_and_send_command(priv, CMD_802_11_SCAN, 0, |
876c9d3a | 677 | 0, 0, pscancfgout); |
2be92196 | 678 | if (scanned >= 2 && !full_scan) { |
9012b28a HS |
679 | ret = 0; |
680 | goto done; | |
064827ed | 681 | } |
2be92196 | 682 | scanned = 0; |
876c9d3a MT |
683 | } |
684 | ||
d9ad2f5d | 685 | done: |
064827ed MT |
686 | priv->adapter->last_scanned_channel = ptmpchan->channumber; |
687 | ||
2afc0c5d DW |
688 | if (priv->adapter->last_scanned_channel) { |
689 | /* Schedule the next part of the partial scan */ | |
690 | if (!full_scan && !priv->adapter->surpriseremoved) { | |
691 | cancel_delayed_work(&priv->scan_work); | |
692 | queue_delayed_work(priv->work_thread, &priv->scan_work, | |
693 | msecs_to_jiffies(300)); | |
694 | } | |
695 | } else { | |
696 | /* All done, tell userspace the scan table has been updated */ | |
697 | memset(&wrqu, 0, sizeof(union iwreq_data)); | |
698 | wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); | |
699 | } | |
064827ed | 700 | |
9012b28a | 701 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); |
876c9d3a MT |
702 | return ret; |
703 | } | |
704 | ||
eb8f7330 DW |
705 | static void |
706 | clear_selected_scan_list_entries(wlan_adapter * adapter, | |
707 | const struct wlan_ioctl_user_scan_cfg * scan_cfg) | |
708 | { | |
709 | struct bss_descriptor * bss; | |
710 | struct bss_descriptor * safe; | |
711 | u32 clear_ssid_flag = 0, clear_bssid_flag = 0; | |
712 | ||
713 | if (!scan_cfg) | |
714 | return; | |
715 | ||
716 | if (scan_cfg->clear_ssid && scan_cfg->ssid_len) | |
717 | clear_ssid_flag = 1; | |
718 | ||
719 | if (scan_cfg->clear_bssid | |
720 | && (compare_ether_addr(scan_cfg->bssid, &zeromac[0]) != 0) | |
721 | && (compare_ether_addr(scan_cfg->bssid, &bcastmac[0]) != 0)) { | |
722 | clear_bssid_flag = 1; | |
723 | } | |
724 | ||
725 | if (!clear_ssid_flag && !clear_bssid_flag) | |
726 | return; | |
727 | ||
728 | mutex_lock(&adapter->lock); | |
729 | list_for_each_entry_safe (bss, safe, &adapter->network_list, list) { | |
730 | u32 clear = 0; | |
731 | ||
732 | /* Check for an SSID match */ | |
733 | if ( clear_ssid_flag | |
d8efea25 DW |
734 | && (bss->ssid_len == scan_cfg->ssid_len) |
735 | && !memcmp(bss->ssid, scan_cfg->ssid, bss->ssid_len)) | |
eb8f7330 DW |
736 | clear = 1; |
737 | ||
738 | /* Check for a BSSID match */ | |
739 | if ( clear_bssid_flag | |
740 | && !compare_ether_addr(bss->bssid, scan_cfg->bssid)) | |
741 | clear = 1; | |
742 | ||
743 | if (clear) { | |
744 | list_move_tail (&bss->list, &adapter->network_free_list); | |
745 | clear_bss_descriptor(bss); | |
746 | } | |
747 | } | |
748 | mutex_unlock(&adapter->lock); | |
749 | } | |
750 | ||
751 | ||
876c9d3a MT |
752 | /** |
753 | * @brief Internal function used to start a scan based on an input config | |
754 | * | |
755 | * Use the input user scan configuration information when provided in | |
756 | * order to send the appropriate scan commands to firmware to populate or | |
757 | * update the internal driver scan table | |
758 | * | |
759 | * @param priv A pointer to wlan_private structure | |
760 | * @param puserscanin Pointer to the input configuration for the requested | |
761 | * scan. | |
762 | * | |
763 | * @return 0 or < 0 if error | |
764 | */ | |
765 | int wlan_scan_networks(wlan_private * priv, | |
2afc0c5d DW |
766 | const struct wlan_ioctl_user_scan_cfg * puserscanin, |
767 | int full_scan) | |
876c9d3a | 768 | { |
eb8f7330 | 769 | wlan_adapter * adapter = priv->adapter; |
876c9d3a MT |
770 | struct mrvlietypes_chanlistparamset *pchantlvout; |
771 | struct chanscanparamset * scan_chan_list = NULL; | |
772 | struct wlan_scan_cmd_config * scan_cfg = NULL; | |
876c9d3a MT |
773 | u8 filteredscan; |
774 | u8 scancurrentchanonly; | |
775 | int maxchanperscan; | |
776 | int ret; | |
f8f55108 DW |
777 | #ifdef CONFIG_LIBERTAS_DEBUG |
778 | struct bss_descriptor * iter_bss; | |
779 | int i = 0; | |
0795af57 | 780 | DECLARE_MAC_BUF(mac); |
f8f55108 | 781 | #endif |
876c9d3a | 782 | |
2afc0c5d DW |
783 | lbs_deb_enter(LBS_DEB_SCAN); |
784 | ||
785 | /* Cancel any partial outstanding partial scans if this scan | |
786 | * is a full scan. | |
787 | */ | |
788 | if (full_scan && delayed_work_pending(&priv->scan_work)) | |
789 | cancel_delayed_work(&priv->scan_work); | |
876c9d3a MT |
790 | |
791 | scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * | |
792 | WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); | |
793 | if (scan_chan_list == NULL) { | |
794 | ret = -ENOMEM; | |
795 | goto out; | |
796 | } | |
797 | ||
798 | scan_cfg = wlan_scan_setup_scan_config(priv, | |
799 | puserscanin, | |
800 | &pchantlvout, | |
801 | scan_chan_list, | |
802 | &maxchanperscan, | |
803 | &filteredscan, | |
804 | &scancurrentchanonly); | |
805 | if (scan_cfg == NULL) { | |
806 | ret = -ENOMEM; | |
807 | goto out; | |
808 | } | |
809 | ||
eb8f7330 | 810 | clear_selected_scan_list_entries(adapter, puserscanin); |
876c9d3a MT |
811 | |
812 | /* Keep the data path active if we are only scanning our current channel */ | |
813 | if (!scancurrentchanonly) { | |
634b8f49 HS |
814 | netif_stop_queue(priv->dev); |
815 | netif_carrier_off(priv->dev); | |
3cf84091 HS |
816 | if (priv->mesh_dev) { |
817 | netif_stop_queue(priv->mesh_dev); | |
818 | netif_carrier_off(priv->mesh_dev); | |
819 | } | |
876c9d3a MT |
820 | } |
821 | ||
822 | ret = wlan_scan_channel_list(priv, | |
823 | maxchanperscan, | |
824 | filteredscan, | |
825 | scan_cfg, | |
826 | pchantlvout, | |
064827ed | 827 | scan_chan_list, |
2be92196 MT |
828 | puserscanin, |
829 | full_scan); | |
876c9d3a | 830 | |
f8f55108 DW |
831 | #ifdef CONFIG_LIBERTAS_DEBUG |
832 | /* Dump the scan table */ | |
833 | mutex_lock(&adapter->lock); | |
834 | list_for_each_entry (iter_bss, &adapter->network_list, list) { | |
0795af57 JP |
835 | lbs_deb_scan("Scan:(%02d) %s, RSSI[%03d], SSID[%s]\n", |
836 | i++, print_mac(mac, iter_bss->bssid), (s32) iter_bss->rssi, | |
f8f55108 DW |
837 | escape_essid(iter_bss->ssid, iter_bss->ssid_len)); |
838 | } | |
839 | mutex_unlock(&adapter->lock); | |
840 | #endif | |
876c9d3a | 841 | |
0aef64d7 | 842 | if (priv->adapter->connect_status == LIBERTAS_CONNECTED) { |
634b8f49 HS |
843 | netif_carrier_on(priv->dev); |
844 | netif_wake_queue(priv->dev); | |
3cf84091 HS |
845 | if (priv->mesh_dev) { |
846 | netif_carrier_on(priv->mesh_dev); | |
847 | netif_wake_queue(priv->mesh_dev); | |
848 | } | |
876c9d3a MT |
849 | } |
850 | ||
851 | out: | |
852 | if (scan_cfg) | |
853 | kfree(scan_cfg); | |
854 | ||
855 | if (scan_chan_list) | |
856 | kfree(scan_chan_list); | |
857 | ||
9012b28a | 858 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); |
876c9d3a MT |
859 | return ret; |
860 | } | |
861 | ||
876c9d3a MT |
862 | /** |
863 | * @brief Interpret a BSS scan response returned from the firmware | |
864 | * | |
865 | * Parse the various fixed fields and IEs passed back for a a BSS probe | |
866 | * response or beacon from the scan command. Record information as needed | |
867 | * in the scan table struct bss_descriptor for that entry. | |
868 | * | |
fcdb53db | 869 | * @param bss Output parameter: Pointer to the BSS Entry |
876c9d3a MT |
870 | * |
871 | * @return 0 or -1 | |
872 | */ | |
fcdb53db DW |
873 | static int libertas_process_bss(struct bss_descriptor * bss, |
874 | u8 ** pbeaconinfo, int *bytesleft) | |
876c9d3a | 875 | { |
876c9d3a MT |
876 | struct ieeetypes_fhparamset *pFH; |
877 | struct ieeetypes_dsparamset *pDS; | |
878 | struct ieeetypes_cfparamset *pCF; | |
879 | struct ieeetypes_ibssparamset *pibss; | |
0795af57 | 880 | DECLARE_MAC_BUF(mac); |
876c9d3a | 881 | struct ieeetypes_countryinfoset *pcountryinfo; |
8c512765 DW |
882 | u8 *pos, *end, *p; |
883 | u8 n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0; | |
884 | u16 beaconsize = 0; | |
885 | int ret; | |
876c9d3a | 886 | |
9012b28a | 887 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 888 | |
876c9d3a MT |
889 | if (*bytesleft >= sizeof(beaconsize)) { |
890 | /* Extract & convert beacon size from the command buffer */ | |
981f187b | 891 | beaconsize = le16_to_cpup((void *)*pbeaconinfo); |
876c9d3a MT |
892 | *bytesleft -= sizeof(beaconsize); |
893 | *pbeaconinfo += sizeof(beaconsize); | |
894 | } | |
895 | ||
896 | if (beaconsize == 0 || beaconsize > *bytesleft) { | |
876c9d3a MT |
897 | *pbeaconinfo += *bytesleft; |
898 | *bytesleft = 0; | |
876c9d3a MT |
899 | return -1; |
900 | } | |
901 | ||
902 | /* Initialize the current working beacon pointer for this BSS iteration */ | |
ab617971 DW |
903 | pos = *pbeaconinfo; |
904 | end = pos + beaconsize; | |
876c9d3a MT |
905 | |
906 | /* Advance the return beacon pointer past the current beacon */ | |
907 | *pbeaconinfo += beaconsize; | |
908 | *bytesleft -= beaconsize; | |
909 | ||
ab617971 | 910 | memcpy(bss->bssid, pos, ETH_ALEN); |
0795af57 | 911 | lbs_deb_scan("process_bss: AP BSSID %s\n", print_mac(mac, bss->bssid)); |
ab617971 | 912 | pos += ETH_ALEN; |
876c9d3a | 913 | |
ab617971 | 914 | if ((end - pos) < 12) { |
fcdb53db | 915 | lbs_deb_scan("process_bss: Not enough bytes left\n"); |
876c9d3a MT |
916 | return -1; |
917 | } | |
918 | ||
919 | /* | |
920 | * next 4 fields are RSSI, time stamp, beacon interval, | |
921 | * and capability information | |
922 | */ | |
923 | ||
924 | /* RSSI is 1 byte long */ | |
ab617971 DW |
925 | bss->rssi = *pos; |
926 | lbs_deb_scan("process_bss: RSSI=%02X\n", *pos); | |
927 | pos++; | |
876c9d3a MT |
928 | |
929 | /* time stamp is 8 bytes long */ | |
ab617971 | 930 | pos += 8; |
876c9d3a MT |
931 | |
932 | /* beacon interval is 2 bytes long */ | |
ab617971 DW |
933 | bss->beaconperiod = le16_to_cpup((void *) pos); |
934 | pos += 2; | |
876c9d3a MT |
935 | |
936 | /* capability information is 2 bytes long */ | |
ab617971 | 937 | bss->capability = le16_to_cpup((void *) pos); |
0c9ca690 | 938 | lbs_deb_scan("process_bss: capabilities = 0x%4X\n", bss->capability); |
ab617971 | 939 | pos += 2; |
876c9d3a | 940 | |
0c9ca690 DW |
941 | if (bss->capability & WLAN_CAPABILITY_PRIVACY) |
942 | lbs_deb_scan("process_bss: AP WEP enabled\n"); | |
943 | if (bss->capability & WLAN_CAPABILITY_IBSS) | |
944 | bss->mode = IW_MODE_ADHOC; | |
945 | else | |
946 | bss->mode = IW_MODE_INFRA; | |
947 | ||
876c9d3a | 948 | /* rest of the current buffer are IE's */ |
ab617971 | 949 | lbs_deb_scan("process_bss: IE length for this AP = %zd\n", end - pos); |
ece56191 | 950 | lbs_deb_hex(LBS_DEB_SCAN, "process_bss: IE info", pos, end - pos); |
876c9d3a | 951 | |
876c9d3a | 952 | /* process variable IE */ |
ab617971 DW |
953 | while (pos <= end - 2) { |
954 | struct ieee80211_info_element * elem = | |
955 | (struct ieee80211_info_element *) pos; | |
876c9d3a | 956 | |
ab617971 | 957 | if (pos + elem->len > end) { |
fcdb53db | 958 | lbs_deb_scan("process_bss: error in processing IE, " |
876c9d3a | 959 | "bytes left < IE length\n"); |
ab617971 | 960 | break; |
876c9d3a MT |
961 | } |
962 | ||
ab617971 DW |
963 | switch (elem->id) { |
964 | case MFIE_TYPE_SSID: | |
965 | bss->ssid_len = elem->len; | |
966 | memcpy(bss->ssid, elem->data, elem->len); | |
d8efea25 DW |
967 | lbs_deb_scan("ssid '%s', ssid length %u\n", |
968 | escape_essid(bss->ssid, bss->ssid_len), | |
969 | bss->ssid_len); | |
876c9d3a MT |
970 | break; |
971 | ||
ab617971 | 972 | case MFIE_TYPE_RATES: |
8c512765 DW |
973 | n_basic_rates = min_t(u8, MAX_RATES, elem->len); |
974 | memcpy(bss->rates, elem->data, n_basic_rates); | |
975 | got_basic_rates = 1; | |
876c9d3a MT |
976 | break; |
977 | ||
ab617971 DW |
978 | case MFIE_TYPE_FH_SET: |
979 | pFH = (struct ieeetypes_fhparamset *) pos; | |
fcdb53db | 980 | memmove(&bss->phyparamset.fhparamset, pFH, |
876c9d3a | 981 | sizeof(struct ieeetypes_fhparamset)); |
981f187b | 982 | #if 0 /* I think we can store these LE */ |
fcdb53db DW |
983 | bss->phyparamset.fhparamset.dwelltime |
984 | = le16_to_cpu(bss->phyparamset.fhparamset.dwelltime); | |
981f187b | 985 | #endif |
876c9d3a MT |
986 | break; |
987 | ||
ab617971 DW |
988 | case MFIE_TYPE_DS_SET: |
989 | pDS = (struct ieeetypes_dsparamset *) pos; | |
fcdb53db DW |
990 | bss->channel = pDS->currentchan; |
991 | memcpy(&bss->phyparamset.dsparamset, pDS, | |
876c9d3a MT |
992 | sizeof(struct ieeetypes_dsparamset)); |
993 | break; | |
994 | ||
ab617971 DW |
995 | case MFIE_TYPE_CF_SET: |
996 | pCF = (struct ieeetypes_cfparamset *) pos; | |
fcdb53db | 997 | memcpy(&bss->ssparamset.cfparamset, pCF, |
876c9d3a MT |
998 | sizeof(struct ieeetypes_cfparamset)); |
999 | break; | |
1000 | ||
ab617971 DW |
1001 | case MFIE_TYPE_IBSS_SET: |
1002 | pibss = (struct ieeetypes_ibssparamset *) pos; | |
fcdb53db DW |
1003 | bss->atimwindow = le32_to_cpu(pibss->atimwindow); |
1004 | memmove(&bss->ssparamset.ibssparamset, pibss, | |
876c9d3a | 1005 | sizeof(struct ieeetypes_ibssparamset)); |
981f187b | 1006 | #if 0 |
fcdb53db DW |
1007 | bss->ssparamset.ibssparamset.atimwindow |
1008 | = le16_to_cpu(bss->ssparamset.ibssparamset.atimwindow); | |
981f187b | 1009 | #endif |
876c9d3a MT |
1010 | break; |
1011 | ||
ab617971 DW |
1012 | case MFIE_TYPE_COUNTRY: |
1013 | pcountryinfo = (struct ieeetypes_countryinfoset *) pos; | |
fcdb53db | 1014 | if (pcountryinfo->len < sizeof(pcountryinfo->countrycode) |
876c9d3a | 1015 | || pcountryinfo->len > 254) { |
fcdb53db | 1016 | lbs_deb_scan("process_bss: 11D- Err " |
4269e2ad | 1017 | "CountryInfo len =%d min=%zd max=254\n", |
876c9d3a MT |
1018 | pcountryinfo->len, |
1019 | sizeof(pcountryinfo->countrycode)); | |
9012b28a HS |
1020 | ret = -1; |
1021 | goto done; | |
876c9d3a MT |
1022 | } |
1023 | ||
fcdb53db | 1024 | memcpy(&bss->countryinfo, |
876c9d3a | 1025 | pcountryinfo, pcountryinfo->len + 2); |
ece56191 | 1026 | lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo", |
876c9d3a MT |
1027 | (u8 *) pcountryinfo, |
1028 | (u32) (pcountryinfo->len + 2)); | |
1029 | break; | |
1030 | ||
ab617971 DW |
1031 | case MFIE_TYPE_RATES_EX: |
1032 | /* only process extended supported rate if data rate is | |
1033 | * already found. Data rate IE should come before | |
876c9d3a MT |
1034 | * extended supported rate IE |
1035 | */ | |
8c512765 | 1036 | if (!got_basic_rates) |
ab617971 | 1037 | break; |
876c9d3a | 1038 | |
8c512765 DW |
1039 | n_ex_rates = elem->len; |
1040 | if (n_basic_rates + n_ex_rates > MAX_RATES) | |
1041 | n_ex_rates = MAX_RATES - n_basic_rates; | |
876c9d3a | 1042 | |
8c512765 DW |
1043 | p = bss->rates + n_basic_rates; |
1044 | memcpy(p, elem->data, n_ex_rates); | |
876c9d3a | 1045 | break; |
ab617971 DW |
1046 | |
1047 | case MFIE_TYPE_GENERIC: | |
1048 | if (elem->len >= 4 && | |
1049 | elem->data[0] == 0x00 && | |
1050 | elem->data[1] == 0x50 && | |
1051 | elem->data[2] == 0xf2 && | |
1052 | elem->data[3] == 0x01) { | |
1053 | bss->wpa_ie_len = min(elem->len + 2, | |
1054 | MAX_WPA_IE_LEN); | |
1055 | memcpy(bss->wpa_ie, elem, bss->wpa_ie_len); | |
ece56191 | 1056 | lbs_deb_hex(LBS_DEB_SCAN, "process_bss: WPA IE", bss->wpa_ie, |
ab617971 | 1057 | elem->len); |
1e838bf3 LCC |
1058 | } else if (elem->len >= MARVELL_MESH_IE_LENGTH && |
1059 | elem->data[0] == 0x00 && | |
1060 | elem->data[1] == 0x50 && | |
1061 | elem->data[2] == 0x43 && | |
1062 | elem->data[3] == 0x04) { | |
1063 | bss->mesh = 1; | |
ab617971 | 1064 | } |
876c9d3a | 1065 | break; |
ab617971 DW |
1066 | |
1067 | case MFIE_TYPE_RSN: | |
1068 | bss->rsn_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN); | |
1069 | memcpy(bss->rsn_ie, elem, bss->rsn_ie_len); | |
ece56191 | 1070 | lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE", bss->rsn_ie, elem->len); |
876c9d3a MT |
1071 | break; |
1072 | ||
ab617971 | 1073 | default: |
876c9d3a MT |
1074 | break; |
1075 | } | |
1076 | ||
ab617971 DW |
1077 | pos += elem->len + 2; |
1078 | } | |
fcdb53db DW |
1079 | |
1080 | /* Timestamp */ | |
1081 | bss->last_scanned = jiffies; | |
8c512765 | 1082 | libertas_unset_basic_rate_flags(bss->rates, sizeof(bss->rates)); |
fcdb53db | 1083 | |
9012b28a | 1084 | ret = 0; |
876c9d3a | 1085 | |
9012b28a HS |
1086 | done: |
1087 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); | |
1088 | return ret; | |
876c9d3a MT |
1089 | } |
1090 | ||
1091 | /** | |
1092 | * @brief Compare two SSIDs | |
1093 | * | |
1094 | * @param ssid1 A pointer to ssid to compare | |
1095 | * @param ssid2 A pointer to ssid to compare | |
1096 | * | |
1097 | * @return 0--ssid is same, otherwise is different | |
1098 | */ | |
717c9339 | 1099 | int libertas_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len) |
876c9d3a | 1100 | { |
d8efea25 | 1101 | if (ssid1_len != ssid2_len) |
876c9d3a MT |
1102 | return -1; |
1103 | ||
d8efea25 | 1104 | return memcmp(ssid1, ssid2, ssid1_len); |
876c9d3a MT |
1105 | } |
1106 | ||
1107 | /** | |
1108 | * @brief This function finds a specific compatible BSSID in the scan list | |
1109 | * | |
1110 | * @param adapter A pointer to wlan_adapter | |
1111 | * @param bssid BSSID to find in the scan list | |
1112 | * @param mode Network mode: Infrastructure or IBSS | |
1113 | * | |
1114 | * @return index in BSSID list, or error return code (< 0) | |
1115 | */ | |
717c9339 | 1116 | struct bss_descriptor * libertas_find_bssid_in_list(wlan_adapter * adapter, |
fcdb53db | 1117 | u8 * bssid, u8 mode) |
876c9d3a | 1118 | { |
fcdb53db DW |
1119 | struct bss_descriptor * iter_bss; |
1120 | struct bss_descriptor * found_bss = NULL; | |
876c9d3a MT |
1121 | |
1122 | if (!bssid) | |
fcdb53db | 1123 | return NULL; |
876c9d3a | 1124 | |
ece56191 | 1125 | lbs_deb_hex(LBS_DEB_SCAN, "looking for", |
fcdb53db | 1126 | bssid, ETH_ALEN); |
876c9d3a | 1127 | |
fcdb53db DW |
1128 | /* Look through the scan table for a compatible match. The loop will |
1129 | * continue past a matched bssid that is not compatible in case there | |
1130 | * is an AP with multiple SSIDs assigned to the same BSSID | |
876c9d3a | 1131 | */ |
fcdb53db DW |
1132 | mutex_lock(&adapter->lock); |
1133 | list_for_each_entry (iter_bss, &adapter->network_list, list) { | |
3cf20931 | 1134 | if (compare_ether_addr(iter_bss->bssid, bssid)) |
fcdb53db DW |
1135 | continue; /* bssid doesn't match */ |
1136 | switch (mode) { | |
1137 | case IW_MODE_INFRA: | |
1138 | case IW_MODE_ADHOC: | |
1139 | if (!is_network_compatible(adapter, iter_bss, mode)) | |
876c9d3a | 1140 | break; |
fcdb53db DW |
1141 | found_bss = iter_bss; |
1142 | break; | |
1143 | default: | |
1144 | found_bss = iter_bss; | |
1145 | break; | |
876c9d3a MT |
1146 | } |
1147 | } | |
fcdb53db | 1148 | mutex_unlock(&adapter->lock); |
876c9d3a | 1149 | |
fcdb53db | 1150 | return found_bss; |
876c9d3a MT |
1151 | } |
1152 | ||
1153 | /** | |
1154 | * @brief This function finds ssid in ssid list. | |
1155 | * | |
1156 | * @param adapter A pointer to wlan_adapter | |
1157 | * @param ssid SSID to find in the list | |
1158 | * @param bssid BSSID to qualify the SSID selection (if provided) | |
1159 | * @param mode Network mode: Infrastructure or IBSS | |
1160 | * | |
1161 | * @return index in BSSID list | |
1162 | */ | |
717c9339 | 1163 | struct bss_descriptor * libertas_find_ssid_in_list(wlan_adapter * adapter, |
d8efea25 | 1164 | u8 *ssid, u8 ssid_len, u8 * bssid, u8 mode, |
aeea0ab4 | 1165 | int channel) |
876c9d3a | 1166 | { |
876c9d3a | 1167 | u8 bestrssi = 0; |
fcdb53db DW |
1168 | struct bss_descriptor * iter_bss = NULL; |
1169 | struct bss_descriptor * found_bss = NULL; | |
1170 | struct bss_descriptor * tmp_oldest = NULL; | |
876c9d3a | 1171 | |
fcdb53db DW |
1172 | mutex_lock(&adapter->lock); |
1173 | ||
1174 | list_for_each_entry (iter_bss, &adapter->network_list, list) { | |
1175 | if ( !tmp_oldest | |
1176 | || (iter_bss->last_scanned < tmp_oldest->last_scanned)) | |
1177 | tmp_oldest = iter_bss; | |
1178 | ||
717c9339 | 1179 | if (libertas_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len, |
d8efea25 | 1180 | ssid, ssid_len) != 0) |
fcdb53db | 1181 | continue; /* ssid doesn't match */ |
3cf20931 | 1182 | if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0) |
fcdb53db | 1183 | continue; /* bssid doesn't match */ |
aeea0ab4 DW |
1184 | if ((channel > 0) && (iter_bss->channel != channel)) |
1185 | continue; /* channel doesn't match */ | |
fcdb53db DW |
1186 | |
1187 | switch (mode) { | |
1188 | case IW_MODE_INFRA: | |
1189 | case IW_MODE_ADHOC: | |
1190 | if (!is_network_compatible(adapter, iter_bss, mode)) | |
876c9d3a | 1191 | break; |
fcdb53db DW |
1192 | |
1193 | if (bssid) { | |
1194 | /* Found requested BSSID */ | |
1195 | found_bss = iter_bss; | |
1196 | goto out; | |
1197 | } | |
1198 | ||
1199 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | |
1200 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
1201 | found_bss = iter_bss; | |
1202 | } | |
1203 | break; | |
1204 | case IW_MODE_AUTO: | |
1205 | default: | |
1206 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | |
1207 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
1208 | found_bss = iter_bss; | |
876c9d3a | 1209 | } |
fcdb53db | 1210 | break; |
876c9d3a MT |
1211 | } |
1212 | } | |
1213 | ||
fcdb53db DW |
1214 | out: |
1215 | mutex_unlock(&adapter->lock); | |
1216 | return found_bss; | |
876c9d3a MT |
1217 | } |
1218 | ||
1219 | /** | |
1220 | * @brief This function finds the best SSID in the Scan List | |
1221 | * | |
1222 | * Search the scan table for the best SSID that also matches the current | |
1223 | * adapter network preference (infrastructure or adhoc) | |
1224 | * | |
1225 | * @param adapter A pointer to wlan_adapter | |
1226 | * | |
1227 | * @return index in BSSID list | |
1228 | */ | |
ac558ca2 | 1229 | static struct bss_descriptor * libertas_find_best_ssid_in_list(wlan_adapter * adapter, |
fcdb53db | 1230 | u8 mode) |
876c9d3a | 1231 | { |
876c9d3a | 1232 | u8 bestrssi = 0; |
fcdb53db DW |
1233 | struct bss_descriptor * iter_bss; |
1234 | struct bss_descriptor * best_bss = NULL; | |
876c9d3a | 1235 | |
fcdb53db | 1236 | mutex_lock(&adapter->lock); |
876c9d3a | 1237 | |
fcdb53db | 1238 | list_for_each_entry (iter_bss, &adapter->network_list, list) { |
876c9d3a | 1239 | switch (mode) { |
0dc5a290 DW |
1240 | case IW_MODE_INFRA: |
1241 | case IW_MODE_ADHOC: | |
fcdb53db DW |
1242 | if (!is_network_compatible(adapter, iter_bss, mode)) |
1243 | break; | |
1244 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | |
1245 | break; | |
1246 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
1247 | best_bss = iter_bss; | |
876c9d3a | 1248 | break; |
0dc5a290 | 1249 | case IW_MODE_AUTO: |
876c9d3a | 1250 | default: |
fcdb53db DW |
1251 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) |
1252 | break; | |
1253 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
1254 | best_bss = iter_bss; | |
876c9d3a MT |
1255 | break; |
1256 | } | |
1257 | } | |
1258 | ||
fcdb53db DW |
1259 | mutex_unlock(&adapter->lock); |
1260 | return best_bss; | |
876c9d3a MT |
1261 | } |
1262 | ||
1263 | /** | |
1264 | * @brief Find the AP with specific ssid in the scan list | |
1265 | * | |
1266 | * @param priv A pointer to wlan_private structure | |
1267 | * @param pSSID A pointer to AP's ssid | |
1268 | * | |
1269 | * @return 0--success, otherwise--fail | |
1270 | */ | |
717c9339 | 1271 | int libertas_find_best_network_ssid(wlan_private * priv, |
d8efea25 | 1272 | u8 *out_ssid, u8 *out_ssid_len, u8 preferred_mode, u8 *out_mode) |
876c9d3a MT |
1273 | { |
1274 | wlan_adapter *adapter = priv->adapter; | |
fcdb53db DW |
1275 | int ret = -1; |
1276 | struct bss_descriptor * found; | |
876c9d3a | 1277 | |
9012b28a | 1278 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 1279 | |
2be92196 | 1280 | wlan_scan_networks(priv, NULL, 1); |
876c9d3a MT |
1281 | if (adapter->surpriseremoved) |
1282 | return -1; | |
876c9d3a | 1283 | |
fcdb53db | 1284 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); |
876c9d3a | 1285 | |
717c9339 | 1286 | found = libertas_find_best_ssid_in_list(adapter, preferred_mode); |
d8efea25 DW |
1287 | if (found && (found->ssid_len > 0)) { |
1288 | memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); | |
1289 | *out_ssid_len = found->ssid_len; | |
fcdb53db DW |
1290 | *out_mode = found->mode; |
1291 | ret = 0; | |
876c9d3a MT |
1292 | } |
1293 | ||
9012b28a | 1294 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); |
876c9d3a MT |
1295 | return ret; |
1296 | } | |
1297 | ||
1298 | /** | |
1299 | * @brief Scan Network | |
1300 | * | |
1301 | * @param dev A pointer to net_device structure | |
1302 | * @param info A pointer to iw_request_info structure | |
1303 | * @param vwrq A pointer to iw_param structure | |
1304 | * @param extra A pointer to extra data buf | |
1305 | * | |
1306 | * @return 0 --success, otherwise fail | |
1307 | */ | |
1308 | int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, | |
1309 | struct iw_param *vwrq, char *extra) | |
1310 | { | |
1311 | wlan_private *priv = dev->priv; | |
1312 | wlan_adapter *adapter = priv->adapter; | |
876c9d3a | 1313 | |
9012b28a | 1314 | lbs_deb_enter(LBS_DEB_SCAN); |
876c9d3a | 1315 | |
2afc0c5d DW |
1316 | if (!delayed_work_pending(&priv->scan_work)) { |
1317 | queue_delayed_work(priv->work_thread, &priv->scan_work, | |
1318 | msecs_to_jiffies(50)); | |
1319 | } | |
876c9d3a MT |
1320 | |
1321 | if (adapter->surpriseremoved) | |
1322 | return -1; | |
1323 | ||
9012b28a | 1324 | lbs_deb_leave(LBS_DEB_SCAN); |
876c9d3a MT |
1325 | return 0; |
1326 | } | |
1327 | ||
1328 | /** | |
1329 | * @brief Send a scan command for all available channels filtered on a spec | |
1330 | * | |
1331 | * @param priv A pointer to wlan_private structure | |
1332 | * @param prequestedssid A pointer to AP's ssid | |
1333 | * @param keeppreviousscan Flag used to save/clear scan table before scan | |
1334 | * | |
1335 | * @return 0-success, otherwise fail | |
1336 | */ | |
717c9339 | 1337 | int libertas_send_specific_ssid_scan(wlan_private * priv, |
d8efea25 | 1338 | u8 *ssid, u8 ssid_len, u8 clear_ssid) |
876c9d3a MT |
1339 | { |
1340 | wlan_adapter *adapter = priv->adapter; | |
1341 | struct wlan_ioctl_user_scan_cfg scancfg; | |
eb8f7330 | 1342 | int ret = 0; |
876c9d3a | 1343 | |
9012b28a | 1344 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 1345 | |
d8efea25 | 1346 | if (!ssid_len) |
eb8f7330 | 1347 | goto out; |
876c9d3a MT |
1348 | |
1349 | memset(&scancfg, 0x00, sizeof(scancfg)); | |
d8efea25 DW |
1350 | memcpy(scancfg.ssid, ssid, ssid_len); |
1351 | scancfg.ssid_len = ssid_len; | |
eb8f7330 | 1352 | scancfg.clear_ssid = clear_ssid; |
876c9d3a | 1353 | |
2be92196 | 1354 | wlan_scan_networks(priv, &scancfg, 1); |
876c9d3a MT |
1355 | if (adapter->surpriseremoved) |
1356 | return -1; | |
1357 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); | |
1358 | ||
eb8f7330 | 1359 | out: |
9012b28a | 1360 | lbs_deb_leave(LBS_DEB_ASSOC); |
eb8f7330 | 1361 | return ret; |
876c9d3a MT |
1362 | } |
1363 | ||
00af0157 DW |
1364 | #define MAX_CUSTOM_LEN 64 |
1365 | ||
fcdb53db DW |
1366 | static inline char *libertas_translate_scan(wlan_private *priv, |
1367 | char *start, char *stop, | |
1368 | struct bss_descriptor *bss) | |
876c9d3a | 1369 | { |
876c9d3a | 1370 | wlan_adapter *adapter = priv->adapter; |
876c9d3a | 1371 | struct chan_freq_power *cfp; |
876c9d3a MT |
1372 | char *current_val; /* For rates */ |
1373 | struct iw_event iwe; /* Temporary buffer */ | |
876c9d3a | 1374 | int j; |
876c9d3a MT |
1375 | #define PERFECT_RSSI ((u8)50) |
1376 | #define WORST_RSSI ((u8)0) | |
1377 | #define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) | |
1378 | u8 rssi; | |
1379 | ||
fcdb53db DW |
1380 | cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, bss->channel); |
1381 | if (!cfp) { | |
1382 | lbs_deb_scan("Invalid channel number %d\n", bss->channel); | |
1383 | return NULL; | |
2be92196 | 1384 | } |
876c9d3a | 1385 | |
fcdb53db DW |
1386 | /* First entry *MUST* be the AP BSSID */ |
1387 | iwe.cmd = SIOCGIWAP; | |
1388 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | |
1389 | memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN); | |
1390 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); | |
1391 | ||
1392 | /* SSID */ | |
1393 | iwe.cmd = SIOCGIWESSID; | |
1394 | iwe.u.data.flags = 1; | |
d8efea25 DW |
1395 | iwe.u.data.length = min((u32) bss->ssid_len, (u32) IW_ESSID_MAX_SIZE); |
1396 | start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); | |
fcdb53db DW |
1397 | |
1398 | /* Mode */ | |
1399 | iwe.cmd = SIOCGIWMODE; | |
1400 | iwe.u.mode = bss->mode; | |
1401 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); | |
1402 | ||
1403 | /* Frequency */ | |
1404 | iwe.cmd = SIOCGIWFREQ; | |
1405 | iwe.u.freq.m = (long)cfp->freq * 100000; | |
1406 | iwe.u.freq.e = 1; | |
1407 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); | |
1408 | ||
1409 | /* Add quality statistics */ | |
1410 | iwe.cmd = IWEVQUAL; | |
1411 | iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; | |
1412 | iwe.u.qual.level = SCAN_RSSI(bss->rssi); | |
1413 | ||
1414 | rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; | |
1415 | iwe.u.qual.qual = | |
1416 | (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * | |
1417 | (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / | |
1418 | (RSSI_DIFF * RSSI_DIFF); | |
1419 | if (iwe.u.qual.qual > 100) | |
1420 | iwe.u.qual.qual = 100; | |
1421 | ||
1422 | if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { | |
1423 | iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; | |
1424 | } else { | |
1425 | iwe.u.qual.noise = | |
1426 | CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | |
1427 | } | |
80e78ef7 DW |
1428 | |
1429 | /* Locally created ad-hoc BSSs won't have beacons if this is the | |
1430 | * only station in the adhoc network; so get signal strength | |
1431 | * from receive statistics. | |
1432 | */ | |
1433 | if ((adapter->mode == IW_MODE_ADHOC) | |
1434 | && adapter->adhoccreate | |
717c9339 | 1435 | && !libertas_ssid_cmp(adapter->curbssparams.ssid, |
d8efea25 DW |
1436 | adapter->curbssparams.ssid_len, |
1437 | bss->ssid, bss->ssid_len)) { | |
80e78ef7 DW |
1438 | int snr, nf; |
1439 | snr = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; | |
1440 | nf = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; | |
1441 | iwe.u.qual.level = CAL_RSSI(snr, nf); | |
fcdb53db DW |
1442 | } |
1443 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); | |
876c9d3a | 1444 | |
fcdb53db DW |
1445 | /* Add encryption capability */ |
1446 | iwe.cmd = SIOCGIWENCODE; | |
0c9ca690 | 1447 | if (bss->capability & WLAN_CAPABILITY_PRIVACY) { |
fcdb53db DW |
1448 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; |
1449 | } else { | |
1450 | iwe.u.data.flags = IW_ENCODE_DISABLED; | |
1451 | } | |
1452 | iwe.u.data.length = 0; | |
d8efea25 | 1453 | start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); |
876c9d3a | 1454 | |
fcdb53db | 1455 | current_val = start + IW_EV_LCP_LEN; |
876c9d3a | 1456 | |
fcdb53db DW |
1457 | iwe.cmd = SIOCGIWRATE; |
1458 | iwe.u.bitrate.fixed = 0; | |
1459 | iwe.u.bitrate.disabled = 0; | |
1460 | iwe.u.bitrate.value = 0; | |
876c9d3a | 1461 | |
8c512765 DW |
1462 | for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) { |
1463 | /* Bit rate given in 500 kb/s units */ | |
1464 | iwe.u.bitrate.value = bss->rates[j] * 500000; | |
fcdb53db DW |
1465 | current_val = iwe_stream_add_value(start, current_val, |
1466 | stop, &iwe, IW_EV_PARAM_LEN); | |
1467 | } | |
1468 | if ((bss->mode == IW_MODE_ADHOC) | |
717c9339 | 1469 | && !libertas_ssid_cmp(adapter->curbssparams.ssid, |
d8efea25 DW |
1470 | adapter->curbssparams.ssid_len, |
1471 | bss->ssid, bss->ssid_len) | |
fcdb53db DW |
1472 | && adapter->adhoccreate) { |
1473 | iwe.u.bitrate.value = 22 * 500000; | |
1474 | current_val = iwe_stream_add_value(start, current_val, | |
1475 | stop, &iwe, IW_EV_PARAM_LEN); | |
1476 | } | |
1477 | /* Check if we added any event */ | |
1478 | if((current_val - start) > IW_EV_LCP_LEN) | |
1479 | start = current_val; | |
1480 | ||
1481 | memset(&iwe, 0, sizeof(iwe)); | |
1482 | if (bss->wpa_ie_len) { | |
1483 | char buf[MAX_WPA_IE_LEN]; | |
1484 | memcpy(buf, bss->wpa_ie, bss->wpa_ie_len); | |
1485 | iwe.cmd = IWEVGENIE; | |
1486 | iwe.u.data.length = bss->wpa_ie_len; | |
1487 | start = iwe_stream_add_point(start, stop, &iwe, buf); | |
1488 | } | |
876c9d3a | 1489 | |
fcdb53db DW |
1490 | memset(&iwe, 0, sizeof(iwe)); |
1491 | if (bss->rsn_ie_len) { | |
1492 | char buf[MAX_WPA_IE_LEN]; | |
1493 | memcpy(buf, bss->rsn_ie, bss->rsn_ie_len); | |
1494 | iwe.cmd = IWEVGENIE; | |
1495 | iwe.u.data.length = bss->rsn_ie_len; | |
1496 | start = iwe_stream_add_point(start, stop, &iwe, buf); | |
1497 | } | |
876c9d3a | 1498 | |
00af0157 DW |
1499 | if (bss->mesh) { |
1500 | char custom[MAX_CUSTOM_LEN]; | |
1501 | char *p = custom; | |
1502 | ||
1503 | iwe.cmd = IWEVCUSTOM; | |
1504 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), | |
1505 | "mesh-type: olpc"); | |
1506 | iwe.u.data.length = p - custom; | |
1507 | if (iwe.u.data.length) | |
1508 | start = iwe_stream_add_point(start, stop, &iwe, custom); | |
1509 | } | |
1510 | ||
fcdb53db DW |
1511 | return start; |
1512 | } | |
876c9d3a | 1513 | |
fcdb53db DW |
1514 | /** |
1515 | * @brief Retrieve the scan table entries via wireless tools IOCTL call | |
1516 | * | |
1517 | * @param dev A pointer to net_device structure | |
1518 | * @param info A pointer to iw_request_info structure | |
1519 | * @param dwrq A pointer to iw_point structure | |
1520 | * @param extra A pointer to extra data buf | |
1521 | * | |
1522 | * @return 0 --success, otherwise fail | |
1523 | */ | |
1524 | int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, | |
1525 | struct iw_point *dwrq, char *extra) | |
1526 | { | |
1527 | #define SCAN_ITEM_SIZE 128 | |
1528 | wlan_private *priv = dev->priv; | |
1529 | wlan_adapter *adapter = priv->adapter; | |
1530 | int err = 0; | |
1531 | char *ev = extra; | |
1532 | char *stop = ev + dwrq->length; | |
1533 | struct bss_descriptor * iter_bss; | |
1534 | struct bss_descriptor * safe; | |
876c9d3a | 1535 | |
fcdb53db | 1536 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 1537 | |
80e78ef7 | 1538 | /* Update RSSI if current BSS is a locally created ad-hoc BSS */ |
aeea0ab4 | 1539 | if ((adapter->mode == IW_MODE_ADHOC) && adapter->adhoccreate) { |
0aef64d7 DW |
1540 | libertas_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, |
1541 | CMD_OPTION_WAITFORRSP, 0, NULL); | |
80e78ef7 DW |
1542 | } |
1543 | ||
fcdb53db DW |
1544 | mutex_lock(&adapter->lock); |
1545 | list_for_each_entry_safe (iter_bss, safe, &adapter->network_list, list) { | |
1546 | char * next_ev; | |
1547 | unsigned long stale_time; | |
876c9d3a | 1548 | |
fcdb53db DW |
1549 | if (stop - ev < SCAN_ITEM_SIZE) { |
1550 | err = -E2BIG; | |
1551 | break; | |
876c9d3a | 1552 | } |
876c9d3a | 1553 | |
1e838bf3 LCC |
1554 | /* For mesh device, list only mesh networks */ |
1555 | if (dev == priv->mesh_dev && !iter_bss->mesh) | |
1556 | continue; | |
1557 | ||
fcdb53db DW |
1558 | /* Prune old an old scan result */ |
1559 | stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE; | |
1560 | if (time_after(jiffies, stale_time)) { | |
1561 | list_move_tail (&iter_bss->list, | |
1562 | &adapter->network_free_list); | |
1563 | clear_bss_descriptor(iter_bss); | |
1564 | continue; | |
876c9d3a MT |
1565 | } |
1566 | ||
fcdb53db DW |
1567 | /* Translate to WE format this entry */ |
1568 | next_ev = libertas_translate_scan(priv, ev, stop, iter_bss); | |
1569 | if (next_ev == NULL) | |
1570 | continue; | |
1571 | ev = next_ev; | |
876c9d3a | 1572 | } |
fcdb53db | 1573 | mutex_unlock(&adapter->lock); |
876c9d3a | 1574 | |
fcdb53db | 1575 | dwrq->length = (ev - extra); |
876c9d3a MT |
1576 | dwrq->flags = 0; |
1577 | ||
9012b28a | 1578 | lbs_deb_leave(LBS_DEB_ASSOC); |
fcdb53db | 1579 | return err; |
876c9d3a MT |
1580 | } |
1581 | ||
1582 | /** | |
1583 | * @brief Prepare a scan command to be sent to the firmware | |
1584 | * | |
1585 | * Use the wlan_scan_cmd_config sent to the command processing module in | |
1586 | * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command | |
1587 | * struct to send to firmware. | |
1588 | * | |
1589 | * The fixed fields specifying the BSS type and BSSID filters as well as a | |
1590 | * variable number/length of TLVs are sent in the command to firmware. | |
1591 | * | |
1592 | * @param priv A pointer to wlan_private structure | |
1593 | * @param cmd A pointer to cmd_ds_command structure to be sent to | |
1594 | * firmware with the cmd_DS_801_11_SCAN structure | |
1595 | * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used | |
1596 | * to set the fields/TLVs for the command sent to firmware | |
1597 | * | |
1598 | * @return 0 or -1 | |
1599 | * | |
1600 | * @sa wlan_scan_create_channel_list | |
1601 | */ | |
1602 | int libertas_cmd_80211_scan(wlan_private * priv, | |
1603 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1604 | { | |
1605 | struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; | |
1606 | struct wlan_scan_cmd_config *pscancfg; | |
1607 | ||
9012b28a | 1608 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
1609 | |
1610 | pscancfg = pdata_buf; | |
1611 | ||
1612 | /* Set fixed field variables in scan command */ | |
1613 | pscan->bsstype = pscancfg->bsstype; | |
492b6da7 | 1614 | memcpy(pscan->bssid, pscancfg->bssid, ETH_ALEN); |
876c9d3a MT |
1615 | memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); |
1616 | ||
0aef64d7 | 1617 | cmd->command = cpu_to_le16(CMD_802_11_SCAN); |
876c9d3a MT |
1618 | |
1619 | /* size is equal to the sizeof(fixed portions) + the TLV len + header */ | |
492b6da7 DW |
1620 | cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + ETH_ALEN |
1621 | + pscancfg->tlvbufferlen + S_DS_GEN); | |
876c9d3a | 1622 | |
9012b28a | 1623 | lbs_deb_scan("SCAN_CMD: command=%x, size=%x, seqnum=%x\n", |
981f187b DW |
1624 | le16_to_cpu(cmd->command), le16_to_cpu(cmd->size), |
1625 | le16_to_cpu(cmd->seqnum)); | |
9012b28a HS |
1626 | |
1627 | lbs_deb_leave(LBS_DEB_ASSOC); | |
876c9d3a MT |
1628 | return 0; |
1629 | } | |
1630 | ||
fcdb53db DW |
1631 | static inline int is_same_network(struct bss_descriptor *src, |
1632 | struct bss_descriptor *dst) | |
1633 | { | |
1634 | /* A network is only a duplicate if the channel, BSSID, and ESSID | |
1635 | * all match. We treat all <hidden> with the same BSSID and channel | |
1636 | * as one network */ | |
d8efea25 | 1637 | return ((src->ssid_len == dst->ssid_len) && |
fcdb53db DW |
1638 | (src->channel == dst->channel) && |
1639 | !compare_ether_addr(src->bssid, dst->bssid) && | |
d8efea25 | 1640 | !memcmp(src->ssid, dst->ssid, src->ssid_len)); |
fcdb53db DW |
1641 | } |
1642 | ||
876c9d3a MT |
1643 | /** |
1644 | * @brief This function handles the command response of scan | |
1645 | * | |
1646 | * The response buffer for the scan command has the following | |
1647 | * memory layout: | |
1648 | * | |
1649 | * .-----------------------------------------------------------. | |
1650 | * | header (4 * sizeof(u16)): Standard command response hdr | | |
1651 | * .-----------------------------------------------------------. | |
1652 | * | bufsize (u16) : sizeof the BSS Description data | | |
1653 | * .-----------------------------------------------------------. | |
1654 | * | NumOfSet (u8) : Number of BSS Descs returned | | |
1655 | * .-----------------------------------------------------------. | |
1656 | * | BSSDescription data (variable, size given in bufsize) | | |
1657 | * .-----------------------------------------------------------. | |
1658 | * | TLV data (variable, size calculated using header->size, | | |
1659 | * | bufsize and sizeof the fixed fields above) | | |
1660 | * .-----------------------------------------------------------. | |
1661 | * | |
1662 | * @param priv A pointer to wlan_private structure | |
1663 | * @param resp A pointer to cmd_ds_command | |
1664 | * | |
1665 | * @return 0 or -1 | |
1666 | */ | |
1667 | int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) | |
1668 | { | |
1669 | wlan_adapter *adapter = priv->adapter; | |
1670 | struct cmd_ds_802_11_scan_rsp *pscan; | |
fcdb53db DW |
1671 | struct bss_descriptor * iter_bss; |
1672 | struct bss_descriptor * safe; | |
876c9d3a MT |
1673 | u8 *pbssinfo; |
1674 | u16 scanrespsize; | |
1675 | int bytesleft; | |
876c9d3a MT |
1676 | int idx; |
1677 | int tlvbufsize; | |
9012b28a | 1678 | int ret; |
876c9d3a | 1679 | |
9012b28a | 1680 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 1681 | |
fcdb53db DW |
1682 | /* Prune old entries from scan table */ |
1683 | list_for_each_entry_safe (iter_bss, safe, &adapter->network_list, list) { | |
1684 | unsigned long stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE; | |
1685 | if (time_before(jiffies, stale_time)) | |
1686 | continue; | |
1687 | list_move_tail (&iter_bss->list, &adapter->network_free_list); | |
1688 | clear_bss_descriptor(iter_bss); | |
1689 | } | |
1690 | ||
876c9d3a MT |
1691 | pscan = &resp->params.scanresp; |
1692 | ||
fcdb53db DW |
1693 | if (pscan->nr_sets > MAX_NETWORK_COUNT) { |
1694 | lbs_deb_scan( | |
1695 | "SCAN_RESP: too many scan results (%d, max %d)!!\n", | |
1696 | pscan->nr_sets, MAX_NETWORK_COUNT); | |
9012b28a HS |
1697 | ret = -1; |
1698 | goto done; | |
876c9d3a MT |
1699 | } |
1700 | ||
1701 | bytesleft = le16_to_cpu(pscan->bssdescriptsize); | |
9012b28a | 1702 | lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft); |
876c9d3a MT |
1703 | |
1704 | scanrespsize = le16_to_cpu(resp->size); | |
9012b28a | 1705 | lbs_deb_scan("SCAN_RESP: returned %d AP before parsing\n", |
876c9d3a MT |
1706 | pscan->nr_sets); |
1707 | ||
876c9d3a MT |
1708 | pbssinfo = pscan->bssdesc_and_tlvbuffer; |
1709 | ||
1710 | /* The size of the TLV buffer is equal to the entire command response | |
1711 | * size (scanrespsize) minus the fixed fields (sizeof()'s), the | |
1712 | * BSS Descriptions (bssdescriptsize as bytesLef) and the command | |
1713 | * response header (S_DS_GEN) | |
1714 | */ | |
1715 | tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) | |
1716 | + sizeof(pscan->nr_sets) | |
1717 | + S_DS_GEN); | |
1718 | ||
876c9d3a MT |
1719 | /* |
1720 | * Process each scan response returned (pscan->nr_sets). Save | |
1721 | * the information in the newbssentry and then insert into the | |
1722 | * driver scan table either as an update to an existing entry | |
1723 | * or as an addition at the end of the table | |
1724 | */ | |
1725 | for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { | |
fcdb53db DW |
1726 | struct bss_descriptor new; |
1727 | struct bss_descriptor * found = NULL; | |
fcdb53db | 1728 | struct bss_descriptor * oldest = NULL; |
0795af57 | 1729 | DECLARE_MAC_BUF(mac); |
876c9d3a MT |
1730 | |
1731 | /* Process the data fields and IEs returned for this BSS */ | |
fcdb53db DW |
1732 | memset(&new, 0, sizeof (struct bss_descriptor)); |
1733 | if (libertas_process_bss(&new, &pbssinfo, &bytesleft) != 0) { | |
1734 | /* error parsing the scan response, skipped */ | |
1735 | lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n"); | |
1736 | continue; | |
1737 | } | |
876c9d3a | 1738 | |
fcdb53db DW |
1739 | /* Try to find this bss in the scan table */ |
1740 | list_for_each_entry (iter_bss, &adapter->network_list, list) { | |
1741 | if (is_same_network(iter_bss, &new)) { | |
1742 | found = iter_bss; | |
1743 | break; | |
876c9d3a MT |
1744 | } |
1745 | ||
fcdb53db DW |
1746 | if ((oldest == NULL) || |
1747 | (iter_bss->last_scanned < oldest->last_scanned)) | |
1748 | oldest = iter_bss; | |
1749 | } | |
876c9d3a | 1750 | |
fcdb53db DW |
1751 | if (found) { |
1752 | /* found, clear it */ | |
1753 | clear_bss_descriptor(found); | |
1754 | } else if (!list_empty(&adapter->network_free_list)) { | |
1755 | /* Pull one from the free list */ | |
1756 | found = list_entry(adapter->network_free_list.next, | |
1757 | struct bss_descriptor, list); | |
1758 | list_move_tail(&found->list, &adapter->network_list); | |
1759 | } else if (oldest) { | |
1760 | /* If there are no more slots, expire the oldest */ | |
1761 | found = oldest; | |
1762 | clear_bss_descriptor(found); | |
1763 | list_move_tail(&found->list, &adapter->network_list); | |
876c9d3a | 1764 | } else { |
fcdb53db DW |
1765 | continue; |
1766 | } | |
876c9d3a | 1767 | |
0795af57 JP |
1768 | lbs_deb_scan("SCAN_RESP: BSSID = %s\n", |
1769 | print_mac(mac, new.bssid)); | |
fcdb53db | 1770 | |
fcdb53db DW |
1771 | /* Copy the locally created newbssentry to the scan table */ |
1772 | memcpy(found, &new, offsetof(struct bss_descriptor, list)); | |
1773 | } | |
876c9d3a | 1774 | |
9012b28a | 1775 | ret = 0; |
876c9d3a | 1776 | |
9012b28a HS |
1777 | done: |
1778 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); | |
1779 | return ret; | |
876c9d3a | 1780 | } |