if_iwm.c revision 330166
1303628Ssbruno/*	$OpenBSD: if_iwm.c,v 1.42 2015/05/30 02:49:23 deraadt Exp $	*/
2286441Srpaulo
3286441Srpaulo/*
4286441Srpaulo * Copyright (c) 2014 genua mbh <info@genua.de>
5286441Srpaulo * Copyright (c) 2014 Fixup Software Ltd.
6286441Srpaulo *
7286441Srpaulo * Permission to use, copy, modify, and distribute this software for any
8286441Srpaulo * purpose with or without fee is hereby granted, provided that the above
9286441Srpaulo * copyright notice and this permission notice appear in all copies.
10286441Srpaulo *
11286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18286441Srpaulo */
19286441Srpaulo
20286441Srpaulo/*-
21286441Srpaulo * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22286441Srpaulo * which were used as the reference documentation for this implementation.
23286441Srpaulo *
24286441Srpaulo * Driver version we are currently based off of is
25286441Srpaulo * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
26286441Srpaulo *
27286441Srpaulo ***********************************************************************
28286441Srpaulo *
29286441Srpaulo * This file is provided under a dual BSD/GPLv2 license.  When using or
30286441Srpaulo * redistributing this file, you may do so under either license.
31286441Srpaulo *
32286441Srpaulo * GPL LICENSE SUMMARY
33286441Srpaulo *
34286441Srpaulo * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
35286441Srpaulo *
36286441Srpaulo * This program is free software; you can redistribute it and/or modify
37286441Srpaulo * it under the terms of version 2 of the GNU General Public License as
38286441Srpaulo * published by the Free Software Foundation.
39286441Srpaulo *
40286441Srpaulo * This program is distributed in the hope that it will be useful, but
41286441Srpaulo * WITHOUT ANY WARRANTY; without even the implied warranty of
42286441Srpaulo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43286441Srpaulo * General Public License for more details.
44286441Srpaulo *
45286441Srpaulo * You should have received a copy of the GNU General Public License
46286441Srpaulo * along with this program; if not, write to the Free Software
47286441Srpaulo * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
48286441Srpaulo * USA
49286441Srpaulo *
50286441Srpaulo * The full GNU General Public License is included in this distribution
51286441Srpaulo * in the file called COPYING.
52286441Srpaulo *
53286441Srpaulo * Contact Information:
54286441Srpaulo *  Intel Linux Wireless <ilw@linux.intel.com>
55286441Srpaulo * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
56286441Srpaulo *
57286441Srpaulo *
58286441Srpaulo * BSD LICENSE
59286441Srpaulo *
60286441Srpaulo * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61286441Srpaulo * All rights reserved.
62286441Srpaulo *
63286441Srpaulo * Redistribution and use in source and binary forms, with or without
64286441Srpaulo * modification, are permitted provided that the following conditions
65286441Srpaulo * are met:
66286441Srpaulo *
67286441Srpaulo *  * Redistributions of source code must retain the above copyright
68286441Srpaulo *    notice, this list of conditions and the following disclaimer.
69286441Srpaulo *  * Redistributions in binary form must reproduce the above copyright
70286441Srpaulo *    notice, this list of conditions and the following disclaimer in
71286441Srpaulo *    the documentation and/or other materials provided with the
72286441Srpaulo *    distribution.
73286441Srpaulo *  * Neither the name Intel Corporation nor the names of its
74286441Srpaulo *    contributors may be used to endorse or promote products derived
75286441Srpaulo *    from this software without specific prior written permission.
76286441Srpaulo *
77286441Srpaulo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78286441Srpaulo * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79286441Srpaulo * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80286441Srpaulo * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81286441Srpaulo * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82286441Srpaulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83286441Srpaulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84286441Srpaulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85286441Srpaulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86286441Srpaulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87286441Srpaulo * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
88286441Srpaulo */
89286441Srpaulo
90286441Srpaulo/*-
91286441Srpaulo * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
92286441Srpaulo *
93286441Srpaulo * Permission to use, copy, modify, and distribute this software for any
94286441Srpaulo * purpose with or without fee is hereby granted, provided that the above
95286441Srpaulo * copyright notice and this permission notice appear in all copies.
96286441Srpaulo *
97286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
100286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
101286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
102286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
103286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
104286441Srpaulo */
105286441Srpaulo#include <sys/cdefs.h>
106286441Srpaulo__FBSDID("$FreeBSD: stable/11/sys/dev/iwm/if_iwm.c 330166 2018-03-01 05:43:10Z eadler $");
107286441Srpaulo
108300248Savos#include "opt_wlan.h"
109300248Savos
110286441Srpaulo#include <sys/param.h>
111286441Srpaulo#include <sys/bus.h>
112286441Srpaulo#include <sys/conf.h>
113286441Srpaulo#include <sys/endian.h>
114286441Srpaulo#include <sys/firmware.h>
115286441Srpaulo#include <sys/kernel.h>
116286441Srpaulo#include <sys/malloc.h>
117286441Srpaulo#include <sys/mbuf.h>
118286441Srpaulo#include <sys/mutex.h>
119286441Srpaulo#include <sys/module.h>
120286441Srpaulo#include <sys/proc.h>
121286441Srpaulo#include <sys/rman.h>
122286441Srpaulo#include <sys/socket.h>
123286441Srpaulo#include <sys/sockio.h>
124286441Srpaulo#include <sys/sysctl.h>
125286441Srpaulo#include <sys/linker.h>
126286441Srpaulo
127286441Srpaulo#include <machine/bus.h>
128286441Srpaulo#include <machine/endian.h>
129286441Srpaulo#include <machine/resource.h>
130286441Srpaulo
131286441Srpaulo#include <dev/pci/pcivar.h>
132286441Srpaulo#include <dev/pci/pcireg.h>
133286441Srpaulo
134286441Srpaulo#include <net/bpf.h>
135286441Srpaulo
136286441Srpaulo#include <net/if.h>
137286441Srpaulo#include <net/if_var.h>
138286441Srpaulo#include <net/if_arp.h>
139286441Srpaulo#include <net/if_dl.h>
140286441Srpaulo#include <net/if_media.h>
141286441Srpaulo#include <net/if_types.h>
142286441Srpaulo
143286441Srpaulo#include <netinet/in.h>
144286441Srpaulo#include <netinet/in_systm.h>
145286441Srpaulo#include <netinet/if_ether.h>
146286441Srpaulo#include <netinet/ip.h>
147286441Srpaulo
148286441Srpaulo#include <net80211/ieee80211_var.h>
149286441Srpaulo#include <net80211/ieee80211_regdomain.h>
150286441Srpaulo#include <net80211/ieee80211_ratectl.h>
151286441Srpaulo#include <net80211/ieee80211_radiotap.h>
152286441Srpaulo
153286475Srpaulo#include <dev/iwm/if_iwmreg.h>
154286475Srpaulo#include <dev/iwm/if_iwmvar.h>
155286475Srpaulo#include <dev/iwm/if_iwm_debug.h>
156286475Srpaulo#include <dev/iwm/if_iwm_util.h>
157286475Srpaulo#include <dev/iwm/if_iwm_binding.h>
158286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h>
159286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h>
160286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h>
161286475Srpaulo#include <dev/iwm/if_iwm_time_event.h>
162286475Srpaulo#include <dev/iwm/if_iwm_power.h>
163286475Srpaulo#include <dev/iwm/if_iwm_scan.h>
164286441Srpaulo
165286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h>
166301187Sadrian#include <dev/iwm/if_iwm_led.h>
167286441Srpaulo
168330166Seadler#define IWM_NVM_HW_SECTION_NUM_FAMILY_7000	0
169330166Seadler#define IWM_NVM_HW_SECTION_NUM_FAMILY_8000	10
170330166Seadler
171330166Seadler/* lower blocks contain EEPROM image and calibration data */
172330166Seadler#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000	(16 * 512 * sizeof(uint16_t)) /* 16 KB */
173330166Seadler#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000	(32 * 512 * sizeof(uint16_t)) /* 32 KB */
174330166Seadler
175330166Seadler#define IWM7260_FW	"iwm7260fw"
176330166Seadler#define IWM3160_FW	"iwm3160fw"
177330166Seadler#define IWM7265_FW	"iwm7265fw"
178330166Seadler#define IWM7265D_FW	"iwm7265Dfw"
179330166Seadler#define IWM8000_FW	"iwm8000Cfw"
180330166Seadler
181330166Seadler#define IWM_DEVICE_7000_COMMON						\
182330166Seadler	.device_family = IWM_DEVICE_FAMILY_7000,			\
183330166Seadler	.eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000,		\
184330166Seadler	.nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_7000
185330166Seadler
186330166Seadlerconst struct iwm_cfg iwm7260_cfg = {
187330166Seadler	.fw_name = IWM7260_FW,
188330166Seadler	IWM_DEVICE_7000_COMMON,
189330166Seadler	.host_interrupt_operation_mode = 1,
190330166Seadler};
191330166Seadler
192330166Seadlerconst struct iwm_cfg iwm3160_cfg = {
193330166Seadler	.fw_name = IWM3160_FW,
194330166Seadler	IWM_DEVICE_7000_COMMON,
195330166Seadler	.host_interrupt_operation_mode = 1,
196330166Seadler};
197330166Seadler
198330166Seadlerconst struct iwm_cfg iwm3165_cfg = {
199330166Seadler	/* XXX IWM7265D_FW doesn't seem to work properly yet */
200330166Seadler	.fw_name = IWM7265_FW,
201330166Seadler	IWM_DEVICE_7000_COMMON,
202330166Seadler	.host_interrupt_operation_mode = 0,
203330166Seadler};
204330166Seadler
205330166Seadlerconst struct iwm_cfg iwm7265_cfg = {
206330166Seadler	.fw_name = IWM7265_FW,
207330166Seadler	IWM_DEVICE_7000_COMMON,
208330166Seadler	.host_interrupt_operation_mode = 0,
209330166Seadler};
210330166Seadler
211330166Seadlerconst struct iwm_cfg iwm7265d_cfg = {
212330166Seadler	/* XXX IWM7265D_FW doesn't seem to work properly yet */
213330166Seadler	.fw_name = IWM7265_FW,
214330166Seadler	IWM_DEVICE_7000_COMMON,
215330166Seadler	.host_interrupt_operation_mode = 0,
216330166Seadler};
217330166Seadler
218330166Seadler#define IWM_DEVICE_8000_COMMON						\
219330166Seadler	.device_family = IWM_DEVICE_FAMILY_8000,			\
220330166Seadler	.eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000,		\
221330166Seadler	.nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_8000
222330166Seadler
223330166Seadlerconst struct iwm_cfg iwm8260_cfg = {
224330166Seadler	.fw_name = IWM8000_FW,
225330166Seadler	IWM_DEVICE_8000_COMMON,
226330166Seadler	.host_interrupt_operation_mode = 0,
227330166Seadler};
228330166Seadler
229286441Srpauloconst uint8_t iwm_nvm_channels[] = {
230286441Srpaulo	/* 2.4 GHz */
231286441Srpaulo	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
232286441Srpaulo	/* 5 GHz */
233298877Savos	36, 40, 44, 48, 52, 56, 60, 64,
234286441Srpaulo	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
235286441Srpaulo	149, 153, 157, 161, 165
236286441Srpaulo};
237298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS,
238298877Savos    "IWM_NUM_CHANNELS is too small");
239298877Savos
240303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = {
241303628Ssbruno	/* 2.4 GHz */
242303628Ssbruno	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
243303628Ssbruno	/* 5 GHz */
244303628Ssbruno	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
245303628Ssbruno	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
246303628Ssbruno	149, 153, 157, 161, 165, 169, 173, 177, 181
247303628Ssbruno};
248303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000,
249303628Ssbruno    "IWM_NUM_CHANNELS_8000 is too small");
250303628Ssbruno
251303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS	14
252303628Ssbruno#define IWM_N_HW_ADDR_MASK	0xF
253303628Ssbruno
254286441Srpaulo/*
255286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries
256286441Srpaulo * that are populated.
257286441Srpaulo */
258286441Srpauloconst struct iwm_rate {
259286441Srpaulo	uint8_t rate;
260286441Srpaulo	uint8_t plcp;
261286441Srpaulo} iwm_rates[] = {
262286441Srpaulo	{   2,	IWM_RATE_1M_PLCP  },
263286441Srpaulo	{   4,	IWM_RATE_2M_PLCP  },
264286441Srpaulo	{  11,	IWM_RATE_5M_PLCP  },
265286441Srpaulo	{  22,	IWM_RATE_11M_PLCP },
266286441Srpaulo	{  12,	IWM_RATE_6M_PLCP  },
267286441Srpaulo	{  18,	IWM_RATE_9M_PLCP  },
268286441Srpaulo	{  24,	IWM_RATE_12M_PLCP },
269286441Srpaulo	{  36,	IWM_RATE_18M_PLCP },
270286441Srpaulo	{  48,	IWM_RATE_24M_PLCP },
271286441Srpaulo	{  72,	IWM_RATE_36M_PLCP },
272286441Srpaulo	{  96,	IWM_RATE_48M_PLCP },
273286441Srpaulo	{ 108,	IWM_RATE_54M_PLCP },
274286441Srpaulo};
275286441Srpaulo#define IWM_RIDX_CCK	0
276286441Srpaulo#define IWM_RIDX_OFDM	4
277286441Srpaulo#define IWM_RIDX_MAX	(nitems(iwm_rates)-1)
278286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
279286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
280286441Srpaulo
281303628Ssbrunostruct iwm_nvm_section {
282303628Ssbruno	uint16_t length;
283303628Ssbruno	uint8_t *data;
284303628Ssbruno};
285303628Ssbruno
286286441Srpaulostatic int	iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t);
287286441Srpaulostatic int	iwm_firmware_store_section(struct iwm_softc *,
288286441Srpaulo                                           enum iwm_ucode_type,
289286441Srpaulo                                           const uint8_t *, size_t);
290286441Srpaulostatic int	iwm_set_default_calib(struct iwm_softc *, const void *);
291286441Srpaulostatic void	iwm_fw_info_free(struct iwm_fw_info *);
292286441Srpaulostatic int	iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type);
293286441Srpaulostatic void	iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int);
294286441Srpaulostatic int	iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *,
295286441Srpaulo                                     bus_size_t, bus_size_t);
296286441Srpaulostatic void	iwm_dma_contig_free(struct iwm_dma_info *);
297286441Srpaulostatic int	iwm_alloc_fwmem(struct iwm_softc *);
298286441Srpaulostatic int	iwm_alloc_sched(struct iwm_softc *);
299286441Srpaulostatic int	iwm_alloc_kw(struct iwm_softc *);
300286441Srpaulostatic int	iwm_alloc_ict(struct iwm_softc *);
301286441Srpaulostatic int	iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
302301191Sadrianstatic void	iwm_disable_rx_dma(struct iwm_softc *);
303286441Srpaulostatic void	iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
304286441Srpaulostatic void	iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
305286441Srpaulostatic int	iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *,
306286441Srpaulo                                  int);
307286441Srpaulostatic void	iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
308286441Srpaulostatic void	iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
309286441Srpaulostatic void	iwm_enable_interrupts(struct iwm_softc *);
310286441Srpaulostatic void	iwm_restore_interrupts(struct iwm_softc *);
311286441Srpaulostatic void	iwm_disable_interrupts(struct iwm_softc *);
312286441Srpaulostatic void	iwm_ict_reset(struct iwm_softc *);
313286441Srpaulostatic int	iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *);
314286441Srpaulostatic void	iwm_stop_device(struct iwm_softc *);
315286441Srpaulostatic void	iwm_mvm_nic_config(struct iwm_softc *);
316286441Srpaulostatic int	iwm_nic_rx_init(struct iwm_softc *);
317286441Srpaulostatic int	iwm_nic_tx_init(struct iwm_softc *);
318286441Srpaulostatic int	iwm_nic_init(struct iwm_softc *);
319303628Ssbrunostatic int	iwm_enable_txq(struct iwm_softc *, int, int, int);
320286441Srpaulostatic int	iwm_post_alive(struct iwm_softc *);
321286441Srpaulostatic int	iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t,
322286441Srpaulo                                   uint16_t, uint8_t *, uint16_t *);
323286441Srpaulostatic int	iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
324330165Seadler				     uint16_t *, uint32_t);
325298877Savosstatic uint32_t	iwm_eeprom_channel_flags(uint16_t);
326298877Savosstatic void	iwm_add_channel_band(struct iwm_softc *,
327303628Ssbruno		    struct ieee80211_channel[], int, int *, int, size_t,
328298877Savos		    const uint8_t[]);
329298877Savosstatic void	iwm_init_channel_map(struct ieee80211com *, int, int *,
330298877Savos		    struct ieee80211_channel[]);
331330165Seadlerstatic struct iwm_nvm_data *
332330165Seadler	iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
333330165Seadler			   const uint16_t *, const uint16_t *,
334330165Seadler			   const uint16_t *, const uint16_t *,
335330165Seadler			   const uint16_t *);
336330165Seadlerstatic void	iwm_free_nvm_data(struct iwm_nvm_data *);
337330165Seadlerstatic void	iwm_set_hw_address_family_8000(struct iwm_softc *,
338330165Seadler					       struct iwm_nvm_data *,
339330165Seadler					       const uint16_t *,
340330165Seadler					       const uint16_t *);
341303628Ssbrunostatic int	iwm_get_sku(const struct iwm_softc *, const uint16_t *,
342303628Ssbruno			    const uint16_t *);
343303628Ssbrunostatic int	iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *);
344303628Ssbrunostatic int	iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *,
345303628Ssbruno				  const uint16_t *);
346303628Ssbrunostatic int	iwm_get_n_hw_addrs(const struct iwm_softc *,
347303628Ssbruno				   const uint16_t *);
348303628Ssbrunostatic void	iwm_set_radio_cfg(const struct iwm_softc *,
349303628Ssbruno				  struct iwm_nvm_data *, uint32_t);
350330165Seadlerstatic struct iwm_nvm_data *
351330165Seadler	iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *);
352286441Srpaulostatic int	iwm_nvm_init(struct iwm_softc *);
353303628Ssbrunostatic int	iwm_firmware_load_sect(struct iwm_softc *, uint32_t,
354303628Ssbruno                                       const uint8_t *, uint32_t);
355286441Srpaulostatic int	iwm_firmware_load_chunk(struct iwm_softc *, uint32_t,
356286441Srpaulo                                        const uint8_t *, uint32_t);
357303628Ssbrunostatic int	iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type);
358303628Ssbrunostatic int	iwm_load_cpu_sections_8000(struct iwm_softc *,
359303628Ssbruno					   struct iwm_fw_sects *, int , int *);
360303628Ssbrunostatic int	iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type);
361286441Srpaulostatic int	iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type);
362286441Srpaulostatic int	iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type);
363286441Srpaulostatic int	iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t);
364286441Srpaulostatic int	iwm_send_phy_cfg_cmd(struct iwm_softc *);
365286441Srpaulostatic int	iwm_mvm_load_ucode_wait_alive(struct iwm_softc *,
366286441Srpaulo                                              enum iwm_ucode_type);
367286441Srpaulostatic int	iwm_run_init_mvm_ucode(struct iwm_softc *, int);
368286441Srpaulostatic int	iwm_rx_addbuf(struct iwm_softc *, int, int);
369286441Srpaulostatic int	iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *);
370286441Srpaulostatic int	iwm_mvm_get_signal_strength(struct iwm_softc *,
371286441Srpaulo					    struct iwm_rx_phy_info *);
372286441Srpaulostatic void	iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *,
373286441Srpaulo                                      struct iwm_rx_packet *,
374286441Srpaulo                                      struct iwm_rx_data *);
375330144Seadlerstatic int	iwm_get_noise(struct iwm_softc *sc,
376330144Seadler		    const struct iwm_mvm_statistics_rx_non_phy *);
377286441Srpaulostatic void	iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
378286441Srpaulo                                   struct iwm_rx_data *);
379293100Savosstatic int	iwm_mvm_rx_tx_cmd_single(struct iwm_softc *,
380286441Srpaulo                                         struct iwm_rx_packet *,
381286441Srpaulo				         struct iwm_node *);
382286441Srpaulostatic void	iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *,
383286441Srpaulo                                  struct iwm_rx_data *);
384286441Srpaulostatic void	iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *);
385286441Srpaulo#if 0
386286441Srpaulostatic void	iwm_update_sched(struct iwm_softc *, int, int, uint8_t,
387286441Srpaulo                                 uint16_t);
388286441Srpaulo#endif
389286441Srpaulostatic const struct iwm_rate *
390286441Srpaulo	iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *,
391330155Seadler			struct mbuf *, struct iwm_tx_cmd *);
392286441Srpaulostatic int	iwm_tx(struct iwm_softc *, struct mbuf *,
393286441Srpaulo                       struct ieee80211_node *, int);
394286441Srpaulostatic int	iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
395286441Srpaulo			     const struct ieee80211_bpf_params *);
396330154Seadlerstatic int	iwm_mvm_flush_tx_path(struct iwm_softc *sc,
397330154Seadler				      uint32_t tfd_msk, uint32_t flags);
398286441Srpaulostatic int	iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *,
399303628Ssbruno					        struct iwm_mvm_add_sta_cmd_v7 *,
400286441Srpaulo                                                int *);
401286441Srpaulostatic int	iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *,
402286441Srpaulo                                       int);
403286441Srpaulostatic int	iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *);
404286441Srpaulostatic int	iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *);
405286441Srpaulostatic int	iwm_mvm_add_int_sta_common(struct iwm_softc *,
406286441Srpaulo                                           struct iwm_int_sta *,
407286441Srpaulo				           const uint8_t *, uint16_t, uint16_t);
408286441Srpaulostatic int	iwm_mvm_add_aux_sta(struct iwm_softc *);
409286441Srpaulostatic int	iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *);
410286441Srpaulostatic int	iwm_auth(struct ieee80211vap *, struct iwm_softc *);
411286441Srpaulostatic int	iwm_assoc(struct ieee80211vap *, struct iwm_softc *);
412286441Srpaulostatic int	iwm_release(struct iwm_softc *, struct iwm_node *);
413286441Srpaulostatic struct ieee80211_node *
414286441Srpaulo		iwm_node_alloc(struct ieee80211vap *,
415286441Srpaulo		               const uint8_t[IEEE80211_ADDR_LEN]);
416286441Srpaulostatic void	iwm_setrates(struct iwm_softc *, struct iwm_node *);
417286441Srpaulostatic int	iwm_media_change(struct ifnet *);
418286441Srpaulostatic int	iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int);
419286441Srpaulostatic void	iwm_endscan_cb(void *, int);
420303628Ssbrunostatic void	iwm_mvm_fill_sf_command(struct iwm_softc *,
421303628Ssbruno					struct iwm_sf_cfg_cmd *,
422303628Ssbruno					struct ieee80211_node *);
423303628Ssbrunostatic int	iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state);
424303628Ssbrunostatic int	iwm_send_bt_init_conf(struct iwm_softc *);
425303628Ssbrunostatic int	iwm_send_update_mcc_cmd(struct iwm_softc *, const char *);
426303628Ssbrunostatic void	iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t);
427286441Srpaulostatic int	iwm_init_hw(struct iwm_softc *);
428287197Sglebiusstatic void	iwm_init(struct iwm_softc *);
429287197Sglebiusstatic void	iwm_start(struct iwm_softc *);
430287197Sglebiusstatic void	iwm_stop(struct iwm_softc *);
431286441Srpaulostatic void	iwm_watchdog(void *);
432287197Sglebiusstatic void	iwm_parent(struct ieee80211com *);
433286441Srpaulo#ifdef IWM_DEBUG
434286441Srpaulostatic const char *
435286441Srpaulo		iwm_desc_lookup(uint32_t);
436286441Srpaulostatic void	iwm_nic_error(struct iwm_softc *);
437303628Ssbrunostatic void	iwm_nic_umac_error(struct iwm_softc *);
438286441Srpaulo#endif
439286441Srpaulostatic void	iwm_notif_intr(struct iwm_softc *);
440286441Srpaulostatic void	iwm_intr(void *);
441286441Srpaulostatic int	iwm_attach(device_t);
442303628Ssbrunostatic int	iwm_is_valid_ether_addr(uint8_t *);
443286441Srpaulostatic void	iwm_preinit(void *);
444286441Srpaulostatic int	iwm_detach_local(struct iwm_softc *sc, int);
445286441Srpaulostatic void	iwm_init_task(void *);
446286441Srpaulostatic void	iwm_radiotap_attach(struct iwm_softc *);
447286441Srpaulostatic struct ieee80211vap *
448286441Srpaulo		iwm_vap_create(struct ieee80211com *,
449286441Srpaulo		               const char [IFNAMSIZ], int,
450286441Srpaulo		               enum ieee80211_opmode, int,
451286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN],
452286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN]);
453286441Srpaulostatic void	iwm_vap_delete(struct ieee80211vap *);
454286441Srpaulostatic void	iwm_scan_start(struct ieee80211com *);
455286441Srpaulostatic void	iwm_scan_end(struct ieee80211com *);
456286441Srpaulostatic void	iwm_update_mcast(struct ieee80211com *);
457286441Srpaulostatic void	iwm_set_channel(struct ieee80211com *);
458286441Srpaulostatic void	iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long);
459286441Srpaulostatic void	iwm_scan_mindwell(struct ieee80211_scan_state *);
460286441Srpaulostatic int	iwm_detach(device_t);
461286441Srpaulo
462286441Srpaulo/*
463286441Srpaulo * Firmware parser.
464286441Srpaulo */
465286441Srpaulo
466286441Srpaulostatic int
467286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen)
468286441Srpaulo{
469286441Srpaulo	const struct iwm_fw_cscheme_list *l = (const void *)data;
470286441Srpaulo
471286441Srpaulo	if (dlen < sizeof(*l) ||
472286441Srpaulo	    dlen < sizeof(l->size) + l->size * sizeof(*l->cs))
473286441Srpaulo		return EINVAL;
474286441Srpaulo
475286441Srpaulo	/* we don't actually store anything for now, always use s/w crypto */
476286441Srpaulo
477286441Srpaulo	return 0;
478286441Srpaulo}
479286441Srpaulo
480286441Srpaulostatic int
481286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc,
482286441Srpaulo    enum iwm_ucode_type type, const uint8_t *data, size_t dlen)
483286441Srpaulo{
484286441Srpaulo	struct iwm_fw_sects *fws;
485286441Srpaulo	struct iwm_fw_onesect *fwone;
486286441Srpaulo
487286441Srpaulo	if (type >= IWM_UCODE_TYPE_MAX)
488286441Srpaulo		return EINVAL;
489286441Srpaulo	if (dlen < sizeof(uint32_t))
490286441Srpaulo		return EINVAL;
491286441Srpaulo
492286441Srpaulo	fws = &sc->sc_fw.fw_sects[type];
493286441Srpaulo	if (fws->fw_count >= IWM_UCODE_SECT_MAX)
494286441Srpaulo		return EINVAL;
495286441Srpaulo
496286441Srpaulo	fwone = &fws->fw_sect[fws->fw_count];
497286441Srpaulo
498286441Srpaulo	/* first 32bit are device load offset */
499286441Srpaulo	memcpy(&fwone->fws_devoff, data, sizeof(uint32_t));
500286441Srpaulo
501286441Srpaulo	/* rest is data */
502286441Srpaulo	fwone->fws_data = data + sizeof(uint32_t);
503286441Srpaulo	fwone->fws_len = dlen - sizeof(uint32_t);
504286441Srpaulo
505286441Srpaulo	fws->fw_count++;
506286441Srpaulo
507286441Srpaulo	return 0;
508286441Srpaulo}
509286441Srpaulo
510330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40
511330152Seadler
512286441Srpaulo/* iwlwifi: iwl-drv.c */
513286441Srpaulostruct iwm_tlv_calib_data {
514286441Srpaulo	uint32_t ucode_type;
515286441Srpaulo	struct iwm_tlv_calib_ctrl calib;
516286441Srpaulo} __packed;
517286441Srpaulo
518286441Srpaulostatic int
519286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data)
520286441Srpaulo{
521286441Srpaulo	const struct iwm_tlv_calib_data *def_calib = data;
522286441Srpaulo	uint32_t ucode_type = le32toh(def_calib->ucode_type);
523286441Srpaulo
524286441Srpaulo	if (ucode_type >= IWM_UCODE_TYPE_MAX) {
525286441Srpaulo		device_printf(sc->sc_dev,
526286441Srpaulo		    "Wrong ucode_type %u for default "
527286441Srpaulo		    "calibration.\n", ucode_type);
528286441Srpaulo		return EINVAL;
529286441Srpaulo	}
530286441Srpaulo
531286441Srpaulo	sc->sc_default_calib[ucode_type].flow_trigger =
532286441Srpaulo	    def_calib->calib.flow_trigger;
533286441Srpaulo	sc->sc_default_calib[ucode_type].event_trigger =
534286441Srpaulo	    def_calib->calib.event_trigger;
535286441Srpaulo
536286441Srpaulo	return 0;
537286441Srpaulo}
538286441Srpaulo
539286441Srpaulostatic void
540286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw)
541286441Srpaulo{
542293177Savos	firmware_put(fw->fw_fp, FIRMWARE_UNLOAD);
543293177Savos	fw->fw_fp = NULL;
544286441Srpaulo	/* don't touch fw->fw_status */
545286441Srpaulo	memset(fw->fw_sects, 0, sizeof(fw->fw_sects));
546286441Srpaulo}
547286441Srpaulo
548286441Srpaulostatic int
549286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
550286441Srpaulo{
551286441Srpaulo	struct iwm_fw_info *fw = &sc->sc_fw;
552286441Srpaulo	const struct iwm_tlv_ucode_header *uhdr;
553286441Srpaulo	struct iwm_ucode_tlv tlv;
554286441Srpaulo	enum iwm_ucode_tlv_type tlv_type;
555286441Srpaulo	const struct firmware *fwp;
556286441Srpaulo	const uint8_t *data;
557286441Srpaulo	int error = 0;
558286441Srpaulo	size_t len;
559286441Srpaulo
560286441Srpaulo	if (fw->fw_status == IWM_FW_STATUS_DONE &&
561286441Srpaulo	    ucode_type != IWM_UCODE_TYPE_INIT)
562286441Srpaulo		return 0;
563286441Srpaulo
564286441Srpaulo	while (fw->fw_status == IWM_FW_STATUS_INPROGRESS)
565286441Srpaulo		msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0);
566286441Srpaulo	fw->fw_status = IWM_FW_STATUS_INPROGRESS;
567286441Srpaulo
568293177Savos	if (fw->fw_fp != NULL)
569286441Srpaulo		iwm_fw_info_free(fw);
570286441Srpaulo
571286441Srpaulo	/*
572286441Srpaulo	 * Load firmware into driver memory.
573293177Savos	 * fw_fp will be set.
574286441Srpaulo	 */
575286441Srpaulo	IWM_UNLOCK(sc);
576330166Seadler	fwp = firmware_get(sc->cfg->fw_name);
577293177Savos	IWM_LOCK(sc);
578286441Srpaulo	if (fwp == NULL) {
579286441Srpaulo		device_printf(sc->sc_dev,
580286441Srpaulo		    "could not read firmware %s (error %d)\n",
581330166Seadler		    sc->cfg->fw_name, error);
582286441Srpaulo		goto out;
583286441Srpaulo	}
584293177Savos	fw->fw_fp = fwp;
585286441Srpaulo
586303628Ssbruno	/* (Re-)Initialize default values. */
587303628Ssbruno	sc->sc_capaflags = 0;
588330152Seadler	sc->sc_capa_n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS;
589303628Ssbruno	memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa));
590303628Ssbruno	memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc));
591303628Ssbruno
592286441Srpaulo	/*
593286441Srpaulo	 * Parse firmware contents
594286441Srpaulo	 */
595286441Srpaulo
596293177Savos	uhdr = (const void *)fw->fw_fp->data;
597293177Savos	if (*(const uint32_t *)fw->fw_fp->data != 0
598286441Srpaulo	    || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) {
599286441Srpaulo		device_printf(sc->sc_dev, "invalid firmware %s\n",
600330166Seadler		    sc->cfg->fw_name);
601286441Srpaulo		error = EINVAL;
602286441Srpaulo		goto out;
603286441Srpaulo	}
604286441Srpaulo
605303628Ssbruno	snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)",
606303628Ssbruno	    IWM_UCODE_MAJOR(le32toh(uhdr->ver)),
607303628Ssbruno	    IWM_UCODE_MINOR(le32toh(uhdr->ver)),
608303628Ssbruno	    IWM_UCODE_API(le32toh(uhdr->ver)));
609286441Srpaulo	data = uhdr->data;
610293177Savos	len = fw->fw_fp->datasize - sizeof(*uhdr);
611286441Srpaulo
612286441Srpaulo	while (len >= sizeof(tlv)) {
613286441Srpaulo		size_t tlv_len;
614286441Srpaulo		const void *tlv_data;
615286441Srpaulo
616286441Srpaulo		memcpy(&tlv, data, sizeof(tlv));
617286441Srpaulo		tlv_len = le32toh(tlv.length);
618286441Srpaulo		tlv_type = le32toh(tlv.type);
619286441Srpaulo
620286441Srpaulo		len -= sizeof(tlv);
621286441Srpaulo		data += sizeof(tlv);
622286441Srpaulo		tlv_data = data;
623286441Srpaulo
624286441Srpaulo		if (len < tlv_len) {
625286441Srpaulo			device_printf(sc->sc_dev,
626286441Srpaulo			    "firmware too short: %zu bytes\n",
627286441Srpaulo			    len);
628286441Srpaulo			error = EINVAL;
629286441Srpaulo			goto parse_out;
630286441Srpaulo		}
631286441Srpaulo
632286441Srpaulo		switch ((int)tlv_type) {
633286441Srpaulo		case IWM_UCODE_TLV_PROBE_MAX_LEN:
634286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
635286441Srpaulo				device_printf(sc->sc_dev,
636286441Srpaulo				    "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n",
637286441Srpaulo				    __func__,
638286441Srpaulo				    (int) tlv_len);
639286441Srpaulo				error = EINVAL;
640286441Srpaulo				goto parse_out;
641286441Srpaulo			}
642286441Srpaulo			sc->sc_capa_max_probe_len
643286441Srpaulo			    = le32toh(*(const uint32_t *)tlv_data);
644286441Srpaulo			/* limit it to something sensible */
645303628Ssbruno			if (sc->sc_capa_max_probe_len >
646303628Ssbruno			    IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) {
647286441Srpaulo				IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
648286441Srpaulo				    "%s: IWM_UCODE_TLV_PROBE_MAX_LEN "
649286441Srpaulo				    "ridiculous\n", __func__);
650286441Srpaulo				error = EINVAL;
651286441Srpaulo				goto parse_out;
652286441Srpaulo			}
653286441Srpaulo			break;
654286441Srpaulo		case IWM_UCODE_TLV_PAN:
655286441Srpaulo			if (tlv_len) {
656286441Srpaulo				device_printf(sc->sc_dev,
657286441Srpaulo				    "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n",
658286441Srpaulo				    __func__,
659286441Srpaulo				    (int) tlv_len);
660286441Srpaulo				error = EINVAL;
661286441Srpaulo				goto parse_out;
662286441Srpaulo			}
663286441Srpaulo			sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN;
664286441Srpaulo			break;
665286441Srpaulo		case IWM_UCODE_TLV_FLAGS:
666286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
667286441Srpaulo				device_printf(sc->sc_dev,
668286441Srpaulo				    "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n",
669286441Srpaulo				    __func__,
670286441Srpaulo				    (int) tlv_len);
671286441Srpaulo				error = EINVAL;
672286441Srpaulo				goto parse_out;
673286441Srpaulo			}
674286441Srpaulo			/*
675286441Srpaulo			 * Apparently there can be many flags, but Linux driver
676286441Srpaulo			 * parses only the first one, and so do we.
677286441Srpaulo			 *
678286441Srpaulo			 * XXX: why does this override IWM_UCODE_TLV_PAN?
679286441Srpaulo			 * Intentional or a bug?  Observations from
680286441Srpaulo			 * current firmware file:
681286441Srpaulo			 *  1) TLV_PAN is parsed first
682286441Srpaulo			 *  2) TLV_FLAGS contains TLV_FLAGS_PAN
683286441Srpaulo			 * ==> this resets TLV_PAN to itself... hnnnk
684286441Srpaulo			 */
685286441Srpaulo			sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data);
686286441Srpaulo			break;
687286441Srpaulo		case IWM_UCODE_TLV_CSCHEME:
688286441Srpaulo			if ((error = iwm_store_cscheme(sc,
689286441Srpaulo			    tlv_data, tlv_len)) != 0) {
690286441Srpaulo				device_printf(sc->sc_dev,
691286441Srpaulo				    "%s: iwm_store_cscheme(): returned %d\n",
692286441Srpaulo				    __func__,
693286441Srpaulo				    error);
694286441Srpaulo				goto parse_out;
695286441Srpaulo			}
696286441Srpaulo			break;
697303628Ssbruno		case IWM_UCODE_TLV_NUM_OF_CPU: {
698303628Ssbruno			uint32_t num_cpu;
699286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
700286441Srpaulo				device_printf(sc->sc_dev,
701286441Srpaulo				    "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n",
702286441Srpaulo				    __func__,
703286441Srpaulo				    (int) tlv_len);
704286441Srpaulo				error = EINVAL;
705286441Srpaulo				goto parse_out;
706286441Srpaulo			}
707303628Ssbruno			num_cpu = le32toh(*(const uint32_t *)tlv_data);
708303628Ssbruno			if (num_cpu < 1 || num_cpu > 2) {
709286441Srpaulo				device_printf(sc->sc_dev,
710303628Ssbruno				    "%s: Driver supports only 1 or 2 CPUs\n",
711286441Srpaulo				    __func__);
712286441Srpaulo				error = EINVAL;
713286441Srpaulo				goto parse_out;
714286441Srpaulo			}
715286441Srpaulo			break;
716303628Ssbruno		}
717286441Srpaulo		case IWM_UCODE_TLV_SEC_RT:
718286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
719286441Srpaulo			    IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) {
720286441Srpaulo				device_printf(sc->sc_dev,
721286441Srpaulo				    "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n",
722286441Srpaulo				    __func__,
723286441Srpaulo				    error);
724286441Srpaulo				goto parse_out;
725286441Srpaulo			}
726286441Srpaulo			break;
727286441Srpaulo		case IWM_UCODE_TLV_SEC_INIT:
728286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
729286441Srpaulo			    IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) {
730286441Srpaulo				device_printf(sc->sc_dev,
731286441Srpaulo				    "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n",
732286441Srpaulo				    __func__,
733286441Srpaulo				    error);
734286441Srpaulo				goto parse_out;
735286441Srpaulo			}
736286441Srpaulo			break;
737286441Srpaulo		case IWM_UCODE_TLV_SEC_WOWLAN:
738286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
739286441Srpaulo			    IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) {
740286441Srpaulo				device_printf(sc->sc_dev,
741286441Srpaulo				    "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n",
742286441Srpaulo				    __func__,
743286441Srpaulo				    error);
744286441Srpaulo				goto parse_out;
745286441Srpaulo			}
746286441Srpaulo			break;
747286441Srpaulo		case IWM_UCODE_TLV_DEF_CALIB:
748286441Srpaulo			if (tlv_len != sizeof(struct iwm_tlv_calib_data)) {
749286441Srpaulo				device_printf(sc->sc_dev,
750286441Srpaulo				    "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n",
751286441Srpaulo				    __func__,
752286441Srpaulo				    (int) tlv_len,
753286441Srpaulo				    (int) sizeof(struct iwm_tlv_calib_data));
754286441Srpaulo				error = EINVAL;
755286441Srpaulo				goto parse_out;
756286441Srpaulo			}
757286441Srpaulo			if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) {
758286441Srpaulo				device_printf(sc->sc_dev,
759286441Srpaulo				    "%s: iwm_set_default_calib() failed: %d\n",
760286441Srpaulo				    __func__,
761286441Srpaulo				    error);
762286441Srpaulo				goto parse_out;
763286441Srpaulo			}
764286441Srpaulo			break;
765286441Srpaulo		case IWM_UCODE_TLV_PHY_SKU:
766286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
767286441Srpaulo				error = EINVAL;
768286441Srpaulo				device_printf(sc->sc_dev,
769286441Srpaulo				    "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n",
770286441Srpaulo				    __func__,
771286441Srpaulo				    (int) tlv_len);
772286441Srpaulo				goto parse_out;
773286441Srpaulo			}
774286441Srpaulo			sc->sc_fw_phy_config =
775286441Srpaulo			    le32toh(*(const uint32_t *)tlv_data);
776286441Srpaulo			break;
777286441Srpaulo
778303628Ssbruno		case IWM_UCODE_TLV_API_CHANGES_SET: {
779303628Ssbruno			const struct iwm_ucode_api *api;
780303628Ssbruno			if (tlv_len != sizeof(*api)) {
781303628Ssbruno				error = EINVAL;
782303628Ssbruno				goto parse_out;
783303628Ssbruno			}
784303628Ssbruno			api = (const struct iwm_ucode_api *)tlv_data;
785303628Ssbruno			/* Flags may exceed 32 bits in future firmware. */
786303628Ssbruno			if (le32toh(api->api_index) > 0) {
787303628Ssbruno				device_printf(sc->sc_dev,
788303628Ssbruno				    "unsupported API index %d\n",
789303628Ssbruno				    le32toh(api->api_index));
790303628Ssbruno				goto parse_out;
791303628Ssbruno			}
792303628Ssbruno			sc->sc_ucode_api = le32toh(api->api_flags);
793303628Ssbruno			break;
794303628Ssbruno		}
795303628Ssbruno
796303628Ssbruno		case IWM_UCODE_TLV_ENABLED_CAPABILITIES: {
797303628Ssbruno			const struct iwm_ucode_capa *capa;
798303628Ssbruno			int idx, i;
799303628Ssbruno			if (tlv_len != sizeof(*capa)) {
800303628Ssbruno				error = EINVAL;
801303628Ssbruno				goto parse_out;
802303628Ssbruno			}
803303628Ssbruno			capa = (const struct iwm_ucode_capa *)tlv_data;
804303628Ssbruno			idx = le32toh(capa->api_index);
805330148Seadler			if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) {
806303628Ssbruno				device_printf(sc->sc_dev,
807303628Ssbruno				    "unsupported API index %d\n", idx);
808303628Ssbruno				goto parse_out;
809303628Ssbruno			}
810303628Ssbruno			for (i = 0; i < 32; i++) {
811303628Ssbruno				if ((le32toh(capa->api_capa) & (1U << i)) == 0)
812303628Ssbruno					continue;
813303628Ssbruno				setbit(sc->sc_enabled_capa, i + (32 * idx));
814303628Ssbruno			}
815303628Ssbruno			break;
816303628Ssbruno		}
817303628Ssbruno
818303628Ssbruno		case 48: /* undocumented TLV */
819303628Ssbruno		case IWM_UCODE_TLV_SDIO_ADMA_ADDR:
820303628Ssbruno		case IWM_UCODE_TLV_FW_GSCAN_CAPA:
821286441Srpaulo			/* ignore, not used by current driver */
822286441Srpaulo			break;
823286441Srpaulo
824303628Ssbruno		case IWM_UCODE_TLV_SEC_RT_USNIFFER:
825303628Ssbruno			if ((error = iwm_firmware_store_section(sc,
826303628Ssbruno			    IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data,
827303628Ssbruno			    tlv_len)) != 0)
828303628Ssbruno				goto parse_out;
829303628Ssbruno			break;
830303628Ssbruno
831303628Ssbruno		case IWM_UCODE_TLV_N_SCAN_CHANNELS:
832303628Ssbruno			if (tlv_len != sizeof(uint32_t)) {
833303628Ssbruno				error = EINVAL;
834303628Ssbruno				goto parse_out;
835303628Ssbruno			}
836303628Ssbruno			sc->sc_capa_n_scan_channels =
837303628Ssbruno			  le32toh(*(const uint32_t *)tlv_data);
838303628Ssbruno			break;
839303628Ssbruno
840303628Ssbruno		case IWM_UCODE_TLV_FW_VERSION:
841303628Ssbruno			if (tlv_len != sizeof(uint32_t) * 3) {
842303628Ssbruno				error = EINVAL;
843303628Ssbruno				goto parse_out;
844303628Ssbruno			}
845303628Ssbruno			snprintf(sc->sc_fwver, sizeof(sc->sc_fwver),
846303628Ssbruno			    "%d.%d.%d",
847303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[0]),
848303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[1]),
849303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[2]));
850303628Ssbruno			break;
851303628Ssbruno
852286441Srpaulo		default:
853286441Srpaulo			device_printf(sc->sc_dev,
854286441Srpaulo			    "%s: unknown firmware section %d, abort\n",
855286441Srpaulo			    __func__, tlv_type);
856286441Srpaulo			error = EINVAL;
857286441Srpaulo			goto parse_out;
858286441Srpaulo		}
859286441Srpaulo
860286441Srpaulo		len -= roundup(tlv_len, 4);
861286441Srpaulo		data += roundup(tlv_len, 4);
862286441Srpaulo	}
863286441Srpaulo
864286441Srpaulo	KASSERT(error == 0, ("unhandled error"));
865286441Srpaulo
866286441Srpaulo parse_out:
867286441Srpaulo	if (error) {
868286441Srpaulo		device_printf(sc->sc_dev, "firmware parse error %d, "
869286441Srpaulo		    "section type %d\n", error, tlv_type);
870286441Srpaulo	}
871286441Srpaulo
872286441Srpaulo	if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
873286441Srpaulo		device_printf(sc->sc_dev,
874286441Srpaulo		    "device uses unsupported power ops\n");
875286441Srpaulo		error = ENOTSUP;
876286441Srpaulo	}
877286441Srpaulo
878286441Srpaulo out:
879286441Srpaulo	if (error) {
880286441Srpaulo		fw->fw_status = IWM_FW_STATUS_NONE;
881293177Savos		if (fw->fw_fp != NULL)
882286441Srpaulo			iwm_fw_info_free(fw);
883286441Srpaulo	} else
884286441Srpaulo		fw->fw_status = IWM_FW_STATUS_DONE;
885286441Srpaulo	wakeup(&sc->sc_fw);
886286441Srpaulo
887286441Srpaulo	return error;
888286441Srpaulo}
889286441Srpaulo
890286441Srpaulo/*
891286441Srpaulo * DMA resource routines
892286441Srpaulo */
893286441Srpaulo
894286441Srpaulostatic void
895286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
896286441Srpaulo{
897286441Srpaulo        if (error != 0)
898286441Srpaulo                return;
899286441Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
900303628Ssbruno	*(bus_addr_t *)arg = segs[0].ds_addr;
901286441Srpaulo}
902286441Srpaulo
903286441Srpaulostatic int
904286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma,
905286441Srpaulo    bus_size_t size, bus_size_t alignment)
906286441Srpaulo{
907286441Srpaulo	int error;
908286441Srpaulo
909286441Srpaulo	dma->tag = NULL;
910303628Ssbruno	dma->map = NULL;
911286441Srpaulo	dma->size = size;
912302102Sadrian	dma->vaddr = NULL;
913286441Srpaulo
914286441Srpaulo	error = bus_dma_tag_create(tag, alignment,
915286441Srpaulo            0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
916289679Skevlo            1, size, 0, NULL, NULL, &dma->tag);
917286441Srpaulo        if (error != 0)
918286441Srpaulo                goto fail;
919286441Srpaulo
920286441Srpaulo        error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
921286441Srpaulo            BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
922286441Srpaulo        if (error != 0)
923286441Srpaulo                goto fail;
924286441Srpaulo
925286441Srpaulo        error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
926286441Srpaulo            iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
927302102Sadrian        if (error != 0) {
928302102Sadrian		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
929302102Sadrian		dma->vaddr = NULL;
930303628Ssbruno		goto fail;
931302102Sadrian	}
932286441Srpaulo
933286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
934286441Srpaulo
935286441Srpaulo	return 0;
936286441Srpaulo
937303628Ssbrunofail:
938303628Ssbruno	iwm_dma_contig_free(dma);
939303628Ssbruno
940286441Srpaulo	return error;
941286441Srpaulo}
942286441Srpaulo
943286441Srpaulostatic void
944286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma)
945286441Srpaulo{
946302102Sadrian	if (dma->vaddr != NULL) {
947302102Sadrian		bus_dmamap_sync(dma->tag, dma->map,
948302102Sadrian		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
949302102Sadrian		bus_dmamap_unload(dma->tag, dma->map);
950302102Sadrian		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
951302102Sadrian		dma->vaddr = NULL;
952286441Srpaulo	}
953286441Srpaulo	if (dma->tag != NULL) {
954286441Srpaulo		bus_dma_tag_destroy(dma->tag);
955286441Srpaulo		dma->tag = NULL;
956286441Srpaulo	}
957286441Srpaulo}
958286441Srpaulo
959286441Srpaulo/* fwmem is used to load firmware onto the card */
960286441Srpaulostatic int
961286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc)
962286441Srpaulo{
963286441Srpaulo	/* Must be aligned on a 16-byte boundary. */
964286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma,
965286441Srpaulo	    sc->sc_fwdmasegsz, 16);
966286441Srpaulo}
967286441Srpaulo
968286441Srpaulo/* tx scheduler rings.  not used? */
969286441Srpaulostatic int
970286441Srpauloiwm_alloc_sched(struct iwm_softc *sc)
971286441Srpaulo{
972286441Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
973303628Ssbruno	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma,
974286441Srpaulo	    nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024);
975286441Srpaulo}
976286441Srpaulo
977286441Srpaulo/* keep-warm page is used internally by the card.  see iwl-fh.h for more info */
978286441Srpaulostatic int
979286441Srpauloiwm_alloc_kw(struct iwm_softc *sc)
980286441Srpaulo{
981286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096);
982286441Srpaulo}
983286441Srpaulo
984286441Srpaulo/* interrupt cause table */
985286441Srpaulostatic int
986286441Srpauloiwm_alloc_ict(struct iwm_softc *sc)
987286441Srpaulo{
988286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma,
989286441Srpaulo	    IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT);
990286441Srpaulo}
991286441Srpaulo
992286441Srpaulostatic int
993286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
994286441Srpaulo{
995286441Srpaulo	bus_size_t size;
996286441Srpaulo	int i, error;
997286441Srpaulo
998286441Srpaulo	ring->cur = 0;
999286441Srpaulo
1000286441Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1001286441Srpaulo	size = IWM_RX_RING_COUNT * sizeof(uint32_t);
1002286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
1003286441Srpaulo	if (error != 0) {
1004286441Srpaulo		device_printf(sc->sc_dev,
1005286441Srpaulo		    "could not allocate RX ring DMA memory\n");
1006286441Srpaulo		goto fail;
1007286441Srpaulo	}
1008286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
1009286441Srpaulo
1010286441Srpaulo	/* Allocate RX status area (16-byte aligned). */
1011286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma,
1012286441Srpaulo	    sizeof(*ring->stat), 16);
1013286441Srpaulo	if (error != 0) {
1014286441Srpaulo		device_printf(sc->sc_dev,
1015286441Srpaulo		    "could not allocate RX status DMA memory\n");
1016286441Srpaulo		goto fail;
1017286441Srpaulo	}
1018286441Srpaulo	ring->stat = ring->stat_dma.vaddr;
1019286441Srpaulo
1020286441Srpaulo        /* Create RX buffer DMA tag. */
1021286441Srpaulo        error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
1022286441Srpaulo            BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1023289679Skevlo            IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat);
1024286441Srpaulo        if (error != 0) {
1025286441Srpaulo                device_printf(sc->sc_dev,
1026286441Srpaulo                    "%s: could not create RX buf DMA tag, error %d\n",
1027286441Srpaulo                    __func__, error);
1028286441Srpaulo                goto fail;
1029286441Srpaulo        }
1030286441Srpaulo
1031301845Sadrian	/* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */
1032301845Sadrian	error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map);
1033301845Sadrian	if (error != 0) {
1034301845Sadrian		device_printf(sc->sc_dev,
1035301845Sadrian		    "%s: could not create RX buf DMA map, error %d\n",
1036301845Sadrian		    __func__, error);
1037301845Sadrian		goto fail;
1038301845Sadrian	}
1039286441Srpaulo	/*
1040286441Srpaulo	 * Allocate and map RX buffers.
1041286441Srpaulo	 */
1042286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
1043301845Sadrian		struct iwm_rx_data *data = &ring->data[i];
1044301845Sadrian		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1045301845Sadrian		if (error != 0) {
1046301845Sadrian			device_printf(sc->sc_dev,
1047301845Sadrian			    "%s: could not create RX buf DMA map, error %d\n",
1048301845Sadrian			    __func__, error);
1049301845Sadrian			goto fail;
1050301845Sadrian		}
1051301845Sadrian		data->m = NULL;
1052301845Sadrian
1053286441Srpaulo		if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) {
1054286441Srpaulo			goto fail;
1055286441Srpaulo		}
1056286441Srpaulo	}
1057286441Srpaulo	return 0;
1058286441Srpaulo
1059286441Srpaulofail:	iwm_free_rx_ring(sc, ring);
1060286441Srpaulo	return error;
1061286441Srpaulo}
1062286441Srpaulo
1063286441Srpaulostatic void
1064301191Sadrianiwm_disable_rx_dma(struct iwm_softc *sc)
1065286441Srpaulo{
1066303628Ssbruno	/* XXX conditional nic locks are stupid */
1067286441Srpaulo	/* XXX print out if we can't lock the NIC? */
1068286441Srpaulo	if (iwm_nic_lock(sc)) {
1069286441Srpaulo		/* XXX handle if RX stop doesn't finish? */
1070286441Srpaulo		(void) iwm_pcie_rx_stop(sc);
1071286441Srpaulo		iwm_nic_unlock(sc);
1072286441Srpaulo	}
1073301191Sadrian}
1074301191Sadrian
1075301191Sadrianstatic void
1076301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1077301191Sadrian{
1078287965Sadrian	/* Reset the ring state */
1079286441Srpaulo	ring->cur = 0;
1080303628Ssbruno
1081303628Ssbruno	/*
1082303628Ssbruno	 * The hw rx ring index in shared memory must also be cleared,
1083303628Ssbruno	 * otherwise the discrepancy can cause reprocessing chaos.
1084303628Ssbruno	 */
1085287965Sadrian	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1086286441Srpaulo}
1087286441Srpaulo
1088286441Srpaulostatic void
1089286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1090286441Srpaulo{
1091286441Srpaulo	int i;
1092286441Srpaulo
1093286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1094286441Srpaulo	iwm_dma_contig_free(&ring->stat_dma);
1095286441Srpaulo
1096286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
1097286441Srpaulo		struct iwm_rx_data *data = &ring->data[i];
1098286441Srpaulo
1099286441Srpaulo		if (data->m != NULL) {
1100286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1101286441Srpaulo			    BUS_DMASYNC_POSTREAD);
1102286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1103286441Srpaulo			m_freem(data->m);
1104286441Srpaulo			data->m = NULL;
1105286441Srpaulo		}
1106286441Srpaulo		if (data->map != NULL) {
1107286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1108286441Srpaulo			data->map = NULL;
1109286441Srpaulo		}
1110286441Srpaulo	}
1111301845Sadrian	if (ring->spare_map != NULL) {
1112301845Sadrian		bus_dmamap_destroy(ring->data_dmat, ring->spare_map);
1113301845Sadrian		ring->spare_map = NULL;
1114301845Sadrian	}
1115286441Srpaulo	if (ring->data_dmat != NULL) {
1116286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1117286441Srpaulo		ring->data_dmat = NULL;
1118286441Srpaulo	}
1119286441Srpaulo}
1120286441Srpaulo
1121286441Srpaulostatic int
1122286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
1123286441Srpaulo{
1124286441Srpaulo	bus_addr_t paddr;
1125286441Srpaulo	bus_size_t size;
1126302104Sadrian	size_t maxsize;
1127302104Sadrian	int nsegments;
1128286441Srpaulo	int i, error;
1129286441Srpaulo
1130286441Srpaulo	ring->qid = qid;
1131286441Srpaulo	ring->queued = 0;
1132286441Srpaulo	ring->cur = 0;
1133286441Srpaulo
1134286441Srpaulo	/* Allocate TX descriptors (256-byte aligned). */
1135286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd);
1136286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
1137286441Srpaulo	if (error != 0) {
1138286441Srpaulo		device_printf(sc->sc_dev,
1139286441Srpaulo		    "could not allocate TX ring DMA memory\n");
1140286441Srpaulo		goto fail;
1141286441Srpaulo	}
1142286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
1143286441Srpaulo
1144286441Srpaulo	/*
1145286441Srpaulo	 * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need
1146286441Srpaulo	 * to allocate commands space for other rings.
1147286441Srpaulo	 */
1148286441Srpaulo	if (qid > IWM_MVM_CMD_QUEUE)
1149286441Srpaulo		return 0;
1150286441Srpaulo
1151286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
1152286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4);
1153286441Srpaulo	if (error != 0) {
1154286441Srpaulo		device_printf(sc->sc_dev,
1155286441Srpaulo		    "could not allocate TX cmd DMA memory\n");
1156286441Srpaulo		goto fail;
1157286441Srpaulo	}
1158286441Srpaulo	ring->cmd = ring->cmd_dma.vaddr;
1159286441Srpaulo
1160302104Sadrian	/* FW commands may require more mapped space than packets. */
1161302104Sadrian	if (qid == IWM_MVM_CMD_QUEUE) {
1162302104Sadrian		maxsize = IWM_RBUF_SIZE;
1163302104Sadrian		nsegments = 1;
1164302104Sadrian	} else {
1165302104Sadrian		maxsize = MCLBYTES;
1166302104Sadrian		nsegments = IWM_MAX_SCATTER - 2;
1167302104Sadrian	}
1168302104Sadrian
1169286441Srpaulo	error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
1170302104Sadrian	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize,
1171302104Sadrian            nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat);
1172286441Srpaulo	if (error != 0) {
1173286441Srpaulo		device_printf(sc->sc_dev, "could not create TX buf DMA tag\n");
1174286441Srpaulo		goto fail;
1175286441Srpaulo	}
1176286441Srpaulo
1177286441Srpaulo	paddr = ring->cmd_dma.paddr;
1178286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1179286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1180286441Srpaulo
1181286441Srpaulo		data->cmd_paddr = paddr;
1182286441Srpaulo		data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header)
1183286441Srpaulo		    + offsetof(struct iwm_tx_cmd, scratch);
1184286441Srpaulo		paddr += sizeof(struct iwm_device_cmd);
1185286441Srpaulo
1186286441Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1187286441Srpaulo		if (error != 0) {
1188286441Srpaulo			device_printf(sc->sc_dev,
1189286441Srpaulo			    "could not create TX buf DMA map\n");
1190286441Srpaulo			goto fail;
1191286441Srpaulo		}
1192286441Srpaulo	}
1193286441Srpaulo	KASSERT(paddr == ring->cmd_dma.paddr + size,
1194286441Srpaulo	    ("invalid physical address"));
1195286441Srpaulo	return 0;
1196286441Srpaulo
1197286441Srpaulofail:	iwm_free_tx_ring(sc, ring);
1198286441Srpaulo	return error;
1199286441Srpaulo}
1200286441Srpaulo
1201286441Srpaulostatic void
1202286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1203286441Srpaulo{
1204286441Srpaulo	int i;
1205286441Srpaulo
1206286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1207286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1208286441Srpaulo
1209286441Srpaulo		if (data->m != NULL) {
1210286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1211286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1212286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1213286441Srpaulo			m_freem(data->m);
1214286441Srpaulo			data->m = NULL;
1215286441Srpaulo		}
1216286441Srpaulo	}
1217286441Srpaulo	/* Clear TX descriptors. */
1218286441Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1219286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1220286441Srpaulo	    BUS_DMASYNC_PREWRITE);
1221286441Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1222286441Srpaulo	ring->queued = 0;
1223286441Srpaulo	ring->cur = 0;
1224286441Srpaulo}
1225286441Srpaulo
1226286441Srpaulostatic void
1227286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1228286441Srpaulo{
1229286441Srpaulo	int i;
1230286441Srpaulo
1231286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1232286441Srpaulo	iwm_dma_contig_free(&ring->cmd_dma);
1233286441Srpaulo
1234286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1235286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1236286441Srpaulo
1237286441Srpaulo		if (data->m != NULL) {
1238286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1239286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1240286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1241286441Srpaulo			m_freem(data->m);
1242286441Srpaulo			data->m = NULL;
1243286441Srpaulo		}
1244286441Srpaulo		if (data->map != NULL) {
1245286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1246286441Srpaulo			data->map = NULL;
1247286441Srpaulo		}
1248286441Srpaulo	}
1249286441Srpaulo	if (ring->data_dmat != NULL) {
1250286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1251286441Srpaulo		ring->data_dmat = NULL;
1252286441Srpaulo	}
1253286441Srpaulo}
1254286441Srpaulo
1255286441Srpaulo/*
1256286441Srpaulo * High-level hardware frobbing routines
1257286441Srpaulo */
1258286441Srpaulo
1259286441Srpaulostatic void
1260286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc)
1261286441Srpaulo{
1262286441Srpaulo	sc->sc_intmask = IWM_CSR_INI_SET_MASK;
1263286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1264286441Srpaulo}
1265286441Srpaulo
1266286441Srpaulostatic void
1267286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc)
1268286441Srpaulo{
1269286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1270286441Srpaulo}
1271286441Srpaulo
1272286441Srpaulostatic void
1273286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc)
1274286441Srpaulo{
1275286441Srpaulo	/* disable interrupts */
1276286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
1277286441Srpaulo
1278286441Srpaulo	/* acknowledge all interrupts */
1279286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1280286441Srpaulo	IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
1281286441Srpaulo}
1282286441Srpaulo
1283286441Srpaulostatic void
1284286441Srpauloiwm_ict_reset(struct iwm_softc *sc)
1285286441Srpaulo{
1286286441Srpaulo	iwm_disable_interrupts(sc);
1287286441Srpaulo
1288286441Srpaulo	/* Reset ICT table. */
1289286441Srpaulo	memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE);
1290286441Srpaulo	sc->ict_cur = 0;
1291286441Srpaulo
1292286441Srpaulo	/* Set physical address of ICT table (4KB aligned). */
1293286441Srpaulo	IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG,
1294286441Srpaulo	    IWM_CSR_DRAM_INT_TBL_ENABLE
1295303628Ssbruno	    | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER
1296286441Srpaulo	    | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK
1297286441Srpaulo	    | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT);
1298286441Srpaulo
1299286441Srpaulo	/* Switch to ICT interrupt mode in driver. */
1300286441Srpaulo	sc->sc_flags |= IWM_FLAG_USE_ICT;
1301286441Srpaulo
1302286441Srpaulo	/* Re-enable interrupts. */
1303286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1304286441Srpaulo	iwm_enable_interrupts(sc);
1305286441Srpaulo}
1306286441Srpaulo
1307286441Srpaulo/* iwlwifi pcie/trans.c */
1308286441Srpaulo
1309286441Srpaulo/*
1310286441Srpaulo * Since this .. hard-resets things, it's time to actually
1311286441Srpaulo * mark the first vap (if any) as having no mac context.
1312286441Srpaulo * It's annoying, but since the driver is potentially being
1313286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we
1314286441Srpaulo * have to correctly track this.
1315286441Srpaulo */
1316286441Srpaulostatic void
1317286441Srpauloiwm_stop_device(struct iwm_softc *sc)
1318286441Srpaulo{
1319287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1320286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1321303628Ssbruno	int chnl, qid;
1322303628Ssbruno	uint32_t mask = 0;
1323286441Srpaulo
1324286441Srpaulo	/* tell the device to stop sending interrupts */
1325286441Srpaulo	iwm_disable_interrupts(sc);
1326286441Srpaulo
1327286441Srpaulo	/*
1328286441Srpaulo	 * FreeBSD-local: mark the first vap as not-uploaded,
1329286441Srpaulo	 * so the next transition through auth/assoc
1330286441Srpaulo	 * will correctly populate the MAC context.
1331286441Srpaulo	 */
1332286441Srpaulo	if (vap) {
1333286441Srpaulo		struct iwm_vap *iv = IWM_VAP(vap);
1334286441Srpaulo		iv->is_uploaded = 0;
1335286441Srpaulo	}
1336286441Srpaulo
1337286441Srpaulo	/* device going down, Stop using ICT table */
1338286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_USE_ICT;
1339286441Srpaulo
1340286441Srpaulo	/* stop tx and rx.  tx and rx bits, as usual, are from if_iwn */
1341286441Srpaulo
1342286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1343286441Srpaulo
1344286441Srpaulo	if (iwm_nic_lock(sc)) {
1345303628Ssbruno		/* Stop each Tx DMA channel */
1346286441Srpaulo		for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1347286441Srpaulo			IWM_WRITE(sc,
1348286441Srpaulo			    IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0);
1349303628Ssbruno			mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl);
1350303628Ssbruno		}
1351286441Srpaulo
1352303628Ssbruno		/* Wait for DMA channels to be idle */
1353303628Ssbruno		if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask,
1354303628Ssbruno		    5000)) {
1355303628Ssbruno			device_printf(sc->sc_dev,
1356303628Ssbruno			    "Failing on timeout while stopping DMA channel: [0x%08x]\n",
1357303628Ssbruno			    IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG));
1358286441Srpaulo		}
1359286441Srpaulo		iwm_nic_unlock(sc);
1360286441Srpaulo	}
1361301191Sadrian	iwm_disable_rx_dma(sc);
1362286441Srpaulo
1363286441Srpaulo	/* Stop RX ring. */
1364286441Srpaulo	iwm_reset_rx_ring(sc, &sc->rxq);
1365286441Srpaulo
1366286441Srpaulo	/* Reset all TX rings. */
1367286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++)
1368286441Srpaulo		iwm_reset_tx_ring(sc, &sc->txq[qid]);
1369286441Srpaulo
1370286441Srpaulo	/*
1371286441Srpaulo	 * Power-down device's busmaster DMA clocks
1372286441Srpaulo	 */
1373286441Srpaulo	iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT);
1374286441Srpaulo	DELAY(5);
1375286441Srpaulo
1376286441Srpaulo	/* Make sure (redundant) we've released our request to stay awake */
1377286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
1378286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1379286441Srpaulo
1380286441Srpaulo	/* Stop the device, and put it in low power state */
1381286441Srpaulo	iwm_apm_stop(sc);
1382286441Srpaulo
1383286441Srpaulo	/* Upon stop, the APM issues an interrupt if HW RF kill is set.
1384286441Srpaulo	 * Clean again the interrupt here
1385286441Srpaulo	 */
1386286441Srpaulo	iwm_disable_interrupts(sc);
1387286441Srpaulo	/* stop and reset the on-board processor */
1388303628Ssbruno	IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET);
1389286441Srpaulo
1390286441Srpaulo	/*
1391286441Srpaulo	 * Even if we stop the HW, we still want the RF kill
1392286441Srpaulo	 * interrupt
1393286441Srpaulo	 */
1394286441Srpaulo	iwm_enable_rfkill_int(sc);
1395286441Srpaulo	iwm_check_rfkill(sc);
1396286441Srpaulo}
1397286441Srpaulo
1398286441Srpaulo/* iwlwifi: mvm/ops.c */
1399286441Srpaulostatic void
1400286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc)
1401286441Srpaulo{
1402286441Srpaulo	uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash;
1403286441Srpaulo	uint32_t reg_val = 0;
1404286441Srpaulo
1405286441Srpaulo	radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >>
1406286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_TYPE_POS;
1407286441Srpaulo	radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >>
1408286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_STEP_POS;
1409286441Srpaulo	radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >>
1410286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_DASH_POS;
1411286441Srpaulo
1412286441Srpaulo	/* SKU control */
1413286441Srpaulo	reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) <<
1414286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
1415286441Srpaulo	reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) <<
1416286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
1417286441Srpaulo
1418286441Srpaulo	/* radio configuration */
1419286441Srpaulo	reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
1420286441Srpaulo	reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
1421286441Srpaulo	reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
1422286441Srpaulo
1423286441Srpaulo	IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val);
1424286441Srpaulo
1425286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1426286441Srpaulo	    "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
1427286441Srpaulo	    radio_cfg_step, radio_cfg_dash);
1428286441Srpaulo
1429286441Srpaulo	/*
1430286441Srpaulo	 * W/A : NIC is stuck in a reset state after Early PCIe power off
1431286441Srpaulo	 * (PCIe power is lost before PERST# is asserted), causing ME FW
1432286441Srpaulo	 * to lose ownership and not being able to obtain it back.
1433286441Srpaulo	 */
1434330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
1435303628Ssbruno		iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG,
1436303628Ssbruno		    IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
1437303628Ssbruno		    ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
1438303628Ssbruno	}
1439286441Srpaulo}
1440286441Srpaulo
1441286441Srpaulostatic int
1442286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc)
1443286441Srpaulo{
1444286441Srpaulo	if (!iwm_nic_lock(sc))
1445286441Srpaulo		return EBUSY;
1446286441Srpaulo
1447286441Srpaulo	/*
1448286441Srpaulo	 * Initialize RX ring.  This is from the iwn driver.
1449286441Srpaulo	 */
1450286441Srpaulo	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1451286441Srpaulo
1452286441Srpaulo	/* stop DMA */
1453301191Sadrian	iwm_disable_rx_dma(sc);
1454286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
1455286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
1456286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0);
1457286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
1458286441Srpaulo
1459286441Srpaulo	/* Set physical address of RX ring (256-byte aligned). */
1460286441Srpaulo	IWM_WRITE(sc,
1461286441Srpaulo	    IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8);
1462286441Srpaulo
1463286441Srpaulo	/* Set physical address of RX status (16-byte aligned). */
1464286441Srpaulo	IWM_WRITE(sc,
1465286441Srpaulo	    IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4);
1466286441Srpaulo
1467286441Srpaulo	/* Enable RX. */
1468286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG,
1469286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL		|
1470286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY		|  /* HW bug */
1471286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL	|
1472303628Ssbruno	    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK	|
1473303628Ssbruno	    (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
1474286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K		|
1475286441Srpaulo	    IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
1476286441Srpaulo
1477286441Srpaulo	IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF);
1478286441Srpaulo
1479286441Srpaulo	/* W/A for interrupt coalescing bug in 7260 and 3160 */
1480330166Seadler	if (sc->cfg->host_interrupt_operation_mode)
1481286441Srpaulo		IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE);
1482286441Srpaulo
1483286441Srpaulo	/*
1484286441Srpaulo	 * Thus sayeth el jefe (iwlwifi) via a comment:
1485286441Srpaulo	 *
1486286441Srpaulo	 * This value should initially be 0 (before preparing any
1487303628Ssbruno	 * RBs), should be 8 after preparing the first 8 RBs (for example)
1488286441Srpaulo	 */
1489286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8);
1490286441Srpaulo
1491286441Srpaulo	iwm_nic_unlock(sc);
1492286441Srpaulo
1493286441Srpaulo	return 0;
1494286441Srpaulo}
1495286441Srpaulo
1496286441Srpaulostatic int
1497286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc)
1498286441Srpaulo{
1499286441Srpaulo	int qid;
1500286441Srpaulo
1501286441Srpaulo	if (!iwm_nic_lock(sc))
1502286441Srpaulo		return EBUSY;
1503286441Srpaulo
1504286441Srpaulo	/* Deactivate TX scheduler. */
1505286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1506286441Srpaulo
1507286441Srpaulo	/* Set physical address of "keep warm" page (16-byte aligned). */
1508286441Srpaulo	IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4);
1509286441Srpaulo
1510286441Srpaulo	/* Initialize TX rings. */
1511286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++) {
1512286441Srpaulo		struct iwm_tx_ring *txq = &sc->txq[qid];
1513286441Srpaulo
1514286441Srpaulo		/* Set physical address of TX ring (256-byte aligned). */
1515286441Srpaulo		IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid),
1516286441Srpaulo		    txq->desc_dma.paddr >> 8);
1517286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
1518286441Srpaulo		    "%s: loading ring %d descriptors (%p) at %lx\n",
1519286441Srpaulo		    __func__,
1520286441Srpaulo		    qid, txq->desc,
1521286441Srpaulo		    (unsigned long) (txq->desc_dma.paddr >> 8));
1522286441Srpaulo	}
1523303628Ssbruno
1524303628Ssbruno	iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE);
1525303628Ssbruno
1526286441Srpaulo	iwm_nic_unlock(sc);
1527286441Srpaulo
1528286441Srpaulo	return 0;
1529286441Srpaulo}
1530286441Srpaulo
1531286441Srpaulostatic int
1532286441Srpauloiwm_nic_init(struct iwm_softc *sc)
1533286441Srpaulo{
1534286441Srpaulo	int error;
1535286441Srpaulo
1536286441Srpaulo	iwm_apm_init(sc);
1537330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
1538303628Ssbruno		iwm_set_pwr(sc);
1539286441Srpaulo
1540286441Srpaulo	iwm_mvm_nic_config(sc);
1541286441Srpaulo
1542286441Srpaulo	if ((error = iwm_nic_rx_init(sc)) != 0)
1543286441Srpaulo		return error;
1544286441Srpaulo
1545286441Srpaulo	/*
1546286441Srpaulo	 * Ditto for TX, from iwn
1547286441Srpaulo	 */
1548286441Srpaulo	if ((error = iwm_nic_tx_init(sc)) != 0)
1549286441Srpaulo		return error;
1550286441Srpaulo
1551286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1552286441Srpaulo	    "%s: shadow registers enabled\n", __func__);
1553286441Srpaulo	IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff);
1554286441Srpaulo
1555286441Srpaulo	return 0;
1556286441Srpaulo}
1557286441Srpaulo
1558286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = {
1559286441Srpaulo	IWM_MVM_TX_FIFO_VO,
1560286441Srpaulo	IWM_MVM_TX_FIFO_VI,
1561286441Srpaulo	IWM_MVM_TX_FIFO_BE,
1562286441Srpaulo	IWM_MVM_TX_FIFO_BK,
1563286441Srpaulo};
1564286441Srpaulo
1565303628Ssbrunostatic int
1566303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
1567286441Srpaulo{
1568286441Srpaulo	if (!iwm_nic_lock(sc)) {
1569286441Srpaulo		device_printf(sc->sc_dev,
1570286441Srpaulo		    "%s: cannot enable txq %d\n",
1571286441Srpaulo		    __func__,
1572286441Srpaulo		    qid);
1573303628Ssbruno		return EBUSY;
1574286441Srpaulo	}
1575286441Srpaulo
1576303628Ssbruno	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
1577286441Srpaulo
1578303628Ssbruno	if (qid == IWM_MVM_CMD_QUEUE) {
1579303628Ssbruno		/* unactivate before configuration */
1580303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1581303628Ssbruno		    (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE)
1582303628Ssbruno		    | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
1583286441Srpaulo
1584303628Ssbruno		iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid));
1585286441Srpaulo
1586303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0);
1587286441Srpaulo
1588303628Ssbruno		iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0);
1589303628Ssbruno		/* Set scheduler window size and frame limit. */
1590303628Ssbruno		iwm_write_mem32(sc,
1591303628Ssbruno		    sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) +
1592303628Ssbruno		    sizeof(uint32_t),
1593303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
1594303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
1595303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
1596303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
1597286441Srpaulo
1598303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1599303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
1600303628Ssbruno		    (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) |
1601303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) |
1602303628Ssbruno		    IWM_SCD_QUEUE_STTS_REG_MSK);
1603303628Ssbruno	} else {
1604303628Ssbruno		struct iwm_scd_txq_cfg_cmd cmd;
1605303628Ssbruno		int error;
1606286441Srpaulo
1607303628Ssbruno		iwm_nic_unlock(sc);
1608303628Ssbruno
1609303628Ssbruno		memset(&cmd, 0, sizeof(cmd));
1610303628Ssbruno		cmd.scd_queue = qid;
1611303628Ssbruno		cmd.enable = 1;
1612303628Ssbruno		cmd.sta_id = sta_id;
1613303628Ssbruno		cmd.tx_fifo = fifo;
1614303628Ssbruno		cmd.aggregate = 0;
1615303628Ssbruno		cmd.window = IWM_FRAME_LIMIT;
1616303628Ssbruno
1617303628Ssbruno		error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC,
1618303628Ssbruno		    sizeof(cmd), &cmd);
1619303628Ssbruno		if (error) {
1620303628Ssbruno			device_printf(sc->sc_dev,
1621303628Ssbruno			    "cannot enable txq %d\n", qid);
1622303628Ssbruno			return error;
1623303628Ssbruno		}
1624303628Ssbruno
1625303628Ssbruno		if (!iwm_nic_lock(sc))
1626303628Ssbruno			return EBUSY;
1627303628Ssbruno	}
1628303628Ssbruno
1629303628Ssbruno	iwm_write_prph(sc, IWM_SCD_EN_CTRL,
1630303628Ssbruno	    iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid);
1631303628Ssbruno
1632286441Srpaulo	iwm_nic_unlock(sc);
1633286441Srpaulo
1634303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n",
1635286441Srpaulo	    __func__, qid, fifo);
1636303628Ssbruno
1637303628Ssbruno	return 0;
1638286441Srpaulo}
1639286441Srpaulo
1640286441Srpaulostatic int
1641286441Srpauloiwm_post_alive(struct iwm_softc *sc)
1642286441Srpaulo{
1643286441Srpaulo	int nwords;
1644286441Srpaulo	int error, chnl;
1645303628Ssbruno	uint32_t base;
1646286441Srpaulo
1647286441Srpaulo	if (!iwm_nic_lock(sc))
1648286441Srpaulo		return EBUSY;
1649286441Srpaulo
1650303628Ssbruno	base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR);
1651303628Ssbruno	if (sc->sched_base != base) {
1652286441Srpaulo		device_printf(sc->sc_dev,
1653303628Ssbruno		    "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n",
1654303628Ssbruno		    __func__, sc->sched_base, base);
1655286441Srpaulo	}
1656286441Srpaulo
1657286441Srpaulo	iwm_ict_reset(sc);
1658286441Srpaulo
1659286441Srpaulo	/* Clear TX scheduler state in SRAM. */
1660286441Srpaulo	nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND -
1661286441Srpaulo	    IWM_SCD_CONTEXT_MEM_LOWER_BOUND)
1662286441Srpaulo	    / sizeof(uint32_t);
1663286441Srpaulo	error = iwm_write_mem(sc,
1664286441Srpaulo	    sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND,
1665286441Srpaulo	    NULL, nwords);
1666286441Srpaulo	if (error)
1667286441Srpaulo		goto out;
1668286441Srpaulo
1669286441Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned). */
1670286441Srpaulo	iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10);
1671286441Srpaulo
1672286441Srpaulo	iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0);
1673286441Srpaulo
1674303628Ssbruno	iwm_nic_unlock(sc);
1675303628Ssbruno
1676286441Srpaulo	/* enable command channel */
1677303628Ssbruno	error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7);
1678303628Ssbruno	if (error)
1679303628Ssbruno		return error;
1680286441Srpaulo
1681303628Ssbruno	if (!iwm_nic_lock(sc))
1682303628Ssbruno		return EBUSY;
1683303628Ssbruno
1684286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff);
1685286441Srpaulo
1686286441Srpaulo	/* Enable DMA channels. */
1687286441Srpaulo	for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1688286441Srpaulo		IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl),
1689286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
1690286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
1691286441Srpaulo	}
1692286441Srpaulo
1693286441Srpaulo	IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG,
1694286441Srpaulo	    IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
1695286441Srpaulo
1696286441Srpaulo	/* Enable L1-Active */
1697330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
1698303628Ssbruno		iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG,
1699303628Ssbruno		    IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
1700303628Ssbruno	}
1701286441Srpaulo
1702286441Srpaulo out:
1703303628Ssbruno	iwm_nic_unlock(sc);
1704286441Srpaulo	return error;
1705286441Srpaulo}
1706286441Srpaulo
1707286441Srpaulo/*
1708286441Srpaulo * NVM read access and content parsing.  We do not support
1709286441Srpaulo * external NVM or writing NVM.
1710286441Srpaulo * iwlwifi/mvm/nvm.c
1711286441Srpaulo */
1712286441Srpaulo
1713286441Srpaulo/* Default NVM size to read */
1714303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE	(2*1024)
1715286441Srpaulo
1716286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1
1717286441Srpaulo#define IWM_NVM_READ_OPCODE 0
1718286441Srpaulo
1719303628Ssbruno/* load nvm chunk response */
1720330160Seadlerenum {
1721330160Seadler	IWM_READ_NVM_CHUNK_SUCCEED = 0,
1722330160Seadler	IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
1723330160Seadler};
1724303628Ssbruno
1725286441Srpaulostatic int
1726286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section,
1727286441Srpaulo	uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len)
1728286441Srpaulo{
1729286441Srpaulo	struct iwm_nvm_access_cmd nvm_access_cmd = {
1730286441Srpaulo		.offset = htole16(offset),
1731286441Srpaulo		.length = htole16(length),
1732286441Srpaulo		.type = htole16(section),
1733286441Srpaulo		.op_code = IWM_NVM_READ_OPCODE,
1734286441Srpaulo	};
1735286441Srpaulo	struct iwm_nvm_access_resp *nvm_resp;
1736286441Srpaulo	struct iwm_rx_packet *pkt;
1737286441Srpaulo	struct iwm_host_cmd cmd = {
1738286441Srpaulo		.id = IWM_NVM_ACCESS_CMD,
1739330160Seadler		.flags = IWM_CMD_WANT_SKB | IWM_CMD_SEND_IN_RFKILL,
1740286441Srpaulo		.data = { &nvm_access_cmd, },
1741286441Srpaulo	};
1742330160Seadler	int ret, bytes_read, offset_read;
1743286441Srpaulo	uint8_t *resp_data;
1744286441Srpaulo
1745286441Srpaulo	cmd.len[0] = sizeof(struct iwm_nvm_access_cmd);
1746286441Srpaulo
1747286441Srpaulo	ret = iwm_send_cmd(sc, &cmd);
1748303628Ssbruno	if (ret) {
1749303628Ssbruno		device_printf(sc->sc_dev,
1750303628Ssbruno		    "Could not send NVM_ACCESS command (error=%d)\n", ret);
1751286441Srpaulo		return ret;
1752303628Ssbruno	}
1753286441Srpaulo
1754286441Srpaulo	pkt = cmd.resp_pkt;
1755286441Srpaulo
1756286441Srpaulo	/* Extract NVM response */
1757286441Srpaulo	nvm_resp = (void *)pkt->data;
1758286441Srpaulo	ret = le16toh(nvm_resp->status);
1759286441Srpaulo	bytes_read = le16toh(nvm_resp->length);
1760286441Srpaulo	offset_read = le16toh(nvm_resp->offset);
1761286441Srpaulo	resp_data = nvm_resp->data;
1762286441Srpaulo	if (ret) {
1763330160Seadler		if ((offset != 0) &&
1764330160Seadler		    (ret == IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
1765330160Seadler			/*
1766330160Seadler			 * meaning of NOT_VALID_ADDRESS:
1767330160Seadler			 * driver try to read chunk from address that is
1768330160Seadler			 * multiple of 2K and got an error since addr is empty.
1769330160Seadler			 * meaning of (offset != 0): driver already
1770330160Seadler			 * read valid data from another chunk so this case
1771330160Seadler			 * is not an error.
1772330160Seadler			 */
1773330160Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1774330160Seadler				    "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
1775330160Seadler				    offset);
1776330160Seadler			*len = 0;
1777330160Seadler			ret = 0;
1778330160Seadler		} else {
1779330160Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1780330160Seadler				    "NVM access command failed with status %d\n", ret);
1781330160Seadler			ret = EIO;
1782330160Seadler		}
1783286441Srpaulo		goto exit;
1784286441Srpaulo	}
1785286441Srpaulo
1786286441Srpaulo	if (offset_read != offset) {
1787286441Srpaulo		device_printf(sc->sc_dev,
1788303628Ssbruno		    "NVM ACCESS response with invalid offset %d\n",
1789303628Ssbruno		    offset_read);
1790286441Srpaulo		ret = EINVAL;
1791286441Srpaulo		goto exit;
1792286441Srpaulo	}
1793286441Srpaulo
1794303628Ssbruno	if (bytes_read > length) {
1795303628Ssbruno		device_printf(sc->sc_dev,
1796303628Ssbruno		    "NVM ACCESS response with too much data "
1797330160Seadler		    "(%d bytes requested, %d bytes received)\n",
1798303628Ssbruno		    length, bytes_read);
1799303628Ssbruno		ret = EINVAL;
1800303628Ssbruno		goto exit;
1801303628Ssbruno	}
1802303628Ssbruno
1803330165Seadler	/* Write data to NVM */
1804286441Srpaulo	memcpy(data + offset, resp_data, bytes_read);
1805286441Srpaulo	*len = bytes_read;
1806286441Srpaulo
1807286441Srpaulo exit:
1808286441Srpaulo	iwm_free_resp(sc, &cmd);
1809286441Srpaulo	return ret;
1810286441Srpaulo}
1811286441Srpaulo
1812286441Srpaulo/*
1813286441Srpaulo * Reads an NVM section completely.
1814303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read
1815286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
1816286441Srpaulo * by uCode, we need to manually check in this case that we don't
1817286441Srpaulo * overflow and try to read more than the EEPROM size.
1818286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and
1819286441Srpaulo * the uCode fills the response with as much data as we can,
1820286441Srpaulo * without overflowing, so no check is needed.
1821286441Srpaulo */
1822286441Srpaulostatic int
1823286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc,
1824330165Seadler	uint16_t section, uint8_t *data, uint16_t *len, uint32_t size_read)
1825286441Srpaulo{
1826330165Seadler	uint16_t seglen, length, offset = 0;
1827330165Seadler	int ret;
1828286441Srpaulo
1829330165Seadler	/* Set nvm section read length */
1830330165Seadler	length = IWM_NVM_DEFAULT_CHUNK_SIZE;
1831303628Ssbruno
1832330165Seadler	seglen = length;
1833286441Srpaulo
1834330165Seadler	/* Read the NVM until exhausted (reading less than requested) */
1835330165Seadler	while (seglen == length) {
1836330165Seadler		/* Check no memory assumptions fail and cause an overflow */
1837330165Seadler		if ((size_read + offset + length) >
1838330166Seadler		    sc->cfg->eeprom_size) {
1839330165Seadler			device_printf(sc->sc_dev,
1840330165Seadler			    "EEPROM size is too small for NVM\n");
1841330165Seadler			return ENOBUFS;
1842286441Srpaulo		}
1843330165Seadler
1844330165Seadler		ret = iwm_nvm_read_chunk(sc, section, offset, length, data, &seglen);
1845330165Seadler		if (ret) {
1846330165Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1847330165Seadler				    "Cannot read NVM from section %d offset %d, length %d\n",
1848330165Seadler				    section, offset, length);
1849330165Seadler			return ret;
1850330165Seadler		}
1851330165Seadler		offset += seglen;
1852286441Srpaulo	}
1853286441Srpaulo
1854330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1855330165Seadler		    "NVM section %d read completed\n", section);
1856330165Seadler	*len = offset;
1857330165Seadler	return 0;
1858286441Srpaulo}
1859286441Srpaulo
1860286441Srpaulo/*
1861286441Srpaulo * BEGIN IWM_NVM_PARSE
1862286441Srpaulo */
1863286441Srpaulo
1864286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */
1865286441Srpaulo
1866286441Srpaulo/* NVM offsets (in words) definitions */
1867303628Ssbrunoenum iwm_nvm_offsets {
1868286441Srpaulo	/* NVM HW-Section offset (in words) definitions */
1869286441Srpaulo	IWM_HW_ADDR = 0x15,
1870286441Srpaulo
1871286441Srpaulo/* NVM SW-Section offset (in words) definitions */
1872286441Srpaulo	IWM_NVM_SW_SECTION = 0x1C0,
1873286441Srpaulo	IWM_NVM_VERSION = 0,
1874286441Srpaulo	IWM_RADIO_CFG = 1,
1875286441Srpaulo	IWM_SKU = 2,
1876286441Srpaulo	IWM_N_HW_ADDRS = 3,
1877286441Srpaulo	IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION,
1878286441Srpaulo
1879286441Srpaulo/* NVM calibration section offset (in words) definitions */
1880286441Srpaulo	IWM_NVM_CALIB_SECTION = 0x2B8,
1881286441Srpaulo	IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION
1882286441Srpaulo};
1883286441Srpaulo
1884303628Ssbrunoenum iwm_8000_nvm_offsets {
1885303628Ssbruno	/* NVM HW-Section offset (in words) definitions */
1886303628Ssbruno	IWM_HW_ADDR0_WFPM_8000 = 0x12,
1887303628Ssbruno	IWM_HW_ADDR1_WFPM_8000 = 0x16,
1888303628Ssbruno	IWM_HW_ADDR0_PCIE_8000 = 0x8A,
1889303628Ssbruno	IWM_HW_ADDR1_PCIE_8000 = 0x8E,
1890303628Ssbruno	IWM_MAC_ADDRESS_OVERRIDE_8000 = 1,
1891303628Ssbruno
1892303628Ssbruno	/* NVM SW-Section offset (in words) definitions */
1893303628Ssbruno	IWM_NVM_SW_SECTION_8000 = 0x1C0,
1894303628Ssbruno	IWM_NVM_VERSION_8000 = 0,
1895303628Ssbruno	IWM_RADIO_CFG_8000 = 0,
1896303628Ssbruno	IWM_SKU_8000 = 2,
1897303628Ssbruno	IWM_N_HW_ADDRS_8000 = 3,
1898303628Ssbruno
1899303628Ssbruno	/* NVM REGULATORY -Section offset (in words) definitions */
1900303628Ssbruno	IWM_NVM_CHANNELS_8000 = 0,
1901303628Ssbruno	IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7,
1902303628Ssbruno	IWM_NVM_LAR_OFFSET_8000 = 0x507,
1903303628Ssbruno	IWM_NVM_LAR_ENABLED_8000 = 0x7,
1904303628Ssbruno
1905303628Ssbruno	/* NVM calibration section offset (in words) definitions */
1906303628Ssbruno	IWM_NVM_CALIB_SECTION_8000 = 0x2B8,
1907303628Ssbruno	IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000
1908303628Ssbruno};
1909303628Ssbruno
1910286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */
1911286441Srpauloenum nvm_sku_bits {
1912286441Srpaulo	IWM_NVM_SKU_CAP_BAND_24GHZ	= (1 << 0),
1913286441Srpaulo	IWM_NVM_SKU_CAP_BAND_52GHZ	= (1 << 1),
1914286441Srpaulo	IWM_NVM_SKU_CAP_11N_ENABLE	= (1 << 2),
1915286441Srpaulo	IWM_NVM_SKU_CAP_11AC_ENABLE	= (1 << 3),
1916286441Srpaulo};
1917286441Srpaulo
1918286441Srpaulo/* radio config bits (actual values from NVM definition) */
1919286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
1920286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
1921286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
1922286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
1923286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
1924286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
1925286441Srpaulo
1926303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x)	(x & 0xF)
1927303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x)		((x >> 4) & 0xF)
1928303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x)		((x >> 8) & 0xF)
1929303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x)		((x >> 12) & 0xFFF)
1930303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x)	((x >> 24) & 0xF)
1931303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x)	((x >> 28) & 0xF)
1932303628Ssbruno
1933286441Srpaulo#define DEFAULT_MAX_TX_POWER 16
1934286441Srpaulo
1935286441Srpaulo/**
1936286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM
1937286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo
1938286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel
1939286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed
1940286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required
1941330165Seadler * XXX cannot find this (DFS) flag in iwm-nvm-parse.c
1942286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate
1943286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
1944286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
1945286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
1946286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
1947286441Srpaulo */
1948286441Srpauloenum iwm_nvm_channel_flags {
1949286441Srpaulo	IWM_NVM_CHANNEL_VALID = (1 << 0),
1950286441Srpaulo	IWM_NVM_CHANNEL_IBSS = (1 << 1),
1951286441Srpaulo	IWM_NVM_CHANNEL_ACTIVE = (1 << 3),
1952286441Srpaulo	IWM_NVM_CHANNEL_RADAR = (1 << 4),
1953286441Srpaulo	IWM_NVM_CHANNEL_DFS = (1 << 7),
1954286441Srpaulo	IWM_NVM_CHANNEL_WIDE = (1 << 8),
1955286441Srpaulo	IWM_NVM_CHANNEL_40MHZ = (1 << 9),
1956286441Srpaulo	IWM_NVM_CHANNEL_80MHZ = (1 << 10),
1957286441Srpaulo	IWM_NVM_CHANNEL_160MHZ = (1 << 11),
1958286441Srpaulo};
1959286441Srpaulo
1960286441Srpaulo/*
1961298877Savos * Translate EEPROM flags to net80211.
1962286441Srpaulo */
1963298877Savosstatic uint32_t
1964298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags)
1965286441Srpaulo{
1966298877Savos	uint32_t nflags;
1967286441Srpaulo
1968298877Savos	nflags = 0;
1969298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0)
1970298877Savos		nflags |= IEEE80211_CHAN_PASSIVE;
1971298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0)
1972298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1973298877Savos	if (ch_flags & IWM_NVM_CHANNEL_RADAR) {
1974298877Savos		nflags |= IEEE80211_CHAN_DFS;
1975298877Savos		/* Just in case. */
1976298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1977286441Srpaulo	}
1978286441Srpaulo
1979298877Savos	return (nflags);
1980286441Srpaulo}
1981286441Srpaulo
1982286441Srpaulostatic void
1983298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[],
1984303628Ssbruno    int maxchans, int *nchans, int ch_idx, size_t ch_num,
1985303628Ssbruno    const uint8_t bands[])
1986286441Srpaulo{
1987330165Seadler	const uint16_t * const nvm_ch_flags = sc->nvm_data->nvm_ch_flags;
1988298877Savos	uint32_t nflags;
1989286441Srpaulo	uint16_t ch_flags;
1990298877Savos	uint8_t ieee;
1991298877Savos	int error;
1992286441Srpaulo
1993298877Savos	for (; ch_idx < ch_num; ch_idx++) {
1994286441Srpaulo		ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx);
1995330166Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
1996303628Ssbruno			ieee = iwm_nvm_channels[ch_idx];
1997303628Ssbruno		else
1998303628Ssbruno			ieee = iwm_nvm_channels_8000[ch_idx];
1999286441Srpaulo
2000286441Srpaulo		if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) {
2001286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
2002286441Srpaulo			    "Ch. %d Flags %x [%sGHz] - No traffic\n",
2003298877Savos			    ieee, ch_flags,
2004286441Srpaulo			    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
2005286441Srpaulo			    "5.2" : "2.4");
2006286441Srpaulo			continue;
2007286441Srpaulo		}
2008286441Srpaulo
2009298877Savos		nflags = iwm_eeprom_channel_flags(ch_flags);
2010298877Savos		error = ieee80211_add_channel(chans, maxchans, nchans,
2011298877Savos		    ieee, 0, 0, nflags, bands);
2012298877Savos		if (error != 0)
2013298877Savos			break;
2014286441Srpaulo
2015286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
2016286441Srpaulo		    "Ch. %d Flags %x [%sGHz] - Added\n",
2017298877Savos		    ieee, ch_flags,
2018286441Srpaulo		    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
2019286441Srpaulo		    "5.2" : "2.4");
2020286441Srpaulo	}
2021286441Srpaulo}
2022286441Srpaulo
2023298877Savosstatic void
2024298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans,
2025298877Savos    struct ieee80211_channel chans[])
2026298877Savos{
2027298877Savos	struct iwm_softc *sc = ic->ic_softc;
2028330165Seadler	struct iwm_nvm_data *data = sc->nvm_data;
2029299883Skevlo	uint8_t bands[IEEE80211_MODE_BYTES];
2030303628Ssbruno	size_t ch_num;
2031298877Savos
2032298877Savos	memset(bands, 0, sizeof(bands));
2033298877Savos	/* 1-13: 11b/g channels. */
2034298877Savos	setbit(bands, IEEE80211_MODE_11B);
2035298877Savos	setbit(bands, IEEE80211_MODE_11G);
2036298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans, 0,
2037298877Savos	    IWM_NUM_2GHZ_CHANNELS - 1, bands);
2038298877Savos
2039298877Savos	/* 14: 11b channel only. */
2040298877Savos	clrbit(bands, IEEE80211_MODE_11G);
2041298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans,
2042298936Savos	    IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands);
2043298877Savos
2044298877Savos	if (data->sku_cap_band_52GHz_enable) {
2045330166Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
2046303628Ssbruno			ch_num = nitems(iwm_nvm_channels);
2047303628Ssbruno		else
2048303628Ssbruno			ch_num = nitems(iwm_nvm_channels_8000);
2049298877Savos		memset(bands, 0, sizeof(bands));
2050298877Savos		setbit(bands, IEEE80211_MODE_11A);
2051298877Savos		iwm_add_channel_band(sc, chans, maxchans, nchans,
2052303628Ssbruno		    IWM_NUM_2GHZ_CHANNELS, ch_num, bands);
2053298877Savos	}
2054298877Savos}
2055298877Savos
2056303628Ssbrunostatic void
2057330165Seadleriwm_set_hw_address_family_8000(struct iwm_softc *sc, struct iwm_nvm_data *data,
2058303628Ssbruno	const uint16_t *mac_override, const uint16_t *nvm_hw)
2059303628Ssbruno{
2060303628Ssbruno	const uint8_t *hw_addr;
2061303628Ssbruno
2062303628Ssbruno	if (mac_override) {
2063303628Ssbruno		static const uint8_t reserved_mac[] = {
2064303628Ssbruno			0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
2065303628Ssbruno		};
2066303628Ssbruno
2067303628Ssbruno		hw_addr = (const uint8_t *)(mac_override +
2068303628Ssbruno				 IWM_MAC_ADDRESS_OVERRIDE_8000);
2069303628Ssbruno
2070303628Ssbruno		/*
2071303628Ssbruno		 * Store the MAC address from MAO section.
2072303628Ssbruno		 * No byte swapping is required in MAO section
2073303628Ssbruno		 */
2074303628Ssbruno		IEEE80211_ADDR_COPY(data->hw_addr, hw_addr);
2075303628Ssbruno
2076303628Ssbruno		/*
2077303628Ssbruno		 * Force the use of the OTP MAC address in case of reserved MAC
2078303628Ssbruno		 * address in the NVM, or if address is given but invalid.
2079303628Ssbruno		 */
2080303628Ssbruno		if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) &&
2081303628Ssbruno		    !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) &&
2082303628Ssbruno		    iwm_is_valid_ether_addr(data->hw_addr) &&
2083303628Ssbruno		    !IEEE80211_IS_MULTICAST(data->hw_addr))
2084303628Ssbruno			return;
2085303628Ssbruno
2086303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2087303628Ssbruno		    "%s: mac address from nvm override section invalid\n",
2088303628Ssbruno		    __func__);
2089303628Ssbruno	}
2090303628Ssbruno
2091303628Ssbruno	if (nvm_hw) {
2092303628Ssbruno		/* read the mac address from WFMP registers */
2093303628Ssbruno		uint32_t mac_addr0 =
2094303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0));
2095303628Ssbruno		uint32_t mac_addr1 =
2096303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1));
2097303628Ssbruno
2098303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr0;
2099303628Ssbruno		data->hw_addr[0] = hw_addr[3];
2100303628Ssbruno		data->hw_addr[1] = hw_addr[2];
2101303628Ssbruno		data->hw_addr[2] = hw_addr[1];
2102303628Ssbruno		data->hw_addr[3] = hw_addr[0];
2103303628Ssbruno
2104303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr1;
2105303628Ssbruno		data->hw_addr[4] = hw_addr[1];
2106303628Ssbruno		data->hw_addr[5] = hw_addr[0];
2107303628Ssbruno
2108303628Ssbruno		return;
2109303628Ssbruno	}
2110303628Ssbruno
2111303628Ssbruno	device_printf(sc->sc_dev, "%s: mac address not found\n", __func__);
2112303628Ssbruno	memset(data->hw_addr, 0, sizeof(data->hw_addr));
2113303628Ssbruno}
2114303628Ssbruno
2115286441Srpaulostatic int
2116303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2117303628Ssbruno	    const uint16_t *phy_sku)
2118303628Ssbruno{
2119330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2120303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_SKU);
2121303628Ssbruno
2122303628Ssbruno	return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000));
2123303628Ssbruno}
2124303628Ssbruno
2125303628Ssbrunostatic int
2126303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2127303628Ssbruno{
2128330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2129303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_NVM_VERSION);
2130303628Ssbruno	else
2131303628Ssbruno		return le32_to_cpup((const uint32_t *)(nvm_sw +
2132303628Ssbruno						IWM_NVM_VERSION_8000));
2133303628Ssbruno}
2134303628Ssbruno
2135303628Ssbrunostatic int
2136303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2137303628Ssbruno		  const uint16_t *phy_sku)
2138303628Ssbruno{
2139330166Seadler        if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2140303628Ssbruno                return le16_to_cpup(nvm_sw + IWM_RADIO_CFG);
2141303628Ssbruno
2142303628Ssbruno        return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000));
2143303628Ssbruno}
2144303628Ssbruno
2145303628Ssbrunostatic int
2146303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2147303628Ssbruno{
2148303628Ssbruno	int n_hw_addr;
2149303628Ssbruno
2150330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2151303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS);
2152303628Ssbruno
2153303628Ssbruno	n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000));
2154303628Ssbruno
2155303628Ssbruno        return n_hw_addr & IWM_N_HW_ADDR_MASK;
2156303628Ssbruno}
2157303628Ssbruno
2158303628Ssbrunostatic void
2159303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data,
2160303628Ssbruno		  uint32_t radio_cfg)
2161303628Ssbruno{
2162330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2163303628Ssbruno		data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg);
2164303628Ssbruno		data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg);
2165303628Ssbruno		data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg);
2166303628Ssbruno		data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg);
2167303628Ssbruno		return;
2168303628Ssbruno	}
2169303628Ssbruno
2170303628Ssbruno	/* set the radio configuration for family 8000 */
2171303628Ssbruno	data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg);
2172303628Ssbruno	data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg);
2173303628Ssbruno	data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg);
2174303628Ssbruno	data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg);
2175303628Ssbruno	data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg);
2176303628Ssbruno	data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg);
2177303628Ssbruno}
2178303628Ssbruno
2179303628Ssbrunostatic int
2180330165Seadleriwm_set_hw_address(struct iwm_softc *sc, struct iwm_nvm_data *data,
2181330165Seadler		   const uint16_t *nvm_hw, const uint16_t *mac_override)
2182330165Seadler{
2183330165Seadler#ifdef notyet /* for FAMILY 9000 */
2184330165Seadler	if (cfg->mac_addr_from_csr) {
2185330165Seadler		iwm_set_hw_address_from_csr(sc, data);
2186330165Seadler        } else
2187330165Seadler#endif
2188330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2189330165Seadler		const uint8_t *hw_addr = (const uint8_t *)(nvm_hw + IWM_HW_ADDR);
2190330165Seadler
2191330165Seadler		/* The byte order is little endian 16 bit, meaning 214365 */
2192330165Seadler		data->hw_addr[0] = hw_addr[1];
2193330165Seadler		data->hw_addr[1] = hw_addr[0];
2194330165Seadler		data->hw_addr[2] = hw_addr[3];
2195330165Seadler		data->hw_addr[3] = hw_addr[2];
2196330165Seadler		data->hw_addr[4] = hw_addr[5];
2197330165Seadler		data->hw_addr[5] = hw_addr[4];
2198330165Seadler	} else {
2199330165Seadler		iwm_set_hw_address_family_8000(sc, data, mac_override, nvm_hw);
2200330165Seadler	}
2201330165Seadler
2202330165Seadler	if (!iwm_is_valid_ether_addr(data->hw_addr)) {
2203330165Seadler		device_printf(sc->sc_dev, "no valid mac address was found\n");
2204330165Seadler		return EINVAL;
2205330165Seadler	}
2206330165Seadler
2207330165Seadler	return 0;
2208330165Seadler}
2209330165Seadler
2210330165Seadlerstatic struct iwm_nvm_data *
2211286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc,
2212303628Ssbruno		   const uint16_t *nvm_hw, const uint16_t *nvm_sw,
2213303628Ssbruno		   const uint16_t *nvm_calib, const uint16_t *mac_override,
2214303628Ssbruno		   const uint16_t *phy_sku, const uint16_t *regulatory)
2215286441Srpaulo{
2216330165Seadler	struct iwm_nvm_data *data;
2217303628Ssbruno	uint32_t sku, radio_cfg;
2218286441Srpaulo
2219330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2220330165Seadler		data = malloc(sizeof(*data) +
2221330165Seadler		    IWM_NUM_CHANNELS * sizeof(uint16_t),
2222330165Seadler		    M_DEVBUF, M_NOWAIT | M_ZERO);
2223330165Seadler	} else {
2224330165Seadler		data = malloc(sizeof(*data) +
2225330165Seadler		    IWM_NUM_CHANNELS_8000 * sizeof(uint16_t),
2226330165Seadler		    M_DEVBUF, M_NOWAIT | M_ZERO);
2227330165Seadler	}
2228330165Seadler	if (!data)
2229330165Seadler		return NULL;
2230330165Seadler
2231303628Ssbruno	data->nvm_version = iwm_get_nvm_version(sc, nvm_sw);
2232286441Srpaulo
2233303628Ssbruno	radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku);
2234303628Ssbruno	iwm_set_radio_cfg(sc, data, radio_cfg);
2235286441Srpaulo
2236303628Ssbruno	sku = iwm_get_sku(sc, nvm_sw, phy_sku);
2237286441Srpaulo	data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ;
2238286441Srpaulo	data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ;
2239286441Srpaulo	data->sku_cap_11n_enable = 0;
2240286441Srpaulo
2241303628Ssbruno	data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw);
2242286441Srpaulo
2243330165Seadler	/* If no valid mac address was found - bail out */
2244330165Seadler	if (iwm_set_hw_address(sc, data, nvm_hw, mac_override)) {
2245330165Seadler		free(data, M_DEVBUF);
2246330165Seadler		return NULL;
2247303628Ssbruno	}
2248286441Srpaulo
2249330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
2250303628Ssbruno		memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS],
2251303628Ssbruno		    IWM_NUM_CHANNELS * sizeof(uint16_t));
2252303628Ssbruno	} else {
2253303628Ssbruno		memcpy(data->nvm_ch_flags, &regulatory[IWM_NVM_CHANNELS_8000],
2254303628Ssbruno		    IWM_NUM_CHANNELS_8000 * sizeof(uint16_t));
2255303628Ssbruno	}
2256286441Srpaulo
2257330165Seadler	return data;
2258286441Srpaulo}
2259286441Srpaulo
2260330165Seadlerstatic void
2261330165Seadleriwm_free_nvm_data(struct iwm_nvm_data *data)
2262330165Seadler{
2263330165Seadler	if (data != NULL)
2264330165Seadler		free(data, M_DEVBUF);
2265330165Seadler}
2266286441Srpaulo
2267330165Seadlerstatic struct iwm_nvm_data *
2268286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections)
2269286441Srpaulo{
2270303628Ssbruno	const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
2271286441Srpaulo
2272286441Srpaulo	/* Checking for required sections */
2273330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
2274303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2275330166Seadler		    !sections[sc->cfg->nvm_hw_section_num].data) {
2276303628Ssbruno			device_printf(sc->sc_dev,
2277303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2278330165Seadler			return NULL;
2279303628Ssbruno		}
2280330166Seadler	} else if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
2281303628Ssbruno		/* SW and REGULATORY sections are mandatory */
2282303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2283303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) {
2284303628Ssbruno			device_printf(sc->sc_dev,
2285303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2286330165Seadler			return NULL;
2287303628Ssbruno		}
2288303628Ssbruno		/* MAC_OVERRIDE or at least HW section must exist */
2289330166Seadler		if (!sections[sc->cfg->nvm_hw_section_num].data &&
2290303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
2291303628Ssbruno			device_printf(sc->sc_dev,
2292303628Ssbruno			    "Can't parse mac_address, empty sections\n");
2293330165Seadler			return NULL;
2294303628Ssbruno		}
2295303628Ssbruno
2296303628Ssbruno		/* PHY_SKU section is mandatory in B0 */
2297303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) {
2298303628Ssbruno			device_printf(sc->sc_dev,
2299303628Ssbruno			    "Can't parse phy_sku in B0, empty sections\n");
2300330165Seadler			return NULL;
2301303628Ssbruno		}
2302303628Ssbruno	} else {
2303330166Seadler		panic("unknown device family %d\n", sc->cfg->device_family);
2304286441Srpaulo	}
2305286441Srpaulo
2306330166Seadler	hw = (const uint16_t *) sections[sc->cfg->nvm_hw_section_num].data;
2307286441Srpaulo	sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data;
2308303628Ssbruno	calib = (const uint16_t *)
2309303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data;
2310303628Ssbruno	regulatory = (const uint16_t *)
2311303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_REGULATORY].data;
2312303628Ssbruno	mac_override = (const uint16_t *)
2313303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data;
2314303628Ssbruno	phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data;
2315303628Ssbruno
2316303628Ssbruno	return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override,
2317303628Ssbruno	    phy_sku, regulatory);
2318286441Srpaulo}
2319286441Srpaulo
2320286441Srpaulostatic int
2321286441Srpauloiwm_nvm_init(struct iwm_softc *sc)
2322286441Srpaulo{
2323330165Seadler	struct iwm_nvm_section nvm_sections[IWM_NVM_MAX_NUM_SECTIONS];
2324330165Seadler	int i, ret, section;
2325330165Seadler	uint32_t size_read = 0;
2326330165Seadler	uint8_t *nvm_buffer, *temp;
2327286441Srpaulo	uint16_t len;
2328286441Srpaulo
2329330165Seadler	memset(nvm_sections, 0, sizeof(nvm_sections));
2330286441Srpaulo
2331330166Seadler	if (sc->cfg->nvm_hw_section_num >= IWM_NVM_MAX_NUM_SECTIONS)
2332330165Seadler		return EINVAL;
2333301970Sadrian
2334330165Seadler	/* load NVM values from nic */
2335330165Seadler	/* Read From FW NVM */
2336330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Read from NVM\n");
2337286441Srpaulo
2338330166Seadler	nvm_buffer = malloc(sc->cfg->eeprom_size, M_DEVBUF, M_NOWAIT | M_ZERO);
2339330165Seadler	if (!nvm_buffer)
2340330165Seadler		return ENOMEM;
2341330165Seadler	for (section = 0; section < IWM_NVM_MAX_NUM_SECTIONS; section++) {
2342330165Seadler		/* we override the constness for initial read */
2343330165Seadler		ret = iwm_nvm_read_section(sc, section, nvm_buffer,
2344330165Seadler					   &len, size_read);
2345330165Seadler		if (ret)
2346303628Ssbruno			continue;
2347330165Seadler		size_read += len;
2348330165Seadler		temp = malloc(len, M_DEVBUF, M_NOWAIT);
2349330165Seadler		if (!temp) {
2350330165Seadler			ret = ENOMEM;
2351286441Srpaulo			break;
2352286441Srpaulo		}
2353330165Seadler		memcpy(temp, nvm_buffer, len);
2354330165Seadler
2355330165Seadler		nvm_sections[section].data = temp;
2356286441Srpaulo		nvm_sections[section].length = len;
2357286441Srpaulo	}
2358330165Seadler	if (!size_read)
2359330165Seadler		device_printf(sc->sc_dev, "OTP is blank\n");
2360330165Seadler	free(nvm_buffer, M_DEVBUF);
2361286441Srpaulo
2362330165Seadler	sc->nvm_data = iwm_parse_nvm_sections(sc, nvm_sections);
2363330165Seadler	if (!sc->nvm_data)
2364330165Seadler		return EINVAL;
2365330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
2366330165Seadler		    "nvm version = %x\n", sc->nvm_data->nvm_version);
2367330165Seadler
2368330165Seadler	for (i = 0; i < IWM_NVM_MAX_NUM_SECTIONS; i++) {
2369301970Sadrian		if (nvm_sections[i].data != NULL)
2370301970Sadrian			free(nvm_sections[i].data, M_DEVBUF);
2371301970Sadrian	}
2372301970Sadrian
2373330165Seadler	return 0;
2374286441Srpaulo}
2375286441Srpaulo
2376286441Srpaulo/*
2377286441Srpaulo * Firmware loading gunk.  This is kind of a weird hybrid between the
2378286441Srpaulo * iwn driver and the Linux iwlwifi driver.
2379286441Srpaulo */
2380286441Srpaulo
2381286441Srpaulostatic int
2382303628Ssbrunoiwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr,
2383286441Srpaulo	const uint8_t *section, uint32_t byte_cnt)
2384286441Srpaulo{
2385303628Ssbruno	int error = EINVAL;
2386303628Ssbruno	uint32_t chunk_sz, offset;
2387303628Ssbruno
2388303628Ssbruno	chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt);
2389303628Ssbruno
2390303628Ssbruno	for (offset = 0; offset < byte_cnt; offset += chunk_sz) {
2391303628Ssbruno		uint32_t addr, len;
2392303628Ssbruno		const uint8_t *data;
2393303628Ssbruno
2394303628Ssbruno		addr = dst_addr + offset;
2395303628Ssbruno		len = MIN(chunk_sz, byte_cnt - offset);
2396303628Ssbruno		data = section + offset;
2397303628Ssbruno
2398303628Ssbruno		error = iwm_firmware_load_chunk(sc, addr, data, len);
2399303628Ssbruno		if (error)
2400303628Ssbruno			break;
2401303628Ssbruno	}
2402303628Ssbruno
2403303628Ssbruno	return error;
2404303628Ssbruno}
2405303628Ssbruno
2406303628Ssbrunostatic int
2407303628Ssbrunoiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr,
2408303628Ssbruno	const uint8_t *chunk, uint32_t byte_cnt)
2409303628Ssbruno{
2410286441Srpaulo	struct iwm_dma_info *dma = &sc->fw_dma;
2411286441Srpaulo	int error;
2412286441Srpaulo
2413303628Ssbruno	/* Copy firmware chunk into pre-allocated DMA-safe memory. */
2414303628Ssbruno	memcpy(dma->vaddr, chunk, byte_cnt);
2415286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
2416286441Srpaulo
2417303628Ssbruno	if (dst_addr >= IWM_FW_MEM_EXTENDED_START &&
2418303628Ssbruno	    dst_addr <= IWM_FW_MEM_EXTENDED_END) {
2419303628Ssbruno		iwm_set_bits_prph(sc, IWM_LMPM_CHICK,
2420303628Ssbruno		    IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2421303628Ssbruno	}
2422303628Ssbruno
2423303628Ssbruno	sc->sc_fw_chunk_done = 0;
2424303628Ssbruno
2425286441Srpaulo	if (!iwm_nic_lock(sc))
2426286441Srpaulo		return EBUSY;
2427286441Srpaulo
2428286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2429286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
2430286441Srpaulo	IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL),
2431286441Srpaulo	    dst_addr);
2432286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL),
2433286441Srpaulo	    dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
2434286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL),
2435286441Srpaulo	    (iwm_get_dma_hi_addr(dma->paddr)
2436286441Srpaulo	      << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
2437286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL),
2438286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
2439286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
2440286441Srpaulo	    IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
2441286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2442286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE    |
2443286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
2444286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
2445286441Srpaulo
2446286441Srpaulo	iwm_nic_unlock(sc);
2447286441Srpaulo
2448286441Srpaulo	/* wait 1s for this segment to load */
2449286441Srpaulo	while (!sc->sc_fw_chunk_done)
2450286441Srpaulo		if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0)
2451286441Srpaulo			break;
2452286441Srpaulo
2453303628Ssbruno	if (!sc->sc_fw_chunk_done) {
2454303628Ssbruno		device_printf(sc->sc_dev,
2455303628Ssbruno		    "fw chunk addr 0x%x len %d failed to load\n",
2456303628Ssbruno		    dst_addr, byte_cnt);
2457303628Ssbruno	}
2458303628Ssbruno
2459303628Ssbruno	if (dst_addr >= IWM_FW_MEM_EXTENDED_START &&
2460303628Ssbruno	    dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) {
2461303628Ssbruno		iwm_clear_bits_prph(sc, IWM_LMPM_CHICK,
2462303628Ssbruno		    IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2463303628Ssbruno		iwm_nic_unlock(sc);
2464303628Ssbruno	}
2465303628Ssbruno
2466286441Srpaulo	return error;
2467286441Srpaulo}
2468286441Srpaulo
2469303628Ssbrunoint
2470303628Ssbrunoiwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws,
2471303628Ssbruno    int cpu, int *first_ucode_section)
2472303628Ssbruno{
2473303628Ssbruno	int shift_param;
2474303628Ssbruno	int i, error = 0, sec_num = 0x1;
2475303628Ssbruno	uint32_t val, last_read_idx = 0;
2476303628Ssbruno	const void *data;
2477303628Ssbruno	uint32_t dlen;
2478303628Ssbruno	uint32_t offset;
2479303628Ssbruno
2480303628Ssbruno	if (cpu == 1) {
2481303628Ssbruno		shift_param = 0;
2482303628Ssbruno		*first_ucode_section = 0;
2483303628Ssbruno	} else {
2484303628Ssbruno		shift_param = 16;
2485303628Ssbruno		(*first_ucode_section)++;
2486303628Ssbruno	}
2487303628Ssbruno
2488303628Ssbruno	for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) {
2489303628Ssbruno		last_read_idx = i;
2490303628Ssbruno		data = fws->fw_sect[i].fws_data;
2491303628Ssbruno		dlen = fws->fw_sect[i].fws_len;
2492303628Ssbruno		offset = fws->fw_sect[i].fws_devoff;
2493303628Ssbruno
2494303628Ssbruno		/*
2495303628Ssbruno		 * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
2496303628Ssbruno		 * CPU1 to CPU2.
2497303628Ssbruno		 * PAGING_SEPARATOR_SECTION delimiter - separate between
2498303628Ssbruno		 * CPU2 non paged to CPU2 paging sec.
2499303628Ssbruno		 */
2500303628Ssbruno		if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION ||
2501303628Ssbruno		    offset == IWM_PAGING_SEPARATOR_SECTION)
2502303628Ssbruno			break;
2503303628Ssbruno
2504303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2505303628Ssbruno		    "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n",
2506303628Ssbruno		    i, offset, dlen, cpu);
2507303628Ssbruno
2508303628Ssbruno		if (dlen > sc->sc_fwdmasegsz) {
2509303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2510303628Ssbruno			    "chunk %d too large (%d bytes)\n", i, dlen);
2511303628Ssbruno			error = EFBIG;
2512303628Ssbruno		} else {
2513303628Ssbruno			error = iwm_firmware_load_sect(sc, offset, data, dlen);
2514303628Ssbruno		}
2515303628Ssbruno		if (error) {
2516303628Ssbruno			device_printf(sc->sc_dev,
2517303628Ssbruno			    "could not load firmware chunk %d (error %d)\n",
2518303628Ssbruno			    i, error);
2519303628Ssbruno			return error;
2520303628Ssbruno		}
2521303628Ssbruno
2522303628Ssbruno		/* Notify the ucode of the loaded section number and status */
2523303628Ssbruno		if (iwm_nic_lock(sc)) {
2524303628Ssbruno			val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS);
2525303628Ssbruno			val = val | (sec_num << shift_param);
2526303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val);
2527303628Ssbruno			sec_num = (sec_num << 1) | 0x1;
2528303628Ssbruno			iwm_nic_unlock(sc);
2529303628Ssbruno
2530303628Ssbruno			/*
2531303628Ssbruno			 * The firmware won't load correctly without this delay.
2532303628Ssbruno			 */
2533303628Ssbruno			DELAY(8000);
2534303628Ssbruno		}
2535303628Ssbruno	}
2536303628Ssbruno
2537303628Ssbruno	*first_ucode_section = last_read_idx;
2538303628Ssbruno
2539303628Ssbruno	if (iwm_nic_lock(sc)) {
2540303628Ssbruno		if (cpu == 1)
2541303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF);
2542303628Ssbruno		else
2543303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
2544303628Ssbruno		iwm_nic_unlock(sc);
2545303628Ssbruno	}
2546303628Ssbruno
2547303628Ssbruno	return 0;
2548303628Ssbruno}
2549303628Ssbruno
2550303628Ssbrunoint
2551303628Ssbrunoiwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2552303628Ssbruno{
2553303628Ssbruno	struct iwm_fw_sects *fws;
2554303628Ssbruno	int error = 0;
2555303628Ssbruno	int first_ucode_section;
2556303628Ssbruno
2557303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n",
2558303628Ssbruno	    ucode_type);
2559303628Ssbruno
2560303628Ssbruno	fws = &sc->sc_fw.fw_sects[ucode_type];
2561303628Ssbruno
2562303628Ssbruno	/* configure the ucode to be ready to get the secured image */
2563303628Ssbruno	/* release CPU reset */
2564303628Ssbruno	iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT);
2565303628Ssbruno
2566303628Ssbruno	/* load to FW the binary Secured sections of CPU1 */
2567303628Ssbruno	error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section);
2568303628Ssbruno	if (error)
2569303628Ssbruno		return error;
2570303628Ssbruno
2571303628Ssbruno	/* load to FW the binary sections of CPU2 */
2572303628Ssbruno	return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section);
2573303628Ssbruno}
2574303628Ssbruno
2575286441Srpaulostatic int
2576303628Ssbrunoiwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2577286441Srpaulo{
2578286441Srpaulo	struct iwm_fw_sects *fws;
2579303628Ssbruno	int error, i;
2580286441Srpaulo	const void *data;
2581286441Srpaulo	uint32_t dlen;
2582286441Srpaulo	uint32_t offset;
2583286441Srpaulo
2584286441Srpaulo	sc->sc_uc.uc_intr = 0;
2585286441Srpaulo
2586286441Srpaulo	fws = &sc->sc_fw.fw_sects[ucode_type];
2587286441Srpaulo	for (i = 0; i < fws->fw_count; i++) {
2588286441Srpaulo		data = fws->fw_sect[i].fws_data;
2589286441Srpaulo		dlen = fws->fw_sect[i].fws_len;
2590286441Srpaulo		offset = fws->fw_sect[i].fws_devoff;
2591286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
2592286441Srpaulo		    "LOAD FIRMWARE type %d offset %u len %d\n",
2593286441Srpaulo		    ucode_type, offset, dlen);
2594303628Ssbruno		if (dlen > sc->sc_fwdmasegsz) {
2595303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
2596303628Ssbruno			    "chunk %d too large (%d bytes)\n", i, dlen);
2597303628Ssbruno			error = EFBIG;
2598303628Ssbruno		} else {
2599303628Ssbruno			error = iwm_firmware_load_sect(sc, offset, data, dlen);
2600303628Ssbruno		}
2601286441Srpaulo		if (error) {
2602286441Srpaulo			device_printf(sc->sc_dev,
2603303628Ssbruno			    "could not load firmware chunk %u of %u "
2604303628Ssbruno			    "(error=%d)\n", i, fws->fw_count, error);
2605286441Srpaulo			return error;
2606286441Srpaulo		}
2607286441Srpaulo	}
2608286441Srpaulo
2609286441Srpaulo	IWM_WRITE(sc, IWM_CSR_RESET, 0);
2610286441Srpaulo
2611303628Ssbruno	return 0;
2612303628Ssbruno}
2613303628Ssbruno
2614303628Ssbrunostatic int
2615303628Ssbrunoiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2616303628Ssbruno{
2617303628Ssbruno	int error, w;
2618303628Ssbruno
2619330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
2620303628Ssbruno		error = iwm_load_firmware_8000(sc, ucode_type);
2621303628Ssbruno	else
2622303628Ssbruno		error = iwm_load_firmware_7000(sc, ucode_type);
2623303628Ssbruno	if (error)
2624303628Ssbruno		return error;
2625303628Ssbruno
2626303628Ssbruno	/* wait for the firmware to load */
2627286441Srpaulo	for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) {
2628286441Srpaulo		error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10);
2629286441Srpaulo	}
2630303628Ssbruno	if (error || !sc->sc_uc.uc_ok) {
2631303628Ssbruno		device_printf(sc->sc_dev, "could not load firmware\n");
2632330166Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
2633303628Ssbruno			device_printf(sc->sc_dev, "cpu1 status: 0x%x\n",
2634303628Ssbruno			    iwm_read_prph(sc, IWM_SB_CPU_1_STATUS));
2635303628Ssbruno			device_printf(sc->sc_dev, "cpu2 status: 0x%x\n",
2636303628Ssbruno			    iwm_read_prph(sc, IWM_SB_CPU_2_STATUS));
2637303628Ssbruno		}
2638303628Ssbruno	}
2639286441Srpaulo
2640303628Ssbruno	/*
2641303628Ssbruno	 * Give the firmware some time to initialize.
2642303628Ssbruno	 * Accessing it too early causes errors.
2643303628Ssbruno	 */
2644303628Ssbruno	msleep(&w, &sc->sc_mtx, 0, "iwmfwinit", hz);
2645303628Ssbruno
2646286441Srpaulo	return error;
2647286441Srpaulo}
2648286441Srpaulo
2649286441Srpaulo/* iwlwifi: pcie/trans.c */
2650286441Srpaulostatic int
2651286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2652286441Srpaulo{
2653286441Srpaulo	int error;
2654286441Srpaulo
2655286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
2656286441Srpaulo
2657286441Srpaulo	if ((error = iwm_nic_init(sc)) != 0) {
2658286441Srpaulo		device_printf(sc->sc_dev, "unable to init nic\n");
2659286441Srpaulo		return error;
2660286441Srpaulo	}
2661286441Srpaulo
2662286441Srpaulo	/* make sure rfkill handshake bits are cleared */
2663286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2664286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR,
2665286441Srpaulo	    IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
2666286441Srpaulo
2667286441Srpaulo	/* clear (again), then enable host interrupts */
2668286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
2669286441Srpaulo	iwm_enable_interrupts(sc);
2670286441Srpaulo
2671286441Srpaulo	/* really make sure rfkill handshake bits are cleared */
2672286441Srpaulo	/* maybe we should write a few times more?  just to make sure */
2673286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2674286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2675286441Srpaulo
2676286441Srpaulo	/* Load the given image to the HW */
2677286441Srpaulo	return iwm_load_firmware(sc, ucode_type);
2678286441Srpaulo}
2679286441Srpaulo
2680286441Srpaulostatic int
2681286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant)
2682286441Srpaulo{
2683286441Srpaulo	struct iwm_tx_ant_cfg_cmd tx_ant_cmd = {
2684286441Srpaulo		.valid = htole32(valid_tx_ant),
2685286441Srpaulo	};
2686286441Srpaulo
2687286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD,
2688286441Srpaulo	    IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd);
2689286441Srpaulo}
2690286441Srpaulo
2691286441Srpaulo/* iwlwifi: mvm/fw.c */
2692286441Srpaulostatic int
2693286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc)
2694286441Srpaulo{
2695286441Srpaulo	struct iwm_phy_cfg_cmd phy_cfg_cmd;
2696286441Srpaulo	enum iwm_ucode_type ucode_type = sc->sc_uc_current;
2697286441Srpaulo
2698286441Srpaulo	/* Set parameters */
2699286441Srpaulo	phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config);
2700286441Srpaulo	phy_cfg_cmd.calib_control.event_trigger =
2701286441Srpaulo	    sc->sc_default_calib[ucode_type].event_trigger;
2702286441Srpaulo	phy_cfg_cmd.calib_control.flow_trigger =
2703286441Srpaulo	    sc->sc_default_calib[ucode_type].flow_trigger;
2704286441Srpaulo
2705286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
2706286441Srpaulo	    "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg);
2707286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC,
2708286441Srpaulo	    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
2709286441Srpaulo}
2710286441Srpaulo
2711286441Srpaulostatic int
2712286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc,
2713286441Srpaulo	enum iwm_ucode_type ucode_type)
2714286441Srpaulo{
2715286441Srpaulo	enum iwm_ucode_type old_type = sc->sc_uc_current;
2716286441Srpaulo	int error;
2717286441Srpaulo
2718303628Ssbruno	if ((error = iwm_read_firmware(sc, ucode_type)) != 0) {
2719303628Ssbruno		device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n",
2720303628Ssbruno			error);
2721286441Srpaulo		return error;
2722303628Ssbruno	}
2723286441Srpaulo
2724286441Srpaulo	sc->sc_uc_current = ucode_type;
2725286441Srpaulo	error = iwm_start_fw(sc, ucode_type);
2726286441Srpaulo	if (error) {
2727303628Ssbruno		device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error);
2728286441Srpaulo		sc->sc_uc_current = old_type;
2729286441Srpaulo		return error;
2730286441Srpaulo	}
2731286441Srpaulo
2732303628Ssbruno	error = iwm_post_alive(sc);
2733303628Ssbruno	if (error) {
2734303628Ssbruno		device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error);
2735303628Ssbruno	}
2736303628Ssbruno	return error;
2737286441Srpaulo}
2738286441Srpaulo
2739286441Srpaulo/*
2740286441Srpaulo * mvm misc bits
2741286441Srpaulo */
2742286441Srpaulo
2743286441Srpaulo/*
2744286441Srpaulo * follows iwlwifi/fw.c
2745286441Srpaulo */
2746286441Srpaulostatic int
2747286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm)
2748286441Srpaulo{
2749286441Srpaulo	int error;
2750286441Srpaulo
2751286441Srpaulo	/* do not operate with rfkill switch turned on */
2752286441Srpaulo	if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) {
2753286441Srpaulo		device_printf(sc->sc_dev,
2754286441Srpaulo		    "radio is disabled by hardware switch\n");
2755286441Srpaulo		return EPERM;
2756286441Srpaulo	}
2757286441Srpaulo
2758286441Srpaulo	sc->sc_init_complete = 0;
2759286441Srpaulo	if ((error = iwm_mvm_load_ucode_wait_alive(sc,
2760301192Sadrian	    IWM_UCODE_TYPE_INIT)) != 0) {
2761301192Sadrian		device_printf(sc->sc_dev, "failed to load init firmware\n");
2762286441Srpaulo		return error;
2763301192Sadrian	}
2764286441Srpaulo
2765286441Srpaulo	if (justnvm) {
2766286441Srpaulo		if ((error = iwm_nvm_init(sc)) != 0) {
2767286441Srpaulo			device_printf(sc->sc_dev, "failed to read nvm\n");
2768286441Srpaulo			return error;
2769286441Srpaulo		}
2770330165Seadler		IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->nvm_data->hw_addr);
2771286441Srpaulo
2772286441Srpaulo		return 0;
2773286441Srpaulo	}
2774286441Srpaulo
2775303628Ssbruno	if ((error = iwm_send_bt_init_conf(sc)) != 0) {
2776303628Ssbruno		device_printf(sc->sc_dev,
2777303628Ssbruno		    "failed to send bt coex configuration: %d\n", error);
2778303628Ssbruno		return error;
2779303628Ssbruno	}
2780303628Ssbruno
2781303628Ssbruno	/* Init Smart FIFO. */
2782303628Ssbruno	error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF);
2783303628Ssbruno	if (error != 0)
2784303628Ssbruno		return error;
2785303628Ssbruno
2786330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2787330156Seadler	    "%s: phy_txant=0x%08x, nvm_valid_tx_ant=0x%02x, valid=0x%02x\n",
2788330156Seadler	    __func__,
2789330156Seadler	    ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN)
2790330156Seadler	      >> IWM_FW_PHY_CFG_TX_CHAIN_POS),
2791330165Seadler	    sc->nvm_data->valid_tx_ant,
2792330156Seadler	    iwm_fw_valid_tx_ant(sc));
2793330156Seadler
2794330156Seadler
2795286441Srpaulo	/* Send TX valid antennas before triggering calibrations */
2796303628Ssbruno	if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) {
2797303628Ssbruno		device_printf(sc->sc_dev,
2798303628Ssbruno		    "failed to send antennas before calibration: %d\n", error);
2799286441Srpaulo		return error;
2800303628Ssbruno	}
2801286441Srpaulo
2802286441Srpaulo	/*
2803303628Ssbruno	 * Send phy configurations command to init uCode
2804303628Ssbruno	 * to start the 16.0 uCode init image internal calibrations.
2805303628Ssbruno	 */
2806286441Srpaulo	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) {
2807286441Srpaulo		device_printf(sc->sc_dev,
2808286441Srpaulo		    "%s: failed to run internal calibration: %d\n",
2809286441Srpaulo		    __func__, error);
2810286441Srpaulo		return error;
2811286441Srpaulo	}
2812286441Srpaulo
2813286441Srpaulo	/*
2814286441Srpaulo	 * Nothing to do but wait for the init complete notification
2815286441Srpaulo	 * from the firmware
2816286441Srpaulo	 */
2817303628Ssbruno	while (!sc->sc_init_complete) {
2818303628Ssbruno		error = msleep(&sc->sc_init_complete, &sc->sc_mtx,
2819303628Ssbruno				 0, "iwminit", 2*hz);
2820303628Ssbruno		if (error) {
2821303628Ssbruno			device_printf(sc->sc_dev, "init complete failed: %d\n",
2822303628Ssbruno				sc->sc_init_complete);
2823286441Srpaulo			break;
2824303628Ssbruno		}
2825303628Ssbruno	}
2826286441Srpaulo
2827303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "init %scomplete\n",
2828303628Ssbruno	    sc->sc_init_complete ? "" : "not ");
2829303628Ssbruno
2830286441Srpaulo	return error;
2831286441Srpaulo}
2832286441Srpaulo
2833286441Srpaulo/*
2834286441Srpaulo * receive side
2835286441Srpaulo */
2836286441Srpaulo
2837286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */
2838286441Srpaulostatic int
2839286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx)
2840286441Srpaulo{
2841286441Srpaulo	struct iwm_rx_ring *ring = &sc->rxq;
2842286441Srpaulo	struct iwm_rx_data *data = &ring->data[idx];
2843286441Srpaulo	struct mbuf *m;
2844301845Sadrian	bus_dmamap_t dmamap = NULL;
2845303628Ssbruno	bus_dma_segment_t seg;
2846303628Ssbruno	int nsegs, error;
2847286441Srpaulo
2848286441Srpaulo	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE);
2849286441Srpaulo	if (m == NULL)
2850286441Srpaulo		return ENOBUFS;
2851286441Srpaulo
2852286441Srpaulo	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
2853303628Ssbruno	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m,
2854303628Ssbruno	    &seg, &nsegs, BUS_DMA_NOWAIT);
2855303628Ssbruno	if (error != 0) {
2856286441Srpaulo		device_printf(sc->sc_dev,
2857301845Sadrian		    "%s: can't map mbuf, error %d\n", __func__, error);
2858286441Srpaulo		goto fail;
2859286441Srpaulo	}
2860301845Sadrian
2861301845Sadrian	if (data->m != NULL)
2862301845Sadrian		bus_dmamap_unload(ring->data_dmat, data->map);
2863301845Sadrian
2864301845Sadrian	/* Swap ring->spare_map with data->map */
2865301845Sadrian	dmamap = data->map;
2866301845Sadrian	data->map = ring->spare_map;
2867301845Sadrian	ring->spare_map = dmamap;
2868301845Sadrian
2869286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
2870301845Sadrian	data->m = m;
2871286441Srpaulo
2872286441Srpaulo	/* Update RX descriptor. */
2873303628Ssbruno	KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned"));
2874303628Ssbruno	ring->desc[idx] = htole32(seg.ds_addr >> 8);
2875286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2876286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2877286441Srpaulo
2878286441Srpaulo	return 0;
2879286441Srpaulofail:
2880303628Ssbruno	m_freem(m);
2881286441Srpaulo	return error;
2882286441Srpaulo}
2883286441Srpaulo
2884286441Srpaulo/* iwlwifi: mvm/rx.c */
2885286441Srpaulo#define IWM_RSSI_OFFSET 50
2886286441Srpaulostatic int
2887286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2888286441Srpaulo{
2889286441Srpaulo	int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
2890286441Srpaulo	uint32_t agc_a, agc_b;
2891286441Srpaulo	uint32_t val;
2892286441Srpaulo
2893286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]);
2894286441Srpaulo	agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS;
2895286441Srpaulo	agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS;
2896286441Srpaulo
2897286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]);
2898286441Srpaulo	rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS;
2899286441Srpaulo	rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS;
2900286441Srpaulo
2901286441Srpaulo	/*
2902286441Srpaulo	 * dBm = rssi dB - agc dB - constant.
2903286441Srpaulo	 * Higher AGC (higher radio gain) means lower signal.
2904286441Srpaulo	 */
2905286441Srpaulo	rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a;
2906286441Srpaulo	rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b;
2907286441Srpaulo	max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm);
2908286441Srpaulo
2909286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2910286441Srpaulo	    "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
2911286441Srpaulo	    rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
2912286441Srpaulo
2913286441Srpaulo	return max_rssi_dbm;
2914286441Srpaulo}
2915286441Srpaulo
2916286441Srpaulo/* iwlwifi: mvm/rx.c */
2917286441Srpaulo/*
2918286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API
2919286441Srpaulo * values are reported by the fw as positive values - need to negate
2920286441Srpaulo * to obtain their dBM.  Account for missing antennas by replacing 0
2921286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
2922286441Srpaulo */
2923286441Srpaulostatic int
2924286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2925286441Srpaulo{
2926286441Srpaulo	int energy_a, energy_b, energy_c, max_energy;
2927286441Srpaulo	uint32_t val;
2928286441Srpaulo
2929286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]);
2930286441Srpaulo	energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >>
2931286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_A_POS;
2932286441Srpaulo	energy_a = energy_a ? -energy_a : -256;
2933286441Srpaulo	energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >>
2934286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_B_POS;
2935286441Srpaulo	energy_b = energy_b ? -energy_b : -256;
2936286441Srpaulo	energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >>
2937286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_C_POS;
2938286441Srpaulo	energy_c = energy_c ? -energy_c : -256;
2939286441Srpaulo	max_energy = MAX(energy_a, energy_b);
2940286441Srpaulo	max_energy = MAX(max_energy, energy_c);
2941286441Srpaulo
2942286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2943286441Srpaulo	    "energy In A %d B %d C %d , and max %d\n",
2944286441Srpaulo	    energy_a, energy_b, energy_c, max_energy);
2945286441Srpaulo
2946286441Srpaulo	return max_energy;
2947286441Srpaulo}
2948286441Srpaulo
2949286441Srpaulostatic void
2950286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc,
2951286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2952286441Srpaulo{
2953286441Srpaulo	struct iwm_rx_phy_info *phy_info = (void *)pkt->data;
2954286441Srpaulo
2955286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n");
2956286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2957286441Srpaulo
2958286441Srpaulo	memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info));
2959286441Srpaulo}
2960286441Srpaulo
2961286441Srpaulo/*
2962286441Srpaulo * Retrieve the average noise (in dBm) among receivers.
2963286441Srpaulo */
2964286441Srpaulostatic int
2965330144Seadleriwm_get_noise(struct iwm_softc *sc,
2966330144Seadler    const struct iwm_mvm_statistics_rx_non_phy *stats)
2967286441Srpaulo{
2968286441Srpaulo	int i, total, nbant, noise;
2969286441Srpaulo
2970286441Srpaulo	total = nbant = noise = 0;
2971286441Srpaulo	for (i = 0; i < 3; i++) {
2972286441Srpaulo		noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff;
2973330144Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n",
2974330144Seadler		    __func__,
2975330144Seadler		    i,
2976330144Seadler		    noise);
2977330144Seadler
2978286441Srpaulo		if (noise) {
2979286441Srpaulo			total += noise;
2980286441Srpaulo			nbant++;
2981286441Srpaulo		}
2982286441Srpaulo	}
2983286441Srpaulo
2984330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n",
2985330144Seadler	    __func__, nbant, total);
2986330144Seadler#if 0
2987286441Srpaulo	/* There should be at least one antenna but check anyway. */
2988286441Srpaulo	return (nbant == 0) ? -127 : (total / nbant) - 107;
2989330144Seadler#else
2990330144Seadler	/* For now, just hard-code it to -96 to be safe */
2991330144Seadler	return (-96);
2992330144Seadler#endif
2993286441Srpaulo}
2994286441Srpaulo
2995286441Srpaulo/*
2996286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler
2997286441Srpaulo *
2998286441Srpaulo * Handles the actual data of the Rx packet from the fw
2999286441Srpaulo */
3000286441Srpaulostatic void
3001286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc,
3002286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
3003286441Srpaulo{
3004287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3005286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3006286441Srpaulo	struct ieee80211_frame *wh;
3007286441Srpaulo	struct ieee80211_node *ni;
3008286441Srpaulo	struct ieee80211_rx_stats rxs;
3009286441Srpaulo	struct mbuf *m;
3010286441Srpaulo	struct iwm_rx_phy_info *phy_info;
3011286441Srpaulo	struct iwm_rx_mpdu_res_start *rx_res;
3012286441Srpaulo	uint32_t len;
3013286441Srpaulo	uint32_t rx_pkt_status;
3014286441Srpaulo	int rssi;
3015286441Srpaulo
3016286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3017286441Srpaulo
3018286441Srpaulo	phy_info = &sc->sc_last_phy_info;
3019286441Srpaulo	rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
3020286441Srpaulo	wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
3021286441Srpaulo	len = le16toh(rx_res->byte_count);
3022286441Srpaulo	rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len));
3023286441Srpaulo
3024286441Srpaulo	m = data->m;
3025286441Srpaulo	m->m_data = pkt->data + sizeof(*rx_res);
3026286441Srpaulo	m->m_pkthdr.len = m->m_len = len;
3027286441Srpaulo
3028286441Srpaulo	if (__predict_false(phy_info->cfg_phy_cnt > 20)) {
3029286441Srpaulo		device_printf(sc->sc_dev,
3030286441Srpaulo		    "dsp size out of range [0,20]: %d\n",
3031286441Srpaulo		    phy_info->cfg_phy_cnt);
3032330157Seadler		goto fail;
3033286441Srpaulo	}
3034286441Srpaulo
3035286441Srpaulo	if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
3036286441Srpaulo	    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) {
3037286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3038286441Srpaulo		    "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
3039330157Seadler		goto fail;
3040286441Srpaulo	}
3041286441Srpaulo
3042286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
3043286441Srpaulo		rssi = iwm_mvm_get_signal_strength(sc, phy_info);
3044286441Srpaulo	} else {
3045286441Srpaulo		rssi = iwm_mvm_calc_rssi(sc, phy_info);
3046286441Srpaulo	}
3047286441Srpaulo
3048330144Seadler	/* Note: RSSI is absolute (ie a -ve value) */
3049330144Seadler	if (rssi < IWM_MIN_DBM)
3050330144Seadler		rssi = IWM_MIN_DBM;
3051330144Seadler	else if (rssi > IWM_MAX_DBM)
3052330144Seadler		rssi = IWM_MAX_DBM;
3053330144Seadler
3054330144Seadler	/* Map it to relative value */
3055330144Seadler	rssi = rssi - sc->sc_noise;
3056330144Seadler
3057286441Srpaulo	/* replenish ring for the buffer we're going to feed to the sharks */
3058286441Srpaulo	if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) {
3059286441Srpaulo		device_printf(sc->sc_dev, "%s: unable to add more buffers\n",
3060286441Srpaulo		    __func__);
3061330157Seadler		goto fail;
3062286441Srpaulo	}
3063286441Srpaulo
3064330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3065330144Seadler	    "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise);
3066330144Seadler
3067286441Srpaulo	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
3068286441Srpaulo
3069286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3070286441Srpaulo	    "%s: phy_info: channel=%d, flags=0x%08x\n",
3071286441Srpaulo	    __func__,
3072286441Srpaulo	    le16toh(phy_info->channel),
3073286441Srpaulo	    le16toh(phy_info->phy_flags));
3074286441Srpaulo
3075286441Srpaulo	/*
3076286441Srpaulo	 * Populate an RX state struct with the provided information.
3077286441Srpaulo	 */
3078286441Srpaulo	bzero(&rxs, sizeof(rxs));
3079286441Srpaulo	rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
3080286441Srpaulo	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
3081286441Srpaulo	rxs.c_ieee = le16toh(phy_info->channel);
3082286441Srpaulo	if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) {
3083286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ);
3084286441Srpaulo	} else {
3085286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ);
3086286441Srpaulo	}
3087330144Seadler
3088330144Seadler	/* rssi is in 1/2db units */
3089330144Seadler	rxs.rssi = rssi * 2;
3090286441Srpaulo	rxs.nf = sc->sc_noise;
3091286441Srpaulo
3092286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3093286441Srpaulo		struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap;
3094286441Srpaulo
3095286441Srpaulo		tap->wr_flags = 0;
3096286441Srpaulo		if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE))
3097286441Srpaulo			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
3098286441Srpaulo		tap->wr_chan_freq = htole16(rxs.c_freq);
3099286441Srpaulo		/* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */
3100286441Srpaulo		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
3101286441Srpaulo		tap->wr_dbm_antsignal = (int8_t)rssi;
3102286441Srpaulo		tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
3103286441Srpaulo		tap->wr_tsft = phy_info->system_timestamp;
3104286441Srpaulo		switch (phy_info->rate) {
3105286441Srpaulo		/* CCK rates. */
3106286441Srpaulo		case  10: tap->wr_rate =   2; break;
3107286441Srpaulo		case  20: tap->wr_rate =   4; break;
3108286441Srpaulo		case  55: tap->wr_rate =  11; break;
3109286441Srpaulo		case 110: tap->wr_rate =  22; break;
3110286441Srpaulo		/* OFDM rates. */
3111286441Srpaulo		case 0xd: tap->wr_rate =  12; break;
3112286441Srpaulo		case 0xf: tap->wr_rate =  18; break;
3113286441Srpaulo		case 0x5: tap->wr_rate =  24; break;
3114286441Srpaulo		case 0x7: tap->wr_rate =  36; break;
3115286441Srpaulo		case 0x9: tap->wr_rate =  48; break;
3116286441Srpaulo		case 0xb: tap->wr_rate =  72; break;
3117286441Srpaulo		case 0x1: tap->wr_rate =  96; break;
3118286441Srpaulo		case 0x3: tap->wr_rate = 108; break;
3119286441Srpaulo		/* Unknown rate: should not happen. */
3120286441Srpaulo		default:  tap->wr_rate =   0;
3121286441Srpaulo		}
3122286441Srpaulo	}
3123286441Srpaulo
3124286441Srpaulo	IWM_UNLOCK(sc);
3125286441Srpaulo	if (ni != NULL) {
3126286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m);
3127286441Srpaulo		ieee80211_input_mimo(ni, m, &rxs);
3128286441Srpaulo		ieee80211_free_node(ni);
3129286441Srpaulo	} else {
3130286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m);
3131286441Srpaulo		ieee80211_input_mimo_all(ic, m, &rxs);
3132286441Srpaulo	}
3133286441Srpaulo	IWM_LOCK(sc);
3134330157Seadler
3135330157Seadler	return;
3136330157Seadler
3137330157Seadlerfail:	counter_u64_add(ic->ic_ierrors, 1);
3138286441Srpaulo}
3139286441Srpaulo
3140293100Savosstatic int
3141286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
3142286441Srpaulo	struct iwm_node *in)
3143286441Srpaulo{
3144286441Srpaulo	struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data;
3145293100Savos	struct ieee80211_node *ni = &in->in_ni;
3146293100Savos	struct ieee80211vap *vap = ni->ni_vap;
3147286441Srpaulo	int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
3148286441Srpaulo	int failack = tx_resp->failure_frame;
3149286441Srpaulo
3150286441Srpaulo	KASSERT(tx_resp->frame_count == 1, ("too many frames"));
3151286441Srpaulo
3152286441Srpaulo	/* Update rate control statistics. */
3153298611Sadrian	IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: status=0x%04x, seq=%d, fc=%d, btc=%d, frts=%d, ff=%d, irate=%08x, wmt=%d\n",
3154298611Sadrian	    __func__,
3155298611Sadrian	    (int) le16toh(tx_resp->status.status),
3156298611Sadrian	    (int) le16toh(tx_resp->status.sequence),
3157298611Sadrian	    tx_resp->frame_count,
3158298611Sadrian	    tx_resp->bt_kill_count,
3159298611Sadrian	    tx_resp->failure_rts,
3160298611Sadrian	    tx_resp->failure_frame,
3161298611Sadrian	    le32toh(tx_resp->initial_rate),
3162298611Sadrian	    (int) le16toh(tx_resp->wireless_media_time));
3163298611Sadrian
3164286441Srpaulo	if (status != IWM_TX_STATUS_SUCCESS &&
3165286441Srpaulo	    status != IWM_TX_STATUS_DIRECT_DONE) {
3166293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3167286441Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &failack, NULL);
3168293100Savos		return (1);
3169286441Srpaulo	} else {
3170293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3171286441Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL);
3172293100Savos		return (0);
3173286441Srpaulo	}
3174286441Srpaulo}
3175286441Srpaulo
3176286441Srpaulostatic void
3177286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc,
3178286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
3179286441Srpaulo{
3180286441Srpaulo	struct iwm_cmd_header *cmd_hdr = &pkt->hdr;
3181286441Srpaulo	int idx = cmd_hdr->idx;
3182286441Srpaulo	int qid = cmd_hdr->qid;
3183286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[qid];
3184286441Srpaulo	struct iwm_tx_data *txd = &ring->data[idx];
3185286441Srpaulo	struct iwm_node *in = txd->in;
3186293100Savos	struct mbuf *m = txd->m;
3187293100Savos	int status;
3188286441Srpaulo
3189293100Savos	KASSERT(txd->done == 0, ("txd not done"));
3190293100Savos	KASSERT(txd->in != NULL, ("txd without node"));
3191293100Savos	KASSERT(txd->m != NULL, ("txd without mbuf"));
3192293100Savos
3193286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3194286441Srpaulo
3195286441Srpaulo	sc->sc_tx_timer = 0;
3196286441Srpaulo
3197293100Savos	status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in);
3198286441Srpaulo
3199286441Srpaulo	/* Unmap and free mbuf. */
3200286441Srpaulo	bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE);
3201286441Srpaulo	bus_dmamap_unload(ring->data_dmat, txd->map);
3202286441Srpaulo
3203286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3204286441Srpaulo	    "free txd %p, in %p\n", txd, txd->in);
3205286441Srpaulo	txd->done = 1;
3206286441Srpaulo	txd->m = NULL;
3207286441Srpaulo	txd->in = NULL;
3208286441Srpaulo
3209293100Savos	ieee80211_tx_complete(&in->in_ni, m, status);
3210293100Savos
3211286441Srpaulo	if (--ring->queued < IWM_TX_RING_LOMARK) {
3212286441Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
3213287197Sglebius		if (sc->qfullmsk == 0) {
3214287197Sglebius			iwm_start(sc);
3215286441Srpaulo		}
3216286441Srpaulo	}
3217286441Srpaulo}
3218286441Srpaulo
3219286441Srpaulo/*
3220286441Srpaulo * transmit side
3221286441Srpaulo */
3222286441Srpaulo
3223286441Srpaulo/*
3224286441Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
3225286441Srpaulo * processes waiting for a synchronous command completion.
3226286441Srpaulo * from if_iwn
3227286441Srpaulo */
3228286441Srpaulostatic void
3229286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3230286441Srpaulo{
3231286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE];
3232286441Srpaulo	struct iwm_tx_data *data;
3233286441Srpaulo
3234286441Srpaulo	if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) {
3235286441Srpaulo		return;	/* Not a command ack. */
3236286441Srpaulo	}
3237286441Srpaulo
3238330142Seadler	/* XXX wide commands? */
3239330142Seadler	IWM_DPRINTF(sc, IWM_DEBUG_CMD,
3240330142Seadler	    "cmd notification type 0x%x qid %d idx %d\n",
3241330142Seadler	    pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx);
3242330142Seadler
3243286441Srpaulo	data = &ring->data[pkt->hdr.idx];
3244286441Srpaulo
3245286441Srpaulo	/* If the command was mapped in an mbuf, free it. */
3246286441Srpaulo	if (data->m != NULL) {
3247286441Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3248286441Srpaulo		    BUS_DMASYNC_POSTWRITE);
3249286441Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
3250286441Srpaulo		m_freem(data->m);
3251286441Srpaulo		data->m = NULL;
3252286441Srpaulo	}
3253286441Srpaulo	wakeup(&ring->desc[pkt->hdr.idx]);
3254286441Srpaulo}
3255286441Srpaulo
3256286441Srpaulo#if 0
3257286441Srpaulo/*
3258286441Srpaulo * necessary only for block ack mode
3259286441Srpaulo */
3260286441Srpaulovoid
3261286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id,
3262286441Srpaulo	uint16_t len)
3263286441Srpaulo{
3264286441Srpaulo	struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
3265286441Srpaulo	uint16_t w_val;
3266286441Srpaulo
3267286441Srpaulo	scd_bc_tbl = sc->sched_dma.vaddr;
3268286441Srpaulo
3269286441Srpaulo	len += 8; /* magic numbers came naturally from paris */
3270286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE)
3271286441Srpaulo		len = roundup(len, 4) / 4;
3272286441Srpaulo
3273286441Srpaulo	w_val = htole16(sta_id << 12 | len);
3274286441Srpaulo
3275286441Srpaulo	/* Update TX scheduler. */
3276286441Srpaulo	scd_bc_tbl[qid].tfd_offset[idx] = w_val;
3277286441Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3278286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3279286441Srpaulo
3280286441Srpaulo	/* I really wonder what this is ?!? */
3281286441Srpaulo	if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) {
3282286441Srpaulo		scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val;
3283286441Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3284286441Srpaulo		    BUS_DMASYNC_PREWRITE);
3285286441Srpaulo	}
3286286441Srpaulo}
3287286441Srpaulo#endif
3288286441Srpaulo
3289286441Srpaulo/*
3290286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate
3291286441Srpaulo * table entry.  return the index into in_ridx[].
3292286441Srpaulo *
3293286441Srpaulo * The caller then uses that index back into in_ridx
3294286441Srpaulo * to figure out the rate index programmed /into/
3295286441Srpaulo * the firmware for this given node.
3296286441Srpaulo */
3297286441Srpaulostatic int
3298286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in,
3299286441Srpaulo    uint8_t rate)
3300286441Srpaulo{
3301286441Srpaulo	int i;
3302286441Srpaulo	uint8_t r;
3303286441Srpaulo
3304286441Srpaulo	for (i = 0; i < nitems(in->in_ridx); i++) {
3305286441Srpaulo		r = iwm_rates[in->in_ridx[i]].rate;
3306286441Srpaulo		if (rate == r)
3307286441Srpaulo			return (i);
3308286441Srpaulo	}
3309330156Seadler
3310330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3311330156Seadler	    "%s: couldn't find an entry for rate=%d\n",
3312330156Seadler	    __func__,
3313330156Seadler	    rate);
3314330156Seadler
3315286441Srpaulo	/* XXX Return the first */
3316286441Srpaulo	/* XXX TODO: have it return the /lowest/ */
3317286441Srpaulo	return (0);
3318286441Srpaulo}
3319286441Srpaulo
3320330156Seadlerstatic int
3321330156Seadleriwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate)
3322330156Seadler{
3323330156Seadler	int i;
3324330156Seadler
3325330156Seadler	for (i = 0; i < nitems(iwm_rates); i++) {
3326330156Seadler		if (iwm_rates[i].rate == rate)
3327330156Seadler			return (i);
3328330156Seadler	}
3329330156Seadler	/* XXX error? */
3330330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3331330156Seadler	    "%s: couldn't find an entry for rate=%d\n",
3332330156Seadler	    __func__,
3333330156Seadler	    rate);
3334330156Seadler	return (0);
3335330156Seadler}
3336330156Seadler
3337286441Srpaulo/*
3338298875Sadrian * Fill in the rate related information for a transmit command.
3339286441Srpaulo */
3340286441Srpaulostatic const struct iwm_rate *
3341286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in,
3342330155Seadler	struct mbuf *m, struct iwm_tx_cmd *tx)
3343286441Srpaulo{
3344286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
3345330155Seadler	struct ieee80211_frame *wh;
3346330155Seadler	const struct ieee80211_txparam *tp = ni->ni_txparms;
3347286441Srpaulo	const struct iwm_rate *rinfo;
3348330155Seadler	int type;
3349330156Seadler	int ridx, rate_flags;
3350286441Srpaulo
3351330155Seadler	wh = mtod(m, struct ieee80211_frame *);
3352330155Seadler	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3353330155Seadler
3354286441Srpaulo	tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT;
3355286441Srpaulo	tx->data_retry_limit = IWM_DEFAULT_TX_RETRY;
3356286441Srpaulo
3357330155Seadler	if (type == IEEE80211_FC0_TYPE_MGT) {
3358330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3359330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3360330156Seadler		    "%s: MGT (%d)\n", __func__, tp->mgmtrate);
3361330155Seadler	} else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3362330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mcastrate);
3363330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3364330156Seadler		    "%s: MCAST (%d)\n", __func__, tp->mcastrate);
3365330155Seadler	} else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
3366330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->ucastrate);
3367330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3368330156Seadler		    "%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate);
3369330155Seadler	} else if (m->m_flags & M_EAPOL) {
3370330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3371330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3372330156Seadler		    "%s: EAPOL\n", __func__);
3373330156Seadler	} else if (type == IEEE80211_FC0_TYPE_DATA) {
3374330156Seadler		int i;
3375330156Seadler
3376286441Srpaulo		/* for data frames, use RS table */
3377330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__);
3378330155Seadler		/* XXX pass pktlen */
3379286441Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3380286441Srpaulo		i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate);
3381286441Srpaulo		ridx = in->in_ridx[i];
3382286441Srpaulo
3383286441Srpaulo		/* This is the index into the programmed table */
3384286441Srpaulo		tx->initial_rate_index = i;
3385286441Srpaulo		tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
3386330156Seadler
3387286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3388286441Srpaulo		    "%s: start with i=%d, txrate %d\n",
3389286441Srpaulo		    __func__, i, iwm_rates[ridx].rate);
3390330156Seadler	} else {
3391330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3392330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DEFAULT (%d)\n",
3393330156Seadler		    __func__, tp->mgmtrate);
3394286441Srpaulo	}
3395286441Srpaulo
3396330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3397330156Seadler	    "%s: frame type=%d txrate %d\n",
3398330156Seadler	        __func__, type, iwm_rates[ridx].rate);
3399330156Seadler
3400286441Srpaulo	rinfo = &iwm_rates[ridx];
3401286441Srpaulo
3402286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n",
3403286441Srpaulo	    __func__, ridx,
3404286441Srpaulo	    rinfo->rate,
3405286441Srpaulo	    !! (IWM_RIDX_IS_CCK(ridx))
3406286441Srpaulo	    );
3407286441Srpaulo
3408286441Srpaulo	/* XXX TODO: hard-coded TX antenna? */
3409286441Srpaulo	rate_flags = 1 << IWM_RATE_MCS_ANT_POS;
3410286441Srpaulo	if (IWM_RIDX_IS_CCK(ridx))
3411286441Srpaulo		rate_flags |= IWM_RATE_MCS_CCK_MSK;
3412286441Srpaulo	tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
3413286441Srpaulo
3414286441Srpaulo	return rinfo;
3415286441Srpaulo}
3416286441Srpaulo
3417286441Srpaulo#define TB0_SIZE 16
3418286441Srpaulostatic int
3419286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac)
3420286441Srpaulo{
3421287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3422286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3423293099Savos	struct iwm_node *in = IWM_NODE(ni);
3424286441Srpaulo	struct iwm_tx_ring *ring;
3425286441Srpaulo	struct iwm_tx_data *data;
3426286441Srpaulo	struct iwm_tfd *desc;
3427286441Srpaulo	struct iwm_device_cmd *cmd;
3428286441Srpaulo	struct iwm_tx_cmd *tx;
3429286441Srpaulo	struct ieee80211_frame *wh;
3430286441Srpaulo	struct ieee80211_key *k = NULL;
3431286441Srpaulo	struct mbuf *m1;
3432286441Srpaulo	const struct iwm_rate *rinfo;
3433286441Srpaulo	uint32_t flags;
3434286441Srpaulo	u_int hdrlen;
3435286441Srpaulo	bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
3436286441Srpaulo	int nsegs;
3437286441Srpaulo	uint8_t tid, type;
3438286441Srpaulo	int i, totlen, error, pad;
3439286441Srpaulo
3440286441Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3441286441Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3442286441Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3443286441Srpaulo	tid = 0;
3444286441Srpaulo	ring = &sc->txq[ac];
3445286441Srpaulo	desc = &ring->desc[ring->cur];
3446286441Srpaulo	memset(desc, 0, sizeof(*desc));
3447286441Srpaulo	data = &ring->data[ring->cur];
3448286441Srpaulo
3449286441Srpaulo	/* Fill out iwm_tx_cmd to send to the firmware */
3450286441Srpaulo	cmd = &ring->cmd[ring->cur];
3451286441Srpaulo	cmd->hdr.code = IWM_TX_CMD;
3452286441Srpaulo	cmd->hdr.flags = 0;
3453286441Srpaulo	cmd->hdr.qid = ring->qid;
3454286441Srpaulo	cmd->hdr.idx = ring->cur;
3455286441Srpaulo
3456286441Srpaulo	tx = (void *)cmd->data;
3457286441Srpaulo	memset(tx, 0, sizeof(*tx));
3458286441Srpaulo
3459330155Seadler	rinfo = iwm_tx_fill_cmd(sc, in, m, tx);
3460286441Srpaulo
3461286441Srpaulo	/* Encrypt the frame if need be. */
3462286441Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
3463286441Srpaulo		/* Retrieve key for TX && do software encryption. */
3464286441Srpaulo		k = ieee80211_crypto_encap(ni, m);
3465286441Srpaulo		if (k == NULL) {
3466286441Srpaulo			m_freem(m);
3467286441Srpaulo			return (ENOBUFS);
3468286441Srpaulo		}
3469286441Srpaulo		/* 802.11 header may have moved. */
3470286441Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3471286441Srpaulo	}
3472286441Srpaulo
3473286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3474286441Srpaulo		struct iwm_tx_radiotap_header *tap = &sc->sc_txtap;
3475286441Srpaulo
3476286441Srpaulo		tap->wt_flags = 0;
3477286441Srpaulo		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
3478286441Srpaulo		tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
3479286441Srpaulo		tap->wt_rate = rinfo->rate;
3480286441Srpaulo		if (k != NULL)
3481286441Srpaulo			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3482286441Srpaulo		ieee80211_radiotap_tx(vap, m);
3483286441Srpaulo	}
3484286441Srpaulo
3485286441Srpaulo
3486286441Srpaulo	totlen = m->m_pkthdr.len;
3487286441Srpaulo
3488286441Srpaulo	flags = 0;
3489286441Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3490286441Srpaulo		flags |= IWM_TX_CMD_FLG_ACK;
3491286441Srpaulo	}
3492286441Srpaulo
3493303628Ssbruno	if (type == IEEE80211_FC0_TYPE_DATA
3494286441Srpaulo	    && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
3495286441Srpaulo	    && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3496286441Srpaulo		flags |= IWM_TX_CMD_FLG_PROT_REQUIRE;
3497286441Srpaulo	}
3498286441Srpaulo
3499286441Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3500286441Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3501286441Srpaulo		tx->sta_id = sc->sc_aux_sta.sta_id;
3502286441Srpaulo	else
3503286441Srpaulo		tx->sta_id = IWM_STATION_ID;
3504286441Srpaulo
3505286441Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3506286441Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3507286441Srpaulo
3508286441Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3509303628Ssbruno		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
3510303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC);
3511303628Ssbruno		} else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) {
3512303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3513303628Ssbruno		} else {
3514303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT);
3515303628Ssbruno		}
3516286441Srpaulo	} else {
3517303628Ssbruno		tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3518286441Srpaulo	}
3519286441Srpaulo
3520286441Srpaulo	if (hdrlen & 3) {
3521286441Srpaulo		/* First segment length must be a multiple of 4. */
3522286441Srpaulo		flags |= IWM_TX_CMD_FLG_MH_PAD;
3523286441Srpaulo		pad = 4 - (hdrlen & 3);
3524286441Srpaulo	} else
3525286441Srpaulo		pad = 0;
3526286441Srpaulo
3527286441Srpaulo	tx->driver_txop = 0;
3528286441Srpaulo	tx->next_frame_len = 0;
3529286441Srpaulo
3530286441Srpaulo	tx->len = htole16(totlen);
3531286441Srpaulo	tx->tid_tspec = tid;
3532286441Srpaulo	tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE);
3533286441Srpaulo
3534286441Srpaulo	/* Set physical address of "scratch area". */
3535286441Srpaulo	tx->dram_lsb_ptr = htole32(data->scratch_paddr);
3536286441Srpaulo	tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr);
3537286441Srpaulo
3538286441Srpaulo	/* Copy 802.11 header in TX command. */
3539286441Srpaulo	memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen);
3540286441Srpaulo
3541286441Srpaulo	flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL;
3542286441Srpaulo
3543286441Srpaulo	tx->sec_ctl = 0;
3544286441Srpaulo	tx->tx_flags |= htole32(flags);
3545286441Srpaulo
3546286441Srpaulo	/* Trim 802.11 header. */
3547286441Srpaulo	m_adj(m, hdrlen);
3548286441Srpaulo	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3549286441Srpaulo	    segs, &nsegs, BUS_DMA_NOWAIT);
3550286441Srpaulo	if (error != 0) {
3551286441Srpaulo		if (error != EFBIG) {
3552286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3553286441Srpaulo			    error);
3554286441Srpaulo			m_freem(m);
3555286441Srpaulo			return error;
3556286441Srpaulo		}
3557286441Srpaulo		/* Too many DMA segments, linearize mbuf. */
3558293119Savos		m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2);
3559286441Srpaulo		if (m1 == NULL) {
3560293119Savos			device_printf(sc->sc_dev,
3561293119Savos			    "%s: could not defrag mbuf\n", __func__);
3562286441Srpaulo			m_freem(m);
3563293119Savos			return (ENOBUFS);
3564286441Srpaulo		}
3565286441Srpaulo		m = m1;
3566293119Savos
3567286441Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3568286441Srpaulo		    segs, &nsegs, BUS_DMA_NOWAIT);
3569286441Srpaulo		if (error != 0) {
3570286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3571286441Srpaulo			    error);
3572286441Srpaulo			m_freem(m);
3573286441Srpaulo			return error;
3574286441Srpaulo		}
3575286441Srpaulo	}
3576286441Srpaulo	data->m = m;
3577286441Srpaulo	data->in = in;
3578286441Srpaulo	data->done = 0;
3579286441Srpaulo
3580286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3581286441Srpaulo	    "sending txd %p, in %p\n", data, data->in);
3582286441Srpaulo	KASSERT(data->in != NULL, ("node is NULL"));
3583286441Srpaulo
3584286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3585303628Ssbruno	    "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n",
3586298611Sadrian	    ring->qid, ring->cur, totlen, nsegs,
3587298611Sadrian	    le32toh(tx->tx_flags),
3588298611Sadrian	    le32toh(tx->rate_n_flags),
3589303628Ssbruno	    tx->initial_rate_index
3590298611Sadrian	    );
3591286441Srpaulo
3592286441Srpaulo	/* Fill TX descriptor. */
3593286441Srpaulo	desc->num_tbs = 2 + nsegs;
3594286441Srpaulo
3595286441Srpaulo	desc->tbs[0].lo = htole32(data->cmd_paddr);
3596286441Srpaulo	desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3597286441Srpaulo	    (TB0_SIZE << 4);
3598286441Srpaulo	desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE);
3599286441Srpaulo	desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3600286441Srpaulo	    ((sizeof(struct iwm_cmd_header) + sizeof(*tx)
3601286441Srpaulo	      + hdrlen + pad - TB0_SIZE) << 4);
3602286441Srpaulo
3603286441Srpaulo	/* Other DMA segments are for data payload. */
3604286441Srpaulo	for (i = 0; i < nsegs; i++) {
3605286441Srpaulo		seg = &segs[i];
3606286441Srpaulo		desc->tbs[i+2].lo = htole32(seg->ds_addr);
3607286441Srpaulo		desc->tbs[i+2].hi_n_len = \
3608286441Srpaulo		    htole16(iwm_get_dma_hi_addr(seg->ds_addr))
3609286441Srpaulo		    | ((seg->ds_len) << 4);
3610286441Srpaulo	}
3611286441Srpaulo
3612286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map,
3613286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3614286441Srpaulo	bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
3615286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3616286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3617286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3618286441Srpaulo
3619286441Srpaulo#if 0
3620286441Srpaulo	iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len));
3621286441Srpaulo#endif
3622286441Srpaulo
3623286441Srpaulo	/* Kick TX ring. */
3624286441Srpaulo	ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
3625286441Srpaulo	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3626286441Srpaulo
3627286441Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3628286441Srpaulo	if (++ring->queued > IWM_TX_RING_HIMARK) {
3629286441Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3630286441Srpaulo	}
3631286441Srpaulo
3632286441Srpaulo	return 0;
3633286441Srpaulo}
3634286441Srpaulo
3635286441Srpaulostatic int
3636286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3637286441Srpaulo    const struct ieee80211_bpf_params *params)
3638286441Srpaulo{
3639286441Srpaulo	struct ieee80211com *ic = ni->ni_ic;
3640286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
3641286441Srpaulo	int error = 0;
3642286441Srpaulo
3643286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3644286441Srpaulo	    "->%s begin\n", __func__);
3645286441Srpaulo
3646287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
3647286441Srpaulo		m_freem(m);
3648286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3649286441Srpaulo		    "<-%s not RUNNING\n", __func__);
3650286441Srpaulo		return (ENETDOWN);
3651286441Srpaulo        }
3652286441Srpaulo
3653286441Srpaulo	IWM_LOCK(sc);
3654286441Srpaulo	/* XXX fix this */
3655286441Srpaulo        if (params == NULL) {
3656286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3657286441Srpaulo	} else {
3658286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3659286441Srpaulo	}
3660286441Srpaulo	sc->sc_tx_timer = 5;
3661286441Srpaulo	IWM_UNLOCK(sc);
3662286441Srpaulo
3663286441Srpaulo        return (error);
3664286441Srpaulo}
3665286441Srpaulo
3666286441Srpaulo/*
3667286441Srpaulo * mvm/tx.c
3668286441Srpaulo */
3669286441Srpaulo
3670286441Srpaulo/*
3671286441Srpaulo * Note that there are transports that buffer frames before they reach
3672286441Srpaulo * the firmware. This means that after flush_tx_path is called, the
3673286441Srpaulo * queue might not be empty. The race-free way to handle this is to:
3674286441Srpaulo * 1) set the station as draining
3675286441Srpaulo * 2) flush the Tx path
3676286441Srpaulo * 3) wait for the transport queues to be empty
3677286441Srpaulo */
3678286441Srpauloint
3679330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags)
3680286441Srpaulo{
3681330154Seadler	int ret;
3682286441Srpaulo	struct iwm_tx_path_flush_cmd flush_cmd = {
3683286441Srpaulo		.queues_ctl = htole32(tfd_msk),
3684286441Srpaulo		.flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH),
3685286441Srpaulo	};
3686286441Srpaulo
3687330154Seadler	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags,
3688286441Srpaulo	    sizeof(flush_cmd), &flush_cmd);
3689286441Srpaulo	if (ret)
3690286441Srpaulo                device_printf(sc->sc_dev,
3691286441Srpaulo		    "Flushing tx queue failed: %d\n", ret);
3692286441Srpaulo	return ret;
3693286441Srpaulo}
3694286441Srpaulo
3695286441Srpaulo/*
3696286441Srpaulo * BEGIN mvm/sta.c
3697286441Srpaulo */
3698286441Srpaulo
3699286441Srpaulostatic int
3700286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc,
3701303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status)
3702286441Srpaulo{
3703303628Ssbruno	return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd),
3704303628Ssbruno	    cmd, status);
3705286441Srpaulo}
3706286441Srpaulo
3707286441Srpaulo/* send station add/update command to firmware */
3708286441Srpaulostatic int
3709286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update)
3710286441Srpaulo{
3711303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 add_sta_cmd;
3712286441Srpaulo	int ret;
3713286441Srpaulo	uint32_t status;
3714286441Srpaulo
3715286441Srpaulo	memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
3716286441Srpaulo
3717286441Srpaulo	add_sta_cmd.sta_id = IWM_STATION_ID;
3718286441Srpaulo	add_sta_cmd.mac_id_n_color
3719286441Srpaulo	    = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID,
3720286441Srpaulo	        IWM_DEFAULT_COLOR));
3721286441Srpaulo	if (!update) {
3722303628Ssbruno		int ac;
3723303628Ssbruno		for (ac = 0; ac < WME_NUM_AC; ac++) {
3724303628Ssbruno			add_sta_cmd.tfd_queue_msk |=
3725303628Ssbruno			    htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
3726303628Ssbruno		}
3727286441Srpaulo		IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
3728286441Srpaulo	}
3729286441Srpaulo	add_sta_cmd.add_modify = update ? 1 : 0;
3730286441Srpaulo	add_sta_cmd.station_flags_msk
3731286441Srpaulo	    |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK);
3732303628Ssbruno	add_sta_cmd.tid_disable_tx = htole16(0xffff);
3733303628Ssbruno	if (update)
3734303628Ssbruno		add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX);
3735286441Srpaulo
3736286441Srpaulo	status = IWM_ADD_STA_SUCCESS;
3737286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status);
3738286441Srpaulo	if (ret)
3739286441Srpaulo		return ret;
3740286441Srpaulo
3741286441Srpaulo	switch (status) {
3742286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3743286441Srpaulo		break;
3744286441Srpaulo	default:
3745286441Srpaulo		ret = EIO;
3746286441Srpaulo		device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
3747286441Srpaulo		break;
3748286441Srpaulo	}
3749286441Srpaulo
3750286441Srpaulo	return ret;
3751286441Srpaulo}
3752286441Srpaulo
3753286441Srpaulostatic int
3754286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
3755286441Srpaulo{
3756301192Sadrian	return iwm_mvm_sta_send_to_fw(sc, in, 0);
3757286441Srpaulo}
3758286441Srpaulo
3759286441Srpaulostatic int
3760286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
3761286441Srpaulo{
3762286441Srpaulo	return iwm_mvm_sta_send_to_fw(sc, in, 1);
3763286441Srpaulo}
3764286441Srpaulo
3765286441Srpaulostatic int
3766286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta,
3767286441Srpaulo	const uint8_t *addr, uint16_t mac_id, uint16_t color)
3768286441Srpaulo{
3769303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 cmd;
3770286441Srpaulo	int ret;
3771286441Srpaulo	uint32_t status;
3772286441Srpaulo
3773286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3774286441Srpaulo	cmd.sta_id = sta->sta_id;
3775286441Srpaulo	cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color));
3776286441Srpaulo
3777286441Srpaulo	cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk);
3778303628Ssbruno	cmd.tid_disable_tx = htole16(0xffff);
3779286441Srpaulo
3780286441Srpaulo	if (addr)
3781286441Srpaulo		IEEE80211_ADDR_COPY(cmd.addr, addr);
3782286441Srpaulo
3783286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status);
3784286441Srpaulo	if (ret)
3785286441Srpaulo		return ret;
3786286441Srpaulo
3787286441Srpaulo	switch (status) {
3788286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3789286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
3790286441Srpaulo		    "%s: Internal station added.\n", __func__);
3791286441Srpaulo		return 0;
3792286441Srpaulo	default:
3793286441Srpaulo		device_printf(sc->sc_dev,
3794286441Srpaulo		    "%s: Add internal station failed, status=0x%x\n",
3795286441Srpaulo		    __func__, status);
3796286441Srpaulo		ret = EIO;
3797286441Srpaulo		break;
3798286441Srpaulo	}
3799286441Srpaulo	return ret;
3800286441Srpaulo}
3801286441Srpaulo
3802286441Srpaulostatic int
3803286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc)
3804286441Srpaulo{
3805286441Srpaulo	int ret;
3806286441Srpaulo
3807303628Ssbruno	sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID;
3808303628Ssbruno	sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE);
3809286441Srpaulo
3810303628Ssbruno	ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST);
3811303628Ssbruno	if (ret)
3812303628Ssbruno		return ret;
3813303628Ssbruno
3814286441Srpaulo	ret = iwm_mvm_add_int_sta_common(sc,
3815286441Srpaulo	    &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0);
3816286441Srpaulo
3817286441Srpaulo	if (ret)
3818286441Srpaulo		memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
3819286441Srpaulo	return ret;
3820286441Srpaulo}
3821286441Srpaulo
3822286441Srpaulo/*
3823286441Srpaulo * END mvm/sta.c
3824286441Srpaulo */
3825286441Srpaulo
3826286441Srpaulo/*
3827286441Srpaulo * BEGIN mvm/quota.c
3828286441Srpaulo */
3829286441Srpaulo
3830286441Srpaulostatic int
3831286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in)
3832286441Srpaulo{
3833286441Srpaulo	struct iwm_time_quota_cmd cmd;
3834286441Srpaulo	int i, idx, ret, num_active_macs, quota, quota_rem;
3835286441Srpaulo	int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, };
3836286441Srpaulo	int n_ifs[IWM_MAX_BINDINGS] = {0, };
3837286441Srpaulo	uint16_t id;
3838286441Srpaulo
3839286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3840286441Srpaulo
3841286441Srpaulo	/* currently, PHY ID == binding ID */
3842286441Srpaulo	if (in) {
3843286441Srpaulo		id = in->in_phyctxt->id;
3844286441Srpaulo		KASSERT(id < IWM_MAX_BINDINGS, ("invalid id"));
3845286441Srpaulo		colors[id] = in->in_phyctxt->color;
3846286441Srpaulo
3847286441Srpaulo		if (1)
3848286441Srpaulo			n_ifs[id] = 1;
3849286441Srpaulo	}
3850286441Srpaulo
3851286441Srpaulo	/*
3852286441Srpaulo	 * The FW's scheduling session consists of
3853286441Srpaulo	 * IWM_MVM_MAX_QUOTA fragments. Divide these fragments
3854286441Srpaulo	 * equally between all the bindings that require quota
3855286441Srpaulo	 */
3856286441Srpaulo	num_active_macs = 0;
3857286441Srpaulo	for (i = 0; i < IWM_MAX_BINDINGS; i++) {
3858286441Srpaulo		cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID);
3859286441Srpaulo		num_active_macs += n_ifs[i];
3860286441Srpaulo	}
3861286441Srpaulo
3862286441Srpaulo	quota = 0;
3863286441Srpaulo	quota_rem = 0;
3864286441Srpaulo	if (num_active_macs) {
3865286441Srpaulo		quota = IWM_MVM_MAX_QUOTA / num_active_macs;
3866286441Srpaulo		quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs;
3867286441Srpaulo	}
3868286441Srpaulo
3869286441Srpaulo	for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) {
3870286441Srpaulo		if (colors[i] < 0)
3871286441Srpaulo			continue;
3872286441Srpaulo
3873286441Srpaulo		cmd.quotas[idx].id_and_color =
3874286441Srpaulo			htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i]));
3875286441Srpaulo
3876286441Srpaulo		if (n_ifs[i] <= 0) {
3877286441Srpaulo			cmd.quotas[idx].quota = htole32(0);
3878286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3879286441Srpaulo		} else {
3880286441Srpaulo			cmd.quotas[idx].quota = htole32(quota * n_ifs[i]);
3881286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3882286441Srpaulo		}
3883286441Srpaulo		idx++;
3884286441Srpaulo	}
3885286441Srpaulo
3886286441Srpaulo	/* Give the remainder of the session to the first binding */
3887286441Srpaulo	cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem);
3888286441Srpaulo
3889286441Srpaulo	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC,
3890286441Srpaulo	    sizeof(cmd), &cmd);
3891286441Srpaulo	if (ret)
3892286441Srpaulo		device_printf(sc->sc_dev,
3893286441Srpaulo		    "%s: Failed to send quota: %d\n", __func__, ret);
3894286441Srpaulo	return ret;
3895286441Srpaulo}
3896286441Srpaulo
3897286441Srpaulo/*
3898286441Srpaulo * END mvm/quota.c
3899286441Srpaulo */
3900286441Srpaulo
3901286441Srpaulo/*
3902286441Srpaulo * ieee80211 routines
3903286441Srpaulo */
3904286441Srpaulo
3905286441Srpaulo/*
3906286441Srpaulo * Change to AUTH state in 80211 state machine.  Roughly matches what
3907286441Srpaulo * Linux does in bss_info_changed().
3908286441Srpaulo */
3909286441Srpaulostatic int
3910286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc)
3911286441Srpaulo{
3912286441Srpaulo	struct ieee80211_node *ni;
3913286441Srpaulo	struct iwm_node *in;
3914286441Srpaulo	struct iwm_vap *iv = IWM_VAP(vap);
3915286441Srpaulo	uint32_t duration;
3916286441Srpaulo	int error;
3917286441Srpaulo
3918286441Srpaulo	/*
3919286441Srpaulo	 * XXX i have a feeling that the vap node is being
3920286441Srpaulo	 * freed from underneath us. Grr.
3921286441Srpaulo	 */
3922286441Srpaulo	ni = ieee80211_ref_node(vap->iv_bss);
3923293099Savos	in = IWM_NODE(ni);
3924286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE,
3925286441Srpaulo	    "%s: called; vap=%p, bss ni=%p\n",
3926286441Srpaulo	    __func__,
3927286441Srpaulo	    vap,
3928286441Srpaulo	    ni);
3929286441Srpaulo
3930286441Srpaulo	in->in_assoc = 0;
3931286441Srpaulo
3932303628Ssbruno	error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON);
3933303628Ssbruno	if (error != 0)
3934303628Ssbruno		return error;
3935303628Ssbruno
3936286441Srpaulo	error = iwm_allow_mcast(vap, sc);
3937286441Srpaulo	if (error) {
3938286441Srpaulo		device_printf(sc->sc_dev,
3939286441Srpaulo		    "%s: failed to set multicast\n", __func__);
3940286441Srpaulo		goto out;
3941286441Srpaulo	}
3942286441Srpaulo
3943286441Srpaulo	/*
3944286441Srpaulo	 * This is where it deviates from what Linux does.
3945286441Srpaulo	 *
3946286441Srpaulo	 * Linux iwlwifi doesn't reset the nic each time, nor does it
3947286441Srpaulo	 * call ctxt_add() here.  Instead, it adds it during vap creation,
3948303628Ssbruno	 * and always does a mac_ctx_changed().
3949286441Srpaulo	 *
3950286441Srpaulo	 * The openbsd port doesn't attempt to do that - it reset things
3951286441Srpaulo	 * at odd states and does the add here.
3952286441Srpaulo	 *
3953286441Srpaulo	 * So, until the state handling is fixed (ie, we never reset
3954286441Srpaulo	 * the NIC except for a firmware failure, which should drag
3955286441Srpaulo	 * the NIC back to IDLE, re-setup and re-add all the mac/phy
3956286441Srpaulo	 * contexts that are required), let's do a dirty hack here.
3957286441Srpaulo	 */
3958286441Srpaulo	if (iv->is_uploaded) {
3959286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
3960286441Srpaulo			device_printf(sc->sc_dev,
3961298582Sadrian			    "%s: failed to update MAC\n", __func__);
3962286441Srpaulo			goto out;
3963286441Srpaulo		}
3964298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3965298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3966298582Sadrian			device_printf(sc->sc_dev,
3967298582Sadrian			    "%s: failed update phy ctxt\n", __func__);
3968298582Sadrian			goto out;
3969298582Sadrian		}
3970298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3971298582Sadrian
3972298582Sadrian		if ((error = iwm_mvm_binding_update(sc, in)) != 0) {
3973298582Sadrian			device_printf(sc->sc_dev,
3974298582Sadrian			    "%s: binding update cmd\n", __func__);
3975298582Sadrian			goto out;
3976298582Sadrian		}
3977298582Sadrian		if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
3978298582Sadrian			device_printf(sc->sc_dev,
3979298582Sadrian			    "%s: failed to update sta\n", __func__);
3980298582Sadrian			goto out;
3981298582Sadrian		}
3982286441Srpaulo	} else {
3983286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) {
3984286441Srpaulo			device_printf(sc->sc_dev,
3985286441Srpaulo			    "%s: failed to add MAC\n", __func__);
3986286441Srpaulo			goto out;
3987286441Srpaulo		}
3988298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3989298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3990286441Srpaulo			device_printf(sc->sc_dev,
3991298582Sadrian			    "%s: failed add phy ctxt!\n", __func__);
3992286441Srpaulo			error = ETIMEDOUT;
3993286441Srpaulo			goto out;
3994298582Sadrian		}
3995298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3996298582Sadrian
3997298582Sadrian		if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) {
3998286441Srpaulo			device_printf(sc->sc_dev,
3999298582Sadrian			    "%s: binding add cmd\n", __func__);
4000286441Srpaulo			goto out;
4001286441Srpaulo		}
4002298582Sadrian		if ((error = iwm_mvm_add_sta(sc, in)) != 0) {
4003298582Sadrian			device_printf(sc->sc_dev,
4004298582Sadrian			    "%s: failed to add sta\n", __func__);
4005298582Sadrian			goto out;
4006298582Sadrian		}
4007286441Srpaulo	}
4008298582Sadrian
4009298582Sadrian	/*
4010298582Sadrian	 * Prevent the FW from wandering off channel during association
4011298582Sadrian	 * by "protecting" the session with a time event.
4012298582Sadrian	 */
4013298582Sadrian	/* XXX duration is in units of TU, not MS */
4014298582Sadrian	duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
4015298582Sadrian	iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */);
4016298582Sadrian	DELAY(100);
4017298582Sadrian
4018286441Srpaulo	error = 0;
4019286441Srpauloout:
4020286441Srpaulo	ieee80211_free_node(ni);
4021286441Srpaulo	return (error);
4022286441Srpaulo}
4023286441Srpaulo
4024286441Srpaulostatic int
4025286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc)
4026286441Srpaulo{
4027293099Savos	struct iwm_node *in = IWM_NODE(vap->iv_bss);
4028286441Srpaulo	int error;
4029286441Srpaulo
4030286441Srpaulo	if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
4031286441Srpaulo		device_printf(sc->sc_dev,
4032286441Srpaulo		    "%s: failed to update STA\n", __func__);
4033286441Srpaulo		return error;
4034286441Srpaulo	}
4035286441Srpaulo
4036286441Srpaulo	in->in_assoc = 1;
4037286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
4038286441Srpaulo		device_printf(sc->sc_dev,
4039286441Srpaulo		    "%s: failed to update MAC\n", __func__);
4040286441Srpaulo		return error;
4041286441Srpaulo	}
4042286441Srpaulo
4043286441Srpaulo	return 0;
4044286441Srpaulo}
4045286441Srpaulo
4046286441Srpaulostatic int
4047286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in)
4048286441Srpaulo{
4049330154Seadler	uint32_t tfd_msk;
4050330154Seadler
4051286441Srpaulo	/*
4052286441Srpaulo	 * Ok, so *technically* the proper set of calls for going
4053286441Srpaulo	 * from RUN back to SCAN is:
4054286441Srpaulo	 *
4055286441Srpaulo	 * iwm_mvm_power_mac_disable(sc, in);
4056286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
4057286441Srpaulo	 * iwm_mvm_rm_sta(sc, in);
4058286441Srpaulo	 * iwm_mvm_update_quotas(sc, NULL);
4059286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
4060286441Srpaulo	 * iwm_mvm_binding_remove_vif(sc, in);
4061286441Srpaulo	 * iwm_mvm_mac_ctxt_remove(sc, in);
4062286441Srpaulo	 *
4063286441Srpaulo	 * However, that freezes the device not matter which permutations
4064286441Srpaulo	 * and modifications are attempted.  Obviously, this driver is missing
4065286441Srpaulo	 * something since it works in the Linux driver, but figuring out what
4066286441Srpaulo	 * is missing is a little more complicated.  Now, since we're going
4067286441Srpaulo	 * back to nothing anyway, we'll just do a complete device reset.
4068286441Srpaulo	 * Up your's, device!
4069286441Srpaulo	 */
4070330154Seadler	/*
4071330154Seadler	 * Just using 0xf for the queues mask is fine as long as we only
4072330154Seadler	 * get here from RUN state.
4073330154Seadler	 */
4074330154Seadler	tfd_msk = 0xf;
4075330154Seadler	mbufq_drain(&sc->sc_snd);
4076330154Seadler	iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC);
4077330154Seadler	/*
4078330154Seadler	 * We seem to get away with just synchronously sending the
4079330154Seadler	 * IWM_TXPATH_FLUSH command.
4080330154Seadler	 */
4081330154Seadler//	iwm_trans_wait_tx_queue_empty(sc, tfd_msk);
4082286441Srpaulo	iwm_stop_device(sc);
4083286441Srpaulo	iwm_init_hw(sc);
4084286441Srpaulo	if (in)
4085286441Srpaulo		in->in_assoc = 0;
4086286441Srpaulo	return 0;
4087286441Srpaulo
4088286441Srpaulo#if 0
4089286441Srpaulo	int error;
4090286441Srpaulo
4091286441Srpaulo	iwm_mvm_power_mac_disable(sc, in);
4092286441Srpaulo
4093286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
4094286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error);
4095286441Srpaulo		return error;
4096286441Srpaulo	}
4097286441Srpaulo
4098286441Srpaulo	if ((error = iwm_mvm_rm_sta(sc, in)) != 0) {
4099286441Srpaulo		device_printf(sc->sc_dev, "sta remove fail %d\n", error);
4100286441Srpaulo		return error;
4101286441Srpaulo	}
4102286441Srpaulo	error = iwm_mvm_rm_sta(sc, in);
4103286441Srpaulo	in->in_assoc = 0;
4104286441Srpaulo	iwm_mvm_update_quotas(sc, NULL);
4105286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
4106286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error);
4107286441Srpaulo		return error;
4108286441Srpaulo	}
4109286441Srpaulo	iwm_mvm_binding_remove_vif(sc, in);
4110286441Srpaulo
4111286441Srpaulo	iwm_mvm_mac_ctxt_remove(sc, in);
4112286441Srpaulo
4113286441Srpaulo	return error;
4114286441Srpaulo#endif
4115286441Srpaulo}
4116286441Srpaulo
4117286441Srpaulostatic struct ieee80211_node *
4118286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
4119286441Srpaulo{
4120286441Srpaulo	return malloc(sizeof (struct iwm_node), M_80211_NODE,
4121286441Srpaulo	    M_NOWAIT | M_ZERO);
4122286441Srpaulo}
4123286441Srpaulo
4124286441Srpaulostatic void
4125286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
4126286441Srpaulo{
4127286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
4128286441Srpaulo	struct iwm_lq_cmd *lq = &in->in_lq;
4129286441Srpaulo	int nrates = ni->ni_rates.rs_nrates;
4130286441Srpaulo	int i, ridx, tab = 0;
4131330156Seadler//	int txant = 0;
4132286441Srpaulo
4133286441Srpaulo	if (nrates > nitems(lq->rs_table)) {
4134286441Srpaulo		device_printf(sc->sc_dev,
4135286441Srpaulo		    "%s: node supports %d rates, driver handles "
4136286441Srpaulo		    "only %zu\n", __func__, nrates, nitems(lq->rs_table));
4137286441Srpaulo		return;
4138286441Srpaulo	}
4139294248Sadrian	if (nrates == 0) {
4140294248Sadrian		device_printf(sc->sc_dev,
4141294248Sadrian		    "%s: node supports 0 rates, odd!\n", __func__);
4142294248Sadrian		return;
4143294248Sadrian	}
4144286441Srpaulo
4145286441Srpaulo	/*
4146286441Srpaulo	 * XXX .. and most of iwm_node is not initialised explicitly;
4147286441Srpaulo	 * it's all just 0x0 passed to the firmware.
4148286441Srpaulo	 */
4149286441Srpaulo
4150286441Srpaulo	/* first figure out which rates we should support */
4151286441Srpaulo	/* XXX TODO: this isn't 11n aware /at all/ */
4152286441Srpaulo	memset(&in->in_ridx, -1, sizeof(in->in_ridx));
4153286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4154286441Srpaulo	    "%s: nrates=%d\n", __func__, nrates);
4155286441Srpaulo
4156294248Sadrian	/*
4157294248Sadrian	 * Loop over nrates and populate in_ridx from the highest
4158294248Sadrian	 * rate to the lowest rate.  Remember, in_ridx[] has
4159294248Sadrian	 * IEEE80211_RATE_MAXSIZE entries!
4160294248Sadrian	 */
4161294248Sadrian	for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) {
4162294248Sadrian		int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL;
4163294248Sadrian
4164286441Srpaulo		/* Map 802.11 rate to HW rate index. */
4165286441Srpaulo		for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
4166286441Srpaulo			if (iwm_rates[ridx].rate == rate)
4167286441Srpaulo				break;
4168286441Srpaulo		if (ridx > IWM_RIDX_MAX) {
4169286441Srpaulo			device_printf(sc->sc_dev,
4170286441Srpaulo			    "%s: WARNING: device rate for %d not found!\n",
4171286441Srpaulo			    __func__, rate);
4172286441Srpaulo		} else {
4173286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4174286441Srpaulo			    "%s: rate: i: %d, rate=%d, ridx=%d\n",
4175286441Srpaulo			    __func__,
4176286441Srpaulo			    i,
4177286441Srpaulo			    rate,
4178286441Srpaulo			    ridx);
4179286441Srpaulo			in->in_ridx[i] = ridx;
4180286441Srpaulo		}
4181286441Srpaulo	}
4182286441Srpaulo
4183286441Srpaulo	/* then construct a lq_cmd based on those */
4184286441Srpaulo	memset(lq, 0, sizeof(*lq));
4185286441Srpaulo	lq->sta_id = IWM_STATION_ID;
4186286441Srpaulo
4187303628Ssbruno	/* For HT, always enable RTS/CTS to avoid excessive retries. */
4188303628Ssbruno	if (ni->ni_flags & IEEE80211_NODE_HT)
4189303628Ssbruno		lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK;
4190303628Ssbruno
4191286441Srpaulo	/*
4192286441Srpaulo	 * are these used? (we don't do SISO or MIMO)
4193286441Srpaulo	 * need to set them to non-zero, though, or we get an error.
4194286441Srpaulo	 */
4195286441Srpaulo	lq->single_stream_ant_msk = 1;
4196286441Srpaulo	lq->dual_stream_ant_msk = 1;
4197286441Srpaulo
4198286441Srpaulo	/*
4199286441Srpaulo	 * Build the actual rate selection table.
4200286441Srpaulo	 * The lowest bits are the rates.  Additionally,
4201286441Srpaulo	 * CCK needs bit 9 to be set.  The rest of the bits
4202286441Srpaulo	 * we add to the table select the tx antenna
4203286441Srpaulo	 * Note that we add the rates in the highest rate first
4204286441Srpaulo	 * (opposite of ni_rates).
4205286441Srpaulo	 */
4206286441Srpaulo	/*
4207286441Srpaulo	 * XXX TODO: this should be looping over the min of nrates
4208286441Srpaulo	 * and LQ_MAX_RETRY_NUM.  Sigh.
4209286441Srpaulo	 */
4210286441Srpaulo	for (i = 0; i < nrates; i++) {
4211286441Srpaulo		int nextant;
4212286441Srpaulo
4213330156Seadler#if 0
4214286441Srpaulo		if (txant == 0)
4215303628Ssbruno			txant = iwm_fw_valid_tx_ant(sc);
4216286441Srpaulo		nextant = 1<<(ffs(txant)-1);
4217286441Srpaulo		txant &= ~nextant;
4218330156Seadler#else
4219330156Seadler		nextant = iwm_fw_valid_tx_ant(sc);
4220330156Seadler#endif
4221286441Srpaulo		/*
4222286441Srpaulo		 * Map the rate id into a rate index into
4223286441Srpaulo		 * our hardware table containing the
4224286441Srpaulo		 * configuration to use for this rate.
4225286441Srpaulo		 */
4226294248Sadrian		ridx = in->in_ridx[i];
4227286441Srpaulo		tab = iwm_rates[ridx].plcp;
4228286441Srpaulo		tab |= nextant << IWM_RATE_MCS_ANT_POS;
4229286441Srpaulo		if (IWM_RIDX_IS_CCK(ridx))
4230286441Srpaulo			tab |= IWM_RATE_MCS_CCK_MSK;
4231286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4232286441Srpaulo		    "station rate i=%d, rate=%d, hw=%x\n",
4233286441Srpaulo		    i, iwm_rates[ridx].rate, tab);
4234286441Srpaulo		lq->rs_table[i] = htole32(tab);
4235286441Srpaulo	}
4236286441Srpaulo	/* then fill the rest with the lowest possible rate */
4237286441Srpaulo	for (i = nrates; i < nitems(lq->rs_table); i++) {
4238286441Srpaulo		KASSERT(tab != 0, ("invalid tab"));
4239286441Srpaulo		lq->rs_table[i] = htole32(tab);
4240286441Srpaulo	}
4241286441Srpaulo}
4242286441Srpaulo
4243286441Srpaulostatic int
4244286441Srpauloiwm_media_change(struct ifnet *ifp)
4245286441Srpaulo{
4246287197Sglebius	struct ieee80211vap *vap = ifp->if_softc;
4247287197Sglebius	struct ieee80211com *ic = vap->iv_ic;
4248287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
4249286441Srpaulo	int error;
4250286441Srpaulo
4251286441Srpaulo	error = ieee80211_media_change(ifp);
4252286441Srpaulo	if (error != ENETRESET)
4253286441Srpaulo		return error;
4254286441Srpaulo
4255287197Sglebius	IWM_LOCK(sc);
4256287197Sglebius	if (ic->ic_nrunning > 0) {
4257287197Sglebius		iwm_stop(sc);
4258286441Srpaulo		iwm_init(sc);
4259286441Srpaulo	}
4260287197Sglebius	IWM_UNLOCK(sc);
4261286441Srpaulo	return error;
4262286441Srpaulo}
4263286441Srpaulo
4264286441Srpaulo
4265286441Srpaulostatic int
4266286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
4267286441Srpaulo{
4268286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
4269286441Srpaulo	struct ieee80211com *ic = vap->iv_ic;
4270286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
4271286441Srpaulo	struct iwm_node *in;
4272286441Srpaulo	int error;
4273286441Srpaulo
4274286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4275286441Srpaulo	    "switching state %s -> %s\n",
4276286441Srpaulo	    ieee80211_state_name[vap->iv_state],
4277286441Srpaulo	    ieee80211_state_name[nstate]);
4278286441Srpaulo	IEEE80211_UNLOCK(ic);
4279286441Srpaulo	IWM_LOCK(sc);
4280301187Sadrian
4281301187Sadrian	if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state)
4282301187Sadrian		iwm_led_blink_stop(sc);
4283301187Sadrian
4284286441Srpaulo	/* disable beacon filtering if we're hopping out of RUN */
4285286441Srpaulo	if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) {
4286286441Srpaulo		iwm_mvm_disable_beacon_filter(sc);
4287286441Srpaulo
4288293099Savos		if (((in = IWM_NODE(vap->iv_bss)) != NULL))
4289286441Srpaulo			in->in_assoc = 0;
4290286441Srpaulo
4291330154Seadler		if (nstate == IEEE80211_S_INIT) {
4292330154Seadler			IWM_UNLOCK(sc);
4293330154Seadler			IEEE80211_LOCK(ic);
4294330154Seadler			error = ivp->iv_newstate(vap, nstate, arg);
4295330154Seadler			IEEE80211_UNLOCK(ic);
4296330154Seadler			IWM_LOCK(sc);
4297330154Seadler			iwm_release(sc, NULL);
4298330154Seadler			IWM_UNLOCK(sc);
4299330154Seadler			IEEE80211_LOCK(ic);
4300330154Seadler			return error;
4301330154Seadler		}
4302286441Srpaulo
4303286441Srpaulo		/*
4304286441Srpaulo		 * It's impossible to directly go RUN->SCAN. If we iwm_release()
4305286441Srpaulo		 * above then the card will be completely reinitialized,
4306286441Srpaulo		 * so the driver must do everything necessary to bring the card
4307286441Srpaulo		 * from INIT to SCAN.
4308286441Srpaulo		 *
4309286441Srpaulo		 * Additionally, upon receiving deauth frame from AP,
4310286441Srpaulo		 * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH
4311286441Srpaulo		 * state. This will also fail with this driver, so bring the FSM
4312286441Srpaulo		 * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well.
4313286441Srpaulo		 *
4314286441Srpaulo		 * XXX TODO: fix this for FreeBSD!
4315286441Srpaulo		 */
4316286441Srpaulo		if (nstate == IEEE80211_S_SCAN ||
4317286441Srpaulo		    nstate == IEEE80211_S_AUTH ||
4318286441Srpaulo		    nstate == IEEE80211_S_ASSOC) {
4319286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4320286441Srpaulo			    "Force transition to INIT; MGT=%d\n", arg);
4321286441Srpaulo			IWM_UNLOCK(sc);
4322286441Srpaulo			IEEE80211_LOCK(ic);
4323303628Ssbruno			/* Always pass arg as -1 since we can't Tx right now. */
4324303628Ssbruno			/*
4325303628Ssbruno			 * XXX arg is just ignored anyway when transitioning
4326303628Ssbruno			 *     to IEEE80211_S_INIT.
4327303628Ssbruno			 */
4328303628Ssbruno			vap->iv_newstate(vap, IEEE80211_S_INIT, -1);
4329286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4330286441Srpaulo			    "Going INIT->SCAN\n");
4331286441Srpaulo			nstate = IEEE80211_S_SCAN;
4332286441Srpaulo			IEEE80211_UNLOCK(ic);
4333286441Srpaulo			IWM_LOCK(sc);
4334286441Srpaulo		}
4335286441Srpaulo	}
4336286441Srpaulo
4337286441Srpaulo	switch (nstate) {
4338286441Srpaulo	case IEEE80211_S_INIT:
4339286441Srpaulo		break;
4340286441Srpaulo
4341286441Srpaulo	case IEEE80211_S_AUTH:
4342286441Srpaulo		if ((error = iwm_auth(vap, sc)) != 0) {
4343286441Srpaulo			device_printf(sc->sc_dev,
4344286441Srpaulo			    "%s: could not move to auth state: %d\n",
4345286441Srpaulo			    __func__, error);
4346286441Srpaulo			break;
4347286441Srpaulo		}
4348286441Srpaulo		break;
4349286441Srpaulo
4350286441Srpaulo	case IEEE80211_S_ASSOC:
4351286441Srpaulo		if ((error = iwm_assoc(vap, sc)) != 0) {
4352286441Srpaulo			device_printf(sc->sc_dev,
4353286441Srpaulo			    "%s: failed to associate: %d\n", __func__,
4354286441Srpaulo			    error);
4355286441Srpaulo			break;
4356286441Srpaulo		}
4357286441Srpaulo		break;
4358286441Srpaulo
4359286441Srpaulo	case IEEE80211_S_RUN:
4360286441Srpaulo	{
4361286441Srpaulo		struct iwm_host_cmd cmd = {
4362286441Srpaulo			.id = IWM_LQ_CMD,
4363286441Srpaulo			.len = { sizeof(in->in_lq), },
4364286441Srpaulo			.flags = IWM_CMD_SYNC,
4365286441Srpaulo		};
4366286441Srpaulo
4367286441Srpaulo		/* Update the association state, now we have it all */
4368286441Srpaulo		/* (eg associd comes in at this point */
4369286441Srpaulo		error = iwm_assoc(vap, sc);
4370286441Srpaulo		if (error != 0) {
4371286441Srpaulo			device_printf(sc->sc_dev,
4372286441Srpaulo			    "%s: failed to update association state: %d\n",
4373286441Srpaulo			    __func__,
4374286441Srpaulo			    error);
4375286441Srpaulo			break;
4376286441Srpaulo		}
4377286441Srpaulo
4378293099Savos		in = IWM_NODE(vap->iv_bss);
4379286441Srpaulo		iwm_mvm_power_mac_update_mode(sc, in);
4380286441Srpaulo		iwm_mvm_enable_beacon_filter(sc, in);
4381286441Srpaulo		iwm_mvm_update_quotas(sc, in);
4382286441Srpaulo		iwm_setrates(sc, in);
4383286441Srpaulo
4384286441Srpaulo		cmd.data[0] = &in->in_lq;
4385286441Srpaulo		if ((error = iwm_send_cmd(sc, &cmd)) != 0) {
4386286441Srpaulo			device_printf(sc->sc_dev,
4387286441Srpaulo			    "%s: IWM_LQ_CMD failed\n", __func__);
4388286441Srpaulo		}
4389286441Srpaulo
4390303628Ssbruno		iwm_mvm_led_enable(sc);
4391286441Srpaulo		break;
4392286441Srpaulo	}
4393286441Srpaulo
4394286441Srpaulo	default:
4395286441Srpaulo		break;
4396286441Srpaulo	}
4397286441Srpaulo	IWM_UNLOCK(sc);
4398286441Srpaulo	IEEE80211_LOCK(ic);
4399286441Srpaulo
4400286441Srpaulo	return (ivp->iv_newstate(vap, nstate, arg));
4401286441Srpaulo}
4402286441Srpaulo
4403286441Srpaulovoid
4404286441Srpauloiwm_endscan_cb(void *arg, int pending)
4405286441Srpaulo{
4406286441Srpaulo	struct iwm_softc *sc = arg;
4407287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4408286441Srpaulo
4409286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE,
4410286441Srpaulo	    "%s: scan ended\n",
4411286441Srpaulo	    __func__);
4412286441Srpaulo
4413303628Ssbruno	ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
4414303628Ssbruno}
4415303628Ssbruno
4416303628Ssbruno/*
4417303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4418303628Ssbruno * in default configuration
4419303628Ssbruno */
4420303628Ssbrunostatic const uint32_t
4421303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4422303628Ssbruno	{
4423303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
4424303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
4425303628Ssbruno	},
4426303628Ssbruno	{
4427303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF),
4428303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF)
4429303628Ssbruno	},
4430303628Ssbruno	{
4431303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER_DEF),
4432303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER_DEF)
4433303628Ssbruno	},
4434303628Ssbruno	{
4435303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER_DEF),
4436303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER_DEF)
4437303628Ssbruno	},
4438303628Ssbruno	{
4439303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER_DEF),
4440303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF)
4441303628Ssbruno	},
4442303628Ssbruno};
4443303628Ssbruno
4444303628Ssbruno/*
4445303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4446303628Ssbruno * in single BSS MAC configuration.
4447303628Ssbruno */
4448303628Ssbrunostatic const uint32_t
4449303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4450303628Ssbruno	{
4451303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER),
4452303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER)
4453303628Ssbruno	},
4454303628Ssbruno	{
4455303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER),
4456303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER)
4457303628Ssbruno	},
4458303628Ssbruno	{
4459303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER),
4460303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER)
4461303628Ssbruno	},
4462303628Ssbruno	{
4463303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER),
4464303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER)
4465303628Ssbruno	},
4466303628Ssbruno	{
4467303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER),
4468303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER)
4469303628Ssbruno	},
4470303628Ssbruno};
4471303628Ssbruno
4472303628Ssbrunostatic void
4473303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd,
4474303628Ssbruno    struct ieee80211_node *ni)
4475303628Ssbruno{
4476303628Ssbruno	int i, j, watermark;
4477303628Ssbruno
4478303628Ssbruno	sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN);
4479303628Ssbruno
4480303628Ssbruno	/*
4481303628Ssbruno	 * If we are in association flow - check antenna configuration
4482303628Ssbruno	 * capabilities of the AP station, and choose the watermark accordingly.
4483303628Ssbruno	 */
4484303628Ssbruno	if (ni) {
4485303628Ssbruno		if (ni->ni_flags & IEEE80211_NODE_HT) {
4486303628Ssbruno#ifdef notyet
4487303628Ssbruno			if (ni->ni_rxmcs[2] != 0)
4488303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO3;
4489303628Ssbruno			else if (ni->ni_rxmcs[1] != 0)
4490303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO2;
4491303628Ssbruno			else
4492303628Ssbruno#endif
4493303628Ssbruno				watermark = IWM_SF_W_MARK_SISO;
4494303628Ssbruno		} else {
4495303628Ssbruno			watermark = IWM_SF_W_MARK_LEGACY;
4496286441Srpaulo		}
4497303628Ssbruno	/* default watermark value for unassociated mode. */
4498286441Srpaulo	} else {
4499303628Ssbruno		watermark = IWM_SF_W_MARK_MIMO2;
4500286441Srpaulo	}
4501303628Ssbruno	sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark);
4502286441Srpaulo
4503303628Ssbruno	for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) {
4504303628Ssbruno		for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) {
4505303628Ssbruno			sf_cmd->long_delay_timeouts[i][j] =
4506303628Ssbruno					htole32(IWM_SF_LONG_DELAY_AGING_TIMER);
4507303628Ssbruno		}
4508286441Srpaulo	}
4509303628Ssbruno
4510303628Ssbruno	if (ni) {
4511303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout,
4512303628Ssbruno		       sizeof(iwm_sf_full_timeout));
4513303628Ssbruno	} else {
4514303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def,
4515303628Ssbruno		       sizeof(iwm_sf_full_timeout_def));
4516303628Ssbruno	}
4517286441Srpaulo}
4518286441Srpaulo
4519286441Srpaulostatic int
4520303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state)
4521303628Ssbruno{
4522303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
4523303628Ssbruno	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4524303628Ssbruno	struct iwm_sf_cfg_cmd sf_cmd = {
4525303628Ssbruno		.state = htole32(IWM_SF_FULL_ON),
4526303628Ssbruno	};
4527303628Ssbruno	int ret = 0;
4528303628Ssbruno
4529330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
4530303628Ssbruno		sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF);
4531303628Ssbruno
4532303628Ssbruno	switch (new_state) {
4533303628Ssbruno	case IWM_SF_UNINIT:
4534303628Ssbruno	case IWM_SF_INIT_OFF:
4535303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL);
4536303628Ssbruno		break;
4537303628Ssbruno	case IWM_SF_FULL_ON:
4538303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss);
4539303628Ssbruno		break;
4540303628Ssbruno	default:
4541303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE,
4542303628Ssbruno		    "Invalid state: %d. not sending Smart Fifo cmd\n",
4543303628Ssbruno			  new_state);
4544303628Ssbruno		return EINVAL;
4545303628Ssbruno	}
4546303628Ssbruno
4547303628Ssbruno	ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC,
4548303628Ssbruno				   sizeof(sf_cmd), &sf_cmd);
4549303628Ssbruno	return ret;
4550303628Ssbruno}
4551303628Ssbruno
4552303628Ssbrunostatic int
4553303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc)
4554303628Ssbruno{
4555303628Ssbruno	struct iwm_bt_coex_cmd bt_cmd;
4556303628Ssbruno
4557303628Ssbruno	bt_cmd.mode = htole32(IWM_BT_COEX_WIFI);
4558303628Ssbruno	bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET);
4559303628Ssbruno
4560303628Ssbruno	return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd),
4561303628Ssbruno	    &bt_cmd);
4562303628Ssbruno}
4563303628Ssbruno
4564303628Ssbrunostatic int
4565303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2)
4566303628Ssbruno{
4567303628Ssbruno	struct iwm_mcc_update_cmd mcc_cmd;
4568303628Ssbruno	struct iwm_host_cmd hcmd = {
4569303628Ssbruno		.id = IWM_MCC_UPDATE_CMD,
4570303628Ssbruno		.flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB),
4571303628Ssbruno		.data = { &mcc_cmd },
4572303628Ssbruno	};
4573303628Ssbruno	int ret;
4574303628Ssbruno#ifdef IWM_DEBUG
4575303628Ssbruno	struct iwm_rx_packet *pkt;
4576303628Ssbruno	struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL;
4577303628Ssbruno	struct iwm_mcc_update_resp *mcc_resp;
4578303628Ssbruno	int n_channels;
4579303628Ssbruno	uint16_t mcc;
4580303628Ssbruno#endif
4581303628Ssbruno	int resp_v2 = isset(sc->sc_enabled_capa,
4582303628Ssbruno	    IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
4583303628Ssbruno
4584303628Ssbruno	memset(&mcc_cmd, 0, sizeof(mcc_cmd));
4585303628Ssbruno	mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]);
4586303628Ssbruno	if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
4587303628Ssbruno	    isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC))
4588303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT;
4589303628Ssbruno	else
4590303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW;
4591303628Ssbruno
4592303628Ssbruno	if (resp_v2)
4593303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd);
4594303628Ssbruno	else
4595303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1);
4596303628Ssbruno
4597303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_NODE,
4598303628Ssbruno	    "send MCC update to FW with '%c%c' src = %d\n",
4599303628Ssbruno	    alpha2[0], alpha2[1], mcc_cmd.source_id);
4600303628Ssbruno
4601303628Ssbruno	ret = iwm_send_cmd(sc, &hcmd);
4602303628Ssbruno	if (ret)
4603303628Ssbruno		return ret;
4604303628Ssbruno
4605303628Ssbruno#ifdef IWM_DEBUG
4606303628Ssbruno	pkt = hcmd.resp_pkt;
4607303628Ssbruno
4608303628Ssbruno	/* Extract MCC response */
4609303628Ssbruno	if (resp_v2) {
4610303628Ssbruno		mcc_resp = (void *)pkt->data;
4611303628Ssbruno		mcc = mcc_resp->mcc;
4612303628Ssbruno		n_channels =  le32toh(mcc_resp->n_channels);
4613303628Ssbruno	} else {
4614303628Ssbruno		mcc_resp_v1 = (void *)pkt->data;
4615303628Ssbruno		mcc = mcc_resp_v1->mcc;
4616303628Ssbruno		n_channels =  le32toh(mcc_resp_v1->n_channels);
4617303628Ssbruno	}
4618303628Ssbruno
4619303628Ssbruno	/* W/A for a FW/NVM issue - returns 0x00 for the world domain */
4620303628Ssbruno	if (mcc == 0)
4621303628Ssbruno		mcc = 0x3030;  /* "00" - world */
4622303628Ssbruno
4623303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_NODE,
4624303628Ssbruno	    "regulatory domain '%c%c' (%d channels available)\n",
4625303628Ssbruno	    mcc >> 8, mcc & 0xff, n_channels);
4626303628Ssbruno#endif
4627303628Ssbruno	iwm_free_resp(sc, &hcmd);
4628303628Ssbruno
4629303628Ssbruno	return 0;
4630303628Ssbruno}
4631303628Ssbruno
4632303628Ssbrunostatic void
4633303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff)
4634303628Ssbruno{
4635303628Ssbruno	struct iwm_host_cmd cmd = {
4636303628Ssbruno		.id = IWM_REPLY_THERMAL_MNG_BACKOFF,
4637303628Ssbruno		.len = { sizeof(uint32_t), },
4638303628Ssbruno		.data = { &backoff, },
4639303628Ssbruno	};
4640303628Ssbruno
4641303628Ssbruno	if (iwm_send_cmd(sc, &cmd) != 0) {
4642303628Ssbruno		device_printf(sc->sc_dev,
4643303628Ssbruno		    "failed to change thermal tx backoff\n");
4644303628Ssbruno	}
4645303628Ssbruno}
4646303628Ssbruno
4647303628Ssbrunostatic int
4648286441Srpauloiwm_init_hw(struct iwm_softc *sc)
4649286441Srpaulo{
4650287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4651303628Ssbruno	int error, i, ac;
4652286441Srpaulo
4653303628Ssbruno	if ((error = iwm_start_hw(sc)) != 0) {
4654303628Ssbruno		printf("iwm_start_hw: failed %d\n", error);
4655286441Srpaulo		return error;
4656303628Ssbruno	}
4657286441Srpaulo
4658286441Srpaulo	if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) {
4659303628Ssbruno		printf("iwm_run_init_mvm_ucode: failed %d\n", error);
4660286441Srpaulo		return error;
4661286441Srpaulo	}
4662286441Srpaulo
4663286441Srpaulo	/*
4664286441Srpaulo	 * should stop and start HW since that INIT
4665286441Srpaulo	 * image just loaded
4666286441Srpaulo	 */
4667286441Srpaulo	iwm_stop_device(sc);
4668286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
4669286441Srpaulo		device_printf(sc->sc_dev, "could not initialize hardware\n");
4670286441Srpaulo		return error;
4671286441Srpaulo	}
4672286441Srpaulo
4673286441Srpaulo	/* omstart, this time with the regular firmware */
4674286441Srpaulo	error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR);
4675286441Srpaulo	if (error) {
4676286441Srpaulo		device_printf(sc->sc_dev, "could not load firmware\n");
4677286441Srpaulo		goto error;
4678286441Srpaulo	}
4679286441Srpaulo
4680303628Ssbruno	if ((error = iwm_send_bt_init_conf(sc)) != 0) {
4681303628Ssbruno		device_printf(sc->sc_dev, "bt init conf failed\n");
4682286441Srpaulo		goto error;
4683303628Ssbruno	}
4684286441Srpaulo
4685303628Ssbruno	if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) {
4686303628Ssbruno		device_printf(sc->sc_dev, "antenna config failed\n");
4687303628Ssbruno		goto error;
4688303628Ssbruno	}
4689303628Ssbruno
4690330163Seadler	/* Send phy db control command and then phy db calibration */
4691330163Seadler	if ((error = iwm_send_phy_db_data(sc->sc_phy_db)) != 0)
4692286441Srpaulo		goto error;
4693286441Srpaulo
4694303628Ssbruno	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) {
4695303628Ssbruno		device_printf(sc->sc_dev, "phy_cfg_cmd failed\n");
4696286441Srpaulo		goto error;
4697303628Ssbruno	}
4698286441Srpaulo
4699286441Srpaulo	/* Add auxiliary station for scanning */
4700303628Ssbruno	if ((error = iwm_mvm_add_aux_sta(sc)) != 0) {
4701303628Ssbruno		device_printf(sc->sc_dev, "add_aux_sta failed\n");
4702286441Srpaulo		goto error;
4703303628Ssbruno	}
4704286441Srpaulo
4705286441Srpaulo	for (i = 0; i < IWM_NUM_PHY_CTX; i++) {
4706286441Srpaulo		/*
4707286441Srpaulo		 * The channel used here isn't relevant as it's
4708286441Srpaulo		 * going to be overwritten in the other flows.
4709286441Srpaulo		 * For now use the first channel we have.
4710286441Srpaulo		 */
4711286441Srpaulo		if ((error = iwm_mvm_phy_ctxt_add(sc,
4712286441Srpaulo		    &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0)
4713286441Srpaulo			goto error;
4714286441Srpaulo	}
4715286441Srpaulo
4716303628Ssbruno	/* Initialize tx backoffs to the minimum. */
4717330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
4718303628Ssbruno		iwm_mvm_tt_tx_backoff(sc, 0);
4719303628Ssbruno
4720286441Srpaulo	error = iwm_mvm_power_update_device(sc);
4721286441Srpaulo	if (error)
4722286441Srpaulo		goto error;
4723286441Srpaulo
4724303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) {
4725303628Ssbruno		if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0)
4726303628Ssbruno			goto error;
4727286441Srpaulo	}
4728286441Srpaulo
4729303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
4730303628Ssbruno		if ((error = iwm_mvm_config_umac_scan(sc)) != 0)
4731303628Ssbruno			goto error;
4732303628Ssbruno	}
4733303628Ssbruno
4734303628Ssbruno	/* Enable Tx queues. */
4735303628Ssbruno	for (ac = 0; ac < WME_NUM_AC; ac++) {
4736303628Ssbruno		error = iwm_enable_txq(sc, IWM_STATION_ID, ac,
4737303628Ssbruno		    iwm_mvm_ac_to_tx_fifo[ac]);
4738303628Ssbruno		if (error)
4739303628Ssbruno			goto error;
4740303628Ssbruno	}
4741303628Ssbruno
4742303628Ssbruno	if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) {
4743303628Ssbruno		device_printf(sc->sc_dev, "failed to disable beacon filter\n");
4744303628Ssbruno		goto error;
4745303628Ssbruno	}
4746303628Ssbruno
4747286441Srpaulo	return 0;
4748286441Srpaulo
4749286441Srpaulo error:
4750286441Srpaulo	iwm_stop_device(sc);
4751286441Srpaulo	return error;
4752286441Srpaulo}
4753286441Srpaulo
4754286441Srpaulo/* Allow multicast from our BSSID. */
4755286441Srpaulostatic int
4756286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc)
4757286441Srpaulo{
4758286441Srpaulo	struct ieee80211_node *ni = vap->iv_bss;
4759286441Srpaulo	struct iwm_mcast_filter_cmd *cmd;
4760286441Srpaulo	size_t size;
4761286441Srpaulo	int error;
4762286441Srpaulo
4763286441Srpaulo	size = roundup(sizeof(*cmd), 4);
4764286441Srpaulo	cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
4765286441Srpaulo	if (cmd == NULL)
4766286441Srpaulo		return ENOMEM;
4767286441Srpaulo	cmd->filter_own = 1;
4768286441Srpaulo	cmd->port_id = 0;
4769286441Srpaulo	cmd->count = 0;
4770286441Srpaulo	cmd->pass_all = 1;
4771286441Srpaulo	IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid);
4772286441Srpaulo
4773286441Srpaulo	error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD,
4774286441Srpaulo	    IWM_CMD_SYNC, size, cmd);
4775286441Srpaulo	free(cmd, M_DEVBUF);
4776286441Srpaulo
4777286441Srpaulo	return (error);
4778286441Srpaulo}
4779286441Srpaulo
4780303628Ssbruno/*
4781303628Ssbruno * ifnet interfaces
4782303628Ssbruno */
4783303628Ssbruno
4784286441Srpaulostatic void
4785287197Sglebiusiwm_init(struct iwm_softc *sc)
4786286441Srpaulo{
4787286441Srpaulo	int error;
4788286441Srpaulo
4789286441Srpaulo	if (sc->sc_flags & IWM_FLAG_HW_INITED) {
4790286441Srpaulo		return;
4791286441Srpaulo	}
4792286441Srpaulo	sc->sc_generation++;
4793286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_STOPPED;
4794286441Srpaulo
4795286441Srpaulo	if ((error = iwm_init_hw(sc)) != 0) {
4796303628Ssbruno		printf("iwm_init_hw failed %d\n", error);
4797287197Sglebius		iwm_stop(sc);
4798286441Srpaulo		return;
4799286441Srpaulo	}
4800286441Srpaulo
4801286441Srpaulo	/*
4802303628Ssbruno	 * Ok, firmware loaded and we are jogging
4803286441Srpaulo	 */
4804286441Srpaulo	sc->sc_flags |= IWM_FLAG_HW_INITED;
4805286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
4806286441Srpaulo}
4807286441Srpaulo
4808287197Sglebiusstatic int
4809287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m)
4810286441Srpaulo{
4811287197Sglebius	struct iwm_softc *sc;
4812287197Sglebius	int error;
4813286441Srpaulo
4814287197Sglebius	sc = ic->ic_softc;
4815287197Sglebius
4816286441Srpaulo	IWM_LOCK(sc);
4817287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
4818287197Sglebius		IWM_UNLOCK(sc);
4819287197Sglebius		return (ENXIO);
4820287197Sglebius	}
4821287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
4822287197Sglebius	if (error) {
4823287197Sglebius		IWM_UNLOCK(sc);
4824287197Sglebius		return (error);
4825287197Sglebius	}
4826287197Sglebius	iwm_start(sc);
4827286441Srpaulo	IWM_UNLOCK(sc);
4828287197Sglebius	return (0);
4829286441Srpaulo}
4830286441Srpaulo
4831287197Sglebius/*
4832287197Sglebius * Dequeue packets from sendq and call send.
4833287197Sglebius */
4834286441Srpaulostatic void
4835287197Sglebiusiwm_start(struct iwm_softc *sc)
4836286441Srpaulo{
4837286441Srpaulo	struct ieee80211_node *ni;
4838286441Srpaulo	struct mbuf *m;
4839286441Srpaulo	int ac = 0;
4840286441Srpaulo
4841286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__);
4842287197Sglebius	while (sc->qfullmsk == 0 &&
4843287197Sglebius		(m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
4844286441Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4845286441Srpaulo		if (iwm_tx(sc, m, ni, ac) != 0) {
4846287197Sglebius			if_inc_counter(ni->ni_vap->iv_ifp,
4847287197Sglebius			    IFCOUNTER_OERRORS, 1);
4848286441Srpaulo			ieee80211_free_node(ni);
4849286441Srpaulo			continue;
4850286441Srpaulo		}
4851287197Sglebius		sc->sc_tx_timer = 15;
4852286441Srpaulo	}
4853286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__);
4854286441Srpaulo}
4855286441Srpaulo
4856286441Srpaulostatic void
4857287197Sglebiusiwm_stop(struct iwm_softc *sc)
4858286441Srpaulo{
4859286441Srpaulo
4860286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_HW_INITED;
4861286441Srpaulo	sc->sc_flags |= IWM_FLAG_STOPPED;
4862286441Srpaulo	sc->sc_generation++;
4863301187Sadrian	iwm_led_blink_stop(sc);
4864286441Srpaulo	sc->sc_tx_timer = 0;
4865286441Srpaulo	iwm_stop_device(sc);
4866286441Srpaulo}
4867286441Srpaulo
4868286441Srpaulostatic void
4869286441Srpauloiwm_watchdog(void *arg)
4870286441Srpaulo{
4871286441Srpaulo	struct iwm_softc *sc = arg;
4872300242Savos	struct ieee80211com *ic = &sc->sc_ic;
4873286441Srpaulo
4874286441Srpaulo	if (sc->sc_tx_timer > 0) {
4875286441Srpaulo		if (--sc->sc_tx_timer == 0) {
4876286441Srpaulo			device_printf(sc->sc_dev, "device timeout\n");
4877286441Srpaulo#ifdef IWM_DEBUG
4878286441Srpaulo			iwm_nic_error(sc);
4879286441Srpaulo#endif
4880300242Savos			ieee80211_restart_all(ic);
4881303628Ssbruno			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
4882286441Srpaulo			return;
4883286441Srpaulo		}
4884286441Srpaulo	}
4885286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
4886286441Srpaulo}
4887286441Srpaulo
4888287197Sglebiusstatic void
4889287197Sglebiusiwm_parent(struct ieee80211com *ic)
4890286441Srpaulo{
4891287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
4892287197Sglebius	int startall = 0;
4893286441Srpaulo
4894287197Sglebius	IWM_LOCK(sc);
4895287197Sglebius	if (ic->ic_nrunning > 0) {
4896287197Sglebius		if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) {
4897287197Sglebius			iwm_init(sc);
4898287197Sglebius			startall = 1;
4899286441Srpaulo		}
4900287197Sglebius	} else if (sc->sc_flags & IWM_FLAG_HW_INITED)
4901287197Sglebius		iwm_stop(sc);
4902287197Sglebius	IWM_UNLOCK(sc);
4903287197Sglebius	if (startall)
4904287197Sglebius		ieee80211_start_all(ic);
4905286441Srpaulo}
4906286441Srpaulo
4907286441Srpaulo/*
4908286441Srpaulo * The interrupt side of things
4909286441Srpaulo */
4910286441Srpaulo
4911286441Srpaulo/*
4912286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c
4913286441Srpaulo */
4914286441Srpaulo
4915286441Srpaulo/*
4916286441Srpaulo * Note: This structure is read from the device with IO accesses,
4917286441Srpaulo * and the reading already does the endian conversion. As it is
4918286441Srpaulo * read with uint32_t-sized accesses, any members with a different size
4919286441Srpaulo * need to be ordered correctly though!
4920286441Srpaulo */
4921286441Srpaulostruct iwm_error_event_table {
4922286441Srpaulo	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
4923286441Srpaulo	uint32_t error_id;		/* type of error */
4924303628Ssbruno	uint32_t trm_hw_status0;	/* TRM HW status */
4925303628Ssbruno	uint32_t trm_hw_status1;	/* TRM HW status */
4926286441Srpaulo	uint32_t blink2;		/* branch link */
4927286441Srpaulo	uint32_t ilink1;		/* interrupt link */
4928286441Srpaulo	uint32_t ilink2;		/* interrupt link */
4929286441Srpaulo	uint32_t data1;		/* error-specific data */
4930286441Srpaulo	uint32_t data2;		/* error-specific data */
4931286441Srpaulo	uint32_t data3;		/* error-specific data */
4932286441Srpaulo	uint32_t bcon_time;		/* beacon timer */
4933286441Srpaulo	uint32_t tsf_low;		/* network timestamp function timer */
4934286441Srpaulo	uint32_t tsf_hi;		/* network timestamp function timer */
4935286441Srpaulo	uint32_t gp1;		/* GP1 timer register */
4936286441Srpaulo	uint32_t gp2;		/* GP2 timer register */
4937303628Ssbruno	uint32_t fw_rev_type;	/* firmware revision type */
4938303628Ssbruno	uint32_t major;		/* uCode version major */
4939303628Ssbruno	uint32_t minor;		/* uCode version minor */
4940286441Srpaulo	uint32_t hw_ver;		/* HW Silicon version */
4941286441Srpaulo	uint32_t brd_ver;		/* HW board version */
4942286441Srpaulo	uint32_t log_pc;		/* log program counter */
4943286441Srpaulo	uint32_t frame_ptr;		/* frame pointer */
4944286441Srpaulo	uint32_t stack_ptr;		/* stack pointer */
4945286441Srpaulo	uint32_t hcmd;		/* last host command header */
4946286441Srpaulo	uint32_t isr0;		/* isr status register LMPM_NIC_ISR0:
4947286441Srpaulo				 * rxtx_flag */
4948286441Srpaulo	uint32_t isr1;		/* isr status register LMPM_NIC_ISR1:
4949286441Srpaulo				 * host_flag */
4950286441Srpaulo	uint32_t isr2;		/* isr status register LMPM_NIC_ISR2:
4951286441Srpaulo				 * enc_flag */
4952286441Srpaulo	uint32_t isr3;		/* isr status register LMPM_NIC_ISR3:
4953286441Srpaulo				 * time_flag */
4954286441Srpaulo	uint32_t isr4;		/* isr status register LMPM_NIC_ISR4:
4955286441Srpaulo				 * wico interrupt */
4956303628Ssbruno	uint32_t last_cmd_id;	/* last HCMD id handled by the firmware */
4957286441Srpaulo	uint32_t wait_event;		/* wait event() caller address */
4958286441Srpaulo	uint32_t l2p_control;	/* L2pControlField */
4959286441Srpaulo	uint32_t l2p_duration;	/* L2pDurationField */
4960286441Srpaulo	uint32_t l2p_mhvalid;	/* L2pMhValidBits */
4961286441Srpaulo	uint32_t l2p_addr_match;	/* L2pAddrMatchStat */
4962286441Srpaulo	uint32_t lmpm_pmg_sel;	/* indicate which clocks are turned on
4963286441Srpaulo				 * (LMPM_PMG_SEL) */
4964286441Srpaulo	uint32_t u_timestamp;	/* indicate when the date and time of the
4965286441Srpaulo				 * compilation */
4966286441Srpaulo	uint32_t flow_handler;	/* FH read/write pointers, RX credit */
4967303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */;
4968303628Ssbruno
4969303628Ssbruno/*
4970303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip.
4971303628Ssbruno * Note: This structure is read from the device with IO accesses,
4972303628Ssbruno * and the reading already does the endian conversion. As it is
4973303628Ssbruno * read with u32-sized accesses, any members with a different size
4974303628Ssbruno * need to be ordered correctly though!
4975303628Ssbruno */
4976303628Ssbrunostruct iwm_umac_error_event_table {
4977303628Ssbruno	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
4978303628Ssbruno	uint32_t error_id;	/* type of error */
4979303628Ssbruno	uint32_t blink1;	/* branch link */
4980303628Ssbruno	uint32_t blink2;	/* branch link */
4981303628Ssbruno	uint32_t ilink1;	/* interrupt link */
4982303628Ssbruno	uint32_t ilink2;	/* interrupt link */
4983303628Ssbruno	uint32_t data1;		/* error-specific data */
4984303628Ssbruno	uint32_t data2;		/* error-specific data */
4985303628Ssbruno	uint32_t data3;		/* error-specific data */
4986303628Ssbruno	uint32_t umac_major;
4987303628Ssbruno	uint32_t umac_minor;
4988303628Ssbruno	uint32_t frame_pointer;	/* core register 27*/
4989303628Ssbruno	uint32_t stack_pointer;	/* core register 28 */
4990303628Ssbruno	uint32_t cmd_header;	/* latest host cmd sent to UMAC */
4991303628Ssbruno	uint32_t nic_isr_pref;	/* ISR status register */
4992286441Srpaulo} __packed;
4993286441Srpaulo
4994286441Srpaulo#define ERROR_START_OFFSET  (1 * sizeof(uint32_t))
4995286441Srpaulo#define ERROR_ELEM_SIZE     (7 * sizeof(uint32_t))
4996286441Srpaulo
4997286441Srpaulo#ifdef IWM_DEBUG
4998286441Srpaulostruct {
4999286441Srpaulo	const char *name;
5000286441Srpaulo	uint8_t num;
5001286441Srpaulo} advanced_lookup[] = {
5002286441Srpaulo	{ "NMI_INTERRUPT_WDG", 0x34 },
5003286441Srpaulo	{ "SYSASSERT", 0x35 },
5004286441Srpaulo	{ "UCODE_VERSION_MISMATCH", 0x37 },
5005286441Srpaulo	{ "BAD_COMMAND", 0x38 },
5006286441Srpaulo	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
5007286441Srpaulo	{ "FATAL_ERROR", 0x3D },
5008286441Srpaulo	{ "NMI_TRM_HW_ERR", 0x46 },
5009286441Srpaulo	{ "NMI_INTERRUPT_TRM", 0x4C },
5010286441Srpaulo	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
5011286441Srpaulo	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
5012286441Srpaulo	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
5013286441Srpaulo	{ "NMI_INTERRUPT_HOST", 0x66 },
5014286441Srpaulo	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
5015286441Srpaulo	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
5016286441Srpaulo	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
5017286441Srpaulo	{ "ADVANCED_SYSASSERT", 0 },
5018286441Srpaulo};
5019286441Srpaulo
5020286441Srpaulostatic const char *
5021286441Srpauloiwm_desc_lookup(uint32_t num)
5022286441Srpaulo{
5023286441Srpaulo	int i;
5024286441Srpaulo
5025286441Srpaulo	for (i = 0; i < nitems(advanced_lookup) - 1; i++)
5026286441Srpaulo		if (advanced_lookup[i].num == num)
5027286441Srpaulo			return advanced_lookup[i].name;
5028286441Srpaulo
5029286441Srpaulo	/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
5030286441Srpaulo	return advanced_lookup[i].name;
5031286441Srpaulo}
5032286441Srpaulo
5033303628Ssbrunostatic void
5034303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc)
5035303628Ssbruno{
5036303628Ssbruno	struct iwm_umac_error_event_table table;
5037303628Ssbruno	uint32_t base;
5038303628Ssbruno
5039303628Ssbruno	base = sc->sc_uc.uc_umac_error_event_table;
5040303628Ssbruno
5041303628Ssbruno	if (base < 0x800000) {
5042303628Ssbruno		device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n",
5043303628Ssbruno		    base);
5044303628Ssbruno		return;
5045303628Ssbruno	}
5046303628Ssbruno
5047303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
5048303628Ssbruno		device_printf(sc->sc_dev, "reading errlog failed\n");
5049303628Ssbruno		return;
5050303628Ssbruno	}
5051303628Ssbruno
5052303628Ssbruno	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
5053303628Ssbruno		device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n");
5054303628Ssbruno		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
5055303628Ssbruno		    sc->sc_flags, table.valid);
5056303628Ssbruno	}
5057303628Ssbruno
5058303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id,
5059303628Ssbruno		iwm_desc_lookup(table.error_id));
5060303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1);
5061303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2);
5062303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n",
5063303628Ssbruno	    table.ilink1);
5064303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n",
5065303628Ssbruno	    table.ilink2);
5066303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1);
5067303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2);
5068303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3);
5069303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major);
5070303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor);
5071303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | frame pointer\n",
5072303628Ssbruno	    table.frame_pointer);
5073303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | stack pointer\n",
5074303628Ssbruno	    table.stack_pointer);
5075303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header);
5076303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | isr status reg\n",
5077303628Ssbruno	    table.nic_isr_pref);
5078303628Ssbruno}
5079303628Ssbruno
5080286441Srpaulo/*
5081286441Srpaulo * Support for dumping the error log seemed like a good idea ...
5082286441Srpaulo * but it's mostly hex junk and the only sensible thing is the
5083286441Srpaulo * hw/ucode revision (which we know anyway).  Since it's here,
5084286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to
5085286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later.
5086286441Srpaulo */
5087286441Srpaulostatic void
5088286441Srpauloiwm_nic_error(struct iwm_softc *sc)
5089286441Srpaulo{
5090286441Srpaulo	struct iwm_error_event_table table;
5091286441Srpaulo	uint32_t base;
5092286441Srpaulo
5093286441Srpaulo	device_printf(sc->sc_dev, "dumping device error log\n");
5094286441Srpaulo	base = sc->sc_uc.uc_error_event_table;
5095303628Ssbruno	if (base < 0x800000) {
5096286441Srpaulo		device_printf(sc->sc_dev,
5097303628Ssbruno		    "Invalid error log pointer 0x%08x\n", base);
5098286441Srpaulo		return;
5099286441Srpaulo	}
5100286441Srpaulo
5101303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
5102286441Srpaulo		device_printf(sc->sc_dev, "reading errlog failed\n");
5103286441Srpaulo		return;
5104286441Srpaulo	}
5105286441Srpaulo
5106286441Srpaulo	if (!table.valid) {
5107286441Srpaulo		device_printf(sc->sc_dev, "errlog not found, skipping\n");
5108286441Srpaulo		return;
5109286441Srpaulo	}
5110286441Srpaulo
5111286441Srpaulo	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
5112303628Ssbruno		device_printf(sc->sc_dev, "Start Error Log Dump:\n");
5113286441Srpaulo		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
5114286441Srpaulo		    sc->sc_flags, table.valid);
5115286441Srpaulo	}
5116286441Srpaulo
5117286441Srpaulo	device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id,
5118303628Ssbruno	    iwm_desc_lookup(table.error_id));
5119303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status0\n",
5120303628Ssbruno	    table.trm_hw_status0);
5121303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status1\n",
5122303628Ssbruno	    table.trm_hw_status1);
5123286441Srpaulo	device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2);
5124286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1);
5125286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2);
5126286441Srpaulo	device_printf(sc->sc_dev, "%08X | data1\n", table.data1);
5127286441Srpaulo	device_printf(sc->sc_dev, "%08X | data2\n", table.data2);
5128286441Srpaulo	device_printf(sc->sc_dev, "%08X | data3\n", table.data3);
5129286441Srpaulo	device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time);
5130286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low);
5131286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi);
5132286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1);
5133286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2);
5134303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode revision type\n",
5135303628Ssbruno	    table.fw_rev_type);
5136303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major);
5137303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor);
5138286441Srpaulo	device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver);
5139286441Srpaulo	device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver);
5140286441Srpaulo	device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd);
5141286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0);
5142286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1);
5143286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2);
5144286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3);
5145286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4);
5146303628Ssbruno	device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id);
5147286441Srpaulo	device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event);
5148286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control);
5149286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration);
5150286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid);
5151286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match);
5152286441Srpaulo	device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
5153286441Srpaulo	device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp);
5154286441Srpaulo	device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler);
5155303628Ssbruno
5156303628Ssbruno	if (sc->sc_uc.uc_umac_error_event_table)
5157303628Ssbruno		iwm_nic_umac_error(sc);
5158286441Srpaulo}
5159286441Srpaulo#endif
5160286441Srpaulo
5161286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
5162286441Srpaulo
5163286441Srpaulo/*
5164286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt.
5165286441Srpaulo * Basic structure from if_iwn
5166286441Srpaulo */
5167286441Srpaulostatic void
5168286441Srpauloiwm_notif_intr(struct iwm_softc *sc)
5169286441Srpaulo{
5170303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
5171286441Srpaulo	uint16_t hw;
5172286441Srpaulo
5173286441Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
5174286441Srpaulo	    BUS_DMASYNC_POSTREAD);
5175286441Srpaulo
5176286441Srpaulo	hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
5177303628Ssbruno
5178303628Ssbruno	/*
5179303628Ssbruno	 * Process responses
5180303628Ssbruno	 */
5181286441Srpaulo	while (sc->rxq.cur != hw) {
5182286441Srpaulo		struct iwm_rx_ring *ring = &sc->rxq;
5183330158Seadler		struct iwm_rx_data *data = &ring->data[ring->cur];
5184286441Srpaulo		struct iwm_rx_packet *pkt;
5185286441Srpaulo		struct iwm_cmd_response *cresp;
5186303628Ssbruno		int qid, idx, code;
5187286441Srpaulo
5188330158Seadler		bus_dmamap_sync(ring->data_dmat, data->map,
5189286441Srpaulo		    BUS_DMASYNC_POSTREAD);
5190286441Srpaulo		pkt = mtod(data->m, struct iwm_rx_packet *);
5191286441Srpaulo
5192286441Srpaulo		qid = pkt->hdr.qid & ~0x80;
5193286441Srpaulo		idx = pkt->hdr.idx;
5194286441Srpaulo
5195303628Ssbruno		code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
5196286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5197303628Ssbruno		    "rx packet qid=%d idx=%d type=%x %d %d\n",
5198330158Seadler		    pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, ring->cur, hw);
5199286441Srpaulo
5200286441Srpaulo		/*
5201286441Srpaulo		 * randomly get these from the firmware, no idea why.
5202286441Srpaulo		 * they at least seem harmless, so just ignore them for now
5203286441Srpaulo		 */
5204286441Srpaulo		if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
5205286441Srpaulo		    || pkt->len_n_flags == htole32(0x55550000))) {
5206286441Srpaulo			ADVANCE_RXQ(sc);
5207286441Srpaulo			continue;
5208286441Srpaulo		}
5209286441Srpaulo
5210303628Ssbruno		switch (code) {
5211286441Srpaulo		case IWM_REPLY_RX_PHY_CMD:
5212286441Srpaulo			iwm_mvm_rx_rx_phy_cmd(sc, pkt, data);
5213286441Srpaulo			break;
5214286441Srpaulo
5215286441Srpaulo		case IWM_REPLY_RX_MPDU_CMD:
5216286441Srpaulo			iwm_mvm_rx_rx_mpdu(sc, pkt, data);
5217286441Srpaulo			break;
5218286441Srpaulo
5219286441Srpaulo		case IWM_TX_CMD:
5220286441Srpaulo			iwm_mvm_rx_tx_cmd(sc, pkt, data);
5221286441Srpaulo			break;
5222286441Srpaulo
5223286441Srpaulo		case IWM_MISSED_BEACONS_NOTIFICATION: {
5224286441Srpaulo			struct iwm_missed_beacons_notif *resp;
5225286441Srpaulo			int missed;
5226286441Srpaulo
5227286441Srpaulo			/* XXX look at mac_id to determine interface ID */
5228286441Srpaulo			struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5229286441Srpaulo
5230330158Seadler			resp = (void *)pkt->data;
5231286441Srpaulo			missed = le32toh(resp->consec_missed_beacons);
5232286441Srpaulo
5233286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE,
5234286441Srpaulo			    "%s: MISSED_BEACON: mac_id=%d, "
5235286441Srpaulo			    "consec_since_last_rx=%d, consec=%d, num_expect=%d "
5236286441Srpaulo			    "num_rx=%d\n",
5237286441Srpaulo			    __func__,
5238286441Srpaulo			    le32toh(resp->mac_id),
5239286441Srpaulo			    le32toh(resp->consec_missed_beacons_since_last_rx),
5240286441Srpaulo			    le32toh(resp->consec_missed_beacons),
5241286441Srpaulo			    le32toh(resp->num_expected_beacons),
5242286441Srpaulo			    le32toh(resp->num_recvd_beacons));
5243286441Srpaulo
5244286441Srpaulo			/* Be paranoid */
5245286441Srpaulo			if (vap == NULL)
5246286441Srpaulo				break;
5247286441Srpaulo
5248286441Srpaulo			/* XXX no net80211 locking? */
5249286441Srpaulo			if (vap->iv_state == IEEE80211_S_RUN &&
5250286441Srpaulo			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
5251286441Srpaulo				if (missed > vap->iv_bmissthreshold) {
5252286441Srpaulo					/* XXX bad locking; turn into task */
5253286441Srpaulo					IWM_UNLOCK(sc);
5254286441Srpaulo					ieee80211_beacon_miss(ic);
5255286441Srpaulo					IWM_LOCK(sc);
5256286441Srpaulo				}
5257286441Srpaulo			}
5258286441Srpaulo
5259286441Srpaulo			break; }
5260286441Srpaulo
5261303628Ssbruno		case IWM_MFUART_LOAD_NOTIFICATION:
5262303628Ssbruno			break;
5263303628Ssbruno
5264286441Srpaulo		case IWM_MVM_ALIVE: {
5265303628Ssbruno			struct iwm_mvm_alive_resp_v1 *resp1;
5266303628Ssbruno			struct iwm_mvm_alive_resp_v2 *resp2;
5267303628Ssbruno			struct iwm_mvm_alive_resp_v3 *resp3;
5268286441Srpaulo
5269303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) {
5270330158Seadler				resp1 = (void *)pkt->data;
5271303628Ssbruno				sc->sc_uc.uc_error_event_table
5272303628Ssbruno				    = le32toh(resp1->error_event_table_ptr);
5273303628Ssbruno				sc->sc_uc.uc_log_event_table
5274303628Ssbruno				    = le32toh(resp1->log_event_table_ptr);
5275303628Ssbruno				sc->sched_base = le32toh(resp1->scd_base_ptr);
5276303628Ssbruno				if (resp1->status == IWM_ALIVE_STATUS_OK)
5277303628Ssbruno					sc->sc_uc.uc_ok = 1;
5278303628Ssbruno				else
5279303628Ssbruno					sc->sc_uc.uc_ok = 0;
5280303628Ssbruno			}
5281286441Srpaulo
5282303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) {
5283330158Seadler				resp2 = (void *)pkt->data;
5284303628Ssbruno				sc->sc_uc.uc_error_event_table
5285303628Ssbruno				    = le32toh(resp2->error_event_table_ptr);
5286303628Ssbruno				sc->sc_uc.uc_log_event_table
5287303628Ssbruno				    = le32toh(resp2->log_event_table_ptr);
5288303628Ssbruno				sc->sched_base = le32toh(resp2->scd_base_ptr);
5289303628Ssbruno				sc->sc_uc.uc_umac_error_event_table
5290303628Ssbruno				    = le32toh(resp2->error_info_addr);
5291303628Ssbruno				if (resp2->status == IWM_ALIVE_STATUS_OK)
5292303628Ssbruno					sc->sc_uc.uc_ok = 1;
5293303628Ssbruno				else
5294303628Ssbruno					sc->sc_uc.uc_ok = 0;
5295303628Ssbruno			}
5296303628Ssbruno
5297303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) {
5298330158Seadler				resp3 = (void *)pkt->data;
5299303628Ssbruno				sc->sc_uc.uc_error_event_table
5300303628Ssbruno				    = le32toh(resp3->error_event_table_ptr);
5301303628Ssbruno				sc->sc_uc.uc_log_event_table
5302303628Ssbruno				    = le32toh(resp3->log_event_table_ptr);
5303303628Ssbruno				sc->sched_base = le32toh(resp3->scd_base_ptr);
5304303628Ssbruno				sc->sc_uc.uc_umac_error_event_table
5305303628Ssbruno				    = le32toh(resp3->error_info_addr);
5306303628Ssbruno				if (resp3->status == IWM_ALIVE_STATUS_OK)
5307303628Ssbruno					sc->sc_uc.uc_ok = 1;
5308303628Ssbruno				else
5309303628Ssbruno					sc->sc_uc.uc_ok = 0;
5310303628Ssbruno			}
5311303628Ssbruno
5312286441Srpaulo			sc->sc_uc.uc_intr = 1;
5313286441Srpaulo			wakeup(&sc->sc_uc);
5314286441Srpaulo			break; }
5315286441Srpaulo
5316286441Srpaulo		case IWM_CALIB_RES_NOTIF_PHY_DB: {
5317286441Srpaulo			struct iwm_calib_res_notif_phy_db *phy_db_notif;
5318330158Seadler			phy_db_notif = (void *)pkt->data;
5319286441Srpaulo
5320330163Seadler			iwm_phy_db_set_section(sc->sc_phy_db, phy_db_notif);
5321286441Srpaulo
5322286441Srpaulo			break; }
5323286441Srpaulo
5324286441Srpaulo		case IWM_STATISTICS_NOTIFICATION: {
5325286441Srpaulo			struct iwm_notif_statistics *stats;
5326330158Seadler			stats = (void *)pkt->data;
5327286441Srpaulo			memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
5328330144Seadler			sc->sc_noise = iwm_get_noise(sc, &stats->rx.general);
5329286441Srpaulo			break; }
5330286441Srpaulo
5331286441Srpaulo		case IWM_NVM_ACCESS_CMD:
5332303628Ssbruno		case IWM_MCC_UPDATE_CMD:
5333286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
5334286441Srpaulo				memcpy(sc->sc_cmd_resp,
5335286441Srpaulo				    pkt, sizeof(sc->sc_cmd_resp));
5336286441Srpaulo			}
5337286441Srpaulo			break;
5338286441Srpaulo
5339303628Ssbruno		case IWM_MCC_CHUB_UPDATE_CMD: {
5340303628Ssbruno			struct iwm_mcc_chub_notif *notif;
5341330158Seadler			notif = (void *)pkt->data;
5342303628Ssbruno
5343303628Ssbruno			sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8;
5344303628Ssbruno			sc->sc_fw_mcc[1] = notif->mcc & 0xff;
5345303628Ssbruno			sc->sc_fw_mcc[2] = '\0';
5346303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
5347303628Ssbruno			    "fw source %d sent CC '%s'\n",
5348303628Ssbruno			    notif->source_id, sc->sc_fw_mcc);
5349303628Ssbruno			break; }
5350303628Ssbruno
5351303628Ssbruno		case IWM_DTS_MEASUREMENT_NOTIFICATION:
5352303628Ssbruno			break;
5353303628Ssbruno
5354286441Srpaulo		case IWM_PHY_CONFIGURATION_CMD:
5355286441Srpaulo		case IWM_TX_ANT_CONFIGURATION_CMD:
5356286441Srpaulo		case IWM_ADD_STA:
5357286441Srpaulo		case IWM_MAC_CONTEXT_CMD:
5358286441Srpaulo		case IWM_REPLY_SF_CFG_CMD:
5359286441Srpaulo		case IWM_POWER_TABLE_CMD:
5360286441Srpaulo		case IWM_PHY_CONTEXT_CMD:
5361286441Srpaulo		case IWM_BINDING_CONTEXT_CMD:
5362286441Srpaulo		case IWM_TIME_EVENT_CMD:
5363303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD):
5364303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC):
5365303628Ssbruno		case IWM_SCAN_OFFLOAD_REQUEST_CMD:
5366286441Srpaulo		case IWM_REPLY_BEACON_FILTERING_CMD:
5367286441Srpaulo		case IWM_MAC_PM_POWER_TABLE:
5368286441Srpaulo		case IWM_TIME_QUOTA_CMD:
5369286441Srpaulo		case IWM_REMOVE_STA:
5370286441Srpaulo		case IWM_TXPATH_FLUSH:
5371286441Srpaulo		case IWM_LQ_CMD:
5372303628Ssbruno		case IWM_BT_CONFIG:
5373303628Ssbruno		case IWM_REPLY_THERMAL_MNG_BACKOFF:
5374330158Seadler			cresp = (void *)pkt->data;
5375286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
5376286441Srpaulo				memcpy(sc->sc_cmd_resp,
5377286441Srpaulo				    pkt, sizeof(*pkt)+sizeof(*cresp));
5378286441Srpaulo			}
5379286441Srpaulo			break;
5380286441Srpaulo
5381286441Srpaulo		/* ignore */
5382286441Srpaulo		case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */
5383286441Srpaulo			break;
5384286441Srpaulo
5385286441Srpaulo		case IWM_INIT_COMPLETE_NOTIF:
5386286441Srpaulo			sc->sc_init_complete = 1;
5387286441Srpaulo			wakeup(&sc->sc_init_complete);
5388286441Srpaulo			break;
5389286441Srpaulo
5390303628Ssbruno		case IWM_SCAN_OFFLOAD_COMPLETE: {
5391303628Ssbruno			struct iwm_periodic_scan_complete *notif;
5392330158Seadler			notif = (void *)pkt->data;
5393303628Ssbruno			break;
5394303628Ssbruno		}
5395303628Ssbruno
5396303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE: {
5397303628Ssbruno			struct iwm_lmac_scan_complete_notif *notif;
5398330158Seadler			notif = (void *)pkt->data;
5399303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5400303628Ssbruno 			break;
5401303628Ssbruno		}
5402303628Ssbruno
5403303628Ssbruno		case IWM_SCAN_COMPLETE_UMAC: {
5404303628Ssbruno			struct iwm_umac_scan_complete *notif;
5405330158Seadler			notif = (void *)pkt->data;
5406303628Ssbruno
5407303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
5408303628Ssbruno			    "UMAC scan complete, status=0x%x\n",
5409303628Ssbruno			    notif->status);
5410303628Ssbruno#if 0	/* XXX This would be a duplicate scan end call */
5411286441Srpaulo			taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task);
5412303628Ssbruno#endif
5413303628Ssbruno			break;
5414303628Ssbruno		}
5415286441Srpaulo
5416303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE_UMAC: {
5417303628Ssbruno			struct iwm_umac_scan_iter_complete_notif *notif;
5418330158Seadler			notif = (void *)pkt->data;
5419303628Ssbruno
5420303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration "
5421303628Ssbruno			    "complete, status=0x%x, %d channels scanned\n",
5422303628Ssbruno			    notif->status, notif->scanned_channels);
5423303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5424303628Ssbruno			break;
5425303628Ssbruno		}
5426303628Ssbruno
5427286441Srpaulo		case IWM_REPLY_ERROR: {
5428286441Srpaulo			struct iwm_error_resp *resp;
5429330158Seadler			resp = (void *)pkt->data;
5430286441Srpaulo
5431286441Srpaulo			device_printf(sc->sc_dev,
5432286441Srpaulo			    "firmware error 0x%x, cmd 0x%x\n",
5433286441Srpaulo			    le32toh(resp->error_type),
5434286441Srpaulo			    resp->cmd_id);
5435303628Ssbruno			break;
5436303628Ssbruno		}
5437286441Srpaulo
5438286441Srpaulo		case IWM_TIME_EVENT_NOTIFICATION: {
5439286441Srpaulo			struct iwm_time_event_notif *notif;
5440330158Seadler			notif = (void *)pkt->data;
5441286441Srpaulo
5442286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5443300833Sadrian			    "TE notif status = 0x%x action = 0x%x\n",
5444303628Ssbruno			    notif->status, notif->action);
5445303628Ssbruno			break;
5446303628Ssbruno		}
5447286441Srpaulo
5448286441Srpaulo		case IWM_MCAST_FILTER_CMD:
5449286441Srpaulo			break;
5450286441Srpaulo
5451303628Ssbruno		case IWM_SCD_QUEUE_CFG: {
5452303628Ssbruno			struct iwm_scd_txq_cfg_rsp *rsp;
5453330158Seadler			rsp = (void *)pkt->data;
5454303628Ssbruno
5455303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_CMD,
5456303628Ssbruno			    "queue cfg token=0x%x sta_id=%d "
5457303628Ssbruno			    "tid=%d scd_queue=%d\n",
5458303628Ssbruno			    rsp->token, rsp->sta_id, rsp->tid,
5459303628Ssbruno			    rsp->scd_queue);
5460303628Ssbruno			break;
5461303628Ssbruno		}
5462303628Ssbruno
5463286441Srpaulo		default:
5464286441Srpaulo			device_printf(sc->sc_dev,
5465286441Srpaulo			    "frame %d/%d %x UNHANDLED (this should "
5466286441Srpaulo			    "not happen)\n", qid, idx,
5467286441Srpaulo			    pkt->len_n_flags);
5468286441Srpaulo			break;
5469286441Srpaulo		}
5470286441Srpaulo
5471286441Srpaulo		/*
5472286441Srpaulo		 * Why test bit 0x80?  The Linux driver:
5473286441Srpaulo		 *
5474286441Srpaulo		 * There is one exception:  uCode sets bit 15 when it
5475286441Srpaulo		 * originates the response/notification, i.e. when the
5476286441Srpaulo		 * response/notification is not a direct response to a
5477286441Srpaulo		 * command sent by the driver.  For example, uCode issues
5478286441Srpaulo		 * IWM_REPLY_RX when it sends a received frame to the driver;
5479286441Srpaulo		 * it is not a direct response to any driver command.
5480286441Srpaulo		 *
5481286441Srpaulo		 * Ok, so since when is 7 == 15?  Well, the Linux driver
5482286441Srpaulo		 * uses a slightly different format for pkt->hdr, and "qid"
5483286441Srpaulo		 * is actually the upper byte of a two-byte field.
5484286441Srpaulo		 */
5485286441Srpaulo		if (!(pkt->hdr.qid & (1 << 7))) {
5486286441Srpaulo			iwm_cmd_done(sc, pkt);
5487286441Srpaulo		}
5488286441Srpaulo
5489286441Srpaulo		ADVANCE_RXQ(sc);
5490286441Srpaulo	}
5491286441Srpaulo
5492286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
5493286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
5494286441Srpaulo
5495286441Srpaulo	/*
5496286441Srpaulo	 * Tell the firmware what we have processed.
5497286441Srpaulo	 * Seems like the hardware gets upset unless we align
5498286441Srpaulo	 * the write by 8??
5499286441Srpaulo	 */
5500286441Srpaulo	hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1;
5501286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7);
5502286441Srpaulo}
5503286441Srpaulo
5504286441Srpaulostatic void
5505286441Srpauloiwm_intr(void *arg)
5506286441Srpaulo{
5507286441Srpaulo	struct iwm_softc *sc = arg;
5508286441Srpaulo	int handled = 0;
5509286441Srpaulo	int r1, r2, rv = 0;
5510286441Srpaulo	int isperiodic = 0;
5511286441Srpaulo
5512286441Srpaulo	IWM_LOCK(sc);
5513286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
5514286441Srpaulo
5515286441Srpaulo	if (sc->sc_flags & IWM_FLAG_USE_ICT) {
5516286441Srpaulo		uint32_t *ict = sc->ict_dma.vaddr;
5517286441Srpaulo		int tmp;
5518286441Srpaulo
5519286441Srpaulo		tmp = htole32(ict[sc->ict_cur]);
5520286441Srpaulo		if (!tmp)
5521286441Srpaulo			goto out_ena;
5522286441Srpaulo
5523286441Srpaulo		/*
5524286441Srpaulo		 * ok, there was something.  keep plowing until we have all.
5525286441Srpaulo		 */
5526286441Srpaulo		r1 = r2 = 0;
5527286441Srpaulo		while (tmp) {
5528286441Srpaulo			r1 |= tmp;
5529286441Srpaulo			ict[sc->ict_cur] = 0;
5530286441Srpaulo			sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT;
5531286441Srpaulo			tmp = htole32(ict[sc->ict_cur]);
5532286441Srpaulo		}
5533286441Srpaulo
5534286441Srpaulo		/* this is where the fun begins.  don't ask */
5535286441Srpaulo		if (r1 == 0xffffffff)
5536286441Srpaulo			r1 = 0;
5537286441Srpaulo
5538286441Srpaulo		/* i am not expected to understand this */
5539286441Srpaulo		if (r1 & 0xc0000)
5540286441Srpaulo			r1 |= 0x8000;
5541286441Srpaulo		r1 = (0xff & r1) | ((0xff00 & r1) << 16);
5542286441Srpaulo	} else {
5543286441Srpaulo		r1 = IWM_READ(sc, IWM_CSR_INT);
5544286441Srpaulo		/* "hardware gone" (where, fishing?) */
5545286441Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
5546286441Srpaulo			goto out;
5547286441Srpaulo		r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS);
5548286441Srpaulo	}
5549286441Srpaulo	if (r1 == 0 && r2 == 0) {
5550286441Srpaulo		goto out_ena;
5551286441Srpaulo	}
5552286441Srpaulo
5553286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask);
5554286441Srpaulo
5555286441Srpaulo	/* ignored */
5556286441Srpaulo	handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/));
5557286441Srpaulo
5558286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_SW_ERR) {
5559286441Srpaulo		int i;
5560287197Sglebius		struct ieee80211com *ic = &sc->sc_ic;
5561286441Srpaulo		struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5562286441Srpaulo
5563298793Sdchagin#ifdef IWM_DEBUG
5564286441Srpaulo		iwm_nic_error(sc);
5565298793Sdchagin#endif
5566286441Srpaulo		/* Dump driver status (TX and RX rings) while we're here. */
5567286441Srpaulo		device_printf(sc->sc_dev, "driver status:\n");
5568286441Srpaulo		for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) {
5569286441Srpaulo			struct iwm_tx_ring *ring = &sc->txq[i];
5570286441Srpaulo			device_printf(sc->sc_dev,
5571286441Srpaulo			    "  tx ring %2d: qid=%-2d cur=%-3d "
5572286441Srpaulo			    "queued=%-3d\n",
5573286441Srpaulo			    i, ring->qid, ring->cur, ring->queued);
5574286441Srpaulo		}
5575286441Srpaulo		device_printf(sc->sc_dev,
5576286441Srpaulo		    "  rx ring: cur=%d\n", sc->rxq.cur);
5577286441Srpaulo		device_printf(sc->sc_dev,
5578298659Scem		    "  802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state);
5579286441Srpaulo
5580298594Sadrian		/* Don't stop the device; just do a VAP restart */
5581298594Sadrian		IWM_UNLOCK(sc);
5582286441Srpaulo
5583298594Sadrian		if (vap == NULL) {
5584298594Sadrian			printf("%s: null vap\n", __func__);
5585298594Sadrian			return;
5586298594Sadrian		}
5587298594Sadrian
5588298594Sadrian		device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; "
5589298594Sadrian		    "restarting\n", __func__, vap->iv_state);
5590298594Sadrian
5591298594Sadrian		/* XXX TODO: turn this into a callout/taskqueue */
5592298594Sadrian		ieee80211_restart_all(ic);
5593298594Sadrian		return;
5594286441Srpaulo	}
5595286441Srpaulo
5596286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_HW_ERR) {
5597286441Srpaulo		handled |= IWM_CSR_INT_BIT_HW_ERR;
5598286441Srpaulo		device_printf(sc->sc_dev, "hardware error, stopping device\n");
5599287197Sglebius		iwm_stop(sc);
5600286441Srpaulo		rv = 1;
5601286441Srpaulo		goto out;
5602286441Srpaulo	}
5603286441Srpaulo
5604286441Srpaulo	/* firmware chunk loaded */
5605286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_FH_TX) {
5606286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK);
5607286441Srpaulo		handled |= IWM_CSR_INT_BIT_FH_TX;
5608286441Srpaulo		sc->sc_fw_chunk_done = 1;
5609286441Srpaulo		wakeup(&sc->sc_fw);
5610286441Srpaulo	}
5611286441Srpaulo
5612286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RF_KILL) {
5613286441Srpaulo		handled |= IWM_CSR_INT_BIT_RF_KILL;
5614287197Sglebius		if (iwm_check_rfkill(sc)) {
5615286441Srpaulo			device_printf(sc->sc_dev,
5616286441Srpaulo			    "%s: rfkill switch, disabling interface\n",
5617286441Srpaulo			    __func__);
5618287197Sglebius			iwm_stop(sc);
5619286441Srpaulo		}
5620286441Srpaulo	}
5621286441Srpaulo
5622286441Srpaulo	/*
5623286441Srpaulo	 * The Linux driver uses periodic interrupts to avoid races.
5624286441Srpaulo	 * We cargo-cult like it's going out of fashion.
5625286441Srpaulo	 */
5626286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
5627286441Srpaulo		handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
5628286441Srpaulo		IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
5629286441Srpaulo		if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0)
5630286441Srpaulo			IWM_WRITE_1(sc,
5631286441Srpaulo			    IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
5632286441Srpaulo		isperiodic = 1;
5633286441Srpaulo	}
5634286441Srpaulo
5635286441Srpaulo	if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) {
5636286441Srpaulo		handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
5637286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
5638286441Srpaulo
5639286441Srpaulo		iwm_notif_intr(sc);
5640286441Srpaulo
5641286441Srpaulo		/* enable periodic interrupt, see above */
5642286441Srpaulo		if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic)
5643286441Srpaulo			IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG,
5644286441Srpaulo			    IWM_CSR_INT_PERIODIC_ENA);
5645286441Srpaulo	}
5646286441Srpaulo
5647286441Srpaulo	if (__predict_false(r1 & ~handled))
5648286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5649286441Srpaulo		    "%s: unhandled interrupts: %x\n", __func__, r1);
5650286441Srpaulo	rv = 1;
5651286441Srpaulo
5652286441Srpaulo out_ena:
5653286441Srpaulo	iwm_restore_interrupts(sc);
5654286441Srpaulo out:
5655286441Srpaulo	IWM_UNLOCK(sc);
5656286441Srpaulo	return;
5657286441Srpaulo}
5658286441Srpaulo
5659286441Srpaulo/*
5660286441Srpaulo * Autoconf glue-sniffing
5661286441Srpaulo */
5662286441Srpaulo#define	PCI_VENDOR_INTEL		0x8086
5663286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_1	0x08b3
5664286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_2	0x08b4
5665303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_1	0x3165
5666303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_2	0x3166
5667286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_1	0x08b1
5668286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_2	0x08b2
5669286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_1	0x095a
5670286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_2	0x095b
5671303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_1	0x24f3
5672303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_2	0x24f4
5673286441Srpaulo
5674286441Srpaulostatic const struct iwm_devices {
5675286441Srpaulo	uint16_t	device;
5676286441Srpaulo	const char	*name;
5677286441Srpaulo} iwm_devices[] = {
5678286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" },
5679286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" },
5680303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" },
5681303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" },
5682286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" },
5683286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" },
5684286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" },
5685286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" },
5686303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" },
5687303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" },
5688286441Srpaulo};
5689286441Srpaulo
5690286441Srpaulostatic int
5691286441Srpauloiwm_probe(device_t dev)
5692286441Srpaulo{
5693286441Srpaulo	int i;
5694286441Srpaulo
5695303628Ssbruno	for (i = 0; i < nitems(iwm_devices); i++) {
5696286441Srpaulo		if (pci_get_vendor(dev) == PCI_VENDOR_INTEL &&
5697286441Srpaulo		    pci_get_device(dev) == iwm_devices[i].device) {
5698286441Srpaulo			device_set_desc(dev, iwm_devices[i].name);
5699286441Srpaulo			return (BUS_PROBE_DEFAULT);
5700286441Srpaulo		}
5701303628Ssbruno	}
5702286441Srpaulo
5703286441Srpaulo	return (ENXIO);
5704286441Srpaulo}
5705286441Srpaulo
5706286441Srpaulostatic int
5707286441Srpauloiwm_dev_check(device_t dev)
5708286441Srpaulo{
5709286441Srpaulo	struct iwm_softc *sc;
5710286441Srpaulo
5711286441Srpaulo	sc = device_get_softc(dev);
5712286441Srpaulo
5713286441Srpaulo	switch (pci_get_device(dev)) {
5714286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_1:
5715286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_2:
5716330166Seadler		sc->cfg = &iwm3160_cfg;
5717303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5718286441Srpaulo		return (0);
5719303628Ssbruno	case PCI_PRODUCT_INTEL_WL_3165_1:
5720303628Ssbruno	case PCI_PRODUCT_INTEL_WL_3165_2:
5721330166Seadler		sc->cfg = &iwm3165_cfg;
5722303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5723303628Ssbruno		return (0);
5724286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_1:
5725286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_2:
5726330166Seadler		sc->cfg = &iwm7260_cfg;
5727303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5728286441Srpaulo		return (0);
5729286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_1:
5730286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_2:
5731330166Seadler		sc->cfg = &iwm7265_cfg;
5732303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5733286441Srpaulo		return (0);
5734303628Ssbruno	case PCI_PRODUCT_INTEL_WL_8260_1:
5735303628Ssbruno	case PCI_PRODUCT_INTEL_WL_8260_2:
5736330166Seadler		sc->cfg = &iwm8260_cfg;
5737303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000;
5738303628Ssbruno		return (0);
5739286441Srpaulo	default:
5740286441Srpaulo		device_printf(dev, "unknown adapter type\n");
5741286441Srpaulo		return ENXIO;
5742286441Srpaulo	}
5743286441Srpaulo}
5744286441Srpaulo
5745330140Seadler/* PCI registers */
5746330140Seadler#define PCI_CFG_RETRY_TIMEOUT	0x041
5747330140Seadler
5748286441Srpaulostatic int
5749286441Srpauloiwm_pci_attach(device_t dev)
5750286441Srpaulo{
5751286441Srpaulo	struct iwm_softc *sc;
5752286441Srpaulo	int count, error, rid;
5753286441Srpaulo	uint16_t reg;
5754286441Srpaulo
5755286441Srpaulo	sc = device_get_softc(dev);
5756286441Srpaulo
5757330140Seadler	/* We disable the RETRY_TIMEOUT register (0x41) to keep
5758330140Seadler	 * PCI Tx retries from interfering with C3 CPU state */
5759330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
5760286441Srpaulo
5761286441Srpaulo	/* Enable bus-mastering and hardware bug workaround. */
5762286441Srpaulo	pci_enable_busmaster(dev);
5763286441Srpaulo	reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg));
5764286441Srpaulo	/* if !MSI */
5765286441Srpaulo	if (reg & PCIM_STATUS_INTxSTATE) {
5766286441Srpaulo		reg &= ~PCIM_STATUS_INTxSTATE;
5767286441Srpaulo	}
5768286441Srpaulo	pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg));
5769286441Srpaulo
5770286441Srpaulo	rid = PCIR_BAR(0);
5771286441Srpaulo	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
5772286441Srpaulo	    RF_ACTIVE);
5773286441Srpaulo	if (sc->sc_mem == NULL) {
5774286441Srpaulo		device_printf(sc->sc_dev, "can't map mem space\n");
5775286441Srpaulo		return (ENXIO);
5776286441Srpaulo	}
5777286441Srpaulo	sc->sc_st = rman_get_bustag(sc->sc_mem);
5778286441Srpaulo	sc->sc_sh = rman_get_bushandle(sc->sc_mem);
5779286441Srpaulo
5780286441Srpaulo	/* Install interrupt handler. */
5781286441Srpaulo	count = 1;
5782286441Srpaulo	rid = 0;
5783286441Srpaulo	if (pci_alloc_msi(dev, &count) == 0)
5784286441Srpaulo		rid = 1;
5785286441Srpaulo	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
5786286441Srpaulo	    (rid != 0 ? 0 : RF_SHAREABLE));
5787286441Srpaulo	if (sc->sc_irq == NULL) {
5788286441Srpaulo		device_printf(dev, "can't map interrupt\n");
5789286441Srpaulo			return (ENXIO);
5790286441Srpaulo	}
5791286441Srpaulo	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
5792286441Srpaulo	    NULL, iwm_intr, sc, &sc->sc_ih);
5793286441Srpaulo	if (sc->sc_ih == NULL) {
5794286441Srpaulo		device_printf(dev, "can't establish interrupt");
5795286441Srpaulo			return (ENXIO);
5796286441Srpaulo	}
5797286441Srpaulo	sc->sc_dmat = bus_get_dma_tag(sc->sc_dev);
5798286441Srpaulo
5799286441Srpaulo	return (0);
5800286441Srpaulo}
5801286441Srpaulo
5802286441Srpaulostatic void
5803286441Srpauloiwm_pci_detach(device_t dev)
5804286441Srpaulo{
5805286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
5806286441Srpaulo
5807286441Srpaulo	if (sc->sc_irq != NULL) {
5808286441Srpaulo		bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
5809286441Srpaulo		bus_release_resource(dev, SYS_RES_IRQ,
5810286441Srpaulo		    rman_get_rid(sc->sc_irq), sc->sc_irq);
5811286441Srpaulo		pci_release_msi(dev);
5812286441Srpaulo        }
5813286441Srpaulo	if (sc->sc_mem != NULL)
5814286441Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY,
5815286441Srpaulo		    rman_get_rid(sc->sc_mem), sc->sc_mem);
5816286441Srpaulo}
5817286441Srpaulo
5818286441Srpaulo
5819286441Srpaulo
5820286441Srpaulostatic int
5821286441Srpauloiwm_attach(device_t dev)
5822286441Srpaulo{
5823287197Sglebius	struct iwm_softc *sc = device_get_softc(dev);
5824287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
5825286441Srpaulo	int error;
5826286441Srpaulo	int txq_i, i;
5827286441Srpaulo
5828286441Srpaulo	sc->sc_dev = dev;
5829330164Seadler	sc->sc_attached = 1;
5830293099Savos	IWM_LOCK_INIT(sc);
5831287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
5832286441Srpaulo	callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0);
5833301187Sadrian	callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0);
5834286441Srpaulo	TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc);
5835286441Srpaulo
5836330163Seadler	/* Init phy db */
5837330163Seadler	sc->sc_phy_db = iwm_phy_db_init(sc);
5838330163Seadler	if (!sc->sc_phy_db) {
5839330163Seadler		device_printf(dev, "Cannot init phy_db\n");
5840330163Seadler		goto fail;
5841330163Seadler	}
5842330163Seadler
5843286441Srpaulo	/* PCI attach */
5844286441Srpaulo	error = iwm_pci_attach(dev);
5845286441Srpaulo	if (error != 0)
5846286441Srpaulo		goto fail;
5847286441Srpaulo
5848286441Srpaulo	sc->sc_wantresp = -1;
5849286441Srpaulo
5850286441Srpaulo	/* Check device type */
5851286441Srpaulo	error = iwm_dev_check(dev);
5852286441Srpaulo	if (error != 0)
5853286441Srpaulo		goto fail;
5854286441Srpaulo
5855330166Seadler	sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV);
5856286441Srpaulo	/*
5857303628Ssbruno	 * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
5858303628Ssbruno	 * changed, and now the revision step also includes bit 0-1 (no more
5859303628Ssbruno	 * "dash" value). To keep hw_rev backwards compatible - we'll store it
5860303628Ssbruno	 * in the old format.
5861303628Ssbruno	 */
5862330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
5863303628Ssbruno		sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) |
5864303628Ssbruno				(IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2);
5865303628Ssbruno
5866286441Srpaulo	if (iwm_prepare_card_hw(sc) != 0) {
5867286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
5868286441Srpaulo		goto fail;
5869286441Srpaulo	}
5870286441Srpaulo
5871330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
5872303628Ssbruno		int ret;
5873303628Ssbruno		uint32_t hw_step;
5874303628Ssbruno
5875303628Ssbruno		/*
5876303628Ssbruno		 * In order to recognize C step the driver should read the
5877303628Ssbruno		 * chip version id located at the AUX bus MISC address.
5878303628Ssbruno		 */
5879303628Ssbruno		IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
5880303628Ssbruno			    IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
5881303628Ssbruno		DELAY(2);
5882303628Ssbruno
5883303628Ssbruno		ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL,
5884303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
5885303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
5886303628Ssbruno				   25000);
5887330149Seadler		if (!ret) {
5888303628Ssbruno			device_printf(sc->sc_dev,
5889303628Ssbruno			    "Failed to wake up the nic\n");
5890303628Ssbruno			goto fail;
5891303628Ssbruno		}
5892303628Ssbruno
5893303628Ssbruno		if (iwm_nic_lock(sc)) {
5894303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG);
5895303628Ssbruno			hw_step |= IWM_ENABLE_WFPM;
5896303628Ssbruno			iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step);
5897303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG);
5898303628Ssbruno			hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF;
5899303628Ssbruno			if (hw_step == 0x3)
5900303628Ssbruno				sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) |
5901303628Ssbruno						(IWM_SILICON_C_STEP << 2);
5902303628Ssbruno			iwm_nic_unlock(sc);
5903303628Ssbruno		} else {
5904303628Ssbruno			device_printf(sc->sc_dev, "Failed to lock the nic\n");
5905303628Ssbruno			goto fail;
5906303628Ssbruno		}
5907303628Ssbruno	}
5908303628Ssbruno
5909330166Seadler	/* special-case 7265D, it has the same PCI IDs. */
5910330166Seadler	if (sc->cfg == &iwm7265_cfg &&
5911330166Seadler	    (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == IWM_CSR_HW_REV_TYPE_7265D) {
5912330166Seadler		sc->cfg = &iwm7265d_cfg;
5913330166Seadler	}
5914330166Seadler
5915286441Srpaulo	/* Allocate DMA memory for firmware transfers. */
5916286441Srpaulo	if ((error = iwm_alloc_fwmem(sc)) != 0) {
5917286441Srpaulo		device_printf(dev, "could not allocate memory for firmware\n");
5918286441Srpaulo		goto fail;
5919286441Srpaulo	}
5920286441Srpaulo
5921286441Srpaulo	/* Allocate "Keep Warm" page. */
5922286441Srpaulo	if ((error = iwm_alloc_kw(sc)) != 0) {
5923286441Srpaulo		device_printf(dev, "could not allocate keep warm page\n");
5924286441Srpaulo		goto fail;
5925286441Srpaulo	}
5926286441Srpaulo
5927286441Srpaulo	/* We use ICT interrupts */
5928286441Srpaulo	if ((error = iwm_alloc_ict(sc)) != 0) {
5929286441Srpaulo		device_printf(dev, "could not allocate ICT table\n");
5930286441Srpaulo		goto fail;
5931286441Srpaulo	}
5932286441Srpaulo
5933286441Srpaulo	/* Allocate TX scheduler "rings". */
5934286441Srpaulo	if ((error = iwm_alloc_sched(sc)) != 0) {
5935286441Srpaulo		device_printf(dev, "could not allocate TX scheduler rings\n");
5936286441Srpaulo		goto fail;
5937286441Srpaulo	}
5938286441Srpaulo
5939286441Srpaulo	/* Allocate TX rings */
5940286441Srpaulo	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) {
5941286441Srpaulo		if ((error = iwm_alloc_tx_ring(sc,
5942286441Srpaulo		    &sc->txq[txq_i], txq_i)) != 0) {
5943286441Srpaulo			device_printf(dev,
5944286441Srpaulo			    "could not allocate TX ring %d\n",
5945286441Srpaulo			    txq_i);
5946286441Srpaulo			goto fail;
5947286441Srpaulo		}
5948286441Srpaulo	}
5949286441Srpaulo
5950286441Srpaulo	/* Allocate RX ring. */
5951286441Srpaulo	if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) {
5952286441Srpaulo		device_printf(dev, "could not allocate RX ring\n");
5953286441Srpaulo		goto fail;
5954286441Srpaulo	}
5955286441Srpaulo
5956286441Srpaulo	/* Clear pending interrupts. */
5957286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff);
5958286441Srpaulo
5959286441Srpaulo	ic->ic_softc = sc;
5960286441Srpaulo	ic->ic_name = device_get_nameunit(sc->sc_dev);
5961286441Srpaulo	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
5962286441Srpaulo	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
5963286441Srpaulo
5964286441Srpaulo	/* Set device capabilities. */
5965286441Srpaulo	ic->ic_caps =
5966286441Srpaulo	    IEEE80211_C_STA |
5967286441Srpaulo	    IEEE80211_C_WPA |		/* WPA/RSN */
5968286441Srpaulo	    IEEE80211_C_WME |
5969286441Srpaulo	    IEEE80211_C_SHSLOT |	/* short slot time supported */
5970286441Srpaulo	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
5971286441Srpaulo//	    IEEE80211_C_BGSCAN		/* capable of bg scanning */
5972286441Srpaulo	    ;
5973286441Srpaulo	for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
5974286441Srpaulo		sc->sc_phyctxt[i].id = i;
5975286441Srpaulo		sc->sc_phyctxt[i].color = 0;
5976286441Srpaulo		sc->sc_phyctxt[i].ref = 0;
5977286441Srpaulo		sc->sc_phyctxt[i].channel = NULL;
5978286441Srpaulo	}
5979286441Srpaulo
5980330144Seadler	/* Default noise floor */
5981330144Seadler	sc->sc_noise = -96;
5982330144Seadler
5983286441Srpaulo	/* Max RSSI */
5984286441Srpaulo	sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM;
5985330144Seadler
5986286441Srpaulo	sc->sc_preinit_hook.ich_func = iwm_preinit;
5987286441Srpaulo	sc->sc_preinit_hook.ich_arg = sc;
5988286441Srpaulo	if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
5989286441Srpaulo		device_printf(dev, "config_intrhook_establish failed\n");
5990286441Srpaulo		goto fail;
5991286441Srpaulo	}
5992286441Srpaulo
5993286441Srpaulo#ifdef IWM_DEBUG
5994286441Srpaulo	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
5995286441Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug",
5996286441Srpaulo	    CTLFLAG_RW, &sc->sc_debug, 0, "control debugging");
5997286441Srpaulo#endif
5998286441Srpaulo
5999286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6000286441Srpaulo	    "<-%s\n", __func__);
6001286441Srpaulo
6002286441Srpaulo	return 0;
6003286441Srpaulo
6004286441Srpaulo	/* Free allocated memory if something failed during attachment. */
6005286441Srpaulofail:
6006286441Srpaulo	iwm_detach_local(sc, 0);
6007286441Srpaulo
6008286441Srpaulo	return ENXIO;
6009286441Srpaulo}
6010286441Srpaulo
6011286441Srpaulostatic int
6012303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr)
6013303628Ssbruno{
6014303628Ssbruno	char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
6015303628Ssbruno
6016303628Ssbruno	if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr))
6017303628Ssbruno		return (FALSE);
6018303628Ssbruno
6019303628Ssbruno	return (TRUE);
6020303628Ssbruno}
6021303628Ssbruno
6022303628Ssbrunostatic int
6023286441Srpauloiwm_update_edca(struct ieee80211com *ic)
6024286441Srpaulo{
6025286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
6026286441Srpaulo
6027286441Srpaulo	device_printf(sc->sc_dev, "%s: called\n", __func__);
6028286441Srpaulo	return (0);
6029286441Srpaulo}
6030286441Srpaulo
6031286441Srpaulostatic void
6032286441Srpauloiwm_preinit(void *arg)
6033286441Srpaulo{
6034286441Srpaulo	struct iwm_softc *sc = arg;
6035286441Srpaulo	device_t dev = sc->sc_dev;
6036287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
6037286441Srpaulo	int error;
6038286441Srpaulo
6039286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6040286441Srpaulo	    "->%s\n", __func__);
6041286441Srpaulo
6042286441Srpaulo	IWM_LOCK(sc);
6043286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
6044286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
6045286441Srpaulo		IWM_UNLOCK(sc);
6046286441Srpaulo		goto fail;
6047286441Srpaulo	}
6048286441Srpaulo
6049286441Srpaulo	error = iwm_run_init_mvm_ucode(sc, 1);
6050286441Srpaulo	iwm_stop_device(sc);
6051286441Srpaulo	if (error) {
6052286441Srpaulo		IWM_UNLOCK(sc);
6053286441Srpaulo		goto fail;
6054286441Srpaulo	}
6055286441Srpaulo	device_printf(dev,
6056303628Ssbruno	    "hw rev 0x%x, fw ver %s, address %s\n",
6057286441Srpaulo	    sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK,
6058330165Seadler	    sc->sc_fwver, ether_sprintf(sc->nvm_data->hw_addr));
6059286441Srpaulo
6060286441Srpaulo	/* not all hardware can do 5GHz band */
6061330165Seadler	if (!sc->nvm_data->sku_cap_band_52GHz_enable)
6062286441Srpaulo		memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
6063286441Srpaulo		    sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A]));
6064286441Srpaulo	IWM_UNLOCK(sc);
6065286441Srpaulo
6066298877Savos	iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
6067298877Savos	    ic->ic_channels);
6068298877Savos
6069286441Srpaulo	/*
6070286441Srpaulo	 * At this point we've committed - if we fail to do setup,
6071286441Srpaulo	 * we now also have to tear down the net80211 state.
6072286441Srpaulo	 */
6073287197Sglebius	ieee80211_ifattach(ic);
6074286441Srpaulo	ic->ic_vap_create = iwm_vap_create;
6075286441Srpaulo	ic->ic_vap_delete = iwm_vap_delete;
6076286441Srpaulo	ic->ic_raw_xmit = iwm_raw_xmit;
6077286441Srpaulo	ic->ic_node_alloc = iwm_node_alloc;
6078286441Srpaulo	ic->ic_scan_start = iwm_scan_start;
6079286441Srpaulo	ic->ic_scan_end = iwm_scan_end;
6080286441Srpaulo	ic->ic_update_mcast = iwm_update_mcast;
6081298877Savos	ic->ic_getradiocaps = iwm_init_channel_map;
6082286441Srpaulo	ic->ic_set_channel = iwm_set_channel;
6083286441Srpaulo	ic->ic_scan_curchan = iwm_scan_curchan;
6084286441Srpaulo	ic->ic_scan_mindwell = iwm_scan_mindwell;
6085286441Srpaulo	ic->ic_wme.wme_update = iwm_update_edca;
6086287197Sglebius	ic->ic_parent = iwm_parent;
6087287197Sglebius	ic->ic_transmit = iwm_transmit;
6088286441Srpaulo	iwm_radiotap_attach(sc);
6089286441Srpaulo	if (bootverbose)
6090286441Srpaulo		ieee80211_announce(ic);
6091286441Srpaulo
6092286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6093286441Srpaulo	    "<-%s\n", __func__);
6094286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
6095286441Srpaulo
6096286441Srpaulo	return;
6097286441Srpaulofail:
6098286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
6099286441Srpaulo	iwm_detach_local(sc, 0);
6100286441Srpaulo}
6101286441Srpaulo
6102286441Srpaulo/*
6103286441Srpaulo * Attach the interface to 802.11 radiotap.
6104286441Srpaulo */
6105286441Srpaulostatic void
6106286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc)
6107286441Srpaulo{
6108287197Sglebius        struct ieee80211com *ic = &sc->sc_ic;
6109286441Srpaulo
6110286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6111286441Srpaulo	    "->%s begin\n", __func__);
6112286441Srpaulo        ieee80211_radiotap_attach(ic,
6113286441Srpaulo            &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
6114286441Srpaulo                IWM_TX_RADIOTAP_PRESENT,
6115286441Srpaulo            &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
6116286441Srpaulo                IWM_RX_RADIOTAP_PRESENT);
6117286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6118286441Srpaulo	    "->%s end\n", __func__);
6119286441Srpaulo}
6120286441Srpaulo
6121286441Srpaulostatic struct ieee80211vap *
6122286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
6123286441Srpaulo    enum ieee80211_opmode opmode, int flags,
6124286441Srpaulo    const uint8_t bssid[IEEE80211_ADDR_LEN],
6125286441Srpaulo    const uint8_t mac[IEEE80211_ADDR_LEN])
6126286441Srpaulo{
6127286441Srpaulo	struct iwm_vap *ivp;
6128286441Srpaulo	struct ieee80211vap *vap;
6129286441Srpaulo
6130286441Srpaulo	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
6131286441Srpaulo		return NULL;
6132287197Sglebius	ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO);
6133286441Srpaulo	vap = &ivp->iv_vap;
6134287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
6135286441Srpaulo	vap->iv_bmissthreshold = 10;            /* override default */
6136286441Srpaulo	/* Override with driver methods. */
6137286441Srpaulo	ivp->iv_newstate = vap->iv_newstate;
6138286441Srpaulo	vap->iv_newstate = iwm_newstate;
6139286441Srpaulo
6140286441Srpaulo	ieee80211_ratectl_init(vap);
6141286441Srpaulo	/* Complete setup. */
6142287197Sglebius	ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status,
6143287197Sglebius	    mac);
6144286441Srpaulo	ic->ic_opmode = opmode;
6145286441Srpaulo
6146286441Srpaulo	return vap;
6147286441Srpaulo}
6148286441Srpaulo
6149286441Srpaulostatic void
6150286441Srpauloiwm_vap_delete(struct ieee80211vap *vap)
6151286441Srpaulo{
6152286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
6153286441Srpaulo
6154286441Srpaulo	ieee80211_ratectl_deinit(vap);
6155286441Srpaulo	ieee80211_vap_detach(vap);
6156286441Srpaulo	free(ivp, M_80211_VAP);
6157286441Srpaulo}
6158286441Srpaulo
6159286441Srpaulostatic void
6160286441Srpauloiwm_scan_start(struct ieee80211com *ic)
6161286441Srpaulo{
6162286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6163303628Ssbruno	struct iwm_softc *sc = ic->ic_softc;
6164286441Srpaulo	int error;
6165286441Srpaulo
6166286441Srpaulo	IWM_LOCK(sc);
6167303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
6168303628Ssbruno		error = iwm_mvm_umac_scan(sc);
6169303628Ssbruno	else
6170303628Ssbruno		error = iwm_mvm_lmac_scan(sc);
6171303628Ssbruno	if (error != 0) {
6172301190Sadrian		device_printf(sc->sc_dev, "could not initiate 2 GHz scan\n");
6173286441Srpaulo		IWM_UNLOCK(sc);
6174286441Srpaulo		ieee80211_cancel_scan(vap);
6175301187Sadrian	} else {
6176301187Sadrian		iwm_led_blink_start(sc);
6177286441Srpaulo		IWM_UNLOCK(sc);
6178301187Sadrian	}
6179286441Srpaulo}
6180286441Srpaulo
6181286441Srpaulostatic void
6182286441Srpauloiwm_scan_end(struct ieee80211com *ic)
6183286441Srpaulo{
6184301187Sadrian	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6185301187Sadrian	struct iwm_softc *sc = ic->ic_softc;
6186301187Sadrian
6187301187Sadrian	IWM_LOCK(sc);
6188301187Sadrian	iwm_led_blink_stop(sc);
6189301187Sadrian	if (vap->iv_state == IEEE80211_S_RUN)
6190301187Sadrian		iwm_mvm_led_enable(sc);
6191301187Sadrian	IWM_UNLOCK(sc);
6192286441Srpaulo}
6193286441Srpaulo
6194286441Srpaulostatic void
6195286441Srpauloiwm_update_mcast(struct ieee80211com *ic)
6196286441Srpaulo{
6197286441Srpaulo}
6198286441Srpaulo
6199286441Srpaulostatic void
6200286441Srpauloiwm_set_channel(struct ieee80211com *ic)
6201286441Srpaulo{
6202286441Srpaulo}
6203286441Srpaulo
6204286441Srpaulostatic void
6205286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6206286441Srpaulo{
6207286441Srpaulo}
6208286441Srpaulo
6209286441Srpaulostatic void
6210286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss)
6211286441Srpaulo{
6212286441Srpaulo	return;
6213286441Srpaulo}
6214286441Srpaulo
6215286441Srpaulovoid
6216286441Srpauloiwm_init_task(void *arg1)
6217286441Srpaulo{
6218286441Srpaulo	struct iwm_softc *sc = arg1;
6219286441Srpaulo
6220286441Srpaulo	IWM_LOCK(sc);
6221286441Srpaulo	while (sc->sc_flags & IWM_FLAG_BUSY)
6222286441Srpaulo		msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0);
6223286441Srpaulo	sc->sc_flags |= IWM_FLAG_BUSY;
6224287197Sglebius	iwm_stop(sc);
6225287197Sglebius	if (sc->sc_ic.ic_nrunning > 0)
6226286441Srpaulo		iwm_init(sc);
6227286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_BUSY;
6228286441Srpaulo	wakeup(&sc->sc_flags);
6229286441Srpaulo	IWM_UNLOCK(sc);
6230286441Srpaulo}
6231286441Srpaulo
6232286441Srpaulostatic int
6233286441Srpauloiwm_resume(device_t dev)
6234286441Srpaulo{
6235298612Sadrian	struct iwm_softc *sc = device_get_softc(dev);
6236298612Sadrian	int do_reinit = 0;
6237286441Srpaulo
6238330140Seadler	/*
6239330140Seadler	 * We disable the RETRY_TIMEOUT register (0x41) to keep
6240330140Seadler	 * PCI Tx retries from interfering with C3 CPU state.
6241330140Seadler	 */
6242330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
6243286441Srpaulo	iwm_init_task(device_get_softc(dev));
6244286441Srpaulo
6245298612Sadrian	IWM_LOCK(sc);
6246303628Ssbruno	if (sc->sc_flags & IWM_FLAG_SCANNING) {
6247303628Ssbruno		sc->sc_flags &= ~IWM_FLAG_SCANNING;
6248298612Sadrian		do_reinit = 1;
6249298612Sadrian	}
6250298612Sadrian	IWM_UNLOCK(sc);
6251298612Sadrian
6252298612Sadrian	if (do_reinit)
6253298612Sadrian		ieee80211_resume_all(&sc->sc_ic);
6254298612Sadrian
6255286441Srpaulo	return 0;
6256286441Srpaulo}
6257286441Srpaulo
6258286441Srpaulostatic int
6259286441Srpauloiwm_suspend(device_t dev)
6260286441Srpaulo{
6261298612Sadrian	int do_stop = 0;
6262286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6263286441Srpaulo
6264298612Sadrian	do_stop = !! (sc->sc_ic.ic_nrunning > 0);
6265298612Sadrian
6266298612Sadrian	ieee80211_suspend_all(&sc->sc_ic);
6267298612Sadrian
6268298612Sadrian	if (do_stop) {
6269287197Sglebius		IWM_LOCK(sc);
6270287197Sglebius		iwm_stop(sc);
6271303628Ssbruno		sc->sc_flags |= IWM_FLAG_SCANNING;
6272287197Sglebius		IWM_UNLOCK(sc);
6273287197Sglebius	}
6274286441Srpaulo
6275286441Srpaulo	return (0);
6276286441Srpaulo}
6277286441Srpaulo
6278286441Srpaulostatic int
6279286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211)
6280286441Srpaulo{
6281293219Savos	struct iwm_fw_info *fw = &sc->sc_fw;
6282286441Srpaulo	device_t dev = sc->sc_dev;
6283286441Srpaulo	int i;
6284286441Srpaulo
6285330164Seadler	if (!sc->sc_attached)
6286330164Seadler		return 0;
6287330164Seadler	sc->sc_attached = 0;
6288330164Seadler
6289330159Seadler	if (do_net80211)
6290330159Seadler		ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task);
6291303628Ssbruno
6292301187Sadrian	callout_drain(&sc->sc_led_blink_to);
6293287197Sglebius	callout_drain(&sc->sc_watchdog_to);
6294287197Sglebius	iwm_stop_device(sc);
6295303628Ssbruno	if (do_net80211) {
6296287197Sglebius		ieee80211_ifdetach(&sc->sc_ic);
6297303628Ssbruno	}
6298286441Srpaulo
6299330163Seadler	iwm_phy_db_free(sc->sc_phy_db);
6300330163Seadler	sc->sc_phy_db = NULL;
6301302103Sadrian
6302330165Seadler	iwm_free_nvm_data(sc->nvm_data);
6303330165Seadler
6304286441Srpaulo	/* Free descriptor rings */
6305301970Sadrian	iwm_free_rx_ring(sc, &sc->rxq);
6306286441Srpaulo	for (i = 0; i < nitems(sc->txq); i++)
6307286441Srpaulo		iwm_free_tx_ring(sc, &sc->txq[i]);
6308286441Srpaulo
6309293219Savos	/* Free firmware */
6310293219Savos	if (fw->fw_fp != NULL)
6311293219Savos		iwm_fw_info_free(fw);
6312293219Savos
6313293178Savos	/* Free scheduler */
6314330150Seadler	iwm_dma_contig_free(&sc->sched_dma);
6315330150Seadler	iwm_dma_contig_free(&sc->ict_dma);
6316330150Seadler	iwm_dma_contig_free(&sc->kw_dma);
6317330150Seadler	iwm_dma_contig_free(&sc->fw_dma);
6318286441Srpaulo
6319286441Srpaulo	/* Finished with the hardware - detach things */
6320286441Srpaulo	iwm_pci_detach(dev);
6321286441Srpaulo
6322287197Sglebius	mbufq_drain(&sc->sc_snd);
6323293099Savos	IWM_LOCK_DESTROY(sc);
6324286441Srpaulo
6325286441Srpaulo	return (0);
6326286441Srpaulo}
6327286441Srpaulo
6328286441Srpaulostatic int
6329286441Srpauloiwm_detach(device_t dev)
6330286441Srpaulo{
6331286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6332286441Srpaulo
6333286441Srpaulo	return (iwm_detach_local(sc, 1));
6334286441Srpaulo}
6335286441Srpaulo
6336286441Srpaulostatic device_method_t iwm_pci_methods[] = {
6337286441Srpaulo        /* Device interface */
6338286441Srpaulo        DEVMETHOD(device_probe,         iwm_probe),
6339286441Srpaulo        DEVMETHOD(device_attach,        iwm_attach),
6340286441Srpaulo        DEVMETHOD(device_detach,        iwm_detach),
6341286441Srpaulo        DEVMETHOD(device_suspend,       iwm_suspend),
6342286441Srpaulo        DEVMETHOD(device_resume,        iwm_resume),
6343286441Srpaulo
6344286441Srpaulo        DEVMETHOD_END
6345286441Srpaulo};
6346286441Srpaulo
6347286441Srpaulostatic driver_t iwm_pci_driver = {
6348286441Srpaulo        "iwm",
6349286441Srpaulo        iwm_pci_methods,
6350286441Srpaulo        sizeof (struct iwm_softc)
6351286441Srpaulo};
6352286441Srpaulo
6353286441Srpaulostatic devclass_t iwm_devclass;
6354286441Srpaulo
6355286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL);
6356286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1);
6357286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1);
6358286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1);
6359