if_iwm - Use chan list from ieee80211_scan_state for scan, not ic_channels.
[dragonfly.git] / sys / dev / netif / iwm / if_iwm_scan.c
blob73be8a66f9165a4814c8d46bb141e24dd3e2bff0
1 /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */
3 /*
4 * Copyright (c) 2014 genua mbh <info@genua.de>
5 * Copyright (c) 2014 Fixup Software Ltd.
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 /*-
21 * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22 * which were used as the reference documentation for this implementation.
24 * Driver version we are currently based off of is
25 * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
27 ***********************************************************************
29 * This file is provided under a dual BSD/GPLv2 license. When using or
30 * redistributing this file, you may do so under either license.
32 * GPL LICENSE SUMMARY
34 * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
36 * This program is free software; you can redistribute it and/or modify
37 * it under the terms of version 2 of the GNU General Public License as
38 * published by the Free Software Foundation.
40 * This program is distributed in the hope that it will be useful, but
41 * WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 * General Public License for more details.
45 * You should have received a copy of the GNU General Public License
46 * along with this program; if not, write to the Free Software
47 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
48 * USA
50 * The full GNU General Public License is included in this distribution
51 * in the file called COPYING.
53 * Contact Information:
54 * Intel Linux Wireless <ilw@linux.intel.com>
55 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
58 * BSD LICENSE
60 * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61 * All rights reserved.
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
65 * are met:
67 * * Redistributions of source code must retain the above copyright
68 * notice, this list of conditions and the following disclaimer.
69 * * Redistributions in binary form must reproduce the above copyright
70 * notice, this list of conditions and the following disclaimer in
71 * the documentation and/or other materials provided with the
72 * distribution.
73 * * Neither the name Intel Corporation nor the names of its
74 * contributors may be used to endorse or promote products derived
75 * from this software without specific prior written permission.
77 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
90 /*-
91 * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
93 * Permission to use, copy, modify, and distribute this software for any
94 * purpose with or without fee is hereby granted, provided that the above
95 * copyright notice and this permission notice appear in all copies.
97 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
100 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
101 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
102 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
103 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
105 #include <sys/cdefs.h>
106 __FBSDID("$FreeBSD$");
108 #include <sys/param.h>
109 #include <sys/bus.h>
110 #include <sys/endian.h>
111 #include <sys/firmware.h>
112 #include <sys/kernel.h>
113 #include <sys/malloc.h>
114 #include <sys/mbuf.h>
115 #include <sys/rman.h>
116 #include <sys/sysctl.h>
117 #include <sys/linker.h>
119 #include <machine/endian.h>
121 #include <bus/pci/pcivar.h>
122 #include <bus/pci/pcireg.h>
124 #include <net/bpf.h>
126 #include <net/if.h>
127 #include <net/if_var.h>
128 #include <net/if_arp.h>
129 #include <net/if_dl.h>
130 #include <net/if_media.h>
131 #include <net/if_types.h>
133 #include <netinet/in.h>
134 #include <netinet/in_systm.h>
135 #include <netinet/if_ether.h>
136 #include <netinet/ip.h>
138 #include <netproto/802_11/ieee80211_var.h>
139 #include <netproto/802_11/ieee80211_regdomain.h>
140 #include <netproto/802_11/ieee80211_ratectl.h>
141 #include <netproto/802_11/ieee80211_radiotap.h>
143 #include "if_iwmreg.h"
144 #include "if_iwmvar.h"
145 #include "if_iwm_debug.h"
146 #include "if_iwm_notif_wait.h"
147 #include "if_iwm_util.h"
148 #include "if_iwm_scan.h"
150 #define IWM_DENSE_EBS_SCAN_RATIO 5
151 #define IWM_SPARSE_EBS_SCAN_RATIO 1
153 static uint16_t
154 iwm_mvm_scan_rx_chain(struct iwm_softc *sc)
156 uint16_t rx_chain;
157 uint8_t rx_ant;
159 rx_ant = iwm_mvm_get_valid_rx_ant(sc);
160 rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS;
161 rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
162 rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS;
163 rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS;
164 return htole16(rx_chain);
167 static uint32_t
168 iwm_mvm_scan_rxon_flags(struct ieee80211_channel *c)
170 if (IEEE80211_IS_CHAN_2GHZ(c))
171 return htole32(IWM_PHY_BAND_24);
172 else
173 return htole32(IWM_PHY_BAND_5);
176 static uint32_t
177 iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck)
179 uint32_t tx_ant;
180 int i, ind;
182 for (i = 0, ind = sc->sc_scan_last_antenna;
183 i < IWM_RATE_MCS_ANT_NUM; i++) {
184 ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM;
185 if (iwm_mvm_get_valid_tx_ant(sc) & (1 << ind)) {
186 sc->sc_scan_last_antenna = ind;
187 break;
190 tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS;
192 if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck)
193 return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK |
194 tx_ant);
195 else
196 return htole32(IWM_RATE_6M_PLCP | tx_ant);
199 static inline boolean_t
200 iwm_mvm_rrm_scan_needed(struct iwm_softc *sc)
202 /* require rrm scan whenever the fw supports it */
203 return fw_has_capa(&sc->ucode_capa,
204 IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT);
207 #ifdef IWM_DEBUG
208 static const char *
209 iwm_mvm_ebs_status_str(enum iwm_scan_ebs_status status)
211 switch (status) {
212 case IWM_SCAN_EBS_SUCCESS:
213 return "successful";
214 case IWM_SCAN_EBS_INACTIVE:
215 return "inactive";
216 case IWM_SCAN_EBS_FAILED:
217 case IWM_SCAN_EBS_CHAN_NOT_FOUND:
218 default:
219 return "failed";
222 #endif
224 void
225 iwm_mvm_rx_lmac_scan_complete_notif(struct iwm_softc *sc,
226 struct iwm_rx_packet *pkt)
228 struct iwm_periodic_scan_complete *scan_notif = (void *)pkt->data;
229 #ifdef IWM_DEBUG
230 boolean_t aborted = (scan_notif->status == IWM_SCAN_OFFLOAD_ABORTED);
231 #endif
233 /* If this happens, the firmware has mistakenly sent an LMAC
234 * notification during UMAC scans -- warn and ignore it.
236 if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
237 device_printf(sc->sc_dev,
238 "%s: Mistakenly got LMAC notification during UMAC scan\n",
239 __func__);
240 return;
243 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Regular scan %s, EBS status %s (FW)\n",
244 aborted ? "aborted" : "completed",
245 iwm_mvm_ebs_status_str(scan_notif->ebs_status));
247 sc->last_ebs_successful =
248 scan_notif->ebs_status == IWM_SCAN_EBS_SUCCESS ||
249 scan_notif->ebs_status == IWM_SCAN_EBS_INACTIVE;
253 void
254 iwm_mvm_rx_umac_scan_complete_notif(struct iwm_softc *sc,
255 struct iwm_rx_packet *pkt)
257 struct iwm_umac_scan_complete *notif = (void *)pkt->data;
258 #ifdef IWM_DEBUG
259 uint32_t uid = le32toh(notif->uid);
260 boolean_t aborted = (notif->status == IWM_SCAN_OFFLOAD_ABORTED);
261 #endif
263 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
264 "Scan completed, uid %u, status %s, EBS status %s\n",
265 uid,
266 aborted ? "aborted" : "completed",
267 iwm_mvm_ebs_status_str(notif->ebs_status));
269 if (notif->ebs_status != IWM_SCAN_EBS_SUCCESS &&
270 notif->ebs_status != IWM_SCAN_EBS_INACTIVE)
271 sc->last_ebs_successful = FALSE;
274 static int
275 iwm_mvm_scan_skip_channel(struct ieee80211_channel *c)
277 if (IEEE80211_IS_CHAN_2GHZ(c) && IEEE80211_IS_CHAN_B(c))
278 return 0;
279 else if (IEEE80211_IS_CHAN_5GHZ(c) && IEEE80211_IS_CHAN_A(c))
280 return 0;
281 else
282 return 1;
285 static uint8_t
286 iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc,
287 struct iwm_scan_channel_cfg_lmac *chan, int n_ssids)
289 struct ieee80211com *ic = &sc->sc_ic;
290 struct ieee80211_scan_state *ss = ic->ic_scan;
291 struct ieee80211_channel *c;
292 uint8_t nchan;
293 int j;
295 for (nchan = j = 0;
296 j < ss->ss_last && nchan < sc->ucode_capa.n_scan_channels; j++) {
297 c = ss->ss_chans[j];
299 * Catch other channels, in case we have 900MHz channels or
300 * something in the chanlist.
302 if (!IEEE80211_IS_CHAN_2GHZ(c) && !IEEE80211_IS_CHAN_5GHZ(c)) {
303 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
304 "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
305 __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
306 continue;
309 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
310 "Adding channel %d (%d Mhz) to the list\n",
311 nchan, c->ic_freq);
312 chan->channel_num = htole16(ieee80211_mhz2ieee(c->ic_freq, 0));
313 chan->iter_count = htole16(1);
314 chan->iter_interval = htole32(0);
315 chan->flags = htole32(IWM_UNIFIED_SCAN_CHANNEL_PARTIAL);
316 #if 0 /* makes scanning while associated less useful */
317 if (n_ssids != 0)
318 chan->flags |= htole32(1 << 1); /* select SSID 0 */
319 #endif
320 chan++;
321 nchan++;
324 return nchan;
327 static uint8_t
328 iwm_mvm_umac_scan_fill_channels(struct iwm_softc *sc,
329 struct iwm_scan_channel_cfg_umac *chan, int n_ssids)
331 struct ieee80211com *ic = &sc->sc_ic;
332 struct ieee80211_scan_state *ss = ic->ic_scan;
333 struct ieee80211_channel *c;
334 uint8_t nchan;
335 int j;
337 for (nchan = j = 0;
338 j < ss->ss_last && nchan < sc->ucode_capa.n_scan_channels; j++) {
339 c = ss->ss_chans[j];
341 * Catch other channels, in case we have 900MHz channels or
342 * something in the chanlist.
344 if (!IEEE80211_IS_CHAN_2GHZ(c) && !IEEE80211_IS_CHAN_5GHZ(c)) {
345 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
346 "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
347 __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
348 continue;
351 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
352 "Adding channel %d (%d Mhz) to the list\n",
353 nchan, c->ic_freq);
354 chan->channel_num = ieee80211_mhz2ieee(c->ic_freq, 0);
355 chan->iter_count = 1;
356 chan->iter_interval = htole16(0);
357 chan->flags = htole32(0);
358 #if 0 /* makes scanning while associated less useful */
359 if (n_ssids != 0)
360 chan->flags = htole32(1 << 0); /* select SSID 0 */
361 #endif
362 chan++;
363 nchan++;
366 return nchan;
369 static int
370 iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq)
372 struct ieee80211com *ic = &sc->sc_ic;
373 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
374 struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf;
375 struct ieee80211_rateset *rs;
376 size_t remain = sizeof(preq->buf);
377 uint8_t *frm, *pos;
378 int ssid_len = 0;
379 const uint8_t *ssid = NULL;
381 memset(preq, 0, sizeof(*preq));
383 /* Ensure enough space for header and SSID IE. */
384 if (remain < sizeof(*wh) + 2 + ssid_len)
385 return ENOBUFS;
388 * Build a probe request frame. Most of the following code is a
389 * copy & paste of what is done in net80211.
391 wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
392 IEEE80211_FC0_SUBTYPE_PROBE_REQ;
393 wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
394 IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
395 IEEE80211_ADDR_COPY(wh->i_addr2, vap ? vap->iv_myaddr : ic->ic_macaddr);
396 IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
397 *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */
398 *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */
400 frm = (uint8_t *)(wh + 1);
401 frm = ieee80211_add_ssid(frm, ssid, ssid_len);
403 /* Tell the firmware where the MAC header is. */
404 preq->mac_header.offset = 0;
405 preq->mac_header.len = htole16(frm - (uint8_t *)wh);
406 remain -= frm - (uint8_t *)wh;
408 /* Fill in 2GHz IEs and tell firmware where they are. */
409 rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
410 if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
411 if (remain < 4 + rs->rs_nrates)
412 return ENOBUFS;
413 } else if (remain < 2 + rs->rs_nrates) {
414 return ENOBUFS;
416 preq->band_data[0].offset = htole16(frm - (uint8_t *)wh);
417 pos = frm;
418 frm = ieee80211_add_rates(frm, rs);
419 if (rs->rs_nrates > IEEE80211_RATE_SIZE)
420 frm = ieee80211_add_xrates(frm, rs);
421 preq->band_data[0].len = htole16(frm - pos);
422 remain -= frm - pos;
424 if (iwm_mvm_rrm_scan_needed(sc)) {
425 if (remain < 3)
426 return ENOBUFS;
427 *frm++ = IEEE80211_ELEMID_DSPARMS;
428 *frm++ = 1;
429 *frm++ = 0;
430 remain -= 3;
433 if (sc->nvm_data->sku_cap_band_52GHz_enable) {
434 /* Fill in 5GHz IEs. */
435 rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
436 if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
437 if (remain < 4 + rs->rs_nrates)
438 return ENOBUFS;
439 } else if (remain < 2 + rs->rs_nrates) {
440 return ENOBUFS;
442 preq->band_data[1].offset = htole16(frm - (uint8_t *)wh);
443 pos = frm;
444 frm = ieee80211_add_rates(frm, rs);
445 if (rs->rs_nrates > IEEE80211_RATE_SIZE)
446 frm = ieee80211_add_xrates(frm, rs);
447 preq->band_data[1].len = htole16(frm - pos);
448 remain -= frm - pos;
451 /* Send 11n IEs on both 2GHz and 5GHz bands. */
452 preq->common_data.offset = htole16(frm - (uint8_t *)wh);
453 pos = frm;
454 #if 0
455 if (ic->ic_flags & IEEE80211_F_HTON) {
456 if (remain < 28)
457 return ENOBUFS;
458 frm = ieee80211_add_htcaps(frm, ic);
459 /* XXX add WME info? */
461 #endif
462 preq->common_data.len = htole16(frm - pos);
464 return 0;
468 iwm_mvm_config_umac_scan(struct iwm_softc *sc)
470 struct ieee80211com *ic = &sc->sc_ic;
471 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
473 struct iwm_scan_config *scan_config;
474 int ret, j, nchan;
475 size_t cmd_size;
476 struct ieee80211_channel *c;
477 struct iwm_host_cmd hcmd = {
478 .id = iwm_cmd_id(IWM_SCAN_CFG_CMD, IWM_ALWAYS_LONG_GROUP, 0),
479 .flags = IWM_CMD_SYNC,
481 static const uint32_t rates = (IWM_SCAN_CONFIG_RATE_1M |
482 IWM_SCAN_CONFIG_RATE_2M | IWM_SCAN_CONFIG_RATE_5M |
483 IWM_SCAN_CONFIG_RATE_11M | IWM_SCAN_CONFIG_RATE_6M |
484 IWM_SCAN_CONFIG_RATE_9M | IWM_SCAN_CONFIG_RATE_12M |
485 IWM_SCAN_CONFIG_RATE_18M | IWM_SCAN_CONFIG_RATE_24M |
486 IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M |
487 IWM_SCAN_CONFIG_RATE_54M);
489 cmd_size = sizeof(*scan_config) + sc->ucode_capa.n_scan_channels;
491 scan_config = kmalloc(cmd_size, M_DEVBUF, M_INTWAIT | M_ZERO);
492 if (scan_config == NULL)
493 return ENOMEM;
495 scan_config->tx_chains = htole32(iwm_mvm_get_valid_tx_ant(sc));
496 scan_config->rx_chains = htole32(iwm_mvm_get_valid_rx_ant(sc));
497 scan_config->legacy_rates = htole32(rates |
498 IWM_SCAN_CONFIG_SUPPORTED_RATE(rates));
500 /* These timings correspond to iwlwifi's UNASSOC scan. */
501 scan_config->dwell_active = 10;
502 scan_config->dwell_passive = 110;
503 scan_config->dwell_fragmented = 44;
504 scan_config->dwell_extended = 90;
505 scan_config->out_of_channel_time = htole32(0);
506 scan_config->suspend_time = htole32(0);
508 IEEE80211_ADDR_COPY(scan_config->mac_addr,
509 vap ? vap->iv_myaddr : ic->ic_macaddr);
511 scan_config->bcast_sta_id = sc->sc_aux_sta.sta_id;
512 scan_config->channel_flags = IWM_CHANNEL_FLAG_EBS |
513 IWM_CHANNEL_FLAG_ACCURATE_EBS | IWM_CHANNEL_FLAG_EBS_ADD |
514 IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
516 for (nchan = j = 0;
517 j < ic->ic_nchans && nchan < sc->ucode_capa.n_scan_channels; j++) {
518 c = &ic->ic_channels[j];
519 /* For 2GHz, only populate 11b channels */
520 /* For 5GHz, only populate 11a channels */
522 * Catch other channels, in case we have 900MHz channels or
523 * something in the chanlist.
525 if (iwm_mvm_scan_skip_channel(c))
526 continue;
527 scan_config->channel_array[nchan++] =
528 ieee80211_mhz2ieee(c->ic_freq, 0);
531 scan_config->flags = htole32(IWM_SCAN_CONFIG_FLAG_ACTIVATE |
532 IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
533 IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS |
534 IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS |
535 IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID |
536 IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES |
537 IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
538 IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR |
539 IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
540 IWM_SCAN_CONFIG_N_CHANNELS(nchan) |
541 IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED);
543 hcmd.data[0] = scan_config;
544 hcmd.len[0] = cmd_size;
546 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending UMAC scan config\n");
548 ret = iwm_send_cmd(sc, &hcmd);
549 if (!ret)
550 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
551 "UMAC scan config was sent successfully\n");
553 kfree(scan_config, M_DEVBUF);
554 return ret;
557 static boolean_t
558 iwm_mvm_scan_use_ebs(struct iwm_softc *sc)
560 const struct iwm_ucode_capabilities *capa = &sc->ucode_capa;
562 /* We can only use EBS if:
563 * 1. the feature is supported;
564 * 2. the last EBS was successful;
565 * 3. if only single scan, the single scan EBS API is supported;
566 * 4. it's not a p2p find operation.
568 return ((capa->flags & IWM_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
569 sc->last_ebs_successful);
573 iwm_mvm_umac_scan(struct iwm_softc *sc)
575 struct iwm_host_cmd hcmd = {
576 .id = iwm_cmd_id(IWM_SCAN_REQ_UMAC, IWM_ALWAYS_LONG_GROUP, 0),
577 .len = { 0, },
578 .data = { NULL, },
579 .flags = IWM_CMD_SYNC,
581 struct iwm_scan_req_umac *req;
582 struct iwm_scan_req_umac_tail *tail;
583 size_t req_len;
584 int ssid_len = 0;
585 const uint8_t *ssid = NULL;
586 int ret;
588 req_len = sizeof(struct iwm_scan_req_umac) +
589 (sizeof(struct iwm_scan_channel_cfg_umac) *
590 sc->ucode_capa.n_scan_channels) +
591 sizeof(struct iwm_scan_req_umac_tail);
592 if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
593 return ENOMEM;
594 req = kmalloc(req_len, M_DEVBUF, M_INTWAIT | M_ZERO);
595 if (req == NULL)
596 return ENOMEM;
598 hcmd.len[0] = (uint16_t)req_len;
599 hcmd.data[0] = (void *)req;
601 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Handling ieee80211 scan request\n");
603 /* These timings correspond to iwlwifi's UNASSOC scan. */
604 req->active_dwell = 10;
605 req->passive_dwell = 110;
606 req->fragmented_dwell = 44;
607 req->extended_dwell = 90;
608 req->max_out_time = 0;
609 req->suspend_time = 0;
611 req->scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
612 req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
614 req->n_channels = iwm_mvm_umac_scan_fill_channels(sc,
615 (struct iwm_scan_channel_cfg_umac *)req->data, ssid_len != 0);
617 req->general_flags = htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL |
618 IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE |
619 IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL);
621 if (iwm_mvm_rrm_scan_needed(sc))
622 req->general_flags |=
623 htole32(IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED);
625 tail = (void *)((char *)&req->data +
626 sizeof(struct iwm_scan_channel_cfg_umac) *
627 sc->ucode_capa.n_scan_channels);
629 /* Check if we're doing an active directed scan. */
630 if (ssid_len != 0) {
631 tail->direct_scan[0].id = IEEE80211_ELEMID_SSID;
632 tail->direct_scan[0].len = ssid_len;
633 memcpy(tail->direct_scan[0].ssid, ssid, ssid_len);
634 req->general_flags |=
635 htole32(IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT);
636 } else {
637 req->general_flags |= htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE);
640 if (iwm_mvm_scan_use_ebs(sc))
641 req->channel_flags = IWM_SCAN_CHANNEL_FLAG_EBS |
642 IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
643 IWM_SCAN_CHANNEL_FLAG_CACHE_ADD;
645 ret = iwm_mvm_fill_probe_req(sc, &tail->preq);
646 if (ret) {
647 kfree(req, M_DEVBUF);
648 return ret;
651 /* Specify the scan plan: We'll do one iteration. */
652 tail->schedule[0].interval = 0;
653 tail->schedule[0].iter_count = 1;
655 ret = iwm_send_cmd(sc, &hcmd);
656 if (!ret)
657 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
658 "Scan request was sent successfully\n");
659 kfree(req, M_DEVBUF);
660 return ret;
664 iwm_mvm_lmac_scan(struct iwm_softc *sc)
666 struct iwm_host_cmd hcmd = {
667 .id = IWM_SCAN_OFFLOAD_REQUEST_CMD,
668 .len = { 0, },
669 .data = { NULL, },
670 .flags = IWM_CMD_SYNC,
672 struct iwm_scan_req_lmac *req;
673 size_t req_len;
674 int ret;
675 int ssid_len = 0;
676 const uint8_t *ssid = NULL;
678 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
679 "Handling ieee80211 scan request\n");
681 req_len = sizeof(struct iwm_scan_req_lmac) +
682 (sizeof(struct iwm_scan_channel_cfg_lmac) *
683 sc->ucode_capa.n_scan_channels) + sizeof(struct iwm_scan_probe_req);
684 if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
685 return ENOMEM;
686 req = kmalloc(req_len, M_DEVBUF, M_INTWAIT | M_ZERO);
687 if (req == NULL)
688 return ENOMEM;
690 hcmd.len[0] = (uint16_t)req_len;
691 hcmd.data[0] = (void *)req;
693 /* These timings correspond to iwlwifi's UNASSOC scan. */
694 req->active_dwell = 10;
695 req->passive_dwell = 110;
696 req->fragmented_dwell = 44;
697 req->extended_dwell = 90;
698 req->max_out_time = 0;
699 req->suspend_time = 0;
701 req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH);
702 req->rx_chain_select = iwm_mvm_scan_rx_chain(sc);
703 req->iter_num = htole32(1);
704 req->delay = 0;
706 req->scan_flags = htole32(IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL |
707 IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE |
708 IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL);
709 if (ssid_len == 0)
710 req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAG_PASSIVE);
711 else
712 req->scan_flags |=
713 htole32(IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION);
714 if (iwm_mvm_rrm_scan_needed(sc))
715 req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED);
717 req->flags = iwm_mvm_scan_rxon_flags(sc->sc_ic.ic_scan->ss_chans[0]);
718 req->filter_flags =
719 htole32(IWM_MAC_FILTER_ACCEPT_GRP | IWM_MAC_FILTER_IN_BEACON);
721 /* Tx flags 2 GHz. */
722 req->tx_cmd[0].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
723 IWM_TX_CMD_FLG_BT_DIS);
724 req->tx_cmd[0].rate_n_flags =
725 iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_2GHZ, 1/*XXX*/);
726 req->tx_cmd[0].sta_id = sc->sc_aux_sta.sta_id;
728 /* Tx flags 5 GHz. */
729 req->tx_cmd[1].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
730 IWM_TX_CMD_FLG_BT_DIS);
731 req->tx_cmd[1].rate_n_flags =
732 iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_5GHZ, 1/*XXX*/);
733 req->tx_cmd[1].sta_id = sc->sc_aux_sta.sta_id;
735 /* Check if we're doing an active directed scan. */
736 if (ssid_len != 0) {
737 req->direct_scan[0].id = IEEE80211_ELEMID_SSID;
738 req->direct_scan[0].len = ssid_len;
739 memcpy(req->direct_scan[0].ssid, ssid, ssid_len);
742 req->n_channels = iwm_mvm_lmac_scan_fill_channels(sc,
743 (struct iwm_scan_channel_cfg_lmac *)req->data,
744 ssid_len != 0);
746 ret = iwm_mvm_fill_probe_req(sc,
747 (struct iwm_scan_probe_req *)(req->data +
748 (sizeof(struct iwm_scan_channel_cfg_lmac) *
749 sc->ucode_capa.n_scan_channels)));
750 if (ret) {
751 kfree(req, M_DEVBUF);
752 return ret;
755 /* Specify the scan plan: We'll do one iteration. */
756 req->schedule[0].iterations = 1;
757 req->schedule[0].full_scan_mul = 1;
759 if (iwm_mvm_scan_use_ebs(sc)) {
760 req->channel_opt[0].flags =
761 htole16(IWM_SCAN_CHANNEL_FLAG_EBS |
762 IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
763 IWM_SCAN_CHANNEL_FLAG_CACHE_ADD);
764 req->channel_opt[0].non_ebs_ratio =
765 htole16(IWM_DENSE_EBS_SCAN_RATIO);
766 req->channel_opt[1].flags =
767 htole16(IWM_SCAN_CHANNEL_FLAG_EBS |
768 IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
769 IWM_SCAN_CHANNEL_FLAG_CACHE_ADD);
770 req->channel_opt[1].non_ebs_ratio =
771 htole16(IWM_SPARSE_EBS_SCAN_RATIO);
774 ret = iwm_send_cmd(sc, &hcmd);
775 if (!ret) {
776 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
777 "Scan request was sent successfully\n");
779 kfree(req, M_DEVBUF);
780 return ret;
783 static int
784 iwm_mvm_lmac_scan_abort(struct iwm_softc *sc)
786 int ret;
787 struct iwm_host_cmd hcmd = {
788 .id = IWM_SCAN_OFFLOAD_ABORT_CMD,
789 .len = { 0, },
790 .data = { NULL, },
791 .flags = IWM_CMD_SYNC,
793 uint32_t status;
795 ret = iwm_mvm_send_cmd_status(sc, &hcmd, &status);
796 if (ret)
797 return ret;
799 if (status != IWM_CAN_ABORT_STATUS) {
801 * The scan abort will return 1 for success or
802 * 2 for "failure". A failure condition can be
803 * due to simply not being in an active scan which
804 * can occur if we send the scan abort before the
805 * microcode has notified us that a scan is completed.
807 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
808 "SCAN OFFLOAD ABORT ret %d.\n", status);
809 ret = ENOENT;
812 return ret;
815 static int
816 iwm_mvm_umac_scan_abort(struct iwm_softc *sc)
818 struct iwm_umac_scan_abort cmd = {};
819 int uid, ret;
821 uid = 0;
822 cmd.uid = htole32(uid);
824 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending scan abort, uid %u\n", uid);
826 ret = iwm_mvm_send_cmd_pdu(sc,
827 iwm_cmd_id(IWM_SCAN_ABORT_UMAC,
828 IWM_ALWAYS_LONG_GROUP, 0),
829 0, sizeof(cmd), &cmd);
831 return ret;
835 iwm_mvm_scan_stop_wait(struct iwm_softc *sc)
837 struct iwm_notification_wait wait_scan_done;
838 static const uint16_t scan_done_notif[] = { IWM_SCAN_COMPLETE_UMAC,
839 IWM_SCAN_OFFLOAD_COMPLETE, };
840 int ret;
842 iwm_init_notification_wait(sc->sc_notif_wait, &wait_scan_done,
843 scan_done_notif, NELEM(scan_done_notif),
844 NULL, NULL);
846 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Preparing to stop scan\n");
848 if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
849 ret = iwm_mvm_umac_scan_abort(sc);
850 else
851 ret = iwm_mvm_lmac_scan_abort(sc);
853 if (ret) {
854 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "couldn't stop scan\n");
855 iwm_remove_notification(sc->sc_notif_wait, &wait_scan_done);
856 return ret;
859 IWM_UNLOCK(sc);
860 ret = iwm_wait_notification(sc->sc_notif_wait, &wait_scan_done, hz);
861 IWM_LOCK(sc);
863 return ret;