if_iwm.c revision 330155
1303628Ssbruno/*	$OpenBSD: if_iwm.c,v 1.42 2015/05/30 02:49:23 deraadt Exp $	*/
2286441Srpaulo
3286441Srpaulo/*
4286441Srpaulo * Copyright (c) 2014 genua mbh <info@genua.de>
5286441Srpaulo * Copyright (c) 2014 Fixup Software Ltd.
6286441Srpaulo *
7286441Srpaulo * Permission to use, copy, modify, and distribute this software for any
8286441Srpaulo * purpose with or without fee is hereby granted, provided that the above
9286441Srpaulo * copyright notice and this permission notice appear in all copies.
10286441Srpaulo *
11286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18286441Srpaulo */
19286441Srpaulo
20286441Srpaulo/*-
21286441Srpaulo * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22286441Srpaulo * which were used as the reference documentation for this implementation.
23286441Srpaulo *
24286441Srpaulo * Driver version we are currently based off of is
25286441Srpaulo * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
26286441Srpaulo *
27286441Srpaulo ***********************************************************************
28286441Srpaulo *
29286441Srpaulo * This file is provided under a dual BSD/GPLv2 license.  When using or
30286441Srpaulo * redistributing this file, you may do so under either license.
31286441Srpaulo *
32286441Srpaulo * GPL LICENSE SUMMARY
33286441Srpaulo *
34286441Srpaulo * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
35286441Srpaulo *
36286441Srpaulo * This program is free software; you can redistribute it and/or modify
37286441Srpaulo * it under the terms of version 2 of the GNU General Public License as
38286441Srpaulo * published by the Free Software Foundation.
39286441Srpaulo *
40286441Srpaulo * This program is distributed in the hope that it will be useful, but
41286441Srpaulo * WITHOUT ANY WARRANTY; without even the implied warranty of
42286441Srpaulo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43286441Srpaulo * General Public License for more details.
44286441Srpaulo *
45286441Srpaulo * You should have received a copy of the GNU General Public License
46286441Srpaulo * along with this program; if not, write to the Free Software
47286441Srpaulo * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
48286441Srpaulo * USA
49286441Srpaulo *
50286441Srpaulo * The full GNU General Public License is included in this distribution
51286441Srpaulo * in the file called COPYING.
52286441Srpaulo *
53286441Srpaulo * Contact Information:
54286441Srpaulo *  Intel Linux Wireless <ilw@linux.intel.com>
55286441Srpaulo * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
56286441Srpaulo *
57286441Srpaulo *
58286441Srpaulo * BSD LICENSE
59286441Srpaulo *
60286441Srpaulo * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61286441Srpaulo * All rights reserved.
62286441Srpaulo *
63286441Srpaulo * Redistribution and use in source and binary forms, with or without
64286441Srpaulo * modification, are permitted provided that the following conditions
65286441Srpaulo * are met:
66286441Srpaulo *
67286441Srpaulo *  * Redistributions of source code must retain the above copyright
68286441Srpaulo *    notice, this list of conditions and the following disclaimer.
69286441Srpaulo *  * Redistributions in binary form must reproduce the above copyright
70286441Srpaulo *    notice, this list of conditions and the following disclaimer in
71286441Srpaulo *    the documentation and/or other materials provided with the
72286441Srpaulo *    distribution.
73286441Srpaulo *  * Neither the name Intel Corporation nor the names of its
74286441Srpaulo *    contributors may be used to endorse or promote products derived
75286441Srpaulo *    from this software without specific prior written permission.
76286441Srpaulo *
77286441Srpaulo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78286441Srpaulo * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79286441Srpaulo * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80286441Srpaulo * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81286441Srpaulo * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82286441Srpaulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83286441Srpaulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84286441Srpaulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85286441Srpaulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86286441Srpaulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87286441Srpaulo * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
88286441Srpaulo */
89286441Srpaulo
90286441Srpaulo/*-
91286441Srpaulo * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
92286441Srpaulo *
93286441Srpaulo * Permission to use, copy, modify, and distribute this software for any
94286441Srpaulo * purpose with or without fee is hereby granted, provided that the above
95286441Srpaulo * copyright notice and this permission notice appear in all copies.
96286441Srpaulo *
97286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
100286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
101286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
102286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
103286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
104286441Srpaulo */
105286441Srpaulo#include <sys/cdefs.h>
106286441Srpaulo__FBSDID("$FreeBSD: stable/11/sys/dev/iwm/if_iwm.c 330155 2018-03-01 04:58:00Z eadler $");
107286441Srpaulo
108300248Savos#include "opt_wlan.h"
109300248Savos
110286441Srpaulo#include <sys/param.h>
111286441Srpaulo#include <sys/bus.h>
112286441Srpaulo#include <sys/conf.h>
113286441Srpaulo#include <sys/endian.h>
114286441Srpaulo#include <sys/firmware.h>
115286441Srpaulo#include <sys/kernel.h>
116286441Srpaulo#include <sys/malloc.h>
117286441Srpaulo#include <sys/mbuf.h>
118286441Srpaulo#include <sys/mutex.h>
119286441Srpaulo#include <sys/module.h>
120286441Srpaulo#include <sys/proc.h>
121286441Srpaulo#include <sys/rman.h>
122286441Srpaulo#include <sys/socket.h>
123286441Srpaulo#include <sys/sockio.h>
124286441Srpaulo#include <sys/sysctl.h>
125286441Srpaulo#include <sys/linker.h>
126286441Srpaulo
127286441Srpaulo#include <machine/bus.h>
128286441Srpaulo#include <machine/endian.h>
129286441Srpaulo#include <machine/resource.h>
130286441Srpaulo
131286441Srpaulo#include <dev/pci/pcivar.h>
132286441Srpaulo#include <dev/pci/pcireg.h>
133286441Srpaulo
134286441Srpaulo#include <net/bpf.h>
135286441Srpaulo
136286441Srpaulo#include <net/if.h>
137286441Srpaulo#include <net/if_var.h>
138286441Srpaulo#include <net/if_arp.h>
139286441Srpaulo#include <net/if_dl.h>
140286441Srpaulo#include <net/if_media.h>
141286441Srpaulo#include <net/if_types.h>
142286441Srpaulo
143286441Srpaulo#include <netinet/in.h>
144286441Srpaulo#include <netinet/in_systm.h>
145286441Srpaulo#include <netinet/if_ether.h>
146286441Srpaulo#include <netinet/ip.h>
147286441Srpaulo
148286441Srpaulo#include <net80211/ieee80211_var.h>
149286441Srpaulo#include <net80211/ieee80211_regdomain.h>
150286441Srpaulo#include <net80211/ieee80211_ratectl.h>
151286441Srpaulo#include <net80211/ieee80211_radiotap.h>
152286441Srpaulo
153286475Srpaulo#include <dev/iwm/if_iwmreg.h>
154286475Srpaulo#include <dev/iwm/if_iwmvar.h>
155286475Srpaulo#include <dev/iwm/if_iwm_debug.h>
156286475Srpaulo#include <dev/iwm/if_iwm_util.h>
157286475Srpaulo#include <dev/iwm/if_iwm_binding.h>
158286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h>
159286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h>
160286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h>
161286475Srpaulo#include <dev/iwm/if_iwm_time_event.h>
162286475Srpaulo#include <dev/iwm/if_iwm_power.h>
163286475Srpaulo#include <dev/iwm/if_iwm_scan.h>
164286441Srpaulo
165286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h>
166301187Sadrian#include <dev/iwm/if_iwm_led.h>
167286441Srpaulo
168286441Srpauloconst uint8_t iwm_nvm_channels[] = {
169286441Srpaulo	/* 2.4 GHz */
170286441Srpaulo	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
171286441Srpaulo	/* 5 GHz */
172298877Savos	36, 40, 44, 48, 52, 56, 60, 64,
173286441Srpaulo	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
174286441Srpaulo	149, 153, 157, 161, 165
175286441Srpaulo};
176298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS,
177298877Savos    "IWM_NUM_CHANNELS is too small");
178298877Savos
179303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = {
180303628Ssbruno	/* 2.4 GHz */
181303628Ssbruno	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
182303628Ssbruno	/* 5 GHz */
183303628Ssbruno	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
184303628Ssbruno	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
185303628Ssbruno	149, 153, 157, 161, 165, 169, 173, 177, 181
186303628Ssbruno};
187303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000,
188303628Ssbruno    "IWM_NUM_CHANNELS_8000 is too small");
189303628Ssbruno
190303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS	14
191303628Ssbruno#define IWM_N_HW_ADDR_MASK	0xF
192303628Ssbruno
193286441Srpaulo/*
194286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries
195286441Srpaulo * that are populated.
196286441Srpaulo */
197286441Srpauloconst struct iwm_rate {
198286441Srpaulo	uint8_t rate;
199286441Srpaulo	uint8_t plcp;
200286441Srpaulo} iwm_rates[] = {
201286441Srpaulo	{   2,	IWM_RATE_1M_PLCP  },
202286441Srpaulo	{   4,	IWM_RATE_2M_PLCP  },
203286441Srpaulo	{  11,	IWM_RATE_5M_PLCP  },
204286441Srpaulo	{  22,	IWM_RATE_11M_PLCP },
205286441Srpaulo	{  12,	IWM_RATE_6M_PLCP  },
206286441Srpaulo	{  18,	IWM_RATE_9M_PLCP  },
207286441Srpaulo	{  24,	IWM_RATE_12M_PLCP },
208286441Srpaulo	{  36,	IWM_RATE_18M_PLCP },
209286441Srpaulo	{  48,	IWM_RATE_24M_PLCP },
210286441Srpaulo	{  72,	IWM_RATE_36M_PLCP },
211286441Srpaulo	{  96,	IWM_RATE_48M_PLCP },
212286441Srpaulo	{ 108,	IWM_RATE_54M_PLCP },
213286441Srpaulo};
214286441Srpaulo#define IWM_RIDX_CCK	0
215286441Srpaulo#define IWM_RIDX_OFDM	4
216286441Srpaulo#define IWM_RIDX_MAX	(nitems(iwm_rates)-1)
217286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
218286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
219286441Srpaulo
220303628Ssbrunostruct iwm_nvm_section {
221303628Ssbruno	uint16_t length;
222303628Ssbruno	uint8_t *data;
223303628Ssbruno};
224303628Ssbruno
225286441Srpaulostatic int	iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t);
226286441Srpaulostatic int	iwm_firmware_store_section(struct iwm_softc *,
227286441Srpaulo                                           enum iwm_ucode_type,
228286441Srpaulo                                           const uint8_t *, size_t);
229286441Srpaulostatic int	iwm_set_default_calib(struct iwm_softc *, const void *);
230286441Srpaulostatic void	iwm_fw_info_free(struct iwm_fw_info *);
231286441Srpaulostatic int	iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type);
232286441Srpaulostatic void	iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int);
233286441Srpaulostatic int	iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *,
234286441Srpaulo                                     bus_size_t, bus_size_t);
235286441Srpaulostatic void	iwm_dma_contig_free(struct iwm_dma_info *);
236286441Srpaulostatic int	iwm_alloc_fwmem(struct iwm_softc *);
237286441Srpaulostatic int	iwm_alloc_sched(struct iwm_softc *);
238286441Srpaulostatic int	iwm_alloc_kw(struct iwm_softc *);
239286441Srpaulostatic int	iwm_alloc_ict(struct iwm_softc *);
240286441Srpaulostatic int	iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
241301191Sadrianstatic void	iwm_disable_rx_dma(struct iwm_softc *);
242286441Srpaulostatic void	iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
243286441Srpaulostatic void	iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
244286441Srpaulostatic int	iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *,
245286441Srpaulo                                  int);
246286441Srpaulostatic void	iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
247286441Srpaulostatic void	iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
248286441Srpaulostatic void	iwm_enable_interrupts(struct iwm_softc *);
249286441Srpaulostatic void	iwm_restore_interrupts(struct iwm_softc *);
250286441Srpaulostatic void	iwm_disable_interrupts(struct iwm_softc *);
251286441Srpaulostatic void	iwm_ict_reset(struct iwm_softc *);
252286441Srpaulostatic int	iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *);
253286441Srpaulostatic void	iwm_stop_device(struct iwm_softc *);
254286441Srpaulostatic void	iwm_mvm_nic_config(struct iwm_softc *);
255286441Srpaulostatic int	iwm_nic_rx_init(struct iwm_softc *);
256286441Srpaulostatic int	iwm_nic_tx_init(struct iwm_softc *);
257286441Srpaulostatic int	iwm_nic_init(struct iwm_softc *);
258303628Ssbrunostatic int	iwm_enable_txq(struct iwm_softc *, int, int, int);
259286441Srpaulostatic int	iwm_post_alive(struct iwm_softc *);
260286441Srpaulostatic int	iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t,
261286441Srpaulo                                   uint16_t, uint8_t *, uint16_t *);
262286441Srpaulostatic int	iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
263303628Ssbruno				     uint16_t *, size_t);
264298877Savosstatic uint32_t	iwm_eeprom_channel_flags(uint16_t);
265298877Savosstatic void	iwm_add_channel_band(struct iwm_softc *,
266303628Ssbruno		    struct ieee80211_channel[], int, int *, int, size_t,
267298877Savos		    const uint8_t[]);
268298877Savosstatic void	iwm_init_channel_map(struct ieee80211com *, int, int *,
269298877Savos		    struct ieee80211_channel[]);
270286441Srpaulostatic int	iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
271303628Ssbruno				   const uint16_t *, const uint16_t *,
272303628Ssbruno				   const uint16_t *, const uint16_t *,
273303628Ssbruno				   const uint16_t *);
274303628Ssbrunostatic void	iwm_set_hw_address_8000(struct iwm_softc *,
275303628Ssbruno					struct iwm_nvm_data *,
276303628Ssbruno					const uint16_t *, const uint16_t *);
277303628Ssbrunostatic int	iwm_get_sku(const struct iwm_softc *, const uint16_t *,
278303628Ssbruno			    const uint16_t *);
279303628Ssbrunostatic int	iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *);
280303628Ssbrunostatic int	iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *,
281303628Ssbruno				  const uint16_t *);
282303628Ssbrunostatic int	iwm_get_n_hw_addrs(const struct iwm_softc *,
283303628Ssbruno				   const uint16_t *);
284303628Ssbrunostatic void	iwm_set_radio_cfg(const struct iwm_softc *,
285303628Ssbruno				  struct iwm_nvm_data *, uint32_t);
286286441Srpaulostatic int	iwm_parse_nvm_sections(struct iwm_softc *,
287286441Srpaulo                                       struct iwm_nvm_section *);
288286441Srpaulostatic int	iwm_nvm_init(struct iwm_softc *);
289303628Ssbrunostatic int	iwm_firmware_load_sect(struct iwm_softc *, uint32_t,
290303628Ssbruno                                       const uint8_t *, uint32_t);
291286441Srpaulostatic int	iwm_firmware_load_chunk(struct iwm_softc *, uint32_t,
292286441Srpaulo                                        const uint8_t *, uint32_t);
293303628Ssbrunostatic int	iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type);
294303628Ssbrunostatic int	iwm_load_cpu_sections_8000(struct iwm_softc *,
295303628Ssbruno					   struct iwm_fw_sects *, int , int *);
296303628Ssbrunostatic int	iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type);
297286441Srpaulostatic int	iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type);
298286441Srpaulostatic int	iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type);
299286441Srpaulostatic int	iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t);
300286441Srpaulostatic int	iwm_send_phy_cfg_cmd(struct iwm_softc *);
301286441Srpaulostatic int	iwm_mvm_load_ucode_wait_alive(struct iwm_softc *,
302286441Srpaulo                                              enum iwm_ucode_type);
303286441Srpaulostatic int	iwm_run_init_mvm_ucode(struct iwm_softc *, int);
304286441Srpaulostatic int	iwm_rx_addbuf(struct iwm_softc *, int, int);
305286441Srpaulostatic int	iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *);
306286441Srpaulostatic int	iwm_mvm_get_signal_strength(struct iwm_softc *,
307286441Srpaulo					    struct iwm_rx_phy_info *);
308286441Srpaulostatic void	iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *,
309286441Srpaulo                                      struct iwm_rx_packet *,
310286441Srpaulo                                      struct iwm_rx_data *);
311330144Seadlerstatic int	iwm_get_noise(struct iwm_softc *sc,
312330144Seadler		    const struct iwm_mvm_statistics_rx_non_phy *);
313286441Srpaulostatic void	iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
314286441Srpaulo                                   struct iwm_rx_data *);
315293100Savosstatic int	iwm_mvm_rx_tx_cmd_single(struct iwm_softc *,
316286441Srpaulo                                         struct iwm_rx_packet *,
317286441Srpaulo				         struct iwm_node *);
318286441Srpaulostatic void	iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *,
319286441Srpaulo                                  struct iwm_rx_data *);
320286441Srpaulostatic void	iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *);
321286441Srpaulo#if 0
322286441Srpaulostatic void	iwm_update_sched(struct iwm_softc *, int, int, uint8_t,
323286441Srpaulo                                 uint16_t);
324286441Srpaulo#endif
325286441Srpaulostatic const struct iwm_rate *
326286441Srpaulo	iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *,
327330155Seadler			struct mbuf *, struct iwm_tx_cmd *);
328286441Srpaulostatic int	iwm_tx(struct iwm_softc *, struct mbuf *,
329286441Srpaulo                       struct ieee80211_node *, int);
330286441Srpaulostatic int	iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
331286441Srpaulo			     const struct ieee80211_bpf_params *);
332330154Seadlerstatic int	iwm_mvm_flush_tx_path(struct iwm_softc *sc,
333330154Seadler				      uint32_t tfd_msk, uint32_t flags);
334286441Srpaulostatic int	iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *,
335303628Ssbruno					        struct iwm_mvm_add_sta_cmd_v7 *,
336286441Srpaulo                                                int *);
337286441Srpaulostatic int	iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *,
338286441Srpaulo                                       int);
339286441Srpaulostatic int	iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *);
340286441Srpaulostatic int	iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *);
341286441Srpaulostatic int	iwm_mvm_add_int_sta_common(struct iwm_softc *,
342286441Srpaulo                                           struct iwm_int_sta *,
343286441Srpaulo				           const uint8_t *, uint16_t, uint16_t);
344286441Srpaulostatic int	iwm_mvm_add_aux_sta(struct iwm_softc *);
345286441Srpaulostatic int	iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *);
346286441Srpaulostatic int	iwm_auth(struct ieee80211vap *, struct iwm_softc *);
347286441Srpaulostatic int	iwm_assoc(struct ieee80211vap *, struct iwm_softc *);
348286441Srpaulostatic int	iwm_release(struct iwm_softc *, struct iwm_node *);
349286441Srpaulostatic struct ieee80211_node *
350286441Srpaulo		iwm_node_alloc(struct ieee80211vap *,
351286441Srpaulo		               const uint8_t[IEEE80211_ADDR_LEN]);
352286441Srpaulostatic void	iwm_setrates(struct iwm_softc *, struct iwm_node *);
353286441Srpaulostatic int	iwm_media_change(struct ifnet *);
354286441Srpaulostatic int	iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int);
355286441Srpaulostatic void	iwm_endscan_cb(void *, int);
356303628Ssbrunostatic void	iwm_mvm_fill_sf_command(struct iwm_softc *,
357303628Ssbruno					struct iwm_sf_cfg_cmd *,
358303628Ssbruno					struct ieee80211_node *);
359303628Ssbrunostatic int	iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state);
360303628Ssbrunostatic int	iwm_send_bt_init_conf(struct iwm_softc *);
361303628Ssbrunostatic int	iwm_send_update_mcc_cmd(struct iwm_softc *, const char *);
362303628Ssbrunostatic void	iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t);
363286441Srpaulostatic int	iwm_init_hw(struct iwm_softc *);
364287197Sglebiusstatic void	iwm_init(struct iwm_softc *);
365287197Sglebiusstatic void	iwm_start(struct iwm_softc *);
366287197Sglebiusstatic void	iwm_stop(struct iwm_softc *);
367286441Srpaulostatic void	iwm_watchdog(void *);
368287197Sglebiusstatic void	iwm_parent(struct ieee80211com *);
369286441Srpaulo#ifdef IWM_DEBUG
370286441Srpaulostatic const char *
371286441Srpaulo		iwm_desc_lookup(uint32_t);
372286441Srpaulostatic void	iwm_nic_error(struct iwm_softc *);
373303628Ssbrunostatic void	iwm_nic_umac_error(struct iwm_softc *);
374286441Srpaulo#endif
375286441Srpaulostatic void	iwm_notif_intr(struct iwm_softc *);
376286441Srpaulostatic void	iwm_intr(void *);
377286441Srpaulostatic int	iwm_attach(device_t);
378303628Ssbrunostatic int	iwm_is_valid_ether_addr(uint8_t *);
379286441Srpaulostatic void	iwm_preinit(void *);
380286441Srpaulostatic int	iwm_detach_local(struct iwm_softc *sc, int);
381286441Srpaulostatic void	iwm_init_task(void *);
382286441Srpaulostatic void	iwm_radiotap_attach(struct iwm_softc *);
383286441Srpaulostatic struct ieee80211vap *
384286441Srpaulo		iwm_vap_create(struct ieee80211com *,
385286441Srpaulo		               const char [IFNAMSIZ], int,
386286441Srpaulo		               enum ieee80211_opmode, int,
387286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN],
388286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN]);
389286441Srpaulostatic void	iwm_vap_delete(struct ieee80211vap *);
390286441Srpaulostatic void	iwm_scan_start(struct ieee80211com *);
391286441Srpaulostatic void	iwm_scan_end(struct ieee80211com *);
392286441Srpaulostatic void	iwm_update_mcast(struct ieee80211com *);
393286441Srpaulostatic void	iwm_set_channel(struct ieee80211com *);
394286441Srpaulostatic void	iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long);
395286441Srpaulostatic void	iwm_scan_mindwell(struct ieee80211_scan_state *);
396286441Srpaulostatic int	iwm_detach(device_t);
397286441Srpaulo
398286441Srpaulo/*
399286441Srpaulo * Firmware parser.
400286441Srpaulo */
401286441Srpaulo
402286441Srpaulostatic int
403286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen)
404286441Srpaulo{
405286441Srpaulo	const struct iwm_fw_cscheme_list *l = (const void *)data;
406286441Srpaulo
407286441Srpaulo	if (dlen < sizeof(*l) ||
408286441Srpaulo	    dlen < sizeof(l->size) + l->size * sizeof(*l->cs))
409286441Srpaulo		return EINVAL;
410286441Srpaulo
411286441Srpaulo	/* we don't actually store anything for now, always use s/w crypto */
412286441Srpaulo
413286441Srpaulo	return 0;
414286441Srpaulo}
415286441Srpaulo
416286441Srpaulostatic int
417286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc,
418286441Srpaulo    enum iwm_ucode_type type, const uint8_t *data, size_t dlen)
419286441Srpaulo{
420286441Srpaulo	struct iwm_fw_sects *fws;
421286441Srpaulo	struct iwm_fw_onesect *fwone;
422286441Srpaulo
423286441Srpaulo	if (type >= IWM_UCODE_TYPE_MAX)
424286441Srpaulo		return EINVAL;
425286441Srpaulo	if (dlen < sizeof(uint32_t))
426286441Srpaulo		return EINVAL;
427286441Srpaulo
428286441Srpaulo	fws = &sc->sc_fw.fw_sects[type];
429286441Srpaulo	if (fws->fw_count >= IWM_UCODE_SECT_MAX)
430286441Srpaulo		return EINVAL;
431286441Srpaulo
432286441Srpaulo	fwone = &fws->fw_sect[fws->fw_count];
433286441Srpaulo
434286441Srpaulo	/* first 32bit are device load offset */
435286441Srpaulo	memcpy(&fwone->fws_devoff, data, sizeof(uint32_t));
436286441Srpaulo
437286441Srpaulo	/* rest is data */
438286441Srpaulo	fwone->fws_data = data + sizeof(uint32_t);
439286441Srpaulo	fwone->fws_len = dlen - sizeof(uint32_t);
440286441Srpaulo
441286441Srpaulo	fws->fw_count++;
442286441Srpaulo
443286441Srpaulo	return 0;
444286441Srpaulo}
445286441Srpaulo
446330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40
447330152Seadler
448286441Srpaulo/* iwlwifi: iwl-drv.c */
449286441Srpaulostruct iwm_tlv_calib_data {
450286441Srpaulo	uint32_t ucode_type;
451286441Srpaulo	struct iwm_tlv_calib_ctrl calib;
452286441Srpaulo} __packed;
453286441Srpaulo
454286441Srpaulostatic int
455286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data)
456286441Srpaulo{
457286441Srpaulo	const struct iwm_tlv_calib_data *def_calib = data;
458286441Srpaulo	uint32_t ucode_type = le32toh(def_calib->ucode_type);
459286441Srpaulo
460286441Srpaulo	if (ucode_type >= IWM_UCODE_TYPE_MAX) {
461286441Srpaulo		device_printf(sc->sc_dev,
462286441Srpaulo		    "Wrong ucode_type %u for default "
463286441Srpaulo		    "calibration.\n", ucode_type);
464286441Srpaulo		return EINVAL;
465286441Srpaulo	}
466286441Srpaulo
467286441Srpaulo	sc->sc_default_calib[ucode_type].flow_trigger =
468286441Srpaulo	    def_calib->calib.flow_trigger;
469286441Srpaulo	sc->sc_default_calib[ucode_type].event_trigger =
470286441Srpaulo	    def_calib->calib.event_trigger;
471286441Srpaulo
472286441Srpaulo	return 0;
473286441Srpaulo}
474286441Srpaulo
475286441Srpaulostatic void
476286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw)
477286441Srpaulo{
478293177Savos	firmware_put(fw->fw_fp, FIRMWARE_UNLOAD);
479293177Savos	fw->fw_fp = NULL;
480286441Srpaulo	/* don't touch fw->fw_status */
481286441Srpaulo	memset(fw->fw_sects, 0, sizeof(fw->fw_sects));
482286441Srpaulo}
483286441Srpaulo
484286441Srpaulostatic int
485286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
486286441Srpaulo{
487286441Srpaulo	struct iwm_fw_info *fw = &sc->sc_fw;
488286441Srpaulo	const struct iwm_tlv_ucode_header *uhdr;
489286441Srpaulo	struct iwm_ucode_tlv tlv;
490286441Srpaulo	enum iwm_ucode_tlv_type tlv_type;
491286441Srpaulo	const struct firmware *fwp;
492286441Srpaulo	const uint8_t *data;
493286441Srpaulo	int error = 0;
494286441Srpaulo	size_t len;
495286441Srpaulo
496286441Srpaulo	if (fw->fw_status == IWM_FW_STATUS_DONE &&
497286441Srpaulo	    ucode_type != IWM_UCODE_TYPE_INIT)
498286441Srpaulo		return 0;
499286441Srpaulo
500286441Srpaulo	while (fw->fw_status == IWM_FW_STATUS_INPROGRESS)
501286441Srpaulo		msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0);
502286441Srpaulo	fw->fw_status = IWM_FW_STATUS_INPROGRESS;
503286441Srpaulo
504293177Savos	if (fw->fw_fp != NULL)
505286441Srpaulo		iwm_fw_info_free(fw);
506286441Srpaulo
507286441Srpaulo	/*
508286441Srpaulo	 * Load firmware into driver memory.
509293177Savos	 * fw_fp will be set.
510286441Srpaulo	 */
511286441Srpaulo	IWM_UNLOCK(sc);
512286441Srpaulo	fwp = firmware_get(sc->sc_fwname);
513293177Savos	IWM_LOCK(sc);
514286441Srpaulo	if (fwp == NULL) {
515286441Srpaulo		device_printf(sc->sc_dev,
516286441Srpaulo		    "could not read firmware %s (error %d)\n",
517286441Srpaulo		    sc->sc_fwname, error);
518286441Srpaulo		goto out;
519286441Srpaulo	}
520293177Savos	fw->fw_fp = fwp;
521286441Srpaulo
522303628Ssbruno	/* (Re-)Initialize default values. */
523303628Ssbruno	sc->sc_capaflags = 0;
524330152Seadler	sc->sc_capa_n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS;
525303628Ssbruno	memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa));
526303628Ssbruno	memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc));
527303628Ssbruno
528286441Srpaulo	/*
529286441Srpaulo	 * Parse firmware contents
530286441Srpaulo	 */
531286441Srpaulo
532293177Savos	uhdr = (const void *)fw->fw_fp->data;
533293177Savos	if (*(const uint32_t *)fw->fw_fp->data != 0
534286441Srpaulo	    || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) {
535286441Srpaulo		device_printf(sc->sc_dev, "invalid firmware %s\n",
536286441Srpaulo		    sc->sc_fwname);
537286441Srpaulo		error = EINVAL;
538286441Srpaulo		goto out;
539286441Srpaulo	}
540286441Srpaulo
541303628Ssbruno	snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)",
542303628Ssbruno	    IWM_UCODE_MAJOR(le32toh(uhdr->ver)),
543303628Ssbruno	    IWM_UCODE_MINOR(le32toh(uhdr->ver)),
544303628Ssbruno	    IWM_UCODE_API(le32toh(uhdr->ver)));
545286441Srpaulo	data = uhdr->data;
546293177Savos	len = fw->fw_fp->datasize - sizeof(*uhdr);
547286441Srpaulo
548286441Srpaulo	while (len >= sizeof(tlv)) {
549286441Srpaulo		size_t tlv_len;
550286441Srpaulo		const void *tlv_data;
551286441Srpaulo
552286441Srpaulo		memcpy(&tlv, data, sizeof(tlv));
553286441Srpaulo		tlv_len = le32toh(tlv.length);
554286441Srpaulo		tlv_type = le32toh(tlv.type);
555286441Srpaulo
556286441Srpaulo		len -= sizeof(tlv);
557286441Srpaulo		data += sizeof(tlv);
558286441Srpaulo		tlv_data = data;
559286441Srpaulo
560286441Srpaulo		if (len < tlv_len) {
561286441Srpaulo			device_printf(sc->sc_dev,
562286441Srpaulo			    "firmware too short: %zu bytes\n",
563286441Srpaulo			    len);
564286441Srpaulo			error = EINVAL;
565286441Srpaulo			goto parse_out;
566286441Srpaulo		}
567286441Srpaulo
568286441Srpaulo		switch ((int)tlv_type) {
569286441Srpaulo		case IWM_UCODE_TLV_PROBE_MAX_LEN:
570286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
571286441Srpaulo				device_printf(sc->sc_dev,
572286441Srpaulo				    "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n",
573286441Srpaulo				    __func__,
574286441Srpaulo				    (int) tlv_len);
575286441Srpaulo				error = EINVAL;
576286441Srpaulo				goto parse_out;
577286441Srpaulo			}
578286441Srpaulo			sc->sc_capa_max_probe_len
579286441Srpaulo			    = le32toh(*(const uint32_t *)tlv_data);
580286441Srpaulo			/* limit it to something sensible */
581303628Ssbruno			if (sc->sc_capa_max_probe_len >
582303628Ssbruno			    IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) {
583286441Srpaulo				IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
584286441Srpaulo				    "%s: IWM_UCODE_TLV_PROBE_MAX_LEN "
585286441Srpaulo				    "ridiculous\n", __func__);
586286441Srpaulo				error = EINVAL;
587286441Srpaulo				goto parse_out;
588286441Srpaulo			}
589286441Srpaulo			break;
590286441Srpaulo		case IWM_UCODE_TLV_PAN:
591286441Srpaulo			if (tlv_len) {
592286441Srpaulo				device_printf(sc->sc_dev,
593286441Srpaulo				    "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n",
594286441Srpaulo				    __func__,
595286441Srpaulo				    (int) tlv_len);
596286441Srpaulo				error = EINVAL;
597286441Srpaulo				goto parse_out;
598286441Srpaulo			}
599286441Srpaulo			sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN;
600286441Srpaulo			break;
601286441Srpaulo		case IWM_UCODE_TLV_FLAGS:
602286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
603286441Srpaulo				device_printf(sc->sc_dev,
604286441Srpaulo				    "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n",
605286441Srpaulo				    __func__,
606286441Srpaulo				    (int) tlv_len);
607286441Srpaulo				error = EINVAL;
608286441Srpaulo				goto parse_out;
609286441Srpaulo			}
610286441Srpaulo			/*
611286441Srpaulo			 * Apparently there can be many flags, but Linux driver
612286441Srpaulo			 * parses only the first one, and so do we.
613286441Srpaulo			 *
614286441Srpaulo			 * XXX: why does this override IWM_UCODE_TLV_PAN?
615286441Srpaulo			 * Intentional or a bug?  Observations from
616286441Srpaulo			 * current firmware file:
617286441Srpaulo			 *  1) TLV_PAN is parsed first
618286441Srpaulo			 *  2) TLV_FLAGS contains TLV_FLAGS_PAN
619286441Srpaulo			 * ==> this resets TLV_PAN to itself... hnnnk
620286441Srpaulo			 */
621286441Srpaulo			sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data);
622286441Srpaulo			break;
623286441Srpaulo		case IWM_UCODE_TLV_CSCHEME:
624286441Srpaulo			if ((error = iwm_store_cscheme(sc,
625286441Srpaulo			    tlv_data, tlv_len)) != 0) {
626286441Srpaulo				device_printf(sc->sc_dev,
627286441Srpaulo				    "%s: iwm_store_cscheme(): returned %d\n",
628286441Srpaulo				    __func__,
629286441Srpaulo				    error);
630286441Srpaulo				goto parse_out;
631286441Srpaulo			}
632286441Srpaulo			break;
633303628Ssbruno		case IWM_UCODE_TLV_NUM_OF_CPU: {
634303628Ssbruno			uint32_t num_cpu;
635286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
636286441Srpaulo				device_printf(sc->sc_dev,
637286441Srpaulo				    "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n",
638286441Srpaulo				    __func__,
639286441Srpaulo				    (int) tlv_len);
640286441Srpaulo				error = EINVAL;
641286441Srpaulo				goto parse_out;
642286441Srpaulo			}
643303628Ssbruno			num_cpu = le32toh(*(const uint32_t *)tlv_data);
644303628Ssbruno			if (num_cpu < 1 || num_cpu > 2) {
645286441Srpaulo				device_printf(sc->sc_dev,
646303628Ssbruno				    "%s: Driver supports only 1 or 2 CPUs\n",
647286441Srpaulo				    __func__);
648286441Srpaulo				error = EINVAL;
649286441Srpaulo				goto parse_out;
650286441Srpaulo			}
651286441Srpaulo			break;
652303628Ssbruno		}
653286441Srpaulo		case IWM_UCODE_TLV_SEC_RT:
654286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
655286441Srpaulo			    IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) {
656286441Srpaulo				device_printf(sc->sc_dev,
657286441Srpaulo				    "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n",
658286441Srpaulo				    __func__,
659286441Srpaulo				    error);
660286441Srpaulo				goto parse_out;
661286441Srpaulo			}
662286441Srpaulo			break;
663286441Srpaulo		case IWM_UCODE_TLV_SEC_INIT:
664286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
665286441Srpaulo			    IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) {
666286441Srpaulo				device_printf(sc->sc_dev,
667286441Srpaulo				    "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n",
668286441Srpaulo				    __func__,
669286441Srpaulo				    error);
670286441Srpaulo				goto parse_out;
671286441Srpaulo			}
672286441Srpaulo			break;
673286441Srpaulo		case IWM_UCODE_TLV_SEC_WOWLAN:
674286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
675286441Srpaulo			    IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) {
676286441Srpaulo				device_printf(sc->sc_dev,
677286441Srpaulo				    "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n",
678286441Srpaulo				    __func__,
679286441Srpaulo				    error);
680286441Srpaulo				goto parse_out;
681286441Srpaulo			}
682286441Srpaulo			break;
683286441Srpaulo		case IWM_UCODE_TLV_DEF_CALIB:
684286441Srpaulo			if (tlv_len != sizeof(struct iwm_tlv_calib_data)) {
685286441Srpaulo				device_printf(sc->sc_dev,
686286441Srpaulo				    "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n",
687286441Srpaulo				    __func__,
688286441Srpaulo				    (int) tlv_len,
689286441Srpaulo				    (int) sizeof(struct iwm_tlv_calib_data));
690286441Srpaulo				error = EINVAL;
691286441Srpaulo				goto parse_out;
692286441Srpaulo			}
693286441Srpaulo			if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) {
694286441Srpaulo				device_printf(sc->sc_dev,
695286441Srpaulo				    "%s: iwm_set_default_calib() failed: %d\n",
696286441Srpaulo				    __func__,
697286441Srpaulo				    error);
698286441Srpaulo				goto parse_out;
699286441Srpaulo			}
700286441Srpaulo			break;
701286441Srpaulo		case IWM_UCODE_TLV_PHY_SKU:
702286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
703286441Srpaulo				error = EINVAL;
704286441Srpaulo				device_printf(sc->sc_dev,
705286441Srpaulo				    "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n",
706286441Srpaulo				    __func__,
707286441Srpaulo				    (int) tlv_len);
708286441Srpaulo				goto parse_out;
709286441Srpaulo			}
710286441Srpaulo			sc->sc_fw_phy_config =
711286441Srpaulo			    le32toh(*(const uint32_t *)tlv_data);
712286441Srpaulo			break;
713286441Srpaulo
714303628Ssbruno		case IWM_UCODE_TLV_API_CHANGES_SET: {
715303628Ssbruno			const struct iwm_ucode_api *api;
716303628Ssbruno			if (tlv_len != sizeof(*api)) {
717303628Ssbruno				error = EINVAL;
718303628Ssbruno				goto parse_out;
719303628Ssbruno			}
720303628Ssbruno			api = (const struct iwm_ucode_api *)tlv_data;
721303628Ssbruno			/* Flags may exceed 32 bits in future firmware. */
722303628Ssbruno			if (le32toh(api->api_index) > 0) {
723303628Ssbruno				device_printf(sc->sc_dev,
724303628Ssbruno				    "unsupported API index %d\n",
725303628Ssbruno				    le32toh(api->api_index));
726303628Ssbruno				goto parse_out;
727303628Ssbruno			}
728303628Ssbruno			sc->sc_ucode_api = le32toh(api->api_flags);
729303628Ssbruno			break;
730303628Ssbruno		}
731303628Ssbruno
732303628Ssbruno		case IWM_UCODE_TLV_ENABLED_CAPABILITIES: {
733303628Ssbruno			const struct iwm_ucode_capa *capa;
734303628Ssbruno			int idx, i;
735303628Ssbruno			if (tlv_len != sizeof(*capa)) {
736303628Ssbruno				error = EINVAL;
737303628Ssbruno				goto parse_out;
738303628Ssbruno			}
739303628Ssbruno			capa = (const struct iwm_ucode_capa *)tlv_data;
740303628Ssbruno			idx = le32toh(capa->api_index);
741330148Seadler			if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) {
742303628Ssbruno				device_printf(sc->sc_dev,
743303628Ssbruno				    "unsupported API index %d\n", idx);
744303628Ssbruno				goto parse_out;
745303628Ssbruno			}
746303628Ssbruno			for (i = 0; i < 32; i++) {
747303628Ssbruno				if ((le32toh(capa->api_capa) & (1U << i)) == 0)
748303628Ssbruno					continue;
749303628Ssbruno				setbit(sc->sc_enabled_capa, i + (32 * idx));
750303628Ssbruno			}
751303628Ssbruno			break;
752303628Ssbruno		}
753303628Ssbruno
754303628Ssbruno		case 48: /* undocumented TLV */
755303628Ssbruno		case IWM_UCODE_TLV_SDIO_ADMA_ADDR:
756303628Ssbruno		case IWM_UCODE_TLV_FW_GSCAN_CAPA:
757286441Srpaulo			/* ignore, not used by current driver */
758286441Srpaulo			break;
759286441Srpaulo
760303628Ssbruno		case IWM_UCODE_TLV_SEC_RT_USNIFFER:
761303628Ssbruno			if ((error = iwm_firmware_store_section(sc,
762303628Ssbruno			    IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data,
763303628Ssbruno			    tlv_len)) != 0)
764303628Ssbruno				goto parse_out;
765303628Ssbruno			break;
766303628Ssbruno
767303628Ssbruno		case IWM_UCODE_TLV_N_SCAN_CHANNELS:
768303628Ssbruno			if (tlv_len != sizeof(uint32_t)) {
769303628Ssbruno				error = EINVAL;
770303628Ssbruno				goto parse_out;
771303628Ssbruno			}
772303628Ssbruno			sc->sc_capa_n_scan_channels =
773303628Ssbruno			  le32toh(*(const uint32_t *)tlv_data);
774303628Ssbruno			break;
775303628Ssbruno
776303628Ssbruno		case IWM_UCODE_TLV_FW_VERSION:
777303628Ssbruno			if (tlv_len != sizeof(uint32_t) * 3) {
778303628Ssbruno				error = EINVAL;
779303628Ssbruno				goto parse_out;
780303628Ssbruno			}
781303628Ssbruno			snprintf(sc->sc_fwver, sizeof(sc->sc_fwver),
782303628Ssbruno			    "%d.%d.%d",
783303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[0]),
784303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[1]),
785303628Ssbruno			    le32toh(((const uint32_t *)tlv_data)[2]));
786303628Ssbruno			break;
787303628Ssbruno
788286441Srpaulo		default:
789286441Srpaulo			device_printf(sc->sc_dev,
790286441Srpaulo			    "%s: unknown firmware section %d, abort\n",
791286441Srpaulo			    __func__, tlv_type);
792286441Srpaulo			error = EINVAL;
793286441Srpaulo			goto parse_out;
794286441Srpaulo		}
795286441Srpaulo
796286441Srpaulo		len -= roundup(tlv_len, 4);
797286441Srpaulo		data += roundup(tlv_len, 4);
798286441Srpaulo	}
799286441Srpaulo
800286441Srpaulo	KASSERT(error == 0, ("unhandled error"));
801286441Srpaulo
802286441Srpaulo parse_out:
803286441Srpaulo	if (error) {
804286441Srpaulo		device_printf(sc->sc_dev, "firmware parse error %d, "
805286441Srpaulo		    "section type %d\n", error, tlv_type);
806286441Srpaulo	}
807286441Srpaulo
808286441Srpaulo	if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
809286441Srpaulo		device_printf(sc->sc_dev,
810286441Srpaulo		    "device uses unsupported power ops\n");
811286441Srpaulo		error = ENOTSUP;
812286441Srpaulo	}
813286441Srpaulo
814286441Srpaulo out:
815286441Srpaulo	if (error) {
816286441Srpaulo		fw->fw_status = IWM_FW_STATUS_NONE;
817293177Savos		if (fw->fw_fp != NULL)
818286441Srpaulo			iwm_fw_info_free(fw);
819286441Srpaulo	} else
820286441Srpaulo		fw->fw_status = IWM_FW_STATUS_DONE;
821286441Srpaulo	wakeup(&sc->sc_fw);
822286441Srpaulo
823286441Srpaulo	return error;
824286441Srpaulo}
825286441Srpaulo
826286441Srpaulo/*
827286441Srpaulo * DMA resource routines
828286441Srpaulo */
829286441Srpaulo
830286441Srpaulostatic void
831286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
832286441Srpaulo{
833286441Srpaulo        if (error != 0)
834286441Srpaulo                return;
835286441Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
836303628Ssbruno	*(bus_addr_t *)arg = segs[0].ds_addr;
837286441Srpaulo}
838286441Srpaulo
839286441Srpaulostatic int
840286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma,
841286441Srpaulo    bus_size_t size, bus_size_t alignment)
842286441Srpaulo{
843286441Srpaulo	int error;
844286441Srpaulo
845286441Srpaulo	dma->tag = NULL;
846303628Ssbruno	dma->map = NULL;
847286441Srpaulo	dma->size = size;
848302102Sadrian	dma->vaddr = NULL;
849286441Srpaulo
850286441Srpaulo	error = bus_dma_tag_create(tag, alignment,
851286441Srpaulo            0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
852289679Skevlo            1, size, 0, NULL, NULL, &dma->tag);
853286441Srpaulo        if (error != 0)
854286441Srpaulo                goto fail;
855286441Srpaulo
856286441Srpaulo        error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
857286441Srpaulo            BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
858286441Srpaulo        if (error != 0)
859286441Srpaulo                goto fail;
860286441Srpaulo
861286441Srpaulo        error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
862286441Srpaulo            iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
863302102Sadrian        if (error != 0) {
864302102Sadrian		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
865302102Sadrian		dma->vaddr = NULL;
866303628Ssbruno		goto fail;
867302102Sadrian	}
868286441Srpaulo
869286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
870286441Srpaulo
871286441Srpaulo	return 0;
872286441Srpaulo
873303628Ssbrunofail:
874303628Ssbruno	iwm_dma_contig_free(dma);
875303628Ssbruno
876286441Srpaulo	return error;
877286441Srpaulo}
878286441Srpaulo
879286441Srpaulostatic void
880286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma)
881286441Srpaulo{
882302102Sadrian	if (dma->vaddr != NULL) {
883302102Sadrian		bus_dmamap_sync(dma->tag, dma->map,
884302102Sadrian		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
885302102Sadrian		bus_dmamap_unload(dma->tag, dma->map);
886302102Sadrian		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
887302102Sadrian		dma->vaddr = NULL;
888286441Srpaulo	}
889286441Srpaulo	if (dma->tag != NULL) {
890286441Srpaulo		bus_dma_tag_destroy(dma->tag);
891286441Srpaulo		dma->tag = NULL;
892286441Srpaulo	}
893286441Srpaulo}
894286441Srpaulo
895286441Srpaulo/* fwmem is used to load firmware onto the card */
896286441Srpaulostatic int
897286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc)
898286441Srpaulo{
899286441Srpaulo	/* Must be aligned on a 16-byte boundary. */
900286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma,
901286441Srpaulo	    sc->sc_fwdmasegsz, 16);
902286441Srpaulo}
903286441Srpaulo
904286441Srpaulo/* tx scheduler rings.  not used? */
905286441Srpaulostatic int
906286441Srpauloiwm_alloc_sched(struct iwm_softc *sc)
907286441Srpaulo{
908286441Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
909303628Ssbruno	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma,
910286441Srpaulo	    nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024);
911286441Srpaulo}
912286441Srpaulo
913286441Srpaulo/* keep-warm page is used internally by the card.  see iwl-fh.h for more info */
914286441Srpaulostatic int
915286441Srpauloiwm_alloc_kw(struct iwm_softc *sc)
916286441Srpaulo{
917286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096);
918286441Srpaulo}
919286441Srpaulo
920286441Srpaulo/* interrupt cause table */
921286441Srpaulostatic int
922286441Srpauloiwm_alloc_ict(struct iwm_softc *sc)
923286441Srpaulo{
924286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma,
925286441Srpaulo	    IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT);
926286441Srpaulo}
927286441Srpaulo
928286441Srpaulostatic int
929286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
930286441Srpaulo{
931286441Srpaulo	bus_size_t size;
932286441Srpaulo	int i, error;
933286441Srpaulo
934286441Srpaulo	ring->cur = 0;
935286441Srpaulo
936286441Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
937286441Srpaulo	size = IWM_RX_RING_COUNT * sizeof(uint32_t);
938286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
939286441Srpaulo	if (error != 0) {
940286441Srpaulo		device_printf(sc->sc_dev,
941286441Srpaulo		    "could not allocate RX ring DMA memory\n");
942286441Srpaulo		goto fail;
943286441Srpaulo	}
944286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
945286441Srpaulo
946286441Srpaulo	/* Allocate RX status area (16-byte aligned). */
947286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma,
948286441Srpaulo	    sizeof(*ring->stat), 16);
949286441Srpaulo	if (error != 0) {
950286441Srpaulo		device_printf(sc->sc_dev,
951286441Srpaulo		    "could not allocate RX status DMA memory\n");
952286441Srpaulo		goto fail;
953286441Srpaulo	}
954286441Srpaulo	ring->stat = ring->stat_dma.vaddr;
955286441Srpaulo
956286441Srpaulo        /* Create RX buffer DMA tag. */
957286441Srpaulo        error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
958286441Srpaulo            BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
959289679Skevlo            IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat);
960286441Srpaulo        if (error != 0) {
961286441Srpaulo                device_printf(sc->sc_dev,
962286441Srpaulo                    "%s: could not create RX buf DMA tag, error %d\n",
963286441Srpaulo                    __func__, error);
964286441Srpaulo                goto fail;
965286441Srpaulo        }
966286441Srpaulo
967301845Sadrian	/* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */
968301845Sadrian	error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map);
969301845Sadrian	if (error != 0) {
970301845Sadrian		device_printf(sc->sc_dev,
971301845Sadrian		    "%s: could not create RX buf DMA map, error %d\n",
972301845Sadrian		    __func__, error);
973301845Sadrian		goto fail;
974301845Sadrian	}
975286441Srpaulo	/*
976286441Srpaulo	 * Allocate and map RX buffers.
977286441Srpaulo	 */
978286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
979301845Sadrian		struct iwm_rx_data *data = &ring->data[i];
980301845Sadrian		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
981301845Sadrian		if (error != 0) {
982301845Sadrian			device_printf(sc->sc_dev,
983301845Sadrian			    "%s: could not create RX buf DMA map, error %d\n",
984301845Sadrian			    __func__, error);
985301845Sadrian			goto fail;
986301845Sadrian		}
987301845Sadrian		data->m = NULL;
988301845Sadrian
989286441Srpaulo		if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) {
990286441Srpaulo			goto fail;
991286441Srpaulo		}
992286441Srpaulo	}
993286441Srpaulo	return 0;
994286441Srpaulo
995286441Srpaulofail:	iwm_free_rx_ring(sc, ring);
996286441Srpaulo	return error;
997286441Srpaulo}
998286441Srpaulo
999286441Srpaulostatic void
1000301191Sadrianiwm_disable_rx_dma(struct iwm_softc *sc)
1001286441Srpaulo{
1002303628Ssbruno	/* XXX conditional nic locks are stupid */
1003286441Srpaulo	/* XXX print out if we can't lock the NIC? */
1004286441Srpaulo	if (iwm_nic_lock(sc)) {
1005286441Srpaulo		/* XXX handle if RX stop doesn't finish? */
1006286441Srpaulo		(void) iwm_pcie_rx_stop(sc);
1007286441Srpaulo		iwm_nic_unlock(sc);
1008286441Srpaulo	}
1009301191Sadrian}
1010301191Sadrian
1011301191Sadrianstatic void
1012301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1013301191Sadrian{
1014287965Sadrian	/* Reset the ring state */
1015286441Srpaulo	ring->cur = 0;
1016303628Ssbruno
1017303628Ssbruno	/*
1018303628Ssbruno	 * The hw rx ring index in shared memory must also be cleared,
1019303628Ssbruno	 * otherwise the discrepancy can cause reprocessing chaos.
1020303628Ssbruno	 */
1021287965Sadrian	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1022286441Srpaulo}
1023286441Srpaulo
1024286441Srpaulostatic void
1025286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
1026286441Srpaulo{
1027286441Srpaulo	int i;
1028286441Srpaulo
1029286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1030286441Srpaulo	iwm_dma_contig_free(&ring->stat_dma);
1031286441Srpaulo
1032286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
1033286441Srpaulo		struct iwm_rx_data *data = &ring->data[i];
1034286441Srpaulo
1035286441Srpaulo		if (data->m != NULL) {
1036286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1037286441Srpaulo			    BUS_DMASYNC_POSTREAD);
1038286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1039286441Srpaulo			m_freem(data->m);
1040286441Srpaulo			data->m = NULL;
1041286441Srpaulo		}
1042286441Srpaulo		if (data->map != NULL) {
1043286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1044286441Srpaulo			data->map = NULL;
1045286441Srpaulo		}
1046286441Srpaulo	}
1047301845Sadrian	if (ring->spare_map != NULL) {
1048301845Sadrian		bus_dmamap_destroy(ring->data_dmat, ring->spare_map);
1049301845Sadrian		ring->spare_map = NULL;
1050301845Sadrian	}
1051286441Srpaulo	if (ring->data_dmat != NULL) {
1052286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1053286441Srpaulo		ring->data_dmat = NULL;
1054286441Srpaulo	}
1055286441Srpaulo}
1056286441Srpaulo
1057286441Srpaulostatic int
1058286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
1059286441Srpaulo{
1060286441Srpaulo	bus_addr_t paddr;
1061286441Srpaulo	bus_size_t size;
1062302104Sadrian	size_t maxsize;
1063302104Sadrian	int nsegments;
1064286441Srpaulo	int i, error;
1065286441Srpaulo
1066286441Srpaulo	ring->qid = qid;
1067286441Srpaulo	ring->queued = 0;
1068286441Srpaulo	ring->cur = 0;
1069286441Srpaulo
1070286441Srpaulo	/* Allocate TX descriptors (256-byte aligned). */
1071286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd);
1072286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
1073286441Srpaulo	if (error != 0) {
1074286441Srpaulo		device_printf(sc->sc_dev,
1075286441Srpaulo		    "could not allocate TX ring DMA memory\n");
1076286441Srpaulo		goto fail;
1077286441Srpaulo	}
1078286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
1079286441Srpaulo
1080286441Srpaulo	/*
1081286441Srpaulo	 * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need
1082286441Srpaulo	 * to allocate commands space for other rings.
1083286441Srpaulo	 */
1084286441Srpaulo	if (qid > IWM_MVM_CMD_QUEUE)
1085286441Srpaulo		return 0;
1086286441Srpaulo
1087286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
1088286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4);
1089286441Srpaulo	if (error != 0) {
1090286441Srpaulo		device_printf(sc->sc_dev,
1091286441Srpaulo		    "could not allocate TX cmd DMA memory\n");
1092286441Srpaulo		goto fail;
1093286441Srpaulo	}
1094286441Srpaulo	ring->cmd = ring->cmd_dma.vaddr;
1095286441Srpaulo
1096302104Sadrian	/* FW commands may require more mapped space than packets. */
1097302104Sadrian	if (qid == IWM_MVM_CMD_QUEUE) {
1098302104Sadrian		maxsize = IWM_RBUF_SIZE;
1099302104Sadrian		nsegments = 1;
1100302104Sadrian	} else {
1101302104Sadrian		maxsize = MCLBYTES;
1102302104Sadrian		nsegments = IWM_MAX_SCATTER - 2;
1103302104Sadrian	}
1104302104Sadrian
1105286441Srpaulo	error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
1106302104Sadrian	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize,
1107302104Sadrian            nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat);
1108286441Srpaulo	if (error != 0) {
1109286441Srpaulo		device_printf(sc->sc_dev, "could not create TX buf DMA tag\n");
1110286441Srpaulo		goto fail;
1111286441Srpaulo	}
1112286441Srpaulo
1113286441Srpaulo	paddr = ring->cmd_dma.paddr;
1114286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1115286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1116286441Srpaulo
1117286441Srpaulo		data->cmd_paddr = paddr;
1118286441Srpaulo		data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header)
1119286441Srpaulo		    + offsetof(struct iwm_tx_cmd, scratch);
1120286441Srpaulo		paddr += sizeof(struct iwm_device_cmd);
1121286441Srpaulo
1122286441Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1123286441Srpaulo		if (error != 0) {
1124286441Srpaulo			device_printf(sc->sc_dev,
1125286441Srpaulo			    "could not create TX buf DMA map\n");
1126286441Srpaulo			goto fail;
1127286441Srpaulo		}
1128286441Srpaulo	}
1129286441Srpaulo	KASSERT(paddr == ring->cmd_dma.paddr + size,
1130286441Srpaulo	    ("invalid physical address"));
1131286441Srpaulo	return 0;
1132286441Srpaulo
1133286441Srpaulofail:	iwm_free_tx_ring(sc, ring);
1134286441Srpaulo	return error;
1135286441Srpaulo}
1136286441Srpaulo
1137286441Srpaulostatic void
1138286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1139286441Srpaulo{
1140286441Srpaulo	int i;
1141286441Srpaulo
1142286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1143286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1144286441Srpaulo
1145286441Srpaulo		if (data->m != NULL) {
1146286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1147286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1148286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1149286441Srpaulo			m_freem(data->m);
1150286441Srpaulo			data->m = NULL;
1151286441Srpaulo		}
1152286441Srpaulo	}
1153286441Srpaulo	/* Clear TX descriptors. */
1154286441Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1155286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1156286441Srpaulo	    BUS_DMASYNC_PREWRITE);
1157286441Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1158286441Srpaulo	ring->queued = 0;
1159286441Srpaulo	ring->cur = 0;
1160286441Srpaulo}
1161286441Srpaulo
1162286441Srpaulostatic void
1163286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1164286441Srpaulo{
1165286441Srpaulo	int i;
1166286441Srpaulo
1167286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1168286441Srpaulo	iwm_dma_contig_free(&ring->cmd_dma);
1169286441Srpaulo
1170286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1171286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1172286441Srpaulo
1173286441Srpaulo		if (data->m != NULL) {
1174286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1175286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1176286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1177286441Srpaulo			m_freem(data->m);
1178286441Srpaulo			data->m = NULL;
1179286441Srpaulo		}
1180286441Srpaulo		if (data->map != NULL) {
1181286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1182286441Srpaulo			data->map = NULL;
1183286441Srpaulo		}
1184286441Srpaulo	}
1185286441Srpaulo	if (ring->data_dmat != NULL) {
1186286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1187286441Srpaulo		ring->data_dmat = NULL;
1188286441Srpaulo	}
1189286441Srpaulo}
1190286441Srpaulo
1191286441Srpaulo/*
1192286441Srpaulo * High-level hardware frobbing routines
1193286441Srpaulo */
1194286441Srpaulo
1195286441Srpaulostatic void
1196286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc)
1197286441Srpaulo{
1198286441Srpaulo	sc->sc_intmask = IWM_CSR_INI_SET_MASK;
1199286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1200286441Srpaulo}
1201286441Srpaulo
1202286441Srpaulostatic void
1203286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc)
1204286441Srpaulo{
1205286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1206286441Srpaulo}
1207286441Srpaulo
1208286441Srpaulostatic void
1209286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc)
1210286441Srpaulo{
1211286441Srpaulo	/* disable interrupts */
1212286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
1213286441Srpaulo
1214286441Srpaulo	/* acknowledge all interrupts */
1215286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1216286441Srpaulo	IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
1217286441Srpaulo}
1218286441Srpaulo
1219286441Srpaulostatic void
1220286441Srpauloiwm_ict_reset(struct iwm_softc *sc)
1221286441Srpaulo{
1222286441Srpaulo	iwm_disable_interrupts(sc);
1223286441Srpaulo
1224286441Srpaulo	/* Reset ICT table. */
1225286441Srpaulo	memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE);
1226286441Srpaulo	sc->ict_cur = 0;
1227286441Srpaulo
1228286441Srpaulo	/* Set physical address of ICT table (4KB aligned). */
1229286441Srpaulo	IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG,
1230286441Srpaulo	    IWM_CSR_DRAM_INT_TBL_ENABLE
1231303628Ssbruno	    | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER
1232286441Srpaulo	    | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK
1233286441Srpaulo	    | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT);
1234286441Srpaulo
1235286441Srpaulo	/* Switch to ICT interrupt mode in driver. */
1236286441Srpaulo	sc->sc_flags |= IWM_FLAG_USE_ICT;
1237286441Srpaulo
1238286441Srpaulo	/* Re-enable interrupts. */
1239286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1240286441Srpaulo	iwm_enable_interrupts(sc);
1241286441Srpaulo}
1242286441Srpaulo
1243286441Srpaulo/* iwlwifi pcie/trans.c */
1244286441Srpaulo
1245286441Srpaulo/*
1246286441Srpaulo * Since this .. hard-resets things, it's time to actually
1247286441Srpaulo * mark the first vap (if any) as having no mac context.
1248286441Srpaulo * It's annoying, but since the driver is potentially being
1249286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we
1250286441Srpaulo * have to correctly track this.
1251286441Srpaulo */
1252286441Srpaulostatic void
1253286441Srpauloiwm_stop_device(struct iwm_softc *sc)
1254286441Srpaulo{
1255287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1256286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1257303628Ssbruno	int chnl, qid;
1258303628Ssbruno	uint32_t mask = 0;
1259286441Srpaulo
1260286441Srpaulo	/* tell the device to stop sending interrupts */
1261286441Srpaulo	iwm_disable_interrupts(sc);
1262286441Srpaulo
1263286441Srpaulo	/*
1264286441Srpaulo	 * FreeBSD-local: mark the first vap as not-uploaded,
1265286441Srpaulo	 * so the next transition through auth/assoc
1266286441Srpaulo	 * will correctly populate the MAC context.
1267286441Srpaulo	 */
1268286441Srpaulo	if (vap) {
1269286441Srpaulo		struct iwm_vap *iv = IWM_VAP(vap);
1270286441Srpaulo		iv->is_uploaded = 0;
1271286441Srpaulo	}
1272286441Srpaulo
1273286441Srpaulo	/* device going down, Stop using ICT table */
1274286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_USE_ICT;
1275286441Srpaulo
1276286441Srpaulo	/* stop tx and rx.  tx and rx bits, as usual, are from if_iwn */
1277286441Srpaulo
1278286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1279286441Srpaulo
1280286441Srpaulo	if (iwm_nic_lock(sc)) {
1281303628Ssbruno		/* Stop each Tx DMA channel */
1282286441Srpaulo		for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1283286441Srpaulo			IWM_WRITE(sc,
1284286441Srpaulo			    IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0);
1285303628Ssbruno			mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl);
1286303628Ssbruno		}
1287286441Srpaulo
1288303628Ssbruno		/* Wait for DMA channels to be idle */
1289303628Ssbruno		if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask,
1290303628Ssbruno		    5000)) {
1291303628Ssbruno			device_printf(sc->sc_dev,
1292303628Ssbruno			    "Failing on timeout while stopping DMA channel: [0x%08x]\n",
1293303628Ssbruno			    IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG));
1294286441Srpaulo		}
1295286441Srpaulo		iwm_nic_unlock(sc);
1296286441Srpaulo	}
1297301191Sadrian	iwm_disable_rx_dma(sc);
1298286441Srpaulo
1299286441Srpaulo	/* Stop RX ring. */
1300286441Srpaulo	iwm_reset_rx_ring(sc, &sc->rxq);
1301286441Srpaulo
1302286441Srpaulo	/* Reset all TX rings. */
1303286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++)
1304286441Srpaulo		iwm_reset_tx_ring(sc, &sc->txq[qid]);
1305286441Srpaulo
1306286441Srpaulo	/*
1307286441Srpaulo	 * Power-down device's busmaster DMA clocks
1308286441Srpaulo	 */
1309286441Srpaulo	iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT);
1310286441Srpaulo	DELAY(5);
1311286441Srpaulo
1312286441Srpaulo	/* Make sure (redundant) we've released our request to stay awake */
1313286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
1314286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1315286441Srpaulo
1316286441Srpaulo	/* Stop the device, and put it in low power state */
1317286441Srpaulo	iwm_apm_stop(sc);
1318286441Srpaulo
1319286441Srpaulo	/* Upon stop, the APM issues an interrupt if HW RF kill is set.
1320286441Srpaulo	 * Clean again the interrupt here
1321286441Srpaulo	 */
1322286441Srpaulo	iwm_disable_interrupts(sc);
1323286441Srpaulo	/* stop and reset the on-board processor */
1324303628Ssbruno	IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET);
1325286441Srpaulo
1326286441Srpaulo	/*
1327286441Srpaulo	 * Even if we stop the HW, we still want the RF kill
1328286441Srpaulo	 * interrupt
1329286441Srpaulo	 */
1330286441Srpaulo	iwm_enable_rfkill_int(sc);
1331286441Srpaulo	iwm_check_rfkill(sc);
1332286441Srpaulo}
1333286441Srpaulo
1334286441Srpaulo/* iwlwifi: mvm/ops.c */
1335286441Srpaulostatic void
1336286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc)
1337286441Srpaulo{
1338286441Srpaulo	uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash;
1339286441Srpaulo	uint32_t reg_val = 0;
1340286441Srpaulo
1341286441Srpaulo	radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >>
1342286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_TYPE_POS;
1343286441Srpaulo	radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >>
1344286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_STEP_POS;
1345286441Srpaulo	radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >>
1346286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_DASH_POS;
1347286441Srpaulo
1348286441Srpaulo	/* SKU control */
1349286441Srpaulo	reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) <<
1350286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
1351286441Srpaulo	reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) <<
1352286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
1353286441Srpaulo
1354286441Srpaulo	/* radio configuration */
1355286441Srpaulo	reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
1356286441Srpaulo	reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
1357286441Srpaulo	reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
1358286441Srpaulo
1359286441Srpaulo	IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val);
1360286441Srpaulo
1361286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1362286441Srpaulo	    "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
1363286441Srpaulo	    radio_cfg_step, radio_cfg_dash);
1364286441Srpaulo
1365286441Srpaulo	/*
1366286441Srpaulo	 * W/A : NIC is stuck in a reset state after Early PCIe power off
1367286441Srpaulo	 * (PCIe power is lost before PERST# is asserted), causing ME FW
1368286441Srpaulo	 * to lose ownership and not being able to obtain it back.
1369286441Srpaulo	 */
1370303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) {
1371303628Ssbruno		iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG,
1372303628Ssbruno		    IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
1373303628Ssbruno		    ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
1374303628Ssbruno	}
1375286441Srpaulo}
1376286441Srpaulo
1377286441Srpaulostatic int
1378286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc)
1379286441Srpaulo{
1380286441Srpaulo	if (!iwm_nic_lock(sc))
1381286441Srpaulo		return EBUSY;
1382286441Srpaulo
1383286441Srpaulo	/*
1384286441Srpaulo	 * Initialize RX ring.  This is from the iwn driver.
1385286441Srpaulo	 */
1386286441Srpaulo	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1387286441Srpaulo
1388286441Srpaulo	/* stop DMA */
1389301191Sadrian	iwm_disable_rx_dma(sc);
1390286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
1391286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
1392286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0);
1393286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
1394286441Srpaulo
1395286441Srpaulo	/* Set physical address of RX ring (256-byte aligned). */
1396286441Srpaulo	IWM_WRITE(sc,
1397286441Srpaulo	    IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8);
1398286441Srpaulo
1399286441Srpaulo	/* Set physical address of RX status (16-byte aligned). */
1400286441Srpaulo	IWM_WRITE(sc,
1401286441Srpaulo	    IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4);
1402286441Srpaulo
1403286441Srpaulo	/* Enable RX. */
1404286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG,
1405286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL		|
1406286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY		|  /* HW bug */
1407286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL	|
1408303628Ssbruno	    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK	|
1409303628Ssbruno	    (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
1410286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K		|
1411286441Srpaulo	    IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
1412286441Srpaulo
1413286441Srpaulo	IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF);
1414286441Srpaulo
1415286441Srpaulo	/* W/A for interrupt coalescing bug in 7260 and 3160 */
1416286441Srpaulo	if (sc->host_interrupt_operation_mode)
1417286441Srpaulo		IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE);
1418286441Srpaulo
1419286441Srpaulo	/*
1420286441Srpaulo	 * Thus sayeth el jefe (iwlwifi) via a comment:
1421286441Srpaulo	 *
1422286441Srpaulo	 * This value should initially be 0 (before preparing any
1423303628Ssbruno	 * RBs), should be 8 after preparing the first 8 RBs (for example)
1424286441Srpaulo	 */
1425286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8);
1426286441Srpaulo
1427286441Srpaulo	iwm_nic_unlock(sc);
1428286441Srpaulo
1429286441Srpaulo	return 0;
1430286441Srpaulo}
1431286441Srpaulo
1432286441Srpaulostatic int
1433286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc)
1434286441Srpaulo{
1435286441Srpaulo	int qid;
1436286441Srpaulo
1437286441Srpaulo	if (!iwm_nic_lock(sc))
1438286441Srpaulo		return EBUSY;
1439286441Srpaulo
1440286441Srpaulo	/* Deactivate TX scheduler. */
1441286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1442286441Srpaulo
1443286441Srpaulo	/* Set physical address of "keep warm" page (16-byte aligned). */
1444286441Srpaulo	IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4);
1445286441Srpaulo
1446286441Srpaulo	/* Initialize TX rings. */
1447286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++) {
1448286441Srpaulo		struct iwm_tx_ring *txq = &sc->txq[qid];
1449286441Srpaulo
1450286441Srpaulo		/* Set physical address of TX ring (256-byte aligned). */
1451286441Srpaulo		IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid),
1452286441Srpaulo		    txq->desc_dma.paddr >> 8);
1453286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
1454286441Srpaulo		    "%s: loading ring %d descriptors (%p) at %lx\n",
1455286441Srpaulo		    __func__,
1456286441Srpaulo		    qid, txq->desc,
1457286441Srpaulo		    (unsigned long) (txq->desc_dma.paddr >> 8));
1458286441Srpaulo	}
1459303628Ssbruno
1460303628Ssbruno	iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE);
1461303628Ssbruno
1462286441Srpaulo	iwm_nic_unlock(sc);
1463286441Srpaulo
1464286441Srpaulo	return 0;
1465286441Srpaulo}
1466286441Srpaulo
1467286441Srpaulostatic int
1468286441Srpauloiwm_nic_init(struct iwm_softc *sc)
1469286441Srpaulo{
1470286441Srpaulo	int error;
1471286441Srpaulo
1472286441Srpaulo	iwm_apm_init(sc);
1473303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000)
1474303628Ssbruno		iwm_set_pwr(sc);
1475286441Srpaulo
1476286441Srpaulo	iwm_mvm_nic_config(sc);
1477286441Srpaulo
1478286441Srpaulo	if ((error = iwm_nic_rx_init(sc)) != 0)
1479286441Srpaulo		return error;
1480286441Srpaulo
1481286441Srpaulo	/*
1482286441Srpaulo	 * Ditto for TX, from iwn
1483286441Srpaulo	 */
1484286441Srpaulo	if ((error = iwm_nic_tx_init(sc)) != 0)
1485286441Srpaulo		return error;
1486286441Srpaulo
1487286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1488286441Srpaulo	    "%s: shadow registers enabled\n", __func__);
1489286441Srpaulo	IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff);
1490286441Srpaulo
1491286441Srpaulo	return 0;
1492286441Srpaulo}
1493286441Srpaulo
1494286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = {
1495286441Srpaulo	IWM_MVM_TX_FIFO_VO,
1496286441Srpaulo	IWM_MVM_TX_FIFO_VI,
1497286441Srpaulo	IWM_MVM_TX_FIFO_BE,
1498286441Srpaulo	IWM_MVM_TX_FIFO_BK,
1499286441Srpaulo};
1500286441Srpaulo
1501303628Ssbrunostatic int
1502303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
1503286441Srpaulo{
1504286441Srpaulo	if (!iwm_nic_lock(sc)) {
1505286441Srpaulo		device_printf(sc->sc_dev,
1506286441Srpaulo		    "%s: cannot enable txq %d\n",
1507286441Srpaulo		    __func__,
1508286441Srpaulo		    qid);
1509303628Ssbruno		return EBUSY;
1510286441Srpaulo	}
1511286441Srpaulo
1512303628Ssbruno	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
1513286441Srpaulo
1514303628Ssbruno	if (qid == IWM_MVM_CMD_QUEUE) {
1515303628Ssbruno		/* unactivate before configuration */
1516303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1517303628Ssbruno		    (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE)
1518303628Ssbruno		    | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
1519286441Srpaulo
1520303628Ssbruno		iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid));
1521286441Srpaulo
1522303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0);
1523286441Srpaulo
1524303628Ssbruno		iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0);
1525303628Ssbruno		/* Set scheduler window size and frame limit. */
1526303628Ssbruno		iwm_write_mem32(sc,
1527303628Ssbruno		    sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) +
1528303628Ssbruno		    sizeof(uint32_t),
1529303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
1530303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
1531303628Ssbruno		    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
1532303628Ssbruno		    IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
1533286441Srpaulo
1534303628Ssbruno		iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1535303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
1536303628Ssbruno		    (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) |
1537303628Ssbruno		    (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) |
1538303628Ssbruno		    IWM_SCD_QUEUE_STTS_REG_MSK);
1539303628Ssbruno	} else {
1540303628Ssbruno		struct iwm_scd_txq_cfg_cmd cmd;
1541303628Ssbruno		int error;
1542286441Srpaulo
1543303628Ssbruno		iwm_nic_unlock(sc);
1544303628Ssbruno
1545303628Ssbruno		memset(&cmd, 0, sizeof(cmd));
1546303628Ssbruno		cmd.scd_queue = qid;
1547303628Ssbruno		cmd.enable = 1;
1548303628Ssbruno		cmd.sta_id = sta_id;
1549303628Ssbruno		cmd.tx_fifo = fifo;
1550303628Ssbruno		cmd.aggregate = 0;
1551303628Ssbruno		cmd.window = IWM_FRAME_LIMIT;
1552303628Ssbruno
1553303628Ssbruno		error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC,
1554303628Ssbruno		    sizeof(cmd), &cmd);
1555303628Ssbruno		if (error) {
1556303628Ssbruno			device_printf(sc->sc_dev,
1557303628Ssbruno			    "cannot enable txq %d\n", qid);
1558303628Ssbruno			return error;
1559303628Ssbruno		}
1560303628Ssbruno
1561303628Ssbruno		if (!iwm_nic_lock(sc))
1562303628Ssbruno			return EBUSY;
1563303628Ssbruno	}
1564303628Ssbruno
1565303628Ssbruno	iwm_write_prph(sc, IWM_SCD_EN_CTRL,
1566303628Ssbruno	    iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid);
1567303628Ssbruno
1568286441Srpaulo	iwm_nic_unlock(sc);
1569286441Srpaulo
1570303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n",
1571286441Srpaulo	    __func__, qid, fifo);
1572303628Ssbruno
1573303628Ssbruno	return 0;
1574286441Srpaulo}
1575286441Srpaulo
1576286441Srpaulostatic int
1577286441Srpauloiwm_post_alive(struct iwm_softc *sc)
1578286441Srpaulo{
1579286441Srpaulo	int nwords;
1580286441Srpaulo	int error, chnl;
1581303628Ssbruno	uint32_t base;
1582286441Srpaulo
1583286441Srpaulo	if (!iwm_nic_lock(sc))
1584286441Srpaulo		return EBUSY;
1585286441Srpaulo
1586303628Ssbruno	base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR);
1587303628Ssbruno	if (sc->sched_base != base) {
1588286441Srpaulo		device_printf(sc->sc_dev,
1589303628Ssbruno		    "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n",
1590303628Ssbruno		    __func__, sc->sched_base, base);
1591286441Srpaulo	}
1592286441Srpaulo
1593286441Srpaulo	iwm_ict_reset(sc);
1594286441Srpaulo
1595286441Srpaulo	/* Clear TX scheduler state in SRAM. */
1596286441Srpaulo	nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND -
1597286441Srpaulo	    IWM_SCD_CONTEXT_MEM_LOWER_BOUND)
1598286441Srpaulo	    / sizeof(uint32_t);
1599286441Srpaulo	error = iwm_write_mem(sc,
1600286441Srpaulo	    sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND,
1601286441Srpaulo	    NULL, nwords);
1602286441Srpaulo	if (error)
1603286441Srpaulo		goto out;
1604286441Srpaulo
1605286441Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned). */
1606286441Srpaulo	iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10);
1607286441Srpaulo
1608286441Srpaulo	iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0);
1609286441Srpaulo
1610303628Ssbruno	iwm_nic_unlock(sc);
1611303628Ssbruno
1612286441Srpaulo	/* enable command channel */
1613303628Ssbruno	error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7);
1614303628Ssbruno	if (error)
1615303628Ssbruno		return error;
1616286441Srpaulo
1617303628Ssbruno	if (!iwm_nic_lock(sc))
1618303628Ssbruno		return EBUSY;
1619303628Ssbruno
1620286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff);
1621286441Srpaulo
1622286441Srpaulo	/* Enable DMA channels. */
1623286441Srpaulo	for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1624286441Srpaulo		IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl),
1625286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
1626286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
1627286441Srpaulo	}
1628286441Srpaulo
1629286441Srpaulo	IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG,
1630286441Srpaulo	    IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
1631286441Srpaulo
1632286441Srpaulo	/* Enable L1-Active */
1633303628Ssbruno	if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) {
1634303628Ssbruno		iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG,
1635303628Ssbruno		    IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
1636303628Ssbruno	}
1637286441Srpaulo
1638286441Srpaulo out:
1639303628Ssbruno	iwm_nic_unlock(sc);
1640286441Srpaulo	return error;
1641286441Srpaulo}
1642286441Srpaulo
1643286441Srpaulo/*
1644286441Srpaulo * NVM read access and content parsing.  We do not support
1645286441Srpaulo * external NVM or writing NVM.
1646286441Srpaulo * iwlwifi/mvm/nvm.c
1647286441Srpaulo */
1648286441Srpaulo
1649286441Srpaulo/* list of NVM sections we are allowed/need to read */
1650286441Srpauloconst int nvm_to_read[] = {
1651286441Srpaulo	IWM_NVM_SECTION_TYPE_HW,
1652286441Srpaulo	IWM_NVM_SECTION_TYPE_SW,
1653303628Ssbruno	IWM_NVM_SECTION_TYPE_REGULATORY,
1654286441Srpaulo	IWM_NVM_SECTION_TYPE_CALIBRATION,
1655286441Srpaulo	IWM_NVM_SECTION_TYPE_PRODUCTION,
1656303628Ssbruno	IWM_NVM_SECTION_TYPE_HW_8000,
1657303628Ssbruno	IWM_NVM_SECTION_TYPE_MAC_OVERRIDE,
1658303628Ssbruno	IWM_NVM_SECTION_TYPE_PHY_SKU,
1659286441Srpaulo};
1660286441Srpaulo
1661286441Srpaulo/* Default NVM size to read */
1662303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE	(2*1024)
1663303628Ssbruno#define IWM_MAX_NVM_SECTION_SIZE	8192
1664286441Srpaulo
1665286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1
1666286441Srpaulo#define IWM_NVM_READ_OPCODE 0
1667286441Srpaulo
1668303628Ssbruno/* load nvm chunk response */
1669303628Ssbruno#define IWM_READ_NVM_CHUNK_SUCCEED		0
1670303628Ssbruno#define IWM_READ_NVM_CHUNK_INVALID_ADDRESS	1
1671303628Ssbruno
1672286441Srpaulostatic int
1673286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section,
1674286441Srpaulo	uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len)
1675286441Srpaulo{
1676286441Srpaulo	offset = 0;
1677286441Srpaulo	struct iwm_nvm_access_cmd nvm_access_cmd = {
1678286441Srpaulo		.offset = htole16(offset),
1679286441Srpaulo		.length = htole16(length),
1680286441Srpaulo		.type = htole16(section),
1681286441Srpaulo		.op_code = IWM_NVM_READ_OPCODE,
1682286441Srpaulo	};
1683286441Srpaulo	struct iwm_nvm_access_resp *nvm_resp;
1684286441Srpaulo	struct iwm_rx_packet *pkt;
1685286441Srpaulo	struct iwm_host_cmd cmd = {
1686286441Srpaulo		.id = IWM_NVM_ACCESS_CMD,
1687286441Srpaulo		.flags = IWM_CMD_SYNC | IWM_CMD_WANT_SKB |
1688286441Srpaulo		    IWM_CMD_SEND_IN_RFKILL,
1689286441Srpaulo		.data = { &nvm_access_cmd, },
1690286441Srpaulo	};
1691303628Ssbruno	int ret, offset_read;
1692303628Ssbruno	size_t bytes_read;
1693286441Srpaulo	uint8_t *resp_data;
1694286441Srpaulo
1695286441Srpaulo	cmd.len[0] = sizeof(struct iwm_nvm_access_cmd);
1696286441Srpaulo
1697286441Srpaulo	ret = iwm_send_cmd(sc, &cmd);
1698303628Ssbruno	if (ret) {
1699303628Ssbruno		device_printf(sc->sc_dev,
1700303628Ssbruno		    "Could not send NVM_ACCESS command (error=%d)\n", ret);
1701286441Srpaulo		return ret;
1702303628Ssbruno	}
1703286441Srpaulo
1704286441Srpaulo	pkt = cmd.resp_pkt;
1705286441Srpaulo	if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) {
1706286441Srpaulo		device_printf(sc->sc_dev,
1707303628Ssbruno		    "Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n",
1708303628Ssbruno		    pkt->hdr.flags);
1709286441Srpaulo		ret = EIO;
1710286441Srpaulo		goto exit;
1711286441Srpaulo	}
1712286441Srpaulo
1713286441Srpaulo	/* Extract NVM response */
1714286441Srpaulo	nvm_resp = (void *)pkt->data;
1715286441Srpaulo
1716286441Srpaulo	ret = le16toh(nvm_resp->status);
1717286441Srpaulo	bytes_read = le16toh(nvm_resp->length);
1718286441Srpaulo	offset_read = le16toh(nvm_resp->offset);
1719286441Srpaulo	resp_data = nvm_resp->data;
1720286441Srpaulo	if (ret) {
1721303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1722303628Ssbruno		    "NVM access command failed with status %d\n", ret);
1723286441Srpaulo		ret = EINVAL;
1724286441Srpaulo		goto exit;
1725286441Srpaulo	}
1726286441Srpaulo
1727286441Srpaulo	if (offset_read != offset) {
1728286441Srpaulo		device_printf(sc->sc_dev,
1729303628Ssbruno		    "NVM ACCESS response with invalid offset %d\n",
1730303628Ssbruno		    offset_read);
1731286441Srpaulo		ret = EINVAL;
1732286441Srpaulo		goto exit;
1733286441Srpaulo	}
1734286441Srpaulo
1735303628Ssbruno	if (bytes_read > length) {
1736303628Ssbruno		device_printf(sc->sc_dev,
1737303628Ssbruno		    "NVM ACCESS response with too much data "
1738303628Ssbruno		    "(%d bytes requested, %zd bytes received)\n",
1739303628Ssbruno		    length, bytes_read);
1740303628Ssbruno		ret = EINVAL;
1741303628Ssbruno		goto exit;
1742303628Ssbruno	}
1743303628Ssbruno
1744286441Srpaulo	memcpy(data + offset, resp_data, bytes_read);
1745286441Srpaulo	*len = bytes_read;
1746286441Srpaulo
1747286441Srpaulo exit:
1748286441Srpaulo	iwm_free_resp(sc, &cmd);
1749286441Srpaulo	return ret;
1750286441Srpaulo}
1751286441Srpaulo
1752286441Srpaulo/*
1753286441Srpaulo * Reads an NVM section completely.
1754303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read
1755286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
1756286441Srpaulo * by uCode, we need to manually check in this case that we don't
1757286441Srpaulo * overflow and try to read more than the EEPROM size.
1758286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and
1759286441Srpaulo * the uCode fills the response with as much data as we can,
1760286441Srpaulo * without overflowing, so no check is needed.
1761286441Srpaulo */
1762286441Srpaulostatic int
1763286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc,
1764303628Ssbruno	uint16_t section, uint8_t *data, uint16_t *len, size_t max_len)
1765286441Srpaulo{
1766303628Ssbruno	uint16_t chunklen, seglen;
1767303628Ssbruno	int error = 0;
1768286441Srpaulo
1769303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1770303628Ssbruno	    "reading NVM section %d\n", section);
1771303628Ssbruno
1772303628Ssbruno	chunklen = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE;
1773286441Srpaulo	*len = 0;
1774286441Srpaulo
1775303628Ssbruno	/* Read NVM chunks until exhausted (reading less than requested) */
1776303628Ssbruno	while (seglen == chunklen && *len < max_len) {
1777286441Srpaulo		error = iwm_nvm_read_chunk(sc,
1778303628Ssbruno		    section, *len, chunklen, data, &seglen);
1779286441Srpaulo		if (error) {
1780303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1781303628Ssbruno			    "Cannot read from NVM section "
1782303628Ssbruno			    "%d at offset %d\n", section, *len);
1783286441Srpaulo			return error;
1784286441Srpaulo		}
1785286441Srpaulo		*len += seglen;
1786286441Srpaulo	}
1787286441Srpaulo
1788286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1789303628Ssbruno	    "NVM section %d read completed (%d bytes, error=%d)\n",
1790303628Ssbruno	    section, *len, error);
1791303628Ssbruno	return error;
1792286441Srpaulo}
1793286441Srpaulo
1794286441Srpaulo/*
1795286441Srpaulo * BEGIN IWM_NVM_PARSE
1796286441Srpaulo */
1797286441Srpaulo
1798286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */
1799286441Srpaulo
1800286441Srpaulo/* NVM offsets (in words) definitions */
1801303628Ssbrunoenum iwm_nvm_offsets {
1802286441Srpaulo	/* NVM HW-Section offset (in words) definitions */
1803286441Srpaulo	IWM_HW_ADDR = 0x15,
1804286441Srpaulo
1805286441Srpaulo/* NVM SW-Section offset (in words) definitions */
1806286441Srpaulo	IWM_NVM_SW_SECTION = 0x1C0,
1807286441Srpaulo	IWM_NVM_VERSION = 0,
1808286441Srpaulo	IWM_RADIO_CFG = 1,
1809286441Srpaulo	IWM_SKU = 2,
1810286441Srpaulo	IWM_N_HW_ADDRS = 3,
1811286441Srpaulo	IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION,
1812286441Srpaulo
1813286441Srpaulo/* NVM calibration section offset (in words) definitions */
1814286441Srpaulo	IWM_NVM_CALIB_SECTION = 0x2B8,
1815286441Srpaulo	IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION
1816286441Srpaulo};
1817286441Srpaulo
1818303628Ssbrunoenum iwm_8000_nvm_offsets {
1819303628Ssbruno	/* NVM HW-Section offset (in words) definitions */
1820303628Ssbruno	IWM_HW_ADDR0_WFPM_8000 = 0x12,
1821303628Ssbruno	IWM_HW_ADDR1_WFPM_8000 = 0x16,
1822303628Ssbruno	IWM_HW_ADDR0_PCIE_8000 = 0x8A,
1823303628Ssbruno	IWM_HW_ADDR1_PCIE_8000 = 0x8E,
1824303628Ssbruno	IWM_MAC_ADDRESS_OVERRIDE_8000 = 1,
1825303628Ssbruno
1826303628Ssbruno	/* NVM SW-Section offset (in words) definitions */
1827303628Ssbruno	IWM_NVM_SW_SECTION_8000 = 0x1C0,
1828303628Ssbruno	IWM_NVM_VERSION_8000 = 0,
1829303628Ssbruno	IWM_RADIO_CFG_8000 = 0,
1830303628Ssbruno	IWM_SKU_8000 = 2,
1831303628Ssbruno	IWM_N_HW_ADDRS_8000 = 3,
1832303628Ssbruno
1833303628Ssbruno	/* NVM REGULATORY -Section offset (in words) definitions */
1834303628Ssbruno	IWM_NVM_CHANNELS_8000 = 0,
1835303628Ssbruno	IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7,
1836303628Ssbruno	IWM_NVM_LAR_OFFSET_8000 = 0x507,
1837303628Ssbruno	IWM_NVM_LAR_ENABLED_8000 = 0x7,
1838303628Ssbruno
1839303628Ssbruno	/* NVM calibration section offset (in words) definitions */
1840303628Ssbruno	IWM_NVM_CALIB_SECTION_8000 = 0x2B8,
1841303628Ssbruno	IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000
1842303628Ssbruno};
1843303628Ssbruno
1844286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */
1845286441Srpauloenum nvm_sku_bits {
1846286441Srpaulo	IWM_NVM_SKU_CAP_BAND_24GHZ	= (1 << 0),
1847286441Srpaulo	IWM_NVM_SKU_CAP_BAND_52GHZ	= (1 << 1),
1848286441Srpaulo	IWM_NVM_SKU_CAP_11N_ENABLE	= (1 << 2),
1849286441Srpaulo	IWM_NVM_SKU_CAP_11AC_ENABLE	= (1 << 3),
1850286441Srpaulo};
1851286441Srpaulo
1852286441Srpaulo/* radio config bits (actual values from NVM definition) */
1853286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
1854286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
1855286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
1856286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
1857286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
1858286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
1859286441Srpaulo
1860303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x)	(x & 0xF)
1861303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x)		((x >> 4) & 0xF)
1862303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x)		((x >> 8) & 0xF)
1863303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x)		((x >> 12) & 0xFFF)
1864303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x)	((x >> 24) & 0xF)
1865303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x)	((x >> 28) & 0xF)
1866303628Ssbruno
1867286441Srpaulo#define DEFAULT_MAX_TX_POWER 16
1868286441Srpaulo
1869286441Srpaulo/**
1870286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM
1871286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo
1872286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel
1873286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed
1874286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required
1875298877Savos * XXX cannot find this (DFS) flag in iwl-nvm-parse.c
1876286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate
1877286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
1878286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
1879286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
1880286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
1881286441Srpaulo */
1882286441Srpauloenum iwm_nvm_channel_flags {
1883286441Srpaulo	IWM_NVM_CHANNEL_VALID = (1 << 0),
1884286441Srpaulo	IWM_NVM_CHANNEL_IBSS = (1 << 1),
1885286441Srpaulo	IWM_NVM_CHANNEL_ACTIVE = (1 << 3),
1886286441Srpaulo	IWM_NVM_CHANNEL_RADAR = (1 << 4),
1887286441Srpaulo	IWM_NVM_CHANNEL_DFS = (1 << 7),
1888286441Srpaulo	IWM_NVM_CHANNEL_WIDE = (1 << 8),
1889286441Srpaulo	IWM_NVM_CHANNEL_40MHZ = (1 << 9),
1890286441Srpaulo	IWM_NVM_CHANNEL_80MHZ = (1 << 10),
1891286441Srpaulo	IWM_NVM_CHANNEL_160MHZ = (1 << 11),
1892286441Srpaulo};
1893286441Srpaulo
1894286441Srpaulo/*
1895298877Savos * Translate EEPROM flags to net80211.
1896286441Srpaulo */
1897298877Savosstatic uint32_t
1898298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags)
1899286441Srpaulo{
1900298877Savos	uint32_t nflags;
1901286441Srpaulo
1902298877Savos	nflags = 0;
1903298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0)
1904298877Savos		nflags |= IEEE80211_CHAN_PASSIVE;
1905298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0)
1906298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1907298877Savos	if (ch_flags & IWM_NVM_CHANNEL_RADAR) {
1908298877Savos		nflags |= IEEE80211_CHAN_DFS;
1909298877Savos		/* Just in case. */
1910298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1911286441Srpaulo	}
1912286441Srpaulo
1913298877Savos	return (nflags);
1914286441Srpaulo}
1915286441Srpaulo
1916286441Srpaulostatic void
1917298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[],
1918303628Ssbruno    int maxchans, int *nchans, int ch_idx, size_t ch_num,
1919303628Ssbruno    const uint8_t bands[])
1920286441Srpaulo{
1921298877Savos	const uint16_t * const nvm_ch_flags = sc->sc_nvm.nvm_ch_flags;
1922298877Savos	uint32_t nflags;
1923286441Srpaulo	uint16_t ch_flags;
1924298877Savos	uint8_t ieee;
1925298877Savos	int error;
1926286441Srpaulo
1927298877Savos	for (; ch_idx < ch_num; ch_idx++) {
1928286441Srpaulo		ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx);
1929303628Ssbruno		if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000)
1930303628Ssbruno			ieee = iwm_nvm_channels[ch_idx];
1931303628Ssbruno		else
1932303628Ssbruno			ieee = iwm_nvm_channels_8000[ch_idx];
1933286441Srpaulo
1934286441Srpaulo		if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) {
1935286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1936286441Srpaulo			    "Ch. %d Flags %x [%sGHz] - No traffic\n",
1937298877Savos			    ieee, ch_flags,
1938286441Srpaulo			    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1939286441Srpaulo			    "5.2" : "2.4");
1940286441Srpaulo			continue;
1941286441Srpaulo		}
1942286441Srpaulo
1943298877Savos		nflags = iwm_eeprom_channel_flags(ch_flags);
1944298877Savos		error = ieee80211_add_channel(chans, maxchans, nchans,
1945298877Savos		    ieee, 0, 0, nflags, bands);
1946298877Savos		if (error != 0)
1947298877Savos			break;
1948286441Srpaulo
1949286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1950286441Srpaulo		    "Ch. %d Flags %x [%sGHz] - Added\n",
1951298877Savos		    ieee, ch_flags,
1952286441Srpaulo		    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1953286441Srpaulo		    "5.2" : "2.4");
1954286441Srpaulo	}
1955286441Srpaulo}
1956286441Srpaulo
1957298877Savosstatic void
1958298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans,
1959298877Savos    struct ieee80211_channel chans[])
1960298877Savos{
1961298877Savos	struct iwm_softc *sc = ic->ic_softc;
1962298877Savos	struct iwm_nvm_data *data = &sc->sc_nvm;
1963299883Skevlo	uint8_t bands[IEEE80211_MODE_BYTES];
1964303628Ssbruno	size_t ch_num;
1965298877Savos
1966298877Savos	memset(bands, 0, sizeof(bands));
1967298877Savos	/* 1-13: 11b/g channels. */
1968298877Savos	setbit(bands, IEEE80211_MODE_11B);
1969298877Savos	setbit(bands, IEEE80211_MODE_11G);
1970298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans, 0,
1971298877Savos	    IWM_NUM_2GHZ_CHANNELS - 1, bands);
1972298877Savos
1973298877Savos	/* 14: 11b channel only. */
1974298877Savos	clrbit(bands, IEEE80211_MODE_11G);
1975298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans,
1976298936Savos	    IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands);
1977298877Savos
1978298877Savos	if (data->sku_cap_band_52GHz_enable) {
1979303628Ssbruno		if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000)
1980303628Ssbruno			ch_num = nitems(iwm_nvm_channels);
1981303628Ssbruno		else
1982303628Ssbruno			ch_num = nitems(iwm_nvm_channels_8000);
1983298877Savos		memset(bands, 0, sizeof(bands));
1984298877Savos		setbit(bands, IEEE80211_MODE_11A);
1985298877Savos		iwm_add_channel_band(sc, chans, maxchans, nchans,
1986303628Ssbruno		    IWM_NUM_2GHZ_CHANNELS, ch_num, bands);
1987298877Savos	}
1988298877Savos}
1989298877Savos
1990303628Ssbrunostatic void
1991303628Ssbrunoiwm_set_hw_address_8000(struct iwm_softc *sc, struct iwm_nvm_data *data,
1992303628Ssbruno	const uint16_t *mac_override, const uint16_t *nvm_hw)
1993303628Ssbruno{
1994303628Ssbruno	const uint8_t *hw_addr;
1995303628Ssbruno
1996303628Ssbruno	if (mac_override) {
1997303628Ssbruno		static const uint8_t reserved_mac[] = {
1998303628Ssbruno			0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
1999303628Ssbruno		};
2000303628Ssbruno
2001303628Ssbruno		hw_addr = (const uint8_t *)(mac_override +
2002303628Ssbruno				 IWM_MAC_ADDRESS_OVERRIDE_8000);
2003303628Ssbruno
2004303628Ssbruno		/*
2005303628Ssbruno		 * Store the MAC address from MAO section.
2006303628Ssbruno		 * No byte swapping is required in MAO section
2007303628Ssbruno		 */
2008303628Ssbruno		IEEE80211_ADDR_COPY(data->hw_addr, hw_addr);
2009303628Ssbruno
2010303628Ssbruno		/*
2011303628Ssbruno		 * Force the use of the OTP MAC address in case of reserved MAC
2012303628Ssbruno		 * address in the NVM, or if address is given but invalid.
2013303628Ssbruno		 */
2014303628Ssbruno		if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) &&
2015303628Ssbruno		    !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) &&
2016303628Ssbruno		    iwm_is_valid_ether_addr(data->hw_addr) &&
2017303628Ssbruno		    !IEEE80211_IS_MULTICAST(data->hw_addr))
2018303628Ssbruno			return;
2019303628Ssbruno
2020303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2021303628Ssbruno		    "%s: mac address from nvm override section invalid\n",
2022303628Ssbruno		    __func__);
2023303628Ssbruno	}
2024303628Ssbruno
2025303628Ssbruno	if (nvm_hw) {
2026303628Ssbruno		/* read the mac address from WFMP registers */
2027303628Ssbruno		uint32_t mac_addr0 =
2028303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0));
2029303628Ssbruno		uint32_t mac_addr1 =
2030303628Ssbruno		    htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1));
2031303628Ssbruno
2032303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr0;
2033303628Ssbruno		data->hw_addr[0] = hw_addr[3];
2034303628Ssbruno		data->hw_addr[1] = hw_addr[2];
2035303628Ssbruno		data->hw_addr[2] = hw_addr[1];
2036303628Ssbruno		data->hw_addr[3] = hw_addr[0];
2037303628Ssbruno
2038303628Ssbruno		hw_addr = (const uint8_t *)&mac_addr1;
2039303628Ssbruno		data->hw_addr[4] = hw_addr[1];
2040303628Ssbruno		data->hw_addr[5] = hw_addr[0];
2041303628Ssbruno
2042303628Ssbruno		return;
2043303628Ssbruno	}
2044303628Ssbruno
2045303628Ssbruno	device_printf(sc->sc_dev, "%s: mac address not found\n", __func__);
2046303628Ssbruno	memset(data->hw_addr, 0, sizeof(data->hw_addr));
2047303628Ssbruno}
2048303628Ssbruno
2049286441Srpaulostatic int
2050303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2051303628Ssbruno	    const uint16_t *phy_sku)
2052303628Ssbruno{
2053303628Ssbruno	if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000)
2054303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_SKU);
2055303628Ssbruno
2056303628Ssbruno	return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000));
2057303628Ssbruno}
2058303628Ssbruno
2059303628Ssbrunostatic int
2060303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2061303628Ssbruno{
2062303628Ssbruno	if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000)
2063303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_NVM_VERSION);
2064303628Ssbruno	else
2065303628Ssbruno		return le32_to_cpup((const uint32_t *)(nvm_sw +
2066303628Ssbruno						IWM_NVM_VERSION_8000));
2067303628Ssbruno}
2068303628Ssbruno
2069303628Ssbrunostatic int
2070303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw,
2071303628Ssbruno		  const uint16_t *phy_sku)
2072303628Ssbruno{
2073303628Ssbruno        if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000)
2074303628Ssbruno                return le16_to_cpup(nvm_sw + IWM_RADIO_CFG);
2075303628Ssbruno
2076303628Ssbruno        return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000));
2077303628Ssbruno}
2078303628Ssbruno
2079303628Ssbrunostatic int
2080303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw)
2081303628Ssbruno{
2082303628Ssbruno	int n_hw_addr;
2083303628Ssbruno
2084303628Ssbruno	if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000)
2085303628Ssbruno		return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS);
2086303628Ssbruno
2087303628Ssbruno	n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000));
2088303628Ssbruno
2089303628Ssbruno        return n_hw_addr & IWM_N_HW_ADDR_MASK;
2090303628Ssbruno}
2091303628Ssbruno
2092303628Ssbrunostatic void
2093303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data,
2094303628Ssbruno		  uint32_t radio_cfg)
2095303628Ssbruno{
2096303628Ssbruno	if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) {
2097303628Ssbruno		data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg);
2098303628Ssbruno		data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg);
2099303628Ssbruno		data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg);
2100303628Ssbruno		data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg);
2101303628Ssbruno		return;
2102303628Ssbruno	}
2103303628Ssbruno
2104303628Ssbruno	/* set the radio configuration for family 8000 */
2105303628Ssbruno	data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg);
2106303628Ssbruno	data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg);
2107303628Ssbruno	data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg);
2108303628Ssbruno	data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg);
2109303628Ssbruno	data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg);
2110303628Ssbruno	data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg);
2111303628Ssbruno}
2112303628Ssbruno
2113303628Ssbrunostatic int
2114286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc,
2115303628Ssbruno		   const uint16_t *nvm_hw, const uint16_t *nvm_sw,
2116303628Ssbruno		   const uint16_t *nvm_calib, const uint16_t *mac_override,
2117303628Ssbruno		   const uint16_t *phy_sku, const uint16_t *regulatory)
2118286441Srpaulo{
2119286441Srpaulo	struct iwm_nvm_data *data = &sc->sc_nvm;
2120286441Srpaulo	uint8_t hw_addr[IEEE80211_ADDR_LEN];
2121303628Ssbruno	uint32_t sku, radio_cfg;
2122286441Srpaulo
2123303628Ssbruno	data->nvm_version = iwm_get_nvm_version(sc, nvm_sw);
2124286441Srpaulo
2125303628Ssbruno	radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku);
2126303628Ssbruno	iwm_set_radio_cfg(sc, data, radio_cfg);
2127286441Srpaulo
2128303628Ssbruno	sku = iwm_get_sku(sc, nvm_sw, phy_sku);
2129286441Srpaulo	data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ;
2130286441Srpaulo	data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ;
2131286441Srpaulo	data->sku_cap_11n_enable = 0;
2132286441Srpaulo
2133303628Ssbruno	data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw);
2134286441Srpaulo
2135286441Srpaulo	/* The byte order is little endian 16 bit, meaning 214365 */
2136303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) {
2137303628Ssbruno		IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR);
2138303628Ssbruno		data->hw_addr[0] = hw_addr[1];
2139303628Ssbruno		data->hw_addr[1] = hw_addr[0];
2140303628Ssbruno		data->hw_addr[2] = hw_addr[3];
2141303628Ssbruno		data->hw_addr[3] = hw_addr[2];
2142303628Ssbruno		data->hw_addr[4] = hw_addr[5];
2143303628Ssbruno		data->hw_addr[5] = hw_addr[4];
2144303628Ssbruno	} else {
2145303628Ssbruno		iwm_set_hw_address_8000(sc, data, mac_override, nvm_hw);
2146303628Ssbruno	}
2147286441Srpaulo
2148303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) {
2149303628Ssbruno		memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS],
2150303628Ssbruno		    IWM_NUM_CHANNELS * sizeof(uint16_t));
2151303628Ssbruno	} else {
2152303628Ssbruno		memcpy(data->nvm_ch_flags, &regulatory[IWM_NVM_CHANNELS_8000],
2153303628Ssbruno		    IWM_NUM_CHANNELS_8000 * sizeof(uint16_t));
2154303628Ssbruno	}
2155286441Srpaulo
2156286441Srpaulo	return 0;
2157286441Srpaulo}
2158286441Srpaulo
2159286441Srpaulo/*
2160286441Srpaulo * END NVM PARSE
2161286441Srpaulo */
2162286441Srpaulo
2163286441Srpaulostatic int
2164286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections)
2165286441Srpaulo{
2166303628Ssbruno	const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
2167286441Srpaulo
2168286441Srpaulo	/* Checking for required sections */
2169303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) {
2170303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2171303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_HW].data) {
2172303628Ssbruno			device_printf(sc->sc_dev,
2173303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2174303628Ssbruno			return ENOENT;
2175303628Ssbruno		}
2176303628Ssbruno
2177303628Ssbruno		hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data;
2178303628Ssbruno	} else if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) {
2179303628Ssbruno		/* SW and REGULATORY sections are mandatory */
2180303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
2181303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) {
2182303628Ssbruno			device_printf(sc->sc_dev,
2183303628Ssbruno			    "Can't parse empty OTP/NVM sections\n");
2184303628Ssbruno			return ENOENT;
2185303628Ssbruno		}
2186303628Ssbruno		/* MAC_OVERRIDE or at least HW section must exist */
2187303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_HW_8000].data &&
2188303628Ssbruno		    !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
2189303628Ssbruno			device_printf(sc->sc_dev,
2190303628Ssbruno			    "Can't parse mac_address, empty sections\n");
2191303628Ssbruno			return ENOENT;
2192303628Ssbruno		}
2193303628Ssbruno
2194303628Ssbruno		/* PHY_SKU section is mandatory in B0 */
2195303628Ssbruno		if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) {
2196303628Ssbruno			device_printf(sc->sc_dev,
2197303628Ssbruno			    "Can't parse phy_sku in B0, empty sections\n");
2198303628Ssbruno			return ENOENT;
2199303628Ssbruno		}
2200303628Ssbruno
2201303628Ssbruno		hw = (const uint16_t *)
2202303628Ssbruno		    sections[IWM_NVM_SECTION_TYPE_HW_8000].data;
2203303628Ssbruno	} else {
2204303628Ssbruno		panic("unknown device family %d\n", sc->sc_device_family);
2205286441Srpaulo	}
2206286441Srpaulo
2207286441Srpaulo	sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data;
2208303628Ssbruno	calib = (const uint16_t *)
2209303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data;
2210303628Ssbruno	regulatory = (const uint16_t *)
2211303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_REGULATORY].data;
2212303628Ssbruno	mac_override = (const uint16_t *)
2213303628Ssbruno	    sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data;
2214303628Ssbruno	phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data;
2215303628Ssbruno
2216303628Ssbruno	return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override,
2217303628Ssbruno	    phy_sku, regulatory);
2218286441Srpaulo}
2219286441Srpaulo
2220286441Srpaulostatic int
2221286441Srpauloiwm_nvm_init(struct iwm_softc *sc)
2222286441Srpaulo{
2223286441Srpaulo	struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS];
2224286441Srpaulo	int i, section, error;
2225286441Srpaulo	uint16_t len;
2226303628Ssbruno	uint8_t *buf;
2227303628Ssbruno	const size_t bufsz = IWM_MAX_NVM_SECTION_SIZE;
2228286441Srpaulo
2229303628Ssbruno	memset(nvm_sections, 0 , sizeof(nvm_sections));
2230286441Srpaulo
2231303628Ssbruno	buf = malloc(bufsz, M_DEVBUF, M_NOWAIT);
2232303628Ssbruno	if (buf == NULL)
2233303628Ssbruno		return ENOMEM;
2234301970Sadrian
2235286441Srpaulo	for (i = 0; i < nitems(nvm_to_read); i++) {
2236286441Srpaulo		section = nvm_to_read[i];
2237286441Srpaulo		KASSERT(section <= nitems(nvm_sections),
2238286441Srpaulo		    ("too many sections"));
2239286441Srpaulo
2240303628Ssbruno		error = iwm_nvm_read_section(sc, section, buf, &len, bufsz);
2241303628Ssbruno		if (error) {
2242303628Ssbruno			error = 0;
2243303628Ssbruno			continue;
2244303628Ssbruno		}
2245303628Ssbruno		nvm_sections[section].data = malloc(len, M_DEVBUF, M_NOWAIT);
2246303628Ssbruno		if (nvm_sections[section].data == NULL) {
2247286441Srpaulo			error = ENOMEM;
2248286441Srpaulo			break;
2249286441Srpaulo		}
2250303628Ssbruno		memcpy(nvm_sections[section].data, buf, len);
2251286441Srpaulo		nvm_sections[section].length = len;
2252286441Srpaulo	}
2253303628Ssbruno	free(buf, M_DEVBUF);
2254301970Sadrian	if (error == 0)
2255301970Sadrian		error = iwm_parse_nvm_sections(sc, nvm_sections);
2256286441Srpaulo
2257301970Sadrian	for (i = 0; i < IWM_NVM_NUM_OF_SECTIONS; i++) {
2258301970Sadrian		if (nvm_sections[i].data != NULL)
2259301970Sadrian			free(nvm_sections[i].data, M_DEVBUF);
2260301970Sadrian	}
2261301970Sadrian
2262301970Sadrian	return error;
2263286441Srpaulo}
2264286441Srpaulo
2265286441Srpaulo/*
2266286441Srpaulo * Firmware loading gunk.  This is kind of a weird hybrid between the
2267286441Srpaulo * iwn driver and the Linux iwlwifi driver.
2268286441Srpaulo */
2269286441Srpaulo
2270286441Srpaulostatic int
2271303628Ssbrunoiwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr,
2272286441Srpaulo	const uint8_t *section, uint32_t byte_cnt)
2273286441Srpaulo{
2274303628Ssbruno	int error = EINVAL;
2275303628Ssbruno	uint32_t chunk_sz, offset;
2276303628Ssbruno
2277303628Ssbruno	chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt);
2278303628Ssbruno
2279303628Ssbruno	for (offset = 0; offset < byte_cnt; offset += chunk_sz) {
2280303628Ssbruno		uint32_t addr, len;
2281303628Ssbruno		const uint8_t *data;
2282303628Ssbruno
2283303628Ssbruno		addr = dst_addr + offset;
2284303628Ssbruno		len = MIN(chunk_sz, byte_cnt - offset);
2285303628Ssbruno		data = section + offset;
2286303628Ssbruno
2287303628Ssbruno		error = iwm_firmware_load_chunk(sc, addr, data, len);
2288303628Ssbruno		if (error)
2289303628Ssbruno			break;
2290303628Ssbruno	}
2291303628Ssbruno
2292303628Ssbruno	return error;
2293303628Ssbruno}
2294303628Ssbruno
2295303628Ssbrunostatic int
2296303628Ssbrunoiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr,
2297303628Ssbruno	const uint8_t *chunk, uint32_t byte_cnt)
2298303628Ssbruno{
2299286441Srpaulo	struct iwm_dma_info *dma = &sc->fw_dma;
2300286441Srpaulo	int error;
2301286441Srpaulo
2302303628Ssbruno	/* Copy firmware chunk into pre-allocated DMA-safe memory. */
2303303628Ssbruno	memcpy(dma->vaddr, chunk, byte_cnt);
2304286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
2305286441Srpaulo
2306303628Ssbruno	if (dst_addr >= IWM_FW_MEM_EXTENDED_START &&
2307303628Ssbruno	    dst_addr <= IWM_FW_MEM_EXTENDED_END) {
2308303628Ssbruno		iwm_set_bits_prph(sc, IWM_LMPM_CHICK,
2309303628Ssbruno		    IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2310303628Ssbruno	}
2311303628Ssbruno
2312303628Ssbruno	sc->sc_fw_chunk_done = 0;
2313303628Ssbruno
2314286441Srpaulo	if (!iwm_nic_lock(sc))
2315286441Srpaulo		return EBUSY;
2316286441Srpaulo
2317286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2318286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
2319286441Srpaulo	IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL),
2320286441Srpaulo	    dst_addr);
2321286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL),
2322286441Srpaulo	    dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
2323286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL),
2324286441Srpaulo	    (iwm_get_dma_hi_addr(dma->paddr)
2325286441Srpaulo	      << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
2326286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL),
2327286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
2328286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
2329286441Srpaulo	    IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
2330286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
2331286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE    |
2332286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
2333286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
2334286441Srpaulo
2335286441Srpaulo	iwm_nic_unlock(sc);
2336286441Srpaulo
2337286441Srpaulo	/* wait 1s for this segment to load */
2338286441Srpaulo	while (!sc->sc_fw_chunk_done)
2339286441Srpaulo		if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0)
2340286441Srpaulo			break;
2341286441Srpaulo
2342303628Ssbruno	if (!sc->sc_fw_chunk_done) {
2343303628Ssbruno		device_printf(sc->sc_dev,
2344303628Ssbruno		    "fw chunk addr 0x%x len %d failed to load\n",
2345303628Ssbruno		    dst_addr, byte_cnt);
2346303628Ssbruno	}
2347303628Ssbruno
2348303628Ssbruno	if (dst_addr >= IWM_FW_MEM_EXTENDED_START &&
2349303628Ssbruno	    dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) {
2350303628Ssbruno		iwm_clear_bits_prph(sc, IWM_LMPM_CHICK,
2351303628Ssbruno		    IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE);
2352303628Ssbruno		iwm_nic_unlock(sc);
2353303628Ssbruno	}
2354303628Ssbruno
2355286441Srpaulo	return error;
2356286441Srpaulo}
2357286441Srpaulo
2358303628Ssbrunoint
2359303628Ssbrunoiwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws,
2360303628Ssbruno    int cpu, int *first_ucode_section)
2361303628Ssbruno{
2362303628Ssbruno	int shift_param;
2363303628Ssbruno	int i, error = 0, sec_num = 0x1;
2364303628Ssbruno	uint32_t val, last_read_idx = 0;
2365303628Ssbruno	const void *data;
2366303628Ssbruno	uint32_t dlen;
2367303628Ssbruno	uint32_t offset;
2368303628Ssbruno
2369303628Ssbruno	if (cpu == 1) {
2370303628Ssbruno		shift_param = 0;
2371303628Ssbruno		*first_ucode_section = 0;
2372303628Ssbruno	} else {
2373303628Ssbruno		shift_param = 16;
2374303628Ssbruno		(*first_ucode_section)++;
2375303628Ssbruno	}
2376303628Ssbruno
2377303628Ssbruno	for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) {
2378303628Ssbruno		last_read_idx = i;
2379303628Ssbruno		data = fws->fw_sect[i].fws_data;
2380303628Ssbruno		dlen = fws->fw_sect[i].fws_len;
2381303628Ssbruno		offset = fws->fw_sect[i].fws_devoff;
2382303628Ssbruno
2383303628Ssbruno		/*
2384303628Ssbruno		 * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
2385303628Ssbruno		 * CPU1 to CPU2.
2386303628Ssbruno		 * PAGING_SEPARATOR_SECTION delimiter - separate between
2387303628Ssbruno		 * CPU2 non paged to CPU2 paging sec.
2388303628Ssbruno		 */
2389303628Ssbruno		if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION ||
2390303628Ssbruno		    offset == IWM_PAGING_SEPARATOR_SECTION)
2391303628Ssbruno			break;
2392303628Ssbruno
2393303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2394303628Ssbruno		    "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n",
2395303628Ssbruno		    i, offset, dlen, cpu);
2396303628Ssbruno
2397303628Ssbruno		if (dlen > sc->sc_fwdmasegsz) {
2398303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
2399303628Ssbruno			    "chunk %d too large (%d bytes)\n", i, dlen);
2400303628Ssbruno			error = EFBIG;
2401303628Ssbruno		} else {
2402303628Ssbruno			error = iwm_firmware_load_sect(sc, offset, data, dlen);
2403303628Ssbruno		}
2404303628Ssbruno		if (error) {
2405303628Ssbruno			device_printf(sc->sc_dev,
2406303628Ssbruno			    "could not load firmware chunk %d (error %d)\n",
2407303628Ssbruno			    i, error);
2408303628Ssbruno			return error;
2409303628Ssbruno		}
2410303628Ssbruno
2411303628Ssbruno		/* Notify the ucode of the loaded section number and status */
2412303628Ssbruno		if (iwm_nic_lock(sc)) {
2413303628Ssbruno			val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS);
2414303628Ssbruno			val = val | (sec_num << shift_param);
2415303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val);
2416303628Ssbruno			sec_num = (sec_num << 1) | 0x1;
2417303628Ssbruno			iwm_nic_unlock(sc);
2418303628Ssbruno
2419303628Ssbruno			/*
2420303628Ssbruno			 * The firmware won't load correctly without this delay.
2421303628Ssbruno			 */
2422303628Ssbruno			DELAY(8000);
2423303628Ssbruno		}
2424303628Ssbruno	}
2425303628Ssbruno
2426303628Ssbruno	*first_ucode_section = last_read_idx;
2427303628Ssbruno
2428303628Ssbruno	if (iwm_nic_lock(sc)) {
2429303628Ssbruno		if (cpu == 1)
2430303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF);
2431303628Ssbruno		else
2432303628Ssbruno			IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
2433303628Ssbruno		iwm_nic_unlock(sc);
2434303628Ssbruno	}
2435303628Ssbruno
2436303628Ssbruno	return 0;
2437303628Ssbruno}
2438303628Ssbruno
2439303628Ssbrunoint
2440303628Ssbrunoiwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2441303628Ssbruno{
2442303628Ssbruno	struct iwm_fw_sects *fws;
2443303628Ssbruno	int error = 0;
2444303628Ssbruno	int first_ucode_section;
2445303628Ssbruno
2446303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n",
2447303628Ssbruno	    ucode_type);
2448303628Ssbruno
2449303628Ssbruno	fws = &sc->sc_fw.fw_sects[ucode_type];
2450303628Ssbruno
2451303628Ssbruno	/* configure the ucode to be ready to get the secured image */
2452303628Ssbruno	/* release CPU reset */
2453303628Ssbruno	iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT);
2454303628Ssbruno
2455303628Ssbruno	/* load to FW the binary Secured sections of CPU1 */
2456303628Ssbruno	error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section);
2457303628Ssbruno	if (error)
2458303628Ssbruno		return error;
2459303628Ssbruno
2460303628Ssbruno	/* load to FW the binary sections of CPU2 */
2461303628Ssbruno	return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section);
2462303628Ssbruno}
2463303628Ssbruno
2464286441Srpaulostatic int
2465303628Ssbrunoiwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2466286441Srpaulo{
2467286441Srpaulo	struct iwm_fw_sects *fws;
2468303628Ssbruno	int error, i;
2469286441Srpaulo	const void *data;
2470286441Srpaulo	uint32_t dlen;
2471286441Srpaulo	uint32_t offset;
2472286441Srpaulo
2473286441Srpaulo	sc->sc_uc.uc_intr = 0;
2474286441Srpaulo
2475286441Srpaulo	fws = &sc->sc_fw.fw_sects[ucode_type];
2476286441Srpaulo	for (i = 0; i < fws->fw_count; i++) {
2477286441Srpaulo		data = fws->fw_sect[i].fws_data;
2478286441Srpaulo		dlen = fws->fw_sect[i].fws_len;
2479286441Srpaulo		offset = fws->fw_sect[i].fws_devoff;
2480286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
2481286441Srpaulo		    "LOAD FIRMWARE type %d offset %u len %d\n",
2482286441Srpaulo		    ucode_type, offset, dlen);
2483303628Ssbruno		if (dlen > sc->sc_fwdmasegsz) {
2484303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
2485303628Ssbruno			    "chunk %d too large (%d bytes)\n", i, dlen);
2486303628Ssbruno			error = EFBIG;
2487303628Ssbruno		} else {
2488303628Ssbruno			error = iwm_firmware_load_sect(sc, offset, data, dlen);
2489303628Ssbruno		}
2490286441Srpaulo		if (error) {
2491286441Srpaulo			device_printf(sc->sc_dev,
2492303628Ssbruno			    "could not load firmware chunk %u of %u "
2493303628Ssbruno			    "(error=%d)\n", i, fws->fw_count, error);
2494286441Srpaulo			return error;
2495286441Srpaulo		}
2496286441Srpaulo	}
2497286441Srpaulo
2498286441Srpaulo	IWM_WRITE(sc, IWM_CSR_RESET, 0);
2499286441Srpaulo
2500303628Ssbruno	return 0;
2501303628Ssbruno}
2502303628Ssbruno
2503303628Ssbrunostatic int
2504303628Ssbrunoiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2505303628Ssbruno{
2506303628Ssbruno	int error, w;
2507303628Ssbruno
2508303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000)
2509303628Ssbruno		error = iwm_load_firmware_8000(sc, ucode_type);
2510303628Ssbruno	else
2511303628Ssbruno		error = iwm_load_firmware_7000(sc, ucode_type);
2512303628Ssbruno	if (error)
2513303628Ssbruno		return error;
2514303628Ssbruno
2515303628Ssbruno	/* wait for the firmware to load */
2516286441Srpaulo	for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) {
2517286441Srpaulo		error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10);
2518286441Srpaulo	}
2519303628Ssbruno	if (error || !sc->sc_uc.uc_ok) {
2520303628Ssbruno		device_printf(sc->sc_dev, "could not load firmware\n");
2521303628Ssbruno		if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) {
2522303628Ssbruno			device_printf(sc->sc_dev, "cpu1 status: 0x%x\n",
2523303628Ssbruno			    iwm_read_prph(sc, IWM_SB_CPU_1_STATUS));
2524303628Ssbruno			device_printf(sc->sc_dev, "cpu2 status: 0x%x\n",
2525303628Ssbruno			    iwm_read_prph(sc, IWM_SB_CPU_2_STATUS));
2526303628Ssbruno		}
2527303628Ssbruno	}
2528286441Srpaulo
2529303628Ssbruno	/*
2530303628Ssbruno	 * Give the firmware some time to initialize.
2531303628Ssbruno	 * Accessing it too early causes errors.
2532303628Ssbruno	 */
2533303628Ssbruno	msleep(&w, &sc->sc_mtx, 0, "iwmfwinit", hz);
2534303628Ssbruno
2535286441Srpaulo	return error;
2536286441Srpaulo}
2537286441Srpaulo
2538286441Srpaulo/* iwlwifi: pcie/trans.c */
2539286441Srpaulostatic int
2540286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
2541286441Srpaulo{
2542286441Srpaulo	int error;
2543286441Srpaulo
2544286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
2545286441Srpaulo
2546286441Srpaulo	if ((error = iwm_nic_init(sc)) != 0) {
2547286441Srpaulo		device_printf(sc->sc_dev, "unable to init nic\n");
2548286441Srpaulo		return error;
2549286441Srpaulo	}
2550286441Srpaulo
2551286441Srpaulo	/* make sure rfkill handshake bits are cleared */
2552286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2553286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR,
2554286441Srpaulo	    IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
2555286441Srpaulo
2556286441Srpaulo	/* clear (again), then enable host interrupts */
2557286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
2558286441Srpaulo	iwm_enable_interrupts(sc);
2559286441Srpaulo
2560286441Srpaulo	/* really make sure rfkill handshake bits are cleared */
2561286441Srpaulo	/* maybe we should write a few times more?  just to make sure */
2562286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2563286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
2564286441Srpaulo
2565286441Srpaulo	/* Load the given image to the HW */
2566286441Srpaulo	return iwm_load_firmware(sc, ucode_type);
2567286441Srpaulo}
2568286441Srpaulo
2569286441Srpaulostatic int
2570286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant)
2571286441Srpaulo{
2572286441Srpaulo	struct iwm_tx_ant_cfg_cmd tx_ant_cmd = {
2573286441Srpaulo		.valid = htole32(valid_tx_ant),
2574286441Srpaulo	};
2575286441Srpaulo
2576286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD,
2577286441Srpaulo	    IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd);
2578286441Srpaulo}
2579286441Srpaulo
2580286441Srpaulo/* iwlwifi: mvm/fw.c */
2581286441Srpaulostatic int
2582286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc)
2583286441Srpaulo{
2584286441Srpaulo	struct iwm_phy_cfg_cmd phy_cfg_cmd;
2585286441Srpaulo	enum iwm_ucode_type ucode_type = sc->sc_uc_current;
2586286441Srpaulo
2587286441Srpaulo	/* Set parameters */
2588286441Srpaulo	phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config);
2589286441Srpaulo	phy_cfg_cmd.calib_control.event_trigger =
2590286441Srpaulo	    sc->sc_default_calib[ucode_type].event_trigger;
2591286441Srpaulo	phy_cfg_cmd.calib_control.flow_trigger =
2592286441Srpaulo	    sc->sc_default_calib[ucode_type].flow_trigger;
2593286441Srpaulo
2594286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
2595286441Srpaulo	    "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg);
2596286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC,
2597286441Srpaulo	    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
2598286441Srpaulo}
2599286441Srpaulo
2600286441Srpaulostatic int
2601286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc,
2602286441Srpaulo	enum iwm_ucode_type ucode_type)
2603286441Srpaulo{
2604286441Srpaulo	enum iwm_ucode_type old_type = sc->sc_uc_current;
2605286441Srpaulo	int error;
2606286441Srpaulo
2607303628Ssbruno	if ((error = iwm_read_firmware(sc, ucode_type)) != 0) {
2608303628Ssbruno		device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n",
2609303628Ssbruno			error);
2610286441Srpaulo		return error;
2611303628Ssbruno	}
2612286441Srpaulo
2613286441Srpaulo	sc->sc_uc_current = ucode_type;
2614286441Srpaulo	error = iwm_start_fw(sc, ucode_type);
2615286441Srpaulo	if (error) {
2616303628Ssbruno		device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error);
2617286441Srpaulo		sc->sc_uc_current = old_type;
2618286441Srpaulo		return error;
2619286441Srpaulo	}
2620286441Srpaulo
2621303628Ssbruno	error = iwm_post_alive(sc);
2622303628Ssbruno	if (error) {
2623303628Ssbruno		device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error);
2624303628Ssbruno	}
2625303628Ssbruno	return error;
2626286441Srpaulo}
2627286441Srpaulo
2628286441Srpaulo/*
2629286441Srpaulo * mvm misc bits
2630286441Srpaulo */
2631286441Srpaulo
2632286441Srpaulo/*
2633286441Srpaulo * follows iwlwifi/fw.c
2634286441Srpaulo */
2635286441Srpaulostatic int
2636286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm)
2637286441Srpaulo{
2638286441Srpaulo	int error;
2639286441Srpaulo
2640286441Srpaulo	/* do not operate with rfkill switch turned on */
2641286441Srpaulo	if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) {
2642286441Srpaulo		device_printf(sc->sc_dev,
2643286441Srpaulo		    "radio is disabled by hardware switch\n");
2644286441Srpaulo		return EPERM;
2645286441Srpaulo	}
2646286441Srpaulo
2647286441Srpaulo	sc->sc_init_complete = 0;
2648286441Srpaulo	if ((error = iwm_mvm_load_ucode_wait_alive(sc,
2649301192Sadrian	    IWM_UCODE_TYPE_INIT)) != 0) {
2650301192Sadrian		device_printf(sc->sc_dev, "failed to load init firmware\n");
2651286441Srpaulo		return error;
2652301192Sadrian	}
2653286441Srpaulo
2654286441Srpaulo	if (justnvm) {
2655286441Srpaulo		if ((error = iwm_nvm_init(sc)) != 0) {
2656286441Srpaulo			device_printf(sc->sc_dev, "failed to read nvm\n");
2657286441Srpaulo			return error;
2658286441Srpaulo		}
2659289729Skevlo		IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->sc_nvm.hw_addr);
2660286441Srpaulo
2661286441Srpaulo		return 0;
2662286441Srpaulo	}
2663286441Srpaulo
2664303628Ssbruno	if ((error = iwm_send_bt_init_conf(sc)) != 0) {
2665303628Ssbruno		device_printf(sc->sc_dev,
2666303628Ssbruno		    "failed to send bt coex configuration: %d\n", error);
2667303628Ssbruno		return error;
2668303628Ssbruno	}
2669303628Ssbruno
2670303628Ssbruno	/* Init Smart FIFO. */
2671303628Ssbruno	error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF);
2672303628Ssbruno	if (error != 0)
2673303628Ssbruno		return error;
2674303628Ssbruno
2675286441Srpaulo	/* Send TX valid antennas before triggering calibrations */
2676303628Ssbruno	if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) {
2677303628Ssbruno		device_printf(sc->sc_dev,
2678303628Ssbruno		    "failed to send antennas before calibration: %d\n", error);
2679286441Srpaulo		return error;
2680303628Ssbruno	}
2681286441Srpaulo
2682286441Srpaulo	/*
2683303628Ssbruno	 * Send phy configurations command to init uCode
2684303628Ssbruno	 * to start the 16.0 uCode init image internal calibrations.
2685303628Ssbruno	 */
2686286441Srpaulo	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) {
2687286441Srpaulo		device_printf(sc->sc_dev,
2688286441Srpaulo		    "%s: failed to run internal calibration: %d\n",
2689286441Srpaulo		    __func__, error);
2690286441Srpaulo		return error;
2691286441Srpaulo	}
2692286441Srpaulo
2693286441Srpaulo	/*
2694286441Srpaulo	 * Nothing to do but wait for the init complete notification
2695286441Srpaulo	 * from the firmware
2696286441Srpaulo	 */
2697303628Ssbruno	while (!sc->sc_init_complete) {
2698303628Ssbruno		error = msleep(&sc->sc_init_complete, &sc->sc_mtx,
2699303628Ssbruno				 0, "iwminit", 2*hz);
2700303628Ssbruno		if (error) {
2701303628Ssbruno			device_printf(sc->sc_dev, "init complete failed: %d\n",
2702303628Ssbruno				sc->sc_init_complete);
2703286441Srpaulo			break;
2704303628Ssbruno		}
2705303628Ssbruno	}
2706286441Srpaulo
2707303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_RESET, "init %scomplete\n",
2708303628Ssbruno	    sc->sc_init_complete ? "" : "not ");
2709303628Ssbruno
2710286441Srpaulo	return error;
2711286441Srpaulo}
2712286441Srpaulo
2713286441Srpaulo/*
2714286441Srpaulo * receive side
2715286441Srpaulo */
2716286441Srpaulo
2717286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */
2718286441Srpaulostatic int
2719286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx)
2720286441Srpaulo{
2721286441Srpaulo	struct iwm_rx_ring *ring = &sc->rxq;
2722286441Srpaulo	struct iwm_rx_data *data = &ring->data[idx];
2723286441Srpaulo	struct mbuf *m;
2724301845Sadrian	bus_dmamap_t dmamap = NULL;
2725303628Ssbruno	bus_dma_segment_t seg;
2726303628Ssbruno	int nsegs, error;
2727286441Srpaulo
2728286441Srpaulo	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE);
2729286441Srpaulo	if (m == NULL)
2730286441Srpaulo		return ENOBUFS;
2731286441Srpaulo
2732286441Srpaulo	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
2733303628Ssbruno	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m,
2734303628Ssbruno	    &seg, &nsegs, BUS_DMA_NOWAIT);
2735303628Ssbruno	if (error != 0) {
2736286441Srpaulo		device_printf(sc->sc_dev,
2737301845Sadrian		    "%s: can't map mbuf, error %d\n", __func__, error);
2738286441Srpaulo		goto fail;
2739286441Srpaulo	}
2740301845Sadrian
2741301845Sadrian	if (data->m != NULL)
2742301845Sadrian		bus_dmamap_unload(ring->data_dmat, data->map);
2743301845Sadrian
2744301845Sadrian	/* Swap ring->spare_map with data->map */
2745301845Sadrian	dmamap = data->map;
2746301845Sadrian	data->map = ring->spare_map;
2747301845Sadrian	ring->spare_map = dmamap;
2748301845Sadrian
2749286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
2750301845Sadrian	data->m = m;
2751286441Srpaulo
2752286441Srpaulo	/* Update RX descriptor. */
2753303628Ssbruno	KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned"));
2754303628Ssbruno	ring->desc[idx] = htole32(seg.ds_addr >> 8);
2755286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2756286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2757286441Srpaulo
2758286441Srpaulo	return 0;
2759286441Srpaulofail:
2760303628Ssbruno	m_freem(m);
2761286441Srpaulo	return error;
2762286441Srpaulo}
2763286441Srpaulo
2764286441Srpaulo/* iwlwifi: mvm/rx.c */
2765286441Srpaulo#define IWM_RSSI_OFFSET 50
2766286441Srpaulostatic int
2767286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2768286441Srpaulo{
2769286441Srpaulo	int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
2770286441Srpaulo	uint32_t agc_a, agc_b;
2771286441Srpaulo	uint32_t val;
2772286441Srpaulo
2773286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]);
2774286441Srpaulo	agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS;
2775286441Srpaulo	agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS;
2776286441Srpaulo
2777286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]);
2778286441Srpaulo	rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS;
2779286441Srpaulo	rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS;
2780286441Srpaulo
2781286441Srpaulo	/*
2782286441Srpaulo	 * dBm = rssi dB - agc dB - constant.
2783286441Srpaulo	 * Higher AGC (higher radio gain) means lower signal.
2784286441Srpaulo	 */
2785286441Srpaulo	rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a;
2786286441Srpaulo	rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b;
2787286441Srpaulo	max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm);
2788286441Srpaulo
2789286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2790286441Srpaulo	    "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
2791286441Srpaulo	    rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
2792286441Srpaulo
2793286441Srpaulo	return max_rssi_dbm;
2794286441Srpaulo}
2795286441Srpaulo
2796286441Srpaulo/* iwlwifi: mvm/rx.c */
2797286441Srpaulo/*
2798286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API
2799286441Srpaulo * values are reported by the fw as positive values - need to negate
2800286441Srpaulo * to obtain their dBM.  Account for missing antennas by replacing 0
2801286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
2802286441Srpaulo */
2803286441Srpaulostatic int
2804286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2805286441Srpaulo{
2806286441Srpaulo	int energy_a, energy_b, energy_c, max_energy;
2807286441Srpaulo	uint32_t val;
2808286441Srpaulo
2809286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]);
2810286441Srpaulo	energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >>
2811286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_A_POS;
2812286441Srpaulo	energy_a = energy_a ? -energy_a : -256;
2813286441Srpaulo	energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >>
2814286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_B_POS;
2815286441Srpaulo	energy_b = energy_b ? -energy_b : -256;
2816286441Srpaulo	energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >>
2817286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_C_POS;
2818286441Srpaulo	energy_c = energy_c ? -energy_c : -256;
2819286441Srpaulo	max_energy = MAX(energy_a, energy_b);
2820286441Srpaulo	max_energy = MAX(max_energy, energy_c);
2821286441Srpaulo
2822286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2823286441Srpaulo	    "energy In A %d B %d C %d , and max %d\n",
2824286441Srpaulo	    energy_a, energy_b, energy_c, max_energy);
2825286441Srpaulo
2826286441Srpaulo	return max_energy;
2827286441Srpaulo}
2828286441Srpaulo
2829286441Srpaulostatic void
2830286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc,
2831286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2832286441Srpaulo{
2833286441Srpaulo	struct iwm_rx_phy_info *phy_info = (void *)pkt->data;
2834286441Srpaulo
2835286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n");
2836286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2837286441Srpaulo
2838286441Srpaulo	memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info));
2839286441Srpaulo}
2840286441Srpaulo
2841286441Srpaulo/*
2842286441Srpaulo * Retrieve the average noise (in dBm) among receivers.
2843286441Srpaulo */
2844286441Srpaulostatic int
2845330144Seadleriwm_get_noise(struct iwm_softc *sc,
2846330144Seadler    const struct iwm_mvm_statistics_rx_non_phy *stats)
2847286441Srpaulo{
2848286441Srpaulo	int i, total, nbant, noise;
2849286441Srpaulo
2850286441Srpaulo	total = nbant = noise = 0;
2851286441Srpaulo	for (i = 0; i < 3; i++) {
2852286441Srpaulo		noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff;
2853330144Seadler		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n",
2854330144Seadler		    __func__,
2855330144Seadler		    i,
2856330144Seadler		    noise);
2857330144Seadler
2858286441Srpaulo		if (noise) {
2859286441Srpaulo			total += noise;
2860286441Srpaulo			nbant++;
2861286441Srpaulo		}
2862286441Srpaulo	}
2863286441Srpaulo
2864330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n",
2865330144Seadler	    __func__, nbant, total);
2866330144Seadler#if 0
2867286441Srpaulo	/* There should be at least one antenna but check anyway. */
2868286441Srpaulo	return (nbant == 0) ? -127 : (total / nbant) - 107;
2869330144Seadler#else
2870330144Seadler	/* For now, just hard-code it to -96 to be safe */
2871330144Seadler	return (-96);
2872330144Seadler#endif
2873286441Srpaulo}
2874286441Srpaulo
2875286441Srpaulo/*
2876286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler
2877286441Srpaulo *
2878286441Srpaulo * Handles the actual data of the Rx packet from the fw
2879286441Srpaulo */
2880286441Srpaulostatic void
2881286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc,
2882286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2883286441Srpaulo{
2884287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
2885286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2886286441Srpaulo	struct ieee80211_frame *wh;
2887286441Srpaulo	struct ieee80211_node *ni;
2888286441Srpaulo	struct ieee80211_rx_stats rxs;
2889286441Srpaulo	struct mbuf *m;
2890286441Srpaulo	struct iwm_rx_phy_info *phy_info;
2891286441Srpaulo	struct iwm_rx_mpdu_res_start *rx_res;
2892286441Srpaulo	uint32_t len;
2893286441Srpaulo	uint32_t rx_pkt_status;
2894286441Srpaulo	int rssi;
2895286441Srpaulo
2896286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2897286441Srpaulo
2898286441Srpaulo	phy_info = &sc->sc_last_phy_info;
2899286441Srpaulo	rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
2900286441Srpaulo	wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
2901286441Srpaulo	len = le16toh(rx_res->byte_count);
2902286441Srpaulo	rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len));
2903286441Srpaulo
2904286441Srpaulo	m = data->m;
2905286441Srpaulo	m->m_data = pkt->data + sizeof(*rx_res);
2906286441Srpaulo	m->m_pkthdr.len = m->m_len = len;
2907286441Srpaulo
2908286441Srpaulo	if (__predict_false(phy_info->cfg_phy_cnt > 20)) {
2909286441Srpaulo		device_printf(sc->sc_dev,
2910286441Srpaulo		    "dsp size out of range [0,20]: %d\n",
2911286441Srpaulo		    phy_info->cfg_phy_cnt);
2912286441Srpaulo		return;
2913286441Srpaulo	}
2914286441Srpaulo
2915286441Srpaulo	if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
2916286441Srpaulo	    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) {
2917286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2918286441Srpaulo		    "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
2919286441Srpaulo		return; /* drop */
2920286441Srpaulo	}
2921286441Srpaulo
2922286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
2923286441Srpaulo		rssi = iwm_mvm_get_signal_strength(sc, phy_info);
2924286441Srpaulo	} else {
2925286441Srpaulo		rssi = iwm_mvm_calc_rssi(sc, phy_info);
2926286441Srpaulo	}
2927286441Srpaulo
2928330144Seadler	/* Note: RSSI is absolute (ie a -ve value) */
2929330144Seadler	if (rssi < IWM_MIN_DBM)
2930330144Seadler		rssi = IWM_MIN_DBM;
2931330144Seadler	else if (rssi > IWM_MAX_DBM)
2932330144Seadler		rssi = IWM_MAX_DBM;
2933330144Seadler
2934330144Seadler	/* Map it to relative value */
2935330144Seadler	rssi = rssi - sc->sc_noise;
2936330144Seadler
2937286441Srpaulo	/* replenish ring for the buffer we're going to feed to the sharks */
2938286441Srpaulo	if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) {
2939286441Srpaulo		device_printf(sc->sc_dev, "%s: unable to add more buffers\n",
2940286441Srpaulo		    __func__);
2941286441Srpaulo		return;
2942286441Srpaulo	}
2943286441Srpaulo
2944330144Seadler	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2945330144Seadler	    "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise);
2946330144Seadler
2947286441Srpaulo	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2948286441Srpaulo
2949286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2950286441Srpaulo	    "%s: phy_info: channel=%d, flags=0x%08x\n",
2951286441Srpaulo	    __func__,
2952286441Srpaulo	    le16toh(phy_info->channel),
2953286441Srpaulo	    le16toh(phy_info->phy_flags));
2954286441Srpaulo
2955286441Srpaulo	/*
2956286441Srpaulo	 * Populate an RX state struct with the provided information.
2957286441Srpaulo	 */
2958286441Srpaulo	bzero(&rxs, sizeof(rxs));
2959286441Srpaulo	rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
2960286441Srpaulo	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
2961286441Srpaulo	rxs.c_ieee = le16toh(phy_info->channel);
2962286441Srpaulo	if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) {
2963286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ);
2964286441Srpaulo	} else {
2965286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ);
2966286441Srpaulo	}
2967330144Seadler
2968330144Seadler	/* rssi is in 1/2db units */
2969330144Seadler	rxs.rssi = rssi * 2;
2970286441Srpaulo	rxs.nf = sc->sc_noise;
2971286441Srpaulo
2972286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
2973286441Srpaulo		struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap;
2974286441Srpaulo
2975286441Srpaulo		tap->wr_flags = 0;
2976286441Srpaulo		if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE))
2977286441Srpaulo			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2978286441Srpaulo		tap->wr_chan_freq = htole16(rxs.c_freq);
2979286441Srpaulo		/* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */
2980286441Srpaulo		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
2981286441Srpaulo		tap->wr_dbm_antsignal = (int8_t)rssi;
2982286441Srpaulo		tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
2983286441Srpaulo		tap->wr_tsft = phy_info->system_timestamp;
2984286441Srpaulo		switch (phy_info->rate) {
2985286441Srpaulo		/* CCK rates. */
2986286441Srpaulo		case  10: tap->wr_rate =   2; break;
2987286441Srpaulo		case  20: tap->wr_rate =   4; break;
2988286441Srpaulo		case  55: tap->wr_rate =  11; break;
2989286441Srpaulo		case 110: tap->wr_rate =  22; break;
2990286441Srpaulo		/* OFDM rates. */
2991286441Srpaulo		case 0xd: tap->wr_rate =  12; break;
2992286441Srpaulo		case 0xf: tap->wr_rate =  18; break;
2993286441Srpaulo		case 0x5: tap->wr_rate =  24; break;
2994286441Srpaulo		case 0x7: tap->wr_rate =  36; break;
2995286441Srpaulo		case 0x9: tap->wr_rate =  48; break;
2996286441Srpaulo		case 0xb: tap->wr_rate =  72; break;
2997286441Srpaulo		case 0x1: tap->wr_rate =  96; break;
2998286441Srpaulo		case 0x3: tap->wr_rate = 108; break;
2999286441Srpaulo		/* Unknown rate: should not happen. */
3000286441Srpaulo		default:  tap->wr_rate =   0;
3001286441Srpaulo		}
3002286441Srpaulo	}
3003286441Srpaulo
3004286441Srpaulo	IWM_UNLOCK(sc);
3005286441Srpaulo	if (ni != NULL) {
3006286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m);
3007286441Srpaulo		ieee80211_input_mimo(ni, m, &rxs);
3008286441Srpaulo		ieee80211_free_node(ni);
3009286441Srpaulo	} else {
3010286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m);
3011286441Srpaulo		ieee80211_input_mimo_all(ic, m, &rxs);
3012286441Srpaulo	}
3013286441Srpaulo	IWM_LOCK(sc);
3014286441Srpaulo}
3015286441Srpaulo
3016293100Savosstatic int
3017286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
3018286441Srpaulo	struct iwm_node *in)
3019286441Srpaulo{
3020286441Srpaulo	struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data;
3021293100Savos	struct ieee80211_node *ni = &in->in_ni;
3022293100Savos	struct ieee80211vap *vap = ni->ni_vap;
3023286441Srpaulo	int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
3024286441Srpaulo	int failack = tx_resp->failure_frame;
3025286441Srpaulo
3026286441Srpaulo	KASSERT(tx_resp->frame_count == 1, ("too many frames"));
3027286441Srpaulo
3028286441Srpaulo	/* Update rate control statistics. */
3029298611Sadrian	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",
3030298611Sadrian	    __func__,
3031298611Sadrian	    (int) le16toh(tx_resp->status.status),
3032298611Sadrian	    (int) le16toh(tx_resp->status.sequence),
3033298611Sadrian	    tx_resp->frame_count,
3034298611Sadrian	    tx_resp->bt_kill_count,
3035298611Sadrian	    tx_resp->failure_rts,
3036298611Sadrian	    tx_resp->failure_frame,
3037298611Sadrian	    le32toh(tx_resp->initial_rate),
3038298611Sadrian	    (int) le16toh(tx_resp->wireless_media_time));
3039298611Sadrian
3040286441Srpaulo	if (status != IWM_TX_STATUS_SUCCESS &&
3041286441Srpaulo	    status != IWM_TX_STATUS_DIRECT_DONE) {
3042293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3043286441Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &failack, NULL);
3044293100Savos		return (1);
3045286441Srpaulo	} else {
3046293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
3047286441Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL);
3048293100Savos		return (0);
3049286441Srpaulo	}
3050286441Srpaulo}
3051286441Srpaulo
3052286441Srpaulostatic void
3053286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc,
3054286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
3055286441Srpaulo{
3056286441Srpaulo	struct iwm_cmd_header *cmd_hdr = &pkt->hdr;
3057286441Srpaulo	int idx = cmd_hdr->idx;
3058286441Srpaulo	int qid = cmd_hdr->qid;
3059286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[qid];
3060286441Srpaulo	struct iwm_tx_data *txd = &ring->data[idx];
3061286441Srpaulo	struct iwm_node *in = txd->in;
3062293100Savos	struct mbuf *m = txd->m;
3063293100Savos	int status;
3064286441Srpaulo
3065293100Savos	KASSERT(txd->done == 0, ("txd not done"));
3066293100Savos	KASSERT(txd->in != NULL, ("txd without node"));
3067293100Savos	KASSERT(txd->m != NULL, ("txd without mbuf"));
3068293100Savos
3069286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3070286441Srpaulo
3071286441Srpaulo	sc->sc_tx_timer = 0;
3072286441Srpaulo
3073293100Savos	status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in);
3074286441Srpaulo
3075286441Srpaulo	/* Unmap and free mbuf. */
3076286441Srpaulo	bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE);
3077286441Srpaulo	bus_dmamap_unload(ring->data_dmat, txd->map);
3078286441Srpaulo
3079286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3080286441Srpaulo	    "free txd %p, in %p\n", txd, txd->in);
3081286441Srpaulo	txd->done = 1;
3082286441Srpaulo	txd->m = NULL;
3083286441Srpaulo	txd->in = NULL;
3084286441Srpaulo
3085293100Savos	ieee80211_tx_complete(&in->in_ni, m, status);
3086293100Savos
3087286441Srpaulo	if (--ring->queued < IWM_TX_RING_LOMARK) {
3088286441Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
3089287197Sglebius		if (sc->qfullmsk == 0) {
3090287197Sglebius			iwm_start(sc);
3091286441Srpaulo		}
3092286441Srpaulo	}
3093286441Srpaulo}
3094286441Srpaulo
3095286441Srpaulo/*
3096286441Srpaulo * transmit side
3097286441Srpaulo */
3098286441Srpaulo
3099286441Srpaulo/*
3100286441Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
3101286441Srpaulo * processes waiting for a synchronous command completion.
3102286441Srpaulo * from if_iwn
3103286441Srpaulo */
3104286441Srpaulostatic void
3105286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
3106286441Srpaulo{
3107286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE];
3108286441Srpaulo	struct iwm_tx_data *data;
3109286441Srpaulo
3110286441Srpaulo	if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) {
3111286441Srpaulo		return;	/* Not a command ack. */
3112286441Srpaulo	}
3113286441Srpaulo
3114330142Seadler	/* XXX wide commands? */
3115330142Seadler	IWM_DPRINTF(sc, IWM_DEBUG_CMD,
3116330142Seadler	    "cmd notification type 0x%x qid %d idx %d\n",
3117330142Seadler	    pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx);
3118330142Seadler
3119286441Srpaulo	data = &ring->data[pkt->hdr.idx];
3120286441Srpaulo
3121286441Srpaulo	/* If the command was mapped in an mbuf, free it. */
3122286441Srpaulo	if (data->m != NULL) {
3123286441Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3124286441Srpaulo		    BUS_DMASYNC_POSTWRITE);
3125286441Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
3126286441Srpaulo		m_freem(data->m);
3127286441Srpaulo		data->m = NULL;
3128286441Srpaulo	}
3129286441Srpaulo	wakeup(&ring->desc[pkt->hdr.idx]);
3130286441Srpaulo}
3131286441Srpaulo
3132286441Srpaulo#if 0
3133286441Srpaulo/*
3134286441Srpaulo * necessary only for block ack mode
3135286441Srpaulo */
3136286441Srpaulovoid
3137286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id,
3138286441Srpaulo	uint16_t len)
3139286441Srpaulo{
3140286441Srpaulo	struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
3141286441Srpaulo	uint16_t w_val;
3142286441Srpaulo
3143286441Srpaulo	scd_bc_tbl = sc->sched_dma.vaddr;
3144286441Srpaulo
3145286441Srpaulo	len += 8; /* magic numbers came naturally from paris */
3146286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE)
3147286441Srpaulo		len = roundup(len, 4) / 4;
3148286441Srpaulo
3149286441Srpaulo	w_val = htole16(sta_id << 12 | len);
3150286441Srpaulo
3151286441Srpaulo	/* Update TX scheduler. */
3152286441Srpaulo	scd_bc_tbl[qid].tfd_offset[idx] = w_val;
3153286441Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3154286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3155286441Srpaulo
3156286441Srpaulo	/* I really wonder what this is ?!? */
3157286441Srpaulo	if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) {
3158286441Srpaulo		scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val;
3159286441Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3160286441Srpaulo		    BUS_DMASYNC_PREWRITE);
3161286441Srpaulo	}
3162286441Srpaulo}
3163286441Srpaulo#endif
3164286441Srpaulo
3165286441Srpaulo/*
3166286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate
3167286441Srpaulo * table entry.  return the index into in_ridx[].
3168286441Srpaulo *
3169286441Srpaulo * The caller then uses that index back into in_ridx
3170286441Srpaulo * to figure out the rate index programmed /into/
3171286441Srpaulo * the firmware for this given node.
3172286441Srpaulo */
3173286441Srpaulostatic int
3174286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in,
3175286441Srpaulo    uint8_t rate)
3176286441Srpaulo{
3177286441Srpaulo	int i;
3178286441Srpaulo	uint8_t r;
3179286441Srpaulo
3180286441Srpaulo	for (i = 0; i < nitems(in->in_ridx); i++) {
3181286441Srpaulo		r = iwm_rates[in->in_ridx[i]].rate;
3182286441Srpaulo		if (rate == r)
3183286441Srpaulo			return (i);
3184286441Srpaulo	}
3185286441Srpaulo	/* XXX Return the first */
3186286441Srpaulo	/* XXX TODO: have it return the /lowest/ */
3187286441Srpaulo	return (0);
3188286441Srpaulo}
3189286441Srpaulo
3190286441Srpaulo/*
3191298875Sadrian * Fill in the rate related information for a transmit command.
3192286441Srpaulo */
3193286441Srpaulostatic const struct iwm_rate *
3194286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in,
3195330155Seadler	struct mbuf *m, struct iwm_tx_cmd *tx)
3196286441Srpaulo{
3197286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
3198330155Seadler	struct ieee80211_frame *wh;
3199330155Seadler	const struct ieee80211_txparam *tp = ni->ni_txparms;
3200286441Srpaulo	const struct iwm_rate *rinfo;
3201330155Seadler	int type;
3202330155Seadler	int ridx, rate_flags, i;
3203286441Srpaulo
3204330155Seadler	wh = mtod(m, struct ieee80211_frame *);
3205330155Seadler	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3206330155Seadler
3207286441Srpaulo	tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT;
3208286441Srpaulo	tx->data_retry_limit = IWM_DEFAULT_TX_RETRY;
3209286441Srpaulo
3210330155Seadler	if (type == IEEE80211_FC0_TYPE_MGT) {
3211330155Seadler		i = iwm_tx_rateidx_lookup(sc, in, tp->mgmtrate);
3212330155Seadler		ridx = in->in_ridx[i];
3213330155Seadler	} else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3214330155Seadler		i = iwm_tx_rateidx_lookup(sc, in, tp->mcastrate);
3215330155Seadler		ridx = in->in_ridx[i];
3216330155Seadler	} else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
3217330155Seadler		i = iwm_tx_rateidx_lookup(sc, in, tp->ucastrate);
3218330155Seadler		ridx = in->in_ridx[i];
3219330155Seadler	} else if (m->m_flags & M_EAPOL) {
3220330155Seadler		i = iwm_tx_rateidx_lookup(sc, in, tp->mgmtrate);
3221330155Seadler		ridx = in->in_ridx[i];
3222330155Seadler	} else {
3223286441Srpaulo		/* for data frames, use RS table */
3224330155Seadler		/* XXX pass pktlen */
3225286441Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3226286441Srpaulo		i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate);
3227286441Srpaulo		ridx = in->in_ridx[i];
3228286441Srpaulo
3229286441Srpaulo		/* This is the index into the programmed table */
3230286441Srpaulo		tx->initial_rate_index = i;
3231286441Srpaulo		tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
3232286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
3233286441Srpaulo		    "%s: start with i=%d, txrate %d\n",
3234286441Srpaulo		    __func__, i, iwm_rates[ridx].rate);
3235286441Srpaulo	}
3236286441Srpaulo
3237286441Srpaulo	rinfo = &iwm_rates[ridx];
3238286441Srpaulo
3239286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n",
3240286441Srpaulo	    __func__, ridx,
3241286441Srpaulo	    rinfo->rate,
3242286441Srpaulo	    !! (IWM_RIDX_IS_CCK(ridx))
3243286441Srpaulo	    );
3244286441Srpaulo
3245286441Srpaulo	/* XXX TODO: hard-coded TX antenna? */
3246286441Srpaulo	rate_flags = 1 << IWM_RATE_MCS_ANT_POS;
3247286441Srpaulo	if (IWM_RIDX_IS_CCK(ridx))
3248286441Srpaulo		rate_flags |= IWM_RATE_MCS_CCK_MSK;
3249286441Srpaulo	tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
3250286441Srpaulo
3251286441Srpaulo	return rinfo;
3252286441Srpaulo}
3253286441Srpaulo
3254286441Srpaulo#define TB0_SIZE 16
3255286441Srpaulostatic int
3256286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac)
3257286441Srpaulo{
3258287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3259286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3260293099Savos	struct iwm_node *in = IWM_NODE(ni);
3261286441Srpaulo	struct iwm_tx_ring *ring;
3262286441Srpaulo	struct iwm_tx_data *data;
3263286441Srpaulo	struct iwm_tfd *desc;
3264286441Srpaulo	struct iwm_device_cmd *cmd;
3265286441Srpaulo	struct iwm_tx_cmd *tx;
3266286441Srpaulo	struct ieee80211_frame *wh;
3267286441Srpaulo	struct ieee80211_key *k = NULL;
3268286441Srpaulo	struct mbuf *m1;
3269286441Srpaulo	const struct iwm_rate *rinfo;
3270286441Srpaulo	uint32_t flags;
3271286441Srpaulo	u_int hdrlen;
3272286441Srpaulo	bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
3273286441Srpaulo	int nsegs;
3274286441Srpaulo	uint8_t tid, type;
3275286441Srpaulo	int i, totlen, error, pad;
3276286441Srpaulo
3277286441Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3278286441Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3279286441Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3280286441Srpaulo	tid = 0;
3281286441Srpaulo	ring = &sc->txq[ac];
3282286441Srpaulo	desc = &ring->desc[ring->cur];
3283286441Srpaulo	memset(desc, 0, sizeof(*desc));
3284286441Srpaulo	data = &ring->data[ring->cur];
3285286441Srpaulo
3286286441Srpaulo	/* Fill out iwm_tx_cmd to send to the firmware */
3287286441Srpaulo	cmd = &ring->cmd[ring->cur];
3288286441Srpaulo	cmd->hdr.code = IWM_TX_CMD;
3289286441Srpaulo	cmd->hdr.flags = 0;
3290286441Srpaulo	cmd->hdr.qid = ring->qid;
3291286441Srpaulo	cmd->hdr.idx = ring->cur;
3292286441Srpaulo
3293286441Srpaulo	tx = (void *)cmd->data;
3294286441Srpaulo	memset(tx, 0, sizeof(*tx));
3295286441Srpaulo
3296330155Seadler	rinfo = iwm_tx_fill_cmd(sc, in, m, tx);
3297286441Srpaulo
3298286441Srpaulo	/* Encrypt the frame if need be. */
3299286441Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
3300286441Srpaulo		/* Retrieve key for TX && do software encryption. */
3301286441Srpaulo		k = ieee80211_crypto_encap(ni, m);
3302286441Srpaulo		if (k == NULL) {
3303286441Srpaulo			m_freem(m);
3304286441Srpaulo			return (ENOBUFS);
3305286441Srpaulo		}
3306286441Srpaulo		/* 802.11 header may have moved. */
3307286441Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3308286441Srpaulo	}
3309286441Srpaulo
3310286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3311286441Srpaulo		struct iwm_tx_radiotap_header *tap = &sc->sc_txtap;
3312286441Srpaulo
3313286441Srpaulo		tap->wt_flags = 0;
3314286441Srpaulo		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
3315286441Srpaulo		tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
3316286441Srpaulo		tap->wt_rate = rinfo->rate;
3317286441Srpaulo		if (k != NULL)
3318286441Srpaulo			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3319286441Srpaulo		ieee80211_radiotap_tx(vap, m);
3320286441Srpaulo	}
3321286441Srpaulo
3322286441Srpaulo
3323286441Srpaulo	totlen = m->m_pkthdr.len;
3324286441Srpaulo
3325286441Srpaulo	flags = 0;
3326286441Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3327286441Srpaulo		flags |= IWM_TX_CMD_FLG_ACK;
3328286441Srpaulo	}
3329286441Srpaulo
3330303628Ssbruno	if (type == IEEE80211_FC0_TYPE_DATA
3331286441Srpaulo	    && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
3332286441Srpaulo	    && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3333286441Srpaulo		flags |= IWM_TX_CMD_FLG_PROT_REQUIRE;
3334286441Srpaulo	}
3335286441Srpaulo
3336286441Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3337286441Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3338286441Srpaulo		tx->sta_id = sc->sc_aux_sta.sta_id;
3339286441Srpaulo	else
3340286441Srpaulo		tx->sta_id = IWM_STATION_ID;
3341286441Srpaulo
3342286441Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3343286441Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3344286441Srpaulo
3345286441Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3346303628Ssbruno		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
3347303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC);
3348303628Ssbruno		} else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) {
3349303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3350303628Ssbruno		} else {
3351303628Ssbruno			tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT);
3352303628Ssbruno		}
3353286441Srpaulo	} else {
3354303628Ssbruno		tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE);
3355286441Srpaulo	}
3356286441Srpaulo
3357286441Srpaulo	if (hdrlen & 3) {
3358286441Srpaulo		/* First segment length must be a multiple of 4. */
3359286441Srpaulo		flags |= IWM_TX_CMD_FLG_MH_PAD;
3360286441Srpaulo		pad = 4 - (hdrlen & 3);
3361286441Srpaulo	} else
3362286441Srpaulo		pad = 0;
3363286441Srpaulo
3364286441Srpaulo	tx->driver_txop = 0;
3365286441Srpaulo	tx->next_frame_len = 0;
3366286441Srpaulo
3367286441Srpaulo	tx->len = htole16(totlen);
3368286441Srpaulo	tx->tid_tspec = tid;
3369286441Srpaulo	tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE);
3370286441Srpaulo
3371286441Srpaulo	/* Set physical address of "scratch area". */
3372286441Srpaulo	tx->dram_lsb_ptr = htole32(data->scratch_paddr);
3373286441Srpaulo	tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr);
3374286441Srpaulo
3375286441Srpaulo	/* Copy 802.11 header in TX command. */
3376286441Srpaulo	memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen);
3377286441Srpaulo
3378286441Srpaulo	flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL;
3379286441Srpaulo
3380286441Srpaulo	tx->sec_ctl = 0;
3381286441Srpaulo	tx->tx_flags |= htole32(flags);
3382286441Srpaulo
3383286441Srpaulo	/* Trim 802.11 header. */
3384286441Srpaulo	m_adj(m, hdrlen);
3385286441Srpaulo	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3386286441Srpaulo	    segs, &nsegs, BUS_DMA_NOWAIT);
3387286441Srpaulo	if (error != 0) {
3388286441Srpaulo		if (error != EFBIG) {
3389286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3390286441Srpaulo			    error);
3391286441Srpaulo			m_freem(m);
3392286441Srpaulo			return error;
3393286441Srpaulo		}
3394286441Srpaulo		/* Too many DMA segments, linearize mbuf. */
3395293119Savos		m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2);
3396286441Srpaulo		if (m1 == NULL) {
3397293119Savos			device_printf(sc->sc_dev,
3398293119Savos			    "%s: could not defrag mbuf\n", __func__);
3399286441Srpaulo			m_freem(m);
3400293119Savos			return (ENOBUFS);
3401286441Srpaulo		}
3402286441Srpaulo		m = m1;
3403293119Savos
3404286441Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3405286441Srpaulo		    segs, &nsegs, BUS_DMA_NOWAIT);
3406286441Srpaulo		if (error != 0) {
3407286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
3408286441Srpaulo			    error);
3409286441Srpaulo			m_freem(m);
3410286441Srpaulo			return error;
3411286441Srpaulo		}
3412286441Srpaulo	}
3413286441Srpaulo	data->m = m;
3414286441Srpaulo	data->in = in;
3415286441Srpaulo	data->done = 0;
3416286441Srpaulo
3417286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3418286441Srpaulo	    "sending txd %p, in %p\n", data, data->in);
3419286441Srpaulo	KASSERT(data->in != NULL, ("node is NULL"));
3420286441Srpaulo
3421286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3422303628Ssbruno	    "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n",
3423298611Sadrian	    ring->qid, ring->cur, totlen, nsegs,
3424298611Sadrian	    le32toh(tx->tx_flags),
3425298611Sadrian	    le32toh(tx->rate_n_flags),
3426303628Ssbruno	    tx->initial_rate_index
3427298611Sadrian	    );
3428286441Srpaulo
3429286441Srpaulo	/* Fill TX descriptor. */
3430286441Srpaulo	desc->num_tbs = 2 + nsegs;
3431286441Srpaulo
3432286441Srpaulo	desc->tbs[0].lo = htole32(data->cmd_paddr);
3433286441Srpaulo	desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3434286441Srpaulo	    (TB0_SIZE << 4);
3435286441Srpaulo	desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE);
3436286441Srpaulo	desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
3437286441Srpaulo	    ((sizeof(struct iwm_cmd_header) + sizeof(*tx)
3438286441Srpaulo	      + hdrlen + pad - TB0_SIZE) << 4);
3439286441Srpaulo
3440286441Srpaulo	/* Other DMA segments are for data payload. */
3441286441Srpaulo	for (i = 0; i < nsegs; i++) {
3442286441Srpaulo		seg = &segs[i];
3443286441Srpaulo		desc->tbs[i+2].lo = htole32(seg->ds_addr);
3444286441Srpaulo		desc->tbs[i+2].hi_n_len = \
3445286441Srpaulo		    htole16(iwm_get_dma_hi_addr(seg->ds_addr))
3446286441Srpaulo		    | ((seg->ds_len) << 4);
3447286441Srpaulo	}
3448286441Srpaulo
3449286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map,
3450286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3451286441Srpaulo	bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
3452286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3453286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3454286441Srpaulo	    BUS_DMASYNC_PREWRITE);
3455286441Srpaulo
3456286441Srpaulo#if 0
3457286441Srpaulo	iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len));
3458286441Srpaulo#endif
3459286441Srpaulo
3460286441Srpaulo	/* Kick TX ring. */
3461286441Srpaulo	ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
3462286441Srpaulo	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3463286441Srpaulo
3464286441Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3465286441Srpaulo	if (++ring->queued > IWM_TX_RING_HIMARK) {
3466286441Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3467286441Srpaulo	}
3468286441Srpaulo
3469286441Srpaulo	return 0;
3470286441Srpaulo}
3471286441Srpaulo
3472286441Srpaulostatic int
3473286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3474286441Srpaulo    const struct ieee80211_bpf_params *params)
3475286441Srpaulo{
3476286441Srpaulo	struct ieee80211com *ic = ni->ni_ic;
3477286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
3478286441Srpaulo	int error = 0;
3479286441Srpaulo
3480286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3481286441Srpaulo	    "->%s begin\n", __func__);
3482286441Srpaulo
3483287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
3484286441Srpaulo		m_freem(m);
3485286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
3486286441Srpaulo		    "<-%s not RUNNING\n", __func__);
3487286441Srpaulo		return (ENETDOWN);
3488286441Srpaulo        }
3489286441Srpaulo
3490286441Srpaulo	IWM_LOCK(sc);
3491286441Srpaulo	/* XXX fix this */
3492286441Srpaulo        if (params == NULL) {
3493286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3494286441Srpaulo	} else {
3495286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
3496286441Srpaulo	}
3497286441Srpaulo	sc->sc_tx_timer = 5;
3498286441Srpaulo	IWM_UNLOCK(sc);
3499286441Srpaulo
3500286441Srpaulo        return (error);
3501286441Srpaulo}
3502286441Srpaulo
3503286441Srpaulo/*
3504286441Srpaulo * mvm/tx.c
3505286441Srpaulo */
3506286441Srpaulo
3507286441Srpaulo/*
3508286441Srpaulo * Note that there are transports that buffer frames before they reach
3509286441Srpaulo * the firmware. This means that after flush_tx_path is called, the
3510286441Srpaulo * queue might not be empty. The race-free way to handle this is to:
3511286441Srpaulo * 1) set the station as draining
3512286441Srpaulo * 2) flush the Tx path
3513286441Srpaulo * 3) wait for the transport queues to be empty
3514286441Srpaulo */
3515286441Srpauloint
3516330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags)
3517286441Srpaulo{
3518330154Seadler	int ret;
3519286441Srpaulo	struct iwm_tx_path_flush_cmd flush_cmd = {
3520286441Srpaulo		.queues_ctl = htole32(tfd_msk),
3521286441Srpaulo		.flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH),
3522286441Srpaulo	};
3523286441Srpaulo
3524330154Seadler	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags,
3525286441Srpaulo	    sizeof(flush_cmd), &flush_cmd);
3526286441Srpaulo	if (ret)
3527286441Srpaulo                device_printf(sc->sc_dev,
3528286441Srpaulo		    "Flushing tx queue failed: %d\n", ret);
3529286441Srpaulo	return ret;
3530286441Srpaulo}
3531286441Srpaulo
3532286441Srpaulo/*
3533286441Srpaulo * BEGIN mvm/sta.c
3534286441Srpaulo */
3535286441Srpaulo
3536286441Srpaulostatic int
3537286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc,
3538303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status)
3539286441Srpaulo{
3540303628Ssbruno	return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd),
3541303628Ssbruno	    cmd, status);
3542286441Srpaulo}
3543286441Srpaulo
3544286441Srpaulo/* send station add/update command to firmware */
3545286441Srpaulostatic int
3546286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update)
3547286441Srpaulo{
3548303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 add_sta_cmd;
3549286441Srpaulo	int ret;
3550286441Srpaulo	uint32_t status;
3551286441Srpaulo
3552286441Srpaulo	memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
3553286441Srpaulo
3554286441Srpaulo	add_sta_cmd.sta_id = IWM_STATION_ID;
3555286441Srpaulo	add_sta_cmd.mac_id_n_color
3556286441Srpaulo	    = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID,
3557286441Srpaulo	        IWM_DEFAULT_COLOR));
3558286441Srpaulo	if (!update) {
3559303628Ssbruno		int ac;
3560303628Ssbruno		for (ac = 0; ac < WME_NUM_AC; ac++) {
3561303628Ssbruno			add_sta_cmd.tfd_queue_msk |=
3562303628Ssbruno			    htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
3563303628Ssbruno		}
3564286441Srpaulo		IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
3565286441Srpaulo	}
3566286441Srpaulo	add_sta_cmd.add_modify = update ? 1 : 0;
3567286441Srpaulo	add_sta_cmd.station_flags_msk
3568286441Srpaulo	    |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK);
3569303628Ssbruno	add_sta_cmd.tid_disable_tx = htole16(0xffff);
3570303628Ssbruno	if (update)
3571303628Ssbruno		add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX);
3572286441Srpaulo
3573286441Srpaulo	status = IWM_ADD_STA_SUCCESS;
3574286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status);
3575286441Srpaulo	if (ret)
3576286441Srpaulo		return ret;
3577286441Srpaulo
3578286441Srpaulo	switch (status) {
3579286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3580286441Srpaulo		break;
3581286441Srpaulo	default:
3582286441Srpaulo		ret = EIO;
3583286441Srpaulo		device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
3584286441Srpaulo		break;
3585286441Srpaulo	}
3586286441Srpaulo
3587286441Srpaulo	return ret;
3588286441Srpaulo}
3589286441Srpaulo
3590286441Srpaulostatic int
3591286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
3592286441Srpaulo{
3593301192Sadrian	return iwm_mvm_sta_send_to_fw(sc, in, 0);
3594286441Srpaulo}
3595286441Srpaulo
3596286441Srpaulostatic int
3597286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
3598286441Srpaulo{
3599286441Srpaulo	return iwm_mvm_sta_send_to_fw(sc, in, 1);
3600286441Srpaulo}
3601286441Srpaulo
3602286441Srpaulostatic int
3603286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta,
3604286441Srpaulo	const uint8_t *addr, uint16_t mac_id, uint16_t color)
3605286441Srpaulo{
3606303628Ssbruno	struct iwm_mvm_add_sta_cmd_v7 cmd;
3607286441Srpaulo	int ret;
3608286441Srpaulo	uint32_t status;
3609286441Srpaulo
3610286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3611286441Srpaulo	cmd.sta_id = sta->sta_id;
3612286441Srpaulo	cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color));
3613286441Srpaulo
3614286441Srpaulo	cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk);
3615303628Ssbruno	cmd.tid_disable_tx = htole16(0xffff);
3616286441Srpaulo
3617286441Srpaulo	if (addr)
3618286441Srpaulo		IEEE80211_ADDR_COPY(cmd.addr, addr);
3619286441Srpaulo
3620286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status);
3621286441Srpaulo	if (ret)
3622286441Srpaulo		return ret;
3623286441Srpaulo
3624286441Srpaulo	switch (status) {
3625286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3626286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
3627286441Srpaulo		    "%s: Internal station added.\n", __func__);
3628286441Srpaulo		return 0;
3629286441Srpaulo	default:
3630286441Srpaulo		device_printf(sc->sc_dev,
3631286441Srpaulo		    "%s: Add internal station failed, status=0x%x\n",
3632286441Srpaulo		    __func__, status);
3633286441Srpaulo		ret = EIO;
3634286441Srpaulo		break;
3635286441Srpaulo	}
3636286441Srpaulo	return ret;
3637286441Srpaulo}
3638286441Srpaulo
3639286441Srpaulostatic int
3640286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc)
3641286441Srpaulo{
3642286441Srpaulo	int ret;
3643286441Srpaulo
3644303628Ssbruno	sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID;
3645303628Ssbruno	sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE);
3646286441Srpaulo
3647303628Ssbruno	ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST);
3648303628Ssbruno	if (ret)
3649303628Ssbruno		return ret;
3650303628Ssbruno
3651286441Srpaulo	ret = iwm_mvm_add_int_sta_common(sc,
3652286441Srpaulo	    &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0);
3653286441Srpaulo
3654286441Srpaulo	if (ret)
3655286441Srpaulo		memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
3656286441Srpaulo	return ret;
3657286441Srpaulo}
3658286441Srpaulo
3659286441Srpaulo/*
3660286441Srpaulo * END mvm/sta.c
3661286441Srpaulo */
3662286441Srpaulo
3663286441Srpaulo/*
3664286441Srpaulo * BEGIN mvm/quota.c
3665286441Srpaulo */
3666286441Srpaulo
3667286441Srpaulostatic int
3668286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in)
3669286441Srpaulo{
3670286441Srpaulo	struct iwm_time_quota_cmd cmd;
3671286441Srpaulo	int i, idx, ret, num_active_macs, quota, quota_rem;
3672286441Srpaulo	int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, };
3673286441Srpaulo	int n_ifs[IWM_MAX_BINDINGS] = {0, };
3674286441Srpaulo	uint16_t id;
3675286441Srpaulo
3676286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3677286441Srpaulo
3678286441Srpaulo	/* currently, PHY ID == binding ID */
3679286441Srpaulo	if (in) {
3680286441Srpaulo		id = in->in_phyctxt->id;
3681286441Srpaulo		KASSERT(id < IWM_MAX_BINDINGS, ("invalid id"));
3682286441Srpaulo		colors[id] = in->in_phyctxt->color;
3683286441Srpaulo
3684286441Srpaulo		if (1)
3685286441Srpaulo			n_ifs[id] = 1;
3686286441Srpaulo	}
3687286441Srpaulo
3688286441Srpaulo	/*
3689286441Srpaulo	 * The FW's scheduling session consists of
3690286441Srpaulo	 * IWM_MVM_MAX_QUOTA fragments. Divide these fragments
3691286441Srpaulo	 * equally between all the bindings that require quota
3692286441Srpaulo	 */
3693286441Srpaulo	num_active_macs = 0;
3694286441Srpaulo	for (i = 0; i < IWM_MAX_BINDINGS; i++) {
3695286441Srpaulo		cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID);
3696286441Srpaulo		num_active_macs += n_ifs[i];
3697286441Srpaulo	}
3698286441Srpaulo
3699286441Srpaulo	quota = 0;
3700286441Srpaulo	quota_rem = 0;
3701286441Srpaulo	if (num_active_macs) {
3702286441Srpaulo		quota = IWM_MVM_MAX_QUOTA / num_active_macs;
3703286441Srpaulo		quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs;
3704286441Srpaulo	}
3705286441Srpaulo
3706286441Srpaulo	for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) {
3707286441Srpaulo		if (colors[i] < 0)
3708286441Srpaulo			continue;
3709286441Srpaulo
3710286441Srpaulo		cmd.quotas[idx].id_and_color =
3711286441Srpaulo			htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i]));
3712286441Srpaulo
3713286441Srpaulo		if (n_ifs[i] <= 0) {
3714286441Srpaulo			cmd.quotas[idx].quota = htole32(0);
3715286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3716286441Srpaulo		} else {
3717286441Srpaulo			cmd.quotas[idx].quota = htole32(quota * n_ifs[i]);
3718286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3719286441Srpaulo		}
3720286441Srpaulo		idx++;
3721286441Srpaulo	}
3722286441Srpaulo
3723286441Srpaulo	/* Give the remainder of the session to the first binding */
3724286441Srpaulo	cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem);
3725286441Srpaulo
3726286441Srpaulo	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC,
3727286441Srpaulo	    sizeof(cmd), &cmd);
3728286441Srpaulo	if (ret)
3729286441Srpaulo		device_printf(sc->sc_dev,
3730286441Srpaulo		    "%s: Failed to send quota: %d\n", __func__, ret);
3731286441Srpaulo	return ret;
3732286441Srpaulo}
3733286441Srpaulo
3734286441Srpaulo/*
3735286441Srpaulo * END mvm/quota.c
3736286441Srpaulo */
3737286441Srpaulo
3738286441Srpaulo/*
3739286441Srpaulo * ieee80211 routines
3740286441Srpaulo */
3741286441Srpaulo
3742286441Srpaulo/*
3743286441Srpaulo * Change to AUTH state in 80211 state machine.  Roughly matches what
3744286441Srpaulo * Linux does in bss_info_changed().
3745286441Srpaulo */
3746286441Srpaulostatic int
3747286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc)
3748286441Srpaulo{
3749286441Srpaulo	struct ieee80211_node *ni;
3750286441Srpaulo	struct iwm_node *in;
3751286441Srpaulo	struct iwm_vap *iv = IWM_VAP(vap);
3752286441Srpaulo	uint32_t duration;
3753286441Srpaulo	int error;
3754286441Srpaulo
3755286441Srpaulo	/*
3756286441Srpaulo	 * XXX i have a feeling that the vap node is being
3757286441Srpaulo	 * freed from underneath us. Grr.
3758286441Srpaulo	 */
3759286441Srpaulo	ni = ieee80211_ref_node(vap->iv_bss);
3760293099Savos	in = IWM_NODE(ni);
3761286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE,
3762286441Srpaulo	    "%s: called; vap=%p, bss ni=%p\n",
3763286441Srpaulo	    __func__,
3764286441Srpaulo	    vap,
3765286441Srpaulo	    ni);
3766286441Srpaulo
3767286441Srpaulo	in->in_assoc = 0;
3768286441Srpaulo
3769303628Ssbruno	error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON);
3770303628Ssbruno	if (error != 0)
3771303628Ssbruno		return error;
3772303628Ssbruno
3773286441Srpaulo	error = iwm_allow_mcast(vap, sc);
3774286441Srpaulo	if (error) {
3775286441Srpaulo		device_printf(sc->sc_dev,
3776286441Srpaulo		    "%s: failed to set multicast\n", __func__);
3777286441Srpaulo		goto out;
3778286441Srpaulo	}
3779286441Srpaulo
3780286441Srpaulo	/*
3781286441Srpaulo	 * This is where it deviates from what Linux does.
3782286441Srpaulo	 *
3783286441Srpaulo	 * Linux iwlwifi doesn't reset the nic each time, nor does it
3784286441Srpaulo	 * call ctxt_add() here.  Instead, it adds it during vap creation,
3785303628Ssbruno	 * and always does a mac_ctx_changed().
3786286441Srpaulo	 *
3787286441Srpaulo	 * The openbsd port doesn't attempt to do that - it reset things
3788286441Srpaulo	 * at odd states and does the add here.
3789286441Srpaulo	 *
3790286441Srpaulo	 * So, until the state handling is fixed (ie, we never reset
3791286441Srpaulo	 * the NIC except for a firmware failure, which should drag
3792286441Srpaulo	 * the NIC back to IDLE, re-setup and re-add all the mac/phy
3793286441Srpaulo	 * contexts that are required), let's do a dirty hack here.
3794286441Srpaulo	 */
3795286441Srpaulo	if (iv->is_uploaded) {
3796286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
3797286441Srpaulo			device_printf(sc->sc_dev,
3798298582Sadrian			    "%s: failed to update MAC\n", __func__);
3799286441Srpaulo			goto out;
3800286441Srpaulo		}
3801298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3802298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3803298582Sadrian			device_printf(sc->sc_dev,
3804298582Sadrian			    "%s: failed update phy ctxt\n", __func__);
3805298582Sadrian			goto out;
3806298582Sadrian		}
3807298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3808298582Sadrian
3809298582Sadrian		if ((error = iwm_mvm_binding_update(sc, in)) != 0) {
3810298582Sadrian			device_printf(sc->sc_dev,
3811298582Sadrian			    "%s: binding update cmd\n", __func__);
3812298582Sadrian			goto out;
3813298582Sadrian		}
3814298582Sadrian		if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
3815298582Sadrian			device_printf(sc->sc_dev,
3816298582Sadrian			    "%s: failed to update sta\n", __func__);
3817298582Sadrian			goto out;
3818298582Sadrian		}
3819286441Srpaulo	} else {
3820286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) {
3821286441Srpaulo			device_printf(sc->sc_dev,
3822286441Srpaulo			    "%s: failed to add MAC\n", __func__);
3823286441Srpaulo			goto out;
3824286441Srpaulo		}
3825298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3826298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3827286441Srpaulo			device_printf(sc->sc_dev,
3828298582Sadrian			    "%s: failed add phy ctxt!\n", __func__);
3829286441Srpaulo			error = ETIMEDOUT;
3830286441Srpaulo			goto out;
3831298582Sadrian		}
3832298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3833298582Sadrian
3834298582Sadrian		if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) {
3835286441Srpaulo			device_printf(sc->sc_dev,
3836298582Sadrian			    "%s: binding add cmd\n", __func__);
3837286441Srpaulo			goto out;
3838286441Srpaulo		}
3839298582Sadrian		if ((error = iwm_mvm_add_sta(sc, in)) != 0) {
3840298582Sadrian			device_printf(sc->sc_dev,
3841298582Sadrian			    "%s: failed to add sta\n", __func__);
3842298582Sadrian			goto out;
3843298582Sadrian		}
3844286441Srpaulo	}
3845298582Sadrian
3846298582Sadrian	/*
3847298582Sadrian	 * Prevent the FW from wandering off channel during association
3848298582Sadrian	 * by "protecting" the session with a time event.
3849298582Sadrian	 */
3850298582Sadrian	/* XXX duration is in units of TU, not MS */
3851298582Sadrian	duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
3852298582Sadrian	iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */);
3853298582Sadrian	DELAY(100);
3854298582Sadrian
3855286441Srpaulo	error = 0;
3856286441Srpauloout:
3857286441Srpaulo	ieee80211_free_node(ni);
3858286441Srpaulo	return (error);
3859286441Srpaulo}
3860286441Srpaulo
3861286441Srpaulostatic int
3862286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc)
3863286441Srpaulo{
3864293099Savos	struct iwm_node *in = IWM_NODE(vap->iv_bss);
3865286441Srpaulo	int error;
3866286441Srpaulo
3867286441Srpaulo	if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
3868286441Srpaulo		device_printf(sc->sc_dev,
3869286441Srpaulo		    "%s: failed to update STA\n", __func__);
3870286441Srpaulo		return error;
3871286441Srpaulo	}
3872286441Srpaulo
3873286441Srpaulo	in->in_assoc = 1;
3874286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
3875286441Srpaulo		device_printf(sc->sc_dev,
3876286441Srpaulo		    "%s: failed to update MAC\n", __func__);
3877286441Srpaulo		return error;
3878286441Srpaulo	}
3879286441Srpaulo
3880286441Srpaulo	return 0;
3881286441Srpaulo}
3882286441Srpaulo
3883286441Srpaulostatic int
3884286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in)
3885286441Srpaulo{
3886330154Seadler	uint32_t tfd_msk;
3887330154Seadler
3888286441Srpaulo	/*
3889286441Srpaulo	 * Ok, so *technically* the proper set of calls for going
3890286441Srpaulo	 * from RUN back to SCAN is:
3891286441Srpaulo	 *
3892286441Srpaulo	 * iwm_mvm_power_mac_disable(sc, in);
3893286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
3894286441Srpaulo	 * iwm_mvm_rm_sta(sc, in);
3895286441Srpaulo	 * iwm_mvm_update_quotas(sc, NULL);
3896286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
3897286441Srpaulo	 * iwm_mvm_binding_remove_vif(sc, in);
3898286441Srpaulo	 * iwm_mvm_mac_ctxt_remove(sc, in);
3899286441Srpaulo	 *
3900286441Srpaulo	 * However, that freezes the device not matter which permutations
3901286441Srpaulo	 * and modifications are attempted.  Obviously, this driver is missing
3902286441Srpaulo	 * something since it works in the Linux driver, but figuring out what
3903286441Srpaulo	 * is missing is a little more complicated.  Now, since we're going
3904286441Srpaulo	 * back to nothing anyway, we'll just do a complete device reset.
3905286441Srpaulo	 * Up your's, device!
3906286441Srpaulo	 */
3907330154Seadler	/*
3908330154Seadler	 * Just using 0xf for the queues mask is fine as long as we only
3909330154Seadler	 * get here from RUN state.
3910330154Seadler	 */
3911330154Seadler	tfd_msk = 0xf;
3912330154Seadler	mbufq_drain(&sc->sc_snd);
3913330154Seadler	iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC);
3914330154Seadler	/*
3915330154Seadler	 * We seem to get away with just synchronously sending the
3916330154Seadler	 * IWM_TXPATH_FLUSH command.
3917330154Seadler	 */
3918330154Seadler//	iwm_trans_wait_tx_queue_empty(sc, tfd_msk);
3919286441Srpaulo	iwm_stop_device(sc);
3920286441Srpaulo	iwm_init_hw(sc);
3921286441Srpaulo	if (in)
3922286441Srpaulo		in->in_assoc = 0;
3923286441Srpaulo	return 0;
3924286441Srpaulo
3925286441Srpaulo#if 0
3926286441Srpaulo	int error;
3927286441Srpaulo
3928286441Srpaulo	iwm_mvm_power_mac_disable(sc, in);
3929286441Srpaulo
3930286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
3931286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error);
3932286441Srpaulo		return error;
3933286441Srpaulo	}
3934286441Srpaulo
3935286441Srpaulo	if ((error = iwm_mvm_rm_sta(sc, in)) != 0) {
3936286441Srpaulo		device_printf(sc->sc_dev, "sta remove fail %d\n", error);
3937286441Srpaulo		return error;
3938286441Srpaulo	}
3939286441Srpaulo	error = iwm_mvm_rm_sta(sc, in);
3940286441Srpaulo	in->in_assoc = 0;
3941286441Srpaulo	iwm_mvm_update_quotas(sc, NULL);
3942286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
3943286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error);
3944286441Srpaulo		return error;
3945286441Srpaulo	}
3946286441Srpaulo	iwm_mvm_binding_remove_vif(sc, in);
3947286441Srpaulo
3948286441Srpaulo	iwm_mvm_mac_ctxt_remove(sc, in);
3949286441Srpaulo
3950286441Srpaulo	return error;
3951286441Srpaulo#endif
3952286441Srpaulo}
3953286441Srpaulo
3954286441Srpaulostatic struct ieee80211_node *
3955286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
3956286441Srpaulo{
3957286441Srpaulo	return malloc(sizeof (struct iwm_node), M_80211_NODE,
3958286441Srpaulo	    M_NOWAIT | M_ZERO);
3959286441Srpaulo}
3960286441Srpaulo
3961286441Srpaulostatic void
3962286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
3963286441Srpaulo{
3964286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
3965286441Srpaulo	struct iwm_lq_cmd *lq = &in->in_lq;
3966286441Srpaulo	int nrates = ni->ni_rates.rs_nrates;
3967286441Srpaulo	int i, ridx, tab = 0;
3968286441Srpaulo	int txant = 0;
3969286441Srpaulo
3970286441Srpaulo	if (nrates > nitems(lq->rs_table)) {
3971286441Srpaulo		device_printf(sc->sc_dev,
3972286441Srpaulo		    "%s: node supports %d rates, driver handles "
3973286441Srpaulo		    "only %zu\n", __func__, nrates, nitems(lq->rs_table));
3974286441Srpaulo		return;
3975286441Srpaulo	}
3976294248Sadrian	if (nrates == 0) {
3977294248Sadrian		device_printf(sc->sc_dev,
3978294248Sadrian		    "%s: node supports 0 rates, odd!\n", __func__);
3979294248Sadrian		return;
3980294248Sadrian	}
3981286441Srpaulo
3982286441Srpaulo	/*
3983286441Srpaulo	 * XXX .. and most of iwm_node is not initialised explicitly;
3984286441Srpaulo	 * it's all just 0x0 passed to the firmware.
3985286441Srpaulo	 */
3986286441Srpaulo
3987286441Srpaulo	/* first figure out which rates we should support */
3988286441Srpaulo	/* XXX TODO: this isn't 11n aware /at all/ */
3989286441Srpaulo	memset(&in->in_ridx, -1, sizeof(in->in_ridx));
3990286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3991286441Srpaulo	    "%s: nrates=%d\n", __func__, nrates);
3992286441Srpaulo
3993294248Sadrian	/*
3994294248Sadrian	 * Loop over nrates and populate in_ridx from the highest
3995294248Sadrian	 * rate to the lowest rate.  Remember, in_ridx[] has
3996294248Sadrian	 * IEEE80211_RATE_MAXSIZE entries!
3997294248Sadrian	 */
3998294248Sadrian	for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) {
3999294248Sadrian		int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL;
4000294248Sadrian
4001286441Srpaulo		/* Map 802.11 rate to HW rate index. */
4002286441Srpaulo		for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
4003286441Srpaulo			if (iwm_rates[ridx].rate == rate)
4004286441Srpaulo				break;
4005286441Srpaulo		if (ridx > IWM_RIDX_MAX) {
4006286441Srpaulo			device_printf(sc->sc_dev,
4007286441Srpaulo			    "%s: WARNING: device rate for %d not found!\n",
4008286441Srpaulo			    __func__, rate);
4009286441Srpaulo		} else {
4010286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4011286441Srpaulo			    "%s: rate: i: %d, rate=%d, ridx=%d\n",
4012286441Srpaulo			    __func__,
4013286441Srpaulo			    i,
4014286441Srpaulo			    rate,
4015286441Srpaulo			    ridx);
4016286441Srpaulo			in->in_ridx[i] = ridx;
4017286441Srpaulo		}
4018286441Srpaulo	}
4019286441Srpaulo
4020286441Srpaulo	/* then construct a lq_cmd based on those */
4021286441Srpaulo	memset(lq, 0, sizeof(*lq));
4022286441Srpaulo	lq->sta_id = IWM_STATION_ID;
4023286441Srpaulo
4024303628Ssbruno	/* For HT, always enable RTS/CTS to avoid excessive retries. */
4025303628Ssbruno	if (ni->ni_flags & IEEE80211_NODE_HT)
4026303628Ssbruno		lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK;
4027303628Ssbruno
4028286441Srpaulo	/*
4029286441Srpaulo	 * are these used? (we don't do SISO or MIMO)
4030286441Srpaulo	 * need to set them to non-zero, though, or we get an error.
4031286441Srpaulo	 */
4032286441Srpaulo	lq->single_stream_ant_msk = 1;
4033286441Srpaulo	lq->dual_stream_ant_msk = 1;
4034286441Srpaulo
4035286441Srpaulo	/*
4036286441Srpaulo	 * Build the actual rate selection table.
4037286441Srpaulo	 * The lowest bits are the rates.  Additionally,
4038286441Srpaulo	 * CCK needs bit 9 to be set.  The rest of the bits
4039286441Srpaulo	 * we add to the table select the tx antenna
4040286441Srpaulo	 * Note that we add the rates in the highest rate first
4041286441Srpaulo	 * (opposite of ni_rates).
4042286441Srpaulo	 */
4043286441Srpaulo	/*
4044286441Srpaulo	 * XXX TODO: this should be looping over the min of nrates
4045286441Srpaulo	 * and LQ_MAX_RETRY_NUM.  Sigh.
4046286441Srpaulo	 */
4047286441Srpaulo	for (i = 0; i < nrates; i++) {
4048286441Srpaulo		int nextant;
4049286441Srpaulo
4050286441Srpaulo		if (txant == 0)
4051303628Ssbruno			txant = iwm_fw_valid_tx_ant(sc);
4052286441Srpaulo		nextant = 1<<(ffs(txant)-1);
4053286441Srpaulo		txant &= ~nextant;
4054286441Srpaulo
4055286441Srpaulo		/*
4056286441Srpaulo		 * Map the rate id into a rate index into
4057286441Srpaulo		 * our hardware table containing the
4058286441Srpaulo		 * configuration to use for this rate.
4059286441Srpaulo		 */
4060294248Sadrian		ridx = in->in_ridx[i];
4061286441Srpaulo		tab = iwm_rates[ridx].plcp;
4062286441Srpaulo		tab |= nextant << IWM_RATE_MCS_ANT_POS;
4063286441Srpaulo		if (IWM_RIDX_IS_CCK(ridx))
4064286441Srpaulo			tab |= IWM_RATE_MCS_CCK_MSK;
4065286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
4066286441Srpaulo		    "station rate i=%d, rate=%d, hw=%x\n",
4067286441Srpaulo		    i, iwm_rates[ridx].rate, tab);
4068286441Srpaulo		lq->rs_table[i] = htole32(tab);
4069286441Srpaulo	}
4070286441Srpaulo	/* then fill the rest with the lowest possible rate */
4071286441Srpaulo	for (i = nrates; i < nitems(lq->rs_table); i++) {
4072286441Srpaulo		KASSERT(tab != 0, ("invalid tab"));
4073286441Srpaulo		lq->rs_table[i] = htole32(tab);
4074286441Srpaulo	}
4075286441Srpaulo}
4076286441Srpaulo
4077286441Srpaulostatic int
4078286441Srpauloiwm_media_change(struct ifnet *ifp)
4079286441Srpaulo{
4080287197Sglebius	struct ieee80211vap *vap = ifp->if_softc;
4081287197Sglebius	struct ieee80211com *ic = vap->iv_ic;
4082287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
4083286441Srpaulo	int error;
4084286441Srpaulo
4085286441Srpaulo	error = ieee80211_media_change(ifp);
4086286441Srpaulo	if (error != ENETRESET)
4087286441Srpaulo		return error;
4088286441Srpaulo
4089287197Sglebius	IWM_LOCK(sc);
4090287197Sglebius	if (ic->ic_nrunning > 0) {
4091287197Sglebius		iwm_stop(sc);
4092286441Srpaulo		iwm_init(sc);
4093286441Srpaulo	}
4094287197Sglebius	IWM_UNLOCK(sc);
4095286441Srpaulo	return error;
4096286441Srpaulo}
4097286441Srpaulo
4098286441Srpaulo
4099286441Srpaulostatic int
4100286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
4101286441Srpaulo{
4102286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
4103286441Srpaulo	struct ieee80211com *ic = vap->iv_ic;
4104286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
4105286441Srpaulo	struct iwm_node *in;
4106286441Srpaulo	int error;
4107286441Srpaulo
4108286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4109286441Srpaulo	    "switching state %s -> %s\n",
4110286441Srpaulo	    ieee80211_state_name[vap->iv_state],
4111286441Srpaulo	    ieee80211_state_name[nstate]);
4112286441Srpaulo	IEEE80211_UNLOCK(ic);
4113286441Srpaulo	IWM_LOCK(sc);
4114301187Sadrian
4115301187Sadrian	if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state)
4116301187Sadrian		iwm_led_blink_stop(sc);
4117301187Sadrian
4118286441Srpaulo	/* disable beacon filtering if we're hopping out of RUN */
4119286441Srpaulo	if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) {
4120286441Srpaulo		iwm_mvm_disable_beacon_filter(sc);
4121286441Srpaulo
4122293099Savos		if (((in = IWM_NODE(vap->iv_bss)) != NULL))
4123286441Srpaulo			in->in_assoc = 0;
4124286441Srpaulo
4125330154Seadler		if (nstate == IEEE80211_S_INIT) {
4126330154Seadler			IWM_UNLOCK(sc);
4127330154Seadler			IEEE80211_LOCK(ic);
4128330154Seadler			error = ivp->iv_newstate(vap, nstate, arg);
4129330154Seadler			IEEE80211_UNLOCK(ic);
4130330154Seadler			IWM_LOCK(sc);
4131330154Seadler			iwm_release(sc, NULL);
4132330154Seadler			IWM_UNLOCK(sc);
4133330154Seadler			IEEE80211_LOCK(ic);
4134330154Seadler			return error;
4135330154Seadler		}
4136286441Srpaulo
4137286441Srpaulo		/*
4138286441Srpaulo		 * It's impossible to directly go RUN->SCAN. If we iwm_release()
4139286441Srpaulo		 * above then the card will be completely reinitialized,
4140286441Srpaulo		 * so the driver must do everything necessary to bring the card
4141286441Srpaulo		 * from INIT to SCAN.
4142286441Srpaulo		 *
4143286441Srpaulo		 * Additionally, upon receiving deauth frame from AP,
4144286441Srpaulo		 * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH
4145286441Srpaulo		 * state. This will also fail with this driver, so bring the FSM
4146286441Srpaulo		 * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well.
4147286441Srpaulo		 *
4148286441Srpaulo		 * XXX TODO: fix this for FreeBSD!
4149286441Srpaulo		 */
4150286441Srpaulo		if (nstate == IEEE80211_S_SCAN ||
4151286441Srpaulo		    nstate == IEEE80211_S_AUTH ||
4152286441Srpaulo		    nstate == IEEE80211_S_ASSOC) {
4153286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4154286441Srpaulo			    "Force transition to INIT; MGT=%d\n", arg);
4155286441Srpaulo			IWM_UNLOCK(sc);
4156286441Srpaulo			IEEE80211_LOCK(ic);
4157303628Ssbruno			/* Always pass arg as -1 since we can't Tx right now. */
4158303628Ssbruno			/*
4159303628Ssbruno			 * XXX arg is just ignored anyway when transitioning
4160303628Ssbruno			 *     to IEEE80211_S_INIT.
4161303628Ssbruno			 */
4162303628Ssbruno			vap->iv_newstate(vap, IEEE80211_S_INIT, -1);
4163286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
4164286441Srpaulo			    "Going INIT->SCAN\n");
4165286441Srpaulo			nstate = IEEE80211_S_SCAN;
4166286441Srpaulo			IEEE80211_UNLOCK(ic);
4167286441Srpaulo			IWM_LOCK(sc);
4168286441Srpaulo		}
4169286441Srpaulo	}
4170286441Srpaulo
4171286441Srpaulo	switch (nstate) {
4172286441Srpaulo	case IEEE80211_S_INIT:
4173286441Srpaulo		break;
4174286441Srpaulo
4175286441Srpaulo	case IEEE80211_S_AUTH:
4176286441Srpaulo		if ((error = iwm_auth(vap, sc)) != 0) {
4177286441Srpaulo			device_printf(sc->sc_dev,
4178286441Srpaulo			    "%s: could not move to auth state: %d\n",
4179286441Srpaulo			    __func__, error);
4180286441Srpaulo			break;
4181286441Srpaulo		}
4182286441Srpaulo		break;
4183286441Srpaulo
4184286441Srpaulo	case IEEE80211_S_ASSOC:
4185286441Srpaulo		if ((error = iwm_assoc(vap, sc)) != 0) {
4186286441Srpaulo			device_printf(sc->sc_dev,
4187286441Srpaulo			    "%s: failed to associate: %d\n", __func__,
4188286441Srpaulo			    error);
4189286441Srpaulo			break;
4190286441Srpaulo		}
4191286441Srpaulo		break;
4192286441Srpaulo
4193286441Srpaulo	case IEEE80211_S_RUN:
4194286441Srpaulo	{
4195286441Srpaulo		struct iwm_host_cmd cmd = {
4196286441Srpaulo			.id = IWM_LQ_CMD,
4197286441Srpaulo			.len = { sizeof(in->in_lq), },
4198286441Srpaulo			.flags = IWM_CMD_SYNC,
4199286441Srpaulo		};
4200286441Srpaulo
4201286441Srpaulo		/* Update the association state, now we have it all */
4202286441Srpaulo		/* (eg associd comes in at this point */
4203286441Srpaulo		error = iwm_assoc(vap, sc);
4204286441Srpaulo		if (error != 0) {
4205286441Srpaulo			device_printf(sc->sc_dev,
4206286441Srpaulo			    "%s: failed to update association state: %d\n",
4207286441Srpaulo			    __func__,
4208286441Srpaulo			    error);
4209286441Srpaulo			break;
4210286441Srpaulo		}
4211286441Srpaulo
4212293099Savos		in = IWM_NODE(vap->iv_bss);
4213286441Srpaulo		iwm_mvm_power_mac_update_mode(sc, in);
4214286441Srpaulo		iwm_mvm_enable_beacon_filter(sc, in);
4215286441Srpaulo		iwm_mvm_update_quotas(sc, in);
4216286441Srpaulo		iwm_setrates(sc, in);
4217286441Srpaulo
4218286441Srpaulo		cmd.data[0] = &in->in_lq;
4219286441Srpaulo		if ((error = iwm_send_cmd(sc, &cmd)) != 0) {
4220286441Srpaulo			device_printf(sc->sc_dev,
4221286441Srpaulo			    "%s: IWM_LQ_CMD failed\n", __func__);
4222286441Srpaulo		}
4223286441Srpaulo
4224303628Ssbruno		iwm_mvm_led_enable(sc);
4225286441Srpaulo		break;
4226286441Srpaulo	}
4227286441Srpaulo
4228286441Srpaulo	default:
4229286441Srpaulo		break;
4230286441Srpaulo	}
4231286441Srpaulo	IWM_UNLOCK(sc);
4232286441Srpaulo	IEEE80211_LOCK(ic);
4233286441Srpaulo
4234286441Srpaulo	return (ivp->iv_newstate(vap, nstate, arg));
4235286441Srpaulo}
4236286441Srpaulo
4237286441Srpaulovoid
4238286441Srpauloiwm_endscan_cb(void *arg, int pending)
4239286441Srpaulo{
4240286441Srpaulo	struct iwm_softc *sc = arg;
4241287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4242286441Srpaulo
4243286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE,
4244286441Srpaulo	    "%s: scan ended\n",
4245286441Srpaulo	    __func__);
4246286441Srpaulo
4247303628Ssbruno	ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
4248303628Ssbruno}
4249303628Ssbruno
4250303628Ssbruno/*
4251303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4252303628Ssbruno * in default configuration
4253303628Ssbruno */
4254303628Ssbrunostatic const uint32_t
4255303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4256303628Ssbruno	{
4257303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
4258303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
4259303628Ssbruno	},
4260303628Ssbruno	{
4261303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF),
4262303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF)
4263303628Ssbruno	},
4264303628Ssbruno	{
4265303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER_DEF),
4266303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER_DEF)
4267303628Ssbruno	},
4268303628Ssbruno	{
4269303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER_DEF),
4270303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER_DEF)
4271303628Ssbruno	},
4272303628Ssbruno	{
4273303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER_DEF),
4274303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF)
4275303628Ssbruno	},
4276303628Ssbruno};
4277303628Ssbruno
4278303628Ssbruno/*
4279303628Ssbruno * Aging and idle timeouts for the different possible scenarios
4280303628Ssbruno * in single BSS MAC configuration.
4281303628Ssbruno */
4282303628Ssbrunostatic const uint32_t
4283303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
4284303628Ssbruno	{
4285303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER),
4286303628Ssbruno		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER)
4287303628Ssbruno	},
4288303628Ssbruno	{
4289303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER),
4290303628Ssbruno		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER)
4291303628Ssbruno	},
4292303628Ssbruno	{
4293303628Ssbruno		htole32(IWM_SF_MCAST_AGING_TIMER),
4294303628Ssbruno		htole32(IWM_SF_MCAST_IDLE_TIMER)
4295303628Ssbruno	},
4296303628Ssbruno	{
4297303628Ssbruno		htole32(IWM_SF_BA_AGING_TIMER),
4298303628Ssbruno		htole32(IWM_SF_BA_IDLE_TIMER)
4299303628Ssbruno	},
4300303628Ssbruno	{
4301303628Ssbruno		htole32(IWM_SF_TX_RE_AGING_TIMER),
4302303628Ssbruno		htole32(IWM_SF_TX_RE_IDLE_TIMER)
4303303628Ssbruno	},
4304303628Ssbruno};
4305303628Ssbruno
4306303628Ssbrunostatic void
4307303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd,
4308303628Ssbruno    struct ieee80211_node *ni)
4309303628Ssbruno{
4310303628Ssbruno	int i, j, watermark;
4311303628Ssbruno
4312303628Ssbruno	sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN);
4313303628Ssbruno
4314303628Ssbruno	/*
4315303628Ssbruno	 * If we are in association flow - check antenna configuration
4316303628Ssbruno	 * capabilities of the AP station, and choose the watermark accordingly.
4317303628Ssbruno	 */
4318303628Ssbruno	if (ni) {
4319303628Ssbruno		if (ni->ni_flags & IEEE80211_NODE_HT) {
4320303628Ssbruno#ifdef notyet
4321303628Ssbruno			if (ni->ni_rxmcs[2] != 0)
4322303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO3;
4323303628Ssbruno			else if (ni->ni_rxmcs[1] != 0)
4324303628Ssbruno				watermark = IWM_SF_W_MARK_MIMO2;
4325303628Ssbruno			else
4326303628Ssbruno#endif
4327303628Ssbruno				watermark = IWM_SF_W_MARK_SISO;
4328303628Ssbruno		} else {
4329303628Ssbruno			watermark = IWM_SF_W_MARK_LEGACY;
4330286441Srpaulo		}
4331303628Ssbruno	/* default watermark value for unassociated mode. */
4332286441Srpaulo	} else {
4333303628Ssbruno		watermark = IWM_SF_W_MARK_MIMO2;
4334286441Srpaulo	}
4335303628Ssbruno	sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark);
4336286441Srpaulo
4337303628Ssbruno	for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) {
4338303628Ssbruno		for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) {
4339303628Ssbruno			sf_cmd->long_delay_timeouts[i][j] =
4340303628Ssbruno					htole32(IWM_SF_LONG_DELAY_AGING_TIMER);
4341303628Ssbruno		}
4342286441Srpaulo	}
4343303628Ssbruno
4344303628Ssbruno	if (ni) {
4345303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout,
4346303628Ssbruno		       sizeof(iwm_sf_full_timeout));
4347303628Ssbruno	} else {
4348303628Ssbruno		memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def,
4349303628Ssbruno		       sizeof(iwm_sf_full_timeout_def));
4350303628Ssbruno	}
4351286441Srpaulo}
4352286441Srpaulo
4353286441Srpaulostatic int
4354303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state)
4355303628Ssbruno{
4356303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
4357303628Ssbruno	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4358303628Ssbruno	struct iwm_sf_cfg_cmd sf_cmd = {
4359303628Ssbruno		.state = htole32(IWM_SF_FULL_ON),
4360303628Ssbruno	};
4361303628Ssbruno	int ret = 0;
4362303628Ssbruno
4363303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000)
4364303628Ssbruno		sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF);
4365303628Ssbruno
4366303628Ssbruno	switch (new_state) {
4367303628Ssbruno	case IWM_SF_UNINIT:
4368303628Ssbruno	case IWM_SF_INIT_OFF:
4369303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL);
4370303628Ssbruno		break;
4371303628Ssbruno	case IWM_SF_FULL_ON:
4372303628Ssbruno		iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss);
4373303628Ssbruno		break;
4374303628Ssbruno	default:
4375303628Ssbruno		IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE,
4376303628Ssbruno		    "Invalid state: %d. not sending Smart Fifo cmd\n",
4377303628Ssbruno			  new_state);
4378303628Ssbruno		return EINVAL;
4379303628Ssbruno	}
4380303628Ssbruno
4381303628Ssbruno	ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC,
4382303628Ssbruno				   sizeof(sf_cmd), &sf_cmd);
4383303628Ssbruno	return ret;
4384303628Ssbruno}
4385303628Ssbruno
4386303628Ssbrunostatic int
4387303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc)
4388303628Ssbruno{
4389303628Ssbruno	struct iwm_bt_coex_cmd bt_cmd;
4390303628Ssbruno
4391303628Ssbruno	bt_cmd.mode = htole32(IWM_BT_COEX_WIFI);
4392303628Ssbruno	bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET);
4393303628Ssbruno
4394303628Ssbruno	return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd),
4395303628Ssbruno	    &bt_cmd);
4396303628Ssbruno}
4397303628Ssbruno
4398303628Ssbrunostatic int
4399303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2)
4400303628Ssbruno{
4401303628Ssbruno	struct iwm_mcc_update_cmd mcc_cmd;
4402303628Ssbruno	struct iwm_host_cmd hcmd = {
4403303628Ssbruno		.id = IWM_MCC_UPDATE_CMD,
4404303628Ssbruno		.flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB),
4405303628Ssbruno		.data = { &mcc_cmd },
4406303628Ssbruno	};
4407303628Ssbruno	int ret;
4408303628Ssbruno#ifdef IWM_DEBUG
4409303628Ssbruno	struct iwm_rx_packet *pkt;
4410303628Ssbruno	struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL;
4411303628Ssbruno	struct iwm_mcc_update_resp *mcc_resp;
4412303628Ssbruno	int n_channels;
4413303628Ssbruno	uint16_t mcc;
4414303628Ssbruno#endif
4415303628Ssbruno	int resp_v2 = isset(sc->sc_enabled_capa,
4416303628Ssbruno	    IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
4417303628Ssbruno
4418303628Ssbruno	memset(&mcc_cmd, 0, sizeof(mcc_cmd));
4419303628Ssbruno	mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]);
4420303628Ssbruno	if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
4421303628Ssbruno	    isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC))
4422303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT;
4423303628Ssbruno	else
4424303628Ssbruno		mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW;
4425303628Ssbruno
4426303628Ssbruno	if (resp_v2)
4427303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd);
4428303628Ssbruno	else
4429303628Ssbruno		hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1);
4430303628Ssbruno
4431303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_NODE,
4432303628Ssbruno	    "send MCC update to FW with '%c%c' src = %d\n",
4433303628Ssbruno	    alpha2[0], alpha2[1], mcc_cmd.source_id);
4434303628Ssbruno
4435303628Ssbruno	ret = iwm_send_cmd(sc, &hcmd);
4436303628Ssbruno	if (ret)
4437303628Ssbruno		return ret;
4438303628Ssbruno
4439303628Ssbruno#ifdef IWM_DEBUG
4440303628Ssbruno	pkt = hcmd.resp_pkt;
4441303628Ssbruno
4442303628Ssbruno	/* Extract MCC response */
4443303628Ssbruno	if (resp_v2) {
4444303628Ssbruno		mcc_resp = (void *)pkt->data;
4445303628Ssbruno		mcc = mcc_resp->mcc;
4446303628Ssbruno		n_channels =  le32toh(mcc_resp->n_channels);
4447303628Ssbruno	} else {
4448303628Ssbruno		mcc_resp_v1 = (void *)pkt->data;
4449303628Ssbruno		mcc = mcc_resp_v1->mcc;
4450303628Ssbruno		n_channels =  le32toh(mcc_resp_v1->n_channels);
4451303628Ssbruno	}
4452303628Ssbruno
4453303628Ssbruno	/* W/A for a FW/NVM issue - returns 0x00 for the world domain */
4454303628Ssbruno	if (mcc == 0)
4455303628Ssbruno		mcc = 0x3030;  /* "00" - world */
4456303628Ssbruno
4457303628Ssbruno	IWM_DPRINTF(sc, IWM_DEBUG_NODE,
4458303628Ssbruno	    "regulatory domain '%c%c' (%d channels available)\n",
4459303628Ssbruno	    mcc >> 8, mcc & 0xff, n_channels);
4460303628Ssbruno#endif
4461303628Ssbruno	iwm_free_resp(sc, &hcmd);
4462303628Ssbruno
4463303628Ssbruno	return 0;
4464303628Ssbruno}
4465303628Ssbruno
4466303628Ssbrunostatic void
4467303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff)
4468303628Ssbruno{
4469303628Ssbruno	struct iwm_host_cmd cmd = {
4470303628Ssbruno		.id = IWM_REPLY_THERMAL_MNG_BACKOFF,
4471303628Ssbruno		.len = { sizeof(uint32_t), },
4472303628Ssbruno		.data = { &backoff, },
4473303628Ssbruno	};
4474303628Ssbruno
4475303628Ssbruno	if (iwm_send_cmd(sc, &cmd) != 0) {
4476303628Ssbruno		device_printf(sc->sc_dev,
4477303628Ssbruno		    "failed to change thermal tx backoff\n");
4478303628Ssbruno	}
4479303628Ssbruno}
4480303628Ssbruno
4481303628Ssbrunostatic int
4482286441Srpauloiwm_init_hw(struct iwm_softc *sc)
4483286441Srpaulo{
4484287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4485303628Ssbruno	int error, i, ac;
4486286441Srpaulo
4487303628Ssbruno	if ((error = iwm_start_hw(sc)) != 0) {
4488303628Ssbruno		printf("iwm_start_hw: failed %d\n", error);
4489286441Srpaulo		return error;
4490303628Ssbruno	}
4491286441Srpaulo
4492286441Srpaulo	if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) {
4493303628Ssbruno		printf("iwm_run_init_mvm_ucode: failed %d\n", error);
4494286441Srpaulo		return error;
4495286441Srpaulo	}
4496286441Srpaulo
4497286441Srpaulo	/*
4498286441Srpaulo	 * should stop and start HW since that INIT
4499286441Srpaulo	 * image just loaded
4500286441Srpaulo	 */
4501286441Srpaulo	iwm_stop_device(sc);
4502286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
4503286441Srpaulo		device_printf(sc->sc_dev, "could not initialize hardware\n");
4504286441Srpaulo		return error;
4505286441Srpaulo	}
4506286441Srpaulo
4507286441Srpaulo	/* omstart, this time with the regular firmware */
4508286441Srpaulo	error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR);
4509286441Srpaulo	if (error) {
4510286441Srpaulo		device_printf(sc->sc_dev, "could not load firmware\n");
4511286441Srpaulo		goto error;
4512286441Srpaulo	}
4513286441Srpaulo
4514303628Ssbruno	if ((error = iwm_send_bt_init_conf(sc)) != 0) {
4515303628Ssbruno		device_printf(sc->sc_dev, "bt init conf failed\n");
4516286441Srpaulo		goto error;
4517303628Ssbruno	}
4518286441Srpaulo
4519303628Ssbruno	if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) {
4520303628Ssbruno		device_printf(sc->sc_dev, "antenna config failed\n");
4521303628Ssbruno		goto error;
4522303628Ssbruno	}
4523303628Ssbruno
4524286441Srpaulo	/* Send phy db control command and then phy db calibration*/
4525303628Ssbruno	if ((error = iwm_send_phy_db_data(sc)) != 0) {
4526303628Ssbruno		device_printf(sc->sc_dev, "phy_db_data failed\n");
4527286441Srpaulo		goto error;
4528303628Ssbruno	}
4529286441Srpaulo
4530303628Ssbruno	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) {
4531303628Ssbruno		device_printf(sc->sc_dev, "phy_cfg_cmd failed\n");
4532286441Srpaulo		goto error;
4533303628Ssbruno	}
4534286441Srpaulo
4535286441Srpaulo	/* Add auxiliary station for scanning */
4536303628Ssbruno	if ((error = iwm_mvm_add_aux_sta(sc)) != 0) {
4537303628Ssbruno		device_printf(sc->sc_dev, "add_aux_sta failed\n");
4538286441Srpaulo		goto error;
4539303628Ssbruno	}
4540286441Srpaulo
4541286441Srpaulo	for (i = 0; i < IWM_NUM_PHY_CTX; i++) {
4542286441Srpaulo		/*
4543286441Srpaulo		 * The channel used here isn't relevant as it's
4544286441Srpaulo		 * going to be overwritten in the other flows.
4545286441Srpaulo		 * For now use the first channel we have.
4546286441Srpaulo		 */
4547286441Srpaulo		if ((error = iwm_mvm_phy_ctxt_add(sc,
4548286441Srpaulo		    &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0)
4549286441Srpaulo			goto error;
4550286441Srpaulo	}
4551286441Srpaulo
4552303628Ssbruno	/* Initialize tx backoffs to the minimum. */
4553303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000)
4554303628Ssbruno		iwm_mvm_tt_tx_backoff(sc, 0);
4555303628Ssbruno
4556286441Srpaulo	error = iwm_mvm_power_update_device(sc);
4557286441Srpaulo	if (error)
4558286441Srpaulo		goto error;
4559286441Srpaulo
4560303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) {
4561303628Ssbruno		if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0)
4562303628Ssbruno			goto error;
4563286441Srpaulo	}
4564286441Srpaulo
4565303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
4566303628Ssbruno		if ((error = iwm_mvm_config_umac_scan(sc)) != 0)
4567303628Ssbruno			goto error;
4568303628Ssbruno	}
4569303628Ssbruno
4570303628Ssbruno	/* Enable Tx queues. */
4571303628Ssbruno	for (ac = 0; ac < WME_NUM_AC; ac++) {
4572303628Ssbruno		error = iwm_enable_txq(sc, IWM_STATION_ID, ac,
4573303628Ssbruno		    iwm_mvm_ac_to_tx_fifo[ac]);
4574303628Ssbruno		if (error)
4575303628Ssbruno			goto error;
4576303628Ssbruno	}
4577303628Ssbruno
4578303628Ssbruno	if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) {
4579303628Ssbruno		device_printf(sc->sc_dev, "failed to disable beacon filter\n");
4580303628Ssbruno		goto error;
4581303628Ssbruno	}
4582303628Ssbruno
4583286441Srpaulo	return 0;
4584286441Srpaulo
4585286441Srpaulo error:
4586286441Srpaulo	iwm_stop_device(sc);
4587286441Srpaulo	return error;
4588286441Srpaulo}
4589286441Srpaulo
4590286441Srpaulo/* Allow multicast from our BSSID. */
4591286441Srpaulostatic int
4592286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc)
4593286441Srpaulo{
4594286441Srpaulo	struct ieee80211_node *ni = vap->iv_bss;
4595286441Srpaulo	struct iwm_mcast_filter_cmd *cmd;
4596286441Srpaulo	size_t size;
4597286441Srpaulo	int error;
4598286441Srpaulo
4599286441Srpaulo	size = roundup(sizeof(*cmd), 4);
4600286441Srpaulo	cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
4601286441Srpaulo	if (cmd == NULL)
4602286441Srpaulo		return ENOMEM;
4603286441Srpaulo	cmd->filter_own = 1;
4604286441Srpaulo	cmd->port_id = 0;
4605286441Srpaulo	cmd->count = 0;
4606286441Srpaulo	cmd->pass_all = 1;
4607286441Srpaulo	IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid);
4608286441Srpaulo
4609286441Srpaulo	error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD,
4610286441Srpaulo	    IWM_CMD_SYNC, size, cmd);
4611286441Srpaulo	free(cmd, M_DEVBUF);
4612286441Srpaulo
4613286441Srpaulo	return (error);
4614286441Srpaulo}
4615286441Srpaulo
4616303628Ssbruno/*
4617303628Ssbruno * ifnet interfaces
4618303628Ssbruno */
4619303628Ssbruno
4620286441Srpaulostatic void
4621287197Sglebiusiwm_init(struct iwm_softc *sc)
4622286441Srpaulo{
4623286441Srpaulo	int error;
4624286441Srpaulo
4625286441Srpaulo	if (sc->sc_flags & IWM_FLAG_HW_INITED) {
4626286441Srpaulo		return;
4627286441Srpaulo	}
4628286441Srpaulo	sc->sc_generation++;
4629286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_STOPPED;
4630286441Srpaulo
4631286441Srpaulo	if ((error = iwm_init_hw(sc)) != 0) {
4632303628Ssbruno		printf("iwm_init_hw failed %d\n", error);
4633287197Sglebius		iwm_stop(sc);
4634286441Srpaulo		return;
4635286441Srpaulo	}
4636286441Srpaulo
4637286441Srpaulo	/*
4638303628Ssbruno	 * Ok, firmware loaded and we are jogging
4639286441Srpaulo	 */
4640286441Srpaulo	sc->sc_flags |= IWM_FLAG_HW_INITED;
4641286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
4642286441Srpaulo}
4643286441Srpaulo
4644287197Sglebiusstatic int
4645287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m)
4646286441Srpaulo{
4647287197Sglebius	struct iwm_softc *sc;
4648287197Sglebius	int error;
4649286441Srpaulo
4650287197Sglebius	sc = ic->ic_softc;
4651287197Sglebius
4652286441Srpaulo	IWM_LOCK(sc);
4653287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
4654287197Sglebius		IWM_UNLOCK(sc);
4655287197Sglebius		return (ENXIO);
4656287197Sglebius	}
4657287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
4658287197Sglebius	if (error) {
4659287197Sglebius		IWM_UNLOCK(sc);
4660287197Sglebius		return (error);
4661287197Sglebius	}
4662287197Sglebius	iwm_start(sc);
4663286441Srpaulo	IWM_UNLOCK(sc);
4664287197Sglebius	return (0);
4665286441Srpaulo}
4666286441Srpaulo
4667287197Sglebius/*
4668287197Sglebius * Dequeue packets from sendq and call send.
4669287197Sglebius */
4670286441Srpaulostatic void
4671287197Sglebiusiwm_start(struct iwm_softc *sc)
4672286441Srpaulo{
4673286441Srpaulo	struct ieee80211_node *ni;
4674286441Srpaulo	struct mbuf *m;
4675286441Srpaulo	int ac = 0;
4676286441Srpaulo
4677286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__);
4678287197Sglebius	while (sc->qfullmsk == 0 &&
4679287197Sglebius		(m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
4680286441Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4681286441Srpaulo		if (iwm_tx(sc, m, ni, ac) != 0) {
4682287197Sglebius			if_inc_counter(ni->ni_vap->iv_ifp,
4683287197Sglebius			    IFCOUNTER_OERRORS, 1);
4684286441Srpaulo			ieee80211_free_node(ni);
4685286441Srpaulo			continue;
4686286441Srpaulo		}
4687287197Sglebius		sc->sc_tx_timer = 15;
4688286441Srpaulo	}
4689286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__);
4690286441Srpaulo}
4691286441Srpaulo
4692286441Srpaulostatic void
4693287197Sglebiusiwm_stop(struct iwm_softc *sc)
4694286441Srpaulo{
4695286441Srpaulo
4696286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_HW_INITED;
4697286441Srpaulo	sc->sc_flags |= IWM_FLAG_STOPPED;
4698286441Srpaulo	sc->sc_generation++;
4699301187Sadrian	iwm_led_blink_stop(sc);
4700286441Srpaulo	sc->sc_tx_timer = 0;
4701286441Srpaulo	iwm_stop_device(sc);
4702286441Srpaulo}
4703286441Srpaulo
4704286441Srpaulostatic void
4705286441Srpauloiwm_watchdog(void *arg)
4706286441Srpaulo{
4707286441Srpaulo	struct iwm_softc *sc = arg;
4708300242Savos	struct ieee80211com *ic = &sc->sc_ic;
4709286441Srpaulo
4710286441Srpaulo	if (sc->sc_tx_timer > 0) {
4711286441Srpaulo		if (--sc->sc_tx_timer == 0) {
4712286441Srpaulo			device_printf(sc->sc_dev, "device timeout\n");
4713286441Srpaulo#ifdef IWM_DEBUG
4714286441Srpaulo			iwm_nic_error(sc);
4715286441Srpaulo#endif
4716300242Savos			ieee80211_restart_all(ic);
4717303628Ssbruno			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
4718286441Srpaulo			return;
4719286441Srpaulo		}
4720286441Srpaulo	}
4721286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
4722286441Srpaulo}
4723286441Srpaulo
4724287197Sglebiusstatic void
4725287197Sglebiusiwm_parent(struct ieee80211com *ic)
4726286441Srpaulo{
4727287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
4728287197Sglebius	int startall = 0;
4729286441Srpaulo
4730287197Sglebius	IWM_LOCK(sc);
4731287197Sglebius	if (ic->ic_nrunning > 0) {
4732287197Sglebius		if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) {
4733287197Sglebius			iwm_init(sc);
4734287197Sglebius			startall = 1;
4735286441Srpaulo		}
4736287197Sglebius	} else if (sc->sc_flags & IWM_FLAG_HW_INITED)
4737287197Sglebius		iwm_stop(sc);
4738287197Sglebius	IWM_UNLOCK(sc);
4739287197Sglebius	if (startall)
4740287197Sglebius		ieee80211_start_all(ic);
4741286441Srpaulo}
4742286441Srpaulo
4743286441Srpaulo/*
4744286441Srpaulo * The interrupt side of things
4745286441Srpaulo */
4746286441Srpaulo
4747286441Srpaulo/*
4748286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c
4749286441Srpaulo */
4750286441Srpaulo
4751286441Srpaulo/*
4752286441Srpaulo * Note: This structure is read from the device with IO accesses,
4753286441Srpaulo * and the reading already does the endian conversion. As it is
4754286441Srpaulo * read with uint32_t-sized accesses, any members with a different size
4755286441Srpaulo * need to be ordered correctly though!
4756286441Srpaulo */
4757286441Srpaulostruct iwm_error_event_table {
4758286441Srpaulo	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
4759286441Srpaulo	uint32_t error_id;		/* type of error */
4760303628Ssbruno	uint32_t trm_hw_status0;	/* TRM HW status */
4761303628Ssbruno	uint32_t trm_hw_status1;	/* TRM HW status */
4762286441Srpaulo	uint32_t blink2;		/* branch link */
4763286441Srpaulo	uint32_t ilink1;		/* interrupt link */
4764286441Srpaulo	uint32_t ilink2;		/* interrupt link */
4765286441Srpaulo	uint32_t data1;		/* error-specific data */
4766286441Srpaulo	uint32_t data2;		/* error-specific data */
4767286441Srpaulo	uint32_t data3;		/* error-specific data */
4768286441Srpaulo	uint32_t bcon_time;		/* beacon timer */
4769286441Srpaulo	uint32_t tsf_low;		/* network timestamp function timer */
4770286441Srpaulo	uint32_t tsf_hi;		/* network timestamp function timer */
4771286441Srpaulo	uint32_t gp1;		/* GP1 timer register */
4772286441Srpaulo	uint32_t gp2;		/* GP2 timer register */
4773303628Ssbruno	uint32_t fw_rev_type;	/* firmware revision type */
4774303628Ssbruno	uint32_t major;		/* uCode version major */
4775303628Ssbruno	uint32_t minor;		/* uCode version minor */
4776286441Srpaulo	uint32_t hw_ver;		/* HW Silicon version */
4777286441Srpaulo	uint32_t brd_ver;		/* HW board version */
4778286441Srpaulo	uint32_t log_pc;		/* log program counter */
4779286441Srpaulo	uint32_t frame_ptr;		/* frame pointer */
4780286441Srpaulo	uint32_t stack_ptr;		/* stack pointer */
4781286441Srpaulo	uint32_t hcmd;		/* last host command header */
4782286441Srpaulo	uint32_t isr0;		/* isr status register LMPM_NIC_ISR0:
4783286441Srpaulo				 * rxtx_flag */
4784286441Srpaulo	uint32_t isr1;		/* isr status register LMPM_NIC_ISR1:
4785286441Srpaulo				 * host_flag */
4786286441Srpaulo	uint32_t isr2;		/* isr status register LMPM_NIC_ISR2:
4787286441Srpaulo				 * enc_flag */
4788286441Srpaulo	uint32_t isr3;		/* isr status register LMPM_NIC_ISR3:
4789286441Srpaulo				 * time_flag */
4790286441Srpaulo	uint32_t isr4;		/* isr status register LMPM_NIC_ISR4:
4791286441Srpaulo				 * wico interrupt */
4792303628Ssbruno	uint32_t last_cmd_id;	/* last HCMD id handled by the firmware */
4793286441Srpaulo	uint32_t wait_event;		/* wait event() caller address */
4794286441Srpaulo	uint32_t l2p_control;	/* L2pControlField */
4795286441Srpaulo	uint32_t l2p_duration;	/* L2pDurationField */
4796286441Srpaulo	uint32_t l2p_mhvalid;	/* L2pMhValidBits */
4797286441Srpaulo	uint32_t l2p_addr_match;	/* L2pAddrMatchStat */
4798286441Srpaulo	uint32_t lmpm_pmg_sel;	/* indicate which clocks are turned on
4799286441Srpaulo				 * (LMPM_PMG_SEL) */
4800286441Srpaulo	uint32_t u_timestamp;	/* indicate when the date and time of the
4801286441Srpaulo				 * compilation */
4802286441Srpaulo	uint32_t flow_handler;	/* FH read/write pointers, RX credit */
4803303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */;
4804303628Ssbruno
4805303628Ssbruno/*
4806303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip.
4807303628Ssbruno * Note: This structure is read from the device with IO accesses,
4808303628Ssbruno * and the reading already does the endian conversion. As it is
4809303628Ssbruno * read with u32-sized accesses, any members with a different size
4810303628Ssbruno * need to be ordered correctly though!
4811303628Ssbruno */
4812303628Ssbrunostruct iwm_umac_error_event_table {
4813303628Ssbruno	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
4814303628Ssbruno	uint32_t error_id;	/* type of error */
4815303628Ssbruno	uint32_t blink1;	/* branch link */
4816303628Ssbruno	uint32_t blink2;	/* branch link */
4817303628Ssbruno	uint32_t ilink1;	/* interrupt link */
4818303628Ssbruno	uint32_t ilink2;	/* interrupt link */
4819303628Ssbruno	uint32_t data1;		/* error-specific data */
4820303628Ssbruno	uint32_t data2;		/* error-specific data */
4821303628Ssbruno	uint32_t data3;		/* error-specific data */
4822303628Ssbruno	uint32_t umac_major;
4823303628Ssbruno	uint32_t umac_minor;
4824303628Ssbruno	uint32_t frame_pointer;	/* core register 27*/
4825303628Ssbruno	uint32_t stack_pointer;	/* core register 28 */
4826303628Ssbruno	uint32_t cmd_header;	/* latest host cmd sent to UMAC */
4827303628Ssbruno	uint32_t nic_isr_pref;	/* ISR status register */
4828286441Srpaulo} __packed;
4829286441Srpaulo
4830286441Srpaulo#define ERROR_START_OFFSET  (1 * sizeof(uint32_t))
4831286441Srpaulo#define ERROR_ELEM_SIZE     (7 * sizeof(uint32_t))
4832286441Srpaulo
4833286441Srpaulo#ifdef IWM_DEBUG
4834286441Srpaulostruct {
4835286441Srpaulo	const char *name;
4836286441Srpaulo	uint8_t num;
4837286441Srpaulo} advanced_lookup[] = {
4838286441Srpaulo	{ "NMI_INTERRUPT_WDG", 0x34 },
4839286441Srpaulo	{ "SYSASSERT", 0x35 },
4840286441Srpaulo	{ "UCODE_VERSION_MISMATCH", 0x37 },
4841286441Srpaulo	{ "BAD_COMMAND", 0x38 },
4842286441Srpaulo	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
4843286441Srpaulo	{ "FATAL_ERROR", 0x3D },
4844286441Srpaulo	{ "NMI_TRM_HW_ERR", 0x46 },
4845286441Srpaulo	{ "NMI_INTERRUPT_TRM", 0x4C },
4846286441Srpaulo	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
4847286441Srpaulo	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
4848286441Srpaulo	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
4849286441Srpaulo	{ "NMI_INTERRUPT_HOST", 0x66 },
4850286441Srpaulo	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
4851286441Srpaulo	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
4852286441Srpaulo	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
4853286441Srpaulo	{ "ADVANCED_SYSASSERT", 0 },
4854286441Srpaulo};
4855286441Srpaulo
4856286441Srpaulostatic const char *
4857286441Srpauloiwm_desc_lookup(uint32_t num)
4858286441Srpaulo{
4859286441Srpaulo	int i;
4860286441Srpaulo
4861286441Srpaulo	for (i = 0; i < nitems(advanced_lookup) - 1; i++)
4862286441Srpaulo		if (advanced_lookup[i].num == num)
4863286441Srpaulo			return advanced_lookup[i].name;
4864286441Srpaulo
4865286441Srpaulo	/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
4866286441Srpaulo	return advanced_lookup[i].name;
4867286441Srpaulo}
4868286441Srpaulo
4869303628Ssbrunostatic void
4870303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc)
4871303628Ssbruno{
4872303628Ssbruno	struct iwm_umac_error_event_table table;
4873303628Ssbruno	uint32_t base;
4874303628Ssbruno
4875303628Ssbruno	base = sc->sc_uc.uc_umac_error_event_table;
4876303628Ssbruno
4877303628Ssbruno	if (base < 0x800000) {
4878303628Ssbruno		device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n",
4879303628Ssbruno		    base);
4880303628Ssbruno		return;
4881303628Ssbruno	}
4882303628Ssbruno
4883303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
4884303628Ssbruno		device_printf(sc->sc_dev, "reading errlog failed\n");
4885303628Ssbruno		return;
4886303628Ssbruno	}
4887303628Ssbruno
4888303628Ssbruno	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
4889303628Ssbruno		device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n");
4890303628Ssbruno		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
4891303628Ssbruno		    sc->sc_flags, table.valid);
4892303628Ssbruno	}
4893303628Ssbruno
4894303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id,
4895303628Ssbruno		iwm_desc_lookup(table.error_id));
4896303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1);
4897303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2);
4898303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n",
4899303628Ssbruno	    table.ilink1);
4900303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n",
4901303628Ssbruno	    table.ilink2);
4902303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1);
4903303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2);
4904303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3);
4905303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major);
4906303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor);
4907303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | frame pointer\n",
4908303628Ssbruno	    table.frame_pointer);
4909303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | stack pointer\n",
4910303628Ssbruno	    table.stack_pointer);
4911303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header);
4912303628Ssbruno	device_printf(sc->sc_dev, "0x%08X | isr status reg\n",
4913303628Ssbruno	    table.nic_isr_pref);
4914303628Ssbruno}
4915303628Ssbruno
4916286441Srpaulo/*
4917286441Srpaulo * Support for dumping the error log seemed like a good idea ...
4918286441Srpaulo * but it's mostly hex junk and the only sensible thing is the
4919286441Srpaulo * hw/ucode revision (which we know anyway).  Since it's here,
4920286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to
4921286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later.
4922286441Srpaulo */
4923286441Srpaulostatic void
4924286441Srpauloiwm_nic_error(struct iwm_softc *sc)
4925286441Srpaulo{
4926286441Srpaulo	struct iwm_error_event_table table;
4927286441Srpaulo	uint32_t base;
4928286441Srpaulo
4929286441Srpaulo	device_printf(sc->sc_dev, "dumping device error log\n");
4930286441Srpaulo	base = sc->sc_uc.uc_error_event_table;
4931303628Ssbruno	if (base < 0x800000) {
4932286441Srpaulo		device_printf(sc->sc_dev,
4933303628Ssbruno		    "Invalid error log pointer 0x%08x\n", base);
4934286441Srpaulo		return;
4935286441Srpaulo	}
4936286441Srpaulo
4937303628Ssbruno	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
4938286441Srpaulo		device_printf(sc->sc_dev, "reading errlog failed\n");
4939286441Srpaulo		return;
4940286441Srpaulo	}
4941286441Srpaulo
4942286441Srpaulo	if (!table.valid) {
4943286441Srpaulo		device_printf(sc->sc_dev, "errlog not found, skipping\n");
4944286441Srpaulo		return;
4945286441Srpaulo	}
4946286441Srpaulo
4947286441Srpaulo	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
4948303628Ssbruno		device_printf(sc->sc_dev, "Start Error Log Dump:\n");
4949286441Srpaulo		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
4950286441Srpaulo		    sc->sc_flags, table.valid);
4951286441Srpaulo	}
4952286441Srpaulo
4953286441Srpaulo	device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id,
4954303628Ssbruno	    iwm_desc_lookup(table.error_id));
4955303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status0\n",
4956303628Ssbruno	    table.trm_hw_status0);
4957303628Ssbruno	device_printf(sc->sc_dev, "%08X | trm_hw_status1\n",
4958303628Ssbruno	    table.trm_hw_status1);
4959286441Srpaulo	device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2);
4960286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1);
4961286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2);
4962286441Srpaulo	device_printf(sc->sc_dev, "%08X | data1\n", table.data1);
4963286441Srpaulo	device_printf(sc->sc_dev, "%08X | data2\n", table.data2);
4964286441Srpaulo	device_printf(sc->sc_dev, "%08X | data3\n", table.data3);
4965286441Srpaulo	device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time);
4966286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low);
4967286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi);
4968286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1);
4969286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2);
4970303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode revision type\n",
4971303628Ssbruno	    table.fw_rev_type);
4972303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major);
4973303628Ssbruno	device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor);
4974286441Srpaulo	device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver);
4975286441Srpaulo	device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver);
4976286441Srpaulo	device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd);
4977286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0);
4978286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1);
4979286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2);
4980286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3);
4981286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4);
4982303628Ssbruno	device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id);
4983286441Srpaulo	device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event);
4984286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control);
4985286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration);
4986286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid);
4987286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match);
4988286441Srpaulo	device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
4989286441Srpaulo	device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp);
4990286441Srpaulo	device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler);
4991303628Ssbruno
4992303628Ssbruno	if (sc->sc_uc.uc_umac_error_event_table)
4993303628Ssbruno		iwm_nic_umac_error(sc);
4994286441Srpaulo}
4995286441Srpaulo#endif
4996286441Srpaulo
4997286441Srpaulo#define SYNC_RESP_STRUCT(_var_, _pkt_)					\
4998286441Srpaulodo {									\
4999286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\
5000286441Srpaulo	_var_ = (void *)((_pkt_)+1);					\
5001286441Srpaulo} while (/*CONSTCOND*/0)
5002286441Srpaulo
5003286441Srpaulo#define SYNC_RESP_PTR(_ptr_, _len_, _pkt_)				\
5004286441Srpaulodo {									\
5005286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\
5006286441Srpaulo	_ptr_ = (void *)((_pkt_)+1);					\
5007286441Srpaulo} while (/*CONSTCOND*/0)
5008286441Srpaulo
5009286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
5010286441Srpaulo
5011286441Srpaulo/*
5012286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt.
5013286441Srpaulo * Basic structure from if_iwn
5014286441Srpaulo */
5015286441Srpaulostatic void
5016286441Srpauloiwm_notif_intr(struct iwm_softc *sc)
5017286441Srpaulo{
5018303628Ssbruno	struct ieee80211com *ic = &sc->sc_ic;
5019286441Srpaulo	uint16_t hw;
5020286441Srpaulo
5021286441Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
5022286441Srpaulo	    BUS_DMASYNC_POSTREAD);
5023286441Srpaulo
5024286441Srpaulo	hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
5025303628Ssbruno
5026303628Ssbruno	/*
5027303628Ssbruno	 * Process responses
5028303628Ssbruno	 */
5029286441Srpaulo	while (sc->rxq.cur != hw) {
5030286441Srpaulo		struct iwm_rx_ring *ring = &sc->rxq;
5031286441Srpaulo		struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
5032286441Srpaulo		struct iwm_rx_packet *pkt;
5033286441Srpaulo		struct iwm_cmd_response *cresp;
5034303628Ssbruno		int qid, idx, code;
5035286441Srpaulo
5036286441Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
5037286441Srpaulo		    BUS_DMASYNC_POSTREAD);
5038286441Srpaulo		pkt = mtod(data->m, struct iwm_rx_packet *);
5039286441Srpaulo
5040286441Srpaulo		qid = pkt->hdr.qid & ~0x80;
5041286441Srpaulo		idx = pkt->hdr.idx;
5042286441Srpaulo
5043303628Ssbruno		code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
5044286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5045303628Ssbruno		    "rx packet qid=%d idx=%d type=%x %d %d\n",
5046303628Ssbruno		    pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, sc->rxq.cur, hw);
5047286441Srpaulo
5048286441Srpaulo		/*
5049286441Srpaulo		 * randomly get these from the firmware, no idea why.
5050286441Srpaulo		 * they at least seem harmless, so just ignore them for now
5051286441Srpaulo		 */
5052286441Srpaulo		if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
5053286441Srpaulo		    || pkt->len_n_flags == htole32(0x55550000))) {
5054286441Srpaulo			ADVANCE_RXQ(sc);
5055286441Srpaulo			continue;
5056286441Srpaulo		}
5057286441Srpaulo
5058303628Ssbruno		switch (code) {
5059286441Srpaulo		case IWM_REPLY_RX_PHY_CMD:
5060286441Srpaulo			iwm_mvm_rx_rx_phy_cmd(sc, pkt, data);
5061286441Srpaulo			break;
5062286441Srpaulo
5063286441Srpaulo		case IWM_REPLY_RX_MPDU_CMD:
5064286441Srpaulo			iwm_mvm_rx_rx_mpdu(sc, pkt, data);
5065286441Srpaulo			break;
5066286441Srpaulo
5067286441Srpaulo		case IWM_TX_CMD:
5068286441Srpaulo			iwm_mvm_rx_tx_cmd(sc, pkt, data);
5069286441Srpaulo			break;
5070286441Srpaulo
5071286441Srpaulo		case IWM_MISSED_BEACONS_NOTIFICATION: {
5072286441Srpaulo			struct iwm_missed_beacons_notif *resp;
5073286441Srpaulo			int missed;
5074286441Srpaulo
5075286441Srpaulo			/* XXX look at mac_id to determine interface ID */
5076286441Srpaulo			struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5077286441Srpaulo
5078286441Srpaulo			SYNC_RESP_STRUCT(resp, pkt);
5079286441Srpaulo			missed = le32toh(resp->consec_missed_beacons);
5080286441Srpaulo
5081286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE,
5082286441Srpaulo			    "%s: MISSED_BEACON: mac_id=%d, "
5083286441Srpaulo			    "consec_since_last_rx=%d, consec=%d, num_expect=%d "
5084286441Srpaulo			    "num_rx=%d\n",
5085286441Srpaulo			    __func__,
5086286441Srpaulo			    le32toh(resp->mac_id),
5087286441Srpaulo			    le32toh(resp->consec_missed_beacons_since_last_rx),
5088286441Srpaulo			    le32toh(resp->consec_missed_beacons),
5089286441Srpaulo			    le32toh(resp->num_expected_beacons),
5090286441Srpaulo			    le32toh(resp->num_recvd_beacons));
5091286441Srpaulo
5092286441Srpaulo			/* Be paranoid */
5093286441Srpaulo			if (vap == NULL)
5094286441Srpaulo				break;
5095286441Srpaulo
5096286441Srpaulo			/* XXX no net80211 locking? */
5097286441Srpaulo			if (vap->iv_state == IEEE80211_S_RUN &&
5098286441Srpaulo			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
5099286441Srpaulo				if (missed > vap->iv_bmissthreshold) {
5100286441Srpaulo					/* XXX bad locking; turn into task */
5101286441Srpaulo					IWM_UNLOCK(sc);
5102286441Srpaulo					ieee80211_beacon_miss(ic);
5103286441Srpaulo					IWM_LOCK(sc);
5104286441Srpaulo				}
5105286441Srpaulo			}
5106286441Srpaulo
5107286441Srpaulo			break; }
5108286441Srpaulo
5109303628Ssbruno		case IWM_MFUART_LOAD_NOTIFICATION:
5110303628Ssbruno			break;
5111303628Ssbruno
5112286441Srpaulo		case IWM_MVM_ALIVE: {
5113303628Ssbruno			struct iwm_mvm_alive_resp_v1 *resp1;
5114303628Ssbruno			struct iwm_mvm_alive_resp_v2 *resp2;
5115303628Ssbruno			struct iwm_mvm_alive_resp_v3 *resp3;
5116286441Srpaulo
5117303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) {
5118303628Ssbruno				SYNC_RESP_STRUCT(resp1, pkt);
5119303628Ssbruno				sc->sc_uc.uc_error_event_table
5120303628Ssbruno				    = le32toh(resp1->error_event_table_ptr);
5121303628Ssbruno				sc->sc_uc.uc_log_event_table
5122303628Ssbruno				    = le32toh(resp1->log_event_table_ptr);
5123303628Ssbruno				sc->sched_base = le32toh(resp1->scd_base_ptr);
5124303628Ssbruno				if (resp1->status == IWM_ALIVE_STATUS_OK)
5125303628Ssbruno					sc->sc_uc.uc_ok = 1;
5126303628Ssbruno				else
5127303628Ssbruno					sc->sc_uc.uc_ok = 0;
5128303628Ssbruno			}
5129286441Srpaulo
5130303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) {
5131303628Ssbruno				SYNC_RESP_STRUCT(resp2, pkt);
5132303628Ssbruno				sc->sc_uc.uc_error_event_table
5133303628Ssbruno				    = le32toh(resp2->error_event_table_ptr);
5134303628Ssbruno				sc->sc_uc.uc_log_event_table
5135303628Ssbruno				    = le32toh(resp2->log_event_table_ptr);
5136303628Ssbruno				sc->sched_base = le32toh(resp2->scd_base_ptr);
5137303628Ssbruno				sc->sc_uc.uc_umac_error_event_table
5138303628Ssbruno				    = le32toh(resp2->error_info_addr);
5139303628Ssbruno				if (resp2->status == IWM_ALIVE_STATUS_OK)
5140303628Ssbruno					sc->sc_uc.uc_ok = 1;
5141303628Ssbruno				else
5142303628Ssbruno					sc->sc_uc.uc_ok = 0;
5143303628Ssbruno			}
5144303628Ssbruno
5145303628Ssbruno			if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) {
5146303628Ssbruno				SYNC_RESP_STRUCT(resp3, pkt);
5147303628Ssbruno				sc->sc_uc.uc_error_event_table
5148303628Ssbruno				    = le32toh(resp3->error_event_table_ptr);
5149303628Ssbruno				sc->sc_uc.uc_log_event_table
5150303628Ssbruno				    = le32toh(resp3->log_event_table_ptr);
5151303628Ssbruno				sc->sched_base = le32toh(resp3->scd_base_ptr);
5152303628Ssbruno				sc->sc_uc.uc_umac_error_event_table
5153303628Ssbruno				    = le32toh(resp3->error_info_addr);
5154303628Ssbruno				if (resp3->status == IWM_ALIVE_STATUS_OK)
5155303628Ssbruno					sc->sc_uc.uc_ok = 1;
5156303628Ssbruno				else
5157303628Ssbruno					sc->sc_uc.uc_ok = 0;
5158303628Ssbruno			}
5159303628Ssbruno
5160286441Srpaulo			sc->sc_uc.uc_intr = 1;
5161286441Srpaulo			wakeup(&sc->sc_uc);
5162286441Srpaulo			break; }
5163286441Srpaulo
5164286441Srpaulo		case IWM_CALIB_RES_NOTIF_PHY_DB: {
5165286441Srpaulo			struct iwm_calib_res_notif_phy_db *phy_db_notif;
5166286441Srpaulo			SYNC_RESP_STRUCT(phy_db_notif, pkt);
5167286441Srpaulo
5168286441Srpaulo			iwm_phy_db_set_section(sc, phy_db_notif);
5169286441Srpaulo
5170286441Srpaulo			break; }
5171286441Srpaulo
5172286441Srpaulo		case IWM_STATISTICS_NOTIFICATION: {
5173286441Srpaulo			struct iwm_notif_statistics *stats;
5174286441Srpaulo			SYNC_RESP_STRUCT(stats, pkt);
5175286441Srpaulo			memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
5176330144Seadler			sc->sc_noise = iwm_get_noise(sc, &stats->rx.general);
5177286441Srpaulo			break; }
5178286441Srpaulo
5179286441Srpaulo		case IWM_NVM_ACCESS_CMD:
5180303628Ssbruno		case IWM_MCC_UPDATE_CMD:
5181286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
5182286441Srpaulo				bus_dmamap_sync(sc->rxq.data_dmat, data->map,
5183286441Srpaulo				    BUS_DMASYNC_POSTREAD);
5184286441Srpaulo				memcpy(sc->sc_cmd_resp,
5185286441Srpaulo				    pkt, sizeof(sc->sc_cmd_resp));
5186286441Srpaulo			}
5187286441Srpaulo			break;
5188286441Srpaulo
5189303628Ssbruno		case IWM_MCC_CHUB_UPDATE_CMD: {
5190303628Ssbruno			struct iwm_mcc_chub_notif *notif;
5191303628Ssbruno			SYNC_RESP_STRUCT(notif, pkt);
5192303628Ssbruno
5193303628Ssbruno			sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8;
5194303628Ssbruno			sc->sc_fw_mcc[1] = notif->mcc & 0xff;
5195303628Ssbruno			sc->sc_fw_mcc[2] = '\0';
5196303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_RESET,
5197303628Ssbruno			    "fw source %d sent CC '%s'\n",
5198303628Ssbruno			    notif->source_id, sc->sc_fw_mcc);
5199303628Ssbruno			break; }
5200303628Ssbruno
5201303628Ssbruno		case IWM_DTS_MEASUREMENT_NOTIFICATION:
5202303628Ssbruno			break;
5203303628Ssbruno
5204286441Srpaulo		case IWM_PHY_CONFIGURATION_CMD:
5205286441Srpaulo		case IWM_TX_ANT_CONFIGURATION_CMD:
5206286441Srpaulo		case IWM_ADD_STA:
5207286441Srpaulo		case IWM_MAC_CONTEXT_CMD:
5208286441Srpaulo		case IWM_REPLY_SF_CFG_CMD:
5209286441Srpaulo		case IWM_POWER_TABLE_CMD:
5210286441Srpaulo		case IWM_PHY_CONTEXT_CMD:
5211286441Srpaulo		case IWM_BINDING_CONTEXT_CMD:
5212286441Srpaulo		case IWM_TIME_EVENT_CMD:
5213303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD):
5214303628Ssbruno		case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC):
5215303628Ssbruno		case IWM_SCAN_OFFLOAD_REQUEST_CMD:
5216286441Srpaulo		case IWM_REPLY_BEACON_FILTERING_CMD:
5217286441Srpaulo		case IWM_MAC_PM_POWER_TABLE:
5218286441Srpaulo		case IWM_TIME_QUOTA_CMD:
5219286441Srpaulo		case IWM_REMOVE_STA:
5220286441Srpaulo		case IWM_TXPATH_FLUSH:
5221286441Srpaulo		case IWM_LQ_CMD:
5222303628Ssbruno		case IWM_BT_CONFIG:
5223303628Ssbruno		case IWM_REPLY_THERMAL_MNG_BACKOFF:
5224286441Srpaulo			SYNC_RESP_STRUCT(cresp, pkt);
5225286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
5226286441Srpaulo				memcpy(sc->sc_cmd_resp,
5227286441Srpaulo				    pkt, sizeof(*pkt)+sizeof(*cresp));
5228286441Srpaulo			}
5229286441Srpaulo			break;
5230286441Srpaulo
5231286441Srpaulo		/* ignore */
5232286441Srpaulo		case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */
5233286441Srpaulo			break;
5234286441Srpaulo
5235286441Srpaulo		case IWM_INIT_COMPLETE_NOTIF:
5236286441Srpaulo			sc->sc_init_complete = 1;
5237286441Srpaulo			wakeup(&sc->sc_init_complete);
5238286441Srpaulo			break;
5239286441Srpaulo
5240303628Ssbruno		case IWM_SCAN_OFFLOAD_COMPLETE: {
5241303628Ssbruno			struct iwm_periodic_scan_complete *notif;
5242286441Srpaulo			SYNC_RESP_STRUCT(notif, pkt);
5243303628Ssbruno			break;
5244303628Ssbruno		}
5245303628Ssbruno
5246303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE: {
5247303628Ssbruno			struct iwm_lmac_scan_complete_notif *notif;
5248303628Ssbruno 			SYNC_RESP_STRUCT(notif, pkt);
5249303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5250303628Ssbruno 			break;
5251303628Ssbruno		}
5252303628Ssbruno
5253303628Ssbruno		case IWM_SCAN_COMPLETE_UMAC: {
5254303628Ssbruno			struct iwm_umac_scan_complete *notif;
5255303628Ssbruno			SYNC_RESP_STRUCT(notif, pkt);
5256303628Ssbruno
5257303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
5258303628Ssbruno			    "UMAC scan complete, status=0x%x\n",
5259303628Ssbruno			    notif->status);
5260303628Ssbruno#if 0	/* XXX This would be a duplicate scan end call */
5261286441Srpaulo			taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task);
5262303628Ssbruno#endif
5263303628Ssbruno			break;
5264303628Ssbruno		}
5265286441Srpaulo
5266303628Ssbruno		case IWM_SCAN_ITERATION_COMPLETE_UMAC: {
5267303628Ssbruno			struct iwm_umac_scan_iter_complete_notif *notif;
5268303628Ssbruno			SYNC_RESP_STRUCT(notif, pkt);
5269303628Ssbruno
5270303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration "
5271303628Ssbruno			    "complete, status=0x%x, %d channels scanned\n",
5272303628Ssbruno			    notif->status, notif->scanned_channels);
5273303628Ssbruno			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
5274303628Ssbruno			break;
5275303628Ssbruno		}
5276303628Ssbruno
5277286441Srpaulo		case IWM_REPLY_ERROR: {
5278286441Srpaulo			struct iwm_error_resp *resp;
5279286441Srpaulo			SYNC_RESP_STRUCT(resp, pkt);
5280286441Srpaulo
5281286441Srpaulo			device_printf(sc->sc_dev,
5282286441Srpaulo			    "firmware error 0x%x, cmd 0x%x\n",
5283286441Srpaulo			    le32toh(resp->error_type),
5284286441Srpaulo			    resp->cmd_id);
5285303628Ssbruno			break;
5286303628Ssbruno		}
5287286441Srpaulo
5288286441Srpaulo		case IWM_TIME_EVENT_NOTIFICATION: {
5289286441Srpaulo			struct iwm_time_event_notif *notif;
5290286441Srpaulo			SYNC_RESP_STRUCT(notif, pkt);
5291286441Srpaulo
5292286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5293300833Sadrian			    "TE notif status = 0x%x action = 0x%x\n",
5294303628Ssbruno			    notif->status, notif->action);
5295303628Ssbruno			break;
5296303628Ssbruno		}
5297286441Srpaulo
5298286441Srpaulo		case IWM_MCAST_FILTER_CMD:
5299286441Srpaulo			break;
5300286441Srpaulo
5301303628Ssbruno		case IWM_SCD_QUEUE_CFG: {
5302303628Ssbruno			struct iwm_scd_txq_cfg_rsp *rsp;
5303303628Ssbruno			SYNC_RESP_STRUCT(rsp, pkt);
5304303628Ssbruno
5305303628Ssbruno			IWM_DPRINTF(sc, IWM_DEBUG_CMD,
5306303628Ssbruno			    "queue cfg token=0x%x sta_id=%d "
5307303628Ssbruno			    "tid=%d scd_queue=%d\n",
5308303628Ssbruno			    rsp->token, rsp->sta_id, rsp->tid,
5309303628Ssbruno			    rsp->scd_queue);
5310303628Ssbruno			break;
5311303628Ssbruno		}
5312303628Ssbruno
5313286441Srpaulo		default:
5314286441Srpaulo			device_printf(sc->sc_dev,
5315286441Srpaulo			    "frame %d/%d %x UNHANDLED (this should "
5316286441Srpaulo			    "not happen)\n", qid, idx,
5317286441Srpaulo			    pkt->len_n_flags);
5318286441Srpaulo			break;
5319286441Srpaulo		}
5320286441Srpaulo
5321286441Srpaulo		/*
5322286441Srpaulo		 * Why test bit 0x80?  The Linux driver:
5323286441Srpaulo		 *
5324286441Srpaulo		 * There is one exception:  uCode sets bit 15 when it
5325286441Srpaulo		 * originates the response/notification, i.e. when the
5326286441Srpaulo		 * response/notification is not a direct response to a
5327286441Srpaulo		 * command sent by the driver.  For example, uCode issues
5328286441Srpaulo		 * IWM_REPLY_RX when it sends a received frame to the driver;
5329286441Srpaulo		 * it is not a direct response to any driver command.
5330286441Srpaulo		 *
5331286441Srpaulo		 * Ok, so since when is 7 == 15?  Well, the Linux driver
5332286441Srpaulo		 * uses a slightly different format for pkt->hdr, and "qid"
5333286441Srpaulo		 * is actually the upper byte of a two-byte field.
5334286441Srpaulo		 */
5335286441Srpaulo		if (!(pkt->hdr.qid & (1 << 7))) {
5336286441Srpaulo			iwm_cmd_done(sc, pkt);
5337286441Srpaulo		}
5338286441Srpaulo
5339286441Srpaulo		ADVANCE_RXQ(sc);
5340286441Srpaulo	}
5341286441Srpaulo
5342286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
5343286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
5344286441Srpaulo
5345286441Srpaulo	/*
5346286441Srpaulo	 * Tell the firmware what we have processed.
5347286441Srpaulo	 * Seems like the hardware gets upset unless we align
5348286441Srpaulo	 * the write by 8??
5349286441Srpaulo	 */
5350286441Srpaulo	hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1;
5351286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7);
5352286441Srpaulo}
5353286441Srpaulo
5354286441Srpaulostatic void
5355286441Srpauloiwm_intr(void *arg)
5356286441Srpaulo{
5357286441Srpaulo	struct iwm_softc *sc = arg;
5358286441Srpaulo	int handled = 0;
5359286441Srpaulo	int r1, r2, rv = 0;
5360286441Srpaulo	int isperiodic = 0;
5361286441Srpaulo
5362286441Srpaulo	IWM_LOCK(sc);
5363286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
5364286441Srpaulo
5365286441Srpaulo	if (sc->sc_flags & IWM_FLAG_USE_ICT) {
5366286441Srpaulo		uint32_t *ict = sc->ict_dma.vaddr;
5367286441Srpaulo		int tmp;
5368286441Srpaulo
5369286441Srpaulo		tmp = htole32(ict[sc->ict_cur]);
5370286441Srpaulo		if (!tmp)
5371286441Srpaulo			goto out_ena;
5372286441Srpaulo
5373286441Srpaulo		/*
5374286441Srpaulo		 * ok, there was something.  keep plowing until we have all.
5375286441Srpaulo		 */
5376286441Srpaulo		r1 = r2 = 0;
5377286441Srpaulo		while (tmp) {
5378286441Srpaulo			r1 |= tmp;
5379286441Srpaulo			ict[sc->ict_cur] = 0;
5380286441Srpaulo			sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT;
5381286441Srpaulo			tmp = htole32(ict[sc->ict_cur]);
5382286441Srpaulo		}
5383286441Srpaulo
5384286441Srpaulo		/* this is where the fun begins.  don't ask */
5385286441Srpaulo		if (r1 == 0xffffffff)
5386286441Srpaulo			r1 = 0;
5387286441Srpaulo
5388286441Srpaulo		/* i am not expected to understand this */
5389286441Srpaulo		if (r1 & 0xc0000)
5390286441Srpaulo			r1 |= 0x8000;
5391286441Srpaulo		r1 = (0xff & r1) | ((0xff00 & r1) << 16);
5392286441Srpaulo	} else {
5393286441Srpaulo		r1 = IWM_READ(sc, IWM_CSR_INT);
5394286441Srpaulo		/* "hardware gone" (where, fishing?) */
5395286441Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
5396286441Srpaulo			goto out;
5397286441Srpaulo		r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS);
5398286441Srpaulo	}
5399286441Srpaulo	if (r1 == 0 && r2 == 0) {
5400286441Srpaulo		goto out_ena;
5401286441Srpaulo	}
5402286441Srpaulo
5403286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask);
5404286441Srpaulo
5405286441Srpaulo	/* ignored */
5406286441Srpaulo	handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/));
5407286441Srpaulo
5408286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_SW_ERR) {
5409286441Srpaulo		int i;
5410287197Sglebius		struct ieee80211com *ic = &sc->sc_ic;
5411286441Srpaulo		struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5412286441Srpaulo
5413298793Sdchagin#ifdef IWM_DEBUG
5414286441Srpaulo		iwm_nic_error(sc);
5415298793Sdchagin#endif
5416286441Srpaulo		/* Dump driver status (TX and RX rings) while we're here. */
5417286441Srpaulo		device_printf(sc->sc_dev, "driver status:\n");
5418286441Srpaulo		for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) {
5419286441Srpaulo			struct iwm_tx_ring *ring = &sc->txq[i];
5420286441Srpaulo			device_printf(sc->sc_dev,
5421286441Srpaulo			    "  tx ring %2d: qid=%-2d cur=%-3d "
5422286441Srpaulo			    "queued=%-3d\n",
5423286441Srpaulo			    i, ring->qid, ring->cur, ring->queued);
5424286441Srpaulo		}
5425286441Srpaulo		device_printf(sc->sc_dev,
5426286441Srpaulo		    "  rx ring: cur=%d\n", sc->rxq.cur);
5427286441Srpaulo		device_printf(sc->sc_dev,
5428298659Scem		    "  802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state);
5429286441Srpaulo
5430298594Sadrian		/* Don't stop the device; just do a VAP restart */
5431298594Sadrian		IWM_UNLOCK(sc);
5432286441Srpaulo
5433298594Sadrian		if (vap == NULL) {
5434298594Sadrian			printf("%s: null vap\n", __func__);
5435298594Sadrian			return;
5436298594Sadrian		}
5437298594Sadrian
5438298594Sadrian		device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; "
5439298594Sadrian		    "restarting\n", __func__, vap->iv_state);
5440298594Sadrian
5441298594Sadrian		/* XXX TODO: turn this into a callout/taskqueue */
5442298594Sadrian		ieee80211_restart_all(ic);
5443298594Sadrian		return;
5444286441Srpaulo	}
5445286441Srpaulo
5446286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_HW_ERR) {
5447286441Srpaulo		handled |= IWM_CSR_INT_BIT_HW_ERR;
5448286441Srpaulo		device_printf(sc->sc_dev, "hardware error, stopping device\n");
5449287197Sglebius		iwm_stop(sc);
5450286441Srpaulo		rv = 1;
5451286441Srpaulo		goto out;
5452286441Srpaulo	}
5453286441Srpaulo
5454286441Srpaulo	/* firmware chunk loaded */
5455286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_FH_TX) {
5456286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK);
5457286441Srpaulo		handled |= IWM_CSR_INT_BIT_FH_TX;
5458286441Srpaulo		sc->sc_fw_chunk_done = 1;
5459286441Srpaulo		wakeup(&sc->sc_fw);
5460286441Srpaulo	}
5461286441Srpaulo
5462286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RF_KILL) {
5463286441Srpaulo		handled |= IWM_CSR_INT_BIT_RF_KILL;
5464287197Sglebius		if (iwm_check_rfkill(sc)) {
5465286441Srpaulo			device_printf(sc->sc_dev,
5466286441Srpaulo			    "%s: rfkill switch, disabling interface\n",
5467286441Srpaulo			    __func__);
5468287197Sglebius			iwm_stop(sc);
5469286441Srpaulo		}
5470286441Srpaulo	}
5471286441Srpaulo
5472286441Srpaulo	/*
5473286441Srpaulo	 * The Linux driver uses periodic interrupts to avoid races.
5474286441Srpaulo	 * We cargo-cult like it's going out of fashion.
5475286441Srpaulo	 */
5476286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
5477286441Srpaulo		handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
5478286441Srpaulo		IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
5479286441Srpaulo		if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0)
5480286441Srpaulo			IWM_WRITE_1(sc,
5481286441Srpaulo			    IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
5482286441Srpaulo		isperiodic = 1;
5483286441Srpaulo	}
5484286441Srpaulo
5485286441Srpaulo	if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) {
5486286441Srpaulo		handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
5487286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
5488286441Srpaulo
5489286441Srpaulo		iwm_notif_intr(sc);
5490286441Srpaulo
5491286441Srpaulo		/* enable periodic interrupt, see above */
5492286441Srpaulo		if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic)
5493286441Srpaulo			IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG,
5494286441Srpaulo			    IWM_CSR_INT_PERIODIC_ENA);
5495286441Srpaulo	}
5496286441Srpaulo
5497286441Srpaulo	if (__predict_false(r1 & ~handled))
5498286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
5499286441Srpaulo		    "%s: unhandled interrupts: %x\n", __func__, r1);
5500286441Srpaulo	rv = 1;
5501286441Srpaulo
5502286441Srpaulo out_ena:
5503286441Srpaulo	iwm_restore_interrupts(sc);
5504286441Srpaulo out:
5505286441Srpaulo	IWM_UNLOCK(sc);
5506286441Srpaulo	return;
5507286441Srpaulo}
5508286441Srpaulo
5509286441Srpaulo/*
5510286441Srpaulo * Autoconf glue-sniffing
5511286441Srpaulo */
5512286441Srpaulo#define	PCI_VENDOR_INTEL		0x8086
5513286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_1	0x08b3
5514286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_2	0x08b4
5515303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_1	0x3165
5516303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_3165_2	0x3166
5517286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_1	0x08b1
5518286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_2	0x08b2
5519286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_1	0x095a
5520286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_2	0x095b
5521303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_1	0x24f3
5522303628Ssbruno#define	PCI_PRODUCT_INTEL_WL_8260_2	0x24f4
5523286441Srpaulo
5524286441Srpaulostatic const struct iwm_devices {
5525286441Srpaulo	uint16_t	device;
5526286441Srpaulo	const char	*name;
5527286441Srpaulo} iwm_devices[] = {
5528286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" },
5529286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" },
5530303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" },
5531303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" },
5532286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" },
5533286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" },
5534286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" },
5535286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" },
5536303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" },
5537303628Ssbruno	{ PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" },
5538286441Srpaulo};
5539286441Srpaulo
5540286441Srpaulostatic int
5541286441Srpauloiwm_probe(device_t dev)
5542286441Srpaulo{
5543286441Srpaulo	int i;
5544286441Srpaulo
5545303628Ssbruno	for (i = 0; i < nitems(iwm_devices); i++) {
5546286441Srpaulo		if (pci_get_vendor(dev) == PCI_VENDOR_INTEL &&
5547286441Srpaulo		    pci_get_device(dev) == iwm_devices[i].device) {
5548286441Srpaulo			device_set_desc(dev, iwm_devices[i].name);
5549286441Srpaulo			return (BUS_PROBE_DEFAULT);
5550286441Srpaulo		}
5551303628Ssbruno	}
5552286441Srpaulo
5553286441Srpaulo	return (ENXIO);
5554286441Srpaulo}
5555286441Srpaulo
5556286441Srpaulostatic int
5557286441Srpauloiwm_dev_check(device_t dev)
5558286441Srpaulo{
5559286441Srpaulo	struct iwm_softc *sc;
5560286441Srpaulo
5561286441Srpaulo	sc = device_get_softc(dev);
5562286441Srpaulo
5563303628Ssbruno	sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV);
5564286441Srpaulo	switch (pci_get_device(dev)) {
5565286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_1:
5566286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_2:
5567286441Srpaulo		sc->sc_fwname = "iwm3160fw";
5568286441Srpaulo		sc->host_interrupt_operation_mode = 1;
5569303628Ssbruno		sc->sc_device_family = IWM_DEVICE_FAMILY_7000;
5570303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5571286441Srpaulo		return (0);
5572303628Ssbruno	case PCI_PRODUCT_INTEL_WL_3165_1:
5573303628Ssbruno	case PCI_PRODUCT_INTEL_WL_3165_2:
5574303628Ssbruno		sc->sc_fwname = "iwm7265fw";
5575303628Ssbruno		sc->host_interrupt_operation_mode = 0;
5576303628Ssbruno		sc->sc_device_family = IWM_DEVICE_FAMILY_7000;
5577303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5578303628Ssbruno		return (0);
5579286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_1:
5580286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_2:
5581286441Srpaulo		sc->sc_fwname = "iwm7260fw";
5582286441Srpaulo		sc->host_interrupt_operation_mode = 1;
5583303628Ssbruno		sc->sc_device_family = IWM_DEVICE_FAMILY_7000;
5584303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5585286441Srpaulo		return (0);
5586286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_1:
5587286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_2:
5588286441Srpaulo		sc->sc_fwname = "iwm7265fw";
5589286441Srpaulo		sc->host_interrupt_operation_mode = 0;
5590303628Ssbruno		sc->sc_device_family = IWM_DEVICE_FAMILY_7000;
5591303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
5592286441Srpaulo		return (0);
5593303628Ssbruno	case PCI_PRODUCT_INTEL_WL_8260_1:
5594303628Ssbruno	case PCI_PRODUCT_INTEL_WL_8260_2:
5595303628Ssbruno		sc->sc_fwname = "iwm8000Cfw";
5596303628Ssbruno		sc->host_interrupt_operation_mode = 0;
5597303628Ssbruno		sc->sc_device_family = IWM_DEVICE_FAMILY_8000;
5598303628Ssbruno		sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000;
5599303628Ssbruno		return (0);
5600286441Srpaulo	default:
5601286441Srpaulo		device_printf(dev, "unknown adapter type\n");
5602286441Srpaulo		return ENXIO;
5603286441Srpaulo	}
5604286441Srpaulo}
5605286441Srpaulo
5606330140Seadler/* PCI registers */
5607330140Seadler#define PCI_CFG_RETRY_TIMEOUT	0x041
5608330140Seadler
5609286441Srpaulostatic int
5610286441Srpauloiwm_pci_attach(device_t dev)
5611286441Srpaulo{
5612286441Srpaulo	struct iwm_softc *sc;
5613286441Srpaulo	int count, error, rid;
5614286441Srpaulo	uint16_t reg;
5615286441Srpaulo
5616286441Srpaulo	sc = device_get_softc(dev);
5617286441Srpaulo
5618330140Seadler	/* We disable the RETRY_TIMEOUT register (0x41) to keep
5619330140Seadler	 * PCI Tx retries from interfering with C3 CPU state */
5620330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
5621286441Srpaulo
5622286441Srpaulo	/* Enable bus-mastering and hardware bug workaround. */
5623286441Srpaulo	pci_enable_busmaster(dev);
5624286441Srpaulo	reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg));
5625286441Srpaulo	/* if !MSI */
5626286441Srpaulo	if (reg & PCIM_STATUS_INTxSTATE) {
5627286441Srpaulo		reg &= ~PCIM_STATUS_INTxSTATE;
5628286441Srpaulo	}
5629286441Srpaulo	pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg));
5630286441Srpaulo
5631286441Srpaulo	rid = PCIR_BAR(0);
5632286441Srpaulo	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
5633286441Srpaulo	    RF_ACTIVE);
5634286441Srpaulo	if (sc->sc_mem == NULL) {
5635286441Srpaulo		device_printf(sc->sc_dev, "can't map mem space\n");
5636286441Srpaulo		return (ENXIO);
5637286441Srpaulo	}
5638286441Srpaulo	sc->sc_st = rman_get_bustag(sc->sc_mem);
5639286441Srpaulo	sc->sc_sh = rman_get_bushandle(sc->sc_mem);
5640286441Srpaulo
5641286441Srpaulo	/* Install interrupt handler. */
5642286441Srpaulo	count = 1;
5643286441Srpaulo	rid = 0;
5644286441Srpaulo	if (pci_alloc_msi(dev, &count) == 0)
5645286441Srpaulo		rid = 1;
5646286441Srpaulo	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
5647286441Srpaulo	    (rid != 0 ? 0 : RF_SHAREABLE));
5648286441Srpaulo	if (sc->sc_irq == NULL) {
5649286441Srpaulo		device_printf(dev, "can't map interrupt\n");
5650286441Srpaulo			return (ENXIO);
5651286441Srpaulo	}
5652286441Srpaulo	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
5653286441Srpaulo	    NULL, iwm_intr, sc, &sc->sc_ih);
5654286441Srpaulo	if (sc->sc_ih == NULL) {
5655286441Srpaulo		device_printf(dev, "can't establish interrupt");
5656286441Srpaulo			return (ENXIO);
5657286441Srpaulo	}
5658286441Srpaulo	sc->sc_dmat = bus_get_dma_tag(sc->sc_dev);
5659286441Srpaulo
5660286441Srpaulo	return (0);
5661286441Srpaulo}
5662286441Srpaulo
5663286441Srpaulostatic void
5664286441Srpauloiwm_pci_detach(device_t dev)
5665286441Srpaulo{
5666286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
5667286441Srpaulo
5668286441Srpaulo	if (sc->sc_irq != NULL) {
5669286441Srpaulo		bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
5670286441Srpaulo		bus_release_resource(dev, SYS_RES_IRQ,
5671286441Srpaulo		    rman_get_rid(sc->sc_irq), sc->sc_irq);
5672286441Srpaulo		pci_release_msi(dev);
5673286441Srpaulo        }
5674286441Srpaulo	if (sc->sc_mem != NULL)
5675286441Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY,
5676286441Srpaulo		    rman_get_rid(sc->sc_mem), sc->sc_mem);
5677286441Srpaulo}
5678286441Srpaulo
5679286441Srpaulo
5680286441Srpaulo
5681286441Srpaulostatic int
5682286441Srpauloiwm_attach(device_t dev)
5683286441Srpaulo{
5684287197Sglebius	struct iwm_softc *sc = device_get_softc(dev);
5685287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
5686286441Srpaulo	int error;
5687286441Srpaulo	int txq_i, i;
5688286441Srpaulo
5689286441Srpaulo	sc->sc_dev = dev;
5690293099Savos	IWM_LOCK_INIT(sc);
5691287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
5692286441Srpaulo	callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0);
5693301187Sadrian	callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0);
5694286441Srpaulo	TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc);
5695286441Srpaulo
5696286441Srpaulo	/* PCI attach */
5697286441Srpaulo	error = iwm_pci_attach(dev);
5698286441Srpaulo	if (error != 0)
5699286441Srpaulo		goto fail;
5700286441Srpaulo
5701286441Srpaulo	sc->sc_wantresp = -1;
5702286441Srpaulo
5703286441Srpaulo	/* Check device type */
5704286441Srpaulo	error = iwm_dev_check(dev);
5705286441Srpaulo	if (error != 0)
5706286441Srpaulo		goto fail;
5707286441Srpaulo
5708286441Srpaulo	/*
5709286441Srpaulo	 * We now start fiddling with the hardware
5710286441Srpaulo	 */
5711303628Ssbruno	/*
5712303628Ssbruno	 * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
5713303628Ssbruno	 * changed, and now the revision step also includes bit 0-1 (no more
5714303628Ssbruno	 * "dash" value). To keep hw_rev backwards compatible - we'll store it
5715303628Ssbruno	 * in the old format.
5716303628Ssbruno	 */
5717303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000)
5718303628Ssbruno		sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) |
5719303628Ssbruno				(IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2);
5720303628Ssbruno
5721286441Srpaulo	if (iwm_prepare_card_hw(sc) != 0) {
5722286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
5723286441Srpaulo		goto fail;
5724286441Srpaulo	}
5725286441Srpaulo
5726303628Ssbruno	if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) {
5727303628Ssbruno		int ret;
5728303628Ssbruno		uint32_t hw_step;
5729303628Ssbruno
5730303628Ssbruno		/*
5731303628Ssbruno		 * In order to recognize C step the driver should read the
5732303628Ssbruno		 * chip version id located at the AUX bus MISC address.
5733303628Ssbruno		 */
5734303628Ssbruno		IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
5735303628Ssbruno			    IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
5736303628Ssbruno		DELAY(2);
5737303628Ssbruno
5738303628Ssbruno		ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL,
5739303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
5740303628Ssbruno				   IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
5741303628Ssbruno				   25000);
5742330149Seadler		if (!ret) {
5743303628Ssbruno			device_printf(sc->sc_dev,
5744303628Ssbruno			    "Failed to wake up the nic\n");
5745303628Ssbruno			goto fail;
5746303628Ssbruno		}
5747303628Ssbruno
5748303628Ssbruno		if (iwm_nic_lock(sc)) {
5749303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG);
5750303628Ssbruno			hw_step |= IWM_ENABLE_WFPM;
5751303628Ssbruno			iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step);
5752303628Ssbruno			hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG);
5753303628Ssbruno			hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF;
5754303628Ssbruno			if (hw_step == 0x3)
5755303628Ssbruno				sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) |
5756303628Ssbruno						(IWM_SILICON_C_STEP << 2);
5757303628Ssbruno			iwm_nic_unlock(sc);
5758303628Ssbruno		} else {
5759303628Ssbruno			device_printf(sc->sc_dev, "Failed to lock the nic\n");
5760303628Ssbruno			goto fail;
5761303628Ssbruno		}
5762303628Ssbruno	}
5763303628Ssbruno
5764286441Srpaulo	/* Allocate DMA memory for firmware transfers. */
5765286441Srpaulo	if ((error = iwm_alloc_fwmem(sc)) != 0) {
5766286441Srpaulo		device_printf(dev, "could not allocate memory for firmware\n");
5767286441Srpaulo		goto fail;
5768286441Srpaulo	}
5769286441Srpaulo
5770286441Srpaulo	/* Allocate "Keep Warm" page. */
5771286441Srpaulo	if ((error = iwm_alloc_kw(sc)) != 0) {
5772286441Srpaulo		device_printf(dev, "could not allocate keep warm page\n");
5773286441Srpaulo		goto fail;
5774286441Srpaulo	}
5775286441Srpaulo
5776286441Srpaulo	/* We use ICT interrupts */
5777286441Srpaulo	if ((error = iwm_alloc_ict(sc)) != 0) {
5778286441Srpaulo		device_printf(dev, "could not allocate ICT table\n");
5779286441Srpaulo		goto fail;
5780286441Srpaulo	}
5781286441Srpaulo
5782286441Srpaulo	/* Allocate TX scheduler "rings". */
5783286441Srpaulo	if ((error = iwm_alloc_sched(sc)) != 0) {
5784286441Srpaulo		device_printf(dev, "could not allocate TX scheduler rings\n");
5785286441Srpaulo		goto fail;
5786286441Srpaulo	}
5787286441Srpaulo
5788286441Srpaulo	/* Allocate TX rings */
5789286441Srpaulo	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) {
5790286441Srpaulo		if ((error = iwm_alloc_tx_ring(sc,
5791286441Srpaulo		    &sc->txq[txq_i], txq_i)) != 0) {
5792286441Srpaulo			device_printf(dev,
5793286441Srpaulo			    "could not allocate TX ring %d\n",
5794286441Srpaulo			    txq_i);
5795286441Srpaulo			goto fail;
5796286441Srpaulo		}
5797286441Srpaulo	}
5798286441Srpaulo
5799286441Srpaulo	/* Allocate RX ring. */
5800286441Srpaulo	if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) {
5801286441Srpaulo		device_printf(dev, "could not allocate RX ring\n");
5802286441Srpaulo		goto fail;
5803286441Srpaulo	}
5804286441Srpaulo
5805286441Srpaulo	/* Clear pending interrupts. */
5806286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff);
5807286441Srpaulo
5808286441Srpaulo	ic->ic_softc = sc;
5809286441Srpaulo	ic->ic_name = device_get_nameunit(sc->sc_dev);
5810286441Srpaulo	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
5811286441Srpaulo	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
5812286441Srpaulo
5813286441Srpaulo	/* Set device capabilities. */
5814286441Srpaulo	ic->ic_caps =
5815286441Srpaulo	    IEEE80211_C_STA |
5816286441Srpaulo	    IEEE80211_C_WPA |		/* WPA/RSN */
5817286441Srpaulo	    IEEE80211_C_WME |
5818286441Srpaulo	    IEEE80211_C_SHSLOT |	/* short slot time supported */
5819286441Srpaulo	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
5820286441Srpaulo//	    IEEE80211_C_BGSCAN		/* capable of bg scanning */
5821286441Srpaulo	    ;
5822286441Srpaulo	for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
5823286441Srpaulo		sc->sc_phyctxt[i].id = i;
5824286441Srpaulo		sc->sc_phyctxt[i].color = 0;
5825286441Srpaulo		sc->sc_phyctxt[i].ref = 0;
5826286441Srpaulo		sc->sc_phyctxt[i].channel = NULL;
5827286441Srpaulo	}
5828286441Srpaulo
5829330144Seadler	/* Default noise floor */
5830330144Seadler	sc->sc_noise = -96;
5831330144Seadler
5832286441Srpaulo	/* Max RSSI */
5833286441Srpaulo	sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM;
5834330144Seadler
5835286441Srpaulo	sc->sc_preinit_hook.ich_func = iwm_preinit;
5836286441Srpaulo	sc->sc_preinit_hook.ich_arg = sc;
5837286441Srpaulo	if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
5838286441Srpaulo		device_printf(dev, "config_intrhook_establish failed\n");
5839286441Srpaulo		goto fail;
5840286441Srpaulo	}
5841286441Srpaulo
5842286441Srpaulo#ifdef IWM_DEBUG
5843286441Srpaulo	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
5844286441Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug",
5845286441Srpaulo	    CTLFLAG_RW, &sc->sc_debug, 0, "control debugging");
5846286441Srpaulo#endif
5847286441Srpaulo
5848286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
5849286441Srpaulo	    "<-%s\n", __func__);
5850286441Srpaulo
5851286441Srpaulo	return 0;
5852286441Srpaulo
5853286441Srpaulo	/* Free allocated memory if something failed during attachment. */
5854286441Srpaulofail:
5855286441Srpaulo	iwm_detach_local(sc, 0);
5856286441Srpaulo
5857286441Srpaulo	return ENXIO;
5858286441Srpaulo}
5859286441Srpaulo
5860286441Srpaulostatic int
5861303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr)
5862303628Ssbruno{
5863303628Ssbruno	char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
5864303628Ssbruno
5865303628Ssbruno	if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr))
5866303628Ssbruno		return (FALSE);
5867303628Ssbruno
5868303628Ssbruno	return (TRUE);
5869303628Ssbruno}
5870303628Ssbruno
5871303628Ssbrunostatic int
5872286441Srpauloiwm_update_edca(struct ieee80211com *ic)
5873286441Srpaulo{
5874286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
5875286441Srpaulo
5876286441Srpaulo	device_printf(sc->sc_dev, "%s: called\n", __func__);
5877286441Srpaulo	return (0);
5878286441Srpaulo}
5879286441Srpaulo
5880286441Srpaulostatic void
5881286441Srpauloiwm_preinit(void *arg)
5882286441Srpaulo{
5883286441Srpaulo	struct iwm_softc *sc = arg;
5884286441Srpaulo	device_t dev = sc->sc_dev;
5885287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
5886286441Srpaulo	int error;
5887286441Srpaulo
5888286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
5889286441Srpaulo	    "->%s\n", __func__);
5890286441Srpaulo
5891286441Srpaulo	IWM_LOCK(sc);
5892286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
5893286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
5894286441Srpaulo		IWM_UNLOCK(sc);
5895286441Srpaulo		goto fail;
5896286441Srpaulo	}
5897286441Srpaulo
5898286441Srpaulo	error = iwm_run_init_mvm_ucode(sc, 1);
5899286441Srpaulo	iwm_stop_device(sc);
5900286441Srpaulo	if (error) {
5901286441Srpaulo		IWM_UNLOCK(sc);
5902286441Srpaulo		goto fail;
5903286441Srpaulo	}
5904286441Srpaulo	device_printf(dev,
5905303628Ssbruno	    "hw rev 0x%x, fw ver %s, address %s\n",
5906286441Srpaulo	    sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK,
5907303628Ssbruno	    sc->sc_fwver, ether_sprintf(sc->sc_nvm.hw_addr));
5908286441Srpaulo
5909286441Srpaulo	/* not all hardware can do 5GHz band */
5910286441Srpaulo	if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
5911286441Srpaulo		memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
5912286441Srpaulo		    sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A]));
5913286441Srpaulo	IWM_UNLOCK(sc);
5914286441Srpaulo
5915298877Savos	iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
5916298877Savos	    ic->ic_channels);
5917298877Savos
5918286441Srpaulo	/*
5919286441Srpaulo	 * At this point we've committed - if we fail to do setup,
5920286441Srpaulo	 * we now also have to tear down the net80211 state.
5921286441Srpaulo	 */
5922287197Sglebius	ieee80211_ifattach(ic);
5923286441Srpaulo	ic->ic_vap_create = iwm_vap_create;
5924286441Srpaulo	ic->ic_vap_delete = iwm_vap_delete;
5925286441Srpaulo	ic->ic_raw_xmit = iwm_raw_xmit;
5926286441Srpaulo	ic->ic_node_alloc = iwm_node_alloc;
5927286441Srpaulo	ic->ic_scan_start = iwm_scan_start;
5928286441Srpaulo	ic->ic_scan_end = iwm_scan_end;
5929286441Srpaulo	ic->ic_update_mcast = iwm_update_mcast;
5930298877Savos	ic->ic_getradiocaps = iwm_init_channel_map;
5931286441Srpaulo	ic->ic_set_channel = iwm_set_channel;
5932286441Srpaulo	ic->ic_scan_curchan = iwm_scan_curchan;
5933286441Srpaulo	ic->ic_scan_mindwell = iwm_scan_mindwell;
5934286441Srpaulo	ic->ic_wme.wme_update = iwm_update_edca;
5935287197Sglebius	ic->ic_parent = iwm_parent;
5936287197Sglebius	ic->ic_transmit = iwm_transmit;
5937286441Srpaulo	iwm_radiotap_attach(sc);
5938286441Srpaulo	if (bootverbose)
5939286441Srpaulo		ieee80211_announce(ic);
5940286441Srpaulo
5941286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
5942286441Srpaulo	    "<-%s\n", __func__);
5943286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
5944286441Srpaulo
5945286441Srpaulo	return;
5946286441Srpaulofail:
5947286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
5948286441Srpaulo	iwm_detach_local(sc, 0);
5949286441Srpaulo}
5950286441Srpaulo
5951286441Srpaulo/*
5952286441Srpaulo * Attach the interface to 802.11 radiotap.
5953286441Srpaulo */
5954286441Srpaulostatic void
5955286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc)
5956286441Srpaulo{
5957287197Sglebius        struct ieee80211com *ic = &sc->sc_ic;
5958286441Srpaulo
5959286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
5960286441Srpaulo	    "->%s begin\n", __func__);
5961286441Srpaulo        ieee80211_radiotap_attach(ic,
5962286441Srpaulo            &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
5963286441Srpaulo                IWM_TX_RADIOTAP_PRESENT,
5964286441Srpaulo            &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
5965286441Srpaulo                IWM_RX_RADIOTAP_PRESENT);
5966286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
5967286441Srpaulo	    "->%s end\n", __func__);
5968286441Srpaulo}
5969286441Srpaulo
5970286441Srpaulostatic struct ieee80211vap *
5971286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
5972286441Srpaulo    enum ieee80211_opmode opmode, int flags,
5973286441Srpaulo    const uint8_t bssid[IEEE80211_ADDR_LEN],
5974286441Srpaulo    const uint8_t mac[IEEE80211_ADDR_LEN])
5975286441Srpaulo{
5976286441Srpaulo	struct iwm_vap *ivp;
5977286441Srpaulo	struct ieee80211vap *vap;
5978286441Srpaulo
5979286441Srpaulo	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
5980286441Srpaulo		return NULL;
5981287197Sglebius	ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO);
5982286441Srpaulo	vap = &ivp->iv_vap;
5983287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
5984286441Srpaulo	vap->iv_bmissthreshold = 10;            /* override default */
5985286441Srpaulo	/* Override with driver methods. */
5986286441Srpaulo	ivp->iv_newstate = vap->iv_newstate;
5987286441Srpaulo	vap->iv_newstate = iwm_newstate;
5988286441Srpaulo
5989286441Srpaulo	ieee80211_ratectl_init(vap);
5990286441Srpaulo	/* Complete setup. */
5991287197Sglebius	ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status,
5992287197Sglebius	    mac);
5993286441Srpaulo	ic->ic_opmode = opmode;
5994286441Srpaulo
5995286441Srpaulo	return vap;
5996286441Srpaulo}
5997286441Srpaulo
5998286441Srpaulostatic void
5999286441Srpauloiwm_vap_delete(struct ieee80211vap *vap)
6000286441Srpaulo{
6001286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
6002286441Srpaulo
6003286441Srpaulo	ieee80211_ratectl_deinit(vap);
6004286441Srpaulo	ieee80211_vap_detach(vap);
6005286441Srpaulo	free(ivp, M_80211_VAP);
6006286441Srpaulo}
6007286441Srpaulo
6008286441Srpaulostatic void
6009286441Srpauloiwm_scan_start(struct ieee80211com *ic)
6010286441Srpaulo{
6011286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6012303628Ssbruno	struct iwm_softc *sc = ic->ic_softc;
6013286441Srpaulo	int error;
6014286441Srpaulo
6015286441Srpaulo	IWM_LOCK(sc);
6016303628Ssbruno	if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
6017303628Ssbruno		error = iwm_mvm_umac_scan(sc);
6018303628Ssbruno	else
6019303628Ssbruno		error = iwm_mvm_lmac_scan(sc);
6020303628Ssbruno	if (error != 0) {
6021301190Sadrian		device_printf(sc->sc_dev, "could not initiate 2 GHz scan\n");
6022286441Srpaulo		IWM_UNLOCK(sc);
6023286441Srpaulo		ieee80211_cancel_scan(vap);
6024301187Sadrian	} else {
6025301187Sadrian		iwm_led_blink_start(sc);
6026286441Srpaulo		IWM_UNLOCK(sc);
6027301187Sadrian	}
6028286441Srpaulo}
6029286441Srpaulo
6030286441Srpaulostatic void
6031286441Srpauloiwm_scan_end(struct ieee80211com *ic)
6032286441Srpaulo{
6033301187Sadrian	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6034301187Sadrian	struct iwm_softc *sc = ic->ic_softc;
6035301187Sadrian
6036301187Sadrian	IWM_LOCK(sc);
6037301187Sadrian	iwm_led_blink_stop(sc);
6038301187Sadrian	if (vap->iv_state == IEEE80211_S_RUN)
6039301187Sadrian		iwm_mvm_led_enable(sc);
6040301187Sadrian	IWM_UNLOCK(sc);
6041286441Srpaulo}
6042286441Srpaulo
6043286441Srpaulostatic void
6044286441Srpauloiwm_update_mcast(struct ieee80211com *ic)
6045286441Srpaulo{
6046286441Srpaulo}
6047286441Srpaulo
6048286441Srpaulostatic void
6049286441Srpauloiwm_set_channel(struct ieee80211com *ic)
6050286441Srpaulo{
6051286441Srpaulo}
6052286441Srpaulo
6053286441Srpaulostatic void
6054286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6055286441Srpaulo{
6056286441Srpaulo}
6057286441Srpaulo
6058286441Srpaulostatic void
6059286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss)
6060286441Srpaulo{
6061286441Srpaulo	return;
6062286441Srpaulo}
6063286441Srpaulo
6064286441Srpaulovoid
6065286441Srpauloiwm_init_task(void *arg1)
6066286441Srpaulo{
6067286441Srpaulo	struct iwm_softc *sc = arg1;
6068286441Srpaulo
6069286441Srpaulo	IWM_LOCK(sc);
6070286441Srpaulo	while (sc->sc_flags & IWM_FLAG_BUSY)
6071286441Srpaulo		msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0);
6072286441Srpaulo	sc->sc_flags |= IWM_FLAG_BUSY;
6073287197Sglebius	iwm_stop(sc);
6074287197Sglebius	if (sc->sc_ic.ic_nrunning > 0)
6075286441Srpaulo		iwm_init(sc);
6076286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_BUSY;
6077286441Srpaulo	wakeup(&sc->sc_flags);
6078286441Srpaulo	IWM_UNLOCK(sc);
6079286441Srpaulo}
6080286441Srpaulo
6081286441Srpaulostatic int
6082286441Srpauloiwm_resume(device_t dev)
6083286441Srpaulo{
6084298612Sadrian	struct iwm_softc *sc = device_get_softc(dev);
6085298612Sadrian	int do_reinit = 0;
6086286441Srpaulo
6087330140Seadler	/*
6088330140Seadler	 * We disable the RETRY_TIMEOUT register (0x41) to keep
6089330140Seadler	 * PCI Tx retries from interfering with C3 CPU state.
6090330140Seadler	 */
6091330140Seadler	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
6092286441Srpaulo	iwm_init_task(device_get_softc(dev));
6093286441Srpaulo
6094298612Sadrian	IWM_LOCK(sc);
6095303628Ssbruno	if (sc->sc_flags & IWM_FLAG_SCANNING) {
6096303628Ssbruno		sc->sc_flags &= ~IWM_FLAG_SCANNING;
6097298612Sadrian		do_reinit = 1;
6098298612Sadrian	}
6099298612Sadrian	IWM_UNLOCK(sc);
6100298612Sadrian
6101298612Sadrian	if (do_reinit)
6102298612Sadrian		ieee80211_resume_all(&sc->sc_ic);
6103298612Sadrian
6104286441Srpaulo	return 0;
6105286441Srpaulo}
6106286441Srpaulo
6107286441Srpaulostatic int
6108286441Srpauloiwm_suspend(device_t dev)
6109286441Srpaulo{
6110298612Sadrian	int do_stop = 0;
6111286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6112286441Srpaulo
6113298612Sadrian	do_stop = !! (sc->sc_ic.ic_nrunning > 0);
6114298612Sadrian
6115298612Sadrian	ieee80211_suspend_all(&sc->sc_ic);
6116298612Sadrian
6117298612Sadrian	if (do_stop) {
6118287197Sglebius		IWM_LOCK(sc);
6119287197Sglebius		iwm_stop(sc);
6120303628Ssbruno		sc->sc_flags |= IWM_FLAG_SCANNING;
6121287197Sglebius		IWM_UNLOCK(sc);
6122287197Sglebius	}
6123286441Srpaulo
6124286441Srpaulo	return (0);
6125286441Srpaulo}
6126286441Srpaulo
6127286441Srpaulostatic int
6128286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211)
6129286441Srpaulo{
6130293219Savos	struct iwm_fw_info *fw = &sc->sc_fw;
6131286441Srpaulo	device_t dev = sc->sc_dev;
6132286441Srpaulo	int i;
6133286441Srpaulo
6134303628Ssbruno	ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task);
6135303628Ssbruno
6136301187Sadrian	callout_drain(&sc->sc_led_blink_to);
6137287197Sglebius	callout_drain(&sc->sc_watchdog_to);
6138287197Sglebius	iwm_stop_device(sc);
6139303628Ssbruno	if (do_net80211) {
6140287197Sglebius		ieee80211_ifdetach(&sc->sc_ic);
6141303628Ssbruno	}
6142286441Srpaulo
6143302103Sadrian	iwm_phy_db_free(sc);
6144302103Sadrian
6145286441Srpaulo	/* Free descriptor rings */
6146301970Sadrian	iwm_free_rx_ring(sc, &sc->rxq);
6147286441Srpaulo	for (i = 0; i < nitems(sc->txq); i++)
6148286441Srpaulo		iwm_free_tx_ring(sc, &sc->txq[i]);
6149286441Srpaulo
6150293219Savos	/* Free firmware */
6151293219Savos	if (fw->fw_fp != NULL)
6152293219Savos		iwm_fw_info_free(fw);
6153293219Savos
6154293178Savos	/* Free scheduler */
6155330150Seadler	iwm_dma_contig_free(&sc->sched_dma);
6156330150Seadler	iwm_dma_contig_free(&sc->ict_dma);
6157330150Seadler	iwm_dma_contig_free(&sc->kw_dma);
6158330150Seadler	iwm_dma_contig_free(&sc->fw_dma);
6159286441Srpaulo
6160286441Srpaulo	/* Finished with the hardware - detach things */
6161286441Srpaulo	iwm_pci_detach(dev);
6162286441Srpaulo
6163287197Sglebius	mbufq_drain(&sc->sc_snd);
6164293099Savos	IWM_LOCK_DESTROY(sc);
6165286441Srpaulo
6166286441Srpaulo	return (0);
6167286441Srpaulo}
6168286441Srpaulo
6169286441Srpaulostatic int
6170286441Srpauloiwm_detach(device_t dev)
6171286441Srpaulo{
6172286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
6173286441Srpaulo
6174286441Srpaulo	return (iwm_detach_local(sc, 1));
6175286441Srpaulo}
6176286441Srpaulo
6177286441Srpaulostatic device_method_t iwm_pci_methods[] = {
6178286441Srpaulo        /* Device interface */
6179286441Srpaulo        DEVMETHOD(device_probe,         iwm_probe),
6180286441Srpaulo        DEVMETHOD(device_attach,        iwm_attach),
6181286441Srpaulo        DEVMETHOD(device_detach,        iwm_detach),
6182286441Srpaulo        DEVMETHOD(device_suspend,       iwm_suspend),
6183286441Srpaulo        DEVMETHOD(device_resume,        iwm_resume),
6184286441Srpaulo
6185286441Srpaulo        DEVMETHOD_END
6186286441Srpaulo};
6187286441Srpaulo
6188286441Srpaulostatic driver_t iwm_pci_driver = {
6189286441Srpaulo        "iwm",
6190286441Srpaulo        iwm_pci_methods,
6191286441Srpaulo        sizeof (struct iwm_softc)
6192286441Srpaulo};
6193286441Srpaulo
6194286441Srpaulostatic devclass_t iwm_devclass;
6195286441Srpaulo
6196286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL);
6197286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1);
6198286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1);
6199286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1);
6200