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> | |
11 | ||
12 | #include <net/ieee80211.h> | |
13 | #include <net/iw_handler.h> | |
14 | ||
15 | #include "host.h" | |
16 | #include "decl.h" | |
17 | #include "dev.h" | |
18 | #include "scan.h" | |
19 | ||
20 | //! Approximate amount of data needed to pass a scan result back to iwlist | |
21 | #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ | |
22 | + IW_ESSID_MAX_SIZE \ | |
23 | + IW_EV_UINT_LEN \ | |
24 | + IW_EV_FREQ_LEN \ | |
25 | + IW_EV_QUAL_LEN \ | |
26 | + IW_ESSID_MAX_SIZE \ | |
27 | + IW_EV_PARAM_LEN \ | |
28 | + 40) /* 40 for WPAIE */ | |
29 | ||
30 | //! Memory needed to store a max sized channel List TLV for a firmware scan | |
31 | #define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ | |
32 | + (MRVDRV_MAX_CHANNELS_PER_SCAN \ | |
33 | * sizeof(struct chanscanparamset))) | |
34 | ||
35 | //! Memory needed to store a max number/size SSID TLV for a firmware scan | |
36 | #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) | |
37 | ||
38 | //! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max | |
39 | #define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ | |
40 | + sizeof(struct mrvlietypes_numprobes) \ | |
41 | + CHAN_TLV_MAX_SIZE \ | |
42 | + SSID_TLV_MAX_SIZE) | |
43 | ||
44 | //! The maximum number of channels the firmware can scan per command | |
45 | #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 | |
46 | ||
47 | /** | |
48 | * @brief Number of channels to scan per firmware scan command issuance. | |
49 | * | |
50 | * Number restricted to prevent hitting the limit on the amount of scan data | |
51 | * returned in a single firmware scan command. | |
52 | */ | |
53 | #define MRVDRV_CHANNELS_PER_SCAN_CMD 4 | |
54 | ||
55 | //! Scan time specified in the channel TLV for each channel for passive scans | |
56 | #define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 | |
57 | ||
58 | //! Scan time specified in the channel TLV for each channel for active scans | |
59 | #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 | |
60 | ||
61 | //! Macro to enable/disable SSID checking before storing a scan table | |
62 | #ifdef DISCARD_BAD_SSID | |
63 | #define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) | |
64 | #else | |
65 | #define CHECK_SSID_IS_VALID(x) 1 | |
66 | #endif | |
67 | ||
68 | /** | |
69 | * @brief Check if a scanned network compatible with the driver settings | |
70 | * | |
71 | * WEP WPA WPA2 ad-hoc encrypt Network | |
72 | * enabled enabled enabled AES mode privacy WPA WPA2 Compatible | |
73 | * 0 0 0 0 NONE 0 0 0 yes No security | |
74 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | |
75 | * 0 1 0 0 x 1x 1 x yes WPA | |
76 | * 0 0 1 0 x 1x x 1 yes WPA2 | |
77 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | |
78 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | |
79 | * | |
80 | * | |
81 | * @param adapter A pointer to wlan_adapter | |
82 | * @param index Index in scantable to check against current driver settings | |
83 | * @param mode Network mode: Infrastructure or IBSS | |
84 | * | |
85 | * @return Index in scantable, or error code if negative | |
86 | */ | |
87 | static int is_network_compatible(wlan_adapter * adapter, int index, int mode) | |
88 | { | |
89 | ENTER(); | |
90 | ||
91 | if (adapter->scantable[index].inframode == mode) { | |
92 | if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | |
93 | && !adapter->secinfo.WPAenabled | |
94 | && !adapter->secinfo.WPA2enabled | |
51b0c9d0 DW |
95 | && adapter->scantable[index].wpa_ie[0] != WPA_IE |
96 | && adapter->scantable[index].rsn_ie[0] != WPA2_IE | |
97 | && adapter->secinfo.Encryptionmode == CIPHER_NONE | |
876c9d3a MT |
98 | && !adapter->scantable[index].privacy) { |
99 | /* no security */ | |
100 | LEAVE(); | |
101 | return index; | |
102 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled | |
103 | && !adapter->secinfo.WPAenabled | |
104 | && !adapter->secinfo.WPA2enabled | |
105 | && adapter->scantable[index].privacy) { | |
106 | /* static WEP enabled */ | |
107 | LEAVE(); | |
108 | return index; | |
109 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | |
110 | && adapter->secinfo.WPAenabled | |
111 | && !adapter->secinfo.WPA2enabled | |
51b0c9d0 | 112 | && (adapter->scantable[index].wpa_ie[0] == WPA_IE) |
876c9d3a MT |
113 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G |
114 | && adapter->scantable[index].privacy */ | |
115 | ) { | |
116 | /* WPA enabled */ | |
51b0c9d0 | 117 | lbs_pr_debug(1, |
876c9d3a MT |
118 | "is_network_compatible() WPA: index=%d wpa_ie=%#x " |
119 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " | |
120 | "privacy=%#x\n", index, | |
51b0c9d0 DW |
121 | adapter->scantable[index].wpa_ie[0], |
122 | adapter->scantable[index].rsn_ie[0], | |
876c9d3a MT |
123 | (adapter->secinfo.WEPstatus == |
124 | wlan802_11WEPenabled) ? "e" : "d", | |
125 | (adapter->secinfo.WPAenabled) ? "e" : "d", | |
126 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | |
127 | adapter->secinfo.Encryptionmode, | |
128 | adapter->scantable[index].privacy); | |
129 | LEAVE(); | |
130 | return index; | |
131 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | |
132 | && !adapter->secinfo.WPAenabled | |
133 | && adapter->secinfo.WPA2enabled | |
51b0c9d0 | 134 | && (adapter->scantable[index].rsn_ie[0] == WPA2_IE) |
876c9d3a MT |
135 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G |
136 | && adapter->scantable[index].privacy */ | |
137 | ) { | |
138 | /* WPA2 enabled */ | |
51b0c9d0 | 139 | lbs_pr_debug(1, |
876c9d3a MT |
140 | "is_network_compatible() WPA2: index=%d wpa_ie=%#x " |
141 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " | |
142 | "privacy=%#x\n", index, | |
51b0c9d0 DW |
143 | adapter->scantable[index].wpa_ie[0], |
144 | adapter->scantable[index].rsn_ie[0], | |
876c9d3a MT |
145 | (adapter->secinfo.WEPstatus == |
146 | wlan802_11WEPenabled) ? "e" : "d", | |
147 | (adapter->secinfo.WPAenabled) ? "e" : "d", | |
148 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | |
149 | adapter->secinfo.Encryptionmode, | |
150 | adapter->scantable[index].privacy); | |
151 | LEAVE(); | |
152 | return index; | |
153 | } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled | |
154 | && !adapter->secinfo.WPAenabled | |
155 | && !adapter->secinfo.WPA2enabled | |
51b0c9d0 DW |
156 | && (adapter->scantable[index].wpa_ie[0] != WPA_IE) |
157 | && (adapter->scantable[index].rsn_ie[0] != WPA2_IE) | |
876c9d3a MT |
158 | && adapter->secinfo.Encryptionmode != CIPHER_NONE |
159 | && adapter->scantable[index].privacy) { | |
160 | /* dynamic WEP enabled */ | |
51b0c9d0 | 161 | lbs_pr_debug(1, |
876c9d3a MT |
162 | "is_network_compatible() dynamic WEP: index=%d " |
163 | "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n", | |
164 | index, | |
51b0c9d0 DW |
165 | adapter->scantable[index].wpa_ie[0], |
166 | adapter->scantable[index].rsn_ie[0], | |
167 | adapter->secinfo.Encryptionmode, | |
876c9d3a MT |
168 | adapter->scantable[index].privacy); |
169 | LEAVE(); | |
170 | return index; | |
171 | } | |
172 | ||
173 | /* security doesn't match */ | |
51b0c9d0 | 174 | lbs_pr_debug(1, |
876c9d3a MT |
175 | "is_network_compatible() FAILED: index=%d wpa_ie=%#x " |
176 | "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n", | |
177 | index, | |
51b0c9d0 DW |
178 | adapter->scantable[index].wpa_ie[0], |
179 | adapter->scantable[index].rsn_ie[0], | |
876c9d3a MT |
180 | (adapter->secinfo.WEPstatus == |
181 | wlan802_11WEPenabled) ? "e" : "d", | |
182 | (adapter->secinfo.WPAenabled) ? "e" : "d", | |
183 | (adapter->secinfo.WPA2enabled) ? "e" : "d", | |
184 | adapter->secinfo.Encryptionmode, | |
185 | adapter->scantable[index].privacy); | |
186 | LEAVE(); | |
187 | return -ECONNREFUSED; | |
188 | } | |
189 | ||
190 | /* mode doesn't match */ | |
191 | LEAVE(); | |
192 | return -ENETUNREACH; | |
193 | } | |
194 | ||
195 | /** | |
196 | * @brief This function validates a SSID as being able to be printed | |
197 | * | |
198 | * @param pssid SSID structure to validate | |
199 | * | |
200 | * @return TRUE or FALSE | |
201 | */ | |
202 | static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) | |
203 | { | |
204 | int ssididx; | |
205 | ||
206 | for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { | |
207 | if (!isprint(pssid->ssid[ssididx])) { | |
208 | return 0; | |
209 | } | |
210 | } | |
211 | ||
212 | return 1; | |
213 | } | |
214 | ||
215 | /** | |
216 | * @brief Post process the scan table after a new scan command has completed | |
217 | * | |
218 | * Inspect each entry of the scan table and try to find an entry that | |
219 | * matches our current associated/joined network from the scan. If | |
220 | * one is found, update the stored copy of the bssdescriptor for our | |
221 | * current network. | |
222 | * | |
223 | * Debug dump the current scan table contents if compiled accordingly. | |
224 | * | |
225 | * @param priv A pointer to wlan_private structure | |
226 | * | |
227 | * @return void | |
228 | */ | |
229 | static void wlan_scan_process_results(wlan_private * priv) | |
230 | { | |
231 | wlan_adapter *adapter = priv->adapter; | |
232 | int foundcurrent; | |
233 | int i; | |
234 | ||
235 | foundcurrent = 0; | |
236 | ||
237 | if (adapter->connect_status == libertas_connected) { | |
238 | /* try to find the current BSSID in the new scan list */ | |
239 | for (i = 0; i < adapter->numinscantable; i++) { | |
240 | if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, | |
241 | &adapter->curbssparams.ssid) && | |
242 | !memcmp(adapter->curbssparams.bssid, | |
243 | adapter->scantable[i].macaddress, | |
244 | ETH_ALEN)) { | |
245 | foundcurrent = 1; | |
246 | } | |
247 | } | |
248 | ||
249 | if (foundcurrent) { | |
250 | /* Make a copy of current BSSID descriptor */ | |
251 | memcpy(&adapter->curbssparams.bssdescriptor, | |
252 | &adapter->scantable[i], | |
253 | sizeof(adapter->curbssparams.bssdescriptor)); | |
254 | } | |
255 | } | |
256 | ||
257 | for (i = 0; i < adapter->numinscantable; i++) { | |
258 | lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " | |
259 | "RSSI[%03d], SSID[%s]\n", | |
260 | i, | |
261 | adapter->scantable[i].macaddress[0], | |
262 | adapter->scantable[i].macaddress[1], | |
263 | adapter->scantable[i].macaddress[2], | |
264 | adapter->scantable[i].macaddress[3], | |
265 | adapter->scantable[i].macaddress[4], | |
266 | adapter->scantable[i].macaddress[5], | |
267 | (s32) adapter->scantable[i].rssi, | |
268 | adapter->scantable[i].ssid.ssid); | |
269 | } | |
270 | } | |
271 | ||
272 | /** | |
273 | * @brief Create a channel list for the driver to scan based on region info | |
274 | * | |
275 | * Use the driver region/band information to construct a comprehensive list | |
276 | * of channels to scan. This routine is used for any scan that is not | |
277 | * provided a specific channel list to scan. | |
278 | * | |
279 | * @param priv A pointer to wlan_private structure | |
280 | * @param scanchanlist Output parameter: resulting channel list to scan | |
281 | * @param filteredscan Flag indicating whether or not a BSSID or SSID filter | |
282 | * is being sent in the command to firmware. Used to | |
283 | * increase the number of channels sent in a scan | |
284 | * command and to disable the firmware channel scan | |
285 | * filter. | |
286 | * | |
287 | * @return void | |
288 | */ | |
289 | static void wlan_scan_create_channel_list(wlan_private * priv, | |
290 | struct chanscanparamset * scanchanlist, | |
291 | u8 filteredscan) | |
292 | { | |
293 | ||
294 | wlan_adapter *adapter = priv->adapter; | |
295 | struct region_channel *scanregion; | |
296 | struct chan_freq_power *cfp; | |
297 | int rgnidx; | |
298 | int chanidx; | |
299 | int nextchan; | |
300 | u8 scantype; | |
301 | ||
302 | chanidx = 0; | |
303 | ||
304 | /* Set the default scan type to the user specified type, will later | |
305 | * be changed to passive on a per channel basis if restricted by | |
306 | * regulatory requirements (11d or 11h) | |
307 | */ | |
308 | scantype = adapter->scantype; | |
309 | ||
310 | for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { | |
311 | if (priv->adapter->enable11d && | |
312 | adapter->connect_status != libertas_connected) { | |
313 | /* Scan all the supported chan for the first scan */ | |
314 | if (!adapter->universal_channel[rgnidx].valid) | |
315 | continue; | |
316 | scanregion = &adapter->universal_channel[rgnidx]; | |
317 | ||
318 | /* clear the parsed_region_chan for the first scan */ | |
319 | memset(&adapter->parsed_region_chan, 0x00, | |
320 | sizeof(adapter->parsed_region_chan)); | |
321 | } else { | |
322 | if (!adapter->region_channel[rgnidx].valid) | |
323 | continue; | |
324 | scanregion = &adapter->region_channel[rgnidx]; | |
325 | } | |
326 | ||
327 | for (nextchan = 0; | |
328 | nextchan < scanregion->nrcfp; nextchan++, chanidx++) { | |
329 | ||
330 | cfp = scanregion->CFP + nextchan; | |
331 | ||
332 | if (priv->adapter->enable11d) { | |
333 | scantype = | |
334 | libertas_get_scan_type_11d(cfp->channel, | |
335 | &adapter-> | |
336 | parsed_region_chan); | |
337 | } | |
338 | ||
339 | switch (scanregion->band) { | |
340 | case BAND_B: | |
341 | case BAND_G: | |
342 | default: | |
343 | scanchanlist[chanidx].radiotype = | |
344 | cmd_scan_radio_type_bg; | |
345 | break; | |
346 | } | |
347 | ||
348 | if (scantype == cmd_scan_type_passive) { | |
349 | scanchanlist[chanidx].maxscantime = | |
350 | cpu_to_le16 | |
351 | (MRVDRV_PASSIVE_SCAN_CHAN_TIME); | |
352 | scanchanlist[chanidx].chanscanmode.passivescan = | |
353 | 1; | |
354 | } else { | |
355 | scanchanlist[chanidx].maxscantime = | |
356 | cpu_to_le16 | |
357 | (MRVDRV_ACTIVE_SCAN_CHAN_TIME); | |
358 | scanchanlist[chanidx].chanscanmode.passivescan = | |
359 | 0; | |
360 | } | |
361 | ||
362 | scanchanlist[chanidx].channumber = cfp->channel; | |
363 | ||
364 | if (filteredscan) { | |
365 | scanchanlist[chanidx].chanscanmode. | |
366 | disablechanfilt = 1; | |
367 | } | |
368 | } | |
369 | } | |
370 | } | |
371 | ||
372 | /** | |
373 | * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds | |
374 | * | |
375 | * Application layer or other functions can invoke wlan_scan_networks | |
376 | * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. | |
377 | * This structure is used as the basis of one or many wlan_scan_cmd_config | |
378 | * commands that are sent to the command processing module and sent to | |
379 | * firmware. | |
380 | * | |
381 | * Create a wlan_scan_cmd_config based on the following user supplied | |
382 | * parameters (if present): | |
383 | * - SSID filter | |
384 | * - BSSID filter | |
385 | * - Number of Probes to be sent | |
386 | * - channel list | |
387 | * | |
388 | * If the SSID or BSSID filter is not present, disable/clear the filter. | |
389 | * If the number of probes is not set, use the adapter default setting | |
390 | * Qualify the channel | |
391 | * | |
392 | * @param priv A pointer to wlan_private structure | |
393 | * @param puserscanin NULL or pointer to scan configuration parameters | |
394 | * @param ppchantlvout Output parameter: Pointer to the start of the | |
395 | * channel TLV portion of the output scan config | |
396 | * @param pscanchanlist Output parameter: Pointer to the resulting channel | |
397 | * list to scan | |
398 | * @param pmaxchanperscan Output parameter: Number of channels to scan for | |
399 | * each issuance of the firmware scan command | |
400 | * @param pfilteredscan Output parameter: Flag indicating whether or not | |
401 | * a BSSID or SSID filter is being sent in the | |
402 | * command to firmware. Used to increase the number | |
403 | * of channels sent in a scan command and to | |
404 | * disable the firmware channel scan filter. | |
405 | * @param pscancurrentonly Output parameter: Flag indicating whether or not | |
406 | * we are only scanning our current active channel | |
407 | * | |
408 | * @return resulting scan configuration | |
409 | */ | |
410 | static struct wlan_scan_cmd_config * | |
411 | wlan_scan_setup_scan_config(wlan_private * priv, | |
412 | const struct wlan_ioctl_user_scan_cfg * puserscanin, | |
413 | struct mrvlietypes_chanlistparamset ** ppchantlvout, | |
414 | struct chanscanparamset * pscanchanlist, | |
415 | int *pmaxchanperscan, | |
416 | u8 * pfilteredscan, | |
417 | u8 * pscancurrentonly) | |
418 | { | |
419 | wlan_adapter *adapter = priv->adapter; | |
420 | const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | |
421 | struct mrvlietypes_numprobes *pnumprobestlv; | |
422 | struct mrvlietypes_ssidparamset *pssidtlv; | |
423 | struct wlan_scan_cmd_config * pscancfgout = NULL; | |
424 | u8 *ptlvpos; | |
425 | u16 numprobes; | |
426 | u16 ssidlen; | |
427 | int chanidx; | |
428 | int scantype; | |
429 | int scandur; | |
430 | int channel; | |
431 | int radiotype; | |
432 | ||
433 | pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); | |
434 | if (pscancfgout == NULL) | |
435 | goto out; | |
436 | ||
437 | /* The tlvbufferlen is calculated for each scan command. The TLVs added | |
438 | * in this routine will be preserved since the routine that sends | |
439 | * the command will append channelTLVs at *ppchantlvout. The difference | |
440 | * between the *ppchantlvout and the tlvbuffer start will be used | |
441 | * to calculate the size of anything we add in this routine. | |
442 | */ | |
443 | pscancfgout->tlvbufferlen = 0; | |
444 | ||
445 | /* Running tlv pointer. Assigned to ppchantlvout at end of function | |
446 | * so later routines know where channels can be added to the command buf | |
447 | */ | |
448 | ptlvpos = pscancfgout->tlvbuffer; | |
449 | ||
450 | /* | |
451 | * Set the initial scan paramters for progressive scanning. If a specific | |
452 | * BSSID or SSID is used, the number of channels in the scan command | |
453 | * will be increased to the absolute maximum | |
454 | */ | |
455 | *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; | |
456 | ||
457 | /* Initialize the scan as un-filtered by firmware, set to TRUE below if | |
458 | * a SSID or BSSID filter is sent in the command | |
459 | */ | |
460 | *pfilteredscan = 0; | |
461 | ||
462 | /* Initialize the scan as not being only on the current channel. If | |
463 | * the channel list is customized, only contains one channel, and | |
464 | * is the active channel, this is set true and data flow is not halted. | |
465 | */ | |
466 | *pscancurrentonly = 0; | |
467 | ||
468 | if (puserscanin) { | |
469 | ||
470 | /* Set the bss type scan filter, use adapter setting if unset */ | |
471 | pscancfgout->bsstype = | |
472 | (puserscanin->bsstype ? puserscanin->bsstype : adapter-> | |
473 | scanmode); | |
474 | ||
475 | /* Set the number of probes to send, use adapter setting if unset */ | |
476 | numprobes = (puserscanin->numprobes ? puserscanin->numprobes : | |
477 | adapter->scanprobes); | |
478 | ||
479 | /* | |
480 | * Set the BSSID filter to the incoming configuration, | |
481 | * if non-zero. If not set, it will remain disabled (all zeros). | |
482 | */ | |
483 | memcpy(pscancfgout->specificBSSID, | |
484 | puserscanin->specificBSSID, | |
485 | sizeof(pscancfgout->specificBSSID)); | |
486 | ||
487 | ssidlen = strlen(puserscanin->specificSSID); | |
488 | ||
489 | if (ssidlen) { | |
490 | pssidtlv = | |
491 | (struct mrvlietypes_ssidparamset *) pscancfgout-> | |
492 | tlvbuffer; | |
493 | pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); | |
494 | pssidtlv->header.len = cpu_to_le16(ssidlen); | |
495 | memcpy(pssidtlv->ssid, puserscanin->specificSSID, | |
496 | ssidlen); | |
497 | ptlvpos += sizeof(pssidtlv->header) + ssidlen; | |
498 | } | |
499 | ||
500 | /* | |
501 | * The default number of channels sent in the command is low to | |
502 | * ensure the response buffer from the firmware does not truncate | |
503 | * scan results. That is not an issue with an SSID or BSSID | |
504 | * filter applied to the scan results in the firmware. | |
505 | */ | |
506 | if (ssidlen || (memcmp(pscancfgout->specificBSSID, | |
507 | &zeromac, sizeof(zeromac)) != 0)) { | |
508 | *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; | |
509 | *pfilteredscan = 1; | |
510 | } | |
511 | } else { | |
512 | pscancfgout->bsstype = adapter->scanmode; | |
513 | numprobes = adapter->scanprobes; | |
514 | } | |
515 | ||
516 | /* If the input config or adapter has the number of Probes set, add tlv */ | |
517 | if (numprobes) { | |
518 | pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; | |
519 | pnumprobestlv->header.type = | |
520 | cpu_to_le16(TLV_TYPE_NUMPROBES); | |
521 | pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); | |
522 | pnumprobestlv->numprobes = cpu_to_le16(numprobes); | |
523 | ||
524 | ptlvpos += | |
525 | sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; | |
526 | ||
527 | pnumprobestlv->header.len = | |
528 | cpu_to_le16(pnumprobestlv->header.len); | |
529 | } | |
530 | ||
531 | /* | |
532 | * Set the output for the channel TLV to the address in the tlv buffer | |
533 | * past any TLVs that were added in this fuction (SSID, numprobes). | |
534 | * channel TLVs will be added past this for each scan command, preserving | |
535 | * the TLVs that were previously added. | |
536 | */ | |
537 | *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; | |
538 | ||
539 | if (puserscanin && puserscanin->chanlist[0].channumber) { | |
540 | ||
541 | lbs_pr_debug(1, "Scan: Using supplied channel list\n"); | |
542 | ||
543 | for (chanidx = 0; | |
544 | chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX | |
545 | && puserscanin->chanlist[chanidx].channumber; chanidx++) { | |
546 | ||
547 | channel = puserscanin->chanlist[chanidx].channumber; | |
548 | (pscanchanlist + chanidx)->channumber = channel; | |
549 | ||
550 | radiotype = puserscanin->chanlist[chanidx].radiotype; | |
551 | (pscanchanlist + chanidx)->radiotype = radiotype; | |
552 | ||
553 | scantype = puserscanin->chanlist[chanidx].scantype; | |
554 | ||
555 | if (scantype == cmd_scan_type_passive) { | |
556 | (pscanchanlist + | |
557 | chanidx)->chanscanmode.passivescan = 1; | |
558 | } else { | |
559 | (pscanchanlist + | |
560 | chanidx)->chanscanmode.passivescan = 0; | |
561 | } | |
562 | ||
563 | if (puserscanin->chanlist[chanidx].scantime) { | |
564 | scandur = | |
565 | puserscanin->chanlist[chanidx].scantime; | |
566 | } else { | |
567 | if (scantype == cmd_scan_type_passive) { | |
568 | scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; | |
569 | } else { | |
570 | scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; | |
571 | } | |
572 | } | |
573 | ||
574 | (pscanchanlist + chanidx)->minscantime = | |
575 | cpu_to_le16(scandur); | |
576 | (pscanchanlist + chanidx)->maxscantime = | |
577 | cpu_to_le16(scandur); | |
578 | } | |
579 | ||
580 | /* Check if we are only scanning the current channel */ | |
581 | if ((chanidx == 1) && (puserscanin->chanlist[0].channumber | |
582 | == | |
583 | priv->adapter->curbssparams.channel)) { | |
584 | *pscancurrentonly = 1; | |
585 | lbs_pr_debug(1, "Scan: Scanning current channel only"); | |
586 | } | |
587 | ||
588 | } else { | |
589 | lbs_pr_debug(1, "Scan: Creating full region channel list\n"); | |
590 | wlan_scan_create_channel_list(priv, pscanchanlist, | |
591 | *pfilteredscan); | |
592 | } | |
593 | ||
594 | out: | |
595 | return pscancfgout; | |
596 | } | |
597 | ||
598 | /** | |
599 | * @brief Construct and send multiple scan config commands to the firmware | |
600 | * | |
601 | * Previous routines have created a wlan_scan_cmd_config with any requested | |
602 | * TLVs. This function splits the channel TLV into maxchanperscan lists | |
603 | * and sends the portion of the channel TLV along with the other TLVs | |
604 | * to the wlan_cmd routines for execution in the firmware. | |
605 | * | |
606 | * @param priv A pointer to wlan_private structure | |
607 | * @param maxchanperscan Maximum number channels to be included in each | |
608 | * scan command sent to firmware | |
609 | * @param filteredscan Flag indicating whether or not a BSSID or SSID | |
610 | * filter is being used for the firmware command | |
611 | * scan command sent to firmware | |
612 | * @param pscancfgout Scan configuration used for this scan. | |
613 | * @param pchantlvout Pointer in the pscancfgout where the channel TLV | |
614 | * should start. This is past any other TLVs that | |
615 | * must be sent down in each firmware command. | |
616 | * @param pscanchanlist List of channels to scan in maxchanperscan segments | |
617 | * | |
618 | * @return 0 or error return otherwise | |
619 | */ | |
620 | static int wlan_scan_channel_list(wlan_private * priv, | |
621 | int maxchanperscan, | |
622 | u8 filteredscan, | |
623 | struct wlan_scan_cmd_config * pscancfgout, | |
624 | struct mrvlietypes_chanlistparamset * pchantlvout, | |
625 | struct chanscanparamset * pscanchanlist) | |
626 | { | |
627 | struct chanscanparamset *ptmpchan; | |
628 | struct chanscanparamset *pstartchan; | |
629 | u8 scanband; | |
630 | int doneearly; | |
631 | int tlvidx; | |
632 | int ret = 0; | |
633 | ||
634 | ENTER(); | |
635 | ||
636 | if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { | |
637 | lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", | |
638 | pscancfgout, pchantlvout, pscanchanlist); | |
639 | return -1; | |
640 | } | |
641 | ||
642 | pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | |
643 | ||
644 | /* Set the temp channel struct pointer to the start of the desired list */ | |
645 | ptmpchan = pscanchanlist; | |
646 | ||
647 | /* Loop through the desired channel list, sending a new firmware scan | |
648 | * commands for each maxchanperscan channels (or for 1,6,11 individually | |
649 | * if configured accordingly) | |
650 | */ | |
651 | while (ptmpchan->channumber) { | |
652 | ||
653 | tlvidx = 0; | |
654 | pchantlvout->header.len = 0; | |
655 | scanband = ptmpchan->radiotype; | |
656 | pstartchan = ptmpchan; | |
657 | doneearly = 0; | |
658 | ||
659 | /* Construct the channel TLV for the scan command. Continue to | |
660 | * insert channel TLVs until: | |
661 | * - the tlvidx hits the maximum configured per scan command | |
662 | * - the next channel to insert is 0 (end of desired channel list) | |
663 | * - doneearly is set (controlling individual scanning of 1,6,11) | |
664 | */ | |
665 | while (tlvidx < maxchanperscan && ptmpchan->channumber | |
666 | && !doneearly) { | |
667 | ||
668 | lbs_pr_debug(1, | |
669 | "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", | |
670 | ptmpchan->channumber, ptmpchan->radiotype, | |
671 | ptmpchan->chanscanmode.passivescan, | |
672 | ptmpchan->chanscanmode.disablechanfilt, | |
673 | ptmpchan->maxscantime); | |
674 | ||
675 | /* Copy the current channel TLV to the command being prepared */ | |
676 | memcpy(pchantlvout->chanscanparam + tlvidx, | |
677 | ptmpchan, sizeof(pchantlvout->chanscanparam)); | |
678 | ||
679 | /* Increment the TLV header length by the size appended */ | |
680 | pchantlvout->header.len += | |
681 | sizeof(pchantlvout->chanscanparam); | |
682 | ||
683 | /* | |
684 | * The tlv buffer length is set to the number of bytes of the | |
685 | * between the channel tlv pointer and the start of the | |
686 | * tlv buffer. This compensates for any TLVs that were appended | |
687 | * before the channel list. | |
688 | */ | |
689 | pscancfgout->tlvbufferlen = ((u8 *) pchantlvout | |
690 | - pscancfgout->tlvbuffer); | |
691 | ||
692 | /* Add the size of the channel tlv header and the data length */ | |
693 | pscancfgout->tlvbufferlen += | |
694 | (sizeof(pchantlvout->header) | |
695 | + pchantlvout->header.len); | |
696 | ||
697 | /* Increment the index to the channel tlv we are constructing */ | |
698 | tlvidx++; | |
699 | ||
700 | doneearly = 0; | |
701 | ||
702 | /* Stop the loop if the *current* channel is in the 1,6,11 set | |
703 | * and we are not filtering on a BSSID or SSID. | |
704 | */ | |
705 | if (!filteredscan && (ptmpchan->channumber == 1 | |
706 | || ptmpchan->channumber == 6 | |
707 | || ptmpchan->channumber == 11)) { | |
708 | doneearly = 1; | |
709 | } | |
710 | ||
711 | /* Increment the tmp pointer to the next channel to be scanned */ | |
712 | ptmpchan++; | |
713 | ||
714 | /* Stop the loop if the *next* channel is in the 1,6,11 set. | |
715 | * This will cause it to be the only channel scanned on the next | |
716 | * interation | |
717 | */ | |
718 | if (!filteredscan && (ptmpchan->channumber == 1 | |
719 | || ptmpchan->channumber == 6 | |
720 | || ptmpchan->channumber == 11)) { | |
721 | doneearly = 1; | |
722 | } | |
723 | } | |
724 | ||
725 | /* Send the scan command to the firmware with the specified cfg */ | |
726 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, | |
727 | 0, 0, pscancfgout); | |
728 | } | |
729 | ||
730 | LEAVE(); | |
731 | return ret; | |
732 | } | |
733 | ||
734 | /** | |
735 | * @brief Internal function used to start a scan based on an input config | |
736 | * | |
737 | * Use the input user scan configuration information when provided in | |
738 | * order to send the appropriate scan commands to firmware to populate or | |
739 | * update the internal driver scan table | |
740 | * | |
741 | * @param priv A pointer to wlan_private structure | |
742 | * @param puserscanin Pointer to the input configuration for the requested | |
743 | * scan. | |
744 | * | |
745 | * @return 0 or < 0 if error | |
746 | */ | |
747 | int wlan_scan_networks(wlan_private * priv, | |
748 | const struct wlan_ioctl_user_scan_cfg * puserscanin) | |
749 | { | |
750 | wlan_adapter *adapter = priv->adapter; | |
751 | struct mrvlietypes_chanlistparamset *pchantlvout; | |
752 | struct chanscanparamset * scan_chan_list = NULL; | |
753 | struct wlan_scan_cmd_config * scan_cfg = NULL; | |
754 | u8 keeppreviousscan; | |
755 | u8 filteredscan; | |
756 | u8 scancurrentchanonly; | |
757 | int maxchanperscan; | |
758 | int ret; | |
759 | ||
760 | ENTER(); | |
761 | ||
762 | scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * | |
763 | WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); | |
764 | if (scan_chan_list == NULL) { | |
765 | ret = -ENOMEM; | |
766 | goto out; | |
767 | } | |
768 | ||
769 | scan_cfg = wlan_scan_setup_scan_config(priv, | |
770 | puserscanin, | |
771 | &pchantlvout, | |
772 | scan_chan_list, | |
773 | &maxchanperscan, | |
774 | &filteredscan, | |
775 | &scancurrentchanonly); | |
776 | if (scan_cfg == NULL) { | |
777 | ret = -ENOMEM; | |
778 | goto out; | |
779 | } | |
780 | ||
781 | keeppreviousscan = 0; | |
782 | ||
783 | if (puserscanin) { | |
784 | keeppreviousscan = puserscanin->keeppreviousscan; | |
785 | } | |
786 | ||
787 | if (!keeppreviousscan) { | |
788 | memset(adapter->scantable, 0x00, | |
789 | sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); | |
790 | adapter->numinscantable = 0; | |
791 | } | |
792 | ||
793 | /* Keep the data path active if we are only scanning our current channel */ | |
794 | if (!scancurrentchanonly) { | |
795 | netif_stop_queue(priv->wlan_dev.netdev); | |
796 | netif_carrier_off(priv->wlan_dev.netdev); | |
797 | } | |
798 | ||
799 | ret = wlan_scan_channel_list(priv, | |
800 | maxchanperscan, | |
801 | filteredscan, | |
802 | scan_cfg, | |
803 | pchantlvout, | |
804 | scan_chan_list); | |
805 | ||
806 | /* Process the resulting scan table: | |
807 | * - Remove any bad ssids | |
808 | * - Update our current BSS information from scan data | |
809 | */ | |
810 | wlan_scan_process_results(priv); | |
811 | ||
812 | if (priv->adapter->connect_status == libertas_connected) { | |
813 | netif_carrier_on(priv->wlan_dev.netdev); | |
814 | netif_wake_queue(priv->wlan_dev.netdev); | |
815 | } | |
816 | ||
817 | out: | |
818 | if (scan_cfg) | |
819 | kfree(scan_cfg); | |
820 | ||
821 | if (scan_chan_list) | |
822 | kfree(scan_chan_list); | |
823 | ||
824 | LEAVE(); | |
825 | return ret; | |
826 | } | |
827 | ||
828 | /** | |
829 | * @brief Inspect the scan response buffer for pointers to expected TLVs | |
830 | * | |
831 | * TLVs can be included at the end of the scan response BSS information. | |
832 | * Parse the data in the buffer for pointers to TLVs that can potentially | |
833 | * be passed back in the response | |
834 | * | |
835 | * @param ptlv Pointer to the start of the TLV buffer to parse | |
836 | * @param tlvbufsize size of the TLV buffer | |
837 | * @param ptsftlv Output parameter: Pointer to the TSF TLV if found | |
838 | * | |
839 | * @return void | |
840 | */ | |
841 | static | |
842 | void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, | |
843 | int tlvbufsize, | |
844 | struct mrvlietypes_tsftimestamp ** ptsftlv) | |
845 | { | |
846 | struct mrvlietypes_data *pcurrenttlv; | |
847 | int tlvbufleft; | |
848 | u16 tlvtype; | |
849 | u16 tlvlen; | |
850 | ||
851 | pcurrenttlv = ptlv; | |
852 | tlvbufleft = tlvbufsize; | |
853 | *ptsftlv = NULL; | |
854 | ||
855 | lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); | |
856 | lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); | |
857 | ||
858 | while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { | |
859 | tlvtype = le16_to_cpu(pcurrenttlv->header.type); | |
860 | tlvlen = le16_to_cpu(pcurrenttlv->header.len); | |
861 | ||
862 | switch (tlvtype) { | |
863 | case TLV_TYPE_TSFTIMESTAMP: | |
864 | *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; | |
865 | break; | |
866 | ||
867 | default: | |
868 | lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", | |
869 | tlvtype); | |
870 | /* Give up, this seems corrupted */ | |
871 | return; | |
872 | } /* switch */ | |
873 | ||
874 | tlvbufleft -= (sizeof(ptlv->header) + tlvlen); | |
875 | pcurrenttlv = | |
876 | (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); | |
877 | } /* while */ | |
878 | } | |
879 | ||
880 | /** | |
881 | * @brief Interpret a BSS scan response returned from the firmware | |
882 | * | |
883 | * Parse the various fixed fields and IEs passed back for a a BSS probe | |
884 | * response or beacon from the scan command. Record information as needed | |
885 | * in the scan table struct bss_descriptor for that entry. | |
886 | * | |
887 | * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry | |
888 | * | |
889 | * @return 0 or -1 | |
890 | */ | |
891 | static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, | |
892 | u8 ** pbeaconinfo, int *bytesleft) | |
893 | { | |
894 | enum ieeetypes_elementid elemID; | |
895 | struct ieeetypes_fhparamset *pFH; | |
896 | struct ieeetypes_dsparamset *pDS; | |
897 | struct ieeetypes_cfparamset *pCF; | |
898 | struct ieeetypes_ibssparamset *pibss; | |
899 | struct ieeetypes_capinfo *pcap; | |
900 | struct WLAN_802_11_FIXED_IEs fixedie; | |
901 | u8 *pcurrentptr; | |
902 | u8 *pRate; | |
903 | u8 elemlen; | |
904 | u8 bytestocopy; | |
905 | u8 ratesize; | |
906 | u16 beaconsize; | |
907 | u8 founddatarateie; | |
908 | int bytesleftforcurrentbeacon; | |
909 | ||
876c9d3a MT |
910 | struct IE_WPA *pIe; |
911 | const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; | |
912 | ||
913 | struct ieeetypes_countryinfoset *pcountryinfo; | |
914 | ||
915 | ENTER(); | |
916 | ||
917 | founddatarateie = 0; | |
918 | ratesize = 0; | |
919 | beaconsize = 0; | |
920 | ||
921 | if (*bytesleft >= sizeof(beaconsize)) { | |
922 | /* Extract & convert beacon size from the command buffer */ | |
923 | memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); | |
924 | beaconsize = le16_to_cpu(beaconsize); | |
925 | *bytesleft -= sizeof(beaconsize); | |
926 | *pbeaconinfo += sizeof(beaconsize); | |
927 | } | |
928 | ||
929 | if (beaconsize == 0 || beaconsize > *bytesleft) { | |
930 | ||
931 | *pbeaconinfo += *bytesleft; | |
932 | *bytesleft = 0; | |
933 | ||
934 | return -1; | |
935 | } | |
936 | ||
937 | /* Initialize the current working beacon pointer for this BSS iteration */ | |
938 | pcurrentptr = *pbeaconinfo; | |
939 | ||
940 | /* Advance the return beacon pointer past the current beacon */ | |
941 | *pbeaconinfo += beaconsize; | |
942 | *bytesleft -= beaconsize; | |
943 | ||
944 | bytesleftforcurrentbeacon = beaconsize; | |
945 | ||
876c9d3a MT |
946 | memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); |
947 | lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", | |
948 | pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], | |
949 | pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], | |
950 | pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); | |
951 | ||
952 | pcurrentptr += ETH_ALEN; | |
953 | bytesleftforcurrentbeacon -= ETH_ALEN; | |
954 | ||
955 | if (bytesleftforcurrentbeacon < 12) { | |
956 | lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); | |
957 | return -1; | |
958 | } | |
959 | ||
960 | /* | |
961 | * next 4 fields are RSSI, time stamp, beacon interval, | |
962 | * and capability information | |
963 | */ | |
964 | ||
965 | /* RSSI is 1 byte long */ | |
966 | pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); | |
967 | lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); | |
968 | pcurrentptr += 1; | |
969 | bytesleftforcurrentbeacon -= 1; | |
970 | ||
971 | /* time stamp is 8 bytes long */ | |
972 | memcpy(fixedie.timestamp, pcurrentptr, 8); | |
973 | memcpy(pBSSEntry->timestamp, pcurrentptr, 8); | |
974 | pcurrentptr += 8; | |
975 | bytesleftforcurrentbeacon -= 8; | |
976 | ||
977 | /* beacon interval is 2 bytes long */ | |
978 | memcpy(&fixedie.beaconinterval, pcurrentptr, 2); | |
979 | pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); | |
980 | pcurrentptr += 2; | |
981 | bytesleftforcurrentbeacon -= 2; | |
982 | ||
983 | /* capability information is 2 bytes long */ | |
984 | memcpy(&fixedie.capabilities, pcurrentptr, 2); | |
985 | lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", | |
986 | fixedie.capabilities); | |
987 | fixedie.capabilities = le16_to_cpu(fixedie.capabilities); | |
988 | pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; | |
989 | memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); | |
990 | pcurrentptr += 2; | |
991 | bytesleftforcurrentbeacon -= 2; | |
992 | ||
993 | /* rest of the current buffer are IE's */ | |
994 | lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", | |
995 | bytesleftforcurrentbeacon); | |
996 | ||
997 | lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, | |
998 | bytesleftforcurrentbeacon); | |
999 | ||
1000 | if (pcap->privacy) { | |
1001 | lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); | |
1002 | pBSSEntry->privacy = wlan802_11privfilter8021xWEP; | |
1003 | } else { | |
1004 | pBSSEntry->privacy = wlan802_11privfilteracceptall; | |
1005 | } | |
1006 | ||
1007 | if (pcap->ibss == 1) { | |
1008 | pBSSEntry->inframode = wlan802_11ibss; | |
1009 | } else { | |
1010 | pBSSEntry->inframode = wlan802_11infrastructure; | |
1011 | } | |
1012 | ||
1013 | /* process variable IE */ | |
1014 | while (bytesleftforcurrentbeacon >= 2) { | |
1015 | elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); | |
1016 | elemlen = *((u8 *) pcurrentptr + 1); | |
1017 | ||
1018 | if (bytesleftforcurrentbeacon < elemlen) { | |
1019 | lbs_pr_debug(1, "InterpretIE: error in processing IE, " | |
1020 | "bytes left < IE length\n"); | |
1021 | bytesleftforcurrentbeacon = 0; | |
1022 | continue; | |
1023 | } | |
1024 | ||
1025 | switch (elemID) { | |
1026 | ||
1027 | case SSID: | |
1028 | pBSSEntry->ssid.ssidlength = elemlen; | |
1029 | memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), | |
1030 | elemlen); | |
1031 | lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); | |
1032 | break; | |
1033 | ||
1034 | case SUPPORTED_RATES: | |
1035 | memcpy(pBSSEntry->datarates, (pcurrentptr + 2), | |
1036 | elemlen); | |
1037 | memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), | |
1038 | elemlen); | |
1039 | ratesize = elemlen; | |
1040 | founddatarateie = 1; | |
1041 | break; | |
1042 | ||
1043 | case EXTRA_IE: | |
1044 | lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); | |
1045 | pBSSEntry->extra_ie = 1; | |
1046 | break; | |
1047 | ||
1048 | case FH_PARAM_SET: | |
1049 | pFH = (struct ieeetypes_fhparamset *) pcurrentptr; | |
1050 | memmove(&pBSSEntry->phyparamset.fhparamset, pFH, | |
1051 | sizeof(struct ieeetypes_fhparamset)); | |
1052 | pBSSEntry->phyparamset.fhparamset.dwelltime | |
1053 | = | |
1054 | le16_to_cpu(pBSSEntry->phyparamset.fhparamset. | |
1055 | dwelltime); | |
1056 | break; | |
1057 | ||
1058 | case DS_PARAM_SET: | |
1059 | pDS = (struct ieeetypes_dsparamset *) pcurrentptr; | |
1060 | ||
1061 | pBSSEntry->channel = pDS->currentchan; | |
1062 | ||
1063 | memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, | |
1064 | sizeof(struct ieeetypes_dsparamset)); | |
1065 | break; | |
1066 | ||
1067 | case CF_PARAM_SET: | |
1068 | pCF = (struct ieeetypes_cfparamset *) pcurrentptr; | |
1069 | ||
1070 | memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, | |
1071 | sizeof(struct ieeetypes_cfparamset)); | |
1072 | break; | |
1073 | ||
1074 | case IBSS_PARAM_SET: | |
1075 | pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; | |
1076 | pBSSEntry->atimwindow = | |
1077 | le32_to_cpu(pibss->atimwindow); | |
1078 | ||
1079 | memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, | |
1080 | sizeof(struct ieeetypes_ibssparamset)); | |
1081 | ||
1082 | pBSSEntry->ssparamset.ibssparamset.atimwindow | |
1083 | = | |
1084 | le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. | |
1085 | atimwindow); | |
1086 | break; | |
1087 | ||
1088 | /* Handle Country Info IE */ | |
1089 | case COUNTRY_INFO: | |
1090 | pcountryinfo = | |
1091 | (struct ieeetypes_countryinfoset *) pcurrentptr; | |
1092 | ||
1093 | if (pcountryinfo->len < | |
1094 | sizeof(pcountryinfo->countrycode) | |
1095 | || pcountryinfo->len > 254) { | |
1096 | lbs_pr_debug(1, "InterpretIE: 11D- Err " | |
1097 | "CountryInfo len =%d min=%d max=254\n", | |
1098 | pcountryinfo->len, | |
1099 | sizeof(pcountryinfo->countrycode)); | |
1100 | LEAVE(); | |
1101 | return -1; | |
1102 | } | |
1103 | ||
1104 | memcpy(&pBSSEntry->countryinfo, | |
1105 | pcountryinfo, pcountryinfo->len + 2); | |
1106 | lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", | |
1107 | (u8 *) pcountryinfo, | |
1108 | (u32) (pcountryinfo->len + 2)); | |
1109 | break; | |
1110 | ||
1111 | case EXTENDED_SUPPORTED_RATES: | |
1112 | /* | |
1113 | * only process extended supported rate | |
1114 | * if data rate is already found. | |
1115 | * data rate IE should come before | |
1116 | * extended supported rate IE | |
1117 | */ | |
1118 | if (founddatarateie) { | |
1119 | if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { | |
1120 | bytestocopy = | |
1121 | (WLAN_SUPPORTED_RATES - ratesize); | |
1122 | } else { | |
1123 | bytestocopy = elemlen; | |
1124 | } | |
1125 | ||
1126 | pRate = (u8 *) pBSSEntry->datarates; | |
1127 | pRate += ratesize; | |
1128 | memmove(pRate, (pcurrentptr + 2), bytestocopy); | |
1129 | ||
1130 | pRate = (u8 *) pBSSEntry->libertas_supported_rates; | |
1131 | ||
1132 | pRate += ratesize; | |
1133 | memmove(pRate, (pcurrentptr + 2), bytestocopy); | |
1134 | } | |
1135 | break; | |
1136 | ||
1137 | case VENDOR_SPECIFIC_221: | |
1138 | #define IE_ID_LEN_FIELDS_BYTES 2 | |
1139 | pIe = (struct IE_WPA *)pcurrentptr; | |
1140 | ||
51b0c9d0 DW |
1141 | if (memcmp(pIe->oui, oui01, sizeof(oui01))) |
1142 | break; | |
1143 | ||
1144 | pBSSEntry->wpa_ie_len = min_t(size_t, | |
1145 | elemlen + IE_ID_LEN_FIELDS_BYTES, | |
1146 | sizeof(pBSSEntry->wpa_ie)); | |
1147 | memcpy(pBSSEntry->wpa_ie, pcurrentptr, | |
1148 | pBSSEntry->wpa_ie_len); | |
1149 | lbs_dbg_hex("InterpretIE: Resp WPA_IE", | |
1150 | pBSSEntry->wpa_ie, elemlen); | |
876c9d3a MT |
1151 | break; |
1152 | case WPA2_IE: | |
1153 | pIe = (struct IE_WPA *)pcurrentptr; | |
876c9d3a | 1154 | |
51b0c9d0 DW |
1155 | pBSSEntry->rsn_ie_len = min_t(size_t, |
1156 | elemlen + IE_ID_LEN_FIELDS_BYTES, | |
1157 | sizeof(pBSSEntry->rsn_ie)); | |
1158 | memcpy(pBSSEntry->rsn_ie, pcurrentptr, | |
1159 | pBSSEntry->rsn_ie_len); | |
876c9d3a | 1160 | lbs_dbg_hex("InterpretIE: Resp WPA2_IE", |
51b0c9d0 | 1161 | pBSSEntry->rsn_ie, elemlen); |
876c9d3a MT |
1162 | break; |
1163 | case TIM: | |
1164 | break; | |
1165 | ||
1166 | case CHALLENGE_TEXT: | |
1167 | break; | |
1168 | } | |
1169 | ||
1170 | pcurrentptr += elemlen + 2; | |
1171 | ||
1172 | /* need to account for IE ID and IE len */ | |
1173 | bytesleftforcurrentbeacon -= (elemlen + 2); | |
1174 | ||
1175 | } /* while (bytesleftforcurrentbeacon > 2) */ | |
1176 | ||
1177 | return 0; | |
1178 | } | |
1179 | ||
1180 | /** | |
1181 | * @brief Compare two SSIDs | |
1182 | * | |
1183 | * @param ssid1 A pointer to ssid to compare | |
1184 | * @param ssid2 A pointer to ssid to compare | |
1185 | * | |
1186 | * @return 0--ssid is same, otherwise is different | |
1187 | */ | |
1188 | int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) | |
1189 | { | |
1190 | if (!ssid1 || !ssid2) | |
1191 | return -1; | |
1192 | ||
1193 | if (ssid1->ssidlength != ssid2->ssidlength) | |
1194 | return -1; | |
1195 | ||
1196 | return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); | |
1197 | } | |
1198 | ||
1199 | /** | |
1200 | * @brief This function finds a specific compatible BSSID in the scan list | |
1201 | * | |
1202 | * @param adapter A pointer to wlan_adapter | |
1203 | * @param bssid BSSID to find in the scan list | |
1204 | * @param mode Network mode: Infrastructure or IBSS | |
1205 | * | |
1206 | * @return index in BSSID list, or error return code (< 0) | |
1207 | */ | |
1208 | int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) | |
1209 | { | |
1210 | int ret = -ENETUNREACH; | |
1211 | int i; | |
1212 | ||
1213 | if (!bssid) | |
1214 | return -EFAULT; | |
1215 | ||
1216 | lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", | |
1217 | adapter->numinscantable); | |
1218 | ||
1219 | /* Look through the scan table for a compatible match. The ret return | |
1220 | * variable will be equal to the index in the scan table (greater | |
1221 | * than zero) if the network is compatible. The loop will continue | |
1222 | * past a matched bssid that is not compatible in case there is an | |
1223 | * AP with multiple SSIDs assigned to the same BSSID | |
1224 | */ | |
1225 | for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { | |
1226 | if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { | |
1227 | switch (mode) { | |
1228 | case wlan802_11infrastructure: | |
1229 | case wlan802_11ibss: | |
1230 | ret = is_network_compatible(adapter, i, mode); | |
1231 | break; | |
1232 | default: | |
1233 | ret = i; | |
1234 | break; | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | return ret; | |
1240 | } | |
1241 | ||
1242 | /** | |
1243 | * @brief This function finds ssid in ssid list. | |
1244 | * | |
1245 | * @param adapter A pointer to wlan_adapter | |
1246 | * @param ssid SSID to find in the list | |
1247 | * @param bssid BSSID to qualify the SSID selection (if provided) | |
1248 | * @param mode Network mode: Infrastructure or IBSS | |
1249 | * | |
1250 | * @return index in BSSID list | |
1251 | */ | |
1252 | int libertas_find_SSID_in_list(wlan_adapter * adapter, | |
1253 | struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) | |
1254 | { | |
1255 | int net = -ENETUNREACH; | |
1256 | u8 bestrssi = 0; | |
1257 | int i; | |
1258 | int j; | |
1259 | ||
1260 | lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); | |
1261 | ||
1262 | for (i = 0; i < adapter->numinscantable; i++) { | |
1263 | if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && | |
1264 | (!bssid || | |
1265 | !memcmp(adapter->scantable[i]. | |
1266 | macaddress, bssid, ETH_ALEN))) { | |
1267 | switch (mode) { | |
1268 | case wlan802_11infrastructure: | |
1269 | case wlan802_11ibss: | |
1270 | j = is_network_compatible(adapter, i, mode); | |
1271 | ||
1272 | if (j >= 0) { | |
1273 | if (bssid) { | |
1274 | return i; | |
1275 | } | |
1276 | ||
1277 | if (SCAN_RSSI | |
1278 | (adapter->scantable[i].rssi) | |
1279 | > bestrssi) { | |
1280 | bestrssi = | |
1281 | SCAN_RSSI(adapter-> | |
1282 | scantable[i]. | |
1283 | rssi); | |
1284 | net = i; | |
1285 | } | |
1286 | } else { | |
1287 | if (net == -ENETUNREACH) { | |
1288 | net = j; | |
1289 | } | |
1290 | } | |
1291 | break; | |
1292 | case wlan802_11autounknown: | |
1293 | default: | |
1294 | if (SCAN_RSSI(adapter->scantable[i].rssi) | |
1295 | > bestrssi) { | |
1296 | bestrssi = | |
1297 | SCAN_RSSI(adapter->scantable[i]. | |
1298 | rssi); | |
1299 | net = i; | |
1300 | } | |
1301 | break; | |
1302 | } | |
1303 | } | |
1304 | } | |
1305 | ||
1306 | return net; | |
1307 | } | |
1308 | ||
1309 | /** | |
1310 | * @brief This function finds the best SSID in the Scan List | |
1311 | * | |
1312 | * Search the scan table for the best SSID that also matches the current | |
1313 | * adapter network preference (infrastructure or adhoc) | |
1314 | * | |
1315 | * @param adapter A pointer to wlan_adapter | |
1316 | * | |
1317 | * @return index in BSSID list | |
1318 | */ | |
1319 | int libertas_find_best_SSID_in_list(wlan_adapter * adapter, | |
1320 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) | |
1321 | { | |
1322 | int bestnet = -ENETUNREACH; | |
1323 | u8 bestrssi = 0; | |
1324 | int i; | |
1325 | ||
1326 | ENTER(); | |
1327 | ||
1328 | lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); | |
1329 | ||
1330 | for (i = 0; i < adapter->numinscantable; i++) { | |
1331 | switch (mode) { | |
1332 | case wlan802_11infrastructure: | |
1333 | case wlan802_11ibss: | |
1334 | if (is_network_compatible(adapter, i, mode) >= 0) { | |
1335 | if (SCAN_RSSI(adapter->scantable[i].rssi) > | |
1336 | bestrssi) { | |
1337 | bestrssi = | |
1338 | SCAN_RSSI(adapter->scantable[i]. | |
1339 | rssi); | |
1340 | bestnet = i; | |
1341 | } | |
1342 | } | |
1343 | break; | |
1344 | case wlan802_11autounknown: | |
1345 | default: | |
1346 | if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { | |
1347 | bestrssi = | |
1348 | SCAN_RSSI(adapter->scantable[i].rssi); | |
1349 | bestnet = i; | |
1350 | } | |
1351 | break; | |
1352 | } | |
1353 | } | |
1354 | ||
1355 | LEAVE(); | |
1356 | return bestnet; | |
1357 | } | |
1358 | ||
1359 | /** | |
1360 | * @brief Find the AP with specific ssid in the scan list | |
1361 | * | |
1362 | * @param priv A pointer to wlan_private structure | |
1363 | * @param pSSID A pointer to AP's ssid | |
1364 | * | |
1365 | * @return 0--success, otherwise--fail | |
1366 | */ | |
1367 | int libertas_find_best_network_SSID(wlan_private * priv, | |
1368 | struct WLAN_802_11_SSID *pSSID, | |
1369 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, | |
1370 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) | |
1371 | { | |
1372 | wlan_adapter *adapter = priv->adapter; | |
1373 | int ret = 0; | |
1374 | struct bss_descriptor *preqbssid; | |
1375 | int i; | |
1376 | ||
1377 | ENTER(); | |
1378 | ||
1379 | memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); | |
1380 | ||
1381 | wlan_scan_networks(priv, NULL); | |
1382 | if (adapter->surpriseremoved) | |
1383 | return -1; | |
1384 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); | |
1385 | ||
1386 | i = libertas_find_best_SSID_in_list(adapter, preferred_mode); | |
1387 | if (i < 0) { | |
1388 | ret = -1; | |
1389 | goto out; | |
1390 | } | |
1391 | ||
1392 | preqbssid = &adapter->scantable[i]; | |
1393 | memcpy(pSSID, &preqbssid->ssid, | |
1394 | sizeof(struct WLAN_802_11_SSID)); | |
1395 | *out_mode = preqbssid->inframode; | |
1396 | ||
1397 | if (!pSSID->ssidlength) { | |
1398 | ret = -1; | |
1399 | } | |
1400 | ||
1401 | out: | |
1402 | LEAVE(); | |
1403 | return ret; | |
1404 | } | |
1405 | ||
1406 | /** | |
1407 | * @brief Scan Network | |
1408 | * | |
1409 | * @param dev A pointer to net_device structure | |
1410 | * @param info A pointer to iw_request_info structure | |
1411 | * @param vwrq A pointer to iw_param structure | |
1412 | * @param extra A pointer to extra data buf | |
1413 | * | |
1414 | * @return 0 --success, otherwise fail | |
1415 | */ | |
1416 | int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, | |
1417 | struct iw_param *vwrq, char *extra) | |
1418 | { | |
1419 | wlan_private *priv = dev->priv; | |
1420 | wlan_adapter *adapter = priv->adapter; | |
1421 | union iwreq_data wrqu; | |
1422 | ||
1423 | ENTER(); | |
1424 | ||
1425 | if (!wlan_scan_networks(priv, NULL)) { | |
1426 | memset(&wrqu, 0, sizeof(union iwreq_data)); | |
1427 | wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, | |
1428 | NULL); | |
1429 | } | |
1430 | ||
1431 | if (adapter->surpriseremoved) | |
1432 | return -1; | |
1433 | ||
1434 | LEAVE(); | |
1435 | return 0; | |
1436 | } | |
1437 | ||
1438 | /** | |
1439 | * @brief Send a scan command for all available channels filtered on a spec | |
1440 | * | |
1441 | * @param priv A pointer to wlan_private structure | |
1442 | * @param prequestedssid A pointer to AP's ssid | |
1443 | * @param keeppreviousscan Flag used to save/clear scan table before scan | |
1444 | * | |
1445 | * @return 0-success, otherwise fail | |
1446 | */ | |
1447 | int libertas_send_specific_SSID_scan(wlan_private * priv, | |
1448 | struct WLAN_802_11_SSID *prequestedssid, | |
1449 | u8 keeppreviousscan) | |
1450 | { | |
1451 | wlan_adapter *adapter = priv->adapter; | |
1452 | struct wlan_ioctl_user_scan_cfg scancfg; | |
1453 | ||
1454 | ENTER(); | |
1455 | ||
1456 | if (prequestedssid == NULL) { | |
1457 | return -1; | |
1458 | } | |
1459 | ||
1460 | memset(&scancfg, 0x00, sizeof(scancfg)); | |
1461 | ||
1462 | memcpy(scancfg.specificSSID, prequestedssid->ssid, | |
1463 | prequestedssid->ssidlength); | |
1464 | scancfg.keeppreviousscan = keeppreviousscan; | |
1465 | ||
1466 | wlan_scan_networks(priv, &scancfg); | |
1467 | if (adapter->surpriseremoved) | |
1468 | return -1; | |
1469 | wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); | |
1470 | ||
1471 | LEAVE(); | |
1472 | return 0; | |
1473 | } | |
1474 | ||
1475 | /** | |
1476 | * @brief scan an AP with specific BSSID | |
1477 | * | |
1478 | * @param priv A pointer to wlan_private structure | |
1479 | * @param bssid A pointer to AP's bssid | |
1480 | * @param keeppreviousscan Flag used to save/clear scan table before scan | |
1481 | * | |
1482 | * @return 0-success, otherwise fail | |
1483 | */ | |
1484 | int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) | |
1485 | { | |
1486 | struct wlan_ioctl_user_scan_cfg scancfg; | |
1487 | ||
1488 | ENTER(); | |
1489 | ||
1490 | if (bssid == NULL) { | |
1491 | return -1; | |
1492 | } | |
1493 | ||
1494 | memset(&scancfg, 0x00, sizeof(scancfg)); | |
1495 | memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); | |
1496 | scancfg.keeppreviousscan = keeppreviousscan; | |
1497 | ||
1498 | wlan_scan_networks(priv, &scancfg); | |
1499 | if (priv->adapter->surpriseremoved) | |
1500 | return -1; | |
1501 | wait_event_interruptible(priv->adapter->cmd_pending, | |
1502 | !priv->adapter->nr_cmd_pending); | |
1503 | ||
1504 | LEAVE(); | |
1505 | return 0; | |
1506 | } | |
1507 | ||
1508 | /** | |
1509 | * @brief Retrieve the scan table entries via wireless tools IOCTL call | |
1510 | * | |
1511 | * @param dev A pointer to net_device structure | |
1512 | * @param info A pointer to iw_request_info structure | |
1513 | * @param dwrq A pointer to iw_point structure | |
1514 | * @param extra A pointer to extra data buf | |
1515 | * | |
1516 | * @return 0 --success, otherwise fail | |
1517 | */ | |
1518 | int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, | |
1519 | struct iw_point *dwrq, char *extra) | |
1520 | { | |
1521 | wlan_private *priv = dev->priv; | |
1522 | wlan_adapter *adapter = priv->adapter; | |
1523 | int ret = 0; | |
1524 | char *current_ev = extra; | |
1525 | char *end_buf = extra + IW_SCAN_MAX_DATA; | |
1526 | struct chan_freq_power *cfp; | |
1527 | struct bss_descriptor *pscantable; | |
1528 | char *current_val; /* For rates */ | |
1529 | struct iw_event iwe; /* Temporary buffer */ | |
1530 | int i; | |
1531 | int j; | |
1532 | int rate; | |
1533 | #define PERFECT_RSSI ((u8)50) | |
1534 | #define WORST_RSSI ((u8)0) | |
1535 | #define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) | |
1536 | u8 rssi; | |
1537 | ||
1538 | u8 buf[16 + 256 * 2]; | |
1539 | u8 *ptr; | |
1540 | ||
1541 | ENTER(); | |
1542 | ||
1543 | /* | |
1544 | * if there's either commands in the queue or one being | |
1545 | * processed return -EAGAIN for iwlist to retry later. | |
1546 | */ | |
1547 | if (adapter->nr_cmd_pending) | |
1548 | return -EAGAIN; | |
1549 | ||
1550 | if (adapter->connect_status == libertas_connected) | |
1551 | lbs_pr_debug(1, "Current ssid: %32s\n", | |
1552 | adapter->curbssparams.ssid.ssid); | |
1553 | ||
1554 | lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", | |
1555 | adapter->numinscantable); | |
1556 | ||
1557 | /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. | |
1558 | * The new API using SIOCGIWSCAN is only limited by buffer size | |
1559 | * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes | |
1560 | * which is 4096. | |
1561 | */ | |
1562 | for (i = 0; i < adapter->numinscantable; i++) { | |
1563 | if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { | |
1564 | lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " | |
1565 | "MAX_SCAN_CELL_SIZE=%d\n", | |
1566 | i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); | |
1567 | break; | |
1568 | } | |
1569 | ||
1570 | pscantable = &adapter->scantable[i]; | |
1571 | ||
1572 | lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); | |
1573 | ||
1574 | cfp = | |
1575 | libertas_find_cfp_by_band_and_channel(adapter, 0, | |
1576 | pscantable->channel); | |
1577 | if (!cfp) { | |
1578 | lbs_pr_debug(1, "Invalid channel number %d\n", | |
1579 | pscantable->channel); | |
1580 | continue; | |
1581 | } | |
1582 | ||
1583 | if (!ssid_valid(&adapter->scantable[i].ssid)) { | |
1584 | continue; | |
1585 | } | |
1586 | ||
1587 | /* First entry *MUST* be the AP MAC address */ | |
1588 | iwe.cmd = SIOCGIWAP; | |
1589 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | |
1590 | memcpy(iwe.u.ap_addr.sa_data, | |
1591 | &adapter->scantable[i].macaddress, ETH_ALEN); | |
1592 | ||
1593 | iwe.len = IW_EV_ADDR_LEN; | |
1594 | current_ev = | |
1595 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | |
1596 | ||
1597 | //Add the ESSID | |
1598 | iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; | |
1599 | ||
1600 | if (iwe.u.data.length > 32) { | |
1601 | iwe.u.data.length = 32; | |
1602 | } | |
1603 | ||
1604 | iwe.cmd = SIOCGIWESSID; | |
1605 | iwe.u.data.flags = 1; | |
1606 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | |
1607 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, | |
1608 | adapter->scantable[i].ssid. | |
1609 | ssid); | |
1610 | ||
1611 | //Add mode | |
1612 | iwe.cmd = SIOCGIWMODE; | |
1613 | iwe.u.mode = adapter->scantable[i].inframode + 1; | |
1614 | iwe.len = IW_EV_UINT_LEN; | |
1615 | current_ev = | |
1616 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | |
1617 | ||
1618 | //frequency | |
1619 | iwe.cmd = SIOCGIWFREQ; | |
1620 | iwe.u.freq.m = (long)cfp->freq * 100000; | |
1621 | iwe.u.freq.e = 1; | |
1622 | iwe.len = IW_EV_FREQ_LEN; | |
1623 | current_ev = | |
1624 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | |
1625 | ||
1626 | /* Add quality statistics */ | |
1627 | iwe.cmd = IWEVQUAL; | |
1628 | iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; | |
1629 | iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); | |
1630 | ||
1631 | rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; | |
1632 | iwe.u.qual.qual = | |
1633 | (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * | |
1634 | (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / | |
1635 | (RSSI_DIFF * RSSI_DIFF); | |
1636 | if (iwe.u.qual.qual > 100) | |
1637 | iwe.u.qual.qual = 100; | |
1638 | else if (iwe.u.qual.qual < 1) | |
1639 | iwe.u.qual.qual = 0; | |
1640 | ||
1641 | if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { | |
1642 | iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; | |
1643 | } else { | |
1644 | iwe.u.qual.noise = | |
1645 | CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); | |
1646 | } | |
1647 | if ((adapter->inframode == wlan802_11ibss) && | |
1648 | !libertas_SSID_cmp(&adapter->curbssparams.ssid, | |
1649 | &adapter->scantable[i].ssid) | |
1650 | && adapter->adhoccreate) { | |
1651 | ret = libertas_prepare_and_send_command(priv, | |
1652 | cmd_802_11_rssi, | |
1653 | 0, | |
1654 | cmd_option_waitforrsp, | |
1655 | 0, NULL); | |
1656 | ||
1657 | if (!ret) { | |
1658 | iwe.u.qual.level = | |
1659 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / | |
1660 | AVG_SCALE, | |
1661 | adapter->NF[TYPE_RXPD][TYPE_AVG] / | |
1662 | AVG_SCALE); | |
1663 | } | |
1664 | } | |
1665 | iwe.len = IW_EV_QUAL_LEN; | |
1666 | current_ev = | |
1667 | iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); | |
1668 | ||
1669 | /* Add encryption capability */ | |
1670 | iwe.cmd = SIOCGIWENCODE; | |
1671 | if (adapter->scantable[i].privacy) { | |
1672 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | |
1673 | } else { | |
1674 | iwe.u.data.flags = IW_ENCODE_DISABLED; | |
1675 | } | |
1676 | iwe.u.data.length = 0; | |
1677 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | |
1678 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, | |
1679 | adapter->scantable->ssid. | |
1680 | ssid); | |
1681 | ||
1682 | current_val = current_ev + IW_EV_LCP_LEN; | |
1683 | ||
1684 | iwe.cmd = SIOCGIWRATE; | |
1685 | ||
1686 | iwe.u.bitrate.fixed = 0; | |
1687 | iwe.u.bitrate.disabled = 0; | |
1688 | iwe.u.bitrate.value = 0; | |
1689 | ||
1690 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | |
1691 | for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); | |
1692 | j++) { | |
1693 | if (adapter->scantable[i].libertas_supported_rates[j] == 0) { | |
1694 | break; | |
1695 | } | |
1696 | rate = | |
1697 | (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * | |
1698 | 500000; | |
1699 | if (rate > iwe.u.bitrate.value) { | |
1700 | iwe.u.bitrate.value = rate; | |
1701 | } | |
1702 | ||
1703 | iwe.u.bitrate.value = | |
1704 | (adapter->scantable[i].libertas_supported_rates[j] | |
1705 | & 0x7f) * 500000; | |
1706 | iwe.len = IW_EV_PARAM_LEN; | |
1707 | current_ev = | |
1708 | iwe_stream_add_value(current_ev, current_val, | |
1709 | end_buf, &iwe, iwe.len); | |
1710 | ||
1711 | } | |
1712 | if ((adapter->scantable[i].inframode == wlan802_11ibss) | |
1713 | && !libertas_SSID_cmp(&adapter->curbssparams.ssid, | |
1714 | &adapter->scantable[i].ssid) | |
1715 | && adapter->adhoccreate) { | |
1716 | iwe.u.bitrate.value = 22 * 500000; | |
1717 | } | |
1718 | iwe.len = IW_EV_PARAM_LEN; | |
1719 | current_ev = | |
1720 | iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, | |
1721 | iwe.len); | |
1722 | ||
1723 | /* Add new value to event */ | |
1724 | current_val = current_ev + IW_EV_LCP_LEN; | |
1725 | ||
51b0c9d0 | 1726 | if (adapter->scantable[i].rsn_ie[0] == WPA2_IE) { |
876c9d3a MT |
1727 | memset(&iwe, 0, sizeof(iwe)); |
1728 | memset(buf, 0, sizeof(buf)); | |
51b0c9d0 DW |
1729 | memcpy(buf, adapter->scantable[i].rsn_ie, |
1730 | adapter->scantable[i].rsn_ie_len); | |
876c9d3a | 1731 | iwe.cmd = IWEVGENIE; |
51b0c9d0 | 1732 | iwe.u.data.length = adapter->scantable[i].rsn_ie_len; |
876c9d3a MT |
1733 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; |
1734 | current_ev = iwe_stream_add_point(current_ev, end_buf, | |
1735 | &iwe, buf); | |
1736 | } | |
51b0c9d0 | 1737 | if (adapter->scantable[i].wpa_ie[0] == WPA_IE) { |
876c9d3a MT |
1738 | memset(&iwe, 0, sizeof(iwe)); |
1739 | memset(buf, 0, sizeof(buf)); | |
51b0c9d0 DW |
1740 | memcpy(buf, adapter->scantable[i].wpa_ie, |
1741 | adapter->scantable[i].wpa_ie_len); | |
876c9d3a | 1742 | iwe.cmd = IWEVGENIE; |
51b0c9d0 | 1743 | iwe.u.data.length = adapter->scantable[i].wpa_ie_len; |
876c9d3a MT |
1744 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; |
1745 | current_ev = iwe_stream_add_point(current_ev, end_buf, | |
1746 | &iwe, buf); | |
1747 | } | |
1748 | ||
1749 | ||
1750 | if (adapter->scantable[i].extra_ie != 0) { | |
1751 | memset(&iwe, 0, sizeof(iwe)); | |
1752 | memset(buf, 0, sizeof(buf)); | |
1753 | ptr = buf; | |
1754 | ptr += sprintf(ptr, "extra_ie"); | |
1755 | iwe.u.data.length = strlen(buf); | |
1756 | ||
1757 | lbs_pr_debug(1, "iwe.u.data.length %d\n", | |
1758 | iwe.u.data.length); | |
1759 | lbs_pr_debug(1, "BUF: %s \n", buf); | |
1760 | ||
1761 | iwe.cmd = IWEVCUSTOM; | |
1762 | iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; | |
1763 | current_ev = | |
1764 | iwe_stream_add_point(current_ev, end_buf, &iwe, | |
1765 | buf); | |
1766 | } | |
1767 | ||
1768 | current_val = current_ev + IW_EV_LCP_LEN; | |
1769 | ||
1770 | /* | |
1771 | * Check if we added any event | |
1772 | */ | |
1773 | if ((current_val - current_ev) > IW_EV_LCP_LEN) | |
1774 | current_ev = current_val; | |
1775 | } | |
1776 | ||
1777 | dwrq->length = (current_ev - extra); | |
1778 | dwrq->flags = 0; | |
1779 | ||
1780 | LEAVE(); | |
1781 | return 0; | |
1782 | } | |
1783 | ||
1784 | /** | |
1785 | * @brief Prepare a scan command to be sent to the firmware | |
1786 | * | |
1787 | * Use the wlan_scan_cmd_config sent to the command processing module in | |
1788 | * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command | |
1789 | * struct to send to firmware. | |
1790 | * | |
1791 | * The fixed fields specifying the BSS type and BSSID filters as well as a | |
1792 | * variable number/length of TLVs are sent in the command to firmware. | |
1793 | * | |
1794 | * @param priv A pointer to wlan_private structure | |
1795 | * @param cmd A pointer to cmd_ds_command structure to be sent to | |
1796 | * firmware with the cmd_DS_801_11_SCAN structure | |
1797 | * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used | |
1798 | * to set the fields/TLVs for the command sent to firmware | |
1799 | * | |
1800 | * @return 0 or -1 | |
1801 | * | |
1802 | * @sa wlan_scan_create_channel_list | |
1803 | */ | |
1804 | int libertas_cmd_80211_scan(wlan_private * priv, | |
1805 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1806 | { | |
1807 | struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; | |
1808 | struct wlan_scan_cmd_config *pscancfg; | |
1809 | ||
1810 | ENTER(); | |
1811 | ||
1812 | pscancfg = pdata_buf; | |
1813 | ||
1814 | /* Set fixed field variables in scan command */ | |
1815 | pscan->bsstype = pscancfg->bsstype; | |
1816 | memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); | |
1817 | memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); | |
1818 | ||
1819 | cmd->command = cpu_to_le16(cmd_802_11_scan); | |
1820 | ||
1821 | /* size is equal to the sizeof(fixed portions) + the TLV len + header */ | |
1822 | cmd->size = cpu_to_le16(sizeof(pscan->bsstype) | |
1823 | + sizeof(pscan->BSSID) | |
1824 | + pscancfg->tlvbufferlen + S_DS_GEN); | |
1825 | ||
1826 | lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", | |
1827 | cmd->command, cmd->size, cmd->seqnum); | |
1828 | LEAVE(); | |
1829 | return 0; | |
1830 | } | |
1831 | ||
1832 | /** | |
1833 | * @brief This function handles the command response of scan | |
1834 | * | |
1835 | * The response buffer for the scan command has the following | |
1836 | * memory layout: | |
1837 | * | |
1838 | * .-----------------------------------------------------------. | |
1839 | * | header (4 * sizeof(u16)): Standard command response hdr | | |
1840 | * .-----------------------------------------------------------. | |
1841 | * | bufsize (u16) : sizeof the BSS Description data | | |
1842 | * .-----------------------------------------------------------. | |
1843 | * | NumOfSet (u8) : Number of BSS Descs returned | | |
1844 | * .-----------------------------------------------------------. | |
1845 | * | BSSDescription data (variable, size given in bufsize) | | |
1846 | * .-----------------------------------------------------------. | |
1847 | * | TLV data (variable, size calculated using header->size, | | |
1848 | * | bufsize and sizeof the fixed fields above) | | |
1849 | * .-----------------------------------------------------------. | |
1850 | * | |
1851 | * @param priv A pointer to wlan_private structure | |
1852 | * @param resp A pointer to cmd_ds_command | |
1853 | * | |
1854 | * @return 0 or -1 | |
1855 | */ | |
1856 | int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) | |
1857 | { | |
1858 | wlan_adapter *adapter = priv->adapter; | |
1859 | struct cmd_ds_802_11_scan_rsp *pscan; | |
1860 | struct bss_descriptor newbssentry; | |
1861 | struct mrvlietypes_data *ptlv; | |
1862 | struct mrvlietypes_tsftimestamp *ptsftlv; | |
1863 | u8 *pbssinfo; | |
1864 | u16 scanrespsize; | |
1865 | int bytesleft; | |
1866 | int numintable; | |
1867 | int bssIdx; | |
1868 | int idx; | |
1869 | int tlvbufsize; | |
1870 | u64 tsfval; | |
1871 | ||
1872 | ENTER(); | |
1873 | ||
1874 | pscan = &resp->params.scanresp; | |
1875 | ||
1876 | if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { | |
1877 | lbs_pr_debug(1, | |
1878 | "SCAN_RESP: Invalid number of AP returned (%d)!!\n", | |
1879 | pscan->nr_sets); | |
1880 | LEAVE(); | |
1881 | return -1; | |
1882 | } | |
1883 | ||
1884 | bytesleft = le16_to_cpu(pscan->bssdescriptsize); | |
1885 | lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); | |
1886 | ||
1887 | scanrespsize = le16_to_cpu(resp->size); | |
1888 | lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", | |
1889 | pscan->nr_sets); | |
1890 | ||
1891 | numintable = adapter->numinscantable; | |
1892 | pbssinfo = pscan->bssdesc_and_tlvbuffer; | |
1893 | ||
1894 | /* The size of the TLV buffer is equal to the entire command response | |
1895 | * size (scanrespsize) minus the fixed fields (sizeof()'s), the | |
1896 | * BSS Descriptions (bssdescriptsize as bytesLef) and the command | |
1897 | * response header (S_DS_GEN) | |
1898 | */ | |
1899 | tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) | |
1900 | + sizeof(pscan->nr_sets) | |
1901 | + S_DS_GEN); | |
1902 | ||
1903 | ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); | |
1904 | ||
1905 | /* Search the TLV buffer space in the scan response for any valid TLVs */ | |
1906 | wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); | |
1907 | ||
1908 | /* | |
1909 | * Process each scan response returned (pscan->nr_sets). Save | |
1910 | * the information in the newbssentry and then insert into the | |
1911 | * driver scan table either as an update to an existing entry | |
1912 | * or as an addition at the end of the table | |
1913 | */ | |
1914 | for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { | |
1915 | /* Zero out the newbssentry we are about to store info in */ | |
1916 | memset(&newbssentry, 0x00, sizeof(newbssentry)); | |
1917 | ||
1918 | /* Process the data fields and IEs returned for this BSS */ | |
1919 | if ((InterpretBSSDescriptionWithIE(&newbssentry, | |
1920 | &pbssinfo, | |
1921 | &bytesleft) == | |
1922 | 0) | |
1923 | && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { | |
1924 | ||
1925 | lbs_pr_debug(1, | |
1926 | "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", | |
1927 | newbssentry.macaddress[0], | |
1928 | newbssentry.macaddress[1], | |
1929 | newbssentry.macaddress[2], | |
1930 | newbssentry.macaddress[3], | |
1931 | newbssentry.macaddress[4], | |
1932 | newbssentry.macaddress[5]); | |
1933 | ||
1934 | /* | |
1935 | * Search the scan table for the same bssid | |
1936 | */ | |
1937 | for (bssIdx = 0; bssIdx < numintable; bssIdx++) { | |
1938 | if (memcmp(newbssentry.macaddress, | |
1939 | adapter->scantable[bssIdx]. | |
1940 | macaddress, | |
1941 | sizeof(newbssentry.macaddress)) == | |
1942 | 0) { | |
1943 | /* | |
1944 | * If the SSID matches as well, it is a duplicate of | |
1945 | * this entry. Keep the bssIdx set to this | |
1946 | * entry so we replace the old contents in the table | |
1947 | */ | |
1948 | if ((newbssentry.ssid.ssidlength == | |
1949 | adapter->scantable[bssIdx].ssid. | |
1950 | ssidlength) | |
1951 | && | |
1952 | (memcmp | |
1953 | (newbssentry.ssid.ssid, | |
1954 | adapter->scantable[bssIdx].ssid. | |
1955 | ssid, | |
1956 | newbssentry.ssid.ssidlength) == | |
1957 | 0)) { | |
1958 | lbs_pr_debug(1, | |
1959 | "SCAN_RESP: Duplicate of index: %d\n", | |
1960 | bssIdx); | |
1961 | break; | |
1962 | } | |
1963 | } | |
1964 | } | |
1965 | /* | |
1966 | * If the bssIdx is equal to the number of entries in the table, | |
1967 | * the new entry was not a duplicate; append it to the scan | |
1968 | * table | |
1969 | */ | |
1970 | if (bssIdx == numintable) { | |
1971 | /* Range check the bssIdx, keep it limited to the last entry */ | |
1972 | if (bssIdx == MRVDRV_MAX_BSSID_LIST) { | |
1973 | bssIdx--; | |
1974 | } else { | |
1975 | numintable++; | |
1976 | } | |
1977 | } | |
1978 | ||
1979 | /* | |
1980 | * If the TSF TLV was appended to the scan results, save the | |
1981 | * this entries TSF value in the networktsf field. The | |
1982 | * networktsf is the firmware's TSF value at the time the | |
1983 | * beacon or probe response was received. | |
1984 | */ | |
1985 | if (ptsftlv) { | |
1986 | memcpy(&tsfval, &ptsftlv->tsftable[idx], | |
1987 | sizeof(tsfval)); | |
1988 | tsfval = le64_to_cpu(tsfval); | |
1989 | ||
1990 | memcpy(&newbssentry.networktsf, | |
1991 | &tsfval, sizeof(newbssentry.networktsf)); | |
1992 | } | |
1993 | ||
1994 | /* Copy the locally created newbssentry to the scan table */ | |
1995 | memcpy(&adapter->scantable[bssIdx], | |
1996 | &newbssentry, | |
1997 | sizeof(adapter->scantable[bssIdx])); | |
1998 | ||
1999 | } else { | |
2000 | ||
2001 | /* error parsing/interpreting the scan response, skipped */ | |
2002 | lbs_pr_debug(1, "SCAN_RESP: " | |
2003 | "InterpretBSSDescriptionWithIE returned ERROR\n"); | |
2004 | } | |
2005 | } | |
2006 | ||
2007 | lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", | |
2008 | pscan->nr_sets, numintable - adapter->numinscantable, | |
2009 | numintable); | |
2010 | ||
2011 | /* Update the total number of BSSIDs in the scan table */ | |
2012 | adapter->numinscantable = numintable; | |
2013 | ||
2014 | LEAVE(); | |
2015 | return 0; | |
2016 | } |