2 * This file is part of wl1271
4 * Copyright (C) 2009-2010 Nokia Corporation
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24 #include <linux/ieee80211.h>
27 #include "wl1271_cmd.h"
28 #include "wl1271_scan.h"
29 #include "wl1271_acx.h"
31 static int wl1271_get_scan_channels(struct wl1271
*wl
,
32 struct cfg80211_scan_request
*req
,
33 struct basic_scan_channel_params
*channels
,
34 enum ieee80211_band band
, bool passive
)
40 i
< req
->n_channels
&& j
< WL1271_SCAN_MAX_CHANNELS
;
43 flags
= req
->channels
[i
]->flags
;
45 if (!wl
->scan
.scanned_ch
[i
] &&
46 !(flags
& IEEE80211_CHAN_DISABLED
) &&
47 ((!!(flags
& IEEE80211_CHAN_PASSIVE_SCAN
)) == passive
) &&
48 (req
->channels
[i
]->band
== band
)) {
50 wl1271_debug(DEBUG_SCAN
, "band %d, center_freq %d ",
51 req
->channels
[i
]->band
,
52 req
->channels
[i
]->center_freq
);
53 wl1271_debug(DEBUG_SCAN
, "hw_value %d, flags %X",
54 req
->channels
[i
]->hw_value
,
55 req
->channels
[i
]->flags
);
56 wl1271_debug(DEBUG_SCAN
,
57 "max_antenna_gain %d, max_power %d",
58 req
->channels
[i
]->max_antenna_gain
,
59 req
->channels
[i
]->max_power
);
60 wl1271_debug(DEBUG_SCAN
, "beacon_found %d",
61 req
->channels
[i
]->beacon_found
);
63 channels
[j
].min_duration
=
64 cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION
);
65 channels
[j
].max_duration
=
66 cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION
);
67 channels
[j
].early_termination
= 0;
68 channels
[j
].tx_power_att
= req
->channels
[i
]->max_power
;
69 channels
[j
].channel
= req
->channels
[i
]->hw_value
;
71 memset(&channels
[j
].bssid_lsb
, 0xff, 4);
72 memset(&channels
[j
].bssid_msb
, 0xff, 2);
74 /* Mark the channels we already used */
75 wl
->scan
.scanned_ch
[i
] = true;
84 #define WL1271_NOTHING_TO_SCAN 1
86 static int wl1271_scan_send(struct wl1271
*wl
, enum ieee80211_band band
,
87 bool passive
, u32 basic_rate
)
89 struct wl1271_cmd_scan
*cmd
;
90 struct wl1271_cmd_trigger_scan_to
*trigger
;
94 cmd
= kzalloc(sizeof(*cmd
), GFP_KERNEL
);
95 trigger
= kzalloc(sizeof(*trigger
), GFP_KERNEL
);
96 if (!cmd
|| !trigger
) {
101 /* We always use high priority scans */
102 scan_options
= WL1271_SCAN_OPT_PRIORITY_HIGH
;
104 scan_options
|= WL1271_SCAN_OPT_PASSIVE
;
105 cmd
->params
.scan_options
= cpu_to_le16(scan_options
);
107 cmd
->params
.n_ch
= wl1271_get_scan_channels(wl
, wl
->scan
.req
,
110 if (cmd
->params
.n_ch
== 0) {
111 ret
= WL1271_NOTHING_TO_SCAN
;
115 cmd
->params
.tx_rate
= cpu_to_le32(basic_rate
);
116 cmd
->params
.rx_config_options
= cpu_to_le32(CFG_RX_ALL_GOOD
);
117 cmd
->params
.rx_filter_options
=
118 cpu_to_le32(CFG_RX_PRSP_EN
| CFG_RX_MGMT_EN
| CFG_RX_BCN_EN
);
120 cmd
->params
.n_probe_reqs
= WL1271_SCAN_PROBE_REQS
;
121 cmd
->params
.tx_rate
= cpu_to_le32(basic_rate
);
122 cmd
->params
.tid_trigger
= 0;
123 cmd
->params
.scan_tag
= WL1271_SCAN_DEFAULT_TAG
;
125 if (band
== IEEE80211_BAND_2GHZ
)
126 cmd
->params
.band
= WL1271_SCAN_BAND_2_4_GHZ
;
128 cmd
->params
.band
= WL1271_SCAN_BAND_5_GHZ
;
130 if (wl
->scan
.ssid_len
&& wl
->scan
.ssid
) {
131 cmd
->params
.ssid_len
= wl
->scan
.ssid_len
;
132 memcpy(cmd
->params
.ssid
, wl
->scan
.ssid
, wl
->scan
.ssid_len
);
135 ret
= wl1271_cmd_build_probe_req(wl
, wl
->scan
.ssid
, wl
->scan
.ssid_len
,
136 wl
->scan
.req
->ie
, wl
->scan
.req
->ie_len
,
139 wl1271_error("PROBE request template failed");
143 /* disable the timeout */
144 trigger
->timeout
= 0;
145 ret
= wl1271_cmd_send(wl
, CMD_TRIGGER_SCAN_TO
, trigger
,
146 sizeof(*trigger
), 0);
148 wl1271_error("trigger scan to failed for hw scan");
152 wl1271_dump(DEBUG_SCAN
, "SCAN: ", cmd
, sizeof(*cmd
));
154 ret
= wl1271_cmd_send(wl
, CMD_SCAN
, cmd
, sizeof(*cmd
), 0);
156 wl1271_error("SCAN failed");
166 void wl1271_scan_stm(struct wl1271
*wl
)
170 switch (wl
->scan
.state
) {
171 case WL1271_SCAN_STATE_IDLE
:
174 case WL1271_SCAN_STATE_2GHZ_ACTIVE
:
175 ret
= wl1271_scan_send(wl
, IEEE80211_BAND_2GHZ
, false,
176 wl
->conf
.tx
.basic_rate
);
177 if (ret
== WL1271_NOTHING_TO_SCAN
) {
178 wl
->scan
.state
= WL1271_SCAN_STATE_2GHZ_PASSIVE
;
184 case WL1271_SCAN_STATE_2GHZ_PASSIVE
:
185 ret
= wl1271_scan_send(wl
, IEEE80211_BAND_2GHZ
, true,
186 wl
->conf
.tx
.basic_rate
);
187 if (ret
== WL1271_NOTHING_TO_SCAN
) {
188 if (wl1271_11a_enabled())
189 wl
->scan
.state
= WL1271_SCAN_STATE_5GHZ_ACTIVE
;
191 wl
->scan
.state
= WL1271_SCAN_STATE_DONE
;
197 case WL1271_SCAN_STATE_5GHZ_ACTIVE
:
198 ret
= wl1271_scan_send(wl
, IEEE80211_BAND_5GHZ
, false,
199 wl
->conf
.tx
.basic_rate_5
);
200 if (ret
== WL1271_NOTHING_TO_SCAN
) {
201 wl
->scan
.state
= WL1271_SCAN_STATE_5GHZ_PASSIVE
;
207 case WL1271_SCAN_STATE_5GHZ_PASSIVE
:
208 ret
= wl1271_scan_send(wl
, IEEE80211_BAND_5GHZ
, true,
209 wl
->conf
.tx
.basic_rate_5
);
210 if (ret
== WL1271_NOTHING_TO_SCAN
) {
211 wl
->scan
.state
= WL1271_SCAN_STATE_DONE
;
217 case WL1271_SCAN_STATE_DONE
:
218 ieee80211_scan_completed(wl
->hw
, false);
220 kfree(wl
->scan
.scanned_ch
);
221 wl
->scan
.scanned_ch
= NULL
;
223 wl
->scan
.state
= WL1271_SCAN_STATE_IDLE
;
227 wl1271_error("invalid scan state");
232 int wl1271_scan(struct wl1271
*wl
, const u8
*ssid
, size_t ssid_len
,
233 struct cfg80211_scan_request
*req
)
235 if (wl
->scan
.state
!= WL1271_SCAN_STATE_IDLE
)
238 wl
->scan
.state
= WL1271_SCAN_STATE_2GHZ_ACTIVE
;
240 if (ssid_len
&& ssid
) {
241 wl
->scan
.ssid_len
= ssid_len
;
242 memcpy(wl
->scan
.ssid
, ssid
, ssid_len
);
244 wl
->scan
.ssid_len
= 0;
249 wl
->scan
.scanned_ch
= kcalloc(req
->n_channels
,
250 sizeof(*wl
->scan
.scanned_ch
),