if_iwm - Sync nvm parsing code with Linux iwlwifi.
[dragonfly.git] / sys / dev / netif / iwm / if_iwm_scan.c
CommitLineData
24a8d46a
MD
1/* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */
2
3/*
4 * Copyright (c) 2014 genua mbh <info@genua.de>
5 * Copyright (c) 2014 Fixup Software Ltd.
6 *
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.
10 *
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.
18 */
19
20/*-
21 * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22 * which were used as the reference documentation for this implementation.
23 *
24 * Driver version we are currently based off of is
25 * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
26 *
27 ***********************************************************************
28 *
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.
31 *
32 * GPL LICENSE SUMMARY
33 *
34 * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
35 *
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.
39 *
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.
44 *
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
49 *
50 * The full GNU General Public License is included in this distribution
51 * in the file called COPYING.
52 *
53 * Contact Information:
54 * Intel Linux Wireless <ilw@linux.intel.com>
55 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
56 *
57 *
58 * BSD LICENSE
59 *
60 * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61 * All rights reserved.
62 *
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
65 * are met:
66 *
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.
76 *
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.
88 */
89
90/*-
91 * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
92 *
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.
96 *
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.
104 */
105#include <sys/cdefs.h>
106__FBSDID("$FreeBSD$");
107
108#include <sys/param.h>
109#include <sys/bus.h>
24a8d46a
MD
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>
24a8d46a 115#include <sys/rman.h>
24a8d46a
MD
116#include <sys/sysctl.h>
117#include <sys/linker.h>
118
24a8d46a 119#include <machine/endian.h>
24a8d46a 120
45bc40b1
MD
121#include <bus/pci/pcivar.h>
122#include <bus/pci/pcireg.h>
24a8d46a
MD
123
124#include <net/bpf.h>
125
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>
132
133#include <netinet/in.h>
134#include <netinet/in_systm.h>
135#include <netinet/if_ether.h>
136#include <netinet/ip.h>
137
45bc40b1
MD
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>
24a8d46a 142
45bc40b1
MD
143#include "if_iwmreg.h"
144#include "if_iwmvar.h"
145#include "if_iwm_debug.h"
146#include "if_iwm_util.h"
147#include "if_iwm_scan.h"
24a8d46a 148
24a8d46a
MD
149#define IWM_PLCP_QUIET_THRESH 1
150#define IWM_ACTIVE_QUIET_TIME 10
151#define LONG_OUT_TIME_PERIOD (600 * IEEE80211_DUR_TU)
152#define SHORT_OUT_TIME_PERIOD (200 * IEEE80211_DUR_TU)
153#define SUSPEND_TIME_PERIOD (100 * IEEE80211_DUR_TU)
154
155static uint16_t
156iwm_mvm_scan_rx_chain(struct iwm_softc *sc)
157{
158 uint16_t rx_chain;
159 uint8_t rx_ant;
160
edfc8a07 161 rx_ant = iwm_fw_valid_rx_ant(sc);
24a8d46a
MD
162 rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS;
163 rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
164 rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS;
165 rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS;
166 return htole16(rx_chain);
167}
168
edfc8a07 169#if 0
24a8d46a
MD
170static uint32_t
171iwm_mvm_scan_max_out_time(struct iwm_softc *sc, uint32_t flags, int is_assoc)
172{
173 if (!is_assoc)
174 return 0;
175 if (flags & 0x1)
176 return htole32(SHORT_OUT_TIME_PERIOD);
177 return htole32(LONG_OUT_TIME_PERIOD);
178}
179
180static uint32_t
181iwm_mvm_scan_suspend_time(struct iwm_softc *sc, int is_assoc)
182{
183 if (!is_assoc)
184 return 0;
185 return htole32(SUSPEND_TIME_PERIOD);
186}
edfc8a07 187#endif
24a8d46a
MD
188
189static uint32_t
190iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck)
191{
192 uint32_t tx_ant;
193 int i, ind;
194
195 for (i = 0, ind = sc->sc_scan_last_antenna;
196 i < IWM_RATE_MCS_ANT_NUM; i++) {
197 ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM;
edfc8a07 198 if (iwm_fw_valid_tx_ant(sc) & (1 << ind)) {
24a8d46a
MD
199 sc->sc_scan_last_antenna = ind;
200 break;
201 }
202 }
203 tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS;
204
205 if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck)
206 return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK |
207 tx_ant);
208 else
209 return htole32(IWM_RATE_6M_PLCP | tx_ant);
210}
211
edfc8a07 212#if 0
24a8d46a
MD
213/*
214 * If req->n_ssids > 0, it means we should do an active scan.
215 * In case of active scan w/o directed scan, we receive a zero-length SSID
216 * just to notify that this scan is active and not passive.
217 * In order to notify the FW of the number of SSIDs we wish to scan (including
218 * the zero-length one), we need to set the corresponding bits in chan->type,
219 * one for each SSID, and set the active bit (first). If the first SSID is
220 * already included in the probe template, so we need to set only
221 * req->n_ssids - 1 bits in addition to the first bit.
222 */
223static uint16_t
224iwm_mvm_get_active_dwell(struct iwm_softc *sc, int flags, int n_ssids)
225{
226 if (flags & IEEE80211_CHAN_2GHZ)
227 return 30 + 3 * (n_ssids + 1);
228 return 20 + 2 * (n_ssids + 1);
229}
230
231static uint16_t
232iwm_mvm_get_passive_dwell(struct iwm_softc *sc, int flags)
233{
234 return (flags & IEEE80211_CHAN_2GHZ) ? 100 + 20 : 100 + 10;
235}
edfc8a07 236#endif
24a8d46a 237
e8951a47
IV
238static int
239iwm_mvm_scan_skip_channel(struct ieee80211_channel *c)
240{
241 if (IEEE80211_IS_CHAN_2GHZ(c) && IEEE80211_IS_CHAN_B(c))
242 return 0;
243 else if (IEEE80211_IS_CHAN_5GHZ(c) && IEEE80211_IS_CHAN_A(c))
244 return 0;
245 else
246 return 1;
247}
248
edfc8a07
IV
249static uint8_t
250iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc,
251 struct iwm_scan_channel_cfg_lmac *chan, int n_ssids)
24a8d46a 252{
77de6c2d 253 struct ieee80211com *ic = &sc->sc_ic;
24a8d46a 254 struct ieee80211_channel *c;
edfc8a07
IV
255 uint8_t nchan;
256 int j;
24a8d46a 257
edfc8a07
IV
258 for (nchan = j = 0;
259 j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) {
24a8d46a
MD
260 c = &ic->ic_channels[j];
261 /* For 2GHz, only populate 11b channels */
262 /* For 5GHz, only populate 11a channels */
263 /*
264 * Catch other channels, in case we have 900MHz channels or
265 * something in the chanlist.
266 */
e8951a47 267 if (iwm_mvm_scan_skip_channel(c)) {
24a8d46a
MD
268 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
269 "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
e8951a47 270 __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
44cdc399 271 continue;
24a8d46a 272 }
44cdc399 273
24a8d46a
MD
274 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
275 "Adding channel %d (%d Mhz) to the list\n",
e8951a47 276 nchan, c->ic_freq);
edfc8a07
IV
277 chan->channel_num = htole16(ieee80211_mhz2ieee(c->ic_freq, 0));
278 chan->iter_count = htole16(1);
e8951a47 279 chan->iter_interval = htole32(0);
edfc8a07
IV
280 chan->flags = htole32(IWM_UNIFIED_SCAN_CHANNEL_PARTIAL);
281#if 0 /* makes scanning while associated less useful */
282 if (n_ssids != 0)
283 chan->flags |= htole32(1 << 1); /* select SSID 0 */
284#endif
24a8d46a
MD
285 chan++;
286 nchan++;
287 }
edfc8a07 288
24a8d46a
MD
289 return nchan;
290}
291
e8951a47
IV
292static uint8_t
293iwm_mvm_umac_scan_fill_channels(struct iwm_softc *sc,
294 struct iwm_scan_channel_cfg_umac *chan, int n_ssids)
295{
296 struct ieee80211com *ic = &sc->sc_ic;
297 struct ieee80211_channel *c;
298 uint8_t nchan;
299 int j;
300
301 for (nchan = j = 0;
302 j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) {
303 c = &ic->ic_channels[j];
304 /* For 2GHz, only populate 11b channels */
305 /* For 5GHz, only populate 11a channels */
306 /*
307 * Catch other channels, in case we have 900MHz channels or
308 * something in the chanlist.
309 */
310 if (iwm_mvm_scan_skip_channel(c)) {
311 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
312 "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
313 __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
314 continue;
315 }
316
317 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
318 "Adding channel %d (%d Mhz) to the list\n",
319 nchan, c->ic_freq);
320 chan->channel_num = ieee80211_mhz2ieee(c->ic_freq, 0);
321 chan->iter_count = 1;
322 chan->iter_interval = htole16(0);
323 chan->flags = htole32(0);
324#if 0 /* makes scanning while associated less useful */
325 if (n_ssids != 0)
326 chan->flags = htole32(1 << 0); /* select SSID 0 */
327#endif
328 chan++;
329 nchan++;
330 }
331
332 return nchan;
333}
334
edfc8a07
IV
335static int
336iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq)
24a8d46a 337{
edfc8a07 338 struct ieee80211com *ic = &sc->sc_ic;
4aee7a78 339 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
edfc8a07
IV
340 struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf;
341 struct ieee80211_rateset *rs;
342 size_t remain = sizeof(preq->buf);
343 uint8_t *frm, *pos;
344 int ssid_len = 0;
e8951a47 345 const uint8_t *ssid = NULL;
24a8d46a 346
edfc8a07 347 memset(preq, 0, sizeof(*preq));
24a8d46a 348
edfc8a07
IV
349 /* Ensure enough space for header and SSID IE. */
350 if (remain < sizeof(*wh) + 2 + ssid_len)
351 return ENOBUFS;
24a8d46a 352
edfc8a07
IV
353 /*
354 * Build a probe request frame. Most of the following code is a
355 * copy & paste of what is done in net80211.
356 */
357 wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
358 IEEE80211_FC0_SUBTYPE_PROBE_REQ;
359 wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
360 IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
4aee7a78 361 IEEE80211_ADDR_COPY(wh->i_addr2, vap ? vap->iv_myaddr : ic->ic_macaddr);
edfc8a07
IV
362 IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
363 *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */
364 *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */
365
366 frm = (uint8_t *)(wh + 1);
e8951a47 367 frm = ieee80211_add_ssid(frm, ssid, ssid_len);
edfc8a07
IV
368
369 /* Tell the firmware where the MAC header is. */
370 preq->mac_header.offset = 0;
371 preq->mac_header.len = htole16(frm - (uint8_t *)wh);
372 remain -= frm - (uint8_t *)wh;
373
374 /* Fill in 2GHz IEs and tell firmware where they are. */
375 rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
376 if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
377 if (remain < 4 + rs->rs_nrates)
378 return ENOBUFS;
379 } else if (remain < 2 + rs->rs_nrates) {
380 return ENOBUFS;
381 }
382 preq->band_data[0].offset = htole16(frm - (uint8_t *)wh);
383 pos = frm;
384 frm = ieee80211_add_rates(frm, rs);
385 if (rs->rs_nrates > IEEE80211_RATE_SIZE)
386 frm = ieee80211_add_xrates(frm, rs);
387 preq->band_data[0].len = htole16(frm - pos);
388 remain -= frm - pos;
389
390 if (isset(sc->sc_enabled_capa,
391 IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) {
392 if (remain < 3)
393 return ENOBUFS;
394 *frm++ = IEEE80211_ELEMID_DSPARMS;
395 *frm++ = 1;
396 *frm++ = 0;
397 remain -= 3;
398 }
24a8d46a 399
39f8331b 400 if (sc->nvm_data->sku_cap_band_52GHz_enable) {
edfc8a07
IV
401 /* Fill in 5GHz IEs. */
402 rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
403 if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
404 if (remain < 4 + rs->rs_nrates)
405 return ENOBUFS;
406 } else if (remain < 2 + rs->rs_nrates) {
407 return ENOBUFS;
408 }
409 preq->band_data[1].offset = htole16(frm - (uint8_t *)wh);
410 pos = frm;
411 frm = ieee80211_add_rates(frm, rs);
412 if (rs->rs_nrates > IEEE80211_RATE_SIZE)
413 frm = ieee80211_add_xrates(frm, rs);
414 preq->band_data[1].len = htole16(frm - pos);
415 remain -= frm - pos;
416 }
24a8d46a 417
edfc8a07
IV
418 /* Send 11n IEs on both 2GHz and 5GHz bands. */
419 preq->common_data.offset = htole16(frm - (uint8_t *)wh);
420 pos = frm;
421#if 0
422 if (ic->ic_flags & IEEE80211_F_HTON) {
423 if (remain < 28)
424 return ENOBUFS;
425 frm = ieee80211_add_htcaps(frm, ic);
426 /* XXX add WME info? */
24a8d46a 427 }
edfc8a07
IV
428#endif
429 preq->common_data.len = htole16(frm - pos);
24a8d46a 430
edfc8a07 431 return 0;
24a8d46a
MD
432}
433
e8951a47
IV
434int
435iwm_mvm_config_umac_scan(struct iwm_softc *sc)
436{
437 struct ieee80211com *ic = &sc->sc_ic;
4aee7a78
IV
438 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
439
e8951a47 440 struct iwm_scan_config *scan_config;
55a26295 441 int ret, j, nchan;
e8951a47
IV
442 size_t cmd_size;
443 struct ieee80211_channel *c;
444 struct iwm_host_cmd hcmd = {
445 .id = iwm_cmd_id(IWM_SCAN_CFG_CMD, IWM_ALWAYS_LONG_GROUP, 0),
446 .flags = IWM_CMD_SYNC,
447 };
448 static const uint32_t rates = (IWM_SCAN_CONFIG_RATE_1M |
449 IWM_SCAN_CONFIG_RATE_2M | IWM_SCAN_CONFIG_RATE_5M |
450 IWM_SCAN_CONFIG_RATE_11M | IWM_SCAN_CONFIG_RATE_6M |
451 IWM_SCAN_CONFIG_RATE_9M | IWM_SCAN_CONFIG_RATE_12M |
452 IWM_SCAN_CONFIG_RATE_18M | IWM_SCAN_CONFIG_RATE_24M |
453 IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M |
454 IWM_SCAN_CONFIG_RATE_54M);
455
456 cmd_size = sizeof(*scan_config) + sc->sc_capa_n_scan_channels;
457
458 scan_config = kmalloc(cmd_size, M_DEVBUF, M_INTWAIT | M_ZERO);
459 if (scan_config == NULL)
460 return ENOMEM;
461
462 scan_config->tx_chains = htole32(iwm_fw_valid_tx_ant(sc));
463 scan_config->rx_chains = htole32(iwm_fw_valid_rx_ant(sc));
464 scan_config->legacy_rates = htole32(rates |
465 IWM_SCAN_CONFIG_SUPPORTED_RATE(rates));
466
467 /* These timings correspond to iwlwifi's UNASSOC scan. */
468 scan_config->dwell_active = 10;
469 scan_config->dwell_passive = 110;
470 scan_config->dwell_fragmented = 44;
471 scan_config->dwell_extended = 90;
472 scan_config->out_of_channel_time = htole32(0);
473 scan_config->suspend_time = htole32(0);
474
4aee7a78
IV
475 IEEE80211_ADDR_COPY(scan_config->mac_addr,
476 vap ? vap->iv_myaddr : ic->ic_macaddr);
e8951a47
IV
477
478 scan_config->bcast_sta_id = sc->sc_aux_sta.sta_id;
479 scan_config->channel_flags = IWM_CHANNEL_FLAG_EBS |
480 IWM_CHANNEL_FLAG_ACCURATE_EBS | IWM_CHANNEL_FLAG_EBS_ADD |
481 IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
482
55a26295
IV
483 for (nchan = j = 0;
484 j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) {
485 c = &ic->ic_channels[j];
486 /* For 2GHz, only populate 11b channels */
487 /* For 5GHz, only populate 11a channels */
488 /*
489 * Catch other channels, in case we have 900MHz channels or
490 * something in the chanlist.
491 */
492 if (iwm_mvm_scan_skip_channel(c))
e8951a47
IV
493 continue;
494 scan_config->channel_array[nchan++] =
495 ieee80211_mhz2ieee(c->ic_freq, 0);
496 }
497
498 scan_config->flags = htole32(IWM_SCAN_CONFIG_FLAG_ACTIVATE |
499 IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
500 IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS |
501 IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS |
502 IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID |
503 IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES |
504 IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
505 IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR |
506 IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
507 IWM_SCAN_CONFIG_N_CHANNELS(nchan) |
508 IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED);
509
510 hcmd.data[0] = scan_config;
511 hcmd.len[0] = cmd_size;
512
513 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending UMAC scan config\n");
514
515 ret = iwm_send_cmd(sc, &hcmd);
516 if (!ret)
517 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
518 "UMAC scan config was sent successfully\n");
519
520 kfree(scan_config, M_DEVBUF);
521 return ret;
522}
523
524int
525iwm_mvm_umac_scan(struct iwm_softc *sc)
526{
527 struct iwm_host_cmd hcmd = {
528 .id = iwm_cmd_id(IWM_SCAN_REQ_UMAC, IWM_ALWAYS_LONG_GROUP, 0),
529 .len = { 0, },
530 .data = { NULL, },
531 .flags = IWM_CMD_SYNC,
532 };
533 struct iwm_scan_req_umac *req;
534 struct iwm_scan_req_umac_tail *tail;
535 size_t req_len;
536 int ssid_len = 0;
537 const uint8_t *ssid = NULL;
538 int ret;
539
540 req_len = sizeof(struct iwm_scan_req_umac) +
541 (sizeof(struct iwm_scan_channel_cfg_umac) *
542 sc->sc_capa_n_scan_channels) +
543 sizeof(struct iwm_scan_req_umac_tail);
544 if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
545 return ENOMEM;
546 req = kmalloc(req_len, M_DEVBUF, M_INTWAIT | M_ZERO);
547 if (req == NULL)
548 return ENOMEM;
549
550 hcmd.len[0] = (uint16_t)req_len;
551 hcmd.data[0] = (void *)req;
552
553 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Handling ieee80211 scan request\n");
554
555 /* These timings correspond to iwlwifi's UNASSOC scan. */
556 req->active_dwell = 10;
557 req->passive_dwell = 110;
558 req->fragmented_dwell = 44;
559 req->extended_dwell = 90;
560 req->max_out_time = 0;
561 req->suspend_time = 0;
562
563 req->scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
564 req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
565
566 req->n_channels = iwm_mvm_umac_scan_fill_channels(sc,
567 (struct iwm_scan_channel_cfg_umac *)req->data, ssid_len != 0);
568
569 req->general_flags = htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL |
570 IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE |
571 IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL);
572
573 tail = (void *)((char *)&req->data +
574 sizeof(struct iwm_scan_channel_cfg_umac) *
575 sc->sc_capa_n_scan_channels);
576
577 /* Check if we're doing an active directed scan. */
578 if (ssid_len != 0) {
579 tail->direct_scan[0].id = IEEE80211_ELEMID_SSID;
580 tail->direct_scan[0].len = ssid_len;
581 memcpy(tail->direct_scan[0].ssid, ssid, ssid_len);
582 req->general_flags |=
583 htole32(IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT);
584 } else {
585 req->general_flags |= htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE);
586 }
587
588 if (isset(sc->sc_enabled_capa,
589 IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
590 req->general_flags |=
591 htole32(IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED);
592
593 ret = iwm_mvm_fill_probe_req(sc, &tail->preq);
594 if (ret) {
595 kfree(req, M_DEVBUF);
596 return ret;
597 }
598
599 /* Specify the scan plan: We'll do one iteration. */
600 tail->schedule[0].interval = 0;
601 tail->schedule[0].iter_count = 1;
602
603 ret = iwm_send_cmd(sc, &hcmd);
604 if (!ret)
605 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
606 "Scan request was sent successfully\n");
607 kfree(req, M_DEVBUF);
608 return ret;
609}
610
24a8d46a 611int
edfc8a07 612iwm_mvm_lmac_scan(struct iwm_softc *sc)
24a8d46a
MD
613{
614 struct iwm_host_cmd hcmd = {
edfc8a07 615 .id = IWM_SCAN_OFFLOAD_REQUEST_CMD,
24a8d46a 616 .len = { 0, },
edfc8a07 617 .data = { NULL, },
24a8d46a 618 .flags = IWM_CMD_SYNC,
24a8d46a 619 };
edfc8a07
IV
620 struct iwm_scan_req_lmac *req;
621 size_t req_len;
24a8d46a 622 int ret;
edfc8a07
IV
623 int ssid_len = 0;
624 const uint8_t *ssid = NULL;
24a8d46a
MD
625
626 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
627 "Handling ieee80211 scan request\n");
24a8d46a 628
edfc8a07
IV
629 req_len = sizeof(struct iwm_scan_req_lmac) +
630 (sizeof(struct iwm_scan_channel_cfg_lmac) *
631 sc->sc_capa_n_scan_channels) + sizeof(struct iwm_scan_probe_req);
632 if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
633 return ENOMEM;
634 req = kmalloc(req_len, M_DEVBUF, M_INTWAIT | M_ZERO);
635 if (req == NULL)
636 return ENOMEM;
637
638 hcmd.len[0] = (uint16_t)req_len;
639 hcmd.data[0] = (void *)req;
640
641 /* These timings correspond to iwlwifi's UNASSOC scan. */
642 req->active_dwell = 10;
643 req->passive_dwell = 110;
644 req->fragmented_dwell = 44;
645 req->extended_dwell = 90;
646 req->max_out_time = 0;
647 req->suspend_time = 0;
648
649 req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH);
650 req->rx_chain_select = iwm_mvm_scan_rx_chain(sc);
651 req->iter_num = htole32(1);
652 req->delay = 0;
653
654 req->scan_flags = htole32(IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL |
655 IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE |
656 IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL);
657 if (ssid_len == 0)
658 req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAG_PASSIVE);
659 else
660 req->scan_flags |=
661 htole32(IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION);
662 if (isset(sc->sc_enabled_capa,
663 IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
664 req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED);
665
666 req->flags = htole32(IWM_PHY_BAND_24);
39f8331b 667 if (sc->nvm_data->sku_cap_band_52GHz_enable)
edfc8a07
IV
668 req->flags |= htole32(IWM_PHY_BAND_5);
669 req->filter_flags =
670 htole32(IWM_MAC_FILTER_ACCEPT_GRP | IWM_MAC_FILTER_IN_BEACON);
671
672 /* Tx flags 2 GHz. */
673 req->tx_cmd[0].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
674 IWM_TX_CMD_FLG_BT_DIS);
675 req->tx_cmd[0].rate_n_flags =
676 iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_2GHZ, 1/*XXX*/);
677 req->tx_cmd[0].sta_id = sc->sc_aux_sta.sta_id;
24a8d46a 678
edfc8a07
IV
679 /* Tx flags 5 GHz. */
680 req->tx_cmd[1].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
681 IWM_TX_CMD_FLG_BT_DIS);
682 req->tx_cmd[1].rate_n_flags =
683 iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_5GHZ, 1/*XXX*/);
684 req->tx_cmd[1].sta_id = sc->sc_aux_sta.sta_id;
685
686 /* Check if we're doing an active directed scan. */
687 if (ssid_len != 0) {
688 req->direct_scan[0].id = IEEE80211_ELEMID_SSID;
689 req->direct_scan[0].len = ssid_len;
e8951a47 690 memcpy(req->direct_scan[0].ssid, ssid, ssid_len);
edfc8a07 691 }
24a8d46a 692
edfc8a07
IV
693 req->n_channels = iwm_mvm_lmac_scan_fill_channels(sc,
694 (struct iwm_scan_channel_cfg_lmac *)req->data,
695 ssid_len != 0);
696
697 ret = iwm_mvm_fill_probe_req(sc,
698 (struct iwm_scan_probe_req *)(req->data +
699 (sizeof(struct iwm_scan_channel_cfg_lmac) *
700 sc->sc_capa_n_scan_channels)));
701 if (ret) {
702 kfree(req, M_DEVBUF);
703 return ret;
24a8d46a
MD
704 }
705
edfc8a07
IV
706 /* Specify the scan plan: We'll do one iteration. */
707 req->schedule[0].iterations = 1;
708 req->schedule[0].full_scan_mul = 1;
709
710 /* Disable EBS. */
711 req->channel_opt[0].non_ebs_ratio = 1;
712 req->channel_opt[1].non_ebs_ratio = 1;
713
714 ret = iwm_send_cmd(sc, &hcmd);
715 if (!ret) {
24a8d46a
MD
716 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
717 "Scan request was sent successfully\n");
24a8d46a 718 }
edfc8a07 719 kfree(req, M_DEVBUF);
24a8d46a
MD
720 return ret;
721}