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