Johannes Berg | 8ca151b | 2013-01-24 14:25:36 +0100 | [diff] [blame^] | 1 | /****************************************************************************** |
| 2 | * |
| 3 | * This file is provided under a dual BSD/GPLv2 license. When using or |
| 4 | * redistributing this file, you may do so under either license. |
| 5 | * |
| 6 | * GPL LICENSE SUMMARY |
| 7 | * |
| 8 | * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of version 2 of the GNU General Public License as |
| 12 | * published by the Free Software Foundation. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, but |
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 17 | * General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU General Public License |
| 20 | * along with this program; if not, write to the Free Software |
| 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
| 22 | * USA |
| 23 | * |
| 24 | * The full GNU General Public License is included in this distribution |
| 25 | * in the file called LICENSE.GPL. |
| 26 | * |
| 27 | * Contact Information: |
| 28 | * Intel Linux Wireless <ilw@linux.intel.com> |
| 29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| 30 | * |
| 31 | * BSD LICENSE |
| 32 | * |
| 33 | * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. |
| 34 | * All rights reserved. |
| 35 | * |
| 36 | * Redistribution and use in source and binary forms, with or without |
| 37 | * modification, are permitted provided that the following conditions |
| 38 | * are met: |
| 39 | * |
| 40 | * * Redistributions of source code must retain the above copyright |
| 41 | * notice, this list of conditions and the following disclaimer. |
| 42 | * * Redistributions in binary form must reproduce the above copyright |
| 43 | * notice, this list of conditions and the following disclaimer in |
| 44 | * the documentation and/or other materials provided with the |
| 45 | * distribution. |
| 46 | * * Neither the name Intel Corporation nor the names of its |
| 47 | * contributors may be used to endorse or promote products derived |
| 48 | * from this software without specific prior written permission. |
| 49 | * |
| 50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 61 | * |
| 62 | *****************************************************************************/ |
| 63 | |
| 64 | #include <linux/etherdevice.h> |
| 65 | #include <net/mac80211.h> |
| 66 | |
| 67 | #include "mvm.h" |
| 68 | #include "iwl-eeprom-parse.h" |
| 69 | #include "fw-api-scan.h" |
| 70 | |
| 71 | #define IWL_PLCP_QUIET_THRESH 1 |
| 72 | #define IWL_ACTIVE_QUIET_TIME 10 |
| 73 | |
| 74 | static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) |
| 75 | { |
| 76 | u16 rx_chain; |
| 77 | u8 rx_ant = mvm->nvm_data->valid_rx_ant; |
| 78 | |
| 79 | rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; |
| 80 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; |
| 81 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; |
| 82 | rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS; |
| 83 | return cpu_to_le16(rx_chain); |
| 84 | } |
| 85 | |
| 86 | static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif) |
| 87 | { |
| 88 | if (vif->bss_conf.assoc) |
| 89 | return cpu_to_le32(200 * 1024); |
| 90 | else |
| 91 | return 0; |
| 92 | } |
| 93 | |
| 94 | static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif) |
| 95 | { |
| 96 | if (vif->bss_conf.assoc) |
| 97 | return cpu_to_le32(vif->bss_conf.beacon_int); |
| 98 | else |
| 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | static inline __le32 |
| 103 | iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req) |
| 104 | { |
| 105 | if (req->channels[0]->band == IEEE80211_BAND_2GHZ) |
| 106 | return cpu_to_le32(PHY_BAND_24); |
| 107 | else |
| 108 | return cpu_to_le32(PHY_BAND_5); |
| 109 | } |
| 110 | |
| 111 | static inline __le32 |
| 112 | iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, |
| 113 | bool no_cck) |
| 114 | { |
| 115 | u32 tx_ant; |
| 116 | |
| 117 | mvm->scan_last_antenna_idx = |
| 118 | iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant, |
| 119 | mvm->scan_last_antenna_idx); |
| 120 | tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; |
| 121 | |
| 122 | if (band == IEEE80211_BAND_2GHZ && !no_cck) |
| 123 | return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK | |
| 124 | tx_ant); |
| 125 | else |
| 126 | return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant); |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | * We insert the SSIDs in an inverted order, because the FW will |
| 131 | * invert it back. The most prioritized SSID, which is first in the |
| 132 | * request list, is not copied here, but inserted directly to the probe |
| 133 | * request. |
| 134 | */ |
| 135 | static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, |
| 136 | struct cfg80211_scan_request *req) |
| 137 | { |
| 138 | int fw_idx, req_idx; |
| 139 | |
| 140 | fw_idx = 0; |
| 141 | for (req_idx = req->n_ssids - 1; req_idx > 0; req_idx--) { |
| 142 | cmd->direct_scan[fw_idx].id = WLAN_EID_SSID; |
| 143 | cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len; |
| 144 | memcpy(cmd->direct_scan[fw_idx].ssid, |
| 145 | req->ssids[req_idx].ssid, |
| 146 | req->ssids[req_idx].ssid_len); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | * If req->n_ssids > 0, it means we should do an active scan. |
| 152 | * In case of active scan w/o directed scan, we receive a zero-length SSID |
| 153 | * just to notify that this scan is active and not passive. |
| 154 | * In order to notify the FW of the number of SSIDs we wish to scan (including |
| 155 | * the zero-length one), we need to set the corresponding bits in chan->type, |
| 156 | * one for each SSID, and set the active bit (first). |
| 157 | */ |
| 158 | static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids) |
| 159 | { |
| 160 | if (band == IEEE80211_BAND_2GHZ) |
| 161 | return 30 + 3 * (n_ssids + 1); |
| 162 | return 20 + 2 * (n_ssids + 1); |
| 163 | } |
| 164 | |
| 165 | static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band) |
| 166 | { |
| 167 | return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10; |
| 168 | } |
| 169 | |
| 170 | static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, |
| 171 | struct cfg80211_scan_request *req) |
| 172 | { |
| 173 | u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band); |
| 174 | u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band, |
| 175 | req->n_ssids); |
| 176 | struct iwl_scan_channel *chan = (struct iwl_scan_channel *) |
| 177 | (cmd->data + le16_to_cpu(cmd->tx_cmd.len)); |
| 178 | int i; |
| 179 | __le32 chan_type_value; |
| 180 | |
| 181 | if (req->n_ssids > 0) |
| 182 | chan_type_value = cpu_to_le32(BIT(req->n_ssids + 1) - 1); |
| 183 | else |
| 184 | chan_type_value = SCAN_CHANNEL_TYPE_PASSIVE; |
| 185 | |
| 186 | for (i = 0; i < cmd->channel_count; i++) { |
| 187 | chan->channel = cpu_to_le16(req->channels[i]->hw_value); |
| 188 | if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
| 189 | chan->type = SCAN_CHANNEL_TYPE_PASSIVE; |
| 190 | else |
| 191 | chan->type = chan_type_value; |
| 192 | chan->active_dwell = cpu_to_le16(active_dwell); |
| 193 | chan->passive_dwell = cpu_to_le16(passive_dwell); |
| 194 | chan->iteration_count = cpu_to_le16(1); |
| 195 | chan++; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | /* |
| 200 | * Fill in probe request with the following parameters: |
| 201 | * TA is our vif HW address, which mac80211 ensures we have. |
| 202 | * Packet is broadcasted, so this is both SA and DA. |
| 203 | * The probe request IE is made out of two: first comes the most prioritized |
| 204 | * SSID if a directed scan is requested. Second comes whatever extra |
| 205 | * information was given to us as the scan request IE. |
| 206 | */ |
| 207 | static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, |
| 208 | int n_ssids, const u8 *ssid, int ssid_len, |
| 209 | const u8 *ie, int ie_len, |
| 210 | int left) |
| 211 | { |
| 212 | int len = 0; |
| 213 | u8 *pos = NULL; |
| 214 | |
| 215 | /* Make sure there is enough space for the probe request, |
| 216 | * two mandatory IEs and the data */ |
| 217 | left -= 24; |
| 218 | if (left < 0) |
| 219 | return 0; |
| 220 | |
| 221 | frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); |
| 222 | eth_broadcast_addr(frame->da); |
| 223 | memcpy(frame->sa, ta, ETH_ALEN); |
| 224 | eth_broadcast_addr(frame->bssid); |
| 225 | frame->seq_ctrl = 0; |
| 226 | |
| 227 | len += 24; |
| 228 | |
| 229 | /* for passive scans, no need to fill anything */ |
| 230 | if (n_ssids == 0) |
| 231 | return (u16)len; |
| 232 | |
| 233 | /* points to the payload of the request */ |
| 234 | pos = &frame->u.probe_req.variable[0]; |
| 235 | |
| 236 | /* fill in our SSID IE */ |
| 237 | left -= ssid_len + 2; |
| 238 | if (left < 0) |
| 239 | return 0; |
| 240 | *pos++ = WLAN_EID_SSID; |
| 241 | *pos++ = ssid_len; |
| 242 | if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */ |
| 243 | memcpy(pos, ssid, ssid_len); |
| 244 | pos += ssid_len; |
| 245 | } |
| 246 | |
| 247 | len += ssid_len + 2; |
| 248 | |
| 249 | if (WARN_ON(left < ie_len)) |
| 250 | return len; |
| 251 | |
| 252 | if (ie && ie_len) { |
| 253 | memcpy(pos, ie, ie_len); |
| 254 | len += ie_len; |
| 255 | } |
| 256 | |
| 257 | return (u16)len; |
| 258 | } |
| 259 | |
| 260 | int iwl_mvm_scan_request(struct iwl_mvm *mvm, |
| 261 | struct ieee80211_vif *vif, |
| 262 | struct cfg80211_scan_request *req) |
| 263 | { |
| 264 | struct iwl_host_cmd hcmd = { |
| 265 | .id = SCAN_REQUEST_CMD, |
| 266 | .len = { 0, }, |
| 267 | .data = { mvm->scan_cmd, }, |
| 268 | .flags = CMD_SYNC, |
| 269 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, |
| 270 | }; |
| 271 | struct iwl_scan_cmd *cmd = mvm->scan_cmd; |
| 272 | int ret; |
| 273 | u32 status; |
| 274 | int ssid_len = 0; |
| 275 | u8 *ssid = NULL; |
| 276 | |
| 277 | lockdep_assert_held(&mvm->mutex); |
| 278 | BUG_ON(mvm->scan_cmd == NULL); |
| 279 | |
| 280 | IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n"); |
| 281 | mvm->scan_status = IWL_MVM_SCAN_OS; |
| 282 | memset(cmd, 0, sizeof(struct iwl_scan_cmd) + |
| 283 | mvm->fw->ucode_capa.max_probe_length + |
| 284 | (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel))); |
| 285 | |
| 286 | cmd->channel_count = (u8)req->n_channels; |
| 287 | cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); |
| 288 | cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); |
| 289 | cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm); |
| 290 | cmd->max_out_time = iwl_mvm_scan_max_out_time(vif); |
| 291 | cmd->suspend_time = iwl_mvm_scan_suspend_time(vif); |
| 292 | cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req); |
| 293 | cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | |
| 294 | MAC_FILTER_IN_BEACON); |
| 295 | cmd->type = SCAN_TYPE_FORCED; |
| 296 | cmd->repeats = cpu_to_le32(1); |
| 297 | |
| 298 | /* |
| 299 | * If the user asked for passive scan, don't change to active scan if |
| 300 | * you see any activity on the channel - remain passive. |
| 301 | */ |
| 302 | if (req->n_ssids > 0) { |
| 303 | cmd->passive2active = cpu_to_le16(1); |
| 304 | ssid = req->ssids[0].ssid; |
| 305 | ssid_len = req->ssids[0].ssid_len; |
| 306 | } else { |
| 307 | cmd->passive2active = 0; |
| 308 | } |
| 309 | |
| 310 | iwl_mvm_scan_fill_ssids(cmd, req); |
| 311 | |
| 312 | cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); |
| 313 | cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id; |
| 314 | cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); |
| 315 | cmd->tx_cmd.rate_n_flags = |
| 316 | iwl_mvm_scan_rate_n_flags(mvm, req->channels[0]->band, |
| 317 | req->no_cck); |
| 318 | |
| 319 | cmd->tx_cmd.len = |
| 320 | cpu_to_le16(iwl_mvm_fill_probe_req( |
| 321 | (struct ieee80211_mgmt *)cmd->data, |
| 322 | vif->addr, |
| 323 | req->n_ssids, ssid, ssid_len, |
| 324 | req->ie, req->ie_len, |
| 325 | mvm->fw->ucode_capa.max_probe_length)); |
| 326 | |
| 327 | iwl_mvm_scan_fill_channels(cmd, req); |
| 328 | |
| 329 | cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) + |
| 330 | le16_to_cpu(cmd->tx_cmd.len) + |
| 331 | (cmd->channel_count * sizeof(struct iwl_scan_channel))); |
| 332 | hcmd.len[0] = le16_to_cpu(cmd->len); |
| 333 | |
| 334 | status = SCAN_RESPONSE_OK; |
| 335 | ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &status); |
| 336 | if (!ret && status == SCAN_RESPONSE_OK) { |
| 337 | IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); |
| 338 | } else { |
| 339 | /* |
| 340 | * If the scan failed, it usually means that the FW was unable |
| 341 | * to allocate the time events. Warn on it, but maybe we |
| 342 | * should try to send the command again with different params. |
| 343 | */ |
| 344 | IWL_ERR(mvm, "Scan failed! status 0x%x ret %d\n", |
| 345 | status, ret); |
| 346 | mvm->scan_status = IWL_MVM_SCAN_NONE; |
| 347 | ret = -EIO; |
| 348 | } |
| 349 | return ret; |
| 350 | } |
| 351 | |
| 352 | int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, |
| 353 | struct iwl_device_cmd *cmd) |
| 354 | { |
| 355 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
| 356 | struct iwl_cmd_response *resp = (void *)pkt->data; |
| 357 | |
| 358 | IWL_DEBUG_SCAN(mvm, "Scan response received. status 0x%x\n", |
| 359 | le32_to_cpu(resp->status)); |
| 360 | return 0; |
| 361 | } |
| 362 | |
| 363 | int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, |
| 364 | struct iwl_device_cmd *cmd) |
| 365 | { |
| 366 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
| 367 | struct iwl_scan_complete_notif *notif = (void *)pkt->data; |
| 368 | |
| 369 | IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n", |
| 370 | notif->status, notif->scanned_channels); |
| 371 | |
| 372 | mvm->scan_status = IWL_MVM_SCAN_NONE; |
| 373 | ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK); |
| 374 | |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, |
| 379 | struct iwl_rx_packet *pkt, void *data) |
| 380 | { |
| 381 | struct iwl_mvm *mvm = |
| 382 | container_of(notif_wait, struct iwl_mvm, notif_wait); |
| 383 | struct iwl_scan_complete_notif *notif; |
| 384 | u32 *resp; |
| 385 | |
| 386 | switch (pkt->hdr.cmd) { |
| 387 | case SCAN_ABORT_CMD: |
| 388 | resp = (void *)pkt->data; |
| 389 | if (*resp == CAN_ABORT_STATUS) { |
| 390 | IWL_DEBUG_SCAN(mvm, |
| 391 | "Scan can be aborted, wait until completion\n"); |
| 392 | return false; |
| 393 | } |
| 394 | |
| 395 | IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n", |
| 396 | *resp); |
| 397 | return true; |
| 398 | |
| 399 | case SCAN_COMPLETE_NOTIFICATION: |
| 400 | notif = (void *)pkt->data; |
| 401 | IWL_DEBUG_SCAN(mvm, "Scan aborted: status 0x%x\n", |
| 402 | notif->status); |
| 403 | return true; |
| 404 | |
| 405 | default: |
| 406 | WARN_ON(1); |
| 407 | return false; |
| 408 | }; |
| 409 | } |
| 410 | |
| 411 | void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) |
| 412 | { |
| 413 | struct iwl_notification_wait wait_scan_abort; |
| 414 | static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD, |
| 415 | SCAN_COMPLETE_NOTIFICATION }; |
| 416 | int ret; |
| 417 | |
| 418 | iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort, |
| 419 | scan_abort_notif, |
| 420 | ARRAY_SIZE(scan_abort_notif), |
| 421 | iwl_mvm_scan_abort_notif, NULL); |
| 422 | |
| 423 | ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL); |
| 424 | if (ret) { |
| 425 | IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret); |
| 426 | goto out_remove_notif; |
| 427 | } |
| 428 | |
| 429 | ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ); |
| 430 | if (ret) |
| 431 | IWL_ERR(mvm, "%s - failed on timeout\n", __func__); |
| 432 | |
| 433 | return; |
| 434 | |
| 435 | out_remove_notif: |
| 436 | iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort); |
| 437 | } |