blob: de9c548cb6a5661fecb75b6c2af072bd74380afe [file] [log] [blame]
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001/**
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>
Dan Williamsfcdb53d2007-05-25 16:15:56 -040011#include <linux/etherdevice.h>
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020012
13#include <net/ieee80211.h>
14#include <net/iw_handler.h>
15
Vladimir Davydovac630c22007-09-06 21:45:36 -040016#include <asm/unaligned.h>
17
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020018#include "host.h"
19#include "decl.h"
20#include "dev.h"
21#include "scan.h"
Dan Williams8c512762007-08-02 11:40:45 -040022#include "join.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020023
24//! Approximate amount of data needed to pass a scan result back to iwlist
25#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \
26 + IW_ESSID_MAX_SIZE \
27 + IW_EV_UINT_LEN \
28 + IW_EV_FREQ_LEN \
29 + IW_EV_QUAL_LEN \
30 + IW_ESSID_MAX_SIZE \
31 + IW_EV_PARAM_LEN \
32 + 40) /* 40 for WPAIE */
33
34//! Memory needed to store a max sized channel List TLV for a firmware scan
35#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \
36 + (MRVDRV_MAX_CHANNELS_PER_SCAN \
37 * sizeof(struct chanscanparamset)))
38
39//! Memory needed to store a max number/size SSID TLV for a firmware scan
40#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset))
41
Holger Schurig10078322007-11-15 18:05:47 -050042//! Maximum memory needed for a lbs_scan_cmd_config with all TLVs at max
43#define MAX_SCAN_CFG_ALLOC (sizeof(struct lbs_scan_cmd_config) \
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020044 + CHAN_TLV_MAX_SIZE \
45 + SSID_TLV_MAX_SIZE)
46
47//! The maximum number of channels the firmware can scan per command
48#define MRVDRV_MAX_CHANNELS_PER_SCAN 14
49
50/**
51 * @brief Number of channels to scan per firmware scan command issuance.
52 *
53 * Number restricted to prevent hitting the limit on the amount of scan data
54 * returned in a single firmware scan command.
55 */
56#define MRVDRV_CHANNELS_PER_SCAN_CMD 4
57
58//! Scan time specified in the channel TLV for each channel for passive scans
59#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100
60
61//! Scan time specified in the channel TLV for each channel for active scans
62#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100
63
Dan Williams123e0e02007-05-25 23:23:43 -040064static const u8 zeromac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
65static const u8 bcastmac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
Dan Williamseb8f7332007-05-25 16:25:21 -040066
Holger Schurige56188a2007-10-09 14:15:19 +020067
68
69
70/*********************************************************************/
71/* */
72/* Misc helper functions */
73/* */
74/*********************************************************************/
75
Dan Williamsfcdb53d2007-05-25 16:15:56 -040076static inline void clear_bss_descriptor (struct bss_descriptor * bss)
77{
78 /* Don't blow away ->list, just BSS data */
79 memset(bss, 0, offsetof(struct bss_descriptor, list));
80}
81
Holger Schurig10078322007-11-15 18:05:47 -050082static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
Dan Williamsfcdb53d2007-05-25 16:15:56 -040083 struct bss_descriptor * match_bss)
84{
85 if ( !secinfo->wep_enabled
86 && !secinfo->WPAenabled
87 && !secinfo->WPA2enabled
Dan Williamsab617972007-08-02 10:48:02 -040088 && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC
89 && match_bss->rsn_ie[0] != MFIE_TYPE_RSN
Dan Williams0c9ca692007-08-02 10:43:44 -040090 && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -040091 return 1;
92 }
93 return 0;
94}
95
Holger Schurig10078322007-11-15 18:05:47 -050096static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo,
Dan Williamsfcdb53d2007-05-25 16:15:56 -040097 struct bss_descriptor * match_bss)
98{
99 if ( secinfo->wep_enabled
100 && !secinfo->WPAenabled
101 && !secinfo->WPA2enabled
Dan Williams0c9ca692007-08-02 10:43:44 -0400102 && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400103 return 1;
104 }
105 return 0;
106}
107
Holger Schurig10078322007-11-15 18:05:47 -0500108static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400109 struct bss_descriptor * match_bss)
110{
111 if ( !secinfo->wep_enabled
112 && secinfo->WPAenabled
Dan Williamsab617972007-08-02 10:48:02 -0400113 && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC)
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400114 /* privacy bit may NOT be set in some APs like LinkSys WRT54G
Dan Williams0c9ca692007-08-02 10:43:44 -0400115 && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
116 */
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400117 ) {
118 return 1;
119 }
120 return 0;
121}
122
Holger Schurig10078322007-11-15 18:05:47 -0500123static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400124 struct bss_descriptor * match_bss)
125{
126 if ( !secinfo->wep_enabled
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400127 && secinfo->WPA2enabled
Dan Williamsab617972007-08-02 10:48:02 -0400128 && (match_bss->rsn_ie[0] == MFIE_TYPE_RSN)
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400129 /* privacy bit may NOT be set in some APs like LinkSys WRT54G
Dan Williams0c9ca692007-08-02 10:43:44 -0400130 && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
131 */
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400132 ) {
133 return 1;
134 }
135 return 0;
136}
137
Holger Schurig10078322007-11-15 18:05:47 -0500138static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400139 struct bss_descriptor * match_bss)
140{
141 if ( !secinfo->wep_enabled
142 && !secinfo->WPAenabled
143 && !secinfo->WPA2enabled
Dan Williamsab617972007-08-02 10:48:02 -0400144 && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC)
145 && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN)
Dan Williams0c9ca692007-08-02 10:43:44 -0400146 && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400147 return 1;
148 }
149 return 0;
150}
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200151
152/**
153 * @brief Check if a scanned network compatible with the driver settings
154 *
155 * WEP WPA WPA2 ad-hoc encrypt Network
156 * enabled enabled enabled AES mode privacy WPA WPA2 Compatible
157 * 0 0 0 0 NONE 0 0 0 yes No security
158 * 1 0 0 0 NONE 1 0 0 yes Static WEP
159 * 0 1 0 0 x 1x 1 x yes WPA
160 * 0 0 1 0 x 1x x 1 yes WPA2
161 * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES
162 * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP
163 *
164 *
Holger Schurig69f90322007-11-23 15:43:44 +0100165 * @param adapter A pointer to struct lbs_adapter
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200166 * @param index Index in scantable to check against current driver settings
167 * @param mode Network mode: Infrastructure or IBSS
168 *
169 * @return Index in scantable, or error code if negative
170 */
Holger Schurig69f90322007-11-23 15:43:44 +0100171static int is_network_compatible(struct lbs_adapter *adapter,
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400172 struct bss_descriptor * bss, u8 mode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200173{
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400174 int matched = 0;
175
Holger Schurige56188a2007-10-09 14:15:19 +0200176 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200177
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400178 if (bss->mode != mode)
179 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200180
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400181 if ((matched = match_bss_no_security(&adapter->secinfo, bss))) {
182 goto done;
183 } else if ((matched = match_bss_static_wep(&adapter->secinfo, bss))) {
184 goto done;
185 } else if ((matched = match_bss_wpa(&adapter->secinfo, bss))) {
186 lbs_deb_scan(
187 "is_network_compatible() WPA: 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],
Dan Williams889c05b2007-05-10 22:57:23 -0400190 adapter->secinfo.wep_enabled ? "e" : "d",
191 adapter->secinfo.WPAenabled ? "e" : "d",
192 adapter->secinfo.WPA2enabled ? "e" : "d",
Dan Williams0c9ca692007-08-02 10:43:44 -0400193 (bss->capability & WLAN_CAPABILITY_PRIVACY));
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400194 goto done;
195 } else if ((matched = match_bss_wpa2(&adapter->secinfo, bss))) {
196 lbs_deb_scan(
197 "is_network_compatible() WPA2: wpa_ie=%#x "
198 "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s "
199 "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0],
200 adapter->secinfo.wep_enabled ? "e" : "d",
201 adapter->secinfo.WPAenabled ? "e" : "d",
202 adapter->secinfo.WPA2enabled ? "e" : "d",
Dan Williams0c9ca692007-08-02 10:43:44 -0400203 (bss->capability & WLAN_CAPABILITY_PRIVACY));
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400204 goto done;
205 } else if ((matched = match_bss_dynamic_wep(&adapter->secinfo, bss))) {
206 lbs_deb_scan(
207 "is_network_compatible() dynamic WEP: "
208 "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n",
Dan Williams0c9ca692007-08-02 10:43:44 -0400209 bss->wpa_ie[0], bss->rsn_ie[0],
210 (bss->capability & WLAN_CAPABILITY_PRIVACY));
Holger Schurig9012b282007-05-25 11:27:16 -0400211 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200212 }
213
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400214 /* bss security settings don't match those configured on card */
215 lbs_deb_scan(
216 "is_network_compatible() FAILED: wpa_ie=%#x "
217 "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n",
218 bss->wpa_ie[0], bss->rsn_ie[0],
219 adapter->secinfo.wep_enabled ? "e" : "d",
220 adapter->secinfo.WPAenabled ? "e" : "d",
221 adapter->secinfo.WPA2enabled ? "e" : "d",
Dan Williams0c9ca692007-08-02 10:43:44 -0400222 (bss->capability & WLAN_CAPABILITY_PRIVACY));
Holger Schurig9012b282007-05-25 11:27:16 -0400223
224done:
Holger Schurige56188a2007-10-09 14:15:19 +0200225 lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched);
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400226 return matched;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200227}
228
229/**
Holger Schurige56188a2007-10-09 14:15:19 +0200230 * @brief Compare two SSIDs
231 *
232 * @param ssid1 A pointer to ssid to compare
233 * @param ssid2 A pointer to ssid to compare
234 *
235 * @return 0--ssid is same, otherwise is different
236 */
Holger Schurig10078322007-11-15 18:05:47 -0500237int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len)
Holger Schurige56188a2007-10-09 14:15:19 +0200238{
239 if (ssid1_len != ssid2_len)
240 return -1;
241
242 return memcmp(ssid1, ssid2, ssid1_len);
243}
244
245
246
247
248/*********************************************************************/
249/* */
250/* Main scanning support */
251/* */
252/*********************************************************************/
253
254
255/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200256 * @brief Create a channel list for the driver to scan based on region info
257 *
Holger Schurig10078322007-11-15 18:05:47 -0500258 * Only used from lbs_scan_setup_scan_config()
Holger Schurige56188a2007-10-09 14:15:19 +0200259 *
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200260 * Use the driver region/band information to construct a comprehensive list
261 * of channels to scan. This routine is used for any scan that is not
262 * provided a specific channel list to scan.
263 *
Holger Schurig69f90322007-11-23 15:43:44 +0100264 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200265 * @param scanchanlist Output parameter: resulting channel list to scan
266 * @param filteredscan Flag indicating whether or not a BSSID or SSID filter
267 * is being sent in the command to firmware. Used to
268 * increase the number of channels sent in a scan
269 * command and to disable the firmware channel scan
270 * filter.
271 *
272 * @return void
273 */
Holger Schurig69f90322007-11-23 15:43:44 +0100274static void lbs_scan_create_channel_list(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200275 struct chanscanparamset * scanchanlist,
276 u8 filteredscan)
277{
278
Holger Schurig69f90322007-11-23 15:43:44 +0100279 struct lbs_adapter *adapter = priv->adapter;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200280 struct region_channel *scanregion;
281 struct chan_freq_power *cfp;
282 int rgnidx;
283 int chanidx;
284 int nextchan;
285 u8 scantype;
286
Holger Schurige56188a2007-10-09 14:15:19 +0200287 lbs_deb_enter_args(LBS_DEB_SCAN, "filteredscan %d", filteredscan);
288
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200289 chanidx = 0;
290
291 /* Set the default scan type to the user specified type, will later
292 * be changed to passive on a per channel basis if restricted by
293 * regulatory requirements (11d or 11h)
294 */
Holger Schurig4f2fdaa2007-08-02 13:12:27 -0400295 scantype = CMD_SCAN_TYPE_ACTIVE;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200296
297 for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) {
298 if (priv->adapter->enable11d &&
Brajesh Dave01d77d82007-11-20 17:44:14 -0500299 (adapter->connect_status != LBS_CONNECTED) &&
300 (adapter->mesh_connect_status != LBS_CONNECTED)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200301 /* Scan all the supported chan for the first scan */
302 if (!adapter->universal_channel[rgnidx].valid)
303 continue;
304 scanregion = &adapter->universal_channel[rgnidx];
305
306 /* clear the parsed_region_chan for the first scan */
307 memset(&adapter->parsed_region_chan, 0x00,
308 sizeof(adapter->parsed_region_chan));
309 } else {
310 if (!adapter->region_channel[rgnidx].valid)
311 continue;
312 scanregion = &adapter->region_channel[rgnidx];
313 }
314
315 for (nextchan = 0;
316 nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
317
318 cfp = scanregion->CFP + nextchan;
319
320 if (priv->adapter->enable11d) {
321 scantype =
Holger Schurig10078322007-11-15 18:05:47 -0500322 lbs_get_scan_type_11d(cfp->channel,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200323 &adapter->
324 parsed_region_chan);
325 }
326
327 switch (scanregion->band) {
328 case BAND_B:
329 case BAND_G:
330 default:
331 scanchanlist[chanidx].radiotype =
Dan Williams0aef64d2007-08-02 11:31:18 -0400332 CMD_SCAN_RADIO_TYPE_BG;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200333 break;
334 }
335
Dan Williams0aef64d2007-08-02 11:31:18 -0400336 if (scantype == CMD_SCAN_TYPE_PASSIVE) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200337 scanchanlist[chanidx].maxscantime =
David Woodhouse981f1872007-05-25 23:36:54 -0400338 cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200339 scanchanlist[chanidx].chanscanmode.passivescan =
340 1;
341 } else {
342 scanchanlist[chanidx].maxscantime =
David Woodhouse981f1872007-05-25 23:36:54 -0400343 cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200344 scanchanlist[chanidx].chanscanmode.passivescan =
345 0;
346 }
347
348 scanchanlist[chanidx].channumber = cfp->channel;
349
350 if (filteredscan) {
351 scanchanlist[chanidx].chanscanmode.
352 disablechanfilt = 1;
353 }
354 }
355 }
356}
357
Dan Williams2afc0c52007-08-02 13:19:04 -0400358
359/* Delayed partial scan worker */
Holger Schurig10078322007-11-15 18:05:47 -0500360void lbs_scan_worker(struct work_struct *work)
Dan Williams2afc0c52007-08-02 13:19:04 -0400361{
Holger Schurig69f90322007-11-23 15:43:44 +0100362 struct lbs_private *priv = container_of(work,
363 struct lbs_private,
364 scan_work.work);
Dan Williams2afc0c52007-08-02 13:19:04 -0400365
Holger Schurig10078322007-11-15 18:05:47 -0500366 lbs_scan_networks(priv, NULL, 0);
Dan Williams2afc0c52007-08-02 13:19:04 -0400367}
368
369
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200370/**
Holger Schurig10078322007-11-15 18:05:47 -0500371 * @brief Construct a lbs_scan_cmd_config structure to use in issue scan cmds
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200372 *
Holger Schurig10078322007-11-15 18:05:47 -0500373 * Application layer or other functions can invoke lbs_scan_networks
374 * with a scan configuration supplied in a lbs_ioctl_user_scan_cfg struct.
375 * This structure is used as the basis of one or many lbs_scan_cmd_config
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200376 * commands that are sent to the command processing module and sent to
377 * firmware.
378 *
Holger Schurig10078322007-11-15 18:05:47 -0500379 * Create a lbs_scan_cmd_config based on the following user supplied
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200380 * parameters (if present):
381 * - SSID filter
382 * - BSSID filter
383 * - Number of Probes to be sent
384 * - channel list
385 *
386 * If the SSID or BSSID filter is not present, disable/clear the filter.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200387 * Qualify the channel
388 *
Holger Schurig69f90322007-11-23 15:43:44 +0100389 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200390 * @param puserscanin NULL or pointer to scan configuration parameters
391 * @param ppchantlvout Output parameter: Pointer to the start of the
392 * channel TLV portion of the output scan config
393 * @param pscanchanlist Output parameter: Pointer to the resulting channel
394 * list to scan
395 * @param pmaxchanperscan Output parameter: Number of channels to scan for
396 * each issuance of the firmware scan command
397 * @param pfilteredscan Output parameter: Flag indicating whether or not
398 * a BSSID or SSID filter is being sent in the
399 * command to firmware. Used to increase the number
400 * of channels sent in a scan command and to
401 * disable the firmware channel scan filter.
402 * @param pscancurrentonly Output parameter: Flag indicating whether or not
403 * we are only scanning our current active channel
404 *
405 * @return resulting scan configuration
406 */
Holger Schurig10078322007-11-15 18:05:47 -0500407static struct lbs_scan_cmd_config *
Holger Schurig69f90322007-11-23 15:43:44 +0100408lbs_scan_setup_scan_config(struct lbs_private *priv,
Holger Schurig10078322007-11-15 18:05:47 -0500409 const struct lbs_ioctl_user_scan_cfg *puserscanin,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200410 struct mrvlietypes_chanlistparamset ** ppchantlvout,
411 struct chanscanparamset * pscanchanlist,
412 int *pmaxchanperscan,
413 u8 * pfilteredscan,
414 u8 * pscancurrentonly)
415{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200416 struct mrvlietypes_ssidparamset *pssidtlv;
Holger Schurig10078322007-11-15 18:05:47 -0500417 struct lbs_scan_cmd_config *pscancfgout = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200418 u8 *ptlvpos;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200419 int chanidx;
420 int scantype;
421 int scandur;
422 int channel;
423 int radiotype;
424
Holger Schurige56188a2007-10-09 14:15:19 +0200425 lbs_deb_enter(LBS_DEB_SCAN);
426
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200427 pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
428 if (pscancfgout == NULL)
429 goto out;
430
431 /* The tlvbufferlen is calculated for each scan command. The TLVs added
432 * in this routine will be preserved since the routine that sends
433 * the command will append channelTLVs at *ppchantlvout. The difference
434 * between the *ppchantlvout and the tlvbuffer start will be used
435 * to calculate the size of anything we add in this routine.
436 */
437 pscancfgout->tlvbufferlen = 0;
438
439 /* Running tlv pointer. Assigned to ppchantlvout at end of function
440 * so later routines know where channels can be added to the command buf
441 */
442 ptlvpos = pscancfgout->tlvbuffer;
443
444 /*
445 * Set the initial scan paramters for progressive scanning. If a specific
446 * BSSID or SSID is used, the number of channels in the scan command
447 * will be increased to the absolute maximum
448 */
449 *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD;
450
451 /* Initialize the scan as un-filtered by firmware, set to TRUE below if
452 * a SSID or BSSID filter is sent in the command
453 */
454 *pfilteredscan = 0;
455
456 /* Initialize the scan as not being only on the current channel. If
457 * the channel list is customized, only contains one channel, and
458 * is the active channel, this is set true and data flow is not halted.
459 */
460 *pscancurrentonly = 0;
461
462 if (puserscanin) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200463 /* Set the bss type scan filter, use adapter setting if unset */
464 pscancfgout->bsstype =
Holger Schurigd65ead82007-08-02 13:12:12 -0400465 puserscanin->bsstype ? puserscanin->bsstype : CMD_BSS_TYPE_ANY;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200466
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200467 /*
468 * Set the BSSID filter to the incoming configuration,
469 * if non-zero. If not set, it will remain disabled (all zeros).
470 */
Dan Williamseb8f7332007-05-25 16:25:21 -0400471 memcpy(pscancfgout->bssid, puserscanin->bssid,
472 sizeof(pscancfgout->bssid));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200473
Dan Williamseb8f7332007-05-25 16:25:21 -0400474 if (puserscanin->ssid_len) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200475 pssidtlv =
476 (struct mrvlietypes_ssidparamset *) pscancfgout->
477 tlvbuffer;
478 pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
Dan Williamseb8f7332007-05-25 16:25:21 -0400479 pssidtlv->header.len = cpu_to_le16(puserscanin->ssid_len);
480 memcpy(pssidtlv->ssid, puserscanin->ssid,
481 puserscanin->ssid_len);
482 ptlvpos += sizeof(pssidtlv->header) + puserscanin->ssid_len;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200483 }
484
485 /*
486 * The default number of channels sent in the command is low to
487 * ensure the response buffer from the firmware does not truncate
488 * scan results. That is not an issue with an SSID or BSSID
489 * filter applied to the scan results in the firmware.
490 */
Dan Williamseb8f7332007-05-25 16:25:21 -0400491 if ( puserscanin->ssid_len
492 || (compare_ether_addr(pscancfgout->bssid, &zeromac[0]) != 0)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200493 *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN;
494 *pfilteredscan = 1;
495 }
496 } else {
Holger Schurigd65ead82007-08-02 13:12:12 -0400497 pscancfgout->bsstype = CMD_BSS_TYPE_ANY;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200498 }
499
500 /*
501 * Set the output for the channel TLV to the address in the tlv buffer
Holger Schurigdd1d12d2007-11-28 17:29:36 +0100502 * past any TLVs that were added in this fuction (SSID).
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200503 * channel TLVs will be added past this for each scan command, preserving
504 * the TLVs that were previously added.
505 */
506 *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos;
507
Dan Williams2afc0c52007-08-02 13:19:04 -0400508 if (!puserscanin || !puserscanin->chanlist[0].channumber) {
509 /* Create a default channel scan list */
Holger Schurige56188a2007-10-09 14:15:19 +0200510 lbs_deb_scan("creating full region channel list\n");
Holger Schurig10078322007-11-15 18:05:47 -0500511 lbs_scan_create_channel_list(priv, pscanchanlist,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200512 *pfilteredscan);
Dan Williams2afc0c52007-08-02 13:19:04 -0400513 goto out;
514 }
515
Dan Williams2afc0c52007-08-02 13:19:04 -0400516 for (chanidx = 0;
Holger Schurig10078322007-11-15 18:05:47 -0500517 chanidx < LBS_IOCTL_USER_SCAN_CHAN_MAX
Dan Williams2afc0c52007-08-02 13:19:04 -0400518 && puserscanin->chanlist[chanidx].channumber; chanidx++) {
519
520 channel = puserscanin->chanlist[chanidx].channumber;
521 (pscanchanlist + chanidx)->channumber = channel;
522
523 radiotype = puserscanin->chanlist[chanidx].radiotype;
524 (pscanchanlist + chanidx)->radiotype = radiotype;
525
526 scantype = puserscanin->chanlist[chanidx].scantype;
527
528 if (scantype == CMD_SCAN_TYPE_PASSIVE) {
529 (pscanchanlist +
530 chanidx)->chanscanmode.passivescan = 1;
531 } else {
532 (pscanchanlist +
533 chanidx)->chanscanmode.passivescan = 0;
534 }
535
536 if (puserscanin->chanlist[chanidx].scantime) {
537 scandur = puserscanin->chanlist[chanidx].scantime;
538 } else {
539 if (scantype == CMD_SCAN_TYPE_PASSIVE) {
540 scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME;
541 } else {
542 scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME;
543 }
544 }
545
546 (pscanchanlist + chanidx)->minscantime =
547 cpu_to_le16(scandur);
548 (pscanchanlist + chanidx)->maxscantime =
549 cpu_to_le16(scandur);
550 }
551
552 /* Check if we are only scanning the current channel */
553 if ((chanidx == 1) &&
554 (puserscanin->chanlist[0].channumber ==
555 priv->adapter->curbssparams.channel)) {
556 *pscancurrentonly = 1;
Holger Schurige56188a2007-10-09 14:15:19 +0200557 lbs_deb_scan("scanning current channel only");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200558 }
559
560out:
561 return pscancfgout;
562}
563
564/**
565 * @brief Construct and send multiple scan config commands to the firmware
566 *
Holger Schurig10078322007-11-15 18:05:47 -0500567 * Only used from lbs_scan_networks()
Holger Schurige56188a2007-10-09 14:15:19 +0200568 *
Holger Schurig10078322007-11-15 18:05:47 -0500569 * Previous routines have created a lbs_scan_cmd_config with any requested
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200570 * TLVs. This function splits the channel TLV into maxchanperscan lists
571 * and sends the portion of the channel TLV along with the other TLVs
Holger Schurig10078322007-11-15 18:05:47 -0500572 * to the lbs_cmd routines for execution in the firmware.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200573 *
Holger Schurig69f90322007-11-23 15:43:44 +0100574 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200575 * @param maxchanperscan Maximum number channels to be included in each
576 * scan command sent to firmware
577 * @param filteredscan Flag indicating whether or not a BSSID or SSID
578 * filter is being used for the firmware command
579 * scan command sent to firmware
580 * @param pscancfgout Scan configuration used for this scan.
581 * @param pchantlvout Pointer in the pscancfgout where the channel TLV
582 * should start. This is past any other TLVs that
583 * must be sent down in each firmware command.
584 * @param pscanchanlist List of channels to scan in maxchanperscan segments
585 *
586 * @return 0 or error return otherwise
587 */
Holger Schurig69f90322007-11-23 15:43:44 +0100588static int lbs_scan_channel_list(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200589 int maxchanperscan,
590 u8 filteredscan,
Holger Schurig10078322007-11-15 18:05:47 -0500591 struct lbs_scan_cmd_config *pscancfgout,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200592 struct mrvlietypes_chanlistparamset * pchantlvout,
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400593 struct chanscanparamset * pscanchanlist,
Holger Schurig10078322007-11-15 18:05:47 -0500594 const struct lbs_ioctl_user_scan_cfg *puserscanin,
Marcelo Tosatti2be92192007-05-25 00:33:28 -0400595 int full_scan)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200596{
597 struct chanscanparamset *ptmpchan;
598 struct chanscanparamset *pstartchan;
599 u8 scanband;
600 int doneearly;
601 int tlvidx;
602 int ret = 0;
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400603 int scanned = 0;
604 union iwreq_data wrqu;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200605
Holger Schurige56188a2007-10-09 14:15:19 +0200606 lbs_deb_enter_args(LBS_DEB_SCAN, "maxchanperscan %d, filteredscan %d, "
607 "full_scan %d", maxchanperscan, filteredscan, full_scan);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200608
Holger Schurig314a8862007-10-08 12:20:04 +0200609 if (!pscancfgout || !pchantlvout || !pscanchanlist) {
Holger Schurige56188a2007-10-09 14:15:19 +0200610 lbs_deb_scan("pscancfgout, pchantlvout or "
611 "pscanchanlist is NULL\n");
612 ret = -1;
613 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200614 }
615
616 pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
617
618 /* Set the temp channel struct pointer to the start of the desired list */
619 ptmpchan = pscanchanlist;
620
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400621 if (priv->adapter->last_scanned_channel && !puserscanin)
622 ptmpchan += priv->adapter->last_scanned_channel;
623
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200624 /* Loop through the desired channel list, sending a new firmware scan
625 * commands for each maxchanperscan channels (or for 1,6,11 individually
626 * if configured accordingly)
627 */
628 while (ptmpchan->channumber) {
629
630 tlvidx = 0;
631 pchantlvout->header.len = 0;
632 scanband = ptmpchan->radiotype;
633 pstartchan = ptmpchan;
634 doneearly = 0;
635
636 /* Construct the channel TLV for the scan command. Continue to
637 * insert channel TLVs until:
638 * - the tlvidx hits the maximum configured per scan command
639 * - the next channel to insert is 0 (end of desired channel list)
640 * - doneearly is set (controlling individual scanning of 1,6,11)
641 */
642 while (tlvidx < maxchanperscan && ptmpchan->channumber
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400643 && !doneearly && scanned < 2) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200644
Holger Schurige56188a2007-10-09 14:15:19 +0200645 lbs_deb_scan("channel %d, radio %d, passive %d, "
646 "dischanflt %d, maxscantime %d\n",
647 ptmpchan->channumber,
648 ptmpchan->radiotype,
Dan Williams2afc0c52007-08-02 13:19:04 -0400649 ptmpchan->chanscanmode.passivescan,
650 ptmpchan->chanscanmode.disablechanfilt,
651 ptmpchan->maxscantime);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200652
653 /* Copy the current channel TLV to the command being prepared */
654 memcpy(pchantlvout->chanscanparam + tlvidx,
655 ptmpchan, sizeof(pchantlvout->chanscanparam));
656
657 /* Increment the TLV header length by the size appended */
David Woodhouse981f1872007-05-25 23:36:54 -0400658 /* Ew, it would be _so_ nice if we could just declare the
659 variable little-endian and let GCC handle it for us */
660 pchantlvout->header.len =
661 cpu_to_le16(le16_to_cpu(pchantlvout->header.len) +
662 sizeof(pchantlvout->chanscanparam));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200663
664 /*
665 * The tlv buffer length is set to the number of bytes of the
666 * between the channel tlv pointer and the start of the
667 * tlv buffer. This compensates for any TLVs that were appended
668 * before the channel list.
669 */
670 pscancfgout->tlvbufferlen = ((u8 *) pchantlvout
671 - pscancfgout->tlvbuffer);
672
673 /* Add the size of the channel tlv header and the data length */
674 pscancfgout->tlvbufferlen +=
675 (sizeof(pchantlvout->header)
David Woodhouse981f1872007-05-25 23:36:54 -0400676 + le16_to_cpu(pchantlvout->header.len));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200677
678 /* Increment the index to the channel tlv we are constructing */
679 tlvidx++;
680
681 doneearly = 0;
682
683 /* Stop the loop if the *current* channel is in the 1,6,11 set
684 * and we are not filtering on a BSSID or SSID.
685 */
686 if (!filteredscan && (ptmpchan->channumber == 1
687 || ptmpchan->channumber == 6
688 || ptmpchan->channumber == 11)) {
689 doneearly = 1;
690 }
691
692 /* Increment the tmp pointer to the next channel to be scanned */
693 ptmpchan++;
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400694 scanned++;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200695
696 /* Stop the loop if the *next* channel is in the 1,6,11 set.
697 * This will cause it to be the only channel scanned on the next
698 * interation
699 */
700 if (!filteredscan && (ptmpchan->channumber == 1
701 || ptmpchan->channumber == 6
702 || ptmpchan->channumber == 11)) {
703 doneearly = 1;
704 }
705 }
706
707 /* Send the scan command to the firmware with the specified cfg */
Holger Schurig10078322007-11-15 18:05:47 -0500708 ret = lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, 0,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200709 0, 0, pscancfgout);
Marcelo Tosatti2be92192007-05-25 00:33:28 -0400710 if (scanned >= 2 && !full_scan) {
Holger Schurig9012b282007-05-25 11:27:16 -0400711 ret = 0;
712 goto done;
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400713 }
Marcelo Tosatti2be92192007-05-25 00:33:28 -0400714 scanned = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200715 }
716
Dan Williamsd9ad2f52007-05-25 22:38:41 -0400717done:
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400718 priv->adapter->last_scanned_channel = ptmpchan->channumber;
719
Dan Williams2afc0c52007-08-02 13:19:04 -0400720 if (priv->adapter->last_scanned_channel) {
721 /* Schedule the next part of the partial scan */
722 if (!full_scan && !priv->adapter->surpriseremoved) {
723 cancel_delayed_work(&priv->scan_work);
724 queue_delayed_work(priv->work_thread, &priv->scan_work,
725 msecs_to_jiffies(300));
726 }
727 } else {
728 /* All done, tell userspace the scan table has been updated */
729 memset(&wrqu, 0, sizeof(union iwreq_data));
730 wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
731 }
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400732
Holger Schurige56188a2007-10-09 14:15:19 +0200733out:
Holger Schurig9012b282007-05-25 11:27:16 -0400734 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200735 return ret;
736}
737
Holger Schurige56188a2007-10-09 14:15:19 +0200738/*
Holger Schurig10078322007-11-15 18:05:47 -0500739 * Only used from lbs_scan_networks()
Holger Schurige56188a2007-10-09 14:15:19 +0200740*/
Holger Schurig69f90322007-11-23 15:43:44 +0100741static void clear_selected_scan_list_entries(struct lbs_adapter *adapter,
Holger Schurig10078322007-11-15 18:05:47 -0500742 const struct lbs_ioctl_user_scan_cfg *scan_cfg)
Dan Williamseb8f7332007-05-25 16:25:21 -0400743{
Holger Schurige56188a2007-10-09 14:15:19 +0200744 struct bss_descriptor *bss;
745 struct bss_descriptor *safe;
Dan Williamseb8f7332007-05-25 16:25:21 -0400746 u32 clear_ssid_flag = 0, clear_bssid_flag = 0;
747
Holger Schurige56188a2007-10-09 14:15:19 +0200748 lbs_deb_enter(LBS_DEB_SCAN);
749
Dan Williamseb8f7332007-05-25 16:25:21 -0400750 if (!scan_cfg)
Holger Schurige56188a2007-10-09 14:15:19 +0200751 goto out;
Dan Williamseb8f7332007-05-25 16:25:21 -0400752
753 if (scan_cfg->clear_ssid && scan_cfg->ssid_len)
754 clear_ssid_flag = 1;
755
756 if (scan_cfg->clear_bssid
757 && (compare_ether_addr(scan_cfg->bssid, &zeromac[0]) != 0)
758 && (compare_ether_addr(scan_cfg->bssid, &bcastmac[0]) != 0)) {
759 clear_bssid_flag = 1;
760 }
761
762 if (!clear_ssid_flag && !clear_bssid_flag)
Holger Schurige56188a2007-10-09 14:15:19 +0200763 goto out;
Dan Williamseb8f7332007-05-25 16:25:21 -0400764
765 mutex_lock(&adapter->lock);
766 list_for_each_entry_safe (bss, safe, &adapter->network_list, list) {
767 u32 clear = 0;
768
769 /* Check for an SSID match */
770 if ( clear_ssid_flag
Dan Williamsd8efea22007-05-28 23:54:55 -0400771 && (bss->ssid_len == scan_cfg->ssid_len)
772 && !memcmp(bss->ssid, scan_cfg->ssid, bss->ssid_len))
Dan Williamseb8f7332007-05-25 16:25:21 -0400773 clear = 1;
774
775 /* Check for a BSSID match */
776 if ( clear_bssid_flag
777 && !compare_ether_addr(bss->bssid, scan_cfg->bssid))
778 clear = 1;
779
780 if (clear) {
781 list_move_tail (&bss->list, &adapter->network_free_list);
782 clear_bss_descriptor(bss);
783 }
784 }
785 mutex_unlock(&adapter->lock);
Holger Schurige56188a2007-10-09 14:15:19 +0200786out:
787 lbs_deb_leave(LBS_DEB_SCAN);
Dan Williamseb8f7332007-05-25 16:25:21 -0400788}
789
790
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200791/**
792 * @brief Internal function used to start a scan based on an input config
793 *
Holger Schurige56188a2007-10-09 14:15:19 +0200794 * Also used from debugfs
795 *
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200796 * Use the input user scan configuration information when provided in
797 * order to send the appropriate scan commands to firmware to populate or
798 * update the internal driver scan table
799 *
Holger Schurig69f90322007-11-23 15:43:44 +0100800 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200801 * @param puserscanin Pointer to the input configuration for the requested
802 * scan.
Holger Schurige56188a2007-10-09 14:15:19 +0200803 * @param full_scan ???
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200804 *
805 * @return 0 or < 0 if error
806 */
Holger Schurig69f90322007-11-23 15:43:44 +0100807int lbs_scan_networks(struct lbs_private *priv,
Holger Schurig10078322007-11-15 18:05:47 -0500808 const struct lbs_ioctl_user_scan_cfg *puserscanin,
Dan Williams2afc0c52007-08-02 13:19:04 -0400809 int full_scan)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200810{
Holger Schurig69f90322007-11-23 15:43:44 +0100811 struct lbs_adapter *adapter = priv->adapter;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200812 struct mrvlietypes_chanlistparamset *pchantlvout;
813 struct chanscanparamset * scan_chan_list = NULL;
Holger Schurig10078322007-11-15 18:05:47 -0500814 struct lbs_scan_cmd_config *scan_cfg = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200815 u8 filteredscan;
816 u8 scancurrentchanonly;
817 int maxchanperscan;
818 int ret;
Dan Williamsf8f55102007-05-30 10:12:55 -0400819#ifdef CONFIG_LIBERTAS_DEBUG
820 struct bss_descriptor * iter_bss;
821 int i = 0;
Joe Perches0795af52007-10-03 17:59:30 -0700822 DECLARE_MAC_BUF(mac);
Dan Williamsf8f55102007-05-30 10:12:55 -0400823#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200824
Holger Schurige56188a2007-10-09 14:15:19 +0200825 lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", full_scan);
Dan Williams2afc0c52007-08-02 13:19:04 -0400826
827 /* Cancel any partial outstanding partial scans if this scan
828 * is a full scan.
829 */
830 if (full_scan && delayed_work_pending(&priv->scan_work))
831 cancel_delayed_work(&priv->scan_work);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200832
833 scan_chan_list = kzalloc(sizeof(struct chanscanparamset) *
Holger Schurig10078322007-11-15 18:05:47 -0500834 LBS_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200835 if (scan_chan_list == NULL) {
836 ret = -ENOMEM;
837 goto out;
838 }
839
Holger Schurig10078322007-11-15 18:05:47 -0500840 scan_cfg = lbs_scan_setup_scan_config(priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200841 puserscanin,
842 &pchantlvout,
843 scan_chan_list,
844 &maxchanperscan,
845 &filteredscan,
846 &scancurrentchanonly);
847 if (scan_cfg == NULL) {
848 ret = -ENOMEM;
849 goto out;
850 }
851
Dan Williamseb8f7332007-05-25 16:25:21 -0400852 clear_selected_scan_list_entries(adapter, puserscanin);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200853
854 /* Keep the data path active if we are only scanning our current channel */
855 if (!scancurrentchanonly) {
Holger Schurig634b8f42007-05-25 13:05:16 -0400856 netif_stop_queue(priv->dev);
857 netif_carrier_off(priv->dev);
Holger Schurig3cf84092007-08-02 11:50:12 -0400858 if (priv->mesh_dev) {
859 netif_stop_queue(priv->mesh_dev);
860 netif_carrier_off(priv->mesh_dev);
861 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200862 }
863
Holger Schurig10078322007-11-15 18:05:47 -0500864 ret = lbs_scan_channel_list(priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200865 maxchanperscan,
866 filteredscan,
867 scan_cfg,
868 pchantlvout,
Marcelo Tosatti064827e2007-05-24 23:37:28 -0400869 scan_chan_list,
Marcelo Tosatti2be92192007-05-25 00:33:28 -0400870 puserscanin,
871 full_scan);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200872
Dan Williamsf8f55102007-05-30 10:12:55 -0400873#ifdef CONFIG_LIBERTAS_DEBUG
874 /* Dump the scan table */
875 mutex_lock(&adapter->lock);
Holger Schurige56188a2007-10-09 14:15:19 +0200876 lbs_deb_scan("The scan table contains:\n");
Dan Williamsf8f55102007-05-30 10:12:55 -0400877 list_for_each_entry (iter_bss, &adapter->network_list, list) {
Holger Schurige56188a2007-10-09 14:15:19 +0200878 lbs_deb_scan("scan %02d, %s, RSSI, %d, SSID '%s'\n",
Joe Perches0795af52007-10-03 17:59:30 -0700879 i++, print_mac(mac, iter_bss->bssid), (s32) iter_bss->rssi,
Dan Williamsf8f55102007-05-30 10:12:55 -0400880 escape_essid(iter_bss->ssid, iter_bss->ssid_len));
881 }
882 mutex_unlock(&adapter->lock);
883#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200884
Brajesh Dave01d77d82007-11-20 17:44:14 -0500885 if (adapter->connect_status == LBS_CONNECTED) {
Holger Schurig634b8f42007-05-25 13:05:16 -0400886 netif_carrier_on(priv->dev);
887 netif_wake_queue(priv->dev);
Brajesh Dave01d77d82007-11-20 17:44:14 -0500888 }
889
890 if (priv->mesh_dev && (adapter->mesh_connect_status == LBS_CONNECTED)) {
891 netif_carrier_on(priv->mesh_dev);
892 netif_wake_queue(priv->mesh_dev);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200893 }
894
895out:
896 if (scan_cfg)
897 kfree(scan_cfg);
898
899 if (scan_chan_list)
900 kfree(scan_chan_list);
901
Holger Schurig9012b282007-05-25 11:27:16 -0400902 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200903 return ret;
904}
905
906/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200907 * @brief Interpret a BSS scan response returned from the firmware
908 *
909 * Parse the various fixed fields and IEs passed back for a a BSS probe
910 * response or beacon from the scan command. Record information as needed
911 * in the scan table struct bss_descriptor for that entry.
912 *
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400913 * @param bss Output parameter: Pointer to the BSS Entry
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200914 *
915 * @return 0 or -1
916 */
Holger Schurig10078322007-11-15 18:05:47 -0500917static int lbs_process_bss(struct bss_descriptor *bss,
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400918 u8 ** pbeaconinfo, int *bytesleft)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200919{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200920 struct ieeetypes_fhparamset *pFH;
921 struct ieeetypes_dsparamset *pDS;
922 struct ieeetypes_cfparamset *pCF;
923 struct ieeetypes_ibssparamset *pibss;
Joe Perches0795af52007-10-03 17:59:30 -0700924 DECLARE_MAC_BUF(mac);
Dan Williams8c512762007-08-02 11:40:45 -0400925 struct ieeetypes_countryinfoset *pcountryinfo;
926 u8 *pos, *end, *p;
927 u8 n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;
928 u16 beaconsize = 0;
Holger Schurig9012b282007-05-25 11:27:16 -0400929 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200930
Holger Schurige56188a2007-10-09 14:15:19 +0200931 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200932
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200933 if (*bytesleft >= sizeof(beaconsize)) {
934 /* Extract & convert beacon size from the command buffer */
Vladimir Davydovac630c22007-09-06 21:45:36 -0400935 beaconsize = le16_to_cpu(get_unaligned((u16 *)*pbeaconinfo));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200936 *bytesleft -= sizeof(beaconsize);
937 *pbeaconinfo += sizeof(beaconsize);
938 }
939
940 if (beaconsize == 0 || beaconsize > *bytesleft) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200941 *pbeaconinfo += *bytesleft;
942 *bytesleft = 0;
Holger Schurige56188a2007-10-09 14:15:19 +0200943 ret = -1;
944 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200945 }
946
947 /* Initialize the current working beacon pointer for this BSS iteration */
Dan Williamsab617972007-08-02 10:48:02 -0400948 pos = *pbeaconinfo;
949 end = pos + beaconsize;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200950
951 /* Advance the return beacon pointer past the current beacon */
952 *pbeaconinfo += beaconsize;
953 *bytesleft -= beaconsize;
954
Dan Williamsab617972007-08-02 10:48:02 -0400955 memcpy(bss->bssid, pos, ETH_ALEN);
Joe Perches0795af52007-10-03 17:59:30 -0700956 lbs_deb_scan("process_bss: AP BSSID %s\n", print_mac(mac, bss->bssid));
Dan Williamsab617972007-08-02 10:48:02 -0400957 pos += ETH_ALEN;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200958
Dan Williamsab617972007-08-02 10:48:02 -0400959 if ((end - pos) < 12) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400960 lbs_deb_scan("process_bss: Not enough bytes left\n");
Holger Schurige56188a2007-10-09 14:15:19 +0200961 ret = -1;
962 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200963 }
964
965 /*
966 * next 4 fields are RSSI, time stamp, beacon interval,
967 * and capability information
968 */
969
970 /* RSSI is 1 byte long */
Dan Williamsab617972007-08-02 10:48:02 -0400971 bss->rssi = *pos;
972 lbs_deb_scan("process_bss: RSSI=%02X\n", *pos);
973 pos++;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200974
975 /* time stamp is 8 bytes long */
Dan Williamsab617972007-08-02 10:48:02 -0400976 pos += 8;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200977
978 /* beacon interval is 2 bytes long */
Dan Williamsab617972007-08-02 10:48:02 -0400979 bss->beaconperiod = le16_to_cpup((void *) pos);
980 pos += 2;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200981
982 /* capability information is 2 bytes long */
Dan Williamsab617972007-08-02 10:48:02 -0400983 bss->capability = le16_to_cpup((void *) pos);
Dan Williams0c9ca692007-08-02 10:43:44 -0400984 lbs_deb_scan("process_bss: capabilities = 0x%4X\n", bss->capability);
Dan Williamsab617972007-08-02 10:48:02 -0400985 pos += 2;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200986
Dan Williams0c9ca692007-08-02 10:43:44 -0400987 if (bss->capability & WLAN_CAPABILITY_PRIVACY)
988 lbs_deb_scan("process_bss: AP WEP enabled\n");
989 if (bss->capability & WLAN_CAPABILITY_IBSS)
990 bss->mode = IW_MODE_ADHOC;
991 else
992 bss->mode = IW_MODE_INFRA;
993
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200994 /* rest of the current buffer are IE's */
Dan Williamsab617972007-08-02 10:48:02 -0400995 lbs_deb_scan("process_bss: IE length for this AP = %zd\n", end - pos);
Holger Schurigece56192007-08-02 11:53:06 -0400996 lbs_deb_hex(LBS_DEB_SCAN, "process_bss: IE info", pos, end - pos);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200997
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200998 /* process variable IE */
Dan Williamsab617972007-08-02 10:48:02 -0400999 while (pos <= end - 2) {
1000 struct ieee80211_info_element * elem =
1001 (struct ieee80211_info_element *) pos;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001002
Dan Williamsab617972007-08-02 10:48:02 -04001003 if (pos + elem->len > end) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001004 lbs_deb_scan("process_bss: error in processing IE, "
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001005 "bytes left < IE length\n");
Dan Williamsab617972007-08-02 10:48:02 -04001006 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001007 }
1008
Dan Williamsab617972007-08-02 10:48:02 -04001009 switch (elem->id) {
1010 case MFIE_TYPE_SSID:
1011 bss->ssid_len = elem->len;
1012 memcpy(bss->ssid, elem->data, elem->len);
Dan Williamsd8efea22007-05-28 23:54:55 -04001013 lbs_deb_scan("ssid '%s', ssid length %u\n",
1014 escape_essid(bss->ssid, bss->ssid_len),
1015 bss->ssid_len);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001016 break;
1017
Dan Williamsab617972007-08-02 10:48:02 -04001018 case MFIE_TYPE_RATES:
Dan Williams8c512762007-08-02 11:40:45 -04001019 n_basic_rates = min_t(u8, MAX_RATES, elem->len);
1020 memcpy(bss->rates, elem->data, n_basic_rates);
1021 got_basic_rates = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001022 break;
1023
Dan Williamsab617972007-08-02 10:48:02 -04001024 case MFIE_TYPE_FH_SET:
1025 pFH = (struct ieeetypes_fhparamset *) pos;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001026 memmove(&bss->phyparamset.fhparamset, pFH,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001027 sizeof(struct ieeetypes_fhparamset));
David Woodhouse981f1872007-05-25 23:36:54 -04001028#if 0 /* I think we can store these LE */
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001029 bss->phyparamset.fhparamset.dwelltime
1030 = le16_to_cpu(bss->phyparamset.fhparamset.dwelltime);
David Woodhouse981f1872007-05-25 23:36:54 -04001031#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001032 break;
1033
Dan Williamsab617972007-08-02 10:48:02 -04001034 case MFIE_TYPE_DS_SET:
1035 pDS = (struct ieeetypes_dsparamset *) pos;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001036 bss->channel = pDS->currentchan;
1037 memcpy(&bss->phyparamset.dsparamset, pDS,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001038 sizeof(struct ieeetypes_dsparamset));
1039 break;
1040
Dan Williamsab617972007-08-02 10:48:02 -04001041 case MFIE_TYPE_CF_SET:
1042 pCF = (struct ieeetypes_cfparamset *) pos;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001043 memcpy(&bss->ssparamset.cfparamset, pCF,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001044 sizeof(struct ieeetypes_cfparamset));
1045 break;
1046
Dan Williamsab617972007-08-02 10:48:02 -04001047 case MFIE_TYPE_IBSS_SET:
1048 pibss = (struct ieeetypes_ibssparamset *) pos;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001049 bss->atimwindow = le32_to_cpu(pibss->atimwindow);
1050 memmove(&bss->ssparamset.ibssparamset, pibss,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001051 sizeof(struct ieeetypes_ibssparamset));
David Woodhouse981f1872007-05-25 23:36:54 -04001052#if 0
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001053 bss->ssparamset.ibssparamset.atimwindow
1054 = le16_to_cpu(bss->ssparamset.ibssparamset.atimwindow);
David Woodhouse981f1872007-05-25 23:36:54 -04001055#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001056 break;
1057
Dan Williamsab617972007-08-02 10:48:02 -04001058 case MFIE_TYPE_COUNTRY:
1059 pcountryinfo = (struct ieeetypes_countryinfoset *) pos;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001060 if (pcountryinfo->len < sizeof(pcountryinfo->countrycode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001061 || pcountryinfo->len > 254) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001062 lbs_deb_scan("process_bss: 11D- Err "
Dan Williams4269e2a2007-05-10 23:10:18 -04001063 "CountryInfo len =%d min=%zd max=254\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001064 pcountryinfo->len,
1065 sizeof(pcountryinfo->countrycode));
Holger Schurig9012b282007-05-25 11:27:16 -04001066 ret = -1;
1067 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001068 }
1069
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001070 memcpy(&bss->countryinfo,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001071 pcountryinfo, pcountryinfo->len + 2);
Holger Schurigece56192007-08-02 11:53:06 -04001072 lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001073 (u8 *) pcountryinfo,
1074 (u32) (pcountryinfo->len + 2));
1075 break;
1076
Dan Williamsab617972007-08-02 10:48:02 -04001077 case MFIE_TYPE_RATES_EX:
1078 /* only process extended supported rate if data rate is
1079 * already found. Data rate IE should come before
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001080 * extended supported rate IE
1081 */
Dan Williams8c512762007-08-02 11:40:45 -04001082 if (!got_basic_rates)
Dan Williamsab617972007-08-02 10:48:02 -04001083 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001084
Dan Williams8c512762007-08-02 11:40:45 -04001085 n_ex_rates = elem->len;
1086 if (n_basic_rates + n_ex_rates > MAX_RATES)
1087 n_ex_rates = MAX_RATES - n_basic_rates;
Dan Williamsab617972007-08-02 10:48:02 -04001088
Dan Williams8c512762007-08-02 11:40:45 -04001089 p = bss->rates + n_basic_rates;
1090 memcpy(p, elem->data, n_ex_rates);
Dan Williamsab617972007-08-02 10:48:02 -04001091 break;
1092
1093 case MFIE_TYPE_GENERIC:
1094 if (elem->len >= 4 &&
1095 elem->data[0] == 0x00 &&
1096 elem->data[1] == 0x50 &&
1097 elem->data[2] == 0xf2 &&
1098 elem->data[3] == 0x01) {
1099 bss->wpa_ie_len = min(elem->len + 2,
1100 MAX_WPA_IE_LEN);
1101 memcpy(bss->wpa_ie, elem, bss->wpa_ie_len);
Holger Schurigece56192007-08-02 11:53:06 -04001102 lbs_deb_hex(LBS_DEB_SCAN, "process_bss: WPA IE", bss->wpa_ie,
Dan Williamsab617972007-08-02 10:48:02 -04001103 elem->len);
Luis Carlos Cobo1e838bf2007-08-02 10:51:27 -04001104 } else if (elem->len >= MARVELL_MESH_IE_LENGTH &&
1105 elem->data[0] == 0x00 &&
1106 elem->data[1] == 0x50 &&
1107 elem->data[2] == 0x43 &&
1108 elem->data[3] == 0x04) {
1109 bss->mesh = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001110 }
1111 break;
1112
Dan Williamsab617972007-08-02 10:48:02 -04001113 case MFIE_TYPE_RSN:
1114 bss->rsn_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN);
1115 memcpy(bss->rsn_ie, elem, bss->rsn_ie_len);
Holger Schurigece56192007-08-02 11:53:06 -04001116 lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE", bss->rsn_ie, elem->len);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001117 break;
1118
Dan Williamsab617972007-08-02 10:48:02 -04001119 default:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001120 break;
1121 }
1122
Dan Williamsab617972007-08-02 10:48:02 -04001123 pos += elem->len + 2;
1124 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001125
1126 /* Timestamp */
1127 bss->last_scanned = jiffies;
Holger Schurig10078322007-11-15 18:05:47 -05001128 lbs_unset_basic_rate_flags(bss->rates, sizeof(bss->rates));
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001129
Holger Schurig9012b282007-05-25 11:27:16 -04001130 ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001131
Holger Schurig9012b282007-05-25 11:27:16 -04001132done:
1133 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
1134 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001135}
1136
1137/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001138 * @brief This function finds a specific compatible BSSID in the scan list
1139 *
Holger Schurige56188a2007-10-09 14:15:19 +02001140 * Used in association code
1141 *
Holger Schurig69f90322007-11-23 15:43:44 +01001142 * @param adapter A pointer to struct lbs_adapter
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001143 * @param bssid BSSID to find in the scan list
1144 * @param mode Network mode: Infrastructure or IBSS
1145 *
1146 * @return index in BSSID list, or error return code (< 0)
1147 */
Holger Schurig69f90322007-11-23 15:43:44 +01001148struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_adapter *adapter,
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001149 u8 * bssid, u8 mode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001150{
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001151 struct bss_descriptor * iter_bss;
1152 struct bss_descriptor * found_bss = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001153
Holger Schurige56188a2007-10-09 14:15:19 +02001154 lbs_deb_enter(LBS_DEB_SCAN);
1155
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001156 if (!bssid)
Holger Schurige56188a2007-10-09 14:15:19 +02001157 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001158
Holger Schurigece56192007-08-02 11:53:06 -04001159 lbs_deb_hex(LBS_DEB_SCAN, "looking for",
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001160 bssid, ETH_ALEN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001161
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001162 /* Look through the scan table for a compatible match. The loop will
1163 * continue past a matched bssid that is not compatible in case there
1164 * is an AP with multiple SSIDs assigned to the same BSSID
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001165 */
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001166 mutex_lock(&adapter->lock);
1167 list_for_each_entry (iter_bss, &adapter->network_list, list) {
Dan Williams3cf209312007-05-25 17:28:30 -04001168 if (compare_ether_addr(iter_bss->bssid, bssid))
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001169 continue; /* bssid doesn't match */
1170 switch (mode) {
1171 case IW_MODE_INFRA:
1172 case IW_MODE_ADHOC:
1173 if (!is_network_compatible(adapter, iter_bss, mode))
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001174 break;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001175 found_bss = iter_bss;
1176 break;
1177 default:
1178 found_bss = iter_bss;
1179 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001180 }
1181 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001182 mutex_unlock(&adapter->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001183
Holger Schurige56188a2007-10-09 14:15:19 +02001184out:
1185 lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001186 return found_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001187}
1188
1189/**
1190 * @brief This function finds ssid in ssid list.
1191 *
Holger Schurige56188a2007-10-09 14:15:19 +02001192 * Used in association code
1193 *
Holger Schurig69f90322007-11-23 15:43:44 +01001194 * @param adapter A pointer to struct lbs_adapter
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001195 * @param ssid SSID to find in the list
1196 * @param bssid BSSID to qualify the SSID selection (if provided)
1197 * @param mode Network mode: Infrastructure or IBSS
1198 *
1199 * @return index in BSSID list
1200 */
Holger Schurig69f90322007-11-23 15:43:44 +01001201struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_adapter *adapter,
Dan Williamsd8efea22007-05-28 23:54:55 -04001202 u8 *ssid, u8 ssid_len, u8 * bssid, u8 mode,
Dan Williamsaeea0ab2007-05-25 22:30:48 -04001203 int channel)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001204{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001205 u8 bestrssi = 0;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001206 struct bss_descriptor * iter_bss = NULL;
1207 struct bss_descriptor * found_bss = NULL;
1208 struct bss_descriptor * tmp_oldest = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001209
Holger Schurige56188a2007-10-09 14:15:19 +02001210 lbs_deb_enter(LBS_DEB_SCAN);
1211
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001212 mutex_lock(&adapter->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001213
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001214 list_for_each_entry (iter_bss, &adapter->network_list, list) {
1215 if ( !tmp_oldest
1216 || (iter_bss->last_scanned < tmp_oldest->last_scanned))
1217 tmp_oldest = iter_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001218
Holger Schurig10078322007-11-15 18:05:47 -05001219 if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len,
Dan Williamsd8efea22007-05-28 23:54:55 -04001220 ssid, ssid_len) != 0)
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001221 continue; /* ssid doesn't match */
Dan Williams3cf209312007-05-25 17:28:30 -04001222 if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0)
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001223 continue; /* bssid doesn't match */
Dan Williamsaeea0ab2007-05-25 22:30:48 -04001224 if ((channel > 0) && (iter_bss->channel != channel))
1225 continue; /* channel doesn't match */
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001226
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001227 switch (mode) {
1228 case IW_MODE_INFRA:
1229 case IW_MODE_ADHOC:
1230 if (!is_network_compatible(adapter, iter_bss, mode))
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001231 break;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001232
1233 if (bssid) {
1234 /* Found requested BSSID */
1235 found_bss = iter_bss;
1236 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001237 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001238
1239 if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
1240 bestrssi = SCAN_RSSI(iter_bss->rssi);
1241 found_bss = iter_bss;
1242 }
1243 break;
1244 case IW_MODE_AUTO:
1245 default:
1246 if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
1247 bestrssi = SCAN_RSSI(iter_bss->rssi);
1248 found_bss = iter_bss;
1249 }
1250 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001251 }
1252 }
1253
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001254out:
1255 mutex_unlock(&adapter->lock);
Holger Schurige56188a2007-10-09 14:15:19 +02001256 lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001257 return found_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001258}
1259
1260/**
1261 * @brief This function finds the best SSID in the Scan List
1262 *
1263 * Search the scan table for the best SSID that also matches the current
1264 * adapter network preference (infrastructure or adhoc)
1265 *
Holger Schurig69f90322007-11-23 15:43:44 +01001266 * @param adapter A pointer to struct lbs_adapter
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001267 *
1268 * @return index in BSSID list
1269 */
Holger Schurig69f90322007-11-23 15:43:44 +01001270static struct bss_descriptor *lbs_find_best_ssid_in_list(
1271 struct lbs_adapter *adapter,
1272 u8 mode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001273{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001274 u8 bestrssi = 0;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001275 struct bss_descriptor * iter_bss;
1276 struct bss_descriptor * best_bss = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001277
Holger Schurige56188a2007-10-09 14:15:19 +02001278 lbs_deb_enter(LBS_DEB_SCAN);
1279
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001280 mutex_lock(&adapter->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001281
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001282 list_for_each_entry (iter_bss, &adapter->network_list, list) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001283 switch (mode) {
Dan Williams0dc5a292007-05-10 22:58:02 -04001284 case IW_MODE_INFRA:
1285 case IW_MODE_ADHOC:
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001286 if (!is_network_compatible(adapter, iter_bss, mode))
1287 break;
1288 if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
1289 break;
1290 bestrssi = SCAN_RSSI(iter_bss->rssi);
1291 best_bss = iter_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001292 break;
Dan Williams0dc5a292007-05-10 22:58:02 -04001293 case IW_MODE_AUTO:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001294 default:
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001295 if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
1296 break;
1297 bestrssi = SCAN_RSSI(iter_bss->rssi);
1298 best_bss = iter_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001299 break;
1300 }
1301 }
1302
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001303 mutex_unlock(&adapter->lock);
Holger Schurige56188a2007-10-09 14:15:19 +02001304 lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001305 return best_bss;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001306}
1307
1308/**
1309 * @brief Find the AP with specific ssid in the scan list
1310 *
Holger Schurige56188a2007-10-09 14:15:19 +02001311 * Used from association worker.
1312 *
Holger Schurig69f90322007-11-23 15:43:44 +01001313 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001314 * @param pSSID A pointer to AP's ssid
1315 *
1316 * @return 0--success, otherwise--fail
1317 */
Holger Schurig69f90322007-11-23 15:43:44 +01001318int lbs_find_best_network_ssid(struct lbs_private *priv,
Dan Williamsd8efea22007-05-28 23:54:55 -04001319 u8 *out_ssid, u8 *out_ssid_len, u8 preferred_mode, u8 *out_mode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001320{
Holger Schurig69f90322007-11-23 15:43:44 +01001321 struct lbs_adapter *adapter = priv->adapter;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001322 int ret = -1;
1323 struct bss_descriptor * found;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001324
Holger Schurige56188a2007-10-09 14:15:19 +02001325 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001326
Holger Schurig10078322007-11-15 18:05:47 -05001327 lbs_scan_networks(priv, NULL, 1);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001328 if (adapter->surpriseremoved)
Holger Schurige56188a2007-10-09 14:15:19 +02001329 goto out;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001330
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001331 wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
1332
Holger Schurig10078322007-11-15 18:05:47 -05001333 found = lbs_find_best_ssid_in_list(adapter, preferred_mode);
Dan Williamsd8efea22007-05-28 23:54:55 -04001334 if (found && (found->ssid_len > 0)) {
1335 memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE);
1336 *out_ssid_len = found->ssid_len;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001337 *out_mode = found->mode;
1338 ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001339 }
1340
Holger Schurige56188a2007-10-09 14:15:19 +02001341out:
Holger Schurig9012b282007-05-25 11:27:16 -04001342 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001343 return ret;
1344}
1345
1346/**
1347 * @brief Scan Network
1348 *
1349 * @param dev A pointer to net_device structure
1350 * @param info A pointer to iw_request_info structure
1351 * @param vwrq A pointer to iw_param structure
1352 * @param extra A pointer to extra data buf
1353 *
1354 * @return 0 --success, otherwise fail
1355 */
Holger Schurig10078322007-11-15 18:05:47 -05001356int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001357 struct iw_param *vwrq, char *extra)
1358{
Holger Schurig69f90322007-11-23 15:43:44 +01001359 struct lbs_private *priv = dev->priv;
1360 struct lbs_adapter *adapter = priv->adapter;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001361
Holger Schurig9012b282007-05-25 11:27:16 -04001362 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001363
Dan Williams2afc0c52007-08-02 13:19:04 -04001364 if (!delayed_work_pending(&priv->scan_work)) {
1365 queue_delayed_work(priv->work_thread, &priv->scan_work,
1366 msecs_to_jiffies(50));
1367 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001368
1369 if (adapter->surpriseremoved)
1370 return -1;
1371
Holger Schurig9012b282007-05-25 11:27:16 -04001372 lbs_deb_leave(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001373 return 0;
1374}
1375
Holger Schurige56188a2007-10-09 14:15:19 +02001376
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001377/**
1378 * @brief Send a scan command for all available channels filtered on a spec
1379 *
Holger Schurige56188a2007-10-09 14:15:19 +02001380 * Used in association code and from debugfs
1381 *
Holger Schurig69f90322007-11-23 15:43:44 +01001382 * @param priv A pointer to struct lbs_private structure
Holger Schurige56188a2007-10-09 14:15:19 +02001383 * @param ssid A pointer to the SSID to scan for
1384 * @param ssid_len Length of the SSID
1385 * @param clear_ssid Should existing scan results with this SSID
1386 * be cleared?
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001387 * @param prequestedssid A pointer to AP's ssid
1388 * @param keeppreviousscan Flag used to save/clear scan table before scan
1389 *
1390 * @return 0-success, otherwise fail
1391 */
Holger Schurig69f90322007-11-23 15:43:44 +01001392int lbs_send_specific_ssid_scan(struct lbs_private *priv,
Dan Williamsd8efea22007-05-28 23:54:55 -04001393 u8 *ssid, u8 ssid_len, u8 clear_ssid)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001394{
Holger Schurig69f90322007-11-23 15:43:44 +01001395 struct lbs_adapter *adapter = priv->adapter;
Holger Schurig10078322007-11-15 18:05:47 -05001396 struct lbs_ioctl_user_scan_cfg scancfg;
Dan Williamseb8f7332007-05-25 16:25:21 -04001397 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001398
Holger Schurige56188a2007-10-09 14:15:19 +02001399 lbs_deb_enter_args(LBS_DEB_SCAN, "SSID '%s', clear %d",
1400 escape_essid(ssid, ssid_len), clear_ssid);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001401
Dan Williamsd8efea22007-05-28 23:54:55 -04001402 if (!ssid_len)
Dan Williamseb8f7332007-05-25 16:25:21 -04001403 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001404
1405 memset(&scancfg, 0x00, sizeof(scancfg));
Dan Williamsd8efea22007-05-28 23:54:55 -04001406 memcpy(scancfg.ssid, ssid, ssid_len);
1407 scancfg.ssid_len = ssid_len;
Dan Williamseb8f7332007-05-25 16:25:21 -04001408 scancfg.clear_ssid = clear_ssid;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001409
Holger Schurig10078322007-11-15 18:05:47 -05001410 lbs_scan_networks(priv, &scancfg, 1);
Holger Schurige56188a2007-10-09 14:15:19 +02001411 if (adapter->surpriseremoved) {
1412 ret = -1;
1413 goto out;
1414 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001415 wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
1416
Dan Williamseb8f7332007-05-25 16:25:21 -04001417out:
Holger Schurige56188a2007-10-09 14:15:19 +02001418 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
Dan Williamseb8f7332007-05-25 16:25:21 -04001419 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001420}
1421
Holger Schurige56188a2007-10-09 14:15:19 +02001422
1423
1424
1425/*********************************************************************/
1426/* */
1427/* Support for Wireless Extensions */
1428/* */
1429/*********************************************************************/
1430
Dan Williams00af0152007-08-02 13:14:56 -04001431#define MAX_CUSTOM_LEN 64
1432
Holger Schurig69f90322007-11-23 15:43:44 +01001433static inline char *lbs_translate_scan(struct lbs_private *priv,
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001434 char *start, char *stop,
1435 struct bss_descriptor *bss)
1436{
Holger Schurig69f90322007-11-23 15:43:44 +01001437 struct lbs_adapter *adapter = priv->adapter;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001438 struct chan_freq_power *cfp;
1439 char *current_val; /* For rates */
1440 struct iw_event iwe; /* Temporary buffer */
1441 int j;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001442#define PERFECT_RSSI ((u8)50)
1443#define WORST_RSSI ((u8)0)
1444#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI))
1445 u8 rssi;
1446
Holger Schurige56188a2007-10-09 14:15:19 +02001447 lbs_deb_enter(LBS_DEB_SCAN);
1448
Holger Schurig10078322007-11-15 18:05:47 -05001449 cfp = lbs_find_cfp_by_band_and_channel(adapter, 0, bss->channel);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001450 if (!cfp) {
1451 lbs_deb_scan("Invalid channel number %d\n", bss->channel);
Holger Schurige56188a2007-10-09 14:15:19 +02001452 start = NULL;
1453 goto out;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001454 }
1455
1456 /* First entry *MUST* be the AP BSSID */
1457 iwe.cmd = SIOCGIWAP;
1458 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1459 memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN);
1460 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
1461
1462 /* SSID */
1463 iwe.cmd = SIOCGIWESSID;
1464 iwe.u.data.flags = 1;
Dan Williamsd8efea22007-05-28 23:54:55 -04001465 iwe.u.data.length = min((u32) bss->ssid_len, (u32) IW_ESSID_MAX_SIZE);
1466 start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001467
1468 /* Mode */
1469 iwe.cmd = SIOCGIWMODE;
1470 iwe.u.mode = bss->mode;
1471 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
1472
1473 /* Frequency */
1474 iwe.cmd = SIOCGIWFREQ;
1475 iwe.u.freq.m = (long)cfp->freq * 100000;
1476 iwe.u.freq.e = 1;
1477 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
1478
1479 /* Add quality statistics */
1480 iwe.cmd = IWEVQUAL;
1481 iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
1482 iwe.u.qual.level = SCAN_RSSI(bss->rssi);
1483
1484 rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE;
1485 iwe.u.qual.qual =
1486 (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) *
1487 (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) /
1488 (RSSI_DIFF * RSSI_DIFF);
1489 if (iwe.u.qual.qual > 100)
1490 iwe.u.qual.qual = 100;
1491
1492 if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
1493 iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
1494 } else {
1495 iwe.u.qual.noise =
1496 CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
1497 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001498
Dan Williams80e78ef2007-05-25 22:18:47 -04001499 /* Locally created ad-hoc BSSs won't have beacons if this is the
1500 * only station in the adhoc network; so get signal strength
1501 * from receive statistics.
1502 */
1503 if ((adapter->mode == IW_MODE_ADHOC)
1504 && adapter->adhoccreate
Holger Schurig10078322007-11-15 18:05:47 -05001505 && !lbs_ssid_cmp(adapter->curbssparams.ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -04001506 adapter->curbssparams.ssid_len,
1507 bss->ssid, bss->ssid_len)) {
Dan Williams80e78ef2007-05-25 22:18:47 -04001508 int snr, nf;
1509 snr = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
1510 nf = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
1511 iwe.u.qual.level = CAL_RSSI(snr, nf);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001512 }
1513 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
1514
1515 /* Add encryption capability */
1516 iwe.cmd = SIOCGIWENCODE;
Dan Williams0c9ca692007-08-02 10:43:44 -04001517 if (bss->capability & WLAN_CAPABILITY_PRIVACY) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001518 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1519 } else {
1520 iwe.u.data.flags = IW_ENCODE_DISABLED;
1521 }
1522 iwe.u.data.length = 0;
Dan Williamsd8efea22007-05-28 23:54:55 -04001523 start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001524
1525 current_val = start + IW_EV_LCP_LEN;
1526
1527 iwe.cmd = SIOCGIWRATE;
1528 iwe.u.bitrate.fixed = 0;
1529 iwe.u.bitrate.disabled = 0;
1530 iwe.u.bitrate.value = 0;
1531
Dan Williams8c512762007-08-02 11:40:45 -04001532 for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) {
1533 /* Bit rate given in 500 kb/s units */
1534 iwe.u.bitrate.value = bss->rates[j] * 500000;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001535 current_val = iwe_stream_add_value(start, current_val,
1536 stop, &iwe, IW_EV_PARAM_LEN);
1537 }
1538 if ((bss->mode == IW_MODE_ADHOC)
Holger Schurig10078322007-11-15 18:05:47 -05001539 && !lbs_ssid_cmp(adapter->curbssparams.ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -04001540 adapter->curbssparams.ssid_len,
1541 bss->ssid, bss->ssid_len)
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001542 && adapter->adhoccreate) {
1543 iwe.u.bitrate.value = 22 * 500000;
1544 current_val = iwe_stream_add_value(start, current_val,
1545 stop, &iwe, IW_EV_PARAM_LEN);
1546 }
1547 /* Check if we added any event */
1548 if((current_val - start) > IW_EV_LCP_LEN)
1549 start = current_val;
1550
1551 memset(&iwe, 0, sizeof(iwe));
1552 if (bss->wpa_ie_len) {
1553 char buf[MAX_WPA_IE_LEN];
1554 memcpy(buf, bss->wpa_ie, bss->wpa_ie_len);
1555 iwe.cmd = IWEVGENIE;
1556 iwe.u.data.length = bss->wpa_ie_len;
1557 start = iwe_stream_add_point(start, stop, &iwe, buf);
1558 }
1559
1560 memset(&iwe, 0, sizeof(iwe));
1561 if (bss->rsn_ie_len) {
1562 char buf[MAX_WPA_IE_LEN];
1563 memcpy(buf, bss->rsn_ie, bss->rsn_ie_len);
1564 iwe.cmd = IWEVGENIE;
1565 iwe.u.data.length = bss->rsn_ie_len;
1566 start = iwe_stream_add_point(start, stop, &iwe, buf);
1567 }
1568
Dan Williams00af0152007-08-02 13:14:56 -04001569 if (bss->mesh) {
1570 char custom[MAX_CUSTOM_LEN];
1571 char *p = custom;
1572
1573 iwe.cmd = IWEVCUSTOM;
1574 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
1575 "mesh-type: olpc");
1576 iwe.u.data.length = p - custom;
1577 if (iwe.u.data.length)
1578 start = iwe_stream_add_point(start, stop, &iwe, custom);
1579 }
1580
Holger Schurige56188a2007-10-09 14:15:19 +02001581out:
1582 lbs_deb_leave_args(LBS_DEB_SCAN, "start %p", start);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001583 return start;
1584}
1585
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001586/**
Holger Schurige56188a2007-10-09 14:15:19 +02001587 * @brief Handle Retrieve scan table ioctl
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001588 *
1589 * @param dev A pointer to net_device structure
1590 * @param info A pointer to iw_request_info structure
1591 * @param dwrq A pointer to iw_point structure
1592 * @param extra A pointer to extra data buf
1593 *
1594 * @return 0 --success, otherwise fail
1595 */
Holger Schurig10078322007-11-15 18:05:47 -05001596int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001597 struct iw_point *dwrq, char *extra)
1598{
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001599#define SCAN_ITEM_SIZE 128
Holger Schurig69f90322007-11-23 15:43:44 +01001600 struct lbs_private *priv = dev->priv;
1601 struct lbs_adapter *adapter = priv->adapter;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001602 int err = 0;
1603 char *ev = extra;
1604 char *stop = ev + dwrq->length;
1605 struct bss_descriptor * iter_bss;
1606 struct bss_descriptor * safe;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001607
Holger Schurige56188a2007-10-09 14:15:19 +02001608 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001609
Dan Williams80e78ef2007-05-25 22:18:47 -04001610 /* Update RSSI if current BSS is a locally created ad-hoc BSS */
Dan Williamsaeea0ab2007-05-25 22:30:48 -04001611 if ((adapter->mode == IW_MODE_ADHOC) && adapter->adhoccreate) {
Holger Schurig10078322007-11-15 18:05:47 -05001612 lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
Dan Williams0aef64d2007-08-02 11:31:18 -04001613 CMD_OPTION_WAITFORRSP, 0, NULL);
Dan Williams80e78ef2007-05-25 22:18:47 -04001614 }
1615
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001616 mutex_lock(&adapter->lock);
1617 list_for_each_entry_safe (iter_bss, safe, &adapter->network_list, list) {
1618 char * next_ev;
1619 unsigned long stale_time;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001620
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001621 if (stop - ev < SCAN_ITEM_SIZE) {
1622 err = -E2BIG;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001623 break;
1624 }
1625
Luis Carlos Cobo1e838bf2007-08-02 10:51:27 -04001626 /* For mesh device, list only mesh networks */
1627 if (dev == priv->mesh_dev && !iter_bss->mesh)
1628 continue;
1629
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001630 /* Prune old an old scan result */
1631 stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
1632 if (time_after(jiffies, stale_time)) {
1633 list_move_tail (&iter_bss->list,
1634 &adapter->network_free_list);
1635 clear_bss_descriptor(iter_bss);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001636 continue;
1637 }
1638
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001639 /* Translate to WE format this entry */
Holger Schurig10078322007-11-15 18:05:47 -05001640 next_ev = lbs_translate_scan(priv, ev, stop, iter_bss);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001641 if (next_ev == NULL)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001642 continue;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001643 ev = next_ev;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001644 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001645 mutex_unlock(&adapter->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001646
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001647 dwrq->length = (ev - extra);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001648 dwrq->flags = 0;
1649
Holger Schurige56188a2007-10-09 14:15:19 +02001650 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", err);
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001651 return err;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001652}
1653
Holger Schurige56188a2007-10-09 14:15:19 +02001654
1655
1656
1657/*********************************************************************/
1658/* */
1659/* Command execution */
1660/* */
1661/*********************************************************************/
1662
1663
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001664/**
1665 * @brief Prepare a scan command to be sent to the firmware
1666 *
Holger Schurig10078322007-11-15 18:05:47 -05001667 * Called from lbs_prepare_and_send_command() in cmd.c
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001668 *
Holger Schurige56188a2007-10-09 14:15:19 +02001669 * Sends a fixed lenght data part (specifying the BSS type and BSSID filters)
1670 * as well as a variable number/length of TLVs to the firmware.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001671 *
Holger Schurig69f90322007-11-23 15:43:44 +01001672 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001673 * @param cmd A pointer to cmd_ds_command structure to be sent to
1674 * firmware with the cmd_DS_801_11_SCAN structure
Holger Schurig10078322007-11-15 18:05:47 -05001675 * @param pdata_buf Void pointer cast of a lbs_scan_cmd_config struct used
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001676 * to set the fields/TLVs for the command sent to firmware
1677 *
1678 * @return 0 or -1
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001679 */
Holger Schurig69f90322007-11-23 15:43:44 +01001680int lbs_cmd_80211_scan(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001681 struct cmd_ds_command *cmd, void *pdata_buf)
1682{
1683 struct cmd_ds_802_11_scan *pscan = &cmd->params.scan;
Holger Schurig10078322007-11-15 18:05:47 -05001684 struct lbs_scan_cmd_config *pscancfg = pdata_buf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001685
Holger Schurige56188a2007-10-09 14:15:19 +02001686 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001687
1688 /* Set fixed field variables in scan command */
1689 pscan->bsstype = pscancfg->bsstype;
Dan Williams492b6da2007-08-02 11:16:07 -04001690 memcpy(pscan->bssid, pscancfg->bssid, ETH_ALEN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001691 memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen);
1692
Dan Williams0aef64d2007-08-02 11:31:18 -04001693 cmd->command = cpu_to_le16(CMD_802_11_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001694
1695 /* size is equal to the sizeof(fixed portions) + the TLV len + header */
Dan Williams492b6da2007-08-02 11:16:07 -04001696 cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + ETH_ALEN
1697 + pscancfg->tlvbufferlen + S_DS_GEN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001698
Holger Schurige56188a2007-10-09 14:15:19 +02001699 lbs_deb_scan("SCAN_CMD: command 0x%04x, size %d, seqnum %d\n",
David Woodhouse981f1872007-05-25 23:36:54 -04001700 le16_to_cpu(cmd->command), le16_to_cpu(cmd->size),
1701 le16_to_cpu(cmd->seqnum));
Holger Schurig9012b282007-05-25 11:27:16 -04001702
Holger Schurige56188a2007-10-09 14:15:19 +02001703 lbs_deb_leave(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001704 return 0;
1705}
1706
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001707static inline int is_same_network(struct bss_descriptor *src,
1708 struct bss_descriptor *dst)
1709{
1710 /* A network is only a duplicate if the channel, BSSID, and ESSID
1711 * all match. We treat all <hidden> with the same BSSID and channel
1712 * as one network */
Dan Williamsd8efea22007-05-28 23:54:55 -04001713 return ((src->ssid_len == dst->ssid_len) &&
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001714 (src->channel == dst->channel) &&
1715 !compare_ether_addr(src->bssid, dst->bssid) &&
Dan Williamsd8efea22007-05-28 23:54:55 -04001716 !memcmp(src->ssid, dst->ssid, src->ssid_len));
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001717}
1718
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001719/**
1720 * @brief This function handles the command response of scan
1721 *
Holger Schurige56188a2007-10-09 14:15:19 +02001722 * Called from handle_cmd_response() in cmdrespc.
1723 *
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001724 * The response buffer for the scan command has the following
1725 * memory layout:
1726 *
1727 * .-----------------------------------------------------------.
1728 * | header (4 * sizeof(u16)): Standard command response hdr |
1729 * .-----------------------------------------------------------.
1730 * | bufsize (u16) : sizeof the BSS Description data |
1731 * .-----------------------------------------------------------.
1732 * | NumOfSet (u8) : Number of BSS Descs returned |
1733 * .-----------------------------------------------------------.
1734 * | BSSDescription data (variable, size given in bufsize) |
1735 * .-----------------------------------------------------------.
1736 * | TLV data (variable, size calculated using header->size, |
1737 * | bufsize and sizeof the fixed fields above) |
1738 * .-----------------------------------------------------------.
1739 *
Holger Schurig69f90322007-11-23 15:43:44 +01001740 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001741 * @param resp A pointer to cmd_ds_command
1742 *
1743 * @return 0 or -1
1744 */
Holger Schurig69f90322007-11-23 15:43:44 +01001745int lbs_ret_80211_scan(struct lbs_private *priv, struct cmd_ds_command *resp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001746{
Holger Schurig69f90322007-11-23 15:43:44 +01001747 struct lbs_adapter *adapter = priv->adapter;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001748 struct cmd_ds_802_11_scan_rsp *pscan;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001749 struct bss_descriptor * iter_bss;
1750 struct bss_descriptor * safe;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001751 u8 *pbssinfo;
1752 u16 scanrespsize;
1753 int bytesleft;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001754 int idx;
1755 int tlvbufsize;
Holger Schurig9012b282007-05-25 11:27:16 -04001756 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001757
Holger Schurige56188a2007-10-09 14:15:19 +02001758 lbs_deb_enter(LBS_DEB_SCAN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001759
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001760 /* Prune old entries from scan table */
1761 list_for_each_entry_safe (iter_bss, safe, &adapter->network_list, list) {
1762 unsigned long stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
1763 if (time_before(jiffies, stale_time))
1764 continue;
1765 list_move_tail (&iter_bss->list, &adapter->network_free_list);
1766 clear_bss_descriptor(iter_bss);
1767 }
1768
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001769 pscan = &resp->params.scanresp;
1770
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001771 if (pscan->nr_sets > MAX_NETWORK_COUNT) {
1772 lbs_deb_scan(
1773 "SCAN_RESP: too many scan results (%d, max %d)!!\n",
1774 pscan->nr_sets, MAX_NETWORK_COUNT);
Holger Schurig9012b282007-05-25 11:27:16 -04001775 ret = -1;
1776 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001777 }
1778
Vladimir Davydovac630c22007-09-06 21:45:36 -04001779 bytesleft = le16_to_cpu(get_unaligned((u16*)&pscan->bssdescriptsize));
Holger Schurig9012b282007-05-25 11:27:16 -04001780 lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001781
Vladimir Davydovac630c22007-09-06 21:45:36 -04001782 scanrespsize = le16_to_cpu(get_unaligned((u16*)&resp->size));
Holger Schurig9012b282007-05-25 11:27:16 -04001783 lbs_deb_scan("SCAN_RESP: returned %d AP before parsing\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001784 pscan->nr_sets);
1785
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001786 pbssinfo = pscan->bssdesc_and_tlvbuffer;
1787
1788 /* The size of the TLV buffer is equal to the entire command response
1789 * size (scanrespsize) minus the fixed fields (sizeof()'s), the
1790 * BSS Descriptions (bssdescriptsize as bytesLef) and the command
1791 * response header (S_DS_GEN)
1792 */
1793 tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize)
1794 + sizeof(pscan->nr_sets)
1795 + S_DS_GEN);
1796
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001797 /*
1798 * Process each scan response returned (pscan->nr_sets). Save
1799 * the information in the newbssentry and then insert into the
1800 * driver scan table either as an update to an existing entry
1801 * or as an addition at the end of the table
1802 */
1803 for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001804 struct bss_descriptor new;
1805 struct bss_descriptor * found = NULL;
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001806 struct bss_descriptor * oldest = NULL;
Joe Perches0795af52007-10-03 17:59:30 -07001807 DECLARE_MAC_BUF(mac);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001808
1809 /* Process the data fields and IEs returned for this BSS */
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001810 memset(&new, 0, sizeof (struct bss_descriptor));
Holger Schurig10078322007-11-15 18:05:47 -05001811 if (lbs_process_bss(&new, &pbssinfo, &bytesleft) != 0) {
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001812 /* error parsing the scan response, skipped */
1813 lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n");
1814 continue;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001815 }
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001816
1817 /* Try to find this bss in the scan table */
1818 list_for_each_entry (iter_bss, &adapter->network_list, list) {
1819 if (is_same_network(iter_bss, &new)) {
1820 found = iter_bss;
1821 break;
1822 }
1823
1824 if ((oldest == NULL) ||
1825 (iter_bss->last_scanned < oldest->last_scanned))
1826 oldest = iter_bss;
1827 }
1828
1829 if (found) {
1830 /* found, clear it */
1831 clear_bss_descriptor(found);
1832 } else if (!list_empty(&adapter->network_free_list)) {
1833 /* Pull one from the free list */
1834 found = list_entry(adapter->network_free_list.next,
1835 struct bss_descriptor, list);
1836 list_move_tail(&found->list, &adapter->network_list);
1837 } else if (oldest) {
1838 /* If there are no more slots, expire the oldest */
1839 found = oldest;
1840 clear_bss_descriptor(found);
1841 list_move_tail(&found->list, &adapter->network_list);
1842 } else {
1843 continue;
1844 }
1845
Joe Perches0795af52007-10-03 17:59:30 -07001846 lbs_deb_scan("SCAN_RESP: BSSID = %s\n",
1847 print_mac(mac, new.bssid));
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001848
Dan Williamsfcdb53d2007-05-25 16:15:56 -04001849 /* Copy the locally created newbssentry to the scan table */
1850 memcpy(found, &new, offsetof(struct bss_descriptor, list));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001851 }
1852
Holger Schurig9012b282007-05-25 11:27:16 -04001853 ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001854
Holger Schurig9012b282007-05-25 11:27:16 -04001855done:
1856 lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
1857 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001858}