1330227Seadler/*	$OpenBSD: if_iwm.c,v 1.167 2017/04/04 00:40:52 claudio 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 346035 2019-04-08 17:55:04Z kevans $");
107286441Srpaulo
108300248Savos#include "opt_wlan.h"
109330455Seadler#include "opt_iwm.h"
110300248Savos
111286441Srpaulo#include <sys/param.h>
112286441Srpaulo#include <sys/bus.h>
113286441Srpaulo#include <sys/conf.h>
114286441Srpaulo#include <sys/endian.h>
115286441Srpaulo#include <sys/firmware.h>
116286441Srpaulo#include <sys/kernel.h>
117286441Srpaulo#include <sys/malloc.h>
118286441Srpaulo#include <sys/mbuf.h>
119286441Srpaulo#include <sys/mutex.h>
120286441Srpaulo#include <sys/module.h>
121286441Srpaulo#include <sys/proc.h>
122286441Srpaulo#include <sys/rman.h>
123286441Srpaulo#include <sys/socket.h>
124286441Srpaulo#include <sys/sockio.h>
125286441Srpaulo#include <sys/sysctl.h>
126286441Srpaulo#include <sys/linker.h>
127286441Srpaulo
128286441Srpaulo#include <machine/bus.h>
129286441Srpaulo#include <machine/endian.h>
130286441Srpaulo#include <machine/resource.h>
131286441Srpaulo
132286441Srpaulo#include <dev/pci/pcivar.h>
133286441Srpaulo#include <dev/pci/pcireg.h>
134286441Srpaulo
135286441Srpaulo#include <net/bpf.h>
136286441Srpaulo
137286441Srpaulo#include <net/if.h>
138286441Srpaulo#include <net/if_var.h>
139286441Srpaulo#include <net/if_arp.h>
140286441Srpaulo#include <net/if_dl.h>
141286441Srpaulo#include <net/if_media.h>
142286441Srpaulo#include <net/if_types.h>
143286441Srpaulo
144286441Srpaulo#include <netinet/in.h>
145286441Srpaulo#include <netinet/in_systm.h>
146286441Srpaulo#include <netinet/if_ether.h>
147286441Srpaulo#include <netinet/ip.h>
148286441Srpaulo
149286441Srpaulo#include <net80211/ieee80211_var.h>
150286441Srpaulo#include <net80211/ieee80211_regdomain.h>
151286441Srpaulo#include <net80211/ieee80211_ratectl.h>
152286441Srpaulo#include <net80211/ieee80211_radiotap.h>
153286441Srpaulo
154286475Srpaulo#include <dev/iwm/if_iwmreg.h>
155286475Srpaulo#include <dev/iwm/if_iwmvar.h>
156330188Seadler#include <dev/iwm/if_iwm_config.h>
157286475Srpaulo#include <dev/iwm/if_iwm_debug.h>
158330170Seadler#include <dev/iwm/if_iwm_notif_wait.h>
159286475Srpaulo#include <dev/iwm/if_iwm_util.h>
160286475Srpaulo#include <dev/iwm/if_iwm_binding.h>
161286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h>
162286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h>
163286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h>
164286475Srpaulo#include <dev/iwm/if_iwm_time_event.h>
165286475Srpaulo#include <dev/iwm/if_iwm_power.h>
166286475Srpaulo#include <dev/iwm/if_iwm_scan.h>
167330210Seadler#include <dev/iwm/if_iwm_sta.h>
168286441Srpaulo
169286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h>
170301187Sadrian#include <dev/iwm/if_iwm_led.h>
171330192Seadler#include <dev/iwm/if_iwm_fw.h>
172286441Srpaulo
173330208Seadler/* From DragonflyBSD */
174330208Seadler#define mtodoff(m, t, off)      ((t)((m)->m_data + (off)))
175330208Seadler
176286441Srpauloconst uint8_t iwm_nvm_channels[] = {
177286441Srpaulo	/* 2.4 GHz */
178286441Srpaulo	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
179286441Srpaulo	/* 5 GHz */
180298877Savos	36, 40, 44, 48, 52, 56, 60, 64,
181286441Srpaulo	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
182286441Srpaulo	149, 153, 157, 161, 165
183286441Srpaulo};
184298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS,
185298877Savos    "IWM_NUM_CHANNELS is too small");
186298877Savos
187303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = {
188303628Ssbruno	/* 2.4 GHz */
189303628Ssbruno	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
190303628Ssbruno	/* 5 GHz */
191303628Ssbruno	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
192303628Ssbruno	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
193303628Ssbruno	149, 153, 157, 161, 165, 169, 173, 177, 181
194303628Ssbruno};
195303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000,
196303628Ssbruno    "IWM_NUM_CHANNELS_8000 is too small");
197303628Ssbruno
198303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS	14
199303628Ssbruno#define IWM_N_HW_ADDR_MASK	0xF
200303628Ssbruno
201286441Srpaulo/*
202286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries
203286441Srpaulo * that are populated.
204286441Srpaulo */
205286441Srpauloconst struct iwm_rate {
206286441Srpaulo	uint8_t rate;
207286441Srpaulo	uint8_t plcp;
208286441Srpaulo} iwm_rates[] = {
209286441Srpaulo	{   2,	IWM_RATE_1M_PLCP  },
210286441Srpaulo	{   4,	IWM_RATE_2M_PLCP  },
211286441Srpaulo	{  11,	IWM_RATE_5M_PLCP  },
212286441Srpaulo	{  22,	IWM_RATE_11M_PLCP },
213286441Srpaulo	{  12,	IWM_RATE_6M_PLCP  },
214286441Srpaulo	{  18,	IWM_RATE_9M_PLCP  },
215286441Srpaulo	{  24,	IWM_RATE_12M_PLCP },
216286441Srpaulo	{  36,	IWM_RATE_18M_PLCP },
217286441Srpaulo	{  48,	IWM_RATE_24M_PLCP },
218286441Srpaulo	{  72,	IWM_RATE_36M_PLCP },
219286441Srpaulo	{  96,	IWM_RATE_48M_PLCP },
220286441Srpaulo	{ 108,	IWM_RATE_54M_PLCP },
221286441Srpaulo};
222286441Srpaulo#define IWM_RIDX_CCK	0
223286441Srpaulo#define IWM_RIDX_OFDM	4
224286441Srpaulo#define IWM_RIDX_MAX	(nitems(iwm_rates)-1)
225286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
226286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
227286441Srpaulo
228303628Ssbrunostruct iwm_nvm_section {
229303628Ssbruno	uint16_t length;
230303628Ssbruno	uint8_t *data;
231303628Ssbruno};
232303628Ssbruno
233330183Seadler#define IWM_MVM_UCODE_ALIVE_TIMEOUT	hz
234330171Seadler#define IWM_MVM_UCODE_CALIB_TIMEOUT	(2*hz)
235330171Seadler
236330183Seadlerstruct iwm_mvm_alive_data {
237330183Seadler	int valid;
238330183Seadler	uint32_t scd_base_addr;
239330183Seadler};
240330183Seadler
241286441Srpaulostatic int	iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t);
242286441Srpaulostatic int	iwm_firmware_store_section(struct iwm_softc *,
243286441Srpaulo                                           enum iwm_ucode_type,
244286441Srpaulo                                           const uint8_t *, size_t);
245286441Srpaulostatic int	iwm_set_default_calib(struct iwm_softc *, const void *);
246286441Srpaulostatic void	iwm_fw_info_free(struct iwm_fw_info *);
247286441Srpaulostatic int	iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type);
248286441Srpaulostatic int	iwm_alloc_fwmem(struct iwm_softc *);
249286441Srpaulostatic int	iwm_alloc_sched(struct iwm_softc *);
250286441Srpaulostatic int	iwm_alloc_kw(struct iwm_softc *);
251286441Srpaulostatic int	iwm_alloc_ict(struct iwm_softc *);
252286441Srpaulostatic int	iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
253286441Srpaulostatic void	iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
254286441Srpaulostatic void	iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
255286441Srpaulostatic int	iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *,
256286441Srpaulo                                  int);
257286441Srpaulostatic void	iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
258286441Srpaulostatic void	iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
259286441Srpaulostatic void	iwm_enable_interrupts(struct iwm_softc *);
260286441Srpaulostatic void	iwm_restore_interrupts(struct iwm_softc *);
261286441Srpaulostatic void	iwm_disable_interrupts(struct iwm_softc *);
262286441Srpaulostatic void	iwm_ict_reset(struct iwm_softc *);
263286441Srpaulostatic int	iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *);
264286441Srpaulostatic void	iwm_stop_device(struct iwm_softc *);
265286441Srpaulostatic void	iwm_mvm_nic_config(struct iwm_softc *);
266286441Srpaulostatic int	iwm_nic_rx_init(struct iwm_softc *);
267286441Srpaulostatic int	iwm_nic_tx_init(struct iwm_softc *);
268286441Srpaulostatic int	iwm_nic_init(struct iwm_softc *);
269330183Seadlerstatic int	iwm_trans_pcie_fw_alive(struct iwm_softc *, uint32_t);
270286441Srpaulostatic int	iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t,
271286441Srpaulo                                   uint16_t, uint8_t *, uint16_t *);
272286441Srpaulostatic int	iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
273330165Seadler				     uint16_t *, uint32_t);
274298877Savosstatic uint32_t	iwm_eeprom_channel_flags(uint16_t);
275298877Savosstatic void	iwm_add_channel_band(struct iwm_softc *,
276303628Ssbruno		    struct ieee80211_channel[], int, int *, int, size_t,
277298877Savos		    const uint8_t[]);
278298877Savosstatic void	iwm_init_channel_map(struct ieee80211com *, int, int *,
279298877Savos		    struct ieee80211_channel[]);
280330165Seadlerstatic struct iwm_nvm_data *
281330165Seadler	iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
282330165Seadler			   const uint16_t *, const uint16_t *,
283330165Seadler			   const uint16_t *, const uint16_t *,
284330165Seadler			   const uint16_t *);
285330165Seadlerstatic void	iwm_free_nvm_data(struct iwm_nvm_data *);
286330165Seadlerstatic void	iwm_set_hw_address_family_8000(struct iwm_softc *,
287330165Seadler					       struct iwm_nvm_data *,
288330165Seadler					       const uint16_t *,
289330165Seadler					       const uint16_t *);
290303628Ssbrunostatic int	iwm_get_sku(const struct iwm_softc *, const uint16_t *,
291303628Ssbruno			    const uint16_t *);
292303628Ssbrunostatic int	iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *);
293303628Ssbrunostatic int	iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *,
294303628Ssbruno				  const uint16_t *);
295303628Ssbrunostatic int	iwm_get_n_hw_addrs(const struct iwm_softc *,
296303628Ssbruno				   const uint16_t *);
297303628Ssbrunostatic void	iwm_set_radio_cfg(const struct iwm_softc *,
298303628Ssbruno				  struct iwm_nvm_data *, uint32_t);
299330165Seadlerstatic struct iwm_nvm_data *
300330165Seadler	iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *);
301286441Srpaulostatic int	iwm_nvm_init(struct iwm_softc *);
302330182Seadlerstatic int	iwm_pcie_load_section(struct iwm_softc *, uint8_t,
303330182Seadler				      const struct iwm_fw_desc *);
304330182Seadlerstatic int	iwm_pcie_load_firmware_chunk(struct iwm_softc *, uint32_t,
305330182Seadler					     bus_addr_t, uint32_t);
306330182Seadlerstatic int	iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc,
307330182Seadler						const struct iwm_fw_sects *,
308330182Seadler						int, int *);
309330182Seadlerstatic int	iwm_pcie_load_cpu_sections(struct iwm_softc *,
310330182Seadler					   const struct iwm_fw_sects *,
311330182Seadler					   int, int *);
312330182Seadlerstatic int	iwm_pcie_load_given_ucode_8000(struct iwm_softc *,
313330182Seadler					       const struct iwm_fw_sects *);
314330182Seadlerstatic int	iwm_pcie_load_given_ucode(struct iwm_softc *,
315330182Seadler					  const struct iwm_fw_sects *);
316330183Seadlerstatic int	iwm_start_fw(struct iwm_softc *, const struct iwm_fw_sects *);
317286441Srpaulostatic int	iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t);
318286441Srpaulostatic int	iwm_send_phy_cfg_cmd(struct iwm_softc *);
319286441Srpaulostatic int	iwm_mvm_load_ucode_wait_alive(struct iwm_softc *,
320286441Srpaulo                                              enum iwm_ucode_type);
321286441Srpaulostatic int	iwm_run_init_mvm_ucode(struct iwm_softc *, int);
322286441Srpaulostatic int	iwm_rx_addbuf(struct iwm_softc *, int, int);
323286441Srpaulostatic int	iwm_mvm_get_signal_strength(struct iwm_softc *,
324286441Srpaulo					    struct iwm_rx_phy_info *);
325286441Srpaulostatic void	iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *,
326330208Seadler                                      struct iwm_rx_packet *);
327330224Seadlerstatic int	iwm_get_noise(struct iwm_softc *,
328330144Seadler		    const struct iwm_mvm_statistics_rx_non_phy *);
329330224Seadlerstatic void	iwm_mvm_handle_rx_statistics(struct iwm_softc *,
330330224Seadler		    struct iwm_rx_packet *);
331330208Seadlerstatic boolean_t iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct mbuf *,
332330208Seadler				    uint32_t, boolean_t);
333293100Savosstatic int	iwm_mvm_rx_tx_cmd_single(struct iwm_softc *,
334286441Srpaulo                                         struct iwm_rx_packet *,
335286441Srpaulo				         struct iwm_node *);
336330208Seadlerstatic void	iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *);
337286441Srpaulostatic void	iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *);
338286441Srpaulo#if 0
339286441Srpaulostatic void	iwm_update_sched(struct iwm_softc *, int, int, uint8_t,
340286441Srpaulo                                 uint16_t);
341286441Srpaulo#endif
342286441Srpaulostatic const struct iwm_rate *
343286441Srpaulo	iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *,
344330155Seadler			struct mbuf *, struct iwm_tx_cmd *);
345286441Srpaulostatic int	iwm_tx(struct iwm_softc *, struct mbuf *,
346286441Srpaulo                       struct ieee80211_node *, int);
347286441Srpaulostatic int	iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
348286441Srpaulo			     const struct ieee80211_bpf_params *);
349330203Seadlerstatic int	iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_vap *);
350286441Srpaulostatic int	iwm_auth(struct ieee80211vap *, struct iwm_softc *);
351286441Srpaulostatic int	iwm_assoc(struct ieee80211vap *, struct iwm_softc *);
352286441Srpaulostatic int	iwm_release(struct iwm_softc *, struct iwm_node *);
353286441Srpaulostatic struct ieee80211_node *
354286441Srpaulo		iwm_node_alloc(struct ieee80211vap *,
355286441Srpaulo		               const uint8_t[IEEE80211_ADDR_LEN]);
356286441Srpaulostatic void	iwm_setrates(struct iwm_softc *, struct iwm_node *);
357286441Srpaulostatic int	iwm_media_change(struct ifnet *);
358286441Srpaulostatic int	iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int);
359286441Srpaulostatic void	iwm_endscan_cb(void *, int);
360303628Ssbrunostatic void	iwm_mvm_fill_sf_command(struct iwm_softc *,
361303628Ssbruno					struct iwm_sf_cfg_cmd *,
362303628Ssbruno					struct ieee80211_node *);
363303628Ssbrunostatic int	iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state);
364303628Ssbrunostatic int	iwm_send_bt_init_conf(struct iwm_softc *);
365330217Seadlerstatic boolean_t iwm_mvm_is_lar_supported(struct iwm_softc *);
366330217Seadlerstatic boolean_t iwm_mvm_is_wifi_mcc_supported(struct iwm_softc *);
367303628Ssbrunostatic int	iwm_send_update_mcc_cmd(struct iwm_softc *, const char *);
368303628Ssbrunostatic void	iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t);
369286441Srpaulostatic int	iwm_init_hw(struct iwm_softc *);
370287197Sglebiusstatic void	iwm_init(struct iwm_softc *);
371287197Sglebiusstatic void	iwm_start(struct iwm_softc *);
372287197Sglebiusstatic void	iwm_stop(struct iwm_softc *);
373286441Srpaulostatic void	iwm_watchdog(void *);
374287197Sglebiusstatic void	iwm_parent(struct ieee80211com *);
375286441Srpaulo#ifdef IWM_DEBUG
376286441Srpaulostatic const char *
377286441Srpaulo		iwm_desc_lookup(uint32_t);
378286441Srpaulostatic void	iwm_nic_error(struct iwm_softc *);
379303628Ssbrunostatic void	iwm_nic_umac_error(struct iwm_softc *);
380286441Srpaulo#endif
381330208Seadlerstatic void	iwm_handle_rxb(struct iwm_softc *, struct mbuf *);
382286441Srpaulostatic void	iwm_notif_intr(struct iwm_softc *);
383286441Srpaulostatic void	iwm_intr(void *);
384286441Srpaulostatic int	iwm_attach(device_t);
385303628Ssbrunostatic int	iwm_is_valid_ether_addr(uint8_t *);
386286441Srpaulostatic void	iwm_preinit(void *);
387286441Srpaulostatic int	iwm_detach_local(struct iwm_softc *sc, int);
388286441Srpaulostatic void	iwm_init_task(void *);
389286441Srpaulostatic void	iwm_radiotap_attach(struct iwm_softc *);
390286441Srpaulostatic struct ieee80211vap *
391286441Srpaulo		iwm_vap_create(struct ieee80211com *,
392286441Srpaulo		               const char [IFNAMSIZ], int,
393286441Srpaulo		               enum ieee80211_opmode, int,
394286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN],
395286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN]);
396286441Srpaulostatic void	iwm_vap_delete(struct ieee80211vap *);
397330223Seadlerstatic void	iwm_xmit_queue_drain(struct iwm_softc *);
398286441Srpaulostatic void	iwm_scan_start(struct ieee80211com *);
399286441Srpaulostatic void	iwm_scan_end(struct ieee80211com *);
400286441Srpaulostatic void	iwm_update_mcast(struct ieee80211com *);
401286441Srpaulostatic void	iwm_set_channel(struct ieee80211com *);
402286441Srpaulostatic void	iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long);
403286441Srpaulostatic void	iwm_scan_mindwell(struct ieee80211_scan_state *);
404286441Srpaulostatic int	iwm_detach(device_t);
405286441Srpaulo
406330217Seadlerstatic int	iwm_lar_disable = 0;
407330217SeadlerTUNABLE_INT("hw.iwm.lar.disable", &iwm_lar_disable);
408330217Seadler
409286441Srpaulo/*
410286441Srpaulo * Firmware parser.
411286441Srpaulo */
412286441Srpaulo
413286441Srpaulostatic int
414286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen)
415286441Srpaulo{
416286441Srpaulo	const struct iwm_fw_cscheme_list *l = (const void *)data;
417286441Srpaulo
418286441Srpaulo	if (dlen < sizeof(*l) ||
419286441Srpaulo	    dlen < sizeof(l->size) + l->size * sizeof(*l->cs))
420286441Srpaulo		return EINVAL;
421286441Srpaulo
422286441Srpaulo	/* we don't actually store anything for now, always use s/w crypto */
423286441Srpaulo
424286441Srpaulo	return 0;
425286441Srpaulo}
426286441Srpaulo
427286441Srpaulostatic int
428286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc,
429286441Srpaulo    enum iwm_ucode_type type, const uint8_t *data, size_t dlen)
430286441Srpaulo{
431286441Srpaulo	struct iwm_fw_sects *fws;
432330182Seadler	struct iwm_fw_desc *fwone;
433286441Srpaulo
434286441Srpaulo	if (type >= IWM_UCODE_TYPE_MAX)
435286441Srpaulo		return EINVAL;
436286441Srpaulo	if (dlen < sizeof(uint32_t))
437286441Srpaulo		return EINVAL;
438286441Srpaulo
439286441Srpaulo	fws = &sc->sc_fw.fw_sects[type];
440330168Seadler	if (fws->fw_count >= IWM_UCODE_SECTION_MAX)
441286441Srpaulo		return EINVAL;
442286441Srpaulo
443286441Srpaulo	fwone = &fws->fw_sect[fws->fw_count];
444286441Srpaulo
445286441Srpaulo	/* first 32bit are device load offset */
446330182Seadler	memcpy(&fwone->offset, data, sizeof(uint32_t));
447286441Srpaulo
448286441Srpaulo	/* rest is data */
449330182Seadler	fwone->data = data + sizeof(uint32_t);
450330182Seadler	fwone->len = dlen - sizeof(uint32_t);
451286441Srpaulo
452286441Srpaulo	fws->fw_count++;
453286441Srpaulo
454286441Srpaulo	return 0;
455286441Srpaulo}
456286441Srpaulo
457330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40
458330152Seadler
459286441Srpaulo/* iwlwifi: iwl-drv.c */
460286441Srpaulostruct iwm_tlv_calib_data {
461286441Srpaulo	uint32_t ucode_type;
462286441Srpaulo	struct iwm_tlv_calib_ctrl calib;
463286441Srpaulo} __packed;
464286441Srpaulo
465286441Srpaulostatic int
466286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data)
467286441Srpaulo{
468286441Srpaulo	const struct iwm_tlv_calib_data *def_calib = data;
469286441Srpaulo	uint32_t ucode_type = le32toh(def_calib->ucode_type);
470286441Srpaulo
471286441Srpaulo	if (ucode_type >= IWM_UCODE_TYPE_MAX) {
472286441Srpaulo		device_printf(sc->sc_dev,
473286441Srpaulo		    "Wrong ucode_type %u for default "
474286441Srpaulo		    "calibration.\n", ucode_type);
475286441Srpaulo		return EINVAL;
476286441Srpaulo	}
477286441Srpaulo
478286441Srpaulo	sc->sc_default_calib[ucode_type].flow_trigger =
479286441Srpaulo	    def_calib->calib.flow_trigger;
480286441Srpaulo	sc->sc_default_calib[ucode_type].event_trigger =
481286441Srpaulo	    def_calib->calib.event_trigger;
482286441Srpaulo
483286441Srpaulo	return 0;
484286441Srpaulo}
485286441Srpaulo
486330196Seadlerstatic int
487330196Seadleriwm_set_ucode_api_flags(struct iwm_softc *sc, const uint8_t *data,
488330196Seadler			struct iwm_ucode_capabilities *capa)
489330196Seadler{
490330196Seadler	const struct iwm_ucode_api *ucode_api = (const void *)data;
491330196Seadler	uint32_t api_index = le32toh(ucode_api->api_index);
492330196Seadler	uint32_t api_flags = le32toh(ucode_api->api_flags);
493330196Seadler	int i;
494330196Seadler
495330196Seadler	if (api_index >= howmany(IWM_NUM_UCODE_TLV_API, 32)) {
496330196Seadler		device_printf(sc->sc_dev,
497330196Seadler		    "api flags index %d larger than supported by driver\n",
498330196Seadler		    api_index);
499330196Seadler		/* don't return an error so we can load FW that has more bits */
500330196Seadler		return 0;
501330196Seadler	}
502330196Seadler
503330196Seadler	for (i = 0; i < 32; i++) {
504330196Seadler		if (api_flags & (1U << i))
505330196Seadler			setbit(capa->enabled_api, i + 32 * api_index);
506330196Seadler	}
507330196Seadler
508330196Seadler	return 0;
509330196Seadler}
510330196Seadler
511330196Seadlerstatic int
512330196Seadleriwm_set_ucode_capabilities(struct iwm_softc *sc, const uint8_t *data,
513330196Seadler			   struct iwm_ucode_capabilities *capa)
514330196Seadler{
515330196Seadler	const struct iwm_ucode_capa *ucode_capa = (const void *)data;
516330196Seadler	uint32_t api_index = le32toh(ucode_capa->api_index);
517330196Seadler	uint32_t api_flags = le32toh(ucode_capa->api_capa);
518330196Seadler	int i;
519330196Seadler
520330196Seadler	if (api_index >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) {
521330196Seadler		device_printf(sc->sc_dev,
522330196Seadler		    "capa flags index %d larger than supported by driver\n",
523330196Seadler		    api_index);
524330196Seadler		/* don't return an error so we can load FW that has more bits */
525330196Seadler		return 0;
526330196Seadler	}
527330196Seadler
528330196Seadler	for (i = 0; i < 32; i++) {
529330196Seadler		if (api_flags & (1U << i))
530330196Seadler			setbit(capa->enabled_capa, i + 32 * api_index);
531330196Seadler	}
532330196Seadler
533330196Seadler	return 0;
534330196Seadler}
535330196Seadler
536286441Srpaulostatic void
537286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw)
538286441Srpaulo{
539293177Savos	firmware_put(fw->fw_fp, FIRMWARE_UNLOAD);
540293177Savos	fw->fw_fp = NULL;
541286441Srpaulo	/* don't touch fw->fw_status */
542286441Srpaulo	memset(fw->fw_sects, 0, sizeof(fw->fw_sects));
543286441Srpaulo}
544286441Srpaulo
545286441Srpaulostatic int
546286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
547286441Srpaulo{
548286441Srpaulo	struct iwm_fw_info *fw = &sc->sc_fw;
549286441Srpaulo	const struct iwm_tlv_ucode_header *uhdr;
550330784Seadler	struct iwm_ucode_tlv tlv;
551330196Seadler	struct iwm_ucode_capabilities *capa = &sc->ucode_capa;
552286441Srpaulo	enum iwm_ucode_tlv_type tlv_type;
553286441Srpaulo	const struct firmware *fwp;
554286441Srpaulo	const uint8_t *data;
555330168Seadler	uint32_t usniffer_img;
556330168Seadler	uint32_t paging_mem_size;
557330182Seadler	int num_of_cpus;
558286441Srpaulo	int error = 0;
559286441Srpaulo	size_t len;
560286441Srpaulo
561286441Srpaulo	if (fw->fw_status == IWM_FW_STATUS_DONE &&
562330168Seadler	    ucode_type != IWM_UCODE_INIT)
563286441Srpaulo		return 0;
564286441Srpaulo
565286441Srpaulo	while (fw->fw_status == IWM_FW_STATUS_INPROGRESS)
566286441Srpaulo		msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0);
567286441Srpaulo	fw->fw_status = IWM_FW_STATUS_INPROGRESS;
568286441Srpaulo
569293177Savos	if (fw->fw_fp != NULL)
570286441Srpaulo		iwm_fw_info_free(fw);
571286441Srpaulo
572286441Srpaulo	/*
573286441Srpaulo	 * Load firmware into driver memory.
574293177Savos	 * fw_fp will be set.
575286441Srpaulo	 */
576286441Srpaulo	IWM_UNLOCK(sc);
577330166Seadler	fwp = firmware_get(sc->cfg->fw_name);
578293177Savos	IWM_LOCK(sc);
579286441Srpaulo	if (fwp == NULL) {
580286441Srpaulo		device_printf(sc->sc_dev,
581286441Srpaulo		    "could not read firmware %s (error %d)\n",
582330166Seadler		    sc->cfg->fw_name, error);
583286441Srpaulo		goto out;
584286441Srpaulo	}
585293177Savos	fw->fw_fp = fwp;
586286441Srpaulo
587303628Ssbruno	/* (Re-)Initialize default values. */
588330196Seadler	capa->flags = 0;
589330196Seadler	capa->max_probe_length = IWM_DEFAULT_MAX_PROBE_LENGTH;
590330196Seadler	capa->n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS;
591330196Seadler	memset(capa->enabled_capa, 0, sizeof(capa->enabled_capa));
592330196Seadler	memset(capa->enabled_api, 0, sizeof(capa->enabled_api));
593303628Ssbruno	memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc));
594303628Ssbruno
595286441Srpaulo	/*
596286441Srpaulo	 * Parse firmware contents
597286441Srpaulo	 */
598286441Srpaulo
599293177Savos	uhdr = (const void *)fw->fw_fp->data;
600293177Savos	if (*(const uint32_t *)fw->fw_fp->data != 0
601286441Srpaulo	    || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) {
602286441Srpaulo		device_printf(sc->sc_dev, "invalid firmware %s\n",
603330166Seadler		    sc->cfg->fw_name);
604286441Srpaulo		error = EINVAL;
605286441Srpaulo		goto out;
606286441Srpaulo	}
607286441Srpaulo
608330784Seadler	snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)",
609303628Ssbruno	    IWM_UCODE_MAJOR(le32toh(uhdr->ver)),
610303628Ssbruno	    IWM_UCODE_MINOR(le32toh(uhdr->ver)),
611303628Ssbruno	    IWM_UCODE_API(le32toh(uhdr->ver)));
612286441Srpaulo	data = uhdr->data;
613293177Savos	len = fw->fw_fp->datasize - sizeof(*uhdr);
614286441Srpaulo
615330784Seadler	while (len >= sizeof(tlv)) {
616330784Seadler		size_t tlv_len;
617330784Seadler		const void *tlv_data;
618286441Srpaulo
619330784Seadler		memcpy(&tlv, data, sizeof(tlv));
620330784Seadler		tlv_len = le32toh(tlv.length);
621330784Seadler		tlv_type = le32toh(tlv.type);
622286441Srpaulo
623330784Seadler		len -= sizeof(tlv);
624330784Seadler		data += sizeof(tlv);
625330784Seadler		tlv_data = data;
626330784Seadler
627286441Srpaulo		if (len < tlv_len) {
628286441Srpaulo			device_printf(sc->sc_dev,
629286441Srpaulo			    "firmware too short: %zu bytes\n",
630286441Srpaulo			    len);
631286441Srpaulo			error = EINVAL;
632286441Srpaulo			goto parse_out;
633286441Srpaulo		}
634286441Srpaulo
635286441Srpaulo		switch ((int)tlv_type) {
636286441Srpaulo		case IWM_UCODE_TLV_PROBE_MAX_LEN:
637330784Seadler			if (tlv_len < sizeof(uint32_t)) {
638286441Srpaulo				device_printf(sc->sc_dev,
639330784Seadler				    "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n",
640286441Srpaulo				    __func__,
641286441Srpaulo				    (int) tlv_len);
642286441Srpaulo				error = EINVAL;
643286441Srpaulo				goto parse_out;
644286441Srpaulo			}
645330196Seadler			capa->max_probe_length =
646330784Seadler			    le32toh(*(const uint32_t *)tlv_data);
647286441Srpaulo			/* limit it to something sensible */
648330196Seadler			if (capa->max_probe_length >
649303628Ssbruno			    IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) {
650286441Srpaulo				IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
651286441Srpaulo				    "%s: IWM_UCODE_TLV_PROBE_MAX_LEN "
652286441Srpaulo				    "ridiculous\n", __func__);
653286441Srpaulo				error = EINVAL;
654286441Srpaulo				goto parse_out;
655286441Srpaulo			}
656286441Srpaulo			break;
657286441Srpaulo		case IWM_UCODE_TLV_PAN:
658286441Srpaulo			if (tlv_len) {
659286441Srpaulo				device_printf(sc->sc_dev,
660286441Srpaulo				    "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n",
661286441Srpaulo				    __func__,
662286441Srpaulo				    (int) tlv_len);
663286441Srpaulo				error = EINVAL;
664286441Srpaulo				goto parse_out;
665286441Srpaulo			}
666330196Seadler			capa->flags |= IWM_UCODE_TLV_FLAGS_PAN;
667286441Srpaulo			break;
668286441Srpaulo		case IWM_UCODE_TLV_FLAGS:
669286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
670286441Srpaulo				device_printf(sc->sc_dev,
671286441Srpaulo				    "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n",
672286441Srpaulo				    __func__,
673286441Srpaulo				    (int) tlv_len);
674286441Srpaulo				error = EINVAL;
675286441Srpaulo				goto parse_out;
676286441Srpaulo			}
677286441Srpaulo			/*
678286441Srpaulo			 * Apparently there can be many flags, but Linux driver
679286441Srpaulo			 * parses only the first one, and so do we.
680286441Srpaulo			 *
681286441Srpaulo			 * XXX: why does this override IWM_UCODE_TLV_PAN?
682286441Srpaulo			 * Intentional or a bug?  Observations from
683286441Srpaulo			 * current firmware file:
684286441Srpaulo			 *  1) TLV_PAN is parsed first
685286441Srpaulo			 *  2) TLV_FLAGS contains TLV_FLAGS_PAN
686286441Srpaulo			 * ==> this resets TLV_PAN to itself... hnnnk
687286441Srpaulo			 */
688330784Seadler			capa->flags = le32toh(*(const uint32_t *)tlv_data);
689286441Srpaulo			break;
690286441Srpaulo		case IWM_UCODE_TLV_CSCHEME:
691286441Srpaulo			if ((error = iwm_store_cscheme(sc,
692286441Srpaulo			    tlv_data, tlv_len)) != 0) {
693286441Srpaulo				device_printf(sc->sc_dev,
694286441Srpaulo				    "%s: iwm_store_cscheme(): returned %d\n",
695286441Srpaulo				    __func__,
696286441Srpaulo				    error);
697286441Srpaulo				goto parse_out;
698286441Srpaulo			}
699286441Srpaulo			break;
700330182Seadler		case IWM_UCODE_TLV_NUM_OF_CPU:
701286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
702286441Srpaulo				device_printf(sc->sc_dev,
703330182Seadler				    "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) != sizeof(uint32_t)\n",
704286441Srpaulo				    __func__,
705286441Srpaulo				    (int) tlv_len);
706286441Srpaulo				error = EINVAL;
707286441Srpaulo				goto parse_out;
708286441Srpaulo			}
709330784Seadler			num_of_cpus = le32toh(*(const uint32_t *)tlv_data);
710330182Seadler			if (num_of_cpus == 2) {
711330182Seadler				fw->fw_sects[IWM_UCODE_REGULAR].is_dual_cpus =
712330182Seadler					TRUE;
713330182Seadler				fw->fw_sects[IWM_UCODE_INIT].is_dual_cpus =
714330182Seadler					TRUE;
715330182Seadler				fw->fw_sects[IWM_UCODE_WOWLAN].is_dual_cpus =
716330182Seadler					TRUE;
717330182Seadler			} else if ((num_of_cpus > 2) || (num_of_cpus < 1)) {
718286441Srpaulo				device_printf(sc->sc_dev,
719303628Ssbruno				    "%s: Driver supports only 1 or 2 CPUs\n",
720286441Srpaulo				    __func__);
721286441Srpaulo				error = EINVAL;
722286441Srpaulo				goto parse_out;
723286441Srpaulo			}
724286441Srpaulo			break;
725286441Srpaulo		case IWM_UCODE_TLV_SEC_RT:
726286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
727330168Seadler			    IWM_UCODE_REGULAR, tlv_data, tlv_len)) != 0) {
728286441Srpaulo				device_printf(sc->sc_dev,
729330168Seadler				    "%s: IWM_UCODE_REGULAR: iwm_firmware_store_section() failed; %d\n",
730286441Srpaulo				    __func__,
731286441Srpaulo				    error);
732286441Srpaulo				goto parse_out;
733286441Srpaulo			}
734286441Srpaulo			break;
735286441Srpaulo		case IWM_UCODE_TLV_SEC_INIT:
736286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
737330168Seadler			    IWM_UCODE_INIT, tlv_data, tlv_len)) != 0) {
738286441Srpaulo				device_printf(sc->sc_dev,
739330168Seadler				    "%s: IWM_UCODE_INIT: iwm_firmware_store_section() failed; %d\n",
740286441Srpaulo				    __func__,
741286441Srpaulo				    error);
742286441Srpaulo				goto parse_out;
743286441Srpaulo			}
744286441Srpaulo			break;
745286441Srpaulo		case IWM_UCODE_TLV_SEC_WOWLAN:
746286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
747330168Seadler			    IWM_UCODE_WOWLAN, tlv_data, tlv_len)) != 0) {
748286441Srpaulo				device_printf(sc->sc_dev,
749330168Seadler				    "%s: IWM_UCODE_WOWLAN: iwm_firmware_store_section() failed; %d\n",
750286441Srpaulo				    __func__,
751286441Srpaulo				    error);
752286441Srpaulo				goto parse_out;
753286441Srpaulo			}
754286441Srpaulo			break;
755286441Srpaulo		case IWM_UCODE_TLV_DEF_CALIB:
756286441Srpaulo			if (tlv_len != sizeof(struct iwm_tlv_calib_data)) {
757286441Srpaulo				device_printf(sc->sc_dev,
758286441Srpaulo				    "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n",
759286441Srpaulo				    __func__,
760286441Srpaulo				    (int) tlv_len,
761286441Srpaulo				    (int) sizeof(struct iwm_tlv_calib_data));
762286441Srpaulo				error = EINVAL;
763286441Srpaulo				goto parse_out;
764286441Srpaulo			}
765286441Srpaulo			if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) {
766286441Srpaulo				device_printf(sc->sc_dev,
767286441Srpaulo				    "%s: iwm_set_default_calib() failed: %d\n",
768286441Srpaulo				    __func__,
769286441Srpaulo				    error);
770286441Srpaulo				goto parse_out;
771286441Srpaulo			}
772286441Srpaulo			break;
773286441Srpaulo		case IWM_UCODE_TLV_PHY_SKU:
774286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
775286441Srpaulo				error = EINVAL;
776286441Srpaulo				device_printf(sc->sc_dev,
777286441Srpaulo				    "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n",
778286441Srpaulo				    __func__,
779286441Srpaulo				    (int) tlv_len);
780286441Srpaulo				goto parse_out;
781286441Srpaulo			}
782330167Seadler			sc->sc_fw.phy_config =
783330784Seadler			    le32toh(*(const uint32_t *)tlv_data);
784330167Seadler			sc->sc_fw.valid_tx_ant = (sc->sc_fw.phy_config &
785330167Seadler						  IWM_FW_PHY_CFG_TX_CHAIN) >>
786330167Seadler						  IWM_FW_PHY_CFG_TX_CHAIN_POS;
787330167Seadler			sc->sc_fw.valid_rx_ant = (sc->sc_fw.phy_config &
788330167Seadler						  IWM_FW_PHY_CFG_RX_CHAIN) >>
789330167Seadler						  IWM_FW_PHY_CFG_RX_CHAIN_POS;
790286441Srpaulo			break;
791286441Srpaulo
792303628Ssbruno		case IWM_UCODE_TLV_API_CHANGES_SET: {
793330196Seadler			if (tlv_len != sizeof(struct iwm_ucode_api)) {
794303628Ssbruno				error = EINVAL;
795303628Ssbruno				goto parse_out;
796303628Ssbruno			}
797330196Seadler			if (iwm_set_ucode_api_flags(sc, tlv_data, capa)) {
798330196Seadler				error = EINVAL;
799303628Ssbruno				goto parse_out;
800303628Ssbruno			}
801303628Ssbruno			break;
802303628Ssbruno		}
803303628Ssbruno
804303628Ssbruno		case IWM_UCODE_TLV_ENABLED_CAPABILITIES: {
805330196Seadler			if (tlv_len != sizeof(struct iwm_ucode_capa)) {
806303628Ssbruno				error = EINVAL;
807303628Ssbruno				goto parse_out;
808303628Ssbruno			}
809330196Seadler			if (iwm_set_ucode_capabilities(sc, tlv_data, capa)) {
810330196Seadler				error = EINVAL;
811303628Ssbruno				goto parse_out;
812303628Ssbruno			}
813303628Ssbruno			break;
814303628Ssbruno		}
815303628Ssbruno
816303628Ssbruno		case 48: /* undocumented TLV */
817303628Ssbruno		case IWM_UCODE_TLV_SDIO_ADMA_ADDR:
818303628Ssbruno		case IWM_UCODE_TLV_FW_GSCAN_CAPA:
819286441Srpaulo			/* ignore, not used by current driver */
820286441Srpaulo			break;
821286441Srpaulo
822303628Ssbruno		case IWM_UCODE_TLV_SEC_RT_USNIFFER:
823303628Ssbruno			if ((error = iwm_firmware_store_section(sc,
824330168Seadler			    IWM_UCODE_REGULAR_USNIFFER, tlv_data,
825303628Ssbruno			    tlv_len)) != 0)
826303628Ssbruno				goto parse_out;
827303628Ssbruno			break;
828303628Ssbruno
829330168Seadler		case IWM_UCODE_TLV_PAGING:
830330168Seadler			if (tlv_len != sizeof(uint32_t)) {
831330168Seadler				error = EINVAL;
832330168Seadler				goto parse_out;
833330168Seadler			}
834330784Seadler			paging_mem_size = le32toh(*(const uint32_t *)tlv_data);
835330168Seadler
836330168Seadler			IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
837330168Seadler			    "%s: Paging: paging enabled (size = %u bytes)\n",
838330168Seadler			    __func__, paging_mem_size);
839330168Seadler			if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) {
840330168Seadler				device_printf(sc->sc_dev,
841330168Seadler					"%s: Paging: driver supports up to %u bytes for paging image\n",
842330168Seadler					__func__, IWM_MAX_PAGING_IMAGE_SIZE);
843330168Seadler				error = EINVAL;
844330168Seadler				goto out;
845330168Seadler			}
846330168Seadler			if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) {
847330168Seadler				device_printf(sc->sc_dev,
848330168Seadler				    "%s: Paging: image isn't multiple %u\n",
849330168Seadler				    __func__, IWM_FW_PAGING_SIZE);
850330168Seadler				error = EINVAL;
851330168Seadler				goto out;
852330168Seadler			}
853330168Seadler
854330168Seadler			sc->sc_fw.fw_sects[IWM_UCODE_REGULAR].paging_mem_size =
855330168Seadler			    paging_mem_size;
856330168Seadler			usniffer_img = IWM_UCODE_REGULAR_USNIFFER;
857330168Seadler			sc->sc_fw.fw_sects[usniffer_img].paging_mem_size =
858330168Seadler			    paging_mem_size;
859330168Seadler			break;
860330168Seadler
861303628Ssbruno		case IWM_UCODE_TLV_N_SCAN_CHANNELS:
862303628Ssbruno			if (tlv_len != sizeof(uint32_t)) {
863303628Ssbruno				error = EINVAL;
864303628Ssbruno				goto parse_out;
865303628Ssbruno			}
866330196Seadler			capa->n_scan_channels =
867330784Seadler			    le32toh(*(const uint32_t *)tlv_data);
868303628Ssbruno			break;
869303628Ssbruno
870303628Ssbruno		case IWM_UCODE_TLV_FW_VERSION:
871303628Ssbruno			if (tlv_len != sizeof(uint32_t) * 3) {
872303628Ssbruno				error = EINVAL;
873303628Ssbruno				goto parse_out;
874303628Ssbruno			}
875303628Ssbruno			snprintf(sc->sc_fwver, sizeof(sc->sc_fwver),
876303628Ssbruno			    "%d.%d.%d",
877303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[0]),
878303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[1]),
879303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[2]));
880303628Ssbruno			break;
881303628Ssbruno
882330179Seadler		case IWM_UCODE_TLV_FW_MEM_SEG:
883330179Seadler			break;
884330179Seadler
885286441Srpaulo		default:
886286441Srpaulo			device_printf(sc->sc_dev,
887286441Srpaulo			    "%s: unknown firmware section %d, abort\n",
888286441Srpaulo			    __func__, tlv_type);
889286441Srpaulo			error = EINVAL;
890286441Srpaulo			goto parse_out;
891286441Srpaulo		}
892330784Seadler
893330784Seadler		len -= roundup(tlv_len, 4);
894330784Seadler		data += roundup(tlv_len, 4);
895286441Srpaulo	}
896286441Srpaulo
897286441Srpaulo	KASSERT(error == 0, ("unhandled error"));
898286441Srpaulo
899286441Srpaulo parse_out:
900286441Srpaulo	if (error) {
901286441Srpaulo		device_printf(sc->sc_dev, "firmware parse error %d, "
902286441Srpaulo		    "section type %d\n", error, tlv_type);
903286441Srpaulo	}
904286441Srpaulo
905286441Srpaulo out:
906286441Srpaulo	if (error) {
907286441Srpaulo		fw->fw_status = IWM_FW_STATUS_NONE;
908293177Savos		if (fw->fw_fp != NULL)
909286441Srpaulo			iwm_fw_info_free(fw);
910286441Srpaulo	} else
911286441Srpaulo		fw->fw_status = IWM_FW_STATUS_DONE;
912286441Srpaulo	wakeup(&sc->sc_fw);
913286441Srpaulo
914286441Srpaulo	return error;
915286441Srpaulo}
916286441Srpaulo
917286441Srpaulo/*
918286441Srpaulo * DMA resource routines
919286441Srpaulo */
920286441Srpaulo
921286441Srpaulo/* fwmem is used to load firmware onto the card */
922286441Srpaulostatic int
923286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc)
924286441Srpaulo{
925286441Srpaulo	/* Must be aligned on a 16-byte boundary. */
926286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma,
927330185Seadler	    IWM_FH_MEM_TB_MAX_LENGTH, 16);
928286441Srpaulo}
929286441Srpaulo
930286441Srpaulo/* tx scheduler rings.  not used? */
931286441Srpaulostatic int
932286441Srpauloiwm_alloc_sched(struct iwm_softc *sc)
933286441Srpaulo{
934286441Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
935303628Ssbruno	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma,
936286441Srpaulo	    nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024);
937286441Srpaulo}
938286441Srpaulo
939286441Srpaulo/* keep-warm page is used internally by the card.  see iwl-fh.h for more info */
940286441Srpaulostatic int
941286441Srpauloiwm_alloc_kw(struct iwm_softc *sc)
942286441Srpaulo{
943286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096);
944286441Srpaulo}
945286441Srpaulo
946286441Srpaulo/* interrupt cause table */
947286441Srpaulostatic int
948286441Srpauloiwm_alloc_ict(struct iwm_softc *sc)
949286441Srpaulo{
950286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma,
951286441Srpaulo	    IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT);
952286441Srpaulo}
953286441Srpaulo
954286441Srpaulostatic int
955286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
956286441Srpaulo{
957286441Srpaulo	bus_size_t size;
958286441Srpaulo	int i, error;
959286441Srpaulo
960286441Srpaulo	ring->cur = 0;
961286441Srpaulo
962286441Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
963286441Srpaulo	size = IWM_RX_RING_COUNT * sizeof(uint32_t);
964286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
965286441Srpaulo	if (error != 0) {
966286441Srpaulo		device_printf(sc->sc_dev,
967286441Srpaulo		    "could not allocate RX ring DMA memory\n");
968286441Srpaulo		goto fail;
969286441Srpaulo	}
970286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
971286441Srpaulo
972286441Srpaulo	/* Allocate RX status area (16-byte aligned). */
973286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma,
974286441Srpaulo	    sizeof(*ring->stat), 16);
975286441Srpaulo	if (error != 0) {
976286441Srpaulo		device_printf(sc->sc_dev,
977286441Srpaulo		    "could not allocate RX status DMA memory\n");
978286441Srpaulo		goto fail;
979286441Srpaulo	}
980286441Srpaulo	ring->stat = ring->stat_dma.vaddr;
981286441Srpaulo
982286441Srpaulo        /* Create RX buffer DMA tag. */
983286441Srpaulo        error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
984286441Srpaulo            BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
985289679Skevlo            IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat);
986286441Srpaulo        if (error != 0) {
987286441Srpaulo                device_printf(sc->sc_dev,
988286441Srpaulo                    "%s: could not create RX buf DMA tag, error %d\n",
989286441Srpaulo                    __func__, error);
990286441Srpaulo                goto fail;
991286441Srpaulo        }
992286441Srpaulo
993301845Sadrian	/* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */
994301845Sadrian	error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map);
995301845Sadrian	if (error != 0) {
996301845Sadrian		device_printf(sc->sc_dev,
997301845Sadrian		    "%s: could not create RX buf DMA map, error %d\n",
998301845Sadrian		    __func__, error);
999301845Sadrian		goto fail;
1000301845Sadrian	}
1001286441Srpaulo	/*
1002286441Srpaulo	 * Allocate and map RX buffers.
1003286441Srpaulo	 */
1004286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
1005301845Sadrian		struct iwm_rx_data *data = &ring->data[i];
1006301845Sadrian		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1007301845Sadrian		if (error != 0) {
1008301845Sadrian			device_printf(sc->sc_dev,
1009301845Sadrian			    "%s: could not create RX buf DMA map, error %d\n",
1010301845Sadrian			    __func__, error);
1011301845Sadrian			goto fail;
1012301845Sadrian		}
1013301845Sadrian		data->m = NULL;
1014301845Sadrian
1015286441Srpaulo		if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) {
1016286441Srpaulo			goto fail;
1017286441Srpaulo		}
1018286441Srpaulo	}
1019286441Srpaulo	return 0;
1020286441Srpaulo
1021286441Srpaulofail:	iwm_free_rx_ring(sc, ring);
1022286441Srpaulo	return error;
1023286441Srpaulo}
1024286441Srpaulo
1025286441Srpaulostatic void
1026301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1027301191Sadrian{
1028287965Sadrian	/* Reset the ring state */
1029286441Srpaulo	ring->cur = 0;
1030303628Ssbruno
1031303628Ssbruno	/*
1032303628Ssbruno	 * The hw rx ring index in shared memory must also be cleared,
1033303628Ssbruno	 * otherwise the discrepancy can cause reprocessing chaos.
1034303628Ssbruno	 */
1035346035Skevans	if (sc->rxq.stat)
1036346035Skevans		memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1037286441Srpaulo}
1038286441Srpaulo
1039286441Srpaulostatic void
1040286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1041286441Srpaulo{
1042286441Srpaulo	int i;
1043286441Srpaulo
1044286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1045286441Srpaulo	iwm_dma_contig_free(&ring->stat_dma);
1046286441Srpaulo
1047286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
1048286441Srpaulo		struct iwm_rx_data *data = &ring->data[i];
1049286441Srpaulo
1050286441Srpaulo		if (data->m != NULL) {
1051286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1052286441Srpaulo			    BUS_DMASYNC_POSTREAD);
1053286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1054286441Srpaulo			m_freem(data->m);
1055286441Srpaulo			data->m = NULL;
1056286441Srpaulo		}
1057286441Srpaulo		if (data->map != NULL) {
1058286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1059286441Srpaulo			data->map = NULL;
1060286441Srpaulo		}
1061286441Srpaulo	}
1062301845Sadrian	if (ring->spare_map != NULL) {
1063301845Sadrian		bus_dmamap_destroy(ring->data_dmat, ring->spare_map);
1064301845Sadrian		ring->spare_map = NULL;
1065301845Sadrian	}
1066286441Srpaulo	if (ring->data_dmat != NULL) {
1067286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1068286441Srpaulo		ring->data_dmat = NULL;
1069286441Srpaulo	}
1070286441Srpaulo}
1071286441Srpaulo
1072286441Srpaulostatic int
1073286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
1074286441Srpaulo{
1075286441Srpaulo	bus_addr_t paddr;
1076286441Srpaulo	bus_size_t size;
1077302104Sadrian	size_t maxsize;
1078302104Sadrian	int nsegments;
1079286441Srpaulo	int i, error;
1080286441Srpaulo
1081286441Srpaulo	ring->qid = qid;
1082286441Srpaulo	ring->queued = 0;
1083286441Srpaulo	ring->cur = 0;
1084286441Srpaulo
1085286441Srpaulo	/* Allocate TX descriptors (256-byte aligned). */
1086286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd);
1087286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
1088286441Srpaulo	if (error != 0) {
1089286441Srpaulo		device_printf(sc->sc_dev,
1090286441Srpaulo		    "could not allocate TX ring DMA memory\n");
1091286441Srpaulo		goto fail;
1092286441Srpaulo	}
1093286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
1094286441Srpaulo
1095286441Srpaulo	/*
1096286441Srpaulo	 * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need
1097286441Srpaulo	 * to allocate commands space for other rings.
1098286441Srpaulo	 */
1099286441Srpaulo	if (qid > IWM_MVM_CMD_QUEUE)
1100286441Srpaulo		return 0;
1101286441Srpaulo
1102286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
1103286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4);
1104286441Srpaulo	if (error != 0) {
1105286441Srpaulo		device_printf(sc->sc_dev,
1106286441Srpaulo		    "could not allocate TX cmd DMA memory\n");
1107286441Srpaulo		goto fail;
1108286441Srpaulo	}
1109286441Srpaulo	ring->cmd = ring->cmd_dma.vaddr;
1110286441Srpaulo
1111302104Sadrian	/* FW commands may require more mapped space than packets. */
1112302104Sadrian	if (qid == IWM_MVM_CMD_QUEUE) {
1113302104Sadrian		maxsize = IWM_RBUF_SIZE;
1114302104Sadrian		nsegments = 1;
1115302104Sadrian	} else {
1116302104Sadrian		maxsize = MCLBYTES;
1117302104Sadrian		nsegments = IWM_MAX_SCATTER - 2;
1118302104Sadrian	}
1119302104Sadrian
1120286441Srpaulo	error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
1121302104Sadrian	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize,
1122302104Sadrian            nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat);
1123286441Srpaulo	if (error != 0) {
1124286441Srpaulo		device_printf(sc->sc_dev, "could not create TX buf DMA tag\n");
1125286441Srpaulo		goto fail;
1126286441Srpaulo	}
1127286441Srpaulo
1128286441Srpaulo	paddr = ring->cmd_dma.paddr;
1129286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1130286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1131286441Srpaulo
1132286441Srpaulo		data->cmd_paddr = paddr;
1133286441Srpaulo		data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header)
1134286441Srpaulo		    + offsetof(struct iwm_tx_cmd, scratch);
1135286441Srpaulo		paddr += sizeof(struct iwm_device_cmd);
1136286441Srpaulo
1137286441Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1138286441Srpaulo		if (error != 0) {
1139286441Srpaulo			device_printf(sc->sc_dev,
1140286441Srpaulo			    "could not create TX buf DMA map\n");
1141286441Srpaulo			goto fail;
1142286441Srpaulo		}
1143286441Srpaulo	}
1144286441Srpaulo	KASSERT(paddr == ring->cmd_dma.paddr + size,
1145286441Srpaulo	    ("invalid physical address"));
1146286441Srpaulo	return 0;
1147286441Srpaulo
1148286441Srpaulofail:	iwm_free_tx_ring(sc, ring);
1149286441Srpaulo	return error;
1150286441Srpaulo}
1151286441Srpaulo
1152286441Srpaulostatic void
1153286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1154286441Srpaulo{
1155286441Srpaulo	int i;
1156286441Srpaulo
1157286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1158286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1159286441Srpaulo
1160286441Srpaulo		if (data->m != NULL) {
1161286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1162286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1163286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1164286441Srpaulo			m_freem(data->m);
1165286441Srpaulo			data->m = NULL;
1166286441Srpaulo		}
1167286441Srpaulo	}
1168286441Srpaulo	/* Clear TX descriptors. */
1169286441Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1170286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1171286441Srpaulo	    BUS_DMASYNC_PREWRITE);
1172286441Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1173286441Srpaulo	ring->queued = 0;
1174286441Srpaulo	ring->cur = 0;
1175330175Seadler
1176330175Seadler	if (ring->qid == IWM_MVM_CMD_QUEUE && sc->cmd_hold_nic_awake)
1177330175Seadler		iwm_pcie_clear_cmd_in_flight(sc);
1178286441Srpaulo}
1179286441Srpaulo
1180286441Srpaulostatic void
1181286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1182286441Srpaulo{
1183286441Srpaulo	int i;
1184286441Srpaulo
1185286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1186286441Srpaulo	iwm_dma_contig_free(&ring->cmd_dma);
1187286441Srpaulo
1188286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1189286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1190286441Srpaulo
1191286441Srpaulo		if (data->m != NULL) {
1192286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1193286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1194286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1195286441Srpaulo			m_freem(data->m);
1196286441Srpaulo			data->m = NULL;
1197286441Srpaulo		}
1198286441Srpaulo		if (data->map != NULL) {
1199286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1200286441Srpaulo			data->map = NULL;
1201286441Srpaulo		}
1202286441Srpaulo	}
1203286441Srpaulo	if (ring->data_dmat != NULL) {
1204286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1205286441Srpaulo		ring->data_dmat = NULL;
1206286441Srpaulo	}
1207286441Srpaulo}
1208286441Srpaulo
1209286441Srpaulo/*
1210286441Srpaulo * High-level hardware frobbing routines
1211286441Srpaulo */
1212286441Srpaulo
1213286441Srpaulostatic void
1214286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc)
1215286441Srpaulo{
1216286441Srpaulo	sc->sc_intmask = IWM_CSR_INI_SET_MASK;
1217286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1218286441Srpaulo}
1219286441Srpaulo
1220286441Srpaulostatic void
1221286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc)
1222286441Srpaulo{
1223286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1224286441Srpaulo}
1225286441Srpaulo
1226286441Srpaulostatic void
1227286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc)
1228286441Srpaulo{
1229286441Srpaulo	/* disable interrupts */
1230286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
1231286441Srpaulo
1232286441Srpaulo	/* acknowledge all interrupts */
1233286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1234286441Srpaulo	IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
1235286441Srpaulo}
1236286441Srpaulo
1237286441Srpaulostatic void
1238286441Srpauloiwm_ict_reset(struct iwm_softc *sc)
1239286441Srpaulo{
1240286441Srpaulo	iwm_disable_interrupts(sc);
1241286441Srpaulo
1242286441Srpaulo	/* Reset ICT table. */
1243286441Srpaulo	memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE);
1244286441Srpaulo	sc->ict_cur = 0;
1245286441Srpaulo
1246286441Srpaulo	/* Set physical address of ICT table (4KB aligned). */
1247286441Srpaulo	IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG,
1248286441Srpaulo	    IWM_CSR_DRAM_INT_TBL_ENABLE
1249303628Ssbruno	    | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER
1250286441Srpaulo	    | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK
1251286441Srpaulo	    | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT);
1252286441Srpaulo
1253286441Srpaulo	/* Switch to ICT interrupt mode in driver. */
1254286441Srpaulo	sc->sc_flags |= IWM_FLAG_USE_ICT;
1255286441Srpaulo
1256286441Srpaulo	/* Re-enable interrupts. */
1257286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1258286441Srpaulo	iwm_enable_interrupts(sc);
1259286441Srpaulo}
1260286441Srpaulo
1261286441Srpaulo/* iwlwifi pcie/trans.c */
1262286441Srpaulo
1263286441Srpaulo/*
1264286441Srpaulo * Since this .. hard-resets things, it's time to actually
1265286441Srpaulo * mark the first vap (if any) as having no mac context.
1266286441Srpaulo * It's annoying, but since the driver is potentially being
1267286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we
1268286441Srpaulo * have to correctly track this.
1269286441Srpaulo */
1270286441Srpaulostatic void
1271286441Srpauloiwm_stop_device(struct iwm_softc *sc)
1272286441Srpaulo{
1273287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1274286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1275303628Ssbruno	int chnl, qid;
1276303628Ssbruno	uint32_t mask = 0;
1277286441Srpaulo
1278286441Srpaulo	/* tell the device to stop sending interrupts */
1279286441Srpaulo	iwm_disable_interrupts(sc);
1280286441Srpaulo
1281286441Srpaulo	/*
1282286441Srpaulo	 * FreeBSD-local: mark the first vap as not-uploaded,
1283286441Srpaulo	 * so the next transition through auth/assoc
1284286441Srpaulo	 * will correctly populate the MAC context.
1285286441Srpaulo	 */
1286286441Srpaulo	if (vap) {
1287286441Srpaulo		struct iwm_vap *iv = IWM_VAP(vap);
1288330203Seadler		iv->phy_ctxt = NULL;
1289286441Srpaulo		iv->is_uploaded = 0;
1290286441Srpaulo	}
1291286441Srpaulo
1292286441Srpaulo	/* device going down, Stop using ICT table */
1293286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_USE_ICT;
1294286441Srpaulo
1295286441Srpaulo	/* stop tx and rx.  tx and rx bits, as usual, are from if_iwn */
1296286441Srpaulo
1297330199Seadler	if (iwm_nic_lock(sc)) {
1298330199Seadler		iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1299286441Srpaulo
1300303628Ssbruno		/* Stop each Tx DMA channel */
1301286441Srpaulo		for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1302286441Srpaulo			IWM_WRITE(sc,
1303286441Srpaulo			    IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0);
1304303628Ssbruno			mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl);
1305303628Ssbruno		}
1306286441Srpaulo
1307303628Ssbruno		/* Wait for DMA channels to be idle */
1308303628Ssbruno		if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask,
1309303628Ssbruno		    5000)) {
1310303628Ssbruno			device_printf(sc->sc_dev,
1311303628Ssbruno			    "Failing on timeout while stopping DMA channel: [0x%08x]\n",
1312303628Ssbruno			    IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG));
1313286441Srpaulo		}
1314286441Srpaulo		iwm_nic_unlock(sc);
1315286441Srpaulo	}
1316330172Seadler	iwm_pcie_rx_stop(sc);
1317286441Srpaulo
1318286441Srpaulo	/* Stop RX ring. */
1319286441Srpaulo	iwm_reset_rx_ring(sc, &sc->rxq);
1320286441Srpaulo
1321286441Srpaulo	/* Reset all TX rings. */
1322286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++)
1323286441Srpaulo		iwm_reset_tx_ring(sc, &sc->txq[qid]);
1324286441Srpaulo
1325330176Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
1326330176Seadler		/* Power-down device's busmaster DMA clocks */
1327330199Seadler		if (iwm_nic_lock(sc)) {
1328330199Seadler			iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG,
1329330199Seadler			    IWM_APMG_CLK_VAL_DMA_CLK_RQT);
1330330200Seadler			iwm_nic_unlock(sc);
1331330199Seadler		}
1332330176Seadler		DELAY(5);
1333330176Seadler	}
1334286441Srpaulo
1335286441Srpaulo	/* Make sure (redundant) we've released our request to stay awake */
1336286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
1337286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1338286441Srpaulo
1339286441Srpaulo	/* Stop the device, and put it in low power state */
1340286441Srpaulo	iwm_apm_stop(sc);
1341286441Srpaulo
1342286441Srpaulo	/* Upon stop, the APM issues an interrupt if HW RF kill is set.
1343286441Srpaulo	 * Clean again the interrupt here
1344286441Srpaulo	 */
1345286441Srpaulo	iwm_disable_interrupts(sc);
1346286441Srpaulo	/* stop and reset the on-board processor */
1347303628Ssbruno	IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET);
1348286441Srpaulo
1349286441Srpaulo	/*
1350286441Srpaulo	 * Even if we stop the HW, we still want the RF kill
1351286441Srpaulo	 * interrupt
1352286441Srpaulo	 */
1353286441Srpaulo	iwm_enable_rfkill_int(sc);
1354286441Srpaulo	iwm_check_rfkill(sc);
1355286441Srpaulo}
1356286441Srpaulo
1357286441Srpaulo/* iwlwifi: mvm/ops.c */
1358286441Srpaulostatic void
1359286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc)
1360286441Srpaulo{
1361286441Srpaulo	uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash;
1362286441Srpaulo	uint32_t reg_val = 0;
1363330167Seadler	uint32_t phy_config = iwm_mvm_get_phy_config(sc);
1364286441Srpaulo
1365330167Seadler	radio_cfg_type = (phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >>
1366286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_TYPE_POS;
1367330167Seadler	radio_cfg_step = (phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >>
1368286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_STEP_POS;
1369330167Seadler	radio_cfg_dash = (phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >>
1370286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_DASH_POS;
1371286441Srpaulo
1372286441Srpaulo	/* SKU control */
1373286441Srpaulo	reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) <<
1374286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
1375286441Srpaulo	reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) <<
1376286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
1377286441Srpaulo
1378286441Srpaulo	/* radio configuration */
1379286441Srpaulo	reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
1380286441Srpaulo	reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
1381286441Srpaulo	reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
1382286441Srpaulo
1383286441Srpaulo	IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val);
1384286441Srpaulo
1385286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1386286441Srpaulo	    "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
1387286441Srpaulo	    radio_cfg_step, radio_cfg_dash);
1388286441Srpaulo
1389286441Srpaulo	/*
1390286441Srpaulo	 * W/A : NIC is stuck in a reset state after Early PCIe power off
1391286441Srpaulo	 * (PCIe power is lost before PERST# is asserted), causing ME FW
1392286441Srpaulo	 * to lose ownership and not being able to obtain it back.
1393286441Srpaulo	 */
1394330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
1395303628Ssbruno		iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG,
1396303628Ssbruno		    IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
1397303628Ssbruno		    ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
1398303628Ssbruno	}
1399286441Srpaulo}
1400286441Srpaulo
1401286441Srpaulostatic int
1402286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc)
1403286441Srpaulo{
1404286441Srpaulo	/*
1405286441Srpaulo	 * Initialize RX ring.  This is from the iwn driver.
1406286441Srpaulo	 */
1407286441Srpaulo	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1408286441Srpaulo
1409330172Seadler	/* Stop Rx DMA */
1410330172Seadler	iwm_pcie_rx_stop(sc);
1411330172Seadler
1412330172Seadler	if (!iwm_nic_lock(sc))
1413330172Seadler		return EBUSY;
1414330172Seadler
1415330172Seadler	/* reset and flush pointers */
1416286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
1417286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
1418286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0);
1419286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
1420286441Srpaulo
1421286441Srpaulo	/* Set physical address of RX ring (256-byte aligned). */
1422286441Srpaulo	IWM_WRITE(sc,
1423286441Srpaulo	    IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8);
1424286441Srpaulo
1425286441Srpaulo	/* Set physical address of RX status (16-byte aligned). */
1426286441Srpaulo	IWM_WRITE(sc,
1427286441Srpaulo	    IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4);
1428286441Srpaulo
1429330208Seadler	/* Enable Rx DMA
1430330208Seadler	 * XXX 5000 HW isn't supported by the iwm(4) driver.
1431330208Seadler	 * IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
1432330208Seadler	 *      the credit mechanism in 5000 HW RX FIFO
1433330208Seadler	 * Direct rx interrupts to hosts
1434330208Seadler	 * Rx buffer size 4 or 8k or 12k
1435330208Seadler	 * RB timeout 0x10
1436330208Seadler	 * 256 RBDs
1437330208Seadler	 */
1438286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG,
1439286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL		|
1440286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY		|  /* HW bug */
1441286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL	|
1442330208Seadler	    IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K		|
1443303628Ssbruno	    (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
1444286441Srpaulo	    IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
1445286441Srpaulo
1446286441Srpaulo	IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF);
1447286441Srpaulo
1448286441Srpaulo	/* W/A for interrupt coalescing bug in 7260 and 3160 */
1449330166Seadler	if (sc->cfg->host_interrupt_operation_mode)
1450286441Srpaulo		IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE);
1451286441Srpaulo
1452286441Srpaulo	/*
1453286441Srpaulo	 * Thus sayeth el jefe (iwlwifi) via a comment:
1454286441Srpaulo	 *
1455286441Srpaulo	 * This value should initially be 0 (before preparing any
1456303628Ssbruno	 * RBs), should be 8 after preparing the first 8 RBs (for example)
1457286441Srpaulo	 */
1458286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8);
1459286441Srpaulo
1460286441Srpaulo	iwm_nic_unlock(sc);
1461286441Srpaulo
1462286441Srpaulo	return 0;
1463286441Srpaulo}
1464286441Srpaulo
1465286441Srpaulostatic int
1466286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc)
1467286441Srpaulo{
1468286441Srpaulo	int qid;
1469286441Srpaulo
1470286441Srpaulo	if (!iwm_nic_lock(sc))
1471286441Srpaulo		return EBUSY;
1472286441Srpaulo
1473286441Srpaulo	/* Deactivate TX scheduler. */
1474286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1475286441Srpaulo
1476286441Srpaulo	/* Set physical address of "keep warm" page (16-byte aligned). */
1477286441Srpaulo	IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4);
1478286441Srpaulo
1479286441Srpaulo	/* Initialize TX rings. */
1480286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++) {
1481286441Srpaulo		struct iwm_tx_ring *txq = &sc->txq[qid];
1482286441Srpaulo
1483286441Srpaulo		/* Set physical address of TX ring (256-byte aligned). */
1484286441Srpaulo		IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid),
1485286441Srpaulo		    txq->desc_dma.paddr >> 8);
1486286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
1487286441Srpaulo		    "%s: loading ring %d descriptors (%p) at %lx\n",
1488286441Srpaulo		    __func__,
1489286441Srpaulo		    qid, txq->desc,
1490286441Srpaulo		    (unsigned long) (txq->desc_dma.paddr >> 8));
1491286441Srpaulo	}
1492303628Ssbruno
1493303628Ssbruno	iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE);
1494303628Ssbruno
1495286441Srpaulo	iwm_nic_unlock(sc);
1496286441Srpaulo
1497286441Srpaulo	return 0;
1498286441Srpaulo}
1499286441Srpaulo
1500286441Srpaulostatic int
1501286441Srpauloiwm_nic_init(struct iwm_softc *sc)
1502286441Srpaulo{
1503286441Srpaulo	int error;
1504286441Srpaulo
1505286441Srpaulo	iwm_apm_init(sc);
1506330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
1507303628Ssbruno		iwm_set_pwr(sc);
1508286441Srpaulo
1509286441Srpaulo	iwm_mvm_nic_config(sc);
1510286441Srpaulo
1511286441Srpaulo	if ((error = iwm_nic_rx_init(sc)) != 0)
1512286441Srpaulo		return error;
1513286441Srpaulo
1514286441Srpaulo	/*
1515286441Srpaulo	 * Ditto for TX, from iwn
1516286441Srpaulo	 */
1517286441Srpaulo	if ((error = iwm_nic_tx_init(sc)) != 0)
1518286441Srpaulo		return error;
1519286441Srpaulo
1520286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1521286441Srpaulo	    "%s: shadow registers enabled\n", __func__);
1522286441Srpaulo	IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff);
1523286441Srpaulo
1524286441Srpaulo	return 0;
1525286441Srpaulo}
1526286441Srpaulo
1527330210Seadlerint
1528303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
1529286441Srpaulo{
1530286441Srpaulo	if (!iwm_nic_lock(sc)) {
1531286441Srpaulo		device_printf(sc->sc_dev,
1532286441Srpaulo		    "%s: cannot enable txq %d\n",
1533286441Srpaulo		    __func__,
1534286441Srpaulo		    qid);
1535303628Ssbruno		return EBUSY;
1536286441Srpaulo	}
1537286441Srpaulo
1538303628Ssbruno	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
1539286441Srpaulo
1540303628Ssbruno	if (qid == IWM_MVM_CMD_QUEUE) {
1541303628Ssbruno		/* unactivate before configuration */
1542303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1543303628Ssbruno		    (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE)
1544303628Ssbruno		    | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
1545286441Srpaulo
1546330183Seadler		iwm_nic_unlock(sc);
1547330183Seadler
1548303628Ssbruno		iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid));
1549286441Srpaulo
1550330183Seadler		if (!iwm_nic_lock(sc)) {
1551330183Seadler			device_printf(sc->sc_dev,
1552330183Seadler			    "%s: cannot enable txq %d\n", __func__, qid);
1553330183Seadler			return EBUSY;
1554330183Seadler		}
1555303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0);
1556330183Seadler		iwm_nic_unlock(sc);
1557286441Srpaulo
1558330183Seadler		iwm_write_mem32(sc, sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0);
1559303628Ssbruno		/* Set scheduler window size and frame limit. */
1560303628Ssbruno		iwm_write_mem32(sc,
1561330183Seadler		    sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) +
1562303628Ssbruno		    sizeof(uint32_t),
1563303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
1564303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
1565303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
1566303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
1567286441Srpaulo
1568330183Seadler		if (!iwm_nic_lock(sc)) {
1569330183Seadler			device_printf(sc->sc_dev,
1570330183Seadler			    "%s: cannot enable txq %d\n", __func__, qid);
1571330183Seadler			return EBUSY;
1572330183Seadler		}
1573303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1574303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
1575303628Ssbruno		    (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) |
1576303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) |
1577303628Ssbruno		    IWM_SCD_QUEUE_STTS_REG_MSK);
1578303628Ssbruno	} else {
1579303628Ssbruno		struct iwm_scd_txq_cfg_cmd cmd;
1580303628Ssbruno		int error;
1581286441Srpaulo
1582303628Ssbruno		iwm_nic_unlock(sc);
1583303628Ssbruno
1584303628Ssbruno		memset(&cmd, 0, sizeof(cmd));
1585303628Ssbruno		cmd.scd_queue = qid;
1586303628Ssbruno		cmd.enable = 1;
1587303628Ssbruno		cmd.sta_id = sta_id;
1588303628Ssbruno		cmd.tx_fifo = fifo;
1589303628Ssbruno		cmd.aggregate = 0;
1590303628Ssbruno		cmd.window = IWM_FRAME_LIMIT;
1591303628Ssbruno
1592303628Ssbruno		error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC,
1593303628Ssbruno		    sizeof(cmd), &cmd);
1594303628Ssbruno		if (error) {
1595303628Ssbruno			device_printf(sc->sc_dev,
1596303628Ssbruno			    "cannot enable txq %d\n", qid);
1597303628Ssbruno			return error;
1598303628Ssbruno		}
1599303628Ssbruno
1600303628Ssbruno		if (!iwm_nic_lock(sc))
1601303628Ssbruno			return EBUSY;
1602303628Ssbruno	}
1603303628Ssbruno
1604303628Ssbruno	iwm_write_prph(sc, IWM_SCD_EN_CTRL,
1605303628Ssbruno	    iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid);
1606303628Ssbruno
1607286441Srpaulo	iwm_nic_unlock(sc);
1608286441Srpaulo
1609303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n",
1610286441Srpaulo	    __func__, qid, fifo);
1611303628Ssbruno
1612303628Ssbruno	return 0;
1613286441Srpaulo}
1614286441Srpaulo
1615286441Srpaulostatic int
1616330183Seadleriwm_trans_pcie_fw_alive(struct iwm_softc *sc, uint32_t scd_base_addr)
1617286441Srpaulo{
1618286441Srpaulo	int error, chnl;
1619286441Srpaulo
1620330183Seadler	int clear_dwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND -
1621330183Seadler	    IWM_SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(uint32_t);
1622330183Seadler
1623286441Srpaulo	if (!iwm_nic_lock(sc))
1624286441Srpaulo		return EBUSY;
1625286441Srpaulo
1626330183Seadler	iwm_ict_reset(sc);
1627330183Seadler
1628330183Seadler	sc->scd_base_addr = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR);
1629330183Seadler	if (scd_base_addr != 0 &&
1630330183Seadler	    scd_base_addr != sc->scd_base_addr) {
1631286441Srpaulo		device_printf(sc->sc_dev,
1632303628Ssbruno		    "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n",
1633330183Seadler		    __func__, sc->scd_base_addr, scd_base_addr);
1634286441Srpaulo	}
1635286441Srpaulo
1636330199Seadler	iwm_nic_unlock(sc);
1637330199Seadler
1638330183Seadler	/* reset context data, TX status and translation data */
1639286441Srpaulo	error = iwm_write_mem(sc,
1640330183Seadler	    sc->scd_base_addr + IWM_SCD_CONTEXT_MEM_LOWER_BOUND,
1641330183Seadler	    NULL, clear_dwords);
1642286441Srpaulo	if (error)
1643330183Seadler		return EBUSY;
1644286441Srpaulo
1645330183Seadler	if (!iwm_nic_lock(sc))
1646330183Seadler		return EBUSY;
1647330183Seadler
1648286441Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned). */
1649286441Srpaulo	iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10);
1650286441Srpaulo
1651286441Srpaulo	iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0);
1652286441Srpaulo
1653303628Ssbruno	iwm_nic_unlock(sc);
1654303628Ssbruno
1655286441Srpaulo	/* enable command channel */
1656303628Ssbruno	error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7);
1657303628Ssbruno	if (error)
1658303628Ssbruno		return error;
1659286441Srpaulo
1660303628Ssbruno	if (!iwm_nic_lock(sc))
1661303628Ssbruno		return EBUSY;
1662303628Ssbruno
1663286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff);
1664286441Srpaulo
1665286441Srpaulo	/* Enable DMA channels. */
1666286441Srpaulo	for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1667286441Srpaulo		IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl),
1668286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
1669286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
1670286441Srpaulo	}
1671286441Srpaulo
1672286441Srpaulo	IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG,
1673286441Srpaulo	    IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
1674286441Srpaulo
1675330183Seadler	iwm_nic_unlock(sc);
1676330183Seadler
1677286441Srpaulo	/* Enable L1-Active */
1678330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
1679303628Ssbruno		iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG,
1680303628Ssbruno		    IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
1681303628Ssbruno	}
1682286441Srpaulo
1683286441Srpaulo	return error;
1684286441Srpaulo}
1685286441Srpaulo
1686286441Srpaulo/*
1687286441Srpaulo * NVM read access and content parsing.  We do not support
1688286441Srpaulo * external NVM or writing NVM.
1689286441Srpaulo * iwlwifi/mvm/nvm.c
1690286441Srpaulo */
1691286441Srpaulo
1692286441Srpaulo/* Default NVM size to read */
1693303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE	(2*1024)
1694286441Srpaulo
1695286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1
1696286441Srpaulo#define IWM_NVM_READ_OPCODE 0
1697286441Srpaulo
1698303628Ssbruno/* load nvm chunk response */
1699330160Seadlerenum {
1700330160Seadler	IWM_READ_NVM_CHUNK_SUCCEED = 0,
1701330160Seadler	IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
1702330160Seadler};
1703303628Ssbruno
1704286441Srpaulostatic int
1705286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section,
1706286441Srpaulo	uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len)
1707286441Srpaulo{
1708286441Srpaulo	struct iwm_nvm_access_cmd nvm_access_cmd = {
1709286441Srpaulo		.offset = htole16(offset),
1710286441Srpaulo		.length = htole16(length),
1711286441Srpaulo		.type = htole16(section),
1712286441Srpaulo		.op_code = IWM_NVM_READ_OPCODE,
1713286441Srpaulo	};
1714286441Srpaulo	struct iwm_nvm_access_resp *nvm_resp;
1715286441Srpaulo	struct iwm_rx_packet *pkt;
1716286441Srpaulo	struct iwm_host_cmd cmd = {
1717286441Srpaulo		.id = IWM_NVM_ACCESS_CMD,
1718330160Seadler		.flags = IWM_CMD_WANT_SKB | IWM_CMD_SEND_IN_RFKILL,
1719286441Srpaulo		.data = { &nvm_access_cmd, },
1720286441Srpaulo	};
1721330160Seadler	int ret, bytes_read, offset_read;
1722286441Srpaulo	uint8_t *resp_data;
1723286441Srpaulo
1724286441Srpaulo	cmd.len[0] = sizeof(struct iwm_nvm_access_cmd);
1725286441Srpaulo
1726286441Srpaulo	ret = iwm_send_cmd(sc, &cmd);
1727303628Ssbruno	if (ret) {
1728303628Ssbruno		device_printf(sc->sc_dev,
1729303628Ssbruno		    "Could not send NVM_ACCESS command (error=%d)\n", ret);
1730286441Srpaulo		return ret;
1731303628Ssbruno	}
1732286441Srpaulo
1733286441Srpaulo	pkt = cmd.resp_pkt;
1734286441Srpaulo
1735286441Srpaulo	/* Extract NVM response */
1736286441Srpaulo	nvm_resp = (void *)pkt->data;
1737286441Srpaulo	ret = le16toh(nvm_resp->status);
1738286441Srpaulo	bytes_read = le16toh(nvm_resp->length);
1739286441Srpaulo	offset_read = le16toh(nvm_resp->offset);
1740286441Srpaulo	resp_data = nvm_resp->data;
1741286441Srpaulo	if (ret) {
1742330160Seadler		if ((offset != 0) &&
1743330160Seadler		    (ret == IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
1744330160Seadler			/*
1745330160Seadler			 * meaning of NOT_VALID_ADDRESS:
1746330160Seadler			 * driver try to read chunk from address that is
1747330160Seadler			 * multiple of 2K and got an error since addr is empty.
1748330160Seadler			 * meaning of (offset != 0): driver already
1749330160Seadler			 * read valid data from another chunk so this case
1750330160Seadler			 * is not an error.
1751330160Seadler			 */
1752330160Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1753330160Seadler				    "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
1754330160Seadler				    offset);
1755330160Seadler			*len = 0;
1756330160Seadler			ret = 0;
1757330160Seadler		} else {
1758330160Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1759330160Seadler				    "NVM access command failed with status %d\n", ret);
1760330160Seadler			ret = EIO;
1761330160Seadler		}
1762286441Srpaulo		goto exit;
1763286441Srpaulo	}
1764286441Srpaulo
1765286441Srpaulo	if (offset_read != offset) {
1766286441Srpaulo		device_printf(sc->sc_dev,
1767303628Ssbruno		    "NVM ACCESS response with invalid offset %d\n",
1768303628Ssbruno		    offset_read);
1769286441Srpaulo		ret = EINVAL;
1770286441Srpaulo		goto exit;
1771286441Srpaulo	}
1772286441Srpaulo
1773303628Ssbruno	if (bytes_read > length) {
1774303628Ssbruno		device_printf(sc->sc_dev,
1775303628Ssbruno		    "NVM ACCESS response with too much data "
1776330160Seadler		    "(%d bytes requested, %d bytes received)\n",
1777303628Ssbruno		    length, bytes_read);
1778303628Ssbruno		ret = EINVAL;
1779303628Ssbruno		goto exit;
1780303628Ssbruno	}
1781303628Ssbruno
1782330165Seadler	/* Write data to NVM */
1783286441Srpaulo	memcpy(data + offset, resp_data, bytes_read);
1784286441Srpaulo	*len = bytes_read;
1785286441Srpaulo
1786286441Srpaulo exit:
1787286441Srpaulo	iwm_free_resp(sc, &cmd);
1788286441Srpaulo	return ret;
1789286441Srpaulo}
1790286441Srpaulo
1791286441Srpaulo/*
1792286441Srpaulo * Reads an NVM section completely.
1793303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read
1794286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
1795286441Srpaulo * by uCode, we need to manually check in this case that we don't
1796286441Srpaulo * overflow and try to read more than the EEPROM size.
1797286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and
1798286441Srpaulo * the uCode fills the response with as much data as we can,
1799286441Srpaulo * without overflowing, so no check is needed.
1800286441Srpaulo */
1801286441Srpaulostatic int
1802286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc,
1803330165Seadler	uint16_t section, uint8_t *data, uint16_t *len, uint32_t size_read)
1804286441Srpaulo{
1805330165Seadler	uint16_t seglen, length, offset = 0;
1806330165Seadler	int ret;
1807286441Srpaulo
1808330165Seadler	/* Set nvm section read length */
1809330165Seadler	length = IWM_NVM_DEFAULT_CHUNK_SIZE;
1810303628Ssbruno
1811330165Seadler	seglen = length;
1812286441Srpaulo
1813330165Seadler	/* Read the NVM until exhausted (reading less than requested) */
1814330165Seadler	while (seglen == length) {
1815330165Seadler		/* Check no memory assumptions fail and cause an overflow */
1816330165Seadler		if ((size_read + offset + length) >
1817330166Seadler		    sc->cfg->eeprom_size) {
1818330165Seadler			device_printf(sc->sc_dev,
1819330165Seadler			    "EEPROM size is too small for NVM\n");
1820330165Seadler			return ENOBUFS;
1821286441Srpaulo		}
1822330165Seadler
1823330165Seadler		ret = iwm_nvm_read_chunk(sc, section, offset, length, data, &seglen);
1824330165Seadler		if (ret) {
1825330165Seadler			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1826330165Seadler				    "Cannot read NVM from section %d offset %d, length %d\n",
1827330165Seadler				    section, offset, length);
1828330165Seadler			return ret;
1829330165Seadler		}
1830330165Seadler		offset += seglen;
1831286441Srpaulo	}
1832286441Srpaulo
1833330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
1834330165Seadler		    "NVM section %d read completed\n", section);
1835330165Seadler	*len = offset;
1836330165Seadler	return 0;
1837286441Srpaulo}
1838286441Srpaulo
1839286441Srpaulo/*
1840286441Srpaulo * BEGIN IWM_NVM_PARSE
1841286441Srpaulo */
1842286441Srpaulo
1843286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */
1844286441Srpaulo
1845286441Srpaulo/* NVM offsets (in words) definitions */
1846303628Ssbrunoenum iwm_nvm_offsets {
1847286441Srpaulo	/* NVM HW-Section offset (in words) definitions */
1848286441Srpaulo	IWM_HW_ADDR = 0x15,
1849286441Srpaulo
1850286441Srpaulo/* NVM SW-Section offset (in words) definitions */
1851286441Srpaulo	IWM_NVM_SW_SECTION = 0x1C0,
1852286441Srpaulo	IWM_NVM_VERSION = 0,
1853286441Srpaulo	IWM_RADIO_CFG = 1,
1854286441Srpaulo	IWM_SKU = 2,
1855286441Srpaulo	IWM_N_HW_ADDRS = 3,
1856286441Srpaulo	IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION,
1857286441Srpaulo
1858286441Srpaulo/* NVM calibration section offset (in words) definitions */
1859286441Srpaulo	IWM_NVM_CALIB_SECTION = 0x2B8,
1860286441Srpaulo	IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION
1861286441Srpaulo};
1862286441Srpaulo
1863303628Ssbrunoenum iwm_8000_nvm_offsets {
1864303628Ssbruno	/* NVM HW-Section offset (in words) definitions */
1865303628Ssbruno	IWM_HW_ADDR0_WFPM_8000 = 0x12,
1866303628Ssbruno	IWM_HW_ADDR1_WFPM_8000 = 0x16,
1867303628Ssbruno	IWM_HW_ADDR0_PCIE_8000 = 0x8A,
1868303628Ssbruno	IWM_HW_ADDR1_PCIE_8000 = 0x8E,
1869303628Ssbruno	IWM_MAC_ADDRESS_OVERRIDE_8000 = 1,
1870303628Ssbruno
1871303628Ssbruno	/* NVM SW-Section offset (in words) definitions */
1872303628Ssbruno	IWM_NVM_SW_SECTION_8000 = 0x1C0,
1873303628Ssbruno	IWM_NVM_VERSION_8000 = 0,
1874303628Ssbruno	IWM_RADIO_CFG_8000 = 0,
1875303628Ssbruno	IWM_SKU_8000 = 2,
1876303628Ssbruno	IWM_N_HW_ADDRS_8000 = 3,
1877303628Ssbruno
1878303628Ssbruno	/* NVM REGULATORY -Section offset (in words) definitions */
1879303628Ssbruno	IWM_NVM_CHANNELS_8000 = 0,
1880303628Ssbruno	IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7,
1881303628Ssbruno	IWM_NVM_LAR_OFFSET_8000 = 0x507,
1882303628Ssbruno	IWM_NVM_LAR_ENABLED_8000 = 0x7,
1883303628Ssbruno
1884303628Ssbruno	/* NVM calibration section offset (in words) definitions */
1885303628Ssbruno	IWM_NVM_CALIB_SECTION_8000 = 0x2B8,
1886303628Ssbruno	IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000
1887303628Ssbruno};
1888303628Ssbruno
1889286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */
1890286441Srpauloenum nvm_sku_bits {
1891286441Srpaulo	IWM_NVM_SKU_CAP_BAND_24GHZ	= (1 << 0),
1892286441Srpaulo	IWM_NVM_SKU_CAP_BAND_52GHZ	= (1 << 1),
1893286441Srpaulo	IWM_NVM_SKU_CAP_11N_ENABLE	= (1 << 2),
1894286441Srpaulo	IWM_NVM_SKU_CAP_11AC_ENABLE	= (1 << 3),
1895286441Srpaulo};
1896286441Srpaulo
1897286441Srpaulo/* radio config bits (actual values from NVM definition) */
1898286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
1899286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
1900286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
1901286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
1902286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
1903286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
1904286441Srpaulo
1905303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x)	(x & 0xF)
1906303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x)		((x >> 4) & 0xF)
1907303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x)		((x >> 8) & 0xF)
1908303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x)		((x >> 12) & 0xFFF)
1909303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x)	((x >> 24) & 0xF)
1910303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x)	((x >> 28) & 0xF)
1911303628Ssbruno
1912286441Srpaulo#define DEFAULT_MAX_TX_POWER 16
1913286441Srpaulo
1914286441Srpaulo/**
1915286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM
1916286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo
1917286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel
1918286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed
1919286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required
1920330165Seadler * XXX cannot find this (DFS) flag in iwm-nvm-parse.c
1921286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate
1922286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
1923286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
1924286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
1925286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
1926286441Srpaulo */
1927286441Srpauloenum iwm_nvm_channel_flags {
1928286441Srpaulo	IWM_NVM_CHANNEL_VALID = (1 << 0),
1929286441Srpaulo	IWM_NVM_CHANNEL_IBSS = (1 << 1),
1930286441Srpaulo	IWM_NVM_CHANNEL_ACTIVE = (1 << 3),
1931286441Srpaulo	IWM_NVM_CHANNEL_RADAR = (1 << 4),
1932286441Srpaulo	IWM_NVM_CHANNEL_DFS = (1 << 7),
1933286441Srpaulo	IWM_NVM_CHANNEL_WIDE = (1 << 8),
1934286441Srpaulo	IWM_NVM_CHANNEL_40MHZ = (1 << 9),
1935286441Srpaulo	IWM_NVM_CHANNEL_80MHZ = (1 << 10),
1936286441Srpaulo	IWM_NVM_CHANNEL_160MHZ = (1 << 11),
1937286441Srpaulo};
1938286441Srpaulo
1939286441Srpaulo/*
1940298877Savos * Translate EEPROM flags to net80211.
1941286441Srpaulo */
1942298877Savosstatic uint32_t
1943298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags)
1944286441Srpaulo{
1945298877Savos	uint32_t nflags;
1946286441Srpaulo
1947298877Savos	nflags = 0;
1948298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0)
1949298877Savos		nflags |= IEEE80211_CHAN_PASSIVE;
1950298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0)
1951298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1952298877Savos	if (ch_flags & IWM_NVM_CHANNEL_RADAR) {
1953298877Savos		nflags |= IEEE80211_CHAN_DFS;
1954298877Savos		/* Just in case. */
1955298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1956286441Srpaulo	}
1957286441Srpaulo
1958298877Savos	return (nflags);
1959286441Srpaulo}
1960286441Srpaulo
1961286441Srpaulostatic void
1962298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[],
1963303628Ssbruno    int maxchans, int *nchans, int ch_idx, size_t ch_num,
1964303628Ssbruno    const uint8_t bands[])
1965286441Srpaulo{
1966330165Seadler	const uint16_t * const nvm_ch_flags = sc->nvm_data->nvm_ch_flags;
1967298877Savos	uint32_t nflags;
1968286441Srpaulo	uint16_t ch_flags;
1969298877Savos	uint8_t ieee;
1970298877Savos	int error;
1971286441Srpaulo
1972298877Savos	for (; ch_idx < ch_num; ch_idx++) {
1973286441Srpaulo		ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx);
1974330166Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
1975303628Ssbruno			ieee = iwm_nvm_channels[ch_idx];
1976303628Ssbruno		else
1977303628Ssbruno			ieee = iwm_nvm_channels_8000[ch_idx];
1978286441Srpaulo
1979286441Srpaulo		if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) {
1980286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1981286441Srpaulo			    "Ch. %d Flags %x [%sGHz] - No traffic\n",
1982298877Savos			    ieee, ch_flags,
1983286441Srpaulo			    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1984286441Srpaulo			    "5.2" : "2.4");
1985286441Srpaulo			continue;
1986286441Srpaulo		}
1987286441Srpaulo
1988298877Savos		nflags = iwm_eeprom_channel_flags(ch_flags);
1989298877Savos		error = ieee80211_add_channel(chans, maxchans, nchans,
1990298877Savos		    ieee, 0, 0, nflags, bands);
1991298877Savos		if (error != 0)
1992298877Savos			break;
1993286441Srpaulo
1994286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1995286441Srpaulo		    "Ch. %d Flags %x [%sGHz] - Added\n",
1996298877Savos		    ieee, ch_flags,
1997286441Srpaulo		    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1998286441Srpaulo		    "5.2" : "2.4");
1999286441Srpaulo	}
2000286441Srpaulo}
2001286441Srpaulo
2002298877Savosstatic void
2003298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans,
2004298877Savos    struct ieee80211_channel chans[])
2005298877Savos{
2006298877Savos	struct iwm_softc *sc = ic->ic_softc;
2007330165Seadler	struct iwm_nvm_data *data = sc->nvm_data;
2008299883Skevlo	uint8_t bands[IEEE80211_MODE_BYTES];
2009303628Ssbruno	size_t ch_num;
2010298877Savos
2011298877Savos	memset(bands, 0, sizeof(bands));
2012298877Savos	/* 1-13: 11b/g channels. */
2013298877Savos	setbit(bands, IEEE80211_MODE_11B);
2014298877Savos	setbit(bands, IEEE80211_MODE_11G);
2015298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans, 0,
2016298877Savos	    IWM_NUM_2GHZ_CHANNELS - 1, bands);
2017298877Savos
2018298877Savos	/* 14: 11b channel only. */
2019298877Savos	clrbit(bands, IEEE80211_MODE_11G);
2020298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans,
2021298936Savos	    IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands);
2022298877Savos
2023298877Savos	if (data->sku_cap_band_52GHz_enable) {
2024330166Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
2025303628Ssbruno			ch_num = nitems(iwm_nvm_channels);
2026303628Ssbruno		else
2027303628Ssbruno			ch_num = nitems(iwm_nvm_channels_8000);
2028298877Savos		memset(bands, 0, sizeof(bands));
2029298877Savos		setbit(bands, IEEE80211_MODE_11A);
2030298877Savos		iwm_add_channel_band(sc, chans, maxchans, nchans,
2031303628Ssbruno		    IWM_NUM_2GHZ_CHANNELS, ch_num, bands);
2032298877Savos	}
2033298877Savos}
2034298877Savos
2035303628Ssbrunostatic void
2036330165Seadleriwm_set_hw_address_family_8000(struct iwm_softc *sc, struct iwm_nvm_data *data,
2037303628Ssbruno	const uint16_t *mac_override, const uint16_t *nvm_hw)
2038303628Ssbruno{
2039303628Ssbruno	const uint8_t *hw_addr;
2040303628Ssbruno
2041303628Ssbruno	if (mac_override) {
2042303628Ssbruno		static const uint8_t reserved_mac[] = {
2043303628Ssbruno			0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
2044303628Ssbruno		};
2045303628Ssbruno
2046303628Ssbruno		hw_addr = (const uint8_t *)(mac_override +
2047303628Ssbruno				 IWM_MAC_ADDRESS_OVERRIDE_8000);
2048303628Ssbruno
2049303628Ssbruno		/*
2050303628Ssbruno		 * Store the MAC address from MAO section.
2051303628Ssbruno		 * No byte swapping is required in MAO section
2052303628Ssbruno		 */
2053303628Ssbruno		IEEE80211_ADDR_COPY(data->hw_addr, hw_addr);
2054303628Ssbruno
2055303628Ssbruno		/*
2056303628Ssbruno		 * Force the use of the OTP MAC address in case of reserved MAC
2057303628Ssbruno		 * address in the NVM, or if address is given but invalid.
2058303628Ssbruno		 */
2059303628Ssbruno		if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) &&
2060303628Ssbruno		    !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) &&
2061303628Ssbruno		    iwm_is_valid_ether_addr(data->hw_addr) &&
2062303628Ssbruno		    !IEEE80211_IS_MULTICAST(data->hw_addr))
2063303628Ssbruno			return;
2064303628Ssbruno
2065303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2066303628Ssbruno		    "%s: mac address from nvm override section invalid\n",
2067303628Ssbruno		    __func__);
2068303628Ssbruno	}
2069303628Ssbruno
2070303628Ssbruno	if (nvm_hw) {
2071303628Ssbruno		/* read the mac address from WFMP registers */
2072303628Ssbruno		uint32_t mac_addr0 =
2073303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0));
2074303628Ssbruno		uint32_t mac_addr1 =
2075303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1));
2076303628Ssbruno
2077303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr0;
2078303628Ssbruno		data->hw_addr[0] = hw_addr[3];
2079303628Ssbruno		data->hw_addr[1] = hw_addr[2];
2080303628Ssbruno		data->hw_addr[2] = hw_addr[1];
2081303628Ssbruno		data->hw_addr[3] = hw_addr[0];
2082303628Ssbruno
2083303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr1;
2084303628Ssbruno		data->hw_addr[4] = hw_addr[1];
2085303628Ssbruno		data->hw_addr[5] = hw_addr[0];
2086303628Ssbruno
2087303628Ssbruno		return;
2088303628Ssbruno	}
2089303628Ssbruno
2090303628Ssbruno	device_printf(sc->sc_dev, "%s: mac address not found\n", __func__);
2091303628Ssbruno	memset(data->hw_addr, 0, sizeof(data->hw_addr));
2092303628Ssbruno}
2093303628Ssbruno
2094286441Srpaulostatic int
2095303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2096303628Ssbruno	    const uint16_t *phy_sku)
2097303628Ssbruno{
2098330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2099303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_SKU);
2100303628Ssbruno
2101303628Ssbruno	return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000));
2102303628Ssbruno}
2103303628Ssbruno
2104303628Ssbrunostatic int
2105303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2106303628Ssbruno{
2107330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2108303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_NVM_VERSION);
2109303628Ssbruno	else
2110303628Ssbruno		return le32_to_cpup((const uint32_t *)(nvm_sw +
2111303628Ssbruno						IWM_NVM_VERSION_8000));
2112303628Ssbruno}
2113303628Ssbruno
2114303628Ssbrunostatic int
2115303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2116303628Ssbruno		  const uint16_t *phy_sku)
2117303628Ssbruno{
2118330166Seadler        if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2119303628Ssbruno                return le16_to_cpup(nvm_sw + IWM_RADIO_CFG);
2120303628Ssbruno
2121303628Ssbruno        return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000));
2122303628Ssbruno}
2123303628Ssbruno
2124303628Ssbrunostatic int
2125303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2126303628Ssbruno{
2127303628Ssbruno	int n_hw_addr;
2128303628Ssbruno
2129330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000)
2130303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS);
2131303628Ssbruno
2132303628Ssbruno	n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000));
2133303628Ssbruno
2134303628Ssbruno        return n_hw_addr & IWM_N_HW_ADDR_MASK;
2135303628Ssbruno}
2136303628Ssbruno
2137303628Ssbrunostatic void
2138303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data,
2139303628Ssbruno		  uint32_t radio_cfg)
2140303628Ssbruno{
2141330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2142303628Ssbruno		data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg);
2143303628Ssbruno		data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg);
2144303628Ssbruno		data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg);
2145303628Ssbruno		data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg);
2146303628Ssbruno		return;
2147303628Ssbruno	}
2148303628Ssbruno
2149303628Ssbruno	/* set the radio configuration for family 8000 */
2150303628Ssbruno	data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg);
2151303628Ssbruno	data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg);
2152303628Ssbruno	data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg);
2153303628Ssbruno	data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg);
2154303628Ssbruno	data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg);
2155303628Ssbruno	data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg);
2156303628Ssbruno}
2157303628Ssbruno
2158303628Ssbrunostatic int
2159330165Seadleriwm_set_hw_address(struct iwm_softc *sc, struct iwm_nvm_data *data,
2160330165Seadler		   const uint16_t *nvm_hw, const uint16_t *mac_override)
2161330165Seadler{
2162330165Seadler#ifdef notyet /* for FAMILY 9000 */
2163330165Seadler	if (cfg->mac_addr_from_csr) {
2164330165Seadler		iwm_set_hw_address_from_csr(sc, data);
2165330165Seadler        } else
2166330165Seadler#endif
2167330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2168330165Seadler		const uint8_t *hw_addr = (const uint8_t *)(nvm_hw + IWM_HW_ADDR);
2169330165Seadler
2170330165Seadler		/* The byte order is little endian 16 bit, meaning 214365 */
2171330165Seadler		data->hw_addr[0] = hw_addr[1];
2172330165Seadler		data->hw_addr[1] = hw_addr[0];
2173330165Seadler		data->hw_addr[2] = hw_addr[3];
2174330165Seadler		data->hw_addr[3] = hw_addr[2];
2175330165Seadler		data->hw_addr[4] = hw_addr[5];
2176330165Seadler		data->hw_addr[5] = hw_addr[4];
2177330165Seadler	} else {
2178330165Seadler		iwm_set_hw_address_family_8000(sc, data, mac_override, nvm_hw);
2179330165Seadler	}
2180330165Seadler
2181330165Seadler	if (!iwm_is_valid_ether_addr(data->hw_addr)) {
2182330165Seadler		device_printf(sc->sc_dev, "no valid mac address was found\n");
2183330165Seadler		return EINVAL;
2184330165Seadler	}
2185330165Seadler
2186330165Seadler	return 0;
2187330165Seadler}
2188330165Seadler
2189330165Seadlerstatic struct iwm_nvm_data *
2190286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc,
2191303628Ssbruno		   const uint16_t *nvm_hw, const uint16_t *nvm_sw,
2192303628Ssbruno		   const uint16_t *nvm_calib, const uint16_t *mac_override,
2193303628Ssbruno		   const uint16_t *phy_sku, const uint16_t *regulatory)
2194286441Srpaulo{
2195330165Seadler	struct iwm_nvm_data *data;
2196303628Ssbruno	uint32_t sku, radio_cfg;
2197330217Seadler	uint16_t lar_config;
2198286441Srpaulo
2199330166Seadler	if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) {
2200330165Seadler		data = malloc(sizeof(*data) +
2201330165Seadler		    IWM_NUM_CHANNELS * sizeof(uint16_t),
2202330165Seadler		    M_DEVBUF, M_NOWAIT | M_ZERO);
2203330165Seadler	} else {
2204330165Seadler		data = malloc(sizeof(*data) +
2205330165Seadler		    IWM_NUM_CHANNELS_8000 * sizeof(uint16_t),
2206330165Seadler		    M_DEVBUF, M_NOWAIT | M_ZERO);
2207330165Seadler	}
2208330165Seadler	if (!data)
2209330165Seadler		return NULL;
2210330165Seadler
2211303628Ssbruno	data->nvm_version = iwm_get_nvm_version(sc, nvm_sw);
2212286441Srpaulo
2213303628Ssbruno	radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku);
2214303628Ssbruno	iwm_set_radio_cfg(sc, data, radio_cfg);
2215286441Srpaulo
2216303628Ssbruno	sku = iwm_get_sku(sc, nvm_sw, phy_sku);
2217286441Srpaulo	data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ;
2218286441Srpaulo	data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ;
2219286441Srpaulo	data->sku_cap_11n_enable = 0;
2220286441Srpaulo
2221303628Ssbruno	data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw);
2222286441Srpaulo
2223330217Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
2224330217Seadler		uint16_t lar_offset = data->nvm_version < 0xE39 ?
2225330217Seadler				       IWM_NVM_LAR_OFFSET_8000_OLD :
2226330217Seadler				       IWM_NVM_LAR_OFFSET_8000;
2227330217Seadler
2228330217Seadler		lar_config = le16_to_cpup(regulatory + lar_offset);
2229330217Seadler		data->lar_enabled = !!(lar_config &
2230330217Seadler				       IWM_NVM_LAR_ENABLED_8000);
2231330217Seadler	}
2232330217Seadler
2233330165Seadler	/* If no valid mac address was found - bail out */
2234330165Seadler	if (iwm_set_hw_address(sc, data, nvm_hw, mac_override)) {
2235330165Seadler		free(data, M_DEVBUF);
2236330165Seadler		return NULL;
2237303628Ssbruno	}
2238286441Srpaulo
2239330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
2240303628Ssbruno		memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS],
2241303628Ssbruno		    IWM_NUM_CHANNELS * sizeof(uint16_t));
2242303628Ssbruno	} else {
2243303628Ssbruno		memcpy(data->nvm_ch_flags, &regulatory[IWM_NVM_CHANNELS_8000],
2244303628Ssbruno		    IWM_NUM_CHANNELS_8000 * sizeof(uint16_t));
2245303628Ssbruno	}
2246286441Srpaulo
2247330165Seadler	return data;
2248286441Srpaulo}
2249286441Srpaulo
2250330165Seadlerstatic void
2251330165Seadleriwm_free_nvm_data(struct iwm_nvm_data *data)
2252330165Seadler{
2253330165Seadler	if (data != NULL)
2254330165Seadler		free(data, M_DEVBUF);
2255330165Seadler}
2256286441Srpaulo
2257330165Seadlerstatic struct iwm_nvm_data *
2258286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections)
2259286441Srpaulo{
2260303628Ssbruno	const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
2261286441Srpaulo
2262286441Srpaulo	/* Checking for required sections */
2263330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) {
2264303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2265330166Seadler		    !sections[sc->cfg->nvm_hw_section_num].data) {
2266303628Ssbruno			device_printf(sc->sc_dev,
2267303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2268330165Seadler			return NULL;
2269303628Ssbruno		}
2270330166Seadler	} else if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
2271303628Ssbruno		/* SW and REGULATORY sections are mandatory */
2272303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2273303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) {
2274303628Ssbruno			device_printf(sc->sc_dev,
2275303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2276330165Seadler			return NULL;
2277303628Ssbruno		}
2278303628Ssbruno		/* MAC_OVERRIDE or at least HW section must exist */
2279330166Seadler		if (!sections[sc->cfg->nvm_hw_section_num].data &&
2280303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
2281303628Ssbruno			device_printf(sc->sc_dev,
2282303628Ssbruno			    "Can't parse mac_address, empty sections\n");
2283330165Seadler			return NULL;
2284303628Ssbruno		}
2285303628Ssbruno
2286303628Ssbruno		/* PHY_SKU section is mandatory in B0 */
2287303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) {
2288303628Ssbruno			device_printf(sc->sc_dev,
2289303628Ssbruno			    "Can't parse phy_sku in B0, empty sections\n");
2290330165Seadler			return NULL;
2291303628Ssbruno		}
2292303628Ssbruno	} else {
2293330166Seadler		panic("unknown device family %d\n", sc->cfg->device_family);
2294286441Srpaulo	}
2295286441Srpaulo
2296330166Seadler	hw = (const uint16_t *) sections[sc->cfg->nvm_hw_section_num].data;
2297286441Srpaulo	sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data;
2298303628Ssbruno	calib = (const uint16_t *)
2299303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data;
2300303628Ssbruno	regulatory = (const uint16_t *)
2301303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_REGULATORY].data;
2302303628Ssbruno	mac_override = (const uint16_t *)
2303303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data;
2304303628Ssbruno	phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data;
2305303628Ssbruno
2306303628Ssbruno	return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override,
2307303628Ssbruno	    phy_sku, regulatory);
2308286441Srpaulo}
2309286441Srpaulo
2310286441Srpaulostatic int
2311286441Srpauloiwm_nvm_init(struct iwm_softc *sc)
2312286441Srpaulo{
2313330165Seadler	struct iwm_nvm_section nvm_sections[IWM_NVM_MAX_NUM_SECTIONS];
2314330165Seadler	int i, ret, section;
2315330165Seadler	uint32_t size_read = 0;
2316330165Seadler	uint8_t *nvm_buffer, *temp;
2317286441Srpaulo	uint16_t len;
2318286441Srpaulo
2319330165Seadler	memset(nvm_sections, 0, sizeof(nvm_sections));
2320286441Srpaulo
2321330166Seadler	if (sc->cfg->nvm_hw_section_num >= IWM_NVM_MAX_NUM_SECTIONS)
2322330165Seadler		return EINVAL;
2323301970Sadrian
2324330165Seadler	/* load NVM values from nic */
2325330165Seadler	/* Read From FW NVM */
2326330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Read from NVM\n");
2327286441Srpaulo
2328330166Seadler	nvm_buffer = malloc(sc->cfg->eeprom_size, M_DEVBUF, M_NOWAIT | M_ZERO);
2329330165Seadler	if (!nvm_buffer)
2330330165Seadler		return ENOMEM;
2331330165Seadler	for (section = 0; section < IWM_NVM_MAX_NUM_SECTIONS; section++) {
2332330165Seadler		/* we override the constness for initial read */
2333330165Seadler		ret = iwm_nvm_read_section(sc, section, nvm_buffer,
2334330165Seadler					   &len, size_read);
2335330165Seadler		if (ret)
2336303628Ssbruno			continue;
2337330165Seadler		size_read += len;
2338330165Seadler		temp = malloc(len, M_DEVBUF, M_NOWAIT);
2339330165Seadler		if (!temp) {
2340330165Seadler			ret = ENOMEM;
2341286441Srpaulo			break;
2342286441Srpaulo		}
2343330165Seadler		memcpy(temp, nvm_buffer, len);
2344330165Seadler
2345330165Seadler		nvm_sections[section].data = temp;
2346286441Srpaulo		nvm_sections[section].length = len;
2347286441Srpaulo	}
2348330165Seadler	if (!size_read)
2349330165Seadler		device_printf(sc->sc_dev, "OTP is blank\n");
2350330165Seadler	free(nvm_buffer, M_DEVBUF);
2351286441Srpaulo
2352330165Seadler	sc->nvm_data = iwm_parse_nvm_sections(sc, nvm_sections);
2353330165Seadler	if (!sc->nvm_data)
2354330165Seadler		return EINVAL;
2355330165Seadler	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET,
2356330165Seadler		    "nvm version = %x\n", sc->nvm_data->nvm_version);
2357330165Seadler
2358330165Seadler	for (i = 0; i < IWM_NVM_MAX_NUM_SECTIONS; i++) {
2359301970Sadrian		if (nvm_sections[i].data != NULL)
2360301970Sadrian			free(nvm_sections[i].data, M_DEVBUF);
2361301970Sadrian	}
2362301970Sadrian
2363330165Seadler	return 0;
2364286441Srpaulo}
2365286441Srpaulo
2366286441Srpaulostatic int
2367330182Seadleriwm_pcie_load_section(struct iwm_softc *sc, uint8_t section_num,
2368330182Seadler	const struct iwm_fw_desc *section)
2369286441Srpaulo{
2370330182Seadler	struct iwm_dma_info *dma = &sc->fw_dma;
2371330182Seadler	uint8_t *v_addr;
2372330182Seadler	bus_addr_t p_addr;
2373330182Seadler	uint32_t offset, chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, section->len);
2374330182Seadler	int ret = 0;
2375303628Ssbruno
2376330182Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2377330182Seadler		    "%s: [%d] uCode section being loaded...\n",
2378330182Seadler		    __func__, section_num);
2379303628Ssbruno
2380330182Seadler	v_addr = dma->vaddr;
2381330182Seadler	p_addr = dma->paddr;
2382303628Ssbruno
2383330182Seadler	for (offset = 0; offset < section->len; offset += chunk_sz) {
2384330182Seadler		uint32_t copy_size, dst_addr;
2385330182Seadler		int extended_addr = FALSE;
2386303628Ssbruno
2387330182Seadler		copy_size = MIN(chunk_sz, section->len - offset);
2388330182Seadler		dst_addr = section->offset + offset;
2389330182Seadler
2390330182Seadler		if (dst_addr >= IWM_FW_MEM_EXTENDED_START &&
2391330182Seadler		    dst_addr <= IWM_FW_MEM_EXTENDED_END)
2392330182Seadler			extended_addr = TRUE;
2393330182Seadler
2394330182Seadler		if (extended_addr)
2395330182Seadler			iwm_set_bits_prph(sc, IWM_LMPM_CHICK,
2396330182Seadler					  IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2397330182Seadler
2398330182Seadler		memcpy(v_addr, (const uint8_t *)section->data + offset,
2399330182Seadler		    copy_size);
2400330182Seadler		bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
2401330182Seadler		ret = iwm_pcie_load_firmware_chunk(sc, dst_addr, p_addr,
2402330182Seadler						   copy_size);
2403330182Seadler
2404330182Seadler		if (extended_addr)
2405330182Seadler			iwm_clear_bits_prph(sc, IWM_LMPM_CHICK,
2406330182Seadler					    IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2407330182Seadler
2408330182Seadler		if (ret) {
2409330182Seadler			device_printf(sc->sc_dev,
2410330182Seadler			    "%s: Could not load the [%d] uCode section\n",
2411330182Seadler			    __func__, section_num);
2412303628Ssbruno			break;
2413330182Seadler		}
2414303628Ssbruno	}
2415303628Ssbruno
2416330182Seadler	return ret;
2417303628Ssbruno}
2418303628Ssbruno
2419330182Seadler/*
2420330182Seadler * ucode
2421330182Seadler */
2422303628Ssbrunostatic int
2423330182Seadleriwm_pcie_load_firmware_chunk(struct iwm_softc *sc, uint32_t dst_addr,
2424330182Seadler			     bus_addr_t phy_addr, uint32_t byte_cnt)
2425303628Ssbruno{
2426330182Seadler	int ret;
2427286441Srpaulo
2428303628Ssbruno	sc->sc_fw_chunk_done = 0;
2429303628Ssbruno
2430286441Srpaulo	if (!iwm_nic_lock(sc))
2431286441Srpaulo		return EBUSY;
2432286441Srpaulo
2433286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2434286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
2435330182Seadler
2436286441Srpaulo	IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL),
2437286441Srpaulo	    dst_addr);
2438330182Seadler
2439286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL),
2440330182Seadler	    phy_addr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
2441330182Seadler
2442286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL),
2443330182Seadler	    (iwm_get_dma_hi_addr(phy_addr)
2444330182Seadler	     << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
2445330182Seadler
2446286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL),
2447286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
2448286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
2449286441Srpaulo	    IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
2450330182Seadler
2451286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2452286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE    |
2453286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
2454286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
2455286441Srpaulo
2456286441Srpaulo	iwm_nic_unlock(sc);
2457286441Srpaulo
2458330182Seadler	/* wait up to 5s for this segment to load */
2459330182Seadler	ret = 0;
2460330182Seadler	while (!sc->sc_fw_chunk_done) {
2461330182Seadler		ret = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz);
2462330182Seadler		if (ret)
2463286441Srpaulo			break;
2464330182Seadler	}
2465286441Srpaulo
2466330182Seadler	if (ret != 0) {
2467303628Ssbruno		device_printf(sc->sc_dev,
2468303628Ssbruno		    "fw chunk addr 0x%x len %d failed to load\n",
2469303628Ssbruno		    dst_addr, byte_cnt);
2470330182Seadler		return ETIMEDOUT;
2471303628Ssbruno	}
2472303628Ssbruno
2473330182Seadler	return 0;
2474286441Srpaulo}
2475286441Srpaulo
2476330182Seadlerstatic int
2477330182Seadleriwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc,
2478330182Seadler	const struct iwm_fw_sects *image, int cpu, int *first_ucode_section)
2479303628Ssbruno{
2480303628Ssbruno	int shift_param;
2481330182Seadler	int i, ret = 0, sec_num = 0x1;
2482303628Ssbruno	uint32_t val, last_read_idx = 0;
2483303628Ssbruno
2484303628Ssbruno	if (cpu == 1) {
2485303628Ssbruno		shift_param = 0;
2486303628Ssbruno		*first_ucode_section = 0;
2487303628Ssbruno	} else {
2488303628Ssbruno		shift_param = 16;
2489303628Ssbruno		(*first_ucode_section)++;
2490303628Ssbruno	}
2491303628Ssbruno
2492330168Seadler	for (i = *first_ucode_section; i < IWM_UCODE_SECTION_MAX; i++) {
2493303628Ssbruno		last_read_idx = i;
2494303628Ssbruno
2495303628Ssbruno		/*
2496303628Ssbruno		 * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
2497303628Ssbruno		 * CPU1 to CPU2.
2498303628Ssbruno		 * PAGING_SEPARATOR_SECTION delimiter - separate between
2499303628Ssbruno		 * CPU2 non paged to CPU2 paging sec.
2500303628Ssbruno		 */
2501330182Seadler		if (!image->fw_sect[i].data ||
2502330182Seadler		    image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION ||
2503330182Seadler		    image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) {
2504330182Seadler			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2505330182Seadler				    "Break since Data not valid or Empty section, sec = %d\n",
2506330182Seadler				    i);
2507303628Ssbruno			break;
2508303628Ssbruno		}
2509330182Seadler		ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]);
2510330182Seadler		if (ret)
2511330182Seadler			return ret;
2512303628Ssbruno
2513303628Ssbruno		/* Notify the ucode of the loaded section number and status */
2514303628Ssbruno		if (iwm_nic_lock(sc)) {
2515303628Ssbruno			val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS);
2516303628Ssbruno			val = val | (sec_num << shift_param);
2517303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val);
2518303628Ssbruno			sec_num = (sec_num << 1) | 0x1;
2519303628Ssbruno			iwm_nic_unlock(sc);
2520303628Ssbruno		}
2521303628Ssbruno	}
2522303628Ssbruno
2523303628Ssbruno	*first_ucode_section = last_read_idx;
2524303628Ssbruno
2525330183Seadler	iwm_enable_interrupts(sc);
2526330183Seadler
2527303628Ssbruno	if (iwm_nic_lock(sc)) {
2528303628Ssbruno		if (cpu == 1)
2529303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF);
2530303628Ssbruno		else
2531303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
2532303628Ssbruno		iwm_nic_unlock(sc);
2533303628Ssbruno	}
2534303628Ssbruno
2535303628Ssbruno	return 0;
2536303628Ssbruno}
2537303628Ssbruno
2538330182Seadlerstatic int
2539330182Seadleriwm_pcie_load_cpu_sections(struct iwm_softc *sc,
2540330182Seadler	const struct iwm_fw_sects *image, int cpu, int *first_ucode_section)
2541303628Ssbruno{
2542330182Seadler	int shift_param;
2543330182Seadler	int i, ret = 0;
2544330182Seadler	uint32_t last_read_idx = 0;
2545303628Ssbruno
2546330182Seadler	if (cpu == 1) {
2547330182Seadler		shift_param = 0;
2548330182Seadler		*first_ucode_section = 0;
2549330182Seadler	} else {
2550330182Seadler		shift_param = 16;
2551330182Seadler		(*first_ucode_section)++;
2552330182Seadler	}
2553303628Ssbruno
2554330182Seadler	for (i = *first_ucode_section; i < IWM_UCODE_SECTION_MAX; i++) {
2555330182Seadler		last_read_idx = i;
2556303628Ssbruno
2557330182Seadler		/*
2558330182Seadler		 * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
2559330182Seadler		 * CPU1 to CPU2.
2560330182Seadler		 * PAGING_SEPARATOR_SECTION delimiter - separate between
2561330182Seadler		 * CPU2 non paged to CPU2 paging sec.
2562330182Seadler		 */
2563330182Seadler		if (!image->fw_sect[i].data ||
2564330182Seadler		    image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION ||
2565330182Seadler		    image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) {
2566330182Seadler			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2567330182Seadler				    "Break since Data not valid or Empty section, sec = %d\n",
2568330182Seadler				     i);
2569330182Seadler			break;
2570330182Seadler		}
2571303628Ssbruno
2572330182Seadler		ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]);
2573330182Seadler		if (ret)
2574330182Seadler			return ret;
2575330182Seadler	}
2576303628Ssbruno
2577330182Seadler	*first_ucode_section = last_read_idx;
2578330182Seadler
2579330182Seadler	return 0;
2580330182Seadler
2581303628Ssbruno}
2582303628Ssbruno
2583286441Srpaulostatic int
2584330182Seadleriwm_pcie_load_given_ucode(struct iwm_softc *sc,
2585330182Seadler	const struct iwm_fw_sects *image)
2586286441Srpaulo{
2587330182Seadler	int ret = 0;
2588330182Seadler	int first_ucode_section;
2589286441Srpaulo
2590330182Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n",
2591330182Seadler		     image->is_dual_cpus ? "Dual" : "Single");
2592286441Srpaulo
2593330182Seadler	/* load to FW the binary non secured sections of CPU1 */
2594330182Seadler	ret = iwm_pcie_load_cpu_sections(sc, image, 1, &first_ucode_section);
2595330182Seadler	if (ret)
2596330182Seadler		return ret;
2597330182Seadler
2598330182Seadler	if (image->is_dual_cpus) {
2599330182Seadler		/* set CPU2 header address */
2600330199Seadler		if (iwm_nic_lock(sc)) {
2601330199Seadler			iwm_write_prph(sc,
2602330199Seadler				       IWM_LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
2603330199Seadler				       IWM_LMPM_SECURE_CPU2_HDR_MEM_SPACE);
2604330200Seadler			iwm_nic_unlock(sc);
2605330199Seadler		}
2606330182Seadler
2607330182Seadler		/* load to FW the binary sections of CPU2 */
2608330182Seadler		ret = iwm_pcie_load_cpu_sections(sc, image, 2,
2609330182Seadler						 &first_ucode_section);
2610330182Seadler		if (ret)
2611330182Seadler			return ret;
2612286441Srpaulo	}
2613286441Srpaulo
2614330183Seadler	iwm_enable_interrupts(sc);
2615330183Seadler
2616330183Seadler	/* release CPU reset */
2617286441Srpaulo	IWM_WRITE(sc, IWM_CSR_RESET, 0);
2618286441Srpaulo
2619303628Ssbruno	return 0;
2620303628Ssbruno}
2621303628Ssbruno
2622330182Seadlerint
2623330182Seadleriwm_pcie_load_given_ucode_8000(struct iwm_softc *sc,
2624330182Seadler	const struct iwm_fw_sects *image)
2625330182Seadler{
2626330182Seadler	int ret = 0;
2627330182Seadler	int first_ucode_section;
2628330182Seadler
2629330182Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n",
2630330182Seadler		    image->is_dual_cpus ? "Dual" : "Single");
2631330182Seadler
2632330182Seadler	/* configure the ucode to be ready to get the secured image */
2633330182Seadler	/* release CPU reset */
2634330199Seadler	if (iwm_nic_lock(sc)) {
2635330199Seadler		iwm_write_prph(sc, IWM_RELEASE_CPU_RESET,
2636330199Seadler		    IWM_RELEASE_CPU_RESET_BIT);
2637330200Seadler		iwm_nic_unlock(sc);
2638330199Seadler	}
2639330182Seadler
2640330182Seadler	/* load to FW the binary Secured sections of CPU1 */
2641330182Seadler	ret = iwm_pcie_load_cpu_sections_8000(sc, image, 1,
2642330182Seadler	    &first_ucode_section);
2643330182Seadler	if (ret)
2644330182Seadler		return ret;
2645330182Seadler
2646330182Seadler	/* load to FW the binary sections of CPU2 */
2647330182Seadler	return iwm_pcie_load_cpu_sections_8000(sc, image, 2,
2648330182Seadler	    &first_ucode_section);
2649330182Seadler}
2650330182Seadler
2651330183Seadler/* XXX Get rid of this definition */
2652330183Seadlerstatic inline void
2653330183Seadleriwm_enable_fw_load_int(struct iwm_softc *sc)
2654303628Ssbruno{
2655330183Seadler	IWM_DPRINTF(sc, IWM_DEBUG_INTR, "Enabling FW load interrupt\n");
2656330183Seadler	sc->sc_intmask = IWM_CSR_INT_BIT_FH_TX;
2657330183Seadler	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
2658286441Srpaulo}
2659286441Srpaulo
2660330183Seadler/* XXX Add proper rfkill support code */
2661286441Srpaulostatic int
2662330183Seadleriwm_start_fw(struct iwm_softc *sc,
2663330183Seadler	const struct iwm_fw_sects *fw)
2664286441Srpaulo{
2665330183Seadler	int ret;
2666286441Srpaulo
2667330183Seadler	/* This may fail if AMT took ownership of the device */
2668330183Seadler	if (iwm_prepare_card_hw(sc)) {
2669330183Seadler		device_printf(sc->sc_dev,
2670330183Seadler		    "%s: Exit HW not ready\n", __func__);
2671330183Seadler		ret = EIO;
2672330183Seadler		goto out;
2673286441Srpaulo	}
2674286441Srpaulo
2675330183Seadler	IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF);
2676330183Seadler
2677330183Seadler	iwm_disable_interrupts(sc);
2678330183Seadler
2679286441Srpaulo	/* make sure rfkill handshake bits are cleared */
2680286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2681286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR,
2682286441Srpaulo	    IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
2683286441Srpaulo
2684286441Srpaulo	/* clear (again), then enable host interrupts */
2685330183Seadler	IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF);
2686286441Srpaulo
2687330183Seadler	ret = iwm_nic_init(sc);
2688330183Seadler	if (ret) {
2689330183Seadler		device_printf(sc->sc_dev, "%s: Unable to init nic\n", __func__);
2690330183Seadler		goto out;
2691330183Seadler	}
2692330183Seadler
2693330183Seadler	/*
2694330183Seadler	 * Now, we load the firmware and don't want to be interrupted, even
2695330183Seadler	 * by the RF-Kill interrupt (hence mask all the interrupt besides the
2696330183Seadler	 * FH_TX interrupt which is needed to load the firmware). If the
2697330183Seadler	 * RF-Kill switch is toggled, we will find out after having loaded
2698330183Seadler	 * the firmware and return the proper value to the caller.
2699330183Seadler	 */
2700330183Seadler	iwm_enable_fw_load_int(sc);
2701330183Seadler
2702286441Srpaulo	/* really make sure rfkill handshake bits are cleared */
2703286441Srpaulo	/* maybe we should write a few times more?  just to make sure */
2704286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2705286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2706286441Srpaulo
2707286441Srpaulo	/* Load the given image to the HW */
2708330183Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
2709330183Seadler		ret = iwm_pcie_load_given_ucode_8000(sc, fw);
2710330183Seadler	else
2711330183Seadler		ret = iwm_pcie_load_given_ucode(sc, fw);
2712330183Seadler
2713330183Seadler	/* XXX re-check RF-Kill state */
2714330183Seadler
2715330183Seadlerout:
2716330183Seadler	return ret;
2717286441Srpaulo}
2718286441Srpaulo
2719286441Srpaulostatic int
2720286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant)
2721286441Srpaulo{
2722286441Srpaulo	struct iwm_tx_ant_cfg_cmd tx_ant_cmd = {
2723286441Srpaulo		.valid = htole32(valid_tx_ant),
2724286441Srpaulo	};
2725286441Srpaulo
2726286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD,
2727286441Srpaulo	    IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd);
2728286441Srpaulo}
2729286441Srpaulo
2730286441Srpaulo/* iwlwifi: mvm/fw.c */
2731286441Srpaulostatic int
2732286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc)
2733286441Srpaulo{
2734286441Srpaulo	struct iwm_phy_cfg_cmd phy_cfg_cmd;
2735330183Seadler	enum iwm_ucode_type ucode_type = sc->cur_ucode;
2736286441Srpaulo
2737286441Srpaulo	/* Set parameters */
2738330167Seadler	phy_cfg_cmd.phy_cfg = htole32(iwm_mvm_get_phy_config(sc));
2739286441Srpaulo	phy_cfg_cmd.calib_control.event_trigger =
2740286441Srpaulo	    sc->sc_default_calib[ucode_type].event_trigger;
2741286441Srpaulo	phy_cfg_cmd.calib_control.flow_trigger =
2742286441Srpaulo	    sc->sc_default_calib[ucode_type].flow_trigger;
2743286441Srpaulo
2744286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
2745286441Srpaulo	    "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg);
2746286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC,
2747286441Srpaulo	    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
2748286441Srpaulo}
2749286441Srpaulo
2750286441Srpaulostatic int
2751330183Seadleriwm_alive_fn(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data)
2752330183Seadler{
2753330183Seadler	struct iwm_mvm_alive_data *alive_data = data;
2754330183Seadler	struct iwm_mvm_alive_resp_ver1 *palive1;
2755330183Seadler	struct iwm_mvm_alive_resp_ver2 *palive2;
2756330183Seadler	struct iwm_mvm_alive_resp *palive;
2757330183Seadler
2758330183Seadler	if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
2759330183Seadler		palive1 = (void *)pkt->data;
2760330183Seadler
2761330183Seadler		sc->support_umac_log = FALSE;
2762330183Seadler                sc->error_event_table =
2763330183Seadler                        le32toh(palive1->error_event_table_ptr);
2764330183Seadler                sc->log_event_table =
2765330183Seadler                        le32toh(palive1->log_event_table_ptr);
2766330183Seadler                alive_data->scd_base_addr = le32toh(palive1->scd_base_ptr);
2767330183Seadler
2768330183Seadler                alive_data->valid = le16toh(palive1->status) ==
2769330183Seadler                                    IWM_ALIVE_STATUS_OK;
2770330183Seadler                IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2771330183Seadler			    "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
2772330183Seadler			     le16toh(palive1->status), palive1->ver_type,
2773330183Seadler                             palive1->ver_subtype, palive1->flags);
2774330183Seadler	} else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
2775330183Seadler		palive2 = (void *)pkt->data;
2776330183Seadler		sc->error_event_table =
2777330183Seadler			le32toh(palive2->error_event_table_ptr);
2778330183Seadler		sc->log_event_table =
2779330183Seadler			le32toh(palive2->log_event_table_ptr);
2780330183Seadler		alive_data->scd_base_addr = le32toh(palive2->scd_base_ptr);
2781330183Seadler		sc->umac_error_event_table =
2782330183Seadler                        le32toh(palive2->error_info_addr);
2783330183Seadler
2784330183Seadler		alive_data->valid = le16toh(palive2->status) ==
2785330183Seadler				    IWM_ALIVE_STATUS_OK;
2786330183Seadler		if (sc->umac_error_event_table)
2787330183Seadler			sc->support_umac_log = TRUE;
2788330183Seadler
2789330183Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2790330183Seadler			    "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
2791330183Seadler			    le16toh(palive2->status), palive2->ver_type,
2792330183Seadler			    palive2->ver_subtype, palive2->flags);
2793330183Seadler
2794330183Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2795330183Seadler			    "UMAC version: Major - 0x%x, Minor - 0x%x\n",
2796330183Seadler			    palive2->umac_major, palive2->umac_minor);
2797330183Seadler	} else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive)) {
2798330183Seadler		palive = (void *)pkt->data;
2799330183Seadler
2800330183Seadler		sc->error_event_table =
2801330183Seadler			le32toh(palive->error_event_table_ptr);
2802330183Seadler		sc->log_event_table =
2803330183Seadler			le32toh(palive->log_event_table_ptr);
2804330183Seadler		alive_data->scd_base_addr = le32toh(palive->scd_base_ptr);
2805330183Seadler		sc->umac_error_event_table =
2806330183Seadler			le32toh(palive->error_info_addr);
2807330183Seadler
2808330183Seadler		alive_data->valid = le16toh(palive->status) ==
2809330183Seadler				    IWM_ALIVE_STATUS_OK;
2810330183Seadler		if (sc->umac_error_event_table)
2811330183Seadler			sc->support_umac_log = TRUE;
2812330183Seadler
2813330183Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2814330183Seadler			    "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
2815330183Seadler			    le16toh(palive->status), palive->ver_type,
2816330183Seadler			    palive->ver_subtype, palive->flags);
2817330183Seadler
2818330183Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2819330183Seadler			    "UMAC version: Major - 0x%x, Minor - 0x%x\n",
2820330183Seadler			    le32toh(palive->umac_major),
2821330183Seadler			    le32toh(palive->umac_minor));
2822330183Seadler	}
2823330183Seadler
2824330183Seadler	return TRUE;
2825330183Seadler}
2826330183Seadler
2827330183Seadlerstatic int
2828330171Seadleriwm_wait_phy_db_entry(struct iwm_softc *sc,
2829330171Seadler	struct iwm_rx_packet *pkt, void *data)
2830330171Seadler{
2831330171Seadler	struct iwm_phy_db *phy_db = data;
2832330171Seadler
2833330171Seadler	if (pkt->hdr.code != IWM_CALIB_RES_NOTIF_PHY_DB) {
2834330171Seadler		if(pkt->hdr.code != IWM_INIT_COMPLETE_NOTIF) {
2835330171Seadler			device_printf(sc->sc_dev, "%s: Unexpected cmd: %d\n",
2836330171Seadler			    __func__, pkt->hdr.code);
2837330171Seadler		}
2838330171Seadler		return TRUE;
2839330171Seadler	}
2840330171Seadler
2841330171Seadler	if (iwm_phy_db_set_section(phy_db, pkt)) {
2842330171Seadler		device_printf(sc->sc_dev,
2843330171Seadler		    "%s: iwm_phy_db_set_section failed\n", __func__);
2844330171Seadler	}
2845330171Seadler
2846330171Seadler	return FALSE;
2847330171Seadler}
2848330171Seadler
2849330171Seadlerstatic int
2850286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc,
2851286441Srpaulo	enum iwm_ucode_type ucode_type)
2852286441Srpaulo{
2853330183Seadler	struct iwm_notification_wait alive_wait;
2854330183Seadler	struct iwm_mvm_alive_data alive_data;
2855330183Seadler	const struct iwm_fw_sects *fw;
2856330183Seadler	enum iwm_ucode_type old_type = sc->cur_ucode;
2857286441Srpaulo	int error;
2858330183Seadler	static const uint16_t alive_cmd[] = { IWM_MVM_ALIVE };
2859286441Srpaulo
2860303628Ssbruno	if ((error = iwm_read_firmware(sc, ucode_type)) != 0) {
2861303628Ssbruno		device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n",
2862303628Ssbruno			error);
2863286441Srpaulo		return error;
2864303628Ssbruno	}
2865330183Seadler	fw = &sc->sc_fw.fw_sects[ucode_type];
2866330183Seadler	sc->cur_ucode = ucode_type;
2867330183Seadler	sc->ucode_loaded = FALSE;
2868286441Srpaulo
2869330183Seadler	memset(&alive_data, 0, sizeof(alive_data));
2870330183Seadler	iwm_init_notification_wait(sc->sc_notif_wait, &alive_wait,
2871330183Seadler				   alive_cmd, nitems(alive_cmd),
2872330183Seadler				   iwm_alive_fn, &alive_data);
2873330183Seadler
2874330183Seadler	error = iwm_start_fw(sc, fw);
2875286441Srpaulo	if (error) {
2876303628Ssbruno		device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error);
2877330183Seadler		sc->cur_ucode = old_type;
2878330183Seadler		iwm_remove_notification(sc->sc_notif_wait, &alive_wait);
2879286441Srpaulo		return error;
2880286441Srpaulo	}
2881286441Srpaulo
2882330183Seadler	/*
2883330183Seadler	 * Some things may run in the background now, but we
2884330183Seadler	 * just wait for the ALIVE notification here.
2885330183Seadler	 */
2886330183Seadler	IWM_UNLOCK(sc);
2887330183Seadler	error = iwm_wait_notification(sc->sc_notif_wait, &alive_wait,
2888330183Seadler				      IWM_MVM_UCODE_ALIVE_TIMEOUT);
2889330183Seadler	IWM_LOCK(sc);
2890303628Ssbruno	if (error) {
2891330183Seadler		if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
2892330199Seadler			uint32_t a = 0x5a5a5a5a, b = 0x5a5a5a5a;
2893330199Seadler			if (iwm_nic_lock(sc)) {
2894330199Seadler				a = iwm_read_prph(sc, IWM_SB_CPU_1_STATUS);
2895330199Seadler				b = iwm_read_prph(sc, IWM_SB_CPU_2_STATUS);
2896330200Seadler				iwm_nic_unlock(sc);
2897330199Seadler			}
2898330183Seadler			device_printf(sc->sc_dev,
2899330183Seadler			    "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
2900330199Seadler			    a, b);
2901330183Seadler		}
2902330183Seadler		sc->cur_ucode = old_type;
2903330183Seadler		return error;
2904303628Ssbruno	}
2905330183Seadler
2906330183Seadler	if (!alive_data.valid) {
2907330183Seadler		device_printf(sc->sc_dev, "%s: Loaded ucode is not valid\n",
2908330183Seadler		    __func__);
2909330183Seadler		sc->cur_ucode = old_type;
2910330183Seadler		return EIO;
2911330183Seadler	}
2912330183Seadler
2913330183Seadler	iwm_trans_pcie_fw_alive(sc, alive_data.scd_base_addr);
2914330183Seadler
2915330183Seadler	/*
2916330183Seadler	 * configure and operate fw paging mechanism.
2917330183Seadler	 * driver configures the paging flow only once, CPU2 paging image
2918330183Seadler	 * included in the IWM_UCODE_INIT image.
2919330183Seadler	 */
2920330183Seadler	if (fw->paging_mem_size) {
2921330192Seadler		error = iwm_save_fw_paging(sc, fw);
2922330192Seadler		if (error) {
2923330192Seadler			device_printf(sc->sc_dev,
2924330192Seadler			    "%s: failed to save the FW paging image\n",
2925330192Seadler			    __func__);
2926330192Seadler			return error;
2927330192Seadler		}
2928330192Seadler
2929330192Seadler		error = iwm_send_paging_cmd(sc, fw);
2930330192Seadler		if (error) {
2931330192Seadler			device_printf(sc->sc_dev,
2932330192Seadler			    "%s: failed to send the paging cmd\n", __func__);
2933330192Seadler			iwm_free_fw_paging(sc);
2934330192Seadler			return error;
2935330192Seadler		}
2936330183Seadler	}
2937330183Seadler
2938330183Seadler	if (!error)
2939330183Seadler		sc->ucode_loaded = TRUE;
2940303628Ssbruno	return error;
2941286441Srpaulo}
2942286441Srpaulo
2943286441Srpaulo/*
2944286441Srpaulo * mvm misc bits
2945286441Srpaulo */
2946286441Srpaulo
2947286441Srpaulo/*
2948286441Srpaulo * follows iwlwifi/fw.c
2949286441Srpaulo */
2950286441Srpaulostatic int
2951286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm)
2952286441Srpaulo{
2953330171Seadler	struct iwm_notification_wait calib_wait;
2954330171Seadler	static const uint16_t init_complete[] = {
2955330171Seadler		IWM_INIT_COMPLETE_NOTIF,
2956330171Seadler		IWM_CALIB_RES_NOTIF_PHY_DB
2957330171Seadler	};
2958330171Seadler	int ret;
2959286441Srpaulo
2960286441Srpaulo	/* do not operate with rfkill switch turned on */
2961286441Srpaulo	if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) {
2962286441Srpaulo		device_printf(sc->sc_dev,
2963286441Srpaulo		    "radio is disabled by hardware switch\n");
2964286441Srpaulo		return EPERM;
2965286441Srpaulo	}
2966286441Srpaulo
2967330171Seadler	iwm_init_notification_wait(sc->sc_notif_wait,
2968330171Seadler				   &calib_wait,
2969330171Seadler				   init_complete,
2970330171Seadler				   nitems(init_complete),
2971330171Seadler				   iwm_wait_phy_db_entry,
2972330171Seadler				   sc->sc_phy_db);
2973330171Seadler
2974330171Seadler	/* Will also start the device */
2975330171Seadler	ret = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_INIT);
2976330171Seadler	if (ret) {
2977330171Seadler		device_printf(sc->sc_dev, "Failed to start INIT ucode: %d\n",
2978330171Seadler		    ret);
2979330171Seadler		goto error;
2980301192Sadrian	}
2981286441Srpaulo
2982286441Srpaulo	if (justnvm) {
2983330171Seadler		/* Read nvm */
2984330171Seadler		ret = iwm_nvm_init(sc);
2985330171Seadler		if (ret) {
2986286441Srpaulo			device_printf(sc->sc_dev, "failed to read nvm\n");
2987330171Seadler			goto error;
2988286441Srpaulo		}
2989330165Seadler		IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->nvm_data->hw_addr);
2990330171Seadler		goto error;
2991286441Srpaulo	}
2992286441Srpaulo
2993330171Seadler	ret = iwm_send_bt_init_conf(sc);
2994330171Seadler	if (ret) {
2995303628Ssbruno		device_printf(sc->sc_dev,
2996330171Seadler		    "failed to send bt coex configuration: %d\n", ret);
2997330171Seadler		goto error;
2998303628Ssbruno	}
2999303628Ssbruno
3000303628Ssbruno	/* Init Smart FIFO. */
3001330171Seadler	ret = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF);
3002330171Seadler	if (ret)
3003330171Seadler		goto error;
3004303628Ssbruno
3005286441Srpaulo	/* Send TX valid antennas before triggering calibrations */
3006330171Seadler	ret = iwm_send_tx_ant_cfg(sc, iwm_mvm_get_valid_tx_ant(sc));
3007330171Seadler	if (ret) {
3008303628Ssbruno		device_printf(sc->sc_dev,
3009330171Seadler		    "failed to send antennas before calibration: %d\n", ret);
3010330171Seadler		goto error;
3011303628Ssbruno	}
3012286441Srpaulo
3013286441Srpaulo	/*
3014303628Ssbruno	 * Send phy configurations command to init uCode
3015303628Ssbruno	 * to start the 16.0 uCode init image internal calibrations.
3016303628Ssbruno	 */
3017330171Seadler	ret = iwm_send_phy_cfg_cmd(sc);
3018330171Seadler	if (ret) {
3019286441Srpaulo		device_printf(sc->sc_dev,
3020330171Seadler		    "%s: Failed to run INIT calibrations: %d\n",
3021330171Seadler		    __func__, ret);
3022330171Seadler		goto error;
3023286441Srpaulo	}
3024286441Srpaulo
3025286441Srpaulo	/*
3026286441Srpaulo	 * Nothing to do but wait for the init complete notification
3027330171Seadler	 * from the firmware.
3028286441Srpaulo	 */
3029330171Seadler	IWM_UNLOCK(sc);
3030330171Seadler	ret = iwm_wait_notification(sc->sc_notif_wait, &calib_wait,
3031330171Seadler	    IWM_MVM_UCODE_CALIB_TIMEOUT);
3032330171Seadler	IWM_LOCK(sc);
3033286441Srpaulo
3034303628Ssbruno
3035330171Seadler	goto out;
3036330171Seadler
3037330171Seadlererror:
3038330171Seadler	iwm_remove_notification(sc->sc_notif_wait, &calib_wait);
3039330171Seadlerout:
3040330171Seadler	return ret;
3041286441Srpaulo}
3042286441Srpaulo
3043286441Srpaulo/*
3044286441Srpaulo * receive side
3045286441Srpaulo */
3046286441Srpaulo
3047286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */
3048286441Srpaulostatic int
3049286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx)
3050286441Srpaulo{
3051286441Srpaulo	struct iwm_rx_ring *ring = &sc->rxq;
3052286441Srpaulo	struct iwm_rx_data *data = &ring->data[idx];
3053286441Srpaulo	struct mbuf *m;
3054330197Seadler	bus_dmamap_t dmamap;
3055303628Ssbruno	bus_dma_segment_t seg;
3056303628Ssbruno	int nsegs, error;
3057286441Srpaulo
3058286441Srpaulo	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE);
3059286441Srpaulo	if (m == NULL)
3060286441Srpaulo		return ENOBUFS;
3061286441Srpaulo
3062286441Srpaulo	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
3063303628Ssbruno	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m,
3064303628Ssbruno	    &seg, &nsegs, BUS_DMA_NOWAIT);
3065303628Ssbruno	if (error != 0) {
3066286441Srpaulo		device_printf(sc->sc_dev,
3067301845Sadrian		    "%s: can't map mbuf, error %d\n", __func__, error);
3068330197Seadler		m_freem(m);
3069330197Seadler		return error;
3070286441Srpaulo	}
3071301845Sadrian
3072301845Sadrian	if (data->m != NULL)
3073301845Sadrian		bus_dmamap_unload(ring->data_dmat, data->map);
3074301845Sadrian
3075301845Sadrian	/* Swap ring->spare_map with data->map */
3076301845Sadrian	dmamap = data->map;
3077301845Sadrian	data->map = ring->spare_map;
3078301845Sadrian	ring->spare_map = dmamap;
3079301845Sadrian
3080286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
3081301845Sadrian	data->m = m;
3082286441Srpaulo
3083286441Srpaulo	/* Update RX descriptor. */
3084303628Ssbruno	KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned"));
3085303628Ssbruno	ring->desc[idx] = htole32(seg.ds_addr >> 8);
3086286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3087286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3088286441Srpaulo
3089286441Srpaulo	return 0;
3090286441Srpaulo}
3091286441Srpaulo
3092286441Srpaulo/* iwlwifi: mvm/rx.c */
3093286441Srpaulo/*
3094286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API
3095286441Srpaulo * values are reported by the fw as positive values - need to negate
3096286441Srpaulo * to obtain their dBM.  Account for missing antennas by replacing 0
3097286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
3098286441Srpaulo */
3099286441Srpaulostatic int
3100286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
3101286441Srpaulo{
3102286441Srpaulo	int energy_a, energy_b, energy_c, max_energy;
3103286441Srpaulo	uint32_t val;
3104286441Srpaulo
3105286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]);
3106286441Srpaulo	energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >>
3107286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_A_POS;
3108286441Srpaulo	energy_a = energy_a ? -energy_a : -256;
3109286441Srpaulo	energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >>
3110286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_B_POS;
3111286441Srpaulo	energy_b = energy_b ? -energy_b : -256;
3112286441Srpaulo	energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >>
3113286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_C_POS;
3114286441Srpaulo	energy_c = energy_c ? -energy_c : -256;
3115286441Srpaulo	max_energy = MAX(energy_a, energy_b);
3116286441Srpaulo	max_energy = MAX(max_energy, energy_c);
3117286441Srpaulo
3118286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3119286441Srpaulo	    "energy In A %d B %d C %d , and max %d\n",
3120286441Srpaulo	    energy_a, energy_b, energy_c, max_energy);
3121286441Srpaulo
3122286441Srpaulo	return max_energy;
3123286441Srpaulo}
3124286441Srpaulo
3125286441Srpaulostatic void
3126330208Seadleriwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3127286441Srpaulo{
3128286441Srpaulo	struct iwm_rx_phy_info *phy_info = (void *)pkt->data;
3129286441Srpaulo
3130286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n");
3131286441Srpaulo
3132286441Srpaulo	memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info));
3133286441Srpaulo}
3134286441Srpaulo
3135286441Srpaulo/*
3136286441Srpaulo * Retrieve the average noise (in dBm) among receivers.
3137286441Srpaulo */
3138286441Srpaulostatic int
3139330144Seadleriwm_get_noise(struct iwm_softc *sc,
3140330144Seadler    const struct iwm_mvm_statistics_rx_non_phy *stats)
3141286441Srpaulo{
3142286441Srpaulo	int i, total, nbant, noise;
3143286441Srpaulo
3144286441Srpaulo	total = nbant = noise = 0;
3145286441Srpaulo	for (i = 0; i < 3; i++) {
3146286441Srpaulo		noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff;
3147330144Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n",
3148330144Seadler		    __func__,
3149330144Seadler		    i,
3150330144Seadler		    noise);
3151330144Seadler
3152286441Srpaulo		if (noise) {
3153286441Srpaulo			total += noise;
3154286441Srpaulo			nbant++;
3155286441Srpaulo		}
3156286441Srpaulo	}
3157286441Srpaulo
3158330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n",
3159330144Seadler	    __func__, nbant, total);
3160330144Seadler#if 0
3161286441Srpaulo	/* There should be at least one antenna but check anyway. */
3162286441Srpaulo	return (nbant == 0) ? -127 : (total / nbant) - 107;
3163330144Seadler#else
3164330144Seadler	/* For now, just hard-code it to -96 to be safe */
3165330144Seadler	return (-96);
3166330144Seadler#endif
3167286441Srpaulo}
3168286441Srpaulo
3169330224Seadlerstatic void
3170330224Seadleriwm_mvm_handle_rx_statistics(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3171330224Seadler{
3172330224Seadler	struct iwm_notif_statistics_v10 *stats = (void *)&pkt->data;
3173330224Seadler
3174330224Seadler	memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
3175330224Seadler	sc->sc_noise = iwm_get_noise(sc, &stats->rx.general);
3176330224Seadler}
3177330224Seadler
3178286441Srpaulo/*
3179286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler
3180286441Srpaulo *
3181286441Srpaulo * Handles the actual data of the Rx packet from the fw
3182286441Srpaulo */
3183330208Seadlerstatic boolean_t
3184330208Seadleriwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, uint32_t offset,
3185330208Seadler	boolean_t stolen)
3186286441Srpaulo{
3187287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3188286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3189286441Srpaulo	struct ieee80211_frame *wh;
3190286441Srpaulo	struct ieee80211_node *ni;
3191286441Srpaulo	struct ieee80211_rx_stats rxs;
3192286441Srpaulo	struct iwm_rx_phy_info *phy_info;
3193286441Srpaulo	struct iwm_rx_mpdu_res_start *rx_res;
3194330208Seadler	struct iwm_rx_packet *pkt = mtodoff(m, struct iwm_rx_packet *, offset);
3195286441Srpaulo	uint32_t len;
3196286441Srpaulo	uint32_t rx_pkt_status;
3197286441Srpaulo	int rssi;
3198286441Srpaulo
3199286441Srpaulo	phy_info = &sc->sc_last_phy_info;
3200286441Srpaulo	rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
3201286441Srpaulo	wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
3202286441Srpaulo	len = le16toh(rx_res->byte_count);
3203286441Srpaulo	rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len));
3204286441Srpaulo
3205286441Srpaulo	if (__predict_false(phy_info->cfg_phy_cnt > 20)) {
3206286441Srpaulo		device_printf(sc->sc_dev,
3207286441Srpaulo		    "dsp size out of range [0,20]: %d\n",
3208286441Srpaulo		    phy_info->cfg_phy_cnt);
3209330157Seadler		goto fail;
3210286441Srpaulo	}
3211286441Srpaulo
3212286441Srpaulo	if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
3213286441Srpaulo	    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) {
3214286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3215286441Srpaulo		    "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
3216330157Seadler		goto fail;
3217286441Srpaulo	}
3218286441Srpaulo
3219330195Seadler	rssi = iwm_mvm_get_signal_strength(sc, phy_info);
3220286441Srpaulo
3221330144Seadler	/* Map it to relative value */
3222330144Seadler	rssi = rssi - sc->sc_noise;
3223330144Seadler
3224286441Srpaulo	/* replenish ring for the buffer we're going to feed to the sharks */
3225330208Seadler	if (!stolen && iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) {
3226286441Srpaulo		device_printf(sc->sc_dev, "%s: unable to add more buffers\n",
3227286441Srpaulo		    __func__);
3228330157Seadler		goto fail;
3229286441Srpaulo	}
3230286441Srpaulo
3231330194Seadler	m->m_data = pkt->data + sizeof(*rx_res);
3232330194Seadler	m->m_pkthdr.len = m->m_len = len;
3233330194Seadler
3234330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3235330144Seadler	    "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise);
3236330144Seadler
3237286441Srpaulo	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
3238286441Srpaulo
3239286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
3240286441Srpaulo	    "%s: phy_info: channel=%d, flags=0x%08x\n",
3241286441Srpaulo	    __func__,
3242286441Srpaulo	    le16toh(phy_info->channel),
3243286441Srpaulo	    le16toh(phy_info->phy_flags));
3244286441Srpaulo
3245286441Srpaulo	/*
3246286441Srpaulo	 * Populate an RX state struct with the provided information.
3247286441Srpaulo	 */
3248286441Srpaulo	bzero(&rxs, sizeof(rxs));
3249286441Srpaulo	rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
3250286441Srpaulo	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
3251286441Srpaulo	rxs.c_ieee = le16toh(phy_info->channel);
3252286441Srpaulo	if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) {
3253286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ);
3254286441Srpaulo	} else {
3255286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ);
3256286441Srpaulo	}
3257330144Seadler
3258330144Seadler	/* rssi is in 1/2db units */
3259330576Seadler	rxs.rssi = rssi * 2;
3260330576Seadler	rxs.nf = sc->sc_noise;
3261286441Srpaulo
3262286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3263286441Srpaulo		struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap;
3264286441Srpaulo
3265286441Srpaulo		tap->wr_flags = 0;
3266286441Srpaulo		if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE))
3267286441Srpaulo			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
3268286441Srpaulo		tap->wr_chan_freq = htole16(rxs.c_freq);
3269286441Srpaulo		/* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */
3270286441Srpaulo		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
3271286441Srpaulo		tap->wr_dbm_antsignal = (int8_t)rssi;
3272286441Srpaulo		tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
3273286441Srpaulo		tap->wr_tsft = phy_info->system_timestamp;
3274286441Srpaulo		switch (phy_info->rate) {
3275286441Srpaulo		/* CCK rates. */
3276286441Srpaulo		case  10: tap->wr_rate =   2; break;
3277286441Srpaulo		case  20: tap->wr_rate =   4; break;
3278286441Srpaulo		case  55: tap->wr_rate =  11; break;
3279286441Srpaulo		case 110: tap->wr_rate =  22; break;
3280286441Srpaulo		/* OFDM rates. */
3281286441Srpaulo		case 0xd: tap->wr_rate =  12; break;
3282286441Srpaulo		case 0xf: tap->wr_rate =  18; break;
3283286441Srpaulo		case 0x5: tap->wr_rate =  24; break;
3284286441Srpaulo		case 0x7: tap->wr_rate =  36; break;
3285286441Srpaulo		case 0x9: tap->wr_rate =  48; break;
3286286441Srpaulo		case 0xb: tap->wr_rate =  72; break;
3287286441Srpaulo		case 0x1: tap->wr_rate =  96; break;
3288286441Srpaulo		case 0x3: tap->wr_rate = 108; break;
3289286441Srpaulo		/* Unknown rate: should not happen. */
3290286441Srpaulo		default:  tap->wr_rate =   0;
3291286441Srpaulo		}
3292286441Srpaulo	}
3293286441Srpaulo
3294286441Srpaulo	IWM_UNLOCK(sc);
3295286441Srpaulo	if (ni != NULL) {
3296286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m);
3297330576Seadler		ieee80211_input_mimo(ni, m, &rxs);
3298286441Srpaulo		ieee80211_free_node(ni);
3299286441Srpaulo	} else {
3300286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m);
3301330576Seadler		ieee80211_input_mimo_all(ic, m, &rxs);
3302286441Srpaulo	}
3303286441Srpaulo	IWM_LOCK(sc);
3304330157Seadler
3305330208Seadler	return TRUE;
3306330157Seadler
3307330576Seadlerfail:	counter_u64_add(ic->ic_ierrors, 1);
3308330208Seadler	return FALSE;
3309286441Srpaulo}
3310286441Srpaulo
3311293100Savosstatic int
3312286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
3313286441Srpaulo	struct iwm_node *in)
3314286441Srpaulo{
3315286441Srpaulo	struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data;
3316293100Savos	struct ieee80211_node *ni = &in->in_ni;
3317293100Savos	struct ieee80211vap *vap = ni->ni_vap;
3318286441Srpaulo	int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
3319286441Srpaulo	int failack = tx_resp->failure_frame;
3320286441Srpaulo
3321286441Srpaulo	KASSERT(tx_resp->frame_count == 1, ("too many frames"));
3322286441Srpaulo
3323286441Srpaulo	/* Update rate control statistics. */
3324298611Sadrian	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",
3325298611Sadrian	    __func__,
3326298611Sadrian	    (int) le16toh(tx_resp->status.status),
3327298611Sadrian	    (int) le16toh(tx_resp->status.sequence),
3328298611Sadrian	    tx_resp->frame_count,
3329298611Sadrian	    tx_resp->bt_kill_count,
3330298611Sadrian	    tx_resp->failure_rts,
3331298611Sadrian	    tx_resp->failure_frame,
3332298611Sadrian	    le32toh(tx_resp->initial_rate),
3333298611Sadrian	    (int) le16toh(tx_resp->wireless_media_time));
3334298611Sadrian
3335286441Srpaulo	if (status != IWM_TX_STATUS_SUCCESS &&
3336286441Srpaulo	    status != IWM_TX_STATUS_DIRECT_DONE) {
3337293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3338286441Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &failack, NULL);
3339293100Savos		return (1);
3340286441Srpaulo	} else {
3341293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3342286441Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL);
3343293100Savos		return (0);
3344286441Srpaulo	}
3345286441Srpaulo}
3346286441Srpaulo
3347286441Srpaulostatic void
3348330208Seadleriwm_mvm_rx_tx_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3349286441Srpaulo{
3350286441Srpaulo	struct iwm_cmd_header *cmd_hdr = &pkt->hdr;
3351286441Srpaulo	int idx = cmd_hdr->idx;
3352286441Srpaulo	int qid = cmd_hdr->qid;
3353286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[qid];
3354286441Srpaulo	struct iwm_tx_data *txd = &ring->data[idx];
3355286441Srpaulo	struct iwm_node *in = txd->in;
3356293100Savos	struct mbuf *m = txd->m;
3357293100Savos	int status;
3358286441Srpaulo
3359293100Savos	KASSERT(txd->done == 0, ("txd not done"));
3360293100Savos	KASSERT(txd->in != NULL, ("txd without node"));
3361293100Savos	KASSERT(txd->m != NULL, ("txd without mbuf"));
3362293100Savos
3363286441Srpaulo	sc->sc_tx_timer = 0;
3364286441Srpaulo
3365293100Savos	status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in);
3366286441Srpaulo
3367286441Srpaulo	/* Unmap and free mbuf. */
3368286441Srpaulo	bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE);
3369286441Srpaulo	bus_dmamap_unload(ring->data_dmat, txd->map);
3370286441Srpaulo
3371286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3372286441Srpaulo	    "free txd %p, in %p\n", txd, txd->in);
3373286441Srpaulo	txd->done = 1;
3374286441Srpaulo	txd->m = NULL;
3375286441Srpaulo	txd->in = NULL;
3376286441Srpaulo
3377293100Savos	ieee80211_tx_complete(&in->in_ni, m, status);
3378293100Savos
3379286441Srpaulo	if (--ring->queued < IWM_TX_RING_LOMARK) {
3380286441Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
3381287197Sglebius		if (sc->qfullmsk == 0) {
3382287197Sglebius			iwm_start(sc);
3383286441Srpaulo		}
3384286441Srpaulo	}
3385286441Srpaulo}
3386286441Srpaulo
3387286441Srpaulo/*
3388286441Srpaulo * transmit side
3389286441Srpaulo */
3390286441Srpaulo
3391286441Srpaulo/*
3392286441Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
3393286441Srpaulo * processes waiting for a synchronous command completion.
3394286441Srpaulo * from if_iwn
3395286441Srpaulo */
3396286441Srpaulostatic void
3397286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3398286441Srpaulo{
3399286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE];
3400286441Srpaulo	struct iwm_tx_data *data;
3401286441Srpaulo
3402286441Srpaulo	if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) {
3403286441Srpaulo		return;	/* Not a command ack. */
3404286441Srpaulo	}
3405286441Srpaulo
3406330142Seadler	/* XXX wide commands? */
3407330142Seadler	IWM_DPRINTF(sc, IWM_DEBUG_CMD,
3408330142Seadler	    "cmd notification type 0x%x qid %d idx %d\n",
3409330142Seadler	    pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx);
3410330142Seadler
3411286441Srpaulo	data = &ring->data[pkt->hdr.idx];
3412286441Srpaulo
3413286441Srpaulo	/* If the command was mapped in an mbuf, free it. */
3414286441Srpaulo	if (data->m != NULL) {
3415286441Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3416286441Srpaulo		    BUS_DMASYNC_POSTWRITE);
3417286441Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
3418286441Srpaulo		m_freem(data->m);
3419286441Srpaulo		data->m = NULL;
3420286441Srpaulo	}
3421286441Srpaulo	wakeup(&ring->desc[pkt->hdr.idx]);
3422330175Seadler
3423330175Seadler	if (((pkt->hdr.idx + ring->queued) % IWM_TX_RING_COUNT) != ring->cur) {
3424330175Seadler		device_printf(sc->sc_dev,
3425330175Seadler		    "%s: Some HCMDs skipped?: idx=%d queued=%d cur=%d\n",
3426330175Seadler		    __func__, pkt->hdr.idx, ring->queued, ring->cur);
3427330175Seadler		/* XXX call iwm_force_nmi() */
3428330175Seadler	}
3429330175Seadler
3430330175Seadler	KASSERT(ring->queued > 0, ("ring->queued is empty?"));
3431330175Seadler	ring->queued--;
3432330175Seadler	if (ring->queued == 0)
3433330175Seadler		iwm_pcie_clear_cmd_in_flight(sc);
3434286441Srpaulo}
3435286441Srpaulo
3436286441Srpaulo#if 0
3437286441Srpaulo/*
3438286441Srpaulo * necessary only for block ack mode
3439286441Srpaulo */
3440286441Srpaulovoid
3441286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id,
3442286441Srpaulo	uint16_t len)
3443286441Srpaulo{
3444286441Srpaulo	struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
3445286441Srpaulo	uint16_t w_val;
3446286441Srpaulo
3447286441Srpaulo	scd_bc_tbl = sc->sched_dma.vaddr;
3448286441Srpaulo
3449286441Srpaulo	len += 8; /* magic numbers came naturally from paris */
3450330221Seadler	len = roundup(len, 4) / 4;
3451286441Srpaulo
3452286441Srpaulo	w_val = htole16(sta_id << 12 | len);
3453286441Srpaulo
3454286441Srpaulo	/* Update TX scheduler. */
3455286441Srpaulo	scd_bc_tbl[qid].tfd_offset[idx] = w_val;
3456286441Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3457286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3458286441Srpaulo
3459286441Srpaulo	/* I really wonder what this is ?!? */
3460286441Srpaulo	if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) {
3461286441Srpaulo		scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val;
3462286441Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3463286441Srpaulo		    BUS_DMASYNC_PREWRITE);
3464286441Srpaulo	}
3465286441Srpaulo}
3466286441Srpaulo#endif
3467286441Srpaulo
3468286441Srpaulo/*
3469286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate
3470286441Srpaulo * table entry.  return the index into in_ridx[].
3471286441Srpaulo *
3472286441Srpaulo * The caller then uses that index back into in_ridx
3473286441Srpaulo * to figure out the rate index programmed /into/
3474286441Srpaulo * the firmware for this given node.
3475286441Srpaulo */
3476286441Srpaulostatic int
3477286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in,
3478286441Srpaulo    uint8_t rate)
3479286441Srpaulo{
3480286441Srpaulo	int i;
3481286441Srpaulo	uint8_t r;
3482286441Srpaulo
3483286441Srpaulo	for (i = 0; i < nitems(in->in_ridx); i++) {
3484286441Srpaulo		r = iwm_rates[in->in_ridx[i]].rate;
3485286441Srpaulo		if (rate == r)
3486286441Srpaulo			return (i);
3487286441Srpaulo	}
3488330156Seadler
3489330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3490330156Seadler	    "%s: couldn't find an entry for rate=%d\n",
3491330156Seadler	    __func__,
3492330156Seadler	    rate);
3493330156Seadler
3494286441Srpaulo	/* XXX Return the first */
3495286441Srpaulo	/* XXX TODO: have it return the /lowest/ */
3496286441Srpaulo	return (0);
3497286441Srpaulo}
3498286441Srpaulo
3499330156Seadlerstatic int
3500330156Seadleriwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate)
3501330156Seadler{
3502330156Seadler	int i;
3503330156Seadler
3504330156Seadler	for (i = 0; i < nitems(iwm_rates); i++) {
3505330156Seadler		if (iwm_rates[i].rate == rate)
3506330156Seadler			return (i);
3507330156Seadler	}
3508330156Seadler	/* XXX error? */
3509330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3510330156Seadler	    "%s: couldn't find an entry for rate=%d\n",
3511330156Seadler	    __func__,
3512330156Seadler	    rate);
3513330156Seadler	return (0);
3514330156Seadler}
3515330156Seadler
3516286441Srpaulo/*
3517298875Sadrian * Fill in the rate related information for a transmit command.
3518286441Srpaulo */
3519286441Srpaulostatic const struct iwm_rate *
3520286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in,
3521330155Seadler	struct mbuf *m, struct iwm_tx_cmd *tx)
3522286441Srpaulo{
3523286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
3524330155Seadler	struct ieee80211_frame *wh;
3525330155Seadler	const struct ieee80211_txparam *tp = ni->ni_txparms;
3526286441Srpaulo	const struct iwm_rate *rinfo;
3527330155Seadler	int type;
3528330156Seadler	int ridx, rate_flags;
3529286441Srpaulo
3530330155Seadler	wh = mtod(m, struct ieee80211_frame *);
3531330155Seadler	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3532330155Seadler
3533286441Srpaulo	tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT;
3534286441Srpaulo	tx->data_retry_limit = IWM_DEFAULT_TX_RETRY;
3535286441Srpaulo
3536330155Seadler	if (type == IEEE80211_FC0_TYPE_MGT) {
3537330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3538330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3539330156Seadler		    "%s: MGT (%d)\n", __func__, tp->mgmtrate);
3540330155Seadler	} else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3541330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mcastrate);
3542330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3543330156Seadler		    "%s: MCAST (%d)\n", __func__, tp->mcastrate);
3544330155Seadler	} else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
3545330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->ucastrate);
3546330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3547330156Seadler		    "%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate);
3548330155Seadler	} else if (m->m_flags & M_EAPOL) {
3549330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3550330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3551330156Seadler		    "%s: EAPOL\n", __func__);
3552330156Seadler	} else if (type == IEEE80211_FC0_TYPE_DATA) {
3553330156Seadler		int i;
3554330156Seadler
3555286441Srpaulo		/* for data frames, use RS table */
3556330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__);
3557330155Seadler		/* XXX pass pktlen */
3558286441Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3559286441Srpaulo		i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate);
3560286441Srpaulo		ridx = in->in_ridx[i];
3561286441Srpaulo
3562286441Srpaulo		/* This is the index into the programmed table */
3563286441Srpaulo		tx->initial_rate_index = i;
3564286441Srpaulo		tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
3565330156Seadler
3566286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3567286441Srpaulo		    "%s: start with i=%d, txrate %d\n",
3568286441Srpaulo		    __func__, i, iwm_rates[ridx].rate);
3569330156Seadler	} else {
3570330156Seadler		ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate);
3571330156Seadler		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DEFAULT (%d)\n",
3572330156Seadler		    __func__, tp->mgmtrate);
3573286441Srpaulo	}
3574286441Srpaulo
3575330156Seadler	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3576330156Seadler	    "%s: frame type=%d txrate %d\n",
3577330156Seadler	        __func__, type, iwm_rates[ridx].rate);
3578330156Seadler
3579286441Srpaulo	rinfo = &iwm_rates[ridx];
3580286441Srpaulo
3581286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n",
3582286441Srpaulo	    __func__, ridx,
3583286441Srpaulo	    rinfo->rate,
3584286441Srpaulo	    !! (IWM_RIDX_IS_CCK(ridx))
3585286441Srpaulo	    );
3586286441Srpaulo
3587286441Srpaulo	/* XXX TODO: hard-coded TX antenna? */
3588286441Srpaulo	rate_flags = 1 << IWM_RATE_MCS_ANT_POS;
3589286441Srpaulo	if (IWM_RIDX_IS_CCK(ridx))
3590286441Srpaulo		rate_flags |= IWM_RATE_MCS_CCK_MSK;
3591286441Srpaulo	tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
3592286441Srpaulo
3593286441Srpaulo	return rinfo;
3594286441Srpaulo}
3595286441Srpaulo
3596286441Srpaulo#define TB0_SIZE 16
3597286441Srpaulostatic int
3598286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac)
3599286441Srpaulo{
3600287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3601286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3602293099Savos	struct iwm_node *in = IWM_NODE(ni);
3603286441Srpaulo	struct iwm_tx_ring *ring;
3604286441Srpaulo	struct iwm_tx_data *data;
3605286441Srpaulo	struct iwm_tfd *desc;
3606286441Srpaulo	struct iwm_device_cmd *cmd;
3607286441Srpaulo	struct iwm_tx_cmd *tx;
3608286441Srpaulo	struct ieee80211_frame *wh;
3609286441Srpaulo	struct ieee80211_key *k = NULL;
3610286441Srpaulo	struct mbuf *m1;
3611286441Srpaulo	const struct iwm_rate *rinfo;
3612286441Srpaulo	uint32_t flags;
3613286441Srpaulo	u_int hdrlen;
3614286441Srpaulo	bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
3615286441Srpaulo	int nsegs;
3616286441Srpaulo	uint8_t tid, type;
3617286441Srpaulo	int i, totlen, error, pad;
3618286441Srpaulo
3619286441Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3620286441Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3621286441Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3622286441Srpaulo	tid = 0;
3623286441Srpaulo	ring = &sc->txq[ac];
3624286441Srpaulo	desc = &ring->desc[ring->cur];
3625286441Srpaulo	memset(desc, 0, sizeof(*desc));
3626286441Srpaulo	data = &ring->data[ring->cur];
3627286441Srpaulo
3628286441Srpaulo	/* Fill out iwm_tx_cmd to send to the firmware */
3629286441Srpaulo	cmd = &ring->cmd[ring->cur];
3630286441Srpaulo	cmd->hdr.code = IWM_TX_CMD;
3631286441Srpaulo	cmd->hdr.flags = 0;
3632286441Srpaulo	cmd->hdr.qid = ring->qid;
3633286441Srpaulo	cmd->hdr.idx = ring->cur;
3634286441Srpaulo
3635286441Srpaulo	tx = (void *)cmd->data;
3636286441Srpaulo	memset(tx, 0, sizeof(*tx));
3637286441Srpaulo
3638330155Seadler	rinfo = iwm_tx_fill_cmd(sc, in, m, tx);
3639286441Srpaulo
3640286441Srpaulo	/* Encrypt the frame if need be. */
3641286441Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
3642286441Srpaulo		/* Retrieve key for TX && do software encryption. */
3643286441Srpaulo		k = ieee80211_crypto_encap(ni, m);
3644286441Srpaulo		if (k == NULL) {
3645286441Srpaulo			m_freem(m);
3646286441Srpaulo			return (ENOBUFS);
3647286441Srpaulo		}
3648286441Srpaulo		/* 802.11 header may have moved. */
3649286441Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3650286441Srpaulo	}
3651286441Srpaulo
3652286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3653286441Srpaulo		struct iwm_tx_radiotap_header *tap = &sc->sc_txtap;
3654286441Srpaulo
3655286441Srpaulo		tap->wt_flags = 0;
3656286441Srpaulo		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
3657286441Srpaulo		tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
3658286441Srpaulo		tap->wt_rate = rinfo->rate;
3659286441Srpaulo		if (k != NULL)
3660286441Srpaulo			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3661286441Srpaulo		ieee80211_radiotap_tx(vap, m);
3662286441Srpaulo	}
3663286441Srpaulo
3664286441Srpaulo
3665286441Srpaulo	totlen = m->m_pkthdr.len;
3666286441Srpaulo
3667286441Srpaulo	flags = 0;
3668286441Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3669286441Srpaulo		flags |= IWM_TX_CMD_FLG_ACK;
3670286441Srpaulo	}
3671286441Srpaulo
3672303628Ssbruno	if (type == IEEE80211_FC0_TYPE_DATA
3673286441Srpaulo	    && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
3674286441Srpaulo	    && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3675286441Srpaulo		flags |= IWM_TX_CMD_FLG_PROT_REQUIRE;
3676286441Srpaulo	}
3677286441Srpaulo
3678286441Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3679286441Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3680286441Srpaulo		tx->sta_id = sc->sc_aux_sta.sta_id;
3681286441Srpaulo	else
3682286441Srpaulo		tx->sta_id = IWM_STATION_ID;
3683286441Srpaulo
3684286441Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3685286441Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3686286441Srpaulo
3687286441Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3688303628Ssbruno		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
3689303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC);
3690303628Ssbruno		} else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) {
3691303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3692303628Ssbruno		} else {
3693303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT);
3694303628Ssbruno		}
3695286441Srpaulo	} else {
3696303628Ssbruno		tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3697286441Srpaulo	}
3698286441Srpaulo
3699286441Srpaulo	if (hdrlen & 3) {
3700286441Srpaulo		/* First segment length must be a multiple of 4. */
3701286441Srpaulo		flags |= IWM_TX_CMD_FLG_MH_PAD;
3702286441Srpaulo		pad = 4 - (hdrlen & 3);
3703286441Srpaulo	} else
3704286441Srpaulo		pad = 0;
3705286441Srpaulo
3706286441Srpaulo	tx->driver_txop = 0;
3707286441Srpaulo	tx->next_frame_len = 0;
3708286441Srpaulo
3709286441Srpaulo	tx->len = htole16(totlen);
3710286441Srpaulo	tx->tid_tspec = tid;
3711286441Srpaulo	tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE);
3712286441Srpaulo
3713286441Srpaulo	/* Set physical address of "scratch area". */
3714286441Srpaulo	tx->dram_lsb_ptr = htole32(data->scratch_paddr);
3715286441Srpaulo	tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr);
3716286441Srpaulo
3717286441Srpaulo	/* Copy 802.11 header in TX command. */
3718286441Srpaulo	memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen);
3719286441Srpaulo
3720286441Srpaulo	flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL;
3721286441Srpaulo
3722286441Srpaulo	tx->sec_ctl = 0;
3723286441Srpaulo	tx->tx_flags |= htole32(flags);
3724286441Srpaulo
3725286441Srpaulo	/* Trim 802.11 header. */
3726286441Srpaulo	m_adj(m, hdrlen);
3727286441Srpaulo	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3728286441Srpaulo	    segs, &nsegs, BUS_DMA_NOWAIT);
3729286441Srpaulo	if (error != 0) {
3730286441Srpaulo		if (error != EFBIG) {
3731286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3732286441Srpaulo			    error);
3733286441Srpaulo			m_freem(m);
3734286441Srpaulo			return error;
3735286441Srpaulo		}
3736286441Srpaulo		/* Too many DMA segments, linearize mbuf. */
3737293119Savos		m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2);
3738286441Srpaulo		if (m1 == NULL) {
3739293119Savos			device_printf(sc->sc_dev,
3740293119Savos			    "%s: could not defrag mbuf\n", __func__);
3741286441Srpaulo			m_freem(m);
3742293119Savos			return (ENOBUFS);
3743286441Srpaulo		}
3744286441Srpaulo		m = m1;
3745293119Savos
3746286441Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3747286441Srpaulo		    segs, &nsegs, BUS_DMA_NOWAIT);
3748286441Srpaulo		if (error != 0) {
3749286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3750286441Srpaulo			    error);
3751286441Srpaulo			m_freem(m);
3752286441Srpaulo			return error;
3753286441Srpaulo		}
3754286441Srpaulo	}
3755286441Srpaulo	data->m = m;
3756286441Srpaulo	data->in = in;
3757286441Srpaulo	data->done = 0;
3758286441Srpaulo
3759286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3760286441Srpaulo	    "sending txd %p, in %p\n", data, data->in);
3761286441Srpaulo	KASSERT(data->in != NULL, ("node is NULL"));
3762286441Srpaulo
3763286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3764303628Ssbruno	    "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n",
3765298611Sadrian	    ring->qid, ring->cur, totlen, nsegs,
3766298611Sadrian	    le32toh(tx->tx_flags),
3767298611Sadrian	    le32toh(tx->rate_n_flags),
3768303628Ssbruno	    tx->initial_rate_index
3769298611Sadrian	    );
3770286441Srpaulo
3771286441Srpaulo	/* Fill TX descriptor. */
3772286441Srpaulo	desc->num_tbs = 2 + nsegs;
3773286441Srpaulo
3774286441Srpaulo	desc->tbs[0].lo = htole32(data->cmd_paddr);
3775286441Srpaulo	desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3776286441Srpaulo	    (TB0_SIZE << 4);
3777286441Srpaulo	desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE);
3778286441Srpaulo	desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3779286441Srpaulo	    ((sizeof(struct iwm_cmd_header) + sizeof(*tx)
3780286441Srpaulo	      + hdrlen + pad - TB0_SIZE) << 4);
3781286441Srpaulo
3782286441Srpaulo	/* Other DMA segments are for data payload. */
3783286441Srpaulo	for (i = 0; i < nsegs; i++) {
3784286441Srpaulo		seg = &segs[i];
3785286441Srpaulo		desc->tbs[i+2].lo = htole32(seg->ds_addr);
3786286441Srpaulo		desc->tbs[i+2].hi_n_len = \
3787286441Srpaulo		    htole16(iwm_get_dma_hi_addr(seg->ds_addr))
3788286441Srpaulo		    | ((seg->ds_len) << 4);
3789286441Srpaulo	}
3790286441Srpaulo
3791286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map,
3792286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3793286441Srpaulo	bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
3794286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3795286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3796286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3797286441Srpaulo
3798286441Srpaulo#if 0
3799286441Srpaulo	iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len));
3800286441Srpaulo#endif
3801286441Srpaulo
3802286441Srpaulo	/* Kick TX ring. */
3803286441Srpaulo	ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
3804286441Srpaulo	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3805286441Srpaulo
3806286441Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3807286441Srpaulo	if (++ring->queued > IWM_TX_RING_HIMARK) {
3808286441Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3809286441Srpaulo	}
3810286441Srpaulo
3811286441Srpaulo	return 0;
3812286441Srpaulo}
3813286441Srpaulo
3814286441Srpaulostatic int
3815286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3816286441Srpaulo    const struct ieee80211_bpf_params *params)
3817286441Srpaulo{
3818286441Srpaulo	struct ieee80211com *ic = ni->ni_ic;
3819286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
3820286441Srpaulo	int error = 0;
3821286441Srpaulo
3822286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3823286441Srpaulo	    "->%s begin\n", __func__);
3824286441Srpaulo
3825287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
3826286441Srpaulo		m_freem(m);
3827286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3828286441Srpaulo		    "<-%s not RUNNING\n", __func__);
3829286441Srpaulo		return (ENETDOWN);
3830286441Srpaulo        }
3831286441Srpaulo
3832286441Srpaulo	IWM_LOCK(sc);
3833286441Srpaulo	/* XXX fix this */
3834286441Srpaulo        if (params == NULL) {
3835286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3836286441Srpaulo	} else {
3837286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3838286441Srpaulo	}
3839286441Srpaulo	sc->sc_tx_timer = 5;
3840286441Srpaulo	IWM_UNLOCK(sc);
3841286441Srpaulo
3842286441Srpaulo        return (error);
3843286441Srpaulo}
3844286441Srpaulo
3845286441Srpaulo/*
3846286441Srpaulo * mvm/tx.c
3847286441Srpaulo */
3848286441Srpaulo
3849286441Srpaulo/*
3850286441Srpaulo * Note that there are transports that buffer frames before they reach
3851286441Srpaulo * the firmware. This means that after flush_tx_path is called, the
3852286441Srpaulo * queue might not be empty. The race-free way to handle this is to:
3853286441Srpaulo * 1) set the station as draining
3854286441Srpaulo * 2) flush the Tx path
3855286441Srpaulo * 3) wait for the transport queues to be empty
3856286441Srpaulo */
3857286441Srpauloint
3858330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags)
3859286441Srpaulo{
3860330154Seadler	int ret;
3861286441Srpaulo	struct iwm_tx_path_flush_cmd flush_cmd = {
3862286441Srpaulo		.queues_ctl = htole32(tfd_msk),
3863286441Srpaulo		.flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH),
3864286441Srpaulo	};
3865286441Srpaulo
3866330154Seadler	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags,
3867286441Srpaulo	    sizeof(flush_cmd), &flush_cmd);
3868286441Srpaulo	if (ret)
3869286441Srpaulo                device_printf(sc->sc_dev,
3870286441Srpaulo		    "Flushing tx queue failed: %d\n", ret);
3871286441Srpaulo	return ret;
3872286441Srpaulo}
3873286441Srpaulo
3874286441Srpaulo/*
3875286441Srpaulo * BEGIN mvm/quota.c
3876286441Srpaulo */
3877286441Srpaulo
3878286441Srpaulostatic int
3879330203Seadleriwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_vap *ivp)
3880286441Srpaulo{
3881286441Srpaulo	struct iwm_time_quota_cmd cmd;
3882286441Srpaulo	int i, idx, ret, num_active_macs, quota, quota_rem;
3883286441Srpaulo	int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, };
3884286441Srpaulo	int n_ifs[IWM_MAX_BINDINGS] = {0, };
3885286441Srpaulo	uint16_t id;
3886286441Srpaulo
3887286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3888286441Srpaulo
3889286441Srpaulo	/* currently, PHY ID == binding ID */
3890330203Seadler	if (ivp) {
3891330203Seadler		id = ivp->phy_ctxt->id;
3892286441Srpaulo		KASSERT(id < IWM_MAX_BINDINGS, ("invalid id"));
3893330203Seadler		colors[id] = ivp->phy_ctxt->color;
3894286441Srpaulo
3895286441Srpaulo		if (1)
3896286441Srpaulo			n_ifs[id] = 1;
3897286441Srpaulo	}
3898286441Srpaulo
3899286441Srpaulo	/*
3900286441Srpaulo	 * The FW's scheduling session consists of
3901286441Srpaulo	 * IWM_MVM_MAX_QUOTA fragments. Divide these fragments
3902286441Srpaulo	 * equally between all the bindings that require quota
3903286441Srpaulo	 */
3904286441Srpaulo	num_active_macs = 0;
3905286441Srpaulo	for (i = 0; i < IWM_MAX_BINDINGS; i++) {
3906286441Srpaulo		cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID);
3907286441Srpaulo		num_active_macs += n_ifs[i];
3908286441Srpaulo	}
3909286441Srpaulo
3910286441Srpaulo	quota = 0;
3911286441Srpaulo	quota_rem = 0;
3912286441Srpaulo	if (num_active_macs) {
3913286441Srpaulo		quota = IWM_MVM_MAX_QUOTA / num_active_macs;
3914286441Srpaulo		quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs;
3915286441Srpaulo	}
3916286441Srpaulo
3917286441Srpaulo	for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) {
3918286441Srpaulo		if (colors[i] < 0)
3919286441Srpaulo			continue;
3920286441Srpaulo
3921286441Srpaulo		cmd.quotas[idx].id_and_color =
3922286441Srpaulo			htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i]));
3923286441Srpaulo
3924286441Srpaulo		if (n_ifs[i] <= 0) {
3925286441Srpaulo			cmd.quotas[idx].quota = htole32(0);
3926286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3927286441Srpaulo		} else {
3928286441Srpaulo			cmd.quotas[idx].quota = htole32(quota * n_ifs[i]);
3929286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3930286441Srpaulo		}
3931286441Srpaulo		idx++;
3932286441Srpaulo	}
3933286441Srpaulo
3934286441Srpaulo	/* Give the remainder of the session to the first binding */
3935286441Srpaulo	cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem);
3936286441Srpaulo
3937286441Srpaulo	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC,
3938286441Srpaulo	    sizeof(cmd), &cmd);
3939286441Srpaulo	if (ret)
3940286441Srpaulo		device_printf(sc->sc_dev,
3941286441Srpaulo		    "%s: Failed to send quota: %d\n", __func__, ret);
3942286441Srpaulo	return ret;
3943286441Srpaulo}
3944286441Srpaulo
3945286441Srpaulo/*
3946286441Srpaulo * END mvm/quota.c
3947286441Srpaulo */
3948286441Srpaulo
3949286441Srpaulo/*
3950286441Srpaulo * ieee80211 routines
3951286441Srpaulo */
3952286441Srpaulo
3953286441Srpaulo/*
3954286441Srpaulo * Change to AUTH state in 80211 state machine.  Roughly matches what
3955286441Srpaulo * Linux does in bss_info_changed().
3956286441Srpaulo */
3957286441Srpaulostatic int
3958286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc)
3959286441Srpaulo{
3960286441Srpaulo	struct ieee80211_node *ni;
3961286441Srpaulo	struct iwm_node *in;
3962286441Srpaulo	struct iwm_vap *iv = IWM_VAP(vap);
3963286441Srpaulo	uint32_t duration;
3964286441Srpaulo	int error;
3965286441Srpaulo
3966286441Srpaulo	/*
3967286441Srpaulo	 * XXX i have a feeling that the vap node is being
3968286441Srpaulo	 * freed from underneath us. Grr.
3969286441Srpaulo	 */
3970286441Srpaulo	ni = ieee80211_ref_node(vap->iv_bss);
3971293099Savos	in = IWM_NODE(ni);
3972286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE,
3973286441Srpaulo	    "%s: called; vap=%p, bss ni=%p\n",
3974286441Srpaulo	    __func__,
3975286441Srpaulo	    vap,
3976286441Srpaulo	    ni);
3977286441Srpaulo
3978286441Srpaulo	in->in_assoc = 0;
3979286441Srpaulo
3980330212Seadler	/*
3981330212Seadler	 * Firmware bug - it'll crash if the beacon interval is less
3982330212Seadler	 * than 16. We can't avoid connecting at all, so refuse the
3983330212Seadler	 * station state change, this will cause net80211 to abandon
3984330212Seadler	 * attempts to connect to this AP, and eventually wpa_s will
3985330212Seadler	 * blacklist the AP...
3986330212Seadler	 */
3987330212Seadler	if (ni->ni_intval < 16) {
3988330212Seadler		device_printf(sc->sc_dev,
3989330212Seadler		    "AP %s beacon interval is %d, refusing due to firmware bug!\n",
3990330212Seadler		    ether_sprintf(ni->ni_bssid), ni->ni_intval);
3991330212Seadler		error = EINVAL;
3992330212Seadler		goto out;
3993330212Seadler	}
3994330212Seadler
3995303628Ssbruno	error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON);
3996303628Ssbruno	if (error != 0)
3997303628Ssbruno		return error;
3998303628Ssbruno
3999286441Srpaulo	error = iwm_allow_mcast(vap, sc);
4000286441Srpaulo	if (error) {
4001286441Srpaulo		device_printf(sc->sc_dev,
4002286441Srpaulo		    "%s: failed to set multicast\n", __func__);
4003286441Srpaulo		goto out;
4004286441Srpaulo	}
4005286441Srpaulo
4006286441Srpaulo	/*
4007286441Srpaulo	 * This is where it deviates from what Linux does.
4008286441Srpaulo	 *
4009286441Srpaulo	 * Linux iwlwifi doesn't reset the nic each time, nor does it
4010286441Srpaulo	 * call ctxt_add() here.  Instead, it adds it during vap creation,
4011303628Ssbruno	 * and always does a mac_ctx_changed().
4012286441Srpaulo	 *
4013286441Srpaulo	 * The openbsd port doesn't attempt to do that - it reset things
4014286441Srpaulo	 * at odd states and does the add here.
4015286441Srpaulo	 *
4016286441Srpaulo	 * So, until the state handling is fixed (ie, we never reset
4017286441Srpaulo	 * the NIC except for a firmware failure, which should drag
4018286441Srpaulo	 * the NIC back to IDLE, re-setup and re-add all the mac/phy
4019286441Srpaulo	 * contexts that are required), let's do a dirty hack here.
4020286441Srpaulo	 */
4021286441Srpaulo	if (iv->is_uploaded) {
4022286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
4023286441Srpaulo			device_printf(sc->sc_dev,
4024298582Sadrian			    "%s: failed to update MAC\n", __func__);
4025286441Srpaulo			goto out;
4026286441Srpaulo		}
4027298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
4028298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
4029298582Sadrian			device_printf(sc->sc_dev,
4030298582Sadrian			    "%s: failed update phy ctxt\n", __func__);
4031298582Sadrian			goto out;
4032298582Sadrian		}
4033330203Seadler		iv->phy_ctxt = &sc->sc_phyctxt[0];
4034298582Sadrian
4035330204Seadler		if ((error = iwm_mvm_binding_add_vif(sc, iv)) != 0) {
4036298582Sadrian			device_printf(sc->sc_dev,
4037298582Sadrian			    "%s: binding update cmd\n", __func__);
4038298582Sadrian			goto out;
4039298582Sadrian		}
4040298582Sadrian		if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
4041298582Sadrian			device_printf(sc->sc_dev,
4042298582Sadrian			    "%s: failed to update sta\n", __func__);
4043298582Sadrian			goto out;
4044298582Sadrian		}
4045286441Srpaulo	} else {
4046286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) {
4047286441Srpaulo			device_printf(sc->sc_dev,
4048286441Srpaulo			    "%s: failed to add MAC\n", __func__);
4049286441Srpaulo			goto out;
4050286441Srpaulo		}
4051330201Seadler		if ((error = iwm_mvm_power_update_mac(sc)) != 0) {
4052330201Seadler			device_printf(sc->sc_dev,
4053330201Seadler			    "%s: failed to update power management\n",
4054330201Seadler			    __func__);
4055330201Seadler			goto out;
4056330201Seadler		}
4057298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
4058298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
4059286441Srpaulo			device_printf(sc->sc_dev,
4060298582Sadrian			    "%s: failed add phy ctxt!\n", __func__);
4061286441Srpaulo			error = ETIMEDOUT;
4062286441Srpaulo			goto out;
4063298582Sadrian		}
4064330203Seadler		iv->phy_ctxt = &sc->sc_phyctxt[0];
4065298582Sadrian
4066330203Seadler		if ((error = iwm_mvm_binding_add_vif(sc, iv)) != 0) {
4067286441Srpaulo			device_printf(sc->sc_dev,
4068298582Sadrian			    "%s: binding add cmd\n", __func__);
4069286441Srpaulo			goto out;
4070286441Srpaulo		}
4071298582Sadrian		if ((error = iwm_mvm_add_sta(sc, in)) != 0) {
4072298582Sadrian			device_printf(sc->sc_dev,
4073298582Sadrian			    "%s: failed to add sta\n", __func__);
4074298582Sadrian			goto out;
4075298582Sadrian		}
4076286441Srpaulo	}
4077298582Sadrian
4078298582Sadrian	/*
4079298582Sadrian	 * Prevent the FW from wandering off channel during association
4080298582Sadrian	 * by "protecting" the session with a time event.
4081298582Sadrian	 */
4082298582Sadrian	/* XXX duration is in units of TU, not MS */
4083298582Sadrian	duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
4084330204Seadler	iwm_mvm_protect_session(sc, iv, duration, 500 /* XXX magic number */);
4085298582Sadrian	DELAY(100);
4086298582Sadrian
4087286441Srpaulo	error = 0;
4088286441Srpauloout:
4089286441Srpaulo	ieee80211_free_node(ni);
4090286441Srpaulo	return (error);
4091286441Srpaulo}
4092286441Srpaulo
4093286441Srpaulostatic int
4094286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc)
4095286441Srpaulo{
4096293099Savos	struct iwm_node *in = IWM_NODE(vap->iv_bss);
4097286441Srpaulo	int error;
4098286441Srpaulo
4099286441Srpaulo	if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
4100286441Srpaulo		device_printf(sc->sc_dev,
4101286441Srpaulo		    "%s: failed to update STA\n", __func__);
4102286441Srpaulo		return error;
4103286441Srpaulo	}
4104286441Srpaulo
4105286441Srpaulo	in->in_assoc = 1;
4106286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
4107286441Srpaulo		device_printf(sc->sc_dev,
4108286441Srpaulo		    "%s: failed to update MAC\n", __func__);
4109286441Srpaulo		return error;
4110286441Srpaulo	}
4111286441Srpaulo
4112286441Srpaulo	return 0;
4113286441Srpaulo}
4114286441Srpaulo
4115286441Srpaulostatic int
4116286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in)
4117286441Srpaulo{
4118330154Seadler	uint32_t tfd_msk;
4119330154Seadler
4120286441Srpaulo	/*
4121286441Srpaulo	 * Ok, so *technically* the proper set of calls for going
4122286441Srpaulo	 * from RUN back to SCAN is:
4123286441Srpaulo	 *
4124286441Srpaulo	 * iwm_mvm_power_mac_disable(sc, in);
4125330209Seadler	 * iwm_mvm_mac_ctxt_changed(sc, vap);
4126286441Srpaulo	 * iwm_mvm_rm_sta(sc, in);
4127286441Srpaulo	 * iwm_mvm_update_quotas(sc, NULL);
4128286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
4129330204Seadler	 * iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap));
4130286441Srpaulo	 * iwm_mvm_mac_ctxt_remove(sc, in);
4131286441Srpaulo	 *
4132286441Srpaulo	 * However, that freezes the device not matter which permutations
4133286441Srpaulo	 * and modifications are attempted.  Obviously, this driver is missing
4134286441Srpaulo	 * something since it works in the Linux driver, but figuring out what
4135286441Srpaulo	 * is missing is a little more complicated.  Now, since we're going
4136286441Srpaulo	 * back to nothing anyway, we'll just do a complete device reset.
4137286441Srpaulo	 * Up your's, device!
4138286441Srpaulo	 */
4139330154Seadler	/*
4140330154Seadler	 * Just using 0xf for the queues mask is fine as long as we only
4141330154Seadler	 * get here from RUN state.
4142330154Seadler	 */
4143330154Seadler	tfd_msk = 0xf;
4144330223Seadler	iwm_xmit_queue_drain(sc);
4145330154Seadler	iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC);
4146330154Seadler	/*
4147330154Seadler	 * We seem to get away with just synchronously sending the
4148330154Seadler	 * IWM_TXPATH_FLUSH command.
4149330154Seadler	 */
4150330154Seadler//	iwm_trans_wait_tx_queue_empty(sc, tfd_msk);
4151286441Srpaulo	iwm_stop_device(sc);
4152286441Srpaulo	iwm_init_hw(sc);
4153286441Srpaulo	if (in)
4154286441Srpaulo		in->in_assoc = 0;
4155286441Srpaulo	return 0;
4156286441Srpaulo
4157286441Srpaulo#if 0
4158286441Srpaulo	int error;
4159286441Srpaulo
4160286441Srpaulo	iwm_mvm_power_mac_disable(sc, in);
4161286441Srpaulo
4162330209Seadler	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
4163286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error);
4164286441Srpaulo		return error;
4165286441Srpaulo	}
4166286441Srpaulo
4167286441Srpaulo	if ((error = iwm_mvm_rm_sta(sc, in)) != 0) {
4168286441Srpaulo		device_printf(sc->sc_dev, "sta remove fail %d\n", error);
4169286441Srpaulo		return error;
4170286441Srpaulo	}
4171286441Srpaulo	error = iwm_mvm_rm_sta(sc, in);
4172286441Srpaulo	in->in_assoc = 0;
4173286441Srpaulo	iwm_mvm_update_quotas(sc, NULL);
4174330209Seadler	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
4175286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error);
4176286441Srpaulo		return error;
4177286441Srpaulo	}
4178330204Seadler	iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap));
4179286441Srpaulo
4180286441Srpaulo	iwm_mvm_mac_ctxt_remove(sc, in);
4181286441Srpaulo
4182286441Srpaulo	return error;
4183286441Srpaulo#endif
4184286441Srpaulo}
4185286441Srpaulo
4186286441Srpaulostatic struct ieee80211_node *
4187286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
4188286441Srpaulo{
4189286441Srpaulo	return malloc(sizeof (struct iwm_node), M_80211_NODE,
4190286441Srpaulo	    M_NOWAIT | M_ZERO);
4191286441Srpaulo}
4192286441Srpaulo
4193330198Seadleruint8_t
4194330198Seadleriwm_ridx2rate(struct ieee80211_rateset *rs, int ridx)
4195330198Seadler{
4196330198Seadler	int i;
4197330198Seadler	uint8_t rval;
4198330198Seadler
4199330198Seadler	for (i = 0; i < rs->rs_nrates; i++) {
4200330198Seadler		rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL);
4201330198Seadler		if (rval == iwm_rates[ridx].rate)
4202330198Seadler			return rs->rs_rates[i];
4203330198Seadler	}
4204330198Seadler
4205330198Seadler	return 0;
4206330198Seadler}
4207330198Seadler
4208286441Srpaulostatic void
4209286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
4210286441Srpaulo{
4211286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
4212286441Srpaulo	struct iwm_lq_cmd *lq = &in->in_lq;
4213286441Srpaulo	int nrates = ni->ni_rates.rs_nrates;
4214286441Srpaulo	int i, ridx, tab = 0;
4215330156Seadler//	int txant = 0;
4216286441Srpaulo
4217286441Srpaulo	if (nrates > nitems(lq->rs_table)) {
4218286441Srpaulo		device_printf(sc->sc_dev,
4219286441Srpaulo		    "%s: node supports %d rates, driver handles "
4220286441Srpaulo		    "only %zu\n", __func__, nrates, nitems(lq->rs_table));
4221286441Srpaulo		return;
4222286441Srpaulo	}
4223294248Sadrian	if (nrates == 0) {
4224294248Sadrian		device_printf(sc->sc_dev,
4225294248Sadrian		    "%s: node supports 0 rates, odd!\n", __func__);
4226294248Sadrian		return;
4227294248Sadrian	}
4228286441Srpaulo
4229286441Srpaulo	/*
4230286441Srpaulo	 * XXX .. and most of iwm_node is not initialised explicitly;
4231286441Srpaulo	 * it's all just 0x0 passed to the firmware.
4232286441Srpaulo	 */
4233286441Srpaulo
4234286441Srpaulo	/* first figure out which rates we should support */
4235286441Srpaulo	/* XXX TODO: this isn't 11n aware /at all/ */
4236286441Srpaulo	memset(&in->in_ridx, -1, sizeof(in->in_ridx));
4237286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4238286441Srpaulo	    "%s: nrates=%d\n", __func__, nrates);
4239286441Srpaulo
4240294248Sadrian	/*
4241294248Sadrian	 * Loop over nrates and populate in_ridx from the highest
4242294248Sadrian	 * rate to the lowest rate.  Remember, in_ridx[] has
4243294248Sadrian	 * IEEE80211_RATE_MAXSIZE entries!
4244294248Sadrian	 */
4245294248Sadrian	for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) {
4246294248Sadrian		int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL;
4247294248Sadrian
4248286441Srpaulo		/* Map 802.11 rate to HW rate index. */
4249286441Srpaulo		for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
4250286441Srpaulo			if (iwm_rates[ridx].rate == rate)
4251286441Srpaulo				break;
4252286441Srpaulo		if (ridx > IWM_RIDX_MAX) {
4253286441Srpaulo			device_printf(sc->sc_dev,
4254286441Srpaulo			    "%s: WARNING: device rate for %d not found!\n",
4255286441Srpaulo			    __func__, rate);
4256286441Srpaulo		} else {
4257286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4258286441Srpaulo			    "%s: rate: i: %d, rate=%d, ridx=%d\n",
4259286441Srpaulo			    __func__,
4260286441Srpaulo			    i,
4261286441Srpaulo			    rate,
4262286441Srpaulo			    ridx);
4263286441Srpaulo			in->in_ridx[i] = ridx;
4264286441Srpaulo		}
4265286441Srpaulo	}
4266286441Srpaulo
4267286441Srpaulo	/* then construct a lq_cmd based on those */
4268286441Srpaulo	memset(lq, 0, sizeof(*lq));
4269286441Srpaulo	lq->sta_id = IWM_STATION_ID;
4270286441Srpaulo
4271303628Ssbruno	/* For HT, always enable RTS/CTS to avoid excessive retries. */
4272303628Ssbruno	if (ni->ni_flags & IEEE80211_NODE_HT)
4273303628Ssbruno		lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK;
4274303628Ssbruno
4275286441Srpaulo	/*
4276286441Srpaulo	 * are these used? (we don't do SISO or MIMO)
4277286441Srpaulo	 * need to set them to non-zero, though, or we get an error.
4278286441Srpaulo	 */
4279286441Srpaulo	lq->single_stream_ant_msk = 1;
4280286441Srpaulo	lq->dual_stream_ant_msk = 1;
4281286441Srpaulo
4282286441Srpaulo	/*
4283286441Srpaulo	 * Build the actual rate selection table.
4284286441Srpaulo	 * The lowest bits are the rates.  Additionally,
4285286441Srpaulo	 * CCK needs bit 9 to be set.  The rest of the bits
4286286441Srpaulo	 * we add to the table select the tx antenna
4287286441Srpaulo	 * Note that we add the rates in the highest rate first
4288286441Srpaulo	 * (opposite of ni_rates).
4289286441Srpaulo	 */
4290286441Srpaulo	/*
4291286441Srpaulo	 * XXX TODO: this should be looping over the min of nrates
4292286441Srpaulo	 * and LQ_MAX_RETRY_NUM.  Sigh.
4293286441Srpaulo	 */
4294286441Srpaulo	for (i = 0; i < nrates; i++) {
4295286441Srpaulo		int nextant;
4296286441Srpaulo
4297330156Seadler#if 0
4298286441Srpaulo		if (txant == 0)
4299330167Seadler			txant = iwm_mvm_get_valid_tx_ant(sc);
4300286441Srpaulo		nextant = 1<<(ffs(txant)-1);
4301286441Srpaulo		txant &= ~nextant;
4302330156Seadler#else
4303330167Seadler		nextant = iwm_mvm_get_valid_tx_ant(sc);
4304330156Seadler#endif
4305286441Srpaulo		/*
4306286441Srpaulo		 * Map the rate id into a rate index into
4307286441Srpaulo		 * our hardware table containing the
4308286441Srpaulo		 * configuration to use for this rate.
4309286441Srpaulo		 */
4310294248Sadrian		ridx = in->in_ridx[i];
4311286441Srpaulo		tab = iwm_rates[ridx].plcp;
4312286441Srpaulo		tab |= nextant << IWM_RATE_MCS_ANT_POS;
4313286441Srpaulo		if (IWM_RIDX_IS_CCK(ridx))
4314286441Srpaulo			tab |= IWM_RATE_MCS_CCK_MSK;
4315286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4316286441Srpaulo		    "station rate i=%d, rate=%d, hw=%x\n",
4317286441Srpaulo		    i, iwm_rates[ridx].rate, tab);
4318286441Srpaulo		lq->rs_table[i] = htole32(tab);
4319286441Srpaulo	}
4320286441Srpaulo	/* then fill the rest with the lowest possible rate */
4321286441Srpaulo	for (i = nrates; i < nitems(lq->rs_table); i++) {
4322286441Srpaulo		KASSERT(tab != 0, ("invalid tab"));
4323286441Srpaulo		lq->rs_table[i] = htole32(tab);
4324286441Srpaulo	}
4325286441Srpaulo}
4326286441Srpaulo
4327286441Srpaulostatic int
4328286441Srpauloiwm_media_change(struct ifnet *ifp)
4329286441Srpaulo{
4330287197Sglebius	struct ieee80211vap *vap = ifp->if_softc;
4331287197Sglebius	struct ieee80211com *ic = vap->iv_ic;
4332287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
4333286441Srpaulo	int error;
4334286441Srpaulo
4335286441Srpaulo	error = ieee80211_media_change(ifp);
4336286441Srpaulo	if (error != ENETRESET)
4337286441Srpaulo		return error;
4338286441Srpaulo
4339287197Sglebius	IWM_LOCK(sc);
4340287197Sglebius	if (ic->ic_nrunning > 0) {
4341287197Sglebius		iwm_stop(sc);
4342286441Srpaulo		iwm_init(sc);
4343286441Srpaulo	}
4344287197Sglebius	IWM_UNLOCK(sc);
4345286441Srpaulo	return error;
4346286441Srpaulo}
4347286441Srpaulo
4348286441Srpaulo
4349286441Srpaulostatic int
4350286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
4351286441Srpaulo{
4352286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
4353286441Srpaulo	struct ieee80211com *ic = vap->iv_ic;
4354286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
4355286441Srpaulo	struct iwm_node *in;
4356286441Srpaulo	int error;
4357286441Srpaulo
4358286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4359286441Srpaulo	    "switching state %s -> %s\n",
4360286441Srpaulo	    ieee80211_state_name[vap->iv_state],
4361286441Srpaulo	    ieee80211_state_name[nstate]);
4362286441Srpaulo	IEEE80211_UNLOCK(ic);
4363286441Srpaulo	IWM_LOCK(sc);
4364301187Sadrian
4365301187Sadrian	if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state)
4366301187Sadrian		iwm_led_blink_stop(sc);
4367301187Sadrian
4368286441Srpaulo	/* disable beacon filtering if we're hopping out of RUN */
4369286441Srpaulo	if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) {
4370286441Srpaulo		iwm_mvm_disable_beacon_filter(sc);
4371286441Srpaulo
4372293099Savos		if (((in = IWM_NODE(vap->iv_bss)) != NULL))
4373286441Srpaulo			in->in_assoc = 0;
4374286441Srpaulo
4375330154Seadler		if (nstate == IEEE80211_S_INIT) {
4376330154Seadler			IWM_UNLOCK(sc);
4377330154Seadler			IEEE80211_LOCK(ic);
4378330154Seadler			error = ivp->iv_newstate(vap, nstate, arg);
4379330154Seadler			IEEE80211_UNLOCK(ic);
4380330154Seadler			IWM_LOCK(sc);
4381330154Seadler			iwm_release(sc, NULL);
4382330154Seadler			IWM_UNLOCK(sc);
4383330154Seadler			IEEE80211_LOCK(ic);
4384330154Seadler			return error;
4385330154Seadler		}
4386286441Srpaulo
4387286441Srpaulo		/*
4388286441Srpaulo		 * It's impossible to directly go RUN->SCAN. If we iwm_release()
4389286441Srpaulo		 * above then the card will be completely reinitialized,
4390286441Srpaulo		 * so the driver must do everything necessary to bring the card
4391286441Srpaulo		 * from INIT to SCAN.
4392286441Srpaulo		 *
4393286441Srpaulo		 * Additionally, upon receiving deauth frame from AP,
4394286441Srpaulo		 * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH
4395286441Srpaulo		 * state. This will also fail with this driver, so bring the FSM
4396286441Srpaulo		 * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well.
4397286441Srpaulo		 *
4398286441Srpaulo		 * XXX TODO: fix this for FreeBSD!
4399286441Srpaulo		 */
4400286441Srpaulo		if (nstate == IEEE80211_S_SCAN ||
4401286441Srpaulo		    nstate == IEEE80211_S_AUTH ||
4402286441Srpaulo		    nstate == IEEE80211_S_ASSOC) {
4403286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4404286441Srpaulo			    "Force transition to INIT; MGT=%d\n", arg);
4405286441Srpaulo			IWM_UNLOCK(sc);
4406286441Srpaulo			IEEE80211_LOCK(ic);
4407303628Ssbruno			/* Always pass arg as -1 since we can't Tx right now. */
4408303628Ssbruno			/*
4409303628Ssbruno			 * XXX arg is just ignored anyway when transitioning
4410303628Ssbruno			 *     to IEEE80211_S_INIT.
4411303628Ssbruno			 */
4412303628Ssbruno			vap->iv_newstate(vap, IEEE80211_S_INIT, -1);
4413286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4414286441Srpaulo			    "Going INIT->SCAN\n");
4415286441Srpaulo			nstate = IEEE80211_S_SCAN;
4416286441Srpaulo			IEEE80211_UNLOCK(ic);
4417286441Srpaulo			IWM_LOCK(sc);
4418286441Srpaulo		}
4419286441Srpaulo	}
4420286441Srpaulo
4421286441Srpaulo	switch (nstate) {
4422286441Srpaulo	case IEEE80211_S_INIT:
4423330211Seadler	case IEEE80211_S_SCAN:
4424330211Seadler		if (vap->iv_state == IEEE80211_S_AUTH ||
4425330211Seadler		    vap->iv_state == IEEE80211_S_ASSOC) {
4426330211Seadler			int myerr;
4427330211Seadler			IWM_UNLOCK(sc);
4428330211Seadler			IEEE80211_LOCK(ic);
4429330211Seadler			myerr = ivp->iv_newstate(vap, nstate, arg);
4430330211Seadler			IEEE80211_UNLOCK(ic);
4431330211Seadler			IWM_LOCK(sc);
4432330213Seadler			error = iwm_mvm_rm_sta(sc, vap, FALSE);
4433330211Seadler                        if (error) {
4434330211Seadler                                device_printf(sc->sc_dev,
4435330211Seadler				    "%s: Failed to remove station: %d\n",
4436330211Seadler				    __func__, error);
4437330211Seadler			}
4438330211Seadler			error = iwm_mvm_mac_ctxt_changed(sc, vap);
4439330211Seadler                        if (error) {
4440330211Seadler                                device_printf(sc->sc_dev,
4441330211Seadler                                    "%s: Failed to change mac context: %d\n",
4442330211Seadler                                    __func__, error);
4443330211Seadler                        }
4444330211Seadler                        error = iwm_mvm_binding_remove_vif(sc, ivp);
4445330211Seadler                        if (error) {
4446330211Seadler                                device_printf(sc->sc_dev,
4447330211Seadler                                    "%s: Failed to remove channel ctx: %d\n",
4448330211Seadler                                    __func__, error);
4449330211Seadler                        }
4450330211Seadler			ivp->phy_ctxt = NULL;
4451330211Seadler			IWM_UNLOCK(sc);
4452330211Seadler			IEEE80211_LOCK(ic);
4453330211Seadler			return myerr;
4454330211Seadler		}
4455286441Srpaulo		break;
4456286441Srpaulo
4457286441Srpaulo	case IEEE80211_S_AUTH:
4458286441Srpaulo		if ((error = iwm_auth(vap, sc)) != 0) {
4459286441Srpaulo			device_printf(sc->sc_dev,
4460286441Srpaulo			    "%s: could not move to auth state: %d\n",
4461286441Srpaulo			    __func__, error);
4462286441Srpaulo			break;
4463286441Srpaulo		}
4464286441Srpaulo		break;
4465286441Srpaulo
4466286441Srpaulo	case IEEE80211_S_ASSOC:
4467286441Srpaulo		if ((error = iwm_assoc(vap, sc)) != 0) {
4468286441Srpaulo			device_printf(sc->sc_dev,
4469286441Srpaulo			    "%s: failed to associate: %d\n", __func__,
4470286441Srpaulo			    error);
4471286441Srpaulo			break;
4472286441Srpaulo		}
4473286441Srpaulo		break;
4474286441Srpaulo
4475286441Srpaulo	case IEEE80211_S_RUN:
4476286441Srpaulo		/* Update the association state, now we have it all */
4477286441Srpaulo		/* (eg associd comes in at this point */
4478286441Srpaulo		error = iwm_assoc(vap, sc);
4479286441Srpaulo		if (error != 0) {
4480286441Srpaulo			device_printf(sc->sc_dev,
4481286441Srpaulo			    "%s: failed to update association state: %d\n",
4482286441Srpaulo			    __func__,
4483286441Srpaulo			    error);
4484286441Srpaulo			break;
4485286441Srpaulo		}
4486286441Srpaulo
4487293099Savos		in = IWM_NODE(vap->iv_bss);
4488286441Srpaulo		iwm_mvm_enable_beacon_filter(sc, in);
4489330201Seadler		iwm_mvm_power_update_mac(sc);
4490330203Seadler		iwm_mvm_update_quotas(sc, ivp);
4491286441Srpaulo		iwm_setrates(sc, in);
4492286441Srpaulo
4493330225Seadler		if ((error = iwm_mvm_send_lq_cmd(sc, &in->in_lq, TRUE)) != 0) {
4494286441Srpaulo			device_printf(sc->sc_dev,
4495330225Seadler			    "%s: IWM_LQ_CMD failed: %d\n", __func__, error);
4496286441Srpaulo		}
4497286441Srpaulo
4498303628Ssbruno		iwm_mvm_led_enable(sc);
4499286441Srpaulo		break;
4500286441Srpaulo
4501286441Srpaulo	default:
4502286441Srpaulo		break;
4503286441Srpaulo	}
4504286441Srpaulo	IWM_UNLOCK(sc);
4505286441Srpaulo	IEEE80211_LOCK(ic);
4506286441Srpaulo
4507286441Srpaulo	return (ivp->iv_newstate(vap, nstate, arg));
4508286441Srpaulo}
4509286441Srpaulo
4510286441Srpaulovoid
4511286441Srpauloiwm_endscan_cb(void *arg, int pending)
4512286441Srpaulo{
4513286441Srpaulo	struct iwm_softc *sc = arg;
4514287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4515286441Srpaulo
4516286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE,
4517286441Srpaulo	    "%s: scan ended\n",
4518286441Srpaulo	    __func__);
4519286441Srpaulo
4520303628Ssbruno	ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
4521303628Ssbruno}
4522303628Ssbruno
4523303628Ssbruno/*
4524303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4525303628Ssbruno * in default configuration
4526303628Ssbruno */
4527303628Ssbrunostatic const uint32_t
4528303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4529303628Ssbruno	{
4530303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
4531303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
4532303628Ssbruno	},
4533303628Ssbruno	{
4534303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF),
4535303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF)
4536303628Ssbruno	},
4537303628Ssbruno	{
4538303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER_DEF),
4539303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER_DEF)
4540303628Ssbruno	},
4541303628Ssbruno	{
4542303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER_DEF),
4543303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER_DEF)
4544303628Ssbruno	},
4545303628Ssbruno	{
4546303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER_DEF),
4547303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF)
4548303628Ssbruno	},
4549303628Ssbruno};
4550303628Ssbruno
4551303628Ssbruno/*
4552303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4553303628Ssbruno * in single BSS MAC configuration.
4554303628Ssbruno */
4555303628Ssbrunostatic const uint32_t
4556303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4557303628Ssbruno	{
4558303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER),
4559303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER)
4560303628Ssbruno	},
4561303628Ssbruno	{
4562303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER),
4563303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER)
4564303628Ssbruno	},
4565303628Ssbruno	{
4566303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER),
4567303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER)
4568303628Ssbruno	},
4569303628Ssbruno	{
4570303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER),
4571303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER)
4572303628Ssbruno	},
4573303628Ssbruno	{
4574303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER),
4575303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER)
4576303628Ssbruno	},
4577303628Ssbruno};
4578303628Ssbruno
4579303628Ssbrunostatic void
4580303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd,
4581303628Ssbruno    struct ieee80211_node *ni)
4582303628Ssbruno{
4583303628Ssbruno	int i, j, watermark;
4584303628Ssbruno
4585303628Ssbruno	sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN);
4586303628Ssbruno
4587303628Ssbruno	/*
4588303628Ssbruno	 * If we are in association flow - check antenna configuration
4589303628Ssbruno	 * capabilities of the AP station, and choose the watermark accordingly.
4590303628Ssbruno	 */
4591303628Ssbruno	if (ni) {
4592303628Ssbruno		if (ni->ni_flags & IEEE80211_NODE_HT) {
4593303628Ssbruno#ifdef notyet
4594303628Ssbruno			if (ni->ni_rxmcs[2] != 0)
4595303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO3;
4596303628Ssbruno			else if (ni->ni_rxmcs[1] != 0)
4597303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO2;
4598303628Ssbruno			else
4599303628Ssbruno#endif
4600303628Ssbruno				watermark = IWM_SF_W_MARK_SISO;
4601303628Ssbruno		} else {
4602303628Ssbruno			watermark = IWM_SF_W_MARK_LEGACY;
4603286441Srpaulo		}
4604303628Ssbruno	/* default watermark value for unassociated mode. */
4605286441Srpaulo	} else {
4606303628Ssbruno		watermark = IWM_SF_W_MARK_MIMO2;
4607286441Srpaulo	}
4608303628Ssbruno	sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark);
4609286441Srpaulo
4610303628Ssbruno	for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) {
4611303628Ssbruno		for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) {
4612303628Ssbruno			sf_cmd->long_delay_timeouts[i][j] =
4613303628Ssbruno					htole32(IWM_SF_LONG_DELAY_AGING_TIMER);
4614303628Ssbruno		}
4615286441Srpaulo	}
4616303628Ssbruno
4617303628Ssbruno	if (ni) {
4618303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout,
4619303628Ssbruno		       sizeof(iwm_sf_full_timeout));
4620303628Ssbruno	} else {
4621303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def,
4622303628Ssbruno		       sizeof(iwm_sf_full_timeout_def));
4623303628Ssbruno	}
4624286441Srpaulo}
4625286441Srpaulo
4626286441Srpaulostatic int
4627303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state)
4628303628Ssbruno{
4629303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
4630303628Ssbruno	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4631303628Ssbruno	struct iwm_sf_cfg_cmd sf_cmd = {
4632303628Ssbruno		.state = htole32(IWM_SF_FULL_ON),
4633303628Ssbruno	};
4634303628Ssbruno	int ret = 0;
4635303628Ssbruno
4636330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
4637303628Ssbruno		sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF);
4638303628Ssbruno
4639303628Ssbruno	switch (new_state) {
4640303628Ssbruno	case IWM_SF_UNINIT:
4641303628Ssbruno	case IWM_SF_INIT_OFF:
4642303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL);
4643303628Ssbruno		break;
4644303628Ssbruno	case IWM_SF_FULL_ON:
4645303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss);
4646303628Ssbruno		break;
4647303628Ssbruno	default:
4648303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE,
4649303628Ssbruno		    "Invalid state: %d. not sending Smart Fifo cmd\n",
4650303628Ssbruno			  new_state);
4651303628Ssbruno		return EINVAL;
4652303628Ssbruno	}
4653303628Ssbruno
4654303628Ssbruno	ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC,
4655303628Ssbruno				   sizeof(sf_cmd), &sf_cmd);
4656303628Ssbruno	return ret;
4657303628Ssbruno}
4658303628Ssbruno
4659303628Ssbrunostatic int
4660303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc)
4661303628Ssbruno{
4662303628Ssbruno	struct iwm_bt_coex_cmd bt_cmd;
4663303628Ssbruno
4664303628Ssbruno	bt_cmd.mode = htole32(IWM_BT_COEX_WIFI);
4665303628Ssbruno	bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET);
4666303628Ssbruno
4667303628Ssbruno	return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd),
4668303628Ssbruno	    &bt_cmd);
4669303628Ssbruno}
4670303628Ssbruno
4671330217Seadlerstatic boolean_t
4672330217Seadleriwm_mvm_is_lar_supported(struct iwm_softc *sc)
4673330217Seadler{
4674330217Seadler	boolean_t nvm_lar = sc->nvm_data->lar_enabled;
4675330217Seadler	boolean_t tlv_lar = fw_has_capa(&sc->ucode_capa,
4676330217Seadler					IWM_UCODE_TLV_CAPA_LAR_SUPPORT);
4677330217Seadler
4678330217Seadler	if (iwm_lar_disable)
4679330217Seadler		return FALSE;
4680330217Seadler
4681330217Seadler	/*
4682330217Seadler	 * Enable LAR only if it is supported by the FW (TLV) &&
4683330217Seadler	 * enabled in the NVM
4684330217Seadler	 */
4685330217Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
4686330217Seadler		return nvm_lar && tlv_lar;
4687330217Seadler	else
4688330217Seadler		return tlv_lar;
4689330217Seadler}
4690330217Seadler
4691330217Seadlerstatic boolean_t
4692330217Seadleriwm_mvm_is_wifi_mcc_supported(struct iwm_softc *sc)
4693330217Seadler{
4694330217Seadler	return fw_has_api(&sc->ucode_capa,
4695330217Seadler			  IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
4696330217Seadler	       fw_has_capa(&sc->ucode_capa,
4697330217Seadler			   IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC);
4698330217Seadler}
4699330217Seadler
4700303628Ssbrunostatic int
4701303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2)
4702303628Ssbruno{
4703303628Ssbruno	struct iwm_mcc_update_cmd mcc_cmd;
4704303628Ssbruno	struct iwm_host_cmd hcmd = {
4705303628Ssbruno		.id = IWM_MCC_UPDATE_CMD,
4706303628Ssbruno		.flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB),
4707303628Ssbruno		.data = { &mcc_cmd },
4708303628Ssbruno	};
4709303628Ssbruno	int ret;
4710303628Ssbruno#ifdef IWM_DEBUG
4711303628Ssbruno	struct iwm_rx_packet *pkt;
4712303628Ssbruno	struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL;
4713303628Ssbruno	struct iwm_mcc_update_resp *mcc_resp;
4714303628Ssbruno	int n_channels;
4715303628Ssbruno	uint16_t mcc;
4716303628Ssbruno#endif
4717330196Seadler	int resp_v2 = fw_has_capa(&sc->ucode_capa,
4718303628Ssbruno	    IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
4719303628Ssbruno
4720330217Seadler	if (!iwm_mvm_is_lar_supported(sc)) {
4721330217Seadler		IWM_DPRINTF(sc, IWM_DEBUG_LAR, "%s: no LAR support\n",
4722330217Seadler		    __func__);
4723330217Seadler		return 0;
4724330217Seadler	}
4725330217Seadler
4726303628Ssbruno	memset(&mcc_cmd, 0, sizeof(mcc_cmd));
4727303628Ssbruno	mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]);
4728330217Seadler	if (iwm_mvm_is_wifi_mcc_supported(sc))
4729303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT;
4730303628Ssbruno	else
4731303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW;
4732303628Ssbruno
4733303628Ssbruno	if (resp_v2)
4734303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd);
4735303628Ssbruno	else
4736303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1);
4737303628Ssbruno
4738330217Seadler	IWM_DPRINTF(sc, IWM_DEBUG_LAR,
4739303628Ssbruno	    "send MCC update to FW with '%c%c' src = %d\n",
4740303628Ssbruno	    alpha2[0], alpha2[1], mcc_cmd.source_id);
4741303628Ssbruno
4742303628Ssbruno	ret = iwm_send_cmd(sc, &hcmd);
4743303628Ssbruno	if (ret)
4744303628Ssbruno		return ret;
4745303628Ssbruno
4746303628Ssbruno#ifdef IWM_DEBUG
4747303628Ssbruno	pkt = hcmd.resp_pkt;
4748303628Ssbruno
4749303628Ssbruno	/* Extract MCC response */
4750303628Ssbruno	if (resp_v2) {
4751303628Ssbruno		mcc_resp = (void *)pkt->data;
4752303628Ssbruno		mcc = mcc_resp->mcc;
4753303628Ssbruno		n_channels =  le32toh(mcc_resp->n_channels);
4754303628Ssbruno	} else {
4755303628Ssbruno		mcc_resp_v1 = (void *)pkt->data;
4756303628Ssbruno		mcc = mcc_resp_v1->mcc;
4757303628Ssbruno		n_channels =  le32toh(mcc_resp_v1->n_channels);
4758303628Ssbruno	}
4759303628Ssbruno
4760303628Ssbruno	/* W/A for a FW/NVM issue - returns 0x00 for the world domain */
4761303628Ssbruno	if (mcc == 0)
4762303628Ssbruno		mcc = 0x3030;  /* "00" - world */
4763303628Ssbruno
4764330217Seadler	IWM_DPRINTF(sc, IWM_DEBUG_LAR,
4765303628Ssbruno	    "regulatory domain '%c%c' (%d channels available)\n",
4766303628Ssbruno	    mcc >> 8, mcc & 0xff, n_channels);
4767303628Ssbruno#endif
4768303628Ssbruno	iwm_free_resp(sc, &hcmd);
4769303628Ssbruno
4770303628Ssbruno	return 0;
4771303628Ssbruno}
4772303628Ssbruno
4773303628Ssbrunostatic void
4774303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff)
4775303628Ssbruno{
4776303628Ssbruno	struct iwm_host_cmd cmd = {
4777303628Ssbruno		.id = IWM_REPLY_THERMAL_MNG_BACKOFF,
4778303628Ssbruno		.len = { sizeof(uint32_t), },
4779303628Ssbruno		.data = { &backoff, },
4780303628Ssbruno	};
4781303628Ssbruno
4782303628Ssbruno	if (iwm_send_cmd(sc, &cmd) != 0) {
4783303628Ssbruno		device_printf(sc->sc_dev,
4784303628Ssbruno		    "failed to change thermal tx backoff\n");
4785303628Ssbruno	}
4786303628Ssbruno}
4787303628Ssbruno
4788303628Ssbrunostatic int
4789286441Srpauloiwm_init_hw(struct iwm_softc *sc)
4790286441Srpaulo{
4791287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4792303628Ssbruno	int error, i, ac;
4793286441Srpaulo
4794303628Ssbruno	if ((error = iwm_start_hw(sc)) != 0) {
4795303628Ssbruno		printf("iwm_start_hw: failed %d\n", error);
4796286441Srpaulo		return error;
4797303628Ssbruno	}
4798286441Srpaulo
4799286441Srpaulo	if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) {
4800303628Ssbruno		printf("iwm_run_init_mvm_ucode: failed %d\n", error);
4801286441Srpaulo		return error;
4802286441Srpaulo	}
4803286441Srpaulo
4804286441Srpaulo	/*
4805286441Srpaulo	 * should stop and start HW since that INIT
4806286441Srpaulo	 * image just loaded
4807286441Srpaulo	 */
4808286441Srpaulo	iwm_stop_device(sc);
4809330201Seadler	sc->sc_ps_disabled = FALSE;
4810286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
4811286441Srpaulo		device_printf(sc->sc_dev, "could not initialize hardware\n");
4812286441Srpaulo		return error;
4813286441Srpaulo	}
4814286441Srpaulo
4815286441Srpaulo	/* omstart, this time with the regular firmware */
4816330168Seadler	error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_REGULAR);
4817286441Srpaulo	if (error) {
4818286441Srpaulo		device_printf(sc->sc_dev, "could not load firmware\n");
4819286441Srpaulo		goto error;
4820286441Srpaulo	}
4821286441Srpaulo
4822303628Ssbruno	if ((error = iwm_send_bt_init_conf(sc)) != 0) {
4823303628Ssbruno		device_printf(sc->sc_dev, "bt init conf failed\n");
4824286441Srpaulo		goto error;
4825303628Ssbruno	}
4826286441Srpaulo
4827330167Seadler	error = iwm_send_tx_ant_cfg(sc, iwm_mvm_get_valid_tx_ant(sc));
4828330167Seadler	if (error != 0) {
4829303628Ssbruno		device_printf(sc->sc_dev, "antenna config failed\n");
4830303628Ssbruno		goto error;
4831303628Ssbruno	}
4832303628Ssbruno
4833330163Seadler	/* Send phy db control command and then phy db calibration */
4834330163Seadler	if ((error = iwm_send_phy_db_data(sc->sc_phy_db)) != 0)
4835286441Srpaulo		goto error;
4836286441Srpaulo
4837303628Ssbruno	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) {
4838303628Ssbruno		device_printf(sc->sc_dev, "phy_cfg_cmd failed\n");
4839286441Srpaulo		goto error;
4840303628Ssbruno	}
4841286441Srpaulo
4842286441Srpaulo	/* Add auxiliary station for scanning */
4843303628Ssbruno	if ((error = iwm_mvm_add_aux_sta(sc)) != 0) {
4844303628Ssbruno		device_printf(sc->sc_dev, "add_aux_sta failed\n");
4845286441Srpaulo		goto error;
4846303628Ssbruno	}
4847286441Srpaulo
4848286441Srpaulo	for (i = 0; i < IWM_NUM_PHY_CTX; i++) {
4849286441Srpaulo		/*
4850286441Srpaulo		 * The channel used here isn't relevant as it's
4851286441Srpaulo		 * going to be overwritten in the other flows.
4852286441Srpaulo		 * For now use the first channel we have.
4853286441Srpaulo		 */
4854286441Srpaulo		if ((error = iwm_mvm_phy_ctxt_add(sc,
4855286441Srpaulo		    &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0)
4856286441Srpaulo			goto error;
4857286441Srpaulo	}
4858286441Srpaulo
4859303628Ssbruno	/* Initialize tx backoffs to the minimum. */
4860330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000)
4861303628Ssbruno		iwm_mvm_tt_tx_backoff(sc, 0);
4862303628Ssbruno
4863286441Srpaulo	error = iwm_mvm_power_update_device(sc);
4864286441Srpaulo	if (error)
4865286441Srpaulo		goto error;
4866286441Srpaulo
4867330217Seadler	if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0)
4868330217Seadler		goto error;
4869286441Srpaulo
4870330196Seadler	if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
4871303628Ssbruno		if ((error = iwm_mvm_config_umac_scan(sc)) != 0)
4872303628Ssbruno			goto error;
4873303628Ssbruno	}
4874303628Ssbruno
4875303628Ssbruno	/* Enable Tx queues. */
4876303628Ssbruno	for (ac = 0; ac < WME_NUM_AC; ac++) {
4877303628Ssbruno		error = iwm_enable_txq(sc, IWM_STATION_ID, ac,
4878303628Ssbruno		    iwm_mvm_ac_to_tx_fifo[ac]);
4879303628Ssbruno		if (error)
4880303628Ssbruno			goto error;
4881303628Ssbruno	}
4882303628Ssbruno
4883303628Ssbruno	if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) {
4884303628Ssbruno		device_printf(sc->sc_dev, "failed to disable beacon filter\n");
4885303628Ssbruno		goto error;
4886303628Ssbruno	}
4887303628Ssbruno
4888286441Srpaulo	return 0;
4889286441Srpaulo
4890286441Srpaulo error:
4891286441Srpaulo	iwm_stop_device(sc);
4892286441Srpaulo	return error;
4893286441Srpaulo}
4894286441Srpaulo
4895286441Srpaulo/* Allow multicast from our BSSID. */
4896286441Srpaulostatic int
4897286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc)
4898286441Srpaulo{
4899286441Srpaulo	struct ieee80211_node *ni = vap->iv_bss;
4900286441Srpaulo	struct iwm_mcast_filter_cmd *cmd;
4901286441Srpaulo	size_t size;
4902286441Srpaulo	int error;
4903286441Srpaulo
4904286441Srpaulo	size = roundup(sizeof(*cmd), 4);
4905286441Srpaulo	cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
4906286441Srpaulo	if (cmd == NULL)
4907286441Srpaulo		return ENOMEM;
4908286441Srpaulo	cmd->filter_own = 1;
4909286441Srpaulo	cmd->port_id = 0;
4910286441Srpaulo	cmd->count = 0;
4911286441Srpaulo	cmd->pass_all = 1;
4912286441Srpaulo	IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid);
4913286441Srpaulo
4914286441Srpaulo	error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD,
4915286441Srpaulo	    IWM_CMD_SYNC, size, cmd);
4916286441Srpaulo	free(cmd, M_DEVBUF);
4917286441Srpaulo
4918286441Srpaulo	return (error);
4919286441Srpaulo}
4920286441Srpaulo
4921303628Ssbruno/*
4922303628Ssbruno * ifnet interfaces
4923303628Ssbruno */
4924303628Ssbruno
4925286441Srpaulostatic void
4926287197Sglebiusiwm_init(struct iwm_softc *sc)
4927286441Srpaulo{
4928286441Srpaulo	int error;
4929286441Srpaulo
4930286441Srpaulo	if (sc->sc_flags & IWM_FLAG_HW_INITED) {
4931286441Srpaulo		return;
4932286441Srpaulo	}
4933286441Srpaulo	sc->sc_generation++;
4934286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_STOPPED;
4935286441Srpaulo
4936286441Srpaulo	if ((error = iwm_init_hw(sc)) != 0) {
4937303628Ssbruno		printf("iwm_init_hw failed %d\n", error);
4938287197Sglebius		iwm_stop(sc);
4939286441Srpaulo		return;
4940286441Srpaulo	}
4941286441Srpaulo
4942286441Srpaulo	/*
4943303628Ssbruno	 * Ok, firmware loaded and we are jogging
4944286441Srpaulo	 */
4945286441Srpaulo	sc->sc_flags |= IWM_FLAG_HW_INITED;
4946286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
4947286441Srpaulo}
4948286441Srpaulo
4949287197Sglebiusstatic int
4950287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m)
4951286441Srpaulo{
4952287197Sglebius	struct iwm_softc *sc;
4953287197Sglebius	int error;
4954286441Srpaulo
4955287197Sglebius	sc = ic->ic_softc;
4956287197Sglebius
4957286441Srpaulo	IWM_LOCK(sc);
4958287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
4959287197Sglebius		IWM_UNLOCK(sc);
4960287197Sglebius		return (ENXIO);
4961287197Sglebius	}
4962287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
4963287197Sglebius	if (error) {
4964287197Sglebius		IWM_UNLOCK(sc);
4965287197Sglebius		return (error);
4966287197Sglebius	}
4967287197Sglebius	iwm_start(sc);
4968286441Srpaulo	IWM_UNLOCK(sc);
4969287197Sglebius	return (0);
4970286441Srpaulo}
4971286441Srpaulo
4972287197Sglebius/*
4973287197Sglebius * Dequeue packets from sendq and call send.
4974287197Sglebius */
4975286441Srpaulostatic void
4976287197Sglebiusiwm_start(struct iwm_softc *sc)
4977286441Srpaulo{
4978286441Srpaulo	struct ieee80211_node *ni;
4979286441Srpaulo	struct mbuf *m;
4980286441Srpaulo	int ac = 0;
4981286441Srpaulo
4982286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__);
4983287197Sglebius	while (sc->qfullmsk == 0 &&
4984287197Sglebius		(m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
4985286441Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4986286441Srpaulo		if (iwm_tx(sc, m, ni, ac) != 0) {
4987287197Sglebius			if_inc_counter(ni->ni_vap->iv_ifp,
4988287197Sglebius			    IFCOUNTER_OERRORS, 1);
4989286441Srpaulo			ieee80211_free_node(ni);
4990286441Srpaulo			continue;
4991286441Srpaulo		}
4992287197Sglebius		sc->sc_tx_timer = 15;
4993286441Srpaulo	}
4994286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__);
4995286441Srpaulo}
4996286441Srpaulo
4997286441Srpaulostatic void
4998287197Sglebiusiwm_stop(struct iwm_softc *sc)
4999286441Srpaulo{
5000286441Srpaulo
5001286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_HW_INITED;
5002286441Srpaulo	sc->sc_flags |= IWM_FLAG_STOPPED;
5003286441Srpaulo	sc->sc_generation++;
5004301187Sadrian	iwm_led_blink_stop(sc);
5005286441Srpaulo	sc->sc_tx_timer = 0;
5006286441Srpaulo	iwm_stop_device(sc);
5007330174Seadler	sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING;
5008286441Srpaulo}
5009286441Srpaulo
5010286441Srpaulostatic void
5011286441Srpauloiwm_watchdog(void *arg)
5012286441Srpaulo{
5013286441Srpaulo	struct iwm_softc *sc = arg;
5014300242Savos	struct ieee80211com *ic = &sc->sc_ic;
5015286441Srpaulo
5016286441Srpaulo	if (sc->sc_tx_timer > 0) {
5017286441Srpaulo		if (--sc->sc_tx_timer == 0) {
5018286441Srpaulo			device_printf(sc->sc_dev, "device timeout\n");
5019286441Srpaulo#ifdef IWM_DEBUG
5020286441Srpaulo			iwm_nic_error(sc);
5021286441Srpaulo#endif
5022300242Savos			ieee80211_restart_all(ic);
5023303628Ssbruno			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
5024286441Srpaulo			return;
5025286441Srpaulo		}
5026286441Srpaulo	}
5027286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
5028286441Srpaulo}
5029286441Srpaulo
5030287197Sglebiusstatic void
5031287197Sglebiusiwm_parent(struct ieee80211com *ic)
5032286441Srpaulo{
5033287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
5034287197Sglebius	int startall = 0;
5035286441Srpaulo
5036287197Sglebius	IWM_LOCK(sc);
5037287197Sglebius	if (ic->ic_nrunning > 0) {
5038287197Sglebius		if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) {
5039287197Sglebius			iwm_init(sc);
5040287197Sglebius			startall = 1;
5041286441Srpaulo		}
5042287197Sglebius	} else if (sc->sc_flags & IWM_FLAG_HW_INITED)
5043287197Sglebius		iwm_stop(sc);
5044287197Sglebius	IWM_UNLOCK(sc);
5045287197Sglebius	if (startall)
5046287197Sglebius		ieee80211_start_all(ic);
5047286441Srpaulo}
5048286441Srpaulo
5049286441Srpaulo/*
5050286441Srpaulo * The interrupt side of things
5051286441Srpaulo */
5052286441Srpaulo
5053286441Srpaulo/*
5054286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c
5055286441Srpaulo */
5056286441Srpaulo
5057286441Srpaulo/*
5058286441Srpaulo * Note: This structure is read from the device with IO accesses,
5059286441Srpaulo * and the reading already does the endian conversion. As it is
5060286441Srpaulo * read with uint32_t-sized accesses, any members with a different size
5061286441Srpaulo * need to be ordered correctly though!
5062286441Srpaulo */
5063286441Srpaulostruct iwm_error_event_table {
5064286441Srpaulo	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
5065286441Srpaulo	uint32_t error_id;		/* type of error */
5066303628Ssbruno	uint32_t trm_hw_status0;	/* TRM HW status */
5067303628Ssbruno	uint32_t trm_hw_status1;	/* TRM HW status */
5068286441Srpaulo	uint32_t blink2;		/* branch link */
5069286441Srpaulo	uint32_t ilink1;		/* interrupt link */
5070286441Srpaulo	uint32_t ilink2;		/* interrupt link */
5071286441Srpaulo	uint32_t data1;		/* error-specific data */
5072286441Srpaulo	uint32_t data2;		/* error-specific data */
5073286441Srpaulo	uint32_t data3;		/* error-specific data */
5074286441Srpaulo	uint32_t bcon_time;		/* beacon timer */
5075286441Srpaulo	uint32_t tsf_low;		/* network timestamp function timer */
5076286441Srpaulo	uint32_t tsf_hi;		/* network timestamp function timer */
5077286441Srpaulo	uint32_t gp1;		/* GP1 timer register */
5078286441Srpaulo	uint32_t gp2;		/* GP2 timer register */
5079303628Ssbruno	uint32_t fw_rev_type;	/* firmware revision type */
5080303628Ssbruno	uint32_t major;		/* uCode version major */
5081303628Ssbruno	uint32_t minor;		/* uCode version minor */
5082286441Srpaulo	uint32_t hw_ver;		/* HW Silicon version */
5083286441Srpaulo	uint32_t brd_ver;		/* HW board version */
5084286441Srpaulo	uint32_t log_pc;		/* log program counter */
5085286441Srpaulo	uint32_t frame_ptr;		/* frame pointer */
5086286441Srpaulo	uint32_t stack_ptr;		/* stack pointer */
5087286441Srpaulo	uint32_t hcmd;		/* last host command header */
5088286441Srpaulo	uint32_t isr0;		/* isr status register LMPM_NIC_ISR0:
5089286441Srpaulo				 * rxtx_flag */
5090286441Srpaulo	uint32_t isr1;		/* isr status register LMPM_NIC_ISR1:
5091286441Srpaulo				 * host_flag */
5092286441Srpaulo	uint32_t isr2;		/* isr status register LMPM_NIC_ISR2:
5093286441Srpaulo				 * enc_flag */
5094286441Srpaulo	uint32_t isr3;		/* isr status register LMPM_NIC_ISR3:
5095286441Srpaulo				 * time_flag */
5096286441Srpaulo	uint32_t isr4;		/* isr status register LMPM_NIC_ISR4:
5097286441Srpaulo				 * wico interrupt */
5098303628Ssbruno	uint32_t last_cmd_id;	/* last HCMD id handled by the firmware */
5099286441Srpaulo	uint32_t wait_event;		/* wait event() caller address */
5100286441Srpaulo	uint32_t l2p_control;	/* L2pControlField */
5101286441Srpaulo	uint32_t l2p_duration;	/* L2pDurationField */
5102286441Srpaulo	uint32_t l2p_mhvalid;	/* L2pMhValidBits */
5103286441Srpaulo	uint32_t l2p_addr_match;	/* L2pAddrMatchStat */
5104286441Srpaulo	uint32_t lmpm_pmg_sel;	/* indicate which clocks are turned on
5105286441Srpaulo				 * (LMPM_PMG_SEL) */
5106286441Srpaulo	uint32_t u_timestamp;	/* indicate when the date and time of the
5107286441Srpaulo				 * compilation */
5108286441Srpaulo	uint32_t flow_handler;	/* FH read/write pointers, RX credit */
5109303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */;
5110303628Ssbruno
5111303628Ssbruno/*
5112303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip.
5113303628Ssbruno * Note: This structure is read from the device with IO accesses,
5114303628Ssbruno * and the reading already does the endian conversion. As it is
5115303628Ssbruno * read with u32-sized accesses, any members with a different size
5116303628Ssbruno * need to be ordered correctly though!
5117303628Ssbruno */
5118303628Ssbrunostruct iwm_umac_error_event_table {
5119303628Ssbruno	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
5120303628Ssbruno	uint32_t error_id;	/* type of error */
5121303628Ssbruno	uint32_t blink1;	/* branch link */
5122303628Ssbruno	uint32_t blink2;	/* branch link */
5123303628Ssbruno	uint32_t ilink1;	/* interrupt link */
5124303628Ssbruno	uint32_t ilink2;	/* interrupt link */
5125303628Ssbruno	uint32_t data1;		/* error-specific data */
5126303628Ssbruno	uint32_t data2;		/* error-specific data */
5127303628Ssbruno	uint32_t data3;		/* error-specific data */
5128303628Ssbruno	uint32_t umac_major;
5129303628Ssbruno	uint32_t umac_minor;
5130303628Ssbruno	uint32_t frame_pointer;	/* core register 27*/
5131303628Ssbruno	uint32_t stack_pointer;	/* core register 28 */
5132303628Ssbruno	uint32_t cmd_header;	/* latest host cmd sent to UMAC */
5133303628Ssbruno	uint32_t nic_isr_pref;	/* ISR status register */
5134286441Srpaulo} __packed;
5135286441Srpaulo
5136286441Srpaulo#define ERROR_START_OFFSET  (1 * sizeof(uint32_t))
5137286441Srpaulo#define ERROR_ELEM_SIZE     (7 * sizeof(uint32_t))
5138286441Srpaulo
5139286441Srpaulo#ifdef IWM_DEBUG
5140286441Srpaulostruct {
5141286441Srpaulo	const char *name;
5142286441Srpaulo	uint8_t num;
5143286441Srpaulo} advanced_lookup[] = {
5144286441Srpaulo	{ "NMI_INTERRUPT_WDG", 0x34 },
5145286441Srpaulo	{ "SYSASSERT", 0x35 },
5146286441Srpaulo	{ "UCODE_VERSION_MISMATCH", 0x37 },
5147286441Srpaulo	{ "BAD_COMMAND", 0x38 },
5148286441Srpaulo	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
5149286441Srpaulo	{ "FATAL_ERROR", 0x3D },
5150286441Srpaulo	{ "NMI_TRM_HW_ERR", 0x46 },
5151286441Srpaulo	{ "NMI_INTERRUPT_TRM", 0x4C },
5152286441Srpaulo	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
5153286441Srpaulo	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
5154286441Srpaulo	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
5155286441Srpaulo	{ "NMI_INTERRUPT_HOST", 0x66 },
5156286441Srpaulo	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
5157286441Srpaulo	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
5158286441Srpaulo	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
5159286441Srpaulo	{ "ADVANCED_SYSASSERT", 0 },
5160286441Srpaulo};
5161286441Srpaulo
5162286441Srpaulostatic const char *
5163286441Srpauloiwm_desc_lookup(uint32_t num)
5164286441Srpaulo{
5165286441Srpaulo	int i;
5166286441Srpaulo
5167286441Srpaulo	for (i = 0; i < nitems(advanced_lookup) - 1; i++)
5168286441Srpaulo		if (advanced_lookup[i].num == num)
5169286441Srpaulo			return advanced_lookup[i].name;
5170286441Srpaulo
5171286441Srpaulo	/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
5172286441Srpaulo	return advanced_lookup[i].name;
5173286441Srpaulo}
5174286441Srpaulo
5175303628Ssbrunostatic void
5176303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc)
5177303628Ssbruno{
5178303628Ssbruno	struct iwm_umac_error_event_table table;
5179303628Ssbruno	uint32_t base;
5180303628Ssbruno
5181330183Seadler	base = sc->umac_error_event_table;
5182303628Ssbruno
5183303628Ssbruno	if (base < 0x800000) {
5184303628Ssbruno		device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n",
5185303628Ssbruno		    base);
5186303628Ssbruno		return;
5187303628Ssbruno	}
5188303628Ssbruno
5189303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
5190303628Ssbruno		device_printf(sc->sc_dev, "reading errlog failed\n");
5191303628Ssbruno		return;
5192303628Ssbruno	}
5193303628Ssbruno
5194303628Ssbruno	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
5195303628Ssbruno		device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n");
5196303628Ssbruno		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
5197303628Ssbruno		    sc->sc_flags, table.valid);
5198303628Ssbruno	}
5199303628Ssbruno
5200303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id,
5201303628Ssbruno		iwm_desc_lookup(table.error_id));
5202303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1);
5203303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2);
5204303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n",
5205303628Ssbruno	    table.ilink1);
5206303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n",
5207303628Ssbruno	    table.ilink2);
5208303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1);
5209303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2);
5210303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3);
5211303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major);
5212303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor);
5213303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | frame pointer\n",
5214303628Ssbruno	    table.frame_pointer);
5215303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | stack pointer\n",
5216303628Ssbruno	    table.stack_pointer);
5217303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header);
5218303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | isr status reg\n",
5219303628Ssbruno	    table.nic_isr_pref);
5220303628Ssbruno}
5221303628Ssbruno
5222286441Srpaulo/*
5223286441Srpaulo * Support for dumping the error log seemed like a good idea ...
5224286441Srpaulo * but it's mostly hex junk and the only sensible thing is the
5225286441Srpaulo * hw/ucode revision (which we know anyway).  Since it's here,
5226286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to
5227286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later.
5228286441Srpaulo */
5229286441Srpaulostatic void
5230286441Srpauloiwm_nic_error(struct iwm_softc *sc)
5231286441Srpaulo{
5232286441Srpaulo	struct iwm_error_event_table table;
5233286441Srpaulo	uint32_t base;
5234286441Srpaulo
5235286441Srpaulo	device_printf(sc->sc_dev, "dumping device error log\n");
5236330186Seadler	base = sc->error_event_table;
5237303628Ssbruno	if (base < 0x800000) {
5238286441Srpaulo		device_printf(sc->sc_dev,
5239303628Ssbruno		    "Invalid error log pointer 0x%08x\n", base);
5240286441Srpaulo		return;
5241286441Srpaulo	}
5242286441Srpaulo
5243303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
5244286441Srpaulo		device_printf(sc->sc_dev, "reading errlog failed\n");
5245286441Srpaulo		return;
5246286441Srpaulo	}
5247286441Srpaulo
5248286441Srpaulo	if (!table.valid) {
5249286441Srpaulo		device_printf(sc->sc_dev, "errlog not found, skipping\n");
5250286441Srpaulo		return;
5251286441Srpaulo	}
5252286441Srpaulo
5253286441Srpaulo	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
5254303628Ssbruno		device_printf(sc->sc_dev, "Start Error Log Dump:\n");
5255286441Srpaulo		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
5256286441Srpaulo		    sc->sc_flags, table.valid);
5257286441Srpaulo	}
5258286441Srpaulo
5259286441Srpaulo	device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id,
5260303628Ssbruno	    iwm_desc_lookup(table.error_id));
5261303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status0\n",
5262303628Ssbruno	    table.trm_hw_status0);
5263303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status1\n",
5264303628Ssbruno	    table.trm_hw_status1);
5265286441Srpaulo	device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2);
5266286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1);
5267286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2);
5268286441Srpaulo	device_printf(sc->sc_dev, "%08X | data1\n", table.data1);
5269286441Srpaulo	device_printf(sc->sc_dev, "%08X | data2\n", table.data2);
5270286441Srpaulo	device_printf(sc->sc_dev, "%08X | data3\n", table.data3);
5271286441Srpaulo	device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time);
5272286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low);
5273286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi);
5274286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1);
5275286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2);
5276303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode revision type\n",
5277303628Ssbruno	    table.fw_rev_type);
5278303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major);
5279303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor);
5280286441Srpaulo	device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver);
5281286441Srpaulo	device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver);
5282286441Srpaulo	device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd);
5283286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0);
5284286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1);
5285286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2);
5286286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3);
5287286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4);
5288303628Ssbruno	device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id);
5289286441Srpaulo	device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event);
5290286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control);
5291286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration);
5292286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid);
5293286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match);
5294286441Srpaulo	device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
5295286441Srpaulo	device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp);
5296286441Srpaulo	device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler);
5297303628Ssbruno
5298330183Seadler	if (sc->umac_error_event_table)
5299303628Ssbruno		iwm_nic_umac_error(sc);
5300286441Srpaulo}
5301286441Srpaulo#endif
5302286441Srpaulo
5303286441Srpaulostatic void
5304330208Seadleriwm_handle_rxb(struct iwm_softc *sc, struct mbuf *m)
5305286441Srpaulo{
5306303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
5307330208Seadler	struct iwm_cmd_response *cresp;
5308330208Seadler	struct mbuf *m1;
5309330208Seadler	uint32_t offset = 0;
5310330208Seadler	uint32_t maxoff = IWM_RBUF_SIZE;
5311330208Seadler	uint32_t nextoff;
5312330208Seadler	boolean_t stolen = FALSE;
5313286441Srpaulo
5314330208Seadler#define HAVEROOM(a)	\
5315330208Seadler    ((a) + sizeof(uint32_t) + sizeof(struct iwm_cmd_header) < maxoff)
5316286441Srpaulo
5317330208Seadler	while (HAVEROOM(offset)) {
5318330208Seadler		struct iwm_rx_packet *pkt = mtodoff(m, struct iwm_rx_packet *,
5319330208Seadler		    offset);
5320330208Seadler		int qid, idx, code, len;
5321303628Ssbruno
5322330208Seadler		qid = pkt->hdr.qid;
5323286441Srpaulo		idx = pkt->hdr.idx;
5324286441Srpaulo
5325303628Ssbruno		code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
5326286441Srpaulo
5327286441Srpaulo		/*
5328286441Srpaulo		 * randomly get these from the firmware, no idea why.
5329286441Srpaulo		 * they at least seem harmless, so just ignore them for now
5330286441Srpaulo		 */
5331330208Seadler		if ((pkt->hdr.code == 0 && (qid & ~0x80) == 0 && idx == 0) ||
5332330208Seadler		    pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID)) {
5333330208Seadler			break;
5334286441Srpaulo		}
5335286441Srpaulo
5336330208Seadler		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5337330208Seadler		    "rx packet qid=%d idx=%d type=%x\n",
5338330208Seadler		    qid & ~0x80, pkt->hdr.idx, code);
5339330208Seadler
5340330220Seadler		len = iwm_rx_packet_len(pkt);
5341330208Seadler		len += sizeof(uint32_t); /* account for status word */
5342330208Seadler		nextoff = offset + roundup2(len, IWM_FH_RSCSR_FRAME_ALIGN);
5343330208Seadler
5344330170Seadler		iwm_notification_wait_notify(sc->sc_notif_wait, code, pkt);
5345330170Seadler
5346303628Ssbruno		switch (code) {
5347286441Srpaulo		case IWM_REPLY_RX_PHY_CMD:
5348330208Seadler			iwm_mvm_rx_rx_phy_cmd(sc, pkt);
5349286441Srpaulo			break;
5350286441Srpaulo
5351330208Seadler		case IWM_REPLY_RX_MPDU_CMD: {
5352330208Seadler			/*
5353330208Seadler			 * If this is the last frame in the RX buffer, we
5354330208Seadler			 * can directly feed the mbuf to the sharks here.
5355330208Seadler			 */
5356330208Seadler			struct iwm_rx_packet *nextpkt = mtodoff(m,
5357330208Seadler			    struct iwm_rx_packet *, nextoff);
5358330208Seadler			if (!HAVEROOM(nextoff) ||
5359330208Seadler			    (nextpkt->hdr.code == 0 &&
5360330208Seadler			     (nextpkt->hdr.qid & ~0x80) == 0 &&
5361330208Seadler			     nextpkt->hdr.idx == 0) ||
5362330208Seadler			    (nextpkt->len_n_flags ==
5363330208Seadler			     htole32(IWM_FH_RSCSR_FRAME_INVALID))) {
5364330208Seadler				if (iwm_mvm_rx_rx_mpdu(sc, m, offset, stolen)) {
5365330208Seadler					stolen = FALSE;
5366330208Seadler					/* Make sure we abort the loop */
5367330208Seadler					nextoff = maxoff;
5368330208Seadler				}
5369330208Seadler				break;
5370330208Seadler			}
5371330208Seadler
5372330208Seadler			/*
5373330208Seadler			 * Use m_copym instead of m_split, because that
5374330208Seadler			 * makes it easier to keep a valid rx buffer in
5375330208Seadler			 * the ring, when iwm_mvm_rx_rx_mpdu() fails.
5376330208Seadler			 *
5377330208Seadler			 * We need to start m_copym() at offset 0, to get the
5378330208Seadler			 * M_PKTHDR flag preserved.
5379330208Seadler			 */
5380330208Seadler			m1 = m_copym(m, 0, M_COPYALL, M_NOWAIT);
5381330208Seadler			if (m1) {
5382330208Seadler				if (iwm_mvm_rx_rx_mpdu(sc, m1, offset, stolen))
5383330208Seadler					stolen = TRUE;
5384330208Seadler				else
5385330208Seadler					m_freem(m1);
5386330208Seadler			}
5387286441Srpaulo			break;
5388330208Seadler		}
5389286441Srpaulo
5390286441Srpaulo		case IWM_TX_CMD:
5391330208Seadler			iwm_mvm_rx_tx_cmd(sc, pkt);
5392286441Srpaulo			break;
5393286441Srpaulo
5394286441Srpaulo		case IWM_MISSED_BEACONS_NOTIFICATION: {
5395286441Srpaulo			struct iwm_missed_beacons_notif *resp;
5396286441Srpaulo			int missed;
5397286441Srpaulo
5398286441Srpaulo			/* XXX look at mac_id to determine interface ID */
5399286441Srpaulo			struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5400286441Srpaulo
5401330158Seadler			resp = (void *)pkt->data;
5402286441Srpaulo			missed = le32toh(resp->consec_missed_beacons);
5403286441Srpaulo
5404286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE,
5405286441Srpaulo			    "%s: MISSED_BEACON: mac_id=%d, "
5406286441Srpaulo			    "consec_since_last_rx=%d, consec=%d, num_expect=%d "
5407286441Srpaulo			    "num_rx=%d\n",
5408286441Srpaulo			    __func__,
5409286441Srpaulo			    le32toh(resp->mac_id),
5410286441Srpaulo			    le32toh(resp->consec_missed_beacons_since_last_rx),
5411286441Srpaulo			    le32toh(resp->consec_missed_beacons),
5412286441Srpaulo			    le32toh(resp->num_expected_beacons),
5413286441Srpaulo			    le32toh(resp->num_recvd_beacons));
5414286441Srpaulo
5415286441Srpaulo			/* Be paranoid */
5416286441Srpaulo			if (vap == NULL)
5417286441Srpaulo				break;
5418286441Srpaulo
5419286441Srpaulo			/* XXX no net80211 locking? */
5420286441Srpaulo			if (vap->iv_state == IEEE80211_S_RUN &&
5421286441Srpaulo			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
5422286441Srpaulo				if (missed > vap->iv_bmissthreshold) {
5423286441Srpaulo					/* XXX bad locking; turn into task */
5424286441Srpaulo					IWM_UNLOCK(sc);
5425286441Srpaulo					ieee80211_beacon_miss(ic);
5426286441Srpaulo					IWM_LOCK(sc);
5427286441Srpaulo				}
5428286441Srpaulo			}
5429286441Srpaulo
5430330193Seadler			break;
5431330193Seadler		}
5432286441Srpaulo
5433303628Ssbruno		case IWM_MFUART_LOAD_NOTIFICATION:
5434303628Ssbruno			break;
5435303628Ssbruno
5436330183Seadler		case IWM_MVM_ALIVE:
5437330183Seadler			break;
5438286441Srpaulo
5439330169Seadler		case IWM_CALIB_RES_NOTIF_PHY_DB:
5440330169Seadler			break;
5441286441Srpaulo
5442330224Seadler		case IWM_STATISTICS_NOTIFICATION:
5443330224Seadler			iwm_mvm_handle_rx_statistics(sc, pkt);
5444330193Seadler			break;
5445286441Srpaulo
5446286441Srpaulo		case IWM_NVM_ACCESS_CMD:
5447303628Ssbruno		case IWM_MCC_UPDATE_CMD:
5448330208Seadler			if (sc->sc_wantresp == (((qid & ~0x80) << 16) | idx)) {
5449286441Srpaulo				memcpy(sc->sc_cmd_resp,
5450286441Srpaulo				    pkt, sizeof(sc->sc_cmd_resp));
5451286441Srpaulo			}
5452286441Srpaulo			break;
5453286441Srpaulo
5454303628Ssbruno		case IWM_MCC_CHUB_UPDATE_CMD: {
5455303628Ssbruno			struct iwm_mcc_chub_notif *notif;
5456330158Seadler			notif = (void *)pkt->data;
5457303628Ssbruno
5458303628Ssbruno			sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8;
5459303628Ssbruno			sc->sc_fw_mcc[1] = notif->mcc & 0xff;
5460303628Ssbruno			sc->sc_fw_mcc[2] = '\0';
5461330217Seadler			IWM_DPRINTF(sc, IWM_DEBUG_LAR,
5462303628Ssbruno			    "fw source %d sent CC '%s'\n",
5463303628Ssbruno			    notif->source_id, sc->sc_fw_mcc);
5464330193Seadler			break;
5465330193Seadler		}
5466303628Ssbruno
5467330178Seadler		case IWM_DTS_MEASUREMENT_NOTIFICATION:
5468330178Seadler		case IWM_WIDE_ID(IWM_PHY_OPS_GROUP,
5469330178Seadler				 IWM_DTS_MEASUREMENT_NOTIF_WIDE): {
5470330177Seadler			struct iwm_dts_measurement_notif_v1 *notif;
5471330177Seadler
5472330177Seadler			if (iwm_rx_packet_payload_len(pkt) < sizeof(*notif)) {
5473330177Seadler				device_printf(sc->sc_dev,
5474330177Seadler				    "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
5475330177Seadler				break;
5476330177Seadler			}
5477330177Seadler			notif = (void *)pkt->data;
5478330177Seadler			IWM_DPRINTF(sc, IWM_DEBUG_TEMP,
5479330177Seadler			    "IWM_DTS_MEASUREMENT_NOTIFICATION - %d\n",
5480330177Seadler			    notif->temp);
5481303628Ssbruno			break;
5482330177Seadler		}
5483303628Ssbruno
5484286441Srpaulo		case IWM_PHY_CONFIGURATION_CMD:
5485286441Srpaulo		case IWM_TX_ANT_CONFIGURATION_CMD:
5486286441Srpaulo		case IWM_ADD_STA:
5487286441Srpaulo		case IWM_MAC_CONTEXT_CMD:
5488286441Srpaulo		case IWM_REPLY_SF_CFG_CMD:
5489286441Srpaulo		case IWM_POWER_TABLE_CMD:
5490286441Srpaulo		case IWM_PHY_CONTEXT_CMD:
5491286441Srpaulo		case IWM_BINDING_CONTEXT_CMD:
5492286441Srpaulo		case IWM_TIME_EVENT_CMD:
5493303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD):
5494303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC):
5495330214Seadler		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC):
5496303628Ssbruno		case IWM_SCAN_OFFLOAD_REQUEST_CMD:
5497330173Seadler		case IWM_SCAN_OFFLOAD_ABORT_CMD:
5498286441Srpaulo		case IWM_REPLY_BEACON_FILTERING_CMD:
5499286441Srpaulo		case IWM_MAC_PM_POWER_TABLE:
5500286441Srpaulo		case IWM_TIME_QUOTA_CMD:
5501286441Srpaulo		case IWM_REMOVE_STA:
5502286441Srpaulo		case IWM_TXPATH_FLUSH:
5503286441Srpaulo		case IWM_LQ_CMD:
5504330216Seadler		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP,
5505330216Seadler				 IWM_FW_PAGING_BLOCK_CMD):
5506303628Ssbruno		case IWM_BT_CONFIG:
5507303628Ssbruno		case IWM_REPLY_THERMAL_MNG_BACKOFF:
5508330158Seadler			cresp = (void *)pkt->data;
5509330208Seadler			if (sc->sc_wantresp == (((qid & ~0x80) << 16) | idx)) {
5510286441Srpaulo				memcpy(sc->sc_cmd_resp,
5511286441Srpaulo				    pkt, sizeof(*pkt)+sizeof(*cresp));
5512286441Srpaulo			}
5513286441Srpaulo			break;
5514286441Srpaulo
5515286441Srpaulo		/* ignore */
5516330219Seadler		case IWM_PHY_DB_CMD:
5517286441Srpaulo			break;
5518286441Srpaulo
5519286441Srpaulo		case IWM_INIT_COMPLETE_NOTIF:
5520286441Srpaulo			break;
5521286441Srpaulo
5522303628Ssbruno		case IWM_SCAN_OFFLOAD_COMPLETE: {
5523303628Ssbruno			struct iwm_periodic_scan_complete *notif;
5524330158Seadler			notif = (void *)pkt->data;
5525330174Seadler			if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) {
5526330174Seadler				sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING;
5527330174Seadler				ieee80211_runtask(ic, &sc->sc_es_task);
5528330174Seadler			}
5529303628Ssbruno			break;
5530303628Ssbruno		}
5531303628Ssbruno
5532303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE: {
5533303628Ssbruno			struct iwm_lmac_scan_complete_notif *notif;
5534330158Seadler			notif = (void *)pkt->data;
5535303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5536303628Ssbruno 			break;
5537303628Ssbruno		}
5538303628Ssbruno
5539303628Ssbruno		case IWM_SCAN_COMPLETE_UMAC: {
5540303628Ssbruno			struct iwm_umac_scan_complete *notif;
5541330158Seadler			notif = (void *)pkt->data;
5542303628Ssbruno
5543303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
5544303628Ssbruno			    "UMAC scan complete, status=0x%x\n",
5545303628Ssbruno			    notif->status);
5546330174Seadler			if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) {
5547330174Seadler				sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING;
5548330174Seadler				ieee80211_runtask(ic, &sc->sc_es_task);
5549330174Seadler			}
5550303628Ssbruno			break;
5551303628Ssbruno		}
5552286441Srpaulo
5553303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE_UMAC: {
5554303628Ssbruno			struct iwm_umac_scan_iter_complete_notif *notif;
5555330158Seadler			notif = (void *)pkt->data;
5556303628Ssbruno
5557303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration "
5558303628Ssbruno			    "complete, status=0x%x, %d channels scanned\n",
5559303628Ssbruno			    notif->status, notif->scanned_channels);
5560303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5561303628Ssbruno			break;
5562303628Ssbruno		}
5563303628Ssbruno
5564286441Srpaulo		case IWM_REPLY_ERROR: {
5565286441Srpaulo			struct iwm_error_resp *resp;
5566330158Seadler			resp = (void *)pkt->data;
5567286441Srpaulo
5568286441Srpaulo			device_printf(sc->sc_dev,
5569286441Srpaulo			    "firmware error 0x%x, cmd 0x%x\n",
5570286441Srpaulo			    le32toh(resp->error_type),
5571286441Srpaulo			    resp->cmd_id);
5572303628Ssbruno			break;
5573303628Ssbruno		}
5574286441Srpaulo
5575286441Srpaulo		case IWM_TIME_EVENT_NOTIFICATION: {
5576286441Srpaulo			struct iwm_time_event_notif *notif;
5577330158Seadler			notif = (void *)pkt->data;
5578286441Srpaulo
5579286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5580300833Sadrian			    "TE notif status = 0x%x action = 0x%x\n",
5581303628Ssbruno			    notif->status, notif->action);
5582303628Ssbruno			break;
5583303628Ssbruno		}
5584286441Srpaulo
5585330220Seadler		/*
5586330220Seadler		 * Firmware versions 21 and 22 generate some DEBUG_LOG_MSG
5587330220Seadler		 * messages. Just ignore them for now.
5588330220Seadler		 */
5589330220Seadler		case IWM_DEBUG_LOG_MSG:
5590330220Seadler			break;
5591330220Seadler
5592286441Srpaulo		case IWM_MCAST_FILTER_CMD:
5593286441Srpaulo			break;
5594286441Srpaulo
5595303628Ssbruno		case IWM_SCD_QUEUE_CFG: {
5596303628Ssbruno			struct iwm_scd_txq_cfg_rsp *rsp;
5597330158Seadler			rsp = (void *)pkt->data;
5598303628Ssbruno
5599303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_CMD,
5600303628Ssbruno			    "queue cfg token=0x%x sta_id=%d "
5601303628Ssbruno			    "tid=%d scd_queue=%d\n",
5602303628Ssbruno			    rsp->token, rsp->sta_id, rsp->tid,
5603303628Ssbruno			    rsp->scd_queue);
5604303628Ssbruno			break;
5605303628Ssbruno		}
5606303628Ssbruno
5607286441Srpaulo		default:
5608286441Srpaulo			device_printf(sc->sc_dev,
5609286441Srpaulo			    "frame %d/%d %x UNHANDLED (this should "
5610330208Seadler			    "not happen)\n", qid & ~0x80, idx,
5611286441Srpaulo			    pkt->len_n_flags);
5612286441Srpaulo			break;
5613286441Srpaulo		}
5614286441Srpaulo
5615286441Srpaulo		/*
5616286441Srpaulo		 * Why test bit 0x80?  The Linux driver:
5617286441Srpaulo		 *
5618286441Srpaulo		 * There is one exception:  uCode sets bit 15 when it
5619286441Srpaulo		 * originates the response/notification, i.e. when the
5620286441Srpaulo		 * response/notification is not a direct response to a
5621286441Srpaulo		 * command sent by the driver.  For example, uCode issues
5622286441Srpaulo		 * IWM_REPLY_RX when it sends a received frame to the driver;
5623286441Srpaulo		 * it is not a direct response to any driver command.
5624286441Srpaulo		 *
5625286441Srpaulo		 * Ok, so since when is 7 == 15?  Well, the Linux driver
5626286441Srpaulo		 * uses a slightly different format for pkt->hdr, and "qid"
5627286441Srpaulo		 * is actually the upper byte of a two-byte field.
5628286441Srpaulo		 */
5629330208Seadler		if (!(qid & (1 << 7)))
5630286441Srpaulo			iwm_cmd_done(sc, pkt);
5631286441Srpaulo
5632330208Seadler		offset = nextoff;
5633286441Srpaulo	}
5634330208Seadler	if (stolen)
5635330208Seadler		m_freem(m);
5636330208Seadler#undef HAVEROOM
5637330208Seadler}
5638286441Srpaulo
5639330208Seadler/*
5640330208Seadler * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt.
5641330208Seadler * Basic structure from if_iwn
5642330208Seadler */
5643330208Seadlerstatic void
5644330208Seadleriwm_notif_intr(struct iwm_softc *sc)
5645330208Seadler{
5646330208Seadler	uint16_t hw;
5647330208Seadler
5648330208Seadler	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
5649330208Seadler	    BUS_DMASYNC_POSTREAD);
5650330208Seadler
5651330208Seadler	hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
5652330208Seadler
5653286441Srpaulo	/*
5654330208Seadler	 * Process responses
5655330208Seadler	 */
5656330208Seadler	while (sc->rxq.cur != hw) {
5657330208Seadler		struct iwm_rx_ring *ring = &sc->rxq;
5658330208Seadler		struct iwm_rx_data *data = &ring->data[ring->cur];
5659330208Seadler
5660330208Seadler		bus_dmamap_sync(ring->data_dmat, data->map,
5661330208Seadler		    BUS_DMASYNC_POSTREAD);
5662330208Seadler
5663330208Seadler		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5664330208Seadler		    "%s: hw = %d cur = %d\n", __func__, hw, ring->cur);
5665330208Seadler		iwm_handle_rxb(sc, data->m);
5666330208Seadler
5667330208Seadler		ring->cur = (ring->cur + 1) % IWM_RX_RING_COUNT;
5668330208Seadler	}
5669330208Seadler
5670330208Seadler	/*
5671330208Seadler	 * Tell the firmware that it can reuse the ring entries that
5672330208Seadler	 * we have just processed.
5673286441Srpaulo	 * Seems like the hardware gets upset unless we align
5674286441Srpaulo	 * the write by 8??
5675286441Srpaulo	 */
5676286441Srpaulo	hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1;
5677330208Seadler	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, rounddown2(hw, 8));
5678286441Srpaulo}
5679286441Srpaulo
5680286441Srpaulostatic void
5681286441Srpauloiwm_intr(void *arg)
5682286441Srpaulo{
5683286441Srpaulo	struct iwm_softc *sc = arg;
5684286441Srpaulo	int handled = 0;
5685286441Srpaulo	int r1, r2, rv = 0;
5686286441Srpaulo	int isperiodic = 0;
5687286441Srpaulo
5688286441Srpaulo	IWM_LOCK(sc);
5689286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
5690286441Srpaulo
5691286441Srpaulo	if (sc->sc_flags & IWM_FLAG_USE_ICT) {
5692286441Srpaulo		uint32_t *ict = sc->ict_dma.vaddr;
5693286441Srpaulo		int tmp;
5694286441Srpaulo
5695286441Srpaulo		tmp = htole32(ict[sc->ict_cur]);
5696286441Srpaulo		if (!tmp)
5697286441Srpaulo			goto out_ena;
5698286441Srpaulo
5699286441Srpaulo		/*
5700286441Srpaulo		 * ok, there was something.  keep plowing until we have all.
5701286441Srpaulo		 */
5702286441Srpaulo		r1 = r2 = 0;
5703286441Srpaulo		while (tmp) {
5704286441Srpaulo			r1 |= tmp;
5705286441Srpaulo			ict[sc->ict_cur] = 0;
5706286441Srpaulo			sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT;
5707286441Srpaulo			tmp = htole32(ict[sc->ict_cur]);
5708286441Srpaulo		}
5709286441Srpaulo
5710286441Srpaulo		/* this is where the fun begins.  don't ask */
5711286441Srpaulo		if (r1 == 0xffffffff)
5712286441Srpaulo			r1 = 0;
5713286441Srpaulo
5714286441Srpaulo		/* i am not expected to understand this */
5715286441Srpaulo		if (r1 & 0xc0000)
5716286441Srpaulo			r1 |= 0x8000;
5717286441Srpaulo		r1 = (0xff & r1) | ((0xff00 & r1) << 16);
5718286441Srpaulo	} else {
5719286441Srpaulo		r1 = IWM_READ(sc, IWM_CSR_INT);
5720286441Srpaulo		/* "hardware gone" (where, fishing?) */
5721286441Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
5722286441Srpaulo			goto out;
5723286441Srpaulo		r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS);
5724286441Srpaulo	}
5725286441Srpaulo	if (r1 == 0 && r2 == 0) {
5726286441Srpaulo		goto out_ena;
5727286441Srpaulo	}
5728286441Srpaulo
5729286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask);
5730286441Srpaulo
5731330183Seadler	/* Safely ignore these bits for debug checks below */
5732330183Seadler	r1 &= ~(IWM_CSR_INT_BIT_ALIVE | IWM_CSR_INT_BIT_SCD);
5733286441Srpaulo
5734286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_SW_ERR) {
5735286441Srpaulo		int i;
5736287197Sglebius		struct ieee80211com *ic = &sc->sc_ic;
5737286441Srpaulo		struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5738286441Srpaulo
5739298793Sdchagin#ifdef IWM_DEBUG
5740286441Srpaulo		iwm_nic_error(sc);
5741298793Sdchagin#endif
5742286441Srpaulo		/* Dump driver status (TX and RX rings) while we're here. */
5743286441Srpaulo		device_printf(sc->sc_dev, "driver status:\n");
5744286441Srpaulo		for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) {
5745286441Srpaulo			struct iwm_tx_ring *ring = &sc->txq[i];
5746286441Srpaulo			device_printf(sc->sc_dev,
5747286441Srpaulo			    "  tx ring %2d: qid=%-2d cur=%-3d "
5748286441Srpaulo			    "queued=%-3d\n",
5749286441Srpaulo			    i, ring->qid, ring->cur, ring->queued);
5750286441Srpaulo		}
5751286441Srpaulo		device_printf(sc->sc_dev,
5752286441Srpaulo		    "  rx ring: cur=%d\n", sc->rxq.cur);
5753286441Srpaulo		device_printf(sc->sc_dev,
5754298659Scem		    "  802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state);
5755286441Srpaulo
5756298594Sadrian		/* Don't stop the device; just do a VAP restart */
5757298594Sadrian		IWM_UNLOCK(sc);
5758286441Srpaulo
5759298594Sadrian		if (vap == NULL) {
5760298594Sadrian			printf("%s: null vap\n", __func__);
5761298594Sadrian			return;
5762298594Sadrian		}
5763298594Sadrian
5764298594Sadrian		device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; "
5765298594Sadrian		    "restarting\n", __func__, vap->iv_state);
5766298594Sadrian
5767298594Sadrian		ieee80211_restart_all(ic);
5768298594Sadrian		return;
5769286441Srpaulo	}
5770286441Srpaulo
5771286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_HW_ERR) {
5772286441Srpaulo		handled |= IWM_CSR_INT_BIT_HW_ERR;
5773286441Srpaulo		device_printf(sc->sc_dev, "hardware error, stopping device\n");
5774287197Sglebius		iwm_stop(sc);
5775286441Srpaulo		rv = 1;
5776286441Srpaulo		goto out;
5777286441Srpaulo	}
5778286441Srpaulo
5779286441Srpaulo	/* firmware chunk loaded */
5780286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_FH_TX) {
5781286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK);
5782286441Srpaulo		handled |= IWM_CSR_INT_BIT_FH_TX;
5783286441Srpaulo		sc->sc_fw_chunk_done = 1;
5784286441Srpaulo		wakeup(&sc->sc_fw);
5785286441Srpaulo	}
5786286441Srpaulo
5787286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RF_KILL) {
5788286441Srpaulo		handled |= IWM_CSR_INT_BIT_RF_KILL;
5789287197Sglebius		if (iwm_check_rfkill(sc)) {
5790286441Srpaulo			device_printf(sc->sc_dev,
5791286441Srpaulo			    "%s: rfkill switch, disabling interface\n",
5792286441Srpaulo			    __func__);
5793287197Sglebius			iwm_stop(sc);
5794286441Srpaulo		}
5795286441Srpaulo	}
5796286441Srpaulo
5797286441Srpaulo	/*
5798286441Srpaulo	 * The Linux driver uses periodic interrupts to avoid races.
5799286441Srpaulo	 * We cargo-cult like it's going out of fashion.
5800286441Srpaulo	 */
5801286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
5802286441Srpaulo		handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
5803286441Srpaulo		IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
5804286441Srpaulo		if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0)
5805286441Srpaulo			IWM_WRITE_1(sc,
5806286441Srpaulo			    IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
5807286441Srpaulo		isperiodic = 1;
5808286441Srpaulo	}
5809286441Srpaulo
5810286441Srpaulo	if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) {
5811286441Srpaulo		handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
5812286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
5813286441Srpaulo
5814286441Srpaulo		iwm_notif_intr(sc);
5815286441Srpaulo
5816286441Srpaulo		/* enable periodic interrupt, see above */
5817286441Srpaulo		if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic)
5818286441Srpaulo			IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG,
5819286441Srpaulo			    IWM_CSR_INT_PERIODIC_ENA);
5820286441Srpaulo	}
5821286441Srpaulo
5822286441Srpaulo	if (__predict_false(r1 & ~handled))
5823286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5824286441Srpaulo		    "%s: unhandled interrupts: %x\n", __func__, r1);
5825286441Srpaulo	rv = 1;
5826286441Srpaulo
5827286441Srpaulo out_ena:
5828286441Srpaulo	iwm_restore_interrupts(sc);
5829286441Srpaulo out:
5830286441Srpaulo	IWM_UNLOCK(sc);
5831286441Srpaulo	return;
5832286441Srpaulo}
5833286441Srpaulo
5834286441Srpaulo/*
5835286441Srpaulo * Autoconf glue-sniffing
5836286441Srpaulo */
5837286441Srpaulo#define	PCI_VENDOR_INTEL		0x8086
5838286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_1	0x08b3
5839286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_2	0x08b4
5840303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_1	0x3165
5841303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_2	0x3166
5842286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_1	0x08b1
5843286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_2	0x08b2
5844286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_1	0x095a
5845286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_2	0x095b
5846303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_1	0x24f3
5847303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_2	0x24f4
5848330227Seadler#define	PCI_PRODUCT_INTEL_WL_8265_1	0x24fd
5849286441Srpaulo
5850286441Srpaulostatic const struct iwm_devices {
5851330187Seadler	uint16_t		device;
5852330187Seadler	const struct iwm_cfg	*cfg;
5853286441Srpaulo} iwm_devices[] = {
5854330188Seadler	{ PCI_PRODUCT_INTEL_WL_3160_1, &iwm3160_cfg },
5855330188Seadler	{ PCI_PRODUCT_INTEL_WL_3160_2, &iwm3160_cfg },
5856330188Seadler	{ PCI_PRODUCT_INTEL_WL_3165_1, &iwm3165_cfg },
5857330188Seadler	{ PCI_PRODUCT_INTEL_WL_3165_2, &iwm3165_cfg },
5858330188Seadler	{ PCI_PRODUCT_INTEL_WL_7260_1, &iwm7260_cfg },
5859330188Seadler	{ PCI_PRODUCT_INTEL_WL_7260_2, &iwm7260_cfg },
5860330188Seadler	{ PCI_PRODUCT_INTEL_WL_7265_1, &iwm7265_cfg },
5861330188Seadler	{ PCI_PRODUCT_INTEL_WL_7265_2, &iwm7265_cfg },
5862330188Seadler	{ PCI_PRODUCT_INTEL_WL_8260_1, &iwm8260_cfg },
5863330188Seadler	{ PCI_PRODUCT_INTEL_WL_8260_2, &iwm8260_cfg },
5864330227Seadler	{ PCI_PRODUCT_INTEL_WL_8265_1, &iwm8265_cfg },
5865286441Srpaulo};
5866286441Srpaulo
5867286441Srpaulostatic int
5868286441Srpauloiwm_probe(device_t dev)
5869286441Srpaulo{
5870286441Srpaulo	int i;
5871286441Srpaulo
5872303628Ssbruno	for (i = 0; i < nitems(iwm_devices); i++) {
5873286441Srpaulo		if (pci_get_vendor(dev) == PCI_VENDOR_INTEL &&
5874286441Srpaulo		    pci_get_device(dev) == iwm_devices[i].device) {
5875330188Seadler			device_set_desc(dev, iwm_devices[i].cfg->name);
5876286441Srpaulo			return (BUS_PROBE_DEFAULT);
5877286441Srpaulo		}
5878303628Ssbruno	}
5879286441Srpaulo
5880286441Srpaulo	return (ENXIO);
5881286441Srpaulo}
5882286441Srpaulo
5883286441Srpaulostatic int
5884286441Srpauloiwm_dev_check(device_t dev)
5885286441Srpaulo{
5886286441Srpaulo	struct iwm_softc *sc;
5887330187Seadler	uint16_t devid;
5888330187Seadler	int i;
5889286441Srpaulo
5890286441Srpaulo	sc = device_get_softc(dev);
5891286441Srpaulo
5892330187Seadler	devid = pci_get_device(dev);
5893330187Seadler	for (i = 0; i < nitems(iwm_devices); i++) {
5894330187Seadler		if (iwm_devices[i].device == devid) {
5895330187Seadler			sc->cfg = iwm_devices[i].cfg;
5896330187Seadler			return (0);
5897330187Seadler		}
5898286441Srpaulo	}
5899330187Seadler	device_printf(dev, "unknown adapter type\n");
5900330187Seadler	return ENXIO;
5901286441Srpaulo}
5902286441Srpaulo
5903330140Seadler/* PCI registers */
5904330140Seadler#define PCI_CFG_RETRY_TIMEOUT	0x041
5905330140Seadler
5906286441Srpaulostatic int
5907286441Srpauloiwm_pci_attach(device_t dev)
5908286441Srpaulo{
5909286441Srpaulo	struct iwm_softc *sc;
5910286441Srpaulo	int count, error, rid;
5911286441Srpaulo	uint16_t reg;
5912286441Srpaulo
5913286441Srpaulo	sc = device_get_softc(dev);
5914286441Srpaulo
5915330140Seadler	/* We disable the RETRY_TIMEOUT register (0x41) to keep
5916330140Seadler	 * PCI Tx retries from interfering with C3 CPU state */
5917330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
5918286441Srpaulo
5919286441Srpaulo	/* Enable bus-mastering and hardware bug workaround. */
5920286441Srpaulo	pci_enable_busmaster(dev);
5921286441Srpaulo	reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg));
5922286441Srpaulo	/* if !MSI */
5923286441Srpaulo	if (reg & PCIM_STATUS_INTxSTATE) {
5924286441Srpaulo		reg &= ~PCIM_STATUS_INTxSTATE;
5925286441Srpaulo	}
5926286441Srpaulo	pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg));
5927286441Srpaulo
5928286441Srpaulo	rid = PCIR_BAR(0);
5929286441Srpaulo	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
5930286441Srpaulo	    RF_ACTIVE);
5931286441Srpaulo	if (sc->sc_mem == NULL) {
5932286441Srpaulo		device_printf(sc->sc_dev, "can't map mem space\n");
5933286441Srpaulo		return (ENXIO);
5934286441Srpaulo	}
5935286441Srpaulo	sc->sc_st = rman_get_bustag(sc->sc_mem);
5936286441Srpaulo	sc->sc_sh = rman_get_bushandle(sc->sc_mem);
5937286441Srpaulo
5938286441Srpaulo	/* Install interrupt handler. */
5939286441Srpaulo	count = 1;
5940286441Srpaulo	rid = 0;
5941286441Srpaulo	if (pci_alloc_msi(dev, &count) == 0)
5942286441Srpaulo		rid = 1;
5943286441Srpaulo	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
5944286441Srpaulo	    (rid != 0 ? 0 : RF_SHAREABLE));
5945286441Srpaulo	if (sc->sc_irq == NULL) {
5946286441Srpaulo		device_printf(dev, "can't map interrupt\n");
5947286441Srpaulo			return (ENXIO);
5948286441Srpaulo	}
5949286441Srpaulo	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
5950286441Srpaulo	    NULL, iwm_intr, sc, &sc->sc_ih);
5951286441Srpaulo	if (sc->sc_ih == NULL) {
5952286441Srpaulo		device_printf(dev, "can't establish interrupt");
5953286441Srpaulo			return (ENXIO);
5954286441Srpaulo	}
5955286441Srpaulo	sc->sc_dmat = bus_get_dma_tag(sc->sc_dev);
5956286441Srpaulo
5957286441Srpaulo	return (0);
5958286441Srpaulo}
5959286441Srpaulo
5960286441Srpaulostatic void
5961286441Srpauloiwm_pci_detach(device_t dev)
5962286441Srpaulo{
5963286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
5964286441Srpaulo
5965286441Srpaulo	if (sc->sc_irq != NULL) {
5966286441Srpaulo		bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
5967286441Srpaulo		bus_release_resource(dev, SYS_RES_IRQ,
5968286441Srpaulo		    rman_get_rid(sc->sc_irq), sc->sc_irq);
5969286441Srpaulo		pci_release_msi(dev);
5970286441Srpaulo        }
5971286441Srpaulo	if (sc->sc_mem != NULL)
5972286441Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY,
5973286441Srpaulo		    rman_get_rid(sc->sc_mem), sc->sc_mem);
5974286441Srpaulo}
5975286441Srpaulo
5976286441Srpaulo
5977286441Srpaulo
5978286441Srpaulostatic int
5979286441Srpauloiwm_attach(device_t dev)
5980286441Srpaulo{
5981287197Sglebius	struct iwm_softc *sc = device_get_softc(dev);
5982287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
5983286441Srpaulo	int error;
5984286441Srpaulo	int txq_i, i;
5985286441Srpaulo
5986286441Srpaulo	sc->sc_dev = dev;
5987330164Seadler	sc->sc_attached = 1;
5988293099Savos	IWM_LOCK_INIT(sc);
5989287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
5990286441Srpaulo	callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0);
5991301187Sadrian	callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0);
5992286441Srpaulo	TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc);
5993286441Srpaulo
5994330170Seadler	sc->sc_notif_wait = iwm_notification_wait_init(sc);
5995330170Seadler	if (sc->sc_notif_wait == NULL) {
5996330170Seadler		device_printf(dev, "failed to init notification wait struct\n");
5997330170Seadler		goto fail;
5998330170Seadler	}
5999330170Seadler
6000330163Seadler	/* Init phy db */
6001330163Seadler	sc->sc_phy_db = iwm_phy_db_init(sc);
6002330163Seadler	if (!sc->sc_phy_db) {
6003330163Seadler		device_printf(dev, "Cannot init phy_db\n");
6004330163Seadler		goto fail;
6005330163Seadler	}
6006330163Seadler
6007286441Srpaulo	/* PCI attach */
6008286441Srpaulo	error = iwm_pci_attach(dev);
6009286441Srpaulo	if (error != 0)
6010286441Srpaulo		goto fail;
6011286441Srpaulo
6012286441Srpaulo	sc->sc_wantresp = -1;
6013286441Srpaulo
6014286441Srpaulo	/* Check device type */
6015286441Srpaulo	error = iwm_dev_check(dev);
6016286441Srpaulo	if (error != 0)
6017286441Srpaulo		goto fail;
6018286441Srpaulo
6019330166Seadler	sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV);
6020286441Srpaulo	/*
6021303628Ssbruno	 * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
6022303628Ssbruno	 * changed, and now the revision step also includes bit 0-1 (no more
6023303628Ssbruno	 * "dash" value). To keep hw_rev backwards compatible - we'll store it
6024303628Ssbruno	 * in the old format.
6025303628Ssbruno	 */
6026330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000)
6027303628Ssbruno		sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) |
6028303628Ssbruno				(IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2);
6029303628Ssbruno
6030286441Srpaulo	if (iwm_prepare_card_hw(sc) != 0) {
6031286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
6032286441Srpaulo		goto fail;
6033286441Srpaulo	}
6034286441Srpaulo
6035330166Seadler	if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) {
6036303628Ssbruno		int ret;
6037303628Ssbruno		uint32_t hw_step;
6038303628Ssbruno
6039303628Ssbruno		/*
6040303628Ssbruno		 * In order to recognize C step the driver should read the
6041303628Ssbruno		 * chip version id located at the AUX bus MISC address.
6042303628Ssbruno		 */
6043303628Ssbruno		IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
6044303628Ssbruno			    IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
6045303628Ssbruno		DELAY(2);
6046303628Ssbruno
6047303628Ssbruno		ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL,
6048303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
6049303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
6050303628Ssbruno				   25000);
6051330149Seadler		if (!ret) {
6052303628Ssbruno			device_printf(sc->sc_dev,
6053303628Ssbruno			    "Failed to wake up the nic\n");
6054303628Ssbruno			goto fail;
6055303628Ssbruno		}
6056303628Ssbruno
6057303628Ssbruno		if (iwm_nic_lock(sc)) {
6058303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG);
6059303628Ssbruno			hw_step |= IWM_ENABLE_WFPM;
6060303628Ssbruno			iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step);
6061303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG);
6062303628Ssbruno			hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF;
6063303628Ssbruno			if (hw_step == 0x3)
6064303628Ssbruno				sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) |
6065303628Ssbruno						(IWM_SILICON_C_STEP << 2);
6066303628Ssbruno			iwm_nic_unlock(sc);
6067303628Ssbruno		} else {
6068303628Ssbruno			device_printf(sc->sc_dev, "Failed to lock the nic\n");
6069303628Ssbruno			goto fail;
6070303628Ssbruno		}
6071303628Ssbruno	}
6072303628Ssbruno
6073330166Seadler	/* special-case 7265D, it has the same PCI IDs. */
6074330166Seadler	if (sc->cfg == &iwm7265_cfg &&
6075330166Seadler	    (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == IWM_CSR_HW_REV_TYPE_7265D) {
6076330166Seadler		sc->cfg = &iwm7265d_cfg;
6077330166Seadler	}
6078330166Seadler
6079286441Srpaulo	/* Allocate DMA memory for firmware transfers. */
6080286441Srpaulo	if ((error = iwm_alloc_fwmem(sc)) != 0) {
6081286441Srpaulo		device_printf(dev, "could not allocate memory for firmware\n");
6082286441Srpaulo		goto fail;
6083286441Srpaulo	}
6084286441Srpaulo
6085286441Srpaulo	/* Allocate "Keep Warm" page. */
6086286441Srpaulo	if ((error = iwm_alloc_kw(sc)) != 0) {
6087286441Srpaulo		device_printf(dev, "could not allocate keep warm page\n");
6088286441Srpaulo		goto fail;
6089286441Srpaulo	}
6090286441Srpaulo
6091286441Srpaulo	/* We use ICT interrupts */
6092286441Srpaulo	if ((error = iwm_alloc_ict(sc)) != 0) {
6093286441Srpaulo		device_printf(dev, "could not allocate ICT table\n");
6094286441Srpaulo		goto fail;
6095286441Srpaulo	}
6096286441Srpaulo
6097286441Srpaulo	/* Allocate TX scheduler "rings". */
6098286441Srpaulo	if ((error = iwm_alloc_sched(sc)) != 0) {
6099286441Srpaulo		device_printf(dev, "could not allocate TX scheduler rings\n");
6100286441Srpaulo		goto fail;
6101286441Srpaulo	}
6102286441Srpaulo
6103286441Srpaulo	/* Allocate TX rings */
6104286441Srpaulo	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) {
6105286441Srpaulo		if ((error = iwm_alloc_tx_ring(sc,
6106286441Srpaulo		    &sc->txq[txq_i], txq_i)) != 0) {
6107286441Srpaulo			device_printf(dev,
6108286441Srpaulo			    "could not allocate TX ring %d\n",
6109286441Srpaulo			    txq_i);
6110286441Srpaulo			goto fail;
6111286441Srpaulo		}
6112286441Srpaulo	}
6113286441Srpaulo
6114286441Srpaulo	/* Allocate RX ring. */
6115286441Srpaulo	if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) {
6116286441Srpaulo		device_printf(dev, "could not allocate RX ring\n");
6117286441Srpaulo		goto fail;
6118286441Srpaulo	}
6119286441Srpaulo
6120286441Srpaulo	/* Clear pending interrupts. */
6121286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff);
6122286441Srpaulo
6123286441Srpaulo	ic->ic_softc = sc;
6124286441Srpaulo	ic->ic_name = device_get_nameunit(sc->sc_dev);
6125286441Srpaulo	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
6126286441Srpaulo	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
6127286441Srpaulo
6128286441Srpaulo	/* Set device capabilities. */
6129286441Srpaulo	ic->ic_caps =
6130286441Srpaulo	    IEEE80211_C_STA |
6131286441Srpaulo	    IEEE80211_C_WPA |		/* WPA/RSN */
6132286441Srpaulo	    IEEE80211_C_WME |
6133330201Seadler	    IEEE80211_C_PMGT |
6134286441Srpaulo	    IEEE80211_C_SHSLOT |	/* short slot time supported */
6135286441Srpaulo	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
6136286441Srpaulo//	    IEEE80211_C_BGSCAN		/* capable of bg scanning */
6137286441Srpaulo	    ;
6138330453Seadler	/* Advertise full-offload scanning */
6139330453Seadler	ic->ic_flags_ext = IEEE80211_FEXT_SCAN_OFFLOAD;
6140286441Srpaulo	for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
6141286441Srpaulo		sc->sc_phyctxt[i].id = i;
6142286441Srpaulo		sc->sc_phyctxt[i].color = 0;
6143286441Srpaulo		sc->sc_phyctxt[i].ref = 0;
6144286441Srpaulo		sc->sc_phyctxt[i].channel = NULL;
6145286441Srpaulo	}
6146286441Srpaulo
6147330144Seadler	/* Default noise floor */
6148330144Seadler	sc->sc_noise = -96;
6149330144Seadler
6150286441Srpaulo	/* Max RSSI */
6151286441Srpaulo	sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM;
6152330144Seadler
6153286441Srpaulo	sc->sc_preinit_hook.ich_func = iwm_preinit;
6154286441Srpaulo	sc->sc_preinit_hook.ich_arg = sc;
6155286441Srpaulo	if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
6156286441Srpaulo		device_printf(dev, "config_intrhook_establish failed\n");
6157286441Srpaulo		goto fail;
6158286441Srpaulo	}
6159286441Srpaulo
6160286441Srpaulo#ifdef IWM_DEBUG
6161286441Srpaulo	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
6162286441Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug",
6163286441Srpaulo	    CTLFLAG_RW, &sc->sc_debug, 0, "control debugging");
6164286441Srpaulo#endif
6165286441Srpaulo
6166286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6167286441Srpaulo	    "<-%s\n", __func__);
6168286441Srpaulo
6169286441Srpaulo	return 0;
6170286441Srpaulo
6171286441Srpaulo	/* Free allocated memory if something failed during attachment. */
6172286441Srpaulofail:
6173286441Srpaulo	iwm_detach_local(sc, 0);
6174286441Srpaulo
6175286441Srpaulo	return ENXIO;
6176286441Srpaulo}
6177286441Srpaulo
6178286441Srpaulostatic int
6179303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr)
6180303628Ssbruno{
6181303628Ssbruno	char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
6182303628Ssbruno
6183303628Ssbruno	if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr))
6184303628Ssbruno		return (FALSE);
6185303628Ssbruno
6186303628Ssbruno	return (TRUE);
6187303628Ssbruno}
6188303628Ssbruno
6189303628Ssbrunostatic int
6190330209Seadleriwm_wme_update(struct ieee80211com *ic)
6191286441Srpaulo{
6192330209Seadler#define IWM_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
6193286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
6194330209Seadler	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6195330209Seadler	struct iwm_vap *ivp = IWM_VAP(vap);
6196330209Seadler	struct iwm_node *in;
6197330209Seadler	struct wmeParams tmp[WME_NUM_AC];
6198330209Seadler	int aci, error;
6199286441Srpaulo
6200330209Seadler	if (vap == NULL)
6201330209Seadler		return (0);
6202330209Seadler
6203330209Seadler	IEEE80211_LOCK(ic);
6204330209Seadler	for (aci = 0; aci < WME_NUM_AC; aci++)
6205330209Seadler		tmp[aci] = ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
6206330209Seadler	IEEE80211_UNLOCK(ic);
6207330209Seadler
6208330209Seadler	IWM_LOCK(sc);
6209330209Seadler	for (aci = 0; aci < WME_NUM_AC; aci++) {
6210330209Seadler		const struct wmeParams *ac = &tmp[aci];
6211330209Seadler		ivp->queue_params[aci].aifsn = ac->wmep_aifsn;
6212330209Seadler		ivp->queue_params[aci].cw_min = IWM_EXP2(ac->wmep_logcwmin);
6213330209Seadler		ivp->queue_params[aci].cw_max = IWM_EXP2(ac->wmep_logcwmax);
6214330209Seadler		ivp->queue_params[aci].edca_txop =
6215330209Seadler		    IEEE80211_TXOP_TO_US(ac->wmep_txopLimit);
6216330209Seadler	}
6217330209Seadler	ivp->have_wme = TRUE;
6218330209Seadler	if (ivp->is_uploaded && vap->iv_bss != NULL) {
6219330209Seadler		in = IWM_NODE(vap->iv_bss);
6220330209Seadler		if (in->in_assoc) {
6221330209Seadler			if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
6222330209Seadler				device_printf(sc->sc_dev,
6223330209Seadler				    "%s: failed to update MAC\n", __func__);
6224330209Seadler			}
6225330209Seadler		}
6226330209Seadler	}
6227330209Seadler	IWM_UNLOCK(sc);
6228330209Seadler
6229286441Srpaulo	return (0);
6230330209Seadler#undef IWM_EXP2
6231286441Srpaulo}
6232286441Srpaulo
6233286441Srpaulostatic void
6234286441Srpauloiwm_preinit(void *arg)
6235286441Srpaulo{
6236286441Srpaulo	struct iwm_softc *sc = arg;
6237286441Srpaulo	device_t dev = sc->sc_dev;
6238287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
6239286441Srpaulo	int error;
6240286441Srpaulo
6241286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6242286441Srpaulo	    "->%s\n", __func__);
6243286441Srpaulo
6244286441Srpaulo	IWM_LOCK(sc);
6245286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
6246286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
6247286441Srpaulo		IWM_UNLOCK(sc);
6248286441Srpaulo		goto fail;
6249286441Srpaulo	}
6250286441Srpaulo
6251286441Srpaulo	error = iwm_run_init_mvm_ucode(sc, 1);
6252286441Srpaulo	iwm_stop_device(sc);
6253286441Srpaulo	if (error) {
6254286441Srpaulo		IWM_UNLOCK(sc);
6255286441Srpaulo		goto fail;
6256286441Srpaulo	}
6257286441Srpaulo	device_printf(dev,
6258303628Ssbruno	    "hw rev 0x%x, fw ver %s, address %s\n",
6259286441Srpaulo	    sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK,
6260330165Seadler	    sc->sc_fwver, ether_sprintf(sc->nvm_data->hw_addr));
6261286441Srpaulo
6262286441Srpaulo	/* not all hardware can do 5GHz band */
6263330165Seadler	if (!sc->nvm_data->sku_cap_band_52GHz_enable)
6264286441Srpaulo		memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
6265286441Srpaulo		    sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A]));
6266286441Srpaulo	IWM_UNLOCK(sc);
6267286441Srpaulo
6268298877Savos	iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
6269298877Savos	    ic->ic_channels);
6270298877Savos
6271286441Srpaulo	/*
6272286441Srpaulo	 * At this point we've committed - if we fail to do setup,
6273286441Srpaulo	 * we now also have to tear down the net80211 state.
6274286441Srpaulo	 */
6275287197Sglebius	ieee80211_ifattach(ic);
6276286441Srpaulo	ic->ic_vap_create = iwm_vap_create;
6277286441Srpaulo	ic->ic_vap_delete = iwm_vap_delete;
6278286441Srpaulo	ic->ic_raw_xmit = iwm_raw_xmit;
6279286441Srpaulo	ic->ic_node_alloc = iwm_node_alloc;
6280286441Srpaulo	ic->ic_scan_start = iwm_scan_start;
6281286441Srpaulo	ic->ic_scan_end = iwm_scan_end;
6282286441Srpaulo	ic->ic_update_mcast = iwm_update_mcast;
6283298877Savos	ic->ic_getradiocaps = iwm_init_channel_map;
6284286441Srpaulo	ic->ic_set_channel = iwm_set_channel;
6285286441Srpaulo	ic->ic_scan_curchan = iwm_scan_curchan;
6286286441Srpaulo	ic->ic_scan_mindwell = iwm_scan_mindwell;
6287330209Seadler	ic->ic_wme.wme_update = iwm_wme_update;
6288287197Sglebius	ic->ic_parent = iwm_parent;
6289287197Sglebius	ic->ic_transmit = iwm_transmit;
6290286441Srpaulo	iwm_radiotap_attach(sc);
6291286441Srpaulo	if (bootverbose)
6292286441Srpaulo		ieee80211_announce(ic);
6293286441Srpaulo
6294286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6295286441Srpaulo	    "<-%s\n", __func__);
6296286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
6297286441Srpaulo
6298286441Srpaulo	return;
6299286441Srpaulofail:
6300286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
6301286441Srpaulo	iwm_detach_local(sc, 0);
6302286441Srpaulo}
6303286441Srpaulo
6304286441Srpaulo/*
6305286441Srpaulo * Attach the interface to 802.11 radiotap.
6306286441Srpaulo */
6307286441Srpaulostatic void
6308286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc)
6309286441Srpaulo{
6310287197Sglebius        struct ieee80211com *ic = &sc->sc_ic;
6311286441Srpaulo
6312286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6313286441Srpaulo	    "->%s begin\n", __func__);
6314286441Srpaulo        ieee80211_radiotap_attach(ic,
6315286441Srpaulo            &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
6316286441Srpaulo                IWM_TX_RADIOTAP_PRESENT,
6317286441Srpaulo            &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
6318286441Srpaulo                IWM_RX_RADIOTAP_PRESENT);
6319286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
6320286441Srpaulo	    "->%s end\n", __func__);
6321286441Srpaulo}
6322286441Srpaulo
6323286441Srpaulostatic struct ieee80211vap *
6324286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
6325286441Srpaulo    enum ieee80211_opmode opmode, int flags,
6326286441Srpaulo    const uint8_t bssid[IEEE80211_ADDR_LEN],
6327286441Srpaulo    const uint8_t mac[IEEE80211_ADDR_LEN])
6328286441Srpaulo{
6329286441Srpaulo	struct iwm_vap *ivp;
6330286441Srpaulo	struct ieee80211vap *vap;
6331286441Srpaulo
6332286441Srpaulo	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
6333286441Srpaulo		return NULL;
6334287197Sglebius	ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO);
6335286441Srpaulo	vap = &ivp->iv_vap;
6336287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
6337286441Srpaulo	vap->iv_bmissthreshold = 10;            /* override default */
6338286441Srpaulo	/* Override with driver methods. */
6339286441Srpaulo	ivp->iv_newstate = vap->iv_newstate;
6340286441Srpaulo	vap->iv_newstate = iwm_newstate;
6341286441Srpaulo
6342330204Seadler	ivp->id = IWM_DEFAULT_MACID;
6343330204Seadler	ivp->color = IWM_DEFAULT_COLOR;
6344330204Seadler
6345330209Seadler	ivp->have_wme = FALSE;
6346330209Seadler
6347286441Srpaulo	ieee80211_ratectl_init(vap);
6348286441Srpaulo	/* Complete setup. */
6349287197Sglebius	ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status,
6350287197Sglebius	    mac);
6351286441Srpaulo	ic->ic_opmode = opmode;
6352286441Srpaulo
6353286441Srpaulo	return vap;
6354286441Srpaulo}
6355286441Srpaulo
6356286441Srpaulostatic void
6357286441Srpauloiwm_vap_delete(struct ieee80211vap *vap)
6358286441Srpaulo{
6359286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
6360286441Srpaulo
6361286441Srpaulo	ieee80211_ratectl_deinit(vap);
6362286441Srpaulo	ieee80211_vap_detach(vap);
6363286441Srpaulo	free(ivp, M_80211_VAP);
6364286441Srpaulo}
6365286441Srpaulo
6366286441Srpaulostatic void
6367330223Seadleriwm_xmit_queue_drain(struct iwm_softc *sc)
6368330223Seadler{
6369330223Seadler	struct mbuf *m;
6370330223Seadler	struct ieee80211_node *ni;
6371330223Seadler
6372330223Seadler	while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
6373330223Seadler		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
6374330223Seadler		ieee80211_free_node(ni);
6375330223Seadler		m_freem(m);
6376330223Seadler	}
6377330223Seadler}
6378330223Seadler
6379330223Seadlerstatic void
6380286441Srpauloiwm_scan_start(struct ieee80211com *ic)
6381286441Srpaulo{
6382286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6383303628Ssbruno	struct iwm_softc *sc = ic->ic_softc;
6384286441Srpaulo	int error;
6385286441Srpaulo
6386286441Srpaulo	IWM_LOCK(sc);
6387330174Seadler	if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) {
6388330174Seadler		/* This should not be possible */
6389330174Seadler		device_printf(sc->sc_dev,
6390330174Seadler		    "%s: Previous scan not completed yet\n", __func__);
6391330174Seadler	}
6392330196Seadler	if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
6393303628Ssbruno		error = iwm_mvm_umac_scan(sc);
6394303628Ssbruno	else
6395303628Ssbruno		error = iwm_mvm_lmac_scan(sc);
6396303628Ssbruno	if (error != 0) {
6397330174Seadler		device_printf(sc->sc_dev, "could not initiate scan\n");
6398286441Srpaulo		IWM_UNLOCK(sc);
6399286441Srpaulo		ieee80211_cancel_scan(vap);
6400301187Sadrian	} else {
6401330174Seadler		sc->sc_flags |= IWM_FLAG_SCAN_RUNNING;
6402301187Sadrian		iwm_led_blink_start(sc);
6403286441Srpaulo		IWM_UNLOCK(sc);
6404301187Sadrian	}
6405286441Srpaulo}
6406286441Srpaulo
6407286441Srpaulostatic void
6408286441Srpauloiwm_scan_end(struct ieee80211com *ic)
6409286441Srpaulo{
6410301187Sadrian	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6411301187Sadrian	struct iwm_softc *sc = ic->ic_softc;
6412301187Sadrian
6413301187Sadrian	IWM_LOCK(sc);
6414301187Sadrian	iwm_led_blink_stop(sc);
6415301187Sadrian	if (vap->iv_state == IEEE80211_S_RUN)
6416301187Sadrian		iwm_mvm_led_enable(sc);
6417330174Seadler	if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) {
6418330174Seadler		/*
6419330174Seadler		 * Removing IWM_FLAG_SCAN_RUNNING now, is fine because
6420330174Seadler		 * both iwm_scan_end and iwm_scan_start run in the ic->ic_tq
6421330174Seadler		 * taskqueue.
6422330174Seadler		 */
6423330174Seadler		sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING;
6424330174Seadler		iwm_mvm_scan_stop_wait(sc);
6425330174Seadler	}
6426301187Sadrian	IWM_UNLOCK(sc);
6427330174Seadler
6428330174Seadler	/*
6429330174Seadler	 * Make sure we don't race, if sc_es_task is still enqueued here.
6430330174Seadler	 * This is to make sure that it won't call ieee80211_scan_done
6431330174Seadler	 * when we have already started the next scan.
6432330174Seadler	 */
6433330174Seadler	taskqueue_cancel(ic->ic_tq, &sc->sc_es_task, NULL);
6434286441Srpaulo}
6435286441Srpaulo
6436286441Srpaulostatic void
6437286441Srpauloiwm_update_mcast(struct ieee80211com *ic)
6438286441Srpaulo{
6439286441Srpaulo}
6440286441Srpaulo
6441286441Srpaulostatic void
6442286441Srpauloiwm_set_channel(struct ieee80211com *ic)
6443286441Srpaulo{
6444286441Srpaulo}
6445286441Srpaulo
6446286441Srpaulostatic void
6447286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6448286441Srpaulo{
6449286441Srpaulo}
6450286441Srpaulo
6451286441Srpaulostatic void
6452286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss)
6453286441Srpaulo{
6454286441Srpaulo	return;
6455286441Srpaulo}
6456286441Srpaulo
6457286441Srpaulovoid
6458286441Srpauloiwm_init_task(void *arg1)
6459286441Srpaulo{
6460286441Srpaulo	struct iwm_softc *sc = arg1;
6461286441Srpaulo
6462286441Srpaulo	IWM_LOCK(sc);
6463286441Srpaulo	while (sc->sc_flags & IWM_FLAG_BUSY)
6464286441Srpaulo		msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0);
6465286441Srpaulo	sc->sc_flags |= IWM_FLAG_BUSY;
6466287197Sglebius	iwm_stop(sc);
6467287197Sglebius	if (sc->sc_ic.ic_nrunning > 0)
6468286441Srpaulo		iwm_init(sc);
6469286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_BUSY;
6470286441Srpaulo	wakeup(&sc->sc_flags);
6471286441Srpaulo	IWM_UNLOCK(sc);
6472286441Srpaulo}
6473286441Srpaulo
6474286441Srpaulostatic int
6475286441Srpauloiwm_resume(device_t dev)
6476286441Srpaulo{
6477298612Sadrian	struct iwm_softc *sc = device_get_softc(dev);
6478298612Sadrian	int do_reinit = 0;
6479286441Srpaulo
6480330140Seadler	/*
6481330140Seadler	 * We disable the RETRY_TIMEOUT register (0x41) to keep
6482330140Seadler	 * PCI Tx retries from interfering with C3 CPU state.
6483330140Seadler	 */
6484330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
6485286441Srpaulo	iwm_init_task(device_get_softc(dev));
6486286441Srpaulo
6487298612Sadrian	IWM_LOCK(sc);
6488303628Ssbruno	if (sc->sc_flags & IWM_FLAG_SCANNING) {
6489303628Ssbruno		sc->sc_flags &= ~IWM_FLAG_SCANNING;
6490298612Sadrian		do_reinit = 1;
6491298612Sadrian	}
6492298612Sadrian	IWM_UNLOCK(sc);
6493298612Sadrian
6494298612Sadrian	if (do_reinit)
6495298612Sadrian		ieee80211_resume_all(&sc->sc_ic);
6496298612Sadrian
6497286441Srpaulo	return 0;
6498286441Srpaulo}
6499286441Srpaulo
6500286441Srpaulostatic int
6501286441Srpauloiwm_suspend(device_t dev)
6502286441Srpaulo{
6503298612Sadrian	int do_stop = 0;
6504286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6505286441Srpaulo
6506298612Sadrian	do_stop = !! (sc->sc_ic.ic_nrunning > 0);
6507298612Sadrian
6508298612Sadrian	ieee80211_suspend_all(&sc->sc_ic);
6509298612Sadrian
6510298612Sadrian	if (do_stop) {
6511287197Sglebius		IWM_LOCK(sc);
6512287197Sglebius		iwm_stop(sc);
6513303628Ssbruno		sc->sc_flags |= IWM_FLAG_SCANNING;
6514287197Sglebius		IWM_UNLOCK(sc);
6515287197Sglebius	}
6516286441Srpaulo
6517286441Srpaulo	return (0);
6518286441Srpaulo}
6519286441Srpaulo
6520286441Srpaulostatic int
6521286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211)
6522286441Srpaulo{
6523293219Savos	struct iwm_fw_info *fw = &sc->sc_fw;
6524286441Srpaulo	device_t dev = sc->sc_dev;
6525286441Srpaulo	int i;
6526286441Srpaulo
6527330164Seadler	if (!sc->sc_attached)
6528330164Seadler		return 0;
6529330164Seadler	sc->sc_attached = 0;
6530330164Seadler
6531330159Seadler	if (do_net80211)
6532330159Seadler		ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task);
6533303628Ssbruno
6534301187Sadrian	callout_drain(&sc->sc_led_blink_to);
6535287197Sglebius	callout_drain(&sc->sc_watchdog_to);
6536287197Sglebius	iwm_stop_device(sc);
6537303628Ssbruno	if (do_net80211) {
6538330223Seadler		IWM_LOCK(sc);
6539330223Seadler		iwm_xmit_queue_drain(sc);
6540330223Seadler		IWM_UNLOCK(sc);
6541287197Sglebius		ieee80211_ifdetach(&sc->sc_ic);
6542303628Ssbruno	}
6543286441Srpaulo
6544330163Seadler	iwm_phy_db_free(sc->sc_phy_db);
6545330163Seadler	sc->sc_phy_db = NULL;
6546302103Sadrian
6547330165Seadler	iwm_free_nvm_data(sc->nvm_data);
6548330165Seadler
6549286441Srpaulo	/* Free descriptor rings */
6550301970Sadrian	iwm_free_rx_ring(sc, &sc->rxq);
6551286441Srpaulo	for (i = 0; i < nitems(sc->txq); i++)
6552286441Srpaulo		iwm_free_tx_ring(sc, &sc->txq[i]);
6553286441Srpaulo
6554293219Savos	/* Free firmware */
6555293219Savos	if (fw->fw_fp != NULL)
6556293219Savos		iwm_fw_info_free(fw);
6557293219Savos
6558293178Savos	/* Free scheduler */
6559330150Seadler	iwm_dma_contig_free(&sc->sched_dma);
6560330150Seadler	iwm_dma_contig_free(&sc->ict_dma);
6561330150Seadler	iwm_dma_contig_free(&sc->kw_dma);
6562330150Seadler	iwm_dma_contig_free(&sc->fw_dma);
6563286441Srpaulo
6564330192Seadler	iwm_free_fw_paging(sc);
6565330192Seadler
6566286441Srpaulo	/* Finished with the hardware - detach things */
6567286441Srpaulo	iwm_pci_detach(dev);
6568286441Srpaulo
6569330170Seadler	if (sc->sc_notif_wait != NULL) {
6570330170Seadler		iwm_notification_wait_free(sc->sc_notif_wait);
6571330170Seadler		sc->sc_notif_wait = NULL;
6572330170Seadler	}
6573330170Seadler
6574293099Savos	IWM_LOCK_DESTROY(sc);
6575286441Srpaulo
6576286441Srpaulo	return (0);
6577286441Srpaulo}
6578286441Srpaulo
6579286441Srpaulostatic int
6580286441Srpauloiwm_detach(device_t dev)
6581286441Srpaulo{
6582286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6583286441Srpaulo
6584286441Srpaulo	return (iwm_detach_local(sc, 1));
6585286441Srpaulo}
6586286441Srpaulo
6587286441Srpaulostatic device_method_t iwm_pci_methods[] = {
6588286441Srpaulo        /* Device interface */
6589286441Srpaulo        DEVMETHOD(device_probe,         iwm_probe),
6590286441Srpaulo        DEVMETHOD(device_attach,        iwm_attach),
6591286441Srpaulo        DEVMETHOD(device_detach,        iwm_detach),
6592286441Srpaulo        DEVMETHOD(device_suspend,       iwm_suspend),
6593286441Srpaulo        DEVMETHOD(device_resume,        iwm_resume),
6594286441Srpaulo
6595286441Srpaulo        DEVMETHOD_END
6596286441Srpaulo};
6597286441Srpaulo
6598286441Srpaulostatic driver_t iwm_pci_driver = {
6599286441Srpaulo        "iwm",
6600286441Srpaulo        iwm_pci_methods,
6601286441Srpaulo        sizeof (struct iwm_softc)
6602286441Srpaulo};
6603286441Srpaulo
6604286441Srpaulostatic devclass_t iwm_devclass;
6605286441Srpaulo
6606286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL);
6607286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1);
6608286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1);
6609286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1);
6610