if_iwm.c revision 299883
1286441Srpaulo/*	$OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg 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: head/sys/dev/iwm/if_iwm.c 299883 2016-05-16 02:27:28Z kevlo $");
107286441Srpaulo
108286441Srpaulo#include <sys/param.h>
109286441Srpaulo#include <sys/bus.h>
110286441Srpaulo#include <sys/conf.h>
111286441Srpaulo#include <sys/endian.h>
112286441Srpaulo#include <sys/firmware.h>
113286441Srpaulo#include <sys/kernel.h>
114286441Srpaulo#include <sys/malloc.h>
115286441Srpaulo#include <sys/mbuf.h>
116286441Srpaulo#include <sys/mutex.h>
117286441Srpaulo#include <sys/module.h>
118286441Srpaulo#include <sys/proc.h>
119286441Srpaulo#include <sys/rman.h>
120286441Srpaulo#include <sys/socket.h>
121286441Srpaulo#include <sys/sockio.h>
122286441Srpaulo#include <sys/sysctl.h>
123286441Srpaulo#include <sys/linker.h>
124286441Srpaulo
125286441Srpaulo#include <machine/bus.h>
126286441Srpaulo#include <machine/endian.h>
127286441Srpaulo#include <machine/resource.h>
128286441Srpaulo
129286441Srpaulo#include <dev/pci/pcivar.h>
130286441Srpaulo#include <dev/pci/pcireg.h>
131286441Srpaulo
132286441Srpaulo#include <net/bpf.h>
133286441Srpaulo
134286441Srpaulo#include <net/if.h>
135286441Srpaulo#include <net/if_var.h>
136286441Srpaulo#include <net/if_arp.h>
137286441Srpaulo#include <net/if_dl.h>
138286441Srpaulo#include <net/if_media.h>
139286441Srpaulo#include <net/if_types.h>
140286441Srpaulo
141286441Srpaulo#include <netinet/in.h>
142286441Srpaulo#include <netinet/in_systm.h>
143286441Srpaulo#include <netinet/if_ether.h>
144286441Srpaulo#include <netinet/ip.h>
145286441Srpaulo
146286441Srpaulo#include <net80211/ieee80211_var.h>
147286441Srpaulo#include <net80211/ieee80211_regdomain.h>
148286441Srpaulo#include <net80211/ieee80211_ratectl.h>
149286441Srpaulo#include <net80211/ieee80211_radiotap.h>
150286441Srpaulo
151286475Srpaulo#include <dev/iwm/if_iwmreg.h>
152286475Srpaulo#include <dev/iwm/if_iwmvar.h>
153286475Srpaulo#include <dev/iwm/if_iwm_debug.h>
154286475Srpaulo#include <dev/iwm/if_iwm_util.h>
155286475Srpaulo#include <dev/iwm/if_iwm_binding.h>
156286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h>
157286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h>
158286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h>
159286475Srpaulo#include <dev/iwm/if_iwm_time_event.h>
160286475Srpaulo#include <dev/iwm/if_iwm_power.h>
161286475Srpaulo#include <dev/iwm/if_iwm_scan.h>
162286441Srpaulo
163286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h>
164286441Srpaulo
165286441Srpauloconst uint8_t iwm_nvm_channels[] = {
166286441Srpaulo	/* 2.4 GHz */
167286441Srpaulo	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
168286441Srpaulo	/* 5 GHz */
169298877Savos	36, 40, 44, 48, 52, 56, 60, 64,
170286441Srpaulo	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
171286441Srpaulo	149, 153, 157, 161, 165
172286441Srpaulo};
173286441Srpaulo#define IWM_NUM_2GHZ_CHANNELS	14
174286441Srpaulo
175298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS,
176298877Savos    "IWM_NUM_CHANNELS is too small");
177298877Savos
178286441Srpaulo/*
179286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries
180286441Srpaulo * that are populated.
181286441Srpaulo */
182286441Srpauloconst struct iwm_rate {
183286441Srpaulo	uint8_t rate;
184286441Srpaulo	uint8_t plcp;
185286441Srpaulo} iwm_rates[] = {
186286441Srpaulo	{   2,	IWM_RATE_1M_PLCP  },
187286441Srpaulo	{   4,	IWM_RATE_2M_PLCP  },
188286441Srpaulo	{  11,	IWM_RATE_5M_PLCP  },
189286441Srpaulo	{  22,	IWM_RATE_11M_PLCP },
190286441Srpaulo	{  12,	IWM_RATE_6M_PLCP  },
191286441Srpaulo	{  18,	IWM_RATE_9M_PLCP  },
192286441Srpaulo	{  24,	IWM_RATE_12M_PLCP },
193286441Srpaulo	{  36,	IWM_RATE_18M_PLCP },
194286441Srpaulo	{  48,	IWM_RATE_24M_PLCP },
195286441Srpaulo	{  72,	IWM_RATE_36M_PLCP },
196286441Srpaulo	{  96,	IWM_RATE_48M_PLCP },
197286441Srpaulo	{ 108,	IWM_RATE_54M_PLCP },
198286441Srpaulo};
199286441Srpaulo#define IWM_RIDX_CCK	0
200286441Srpaulo#define IWM_RIDX_OFDM	4
201286441Srpaulo#define IWM_RIDX_MAX	(nitems(iwm_rates)-1)
202286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
203286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
204286441Srpaulo
205286441Srpaulostatic int	iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t);
206286441Srpaulostatic int	iwm_firmware_store_section(struct iwm_softc *,
207286441Srpaulo                                           enum iwm_ucode_type,
208286441Srpaulo                                           const uint8_t *, size_t);
209286441Srpaulostatic int	iwm_set_default_calib(struct iwm_softc *, const void *);
210286441Srpaulostatic void	iwm_fw_info_free(struct iwm_fw_info *);
211286441Srpaulostatic int	iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type);
212286441Srpaulostatic void	iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int);
213286441Srpaulostatic int	iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *,
214286441Srpaulo                                     bus_size_t, bus_size_t);
215286441Srpaulostatic void	iwm_dma_contig_free(struct iwm_dma_info *);
216286441Srpaulostatic int	iwm_alloc_fwmem(struct iwm_softc *);
217286441Srpaulostatic void	iwm_free_fwmem(struct iwm_softc *);
218286441Srpaulostatic int	iwm_alloc_sched(struct iwm_softc *);
219286441Srpaulostatic void	iwm_free_sched(struct iwm_softc *);
220286441Srpaulostatic int	iwm_alloc_kw(struct iwm_softc *);
221286441Srpaulostatic void	iwm_free_kw(struct iwm_softc *);
222286441Srpaulostatic int	iwm_alloc_ict(struct iwm_softc *);
223286441Srpaulostatic void	iwm_free_ict(struct iwm_softc *);
224286441Srpaulostatic int	iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
225286441Srpaulostatic void	iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
226286441Srpaulostatic void	iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *);
227286441Srpaulostatic int	iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *,
228286441Srpaulo                                  int);
229286441Srpaulostatic void	iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
230286441Srpaulostatic void	iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *);
231286441Srpaulostatic void	iwm_enable_interrupts(struct iwm_softc *);
232286441Srpaulostatic void	iwm_restore_interrupts(struct iwm_softc *);
233286441Srpaulostatic void	iwm_disable_interrupts(struct iwm_softc *);
234286441Srpaulostatic void	iwm_ict_reset(struct iwm_softc *);
235286441Srpaulostatic int	iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *);
236286441Srpaulostatic void	iwm_stop_device(struct iwm_softc *);
237286441Srpaulostatic void	iwm_mvm_nic_config(struct iwm_softc *);
238286441Srpaulostatic int	iwm_nic_rx_init(struct iwm_softc *);
239286441Srpaulostatic int	iwm_nic_tx_init(struct iwm_softc *);
240286441Srpaulostatic int	iwm_nic_init(struct iwm_softc *);
241286441Srpaulostatic void	iwm_enable_txq(struct iwm_softc *, int, int);
242286441Srpaulostatic int	iwm_post_alive(struct iwm_softc *);
243286441Srpaulostatic int	iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t,
244286441Srpaulo                                   uint16_t, uint8_t *, uint16_t *);
245286441Srpaulostatic int	iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
246286441Srpaulo				     uint16_t *);
247298877Savosstatic uint32_t	iwm_eeprom_channel_flags(uint16_t);
248298877Savosstatic void	iwm_add_channel_band(struct iwm_softc *,
249298877Savos		    struct ieee80211_channel[], int, int *, int, int,
250298877Savos		    const uint8_t[]);
251298877Savosstatic void	iwm_init_channel_map(struct ieee80211com *, int, int *,
252298877Savos		    struct ieee80211_channel[]);
253286441Srpaulostatic int	iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
254286441Srpaulo			           const uint16_t *, const uint16_t *, uint8_t,
255286441Srpaulo				   uint8_t);
256286441Srpaulostruct iwm_nvm_section;
257286441Srpaulostatic int	iwm_parse_nvm_sections(struct iwm_softc *,
258286441Srpaulo                                       struct iwm_nvm_section *);
259286441Srpaulostatic int	iwm_nvm_init(struct iwm_softc *);
260286441Srpaulostatic int	iwm_firmware_load_chunk(struct iwm_softc *, uint32_t,
261286441Srpaulo                                        const uint8_t *, uint32_t);
262286441Srpaulostatic int	iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type);
263286441Srpaulostatic int	iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type);
264286441Srpaulostatic int	iwm_fw_alive(struct iwm_softc *, uint32_t);
265286441Srpaulostatic int	iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t);
266286441Srpaulostatic int	iwm_send_phy_cfg_cmd(struct iwm_softc *);
267286441Srpaulostatic int	iwm_mvm_load_ucode_wait_alive(struct iwm_softc *,
268286441Srpaulo                                              enum iwm_ucode_type);
269286441Srpaulostatic int	iwm_run_init_mvm_ucode(struct iwm_softc *, int);
270286441Srpaulostatic int	iwm_rx_addbuf(struct iwm_softc *, int, int);
271286441Srpaulostatic int	iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *);
272286441Srpaulostatic int	iwm_mvm_get_signal_strength(struct iwm_softc *,
273286441Srpaulo					    struct iwm_rx_phy_info *);
274286441Srpaulostatic void	iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *,
275286441Srpaulo                                      struct iwm_rx_packet *,
276286441Srpaulo                                      struct iwm_rx_data *);
277286441Srpaulostatic int	iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *);
278286441Srpaulostatic void	iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
279286441Srpaulo                                   struct iwm_rx_data *);
280293100Savosstatic int	iwm_mvm_rx_tx_cmd_single(struct iwm_softc *,
281286441Srpaulo                                         struct iwm_rx_packet *,
282286441Srpaulo				         struct iwm_node *);
283286441Srpaulostatic void	iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *,
284286441Srpaulo                                  struct iwm_rx_data *);
285286441Srpaulostatic void	iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *);
286286441Srpaulo#if 0
287286441Srpaulostatic void	iwm_update_sched(struct iwm_softc *, int, int, uint8_t,
288286441Srpaulo                                 uint16_t);
289286441Srpaulo#endif
290286441Srpaulostatic const struct iwm_rate *
291286441Srpaulo	iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *,
292286441Srpaulo			struct ieee80211_frame *, struct iwm_tx_cmd *);
293286441Srpaulostatic int	iwm_tx(struct iwm_softc *, struct mbuf *,
294286441Srpaulo                       struct ieee80211_node *, int);
295286441Srpaulostatic int	iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
296286441Srpaulo			     const struct ieee80211_bpf_params *);
297286441Srpaulostatic void	iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *,
298286441Srpaulo					     struct iwm_mvm_add_sta_cmd_v5 *);
299286441Srpaulostatic int	iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *,
300286441Srpaulo					        struct iwm_mvm_add_sta_cmd_v6 *,
301286441Srpaulo                                                int *);
302286441Srpaulostatic int	iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *,
303286441Srpaulo                                       int);
304286441Srpaulostatic int	iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *);
305286441Srpaulostatic int	iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *);
306286441Srpaulostatic int	iwm_mvm_add_int_sta_common(struct iwm_softc *,
307286441Srpaulo                                           struct iwm_int_sta *,
308286441Srpaulo				           const uint8_t *, uint16_t, uint16_t);
309286441Srpaulostatic int	iwm_mvm_add_aux_sta(struct iwm_softc *);
310286441Srpaulostatic int	iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *);
311286441Srpaulostatic int	iwm_auth(struct ieee80211vap *, struct iwm_softc *);
312286441Srpaulostatic int	iwm_assoc(struct ieee80211vap *, struct iwm_softc *);
313286441Srpaulostatic int	iwm_release(struct iwm_softc *, struct iwm_node *);
314286441Srpaulostatic struct ieee80211_node *
315286441Srpaulo		iwm_node_alloc(struct ieee80211vap *,
316286441Srpaulo		               const uint8_t[IEEE80211_ADDR_LEN]);
317286441Srpaulostatic void	iwm_setrates(struct iwm_softc *, struct iwm_node *);
318286441Srpaulostatic int	iwm_media_change(struct ifnet *);
319286441Srpaulostatic int	iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int);
320286441Srpaulostatic void	iwm_endscan_cb(void *, int);
321286441Srpaulostatic int	iwm_init_hw(struct iwm_softc *);
322287197Sglebiusstatic void	iwm_init(struct iwm_softc *);
323287197Sglebiusstatic void	iwm_start(struct iwm_softc *);
324287197Sglebiusstatic void	iwm_stop(struct iwm_softc *);
325286441Srpaulostatic void	iwm_watchdog(void *);
326287197Sglebiusstatic void	iwm_parent(struct ieee80211com *);
327286441Srpaulo#ifdef IWM_DEBUG
328286441Srpaulostatic const char *
329286441Srpaulo		iwm_desc_lookup(uint32_t);
330286441Srpaulostatic void	iwm_nic_error(struct iwm_softc *);
331286441Srpaulo#endif
332286441Srpaulostatic void	iwm_notif_intr(struct iwm_softc *);
333286441Srpaulostatic void	iwm_intr(void *);
334286441Srpaulostatic int	iwm_attach(device_t);
335286441Srpaulostatic void	iwm_preinit(void *);
336286441Srpaulostatic int	iwm_detach_local(struct iwm_softc *sc, int);
337286441Srpaulostatic void	iwm_init_task(void *);
338286441Srpaulostatic void	iwm_radiotap_attach(struct iwm_softc *);
339286441Srpaulostatic struct ieee80211vap *
340286441Srpaulo		iwm_vap_create(struct ieee80211com *,
341286441Srpaulo		               const char [IFNAMSIZ], int,
342286441Srpaulo		               enum ieee80211_opmode, int,
343286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN],
344286441Srpaulo		               const uint8_t [IEEE80211_ADDR_LEN]);
345286441Srpaulostatic void	iwm_vap_delete(struct ieee80211vap *);
346286441Srpaulostatic void	iwm_scan_start(struct ieee80211com *);
347286441Srpaulostatic void	iwm_scan_end(struct ieee80211com *);
348286441Srpaulostatic void	iwm_update_mcast(struct ieee80211com *);
349286441Srpaulostatic void	iwm_set_channel(struct ieee80211com *);
350286441Srpaulostatic void	iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long);
351286441Srpaulostatic void	iwm_scan_mindwell(struct ieee80211_scan_state *);
352286441Srpaulostatic int	iwm_detach(device_t);
353286441Srpaulo
354286441Srpaulo/*
355286441Srpaulo * Firmware parser.
356286441Srpaulo */
357286441Srpaulo
358286441Srpaulostatic int
359286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen)
360286441Srpaulo{
361286441Srpaulo	const struct iwm_fw_cscheme_list *l = (const void *)data;
362286441Srpaulo
363286441Srpaulo	if (dlen < sizeof(*l) ||
364286441Srpaulo	    dlen < sizeof(l->size) + l->size * sizeof(*l->cs))
365286441Srpaulo		return EINVAL;
366286441Srpaulo
367286441Srpaulo	/* we don't actually store anything for now, always use s/w crypto */
368286441Srpaulo
369286441Srpaulo	return 0;
370286441Srpaulo}
371286441Srpaulo
372286441Srpaulostatic int
373286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc,
374286441Srpaulo    enum iwm_ucode_type type, const uint8_t *data, size_t dlen)
375286441Srpaulo{
376286441Srpaulo	struct iwm_fw_sects *fws;
377286441Srpaulo	struct iwm_fw_onesect *fwone;
378286441Srpaulo
379286441Srpaulo	if (type >= IWM_UCODE_TYPE_MAX)
380286441Srpaulo		return EINVAL;
381286441Srpaulo	if (dlen < sizeof(uint32_t))
382286441Srpaulo		return EINVAL;
383286441Srpaulo
384286441Srpaulo	fws = &sc->sc_fw.fw_sects[type];
385286441Srpaulo	if (fws->fw_count >= IWM_UCODE_SECT_MAX)
386286441Srpaulo		return EINVAL;
387286441Srpaulo
388286441Srpaulo	fwone = &fws->fw_sect[fws->fw_count];
389286441Srpaulo
390286441Srpaulo	/* first 32bit are device load offset */
391286441Srpaulo	memcpy(&fwone->fws_devoff, data, sizeof(uint32_t));
392286441Srpaulo
393286441Srpaulo	/* rest is data */
394286441Srpaulo	fwone->fws_data = data + sizeof(uint32_t);
395286441Srpaulo	fwone->fws_len = dlen - sizeof(uint32_t);
396286441Srpaulo
397286441Srpaulo	fws->fw_count++;
398286441Srpaulo	fws->fw_totlen += fwone->fws_len;
399286441Srpaulo
400286441Srpaulo	return 0;
401286441Srpaulo}
402286441Srpaulo
403286441Srpaulo/* iwlwifi: iwl-drv.c */
404286441Srpaulostruct iwm_tlv_calib_data {
405286441Srpaulo	uint32_t ucode_type;
406286441Srpaulo	struct iwm_tlv_calib_ctrl calib;
407286441Srpaulo} __packed;
408286441Srpaulo
409286441Srpaulostatic int
410286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data)
411286441Srpaulo{
412286441Srpaulo	const struct iwm_tlv_calib_data *def_calib = data;
413286441Srpaulo	uint32_t ucode_type = le32toh(def_calib->ucode_type);
414286441Srpaulo
415286441Srpaulo	if (ucode_type >= IWM_UCODE_TYPE_MAX) {
416286441Srpaulo		device_printf(sc->sc_dev,
417286441Srpaulo		    "Wrong ucode_type %u for default "
418286441Srpaulo		    "calibration.\n", ucode_type);
419286441Srpaulo		return EINVAL;
420286441Srpaulo	}
421286441Srpaulo
422286441Srpaulo	sc->sc_default_calib[ucode_type].flow_trigger =
423286441Srpaulo	    def_calib->calib.flow_trigger;
424286441Srpaulo	sc->sc_default_calib[ucode_type].event_trigger =
425286441Srpaulo	    def_calib->calib.event_trigger;
426286441Srpaulo
427286441Srpaulo	return 0;
428286441Srpaulo}
429286441Srpaulo
430286441Srpaulostatic void
431286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw)
432286441Srpaulo{
433293177Savos	firmware_put(fw->fw_fp, FIRMWARE_UNLOAD);
434293177Savos	fw->fw_fp = NULL;
435286441Srpaulo	/* don't touch fw->fw_status */
436286441Srpaulo	memset(fw->fw_sects, 0, sizeof(fw->fw_sects));
437286441Srpaulo}
438286441Srpaulo
439286441Srpaulostatic int
440286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
441286441Srpaulo{
442286441Srpaulo	struct iwm_fw_info *fw = &sc->sc_fw;
443286441Srpaulo	const struct iwm_tlv_ucode_header *uhdr;
444286441Srpaulo	struct iwm_ucode_tlv tlv;
445286441Srpaulo	enum iwm_ucode_tlv_type tlv_type;
446286441Srpaulo	const struct firmware *fwp;
447286441Srpaulo	const uint8_t *data;
448286441Srpaulo	int error = 0;
449286441Srpaulo	size_t len;
450286441Srpaulo
451286441Srpaulo	if (fw->fw_status == IWM_FW_STATUS_DONE &&
452286441Srpaulo	    ucode_type != IWM_UCODE_TYPE_INIT)
453286441Srpaulo		return 0;
454286441Srpaulo
455286441Srpaulo	while (fw->fw_status == IWM_FW_STATUS_INPROGRESS)
456286441Srpaulo		msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0);
457286441Srpaulo	fw->fw_status = IWM_FW_STATUS_INPROGRESS;
458286441Srpaulo
459293177Savos	if (fw->fw_fp != NULL)
460286441Srpaulo		iwm_fw_info_free(fw);
461286441Srpaulo
462286441Srpaulo	/*
463286441Srpaulo	 * Load firmware into driver memory.
464293177Savos	 * fw_fp will be set.
465286441Srpaulo	 */
466286441Srpaulo	IWM_UNLOCK(sc);
467286441Srpaulo	fwp = firmware_get(sc->sc_fwname);
468293177Savos	IWM_LOCK(sc);
469286441Srpaulo	if (fwp == NULL) {
470286441Srpaulo		device_printf(sc->sc_dev,
471286441Srpaulo		    "could not read firmware %s (error %d)\n",
472286441Srpaulo		    sc->sc_fwname, error);
473286441Srpaulo		goto out;
474286441Srpaulo	}
475293177Savos	fw->fw_fp = fwp;
476286441Srpaulo
477286441Srpaulo	/*
478286441Srpaulo	 * Parse firmware contents
479286441Srpaulo	 */
480286441Srpaulo
481293177Savos	uhdr = (const void *)fw->fw_fp->data;
482293177Savos	if (*(const uint32_t *)fw->fw_fp->data != 0
483286441Srpaulo	    || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) {
484286441Srpaulo		device_printf(sc->sc_dev, "invalid firmware %s\n",
485286441Srpaulo		    sc->sc_fwname);
486286441Srpaulo		error = EINVAL;
487286441Srpaulo		goto out;
488286441Srpaulo	}
489286441Srpaulo
490286441Srpaulo	sc->sc_fwver = le32toh(uhdr->ver);
491286441Srpaulo	data = uhdr->data;
492293177Savos	len = fw->fw_fp->datasize - sizeof(*uhdr);
493286441Srpaulo
494286441Srpaulo	while (len >= sizeof(tlv)) {
495286441Srpaulo		size_t tlv_len;
496286441Srpaulo		const void *tlv_data;
497286441Srpaulo
498286441Srpaulo		memcpy(&tlv, data, sizeof(tlv));
499286441Srpaulo		tlv_len = le32toh(tlv.length);
500286441Srpaulo		tlv_type = le32toh(tlv.type);
501286441Srpaulo
502286441Srpaulo		len -= sizeof(tlv);
503286441Srpaulo		data += sizeof(tlv);
504286441Srpaulo		tlv_data = data;
505286441Srpaulo
506286441Srpaulo		if (len < tlv_len) {
507286441Srpaulo			device_printf(sc->sc_dev,
508286441Srpaulo			    "firmware too short: %zu bytes\n",
509286441Srpaulo			    len);
510286441Srpaulo			error = EINVAL;
511286441Srpaulo			goto parse_out;
512286441Srpaulo		}
513286441Srpaulo
514286441Srpaulo		switch ((int)tlv_type) {
515286441Srpaulo		case IWM_UCODE_TLV_PROBE_MAX_LEN:
516286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
517286441Srpaulo				device_printf(sc->sc_dev,
518286441Srpaulo				    "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n",
519286441Srpaulo				    __func__,
520286441Srpaulo				    (int) tlv_len);
521286441Srpaulo				error = EINVAL;
522286441Srpaulo				goto parse_out;
523286441Srpaulo			}
524286441Srpaulo			sc->sc_capa_max_probe_len
525286441Srpaulo			    = le32toh(*(const uint32_t *)tlv_data);
526286441Srpaulo			/* limit it to something sensible */
527286441Srpaulo			if (sc->sc_capa_max_probe_len > (1<<16)) {
528286441Srpaulo				IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
529286441Srpaulo				    "%s: IWM_UCODE_TLV_PROBE_MAX_LEN "
530286441Srpaulo				    "ridiculous\n", __func__);
531286441Srpaulo				error = EINVAL;
532286441Srpaulo				goto parse_out;
533286441Srpaulo			}
534286441Srpaulo			break;
535286441Srpaulo		case IWM_UCODE_TLV_PAN:
536286441Srpaulo			if (tlv_len) {
537286441Srpaulo				device_printf(sc->sc_dev,
538286441Srpaulo				    "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n",
539286441Srpaulo				    __func__,
540286441Srpaulo				    (int) tlv_len);
541286441Srpaulo				error = EINVAL;
542286441Srpaulo				goto parse_out;
543286441Srpaulo			}
544286441Srpaulo			sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN;
545286441Srpaulo			break;
546286441Srpaulo		case IWM_UCODE_TLV_FLAGS:
547286441Srpaulo			if (tlv_len < sizeof(uint32_t)) {
548286441Srpaulo				device_printf(sc->sc_dev,
549286441Srpaulo				    "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n",
550286441Srpaulo				    __func__,
551286441Srpaulo				    (int) tlv_len);
552286441Srpaulo				error = EINVAL;
553286441Srpaulo				goto parse_out;
554286441Srpaulo			}
555286441Srpaulo			/*
556286441Srpaulo			 * Apparently there can be many flags, but Linux driver
557286441Srpaulo			 * parses only the first one, and so do we.
558286441Srpaulo			 *
559286441Srpaulo			 * XXX: why does this override IWM_UCODE_TLV_PAN?
560286441Srpaulo			 * Intentional or a bug?  Observations from
561286441Srpaulo			 * current firmware file:
562286441Srpaulo			 *  1) TLV_PAN is parsed first
563286441Srpaulo			 *  2) TLV_FLAGS contains TLV_FLAGS_PAN
564286441Srpaulo			 * ==> this resets TLV_PAN to itself... hnnnk
565286441Srpaulo			 */
566286441Srpaulo			sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data);
567286441Srpaulo			break;
568286441Srpaulo		case IWM_UCODE_TLV_CSCHEME:
569286441Srpaulo			if ((error = iwm_store_cscheme(sc,
570286441Srpaulo			    tlv_data, tlv_len)) != 0) {
571286441Srpaulo				device_printf(sc->sc_dev,
572286441Srpaulo				    "%s: iwm_store_cscheme(): returned %d\n",
573286441Srpaulo				    __func__,
574286441Srpaulo				    error);
575286441Srpaulo				goto parse_out;
576286441Srpaulo			}
577286441Srpaulo			break;
578286441Srpaulo		case IWM_UCODE_TLV_NUM_OF_CPU:
579286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
580286441Srpaulo				device_printf(sc->sc_dev,
581286441Srpaulo				    "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n",
582286441Srpaulo				    __func__,
583286441Srpaulo				    (int) tlv_len);
584286441Srpaulo				error = EINVAL;
585286441Srpaulo				goto parse_out;
586286441Srpaulo			}
587286441Srpaulo			if (le32toh(*(const uint32_t*)tlv_data) != 1) {
588286441Srpaulo				device_printf(sc->sc_dev,
589286441Srpaulo				    "%s: driver supports "
590286441Srpaulo				    "only TLV_NUM_OF_CPU == 1",
591286441Srpaulo				    __func__);
592286441Srpaulo				error = EINVAL;
593286441Srpaulo				goto parse_out;
594286441Srpaulo			}
595286441Srpaulo			break;
596286441Srpaulo		case IWM_UCODE_TLV_SEC_RT:
597286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
598286441Srpaulo			    IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) {
599286441Srpaulo				device_printf(sc->sc_dev,
600286441Srpaulo				    "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n",
601286441Srpaulo				    __func__,
602286441Srpaulo				    error);
603286441Srpaulo				goto parse_out;
604286441Srpaulo			}
605286441Srpaulo			break;
606286441Srpaulo		case IWM_UCODE_TLV_SEC_INIT:
607286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
608286441Srpaulo			    IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) {
609286441Srpaulo				device_printf(sc->sc_dev,
610286441Srpaulo				    "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n",
611286441Srpaulo				    __func__,
612286441Srpaulo				    error);
613286441Srpaulo				goto parse_out;
614286441Srpaulo			}
615286441Srpaulo			break;
616286441Srpaulo		case IWM_UCODE_TLV_SEC_WOWLAN:
617286441Srpaulo			if ((error = iwm_firmware_store_section(sc,
618286441Srpaulo			    IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) {
619286441Srpaulo				device_printf(sc->sc_dev,
620286441Srpaulo				    "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n",
621286441Srpaulo				    __func__,
622286441Srpaulo				    error);
623286441Srpaulo				goto parse_out;
624286441Srpaulo			}
625286441Srpaulo			break;
626286441Srpaulo		case IWM_UCODE_TLV_DEF_CALIB:
627286441Srpaulo			if (tlv_len != sizeof(struct iwm_tlv_calib_data)) {
628286441Srpaulo				device_printf(sc->sc_dev,
629286441Srpaulo				    "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n",
630286441Srpaulo				    __func__,
631286441Srpaulo				    (int) tlv_len,
632286441Srpaulo				    (int) sizeof(struct iwm_tlv_calib_data));
633286441Srpaulo				error = EINVAL;
634286441Srpaulo				goto parse_out;
635286441Srpaulo			}
636286441Srpaulo			if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) {
637286441Srpaulo				device_printf(sc->sc_dev,
638286441Srpaulo				    "%s: iwm_set_default_calib() failed: %d\n",
639286441Srpaulo				    __func__,
640286441Srpaulo				    error);
641286441Srpaulo				goto parse_out;
642286441Srpaulo			}
643286441Srpaulo			break;
644286441Srpaulo		case IWM_UCODE_TLV_PHY_SKU:
645286441Srpaulo			if (tlv_len != sizeof(uint32_t)) {
646286441Srpaulo				error = EINVAL;
647286441Srpaulo				device_printf(sc->sc_dev,
648286441Srpaulo				    "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n",
649286441Srpaulo				    __func__,
650286441Srpaulo				    (int) tlv_len);
651286441Srpaulo				goto parse_out;
652286441Srpaulo			}
653286441Srpaulo			sc->sc_fw_phy_config =
654286441Srpaulo			    le32toh(*(const uint32_t *)tlv_data);
655286441Srpaulo			break;
656286441Srpaulo
657286441Srpaulo		case IWM_UCODE_TLV_API_CHANGES_SET:
658286441Srpaulo		case IWM_UCODE_TLV_ENABLED_CAPABILITIES:
659286441Srpaulo			/* ignore, not used by current driver */
660286441Srpaulo			break;
661286441Srpaulo
662286441Srpaulo		default:
663286441Srpaulo			device_printf(sc->sc_dev,
664286441Srpaulo			    "%s: unknown firmware section %d, abort\n",
665286441Srpaulo			    __func__, tlv_type);
666286441Srpaulo			error = EINVAL;
667286441Srpaulo			goto parse_out;
668286441Srpaulo		}
669286441Srpaulo
670286441Srpaulo		len -= roundup(tlv_len, 4);
671286441Srpaulo		data += roundup(tlv_len, 4);
672286441Srpaulo	}
673286441Srpaulo
674286441Srpaulo	KASSERT(error == 0, ("unhandled error"));
675286441Srpaulo
676286441Srpaulo parse_out:
677286441Srpaulo	if (error) {
678286441Srpaulo		device_printf(sc->sc_dev, "firmware parse error %d, "
679286441Srpaulo		    "section type %d\n", error, tlv_type);
680286441Srpaulo	}
681286441Srpaulo
682286441Srpaulo	if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
683286441Srpaulo		device_printf(sc->sc_dev,
684286441Srpaulo		    "device uses unsupported power ops\n");
685286441Srpaulo		error = ENOTSUP;
686286441Srpaulo	}
687286441Srpaulo
688286441Srpaulo out:
689286441Srpaulo	if (error) {
690286441Srpaulo		fw->fw_status = IWM_FW_STATUS_NONE;
691293177Savos		if (fw->fw_fp != NULL)
692286441Srpaulo			iwm_fw_info_free(fw);
693286441Srpaulo	} else
694286441Srpaulo		fw->fw_status = IWM_FW_STATUS_DONE;
695286441Srpaulo	wakeup(&sc->sc_fw);
696286441Srpaulo
697286441Srpaulo	return error;
698286441Srpaulo}
699286441Srpaulo
700286441Srpaulo/*
701286441Srpaulo * DMA resource routines
702286441Srpaulo */
703286441Srpaulo
704286441Srpaulostatic void
705286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
706286441Srpaulo{
707286441Srpaulo        if (error != 0)
708286441Srpaulo                return;
709286441Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
710286441Srpaulo        *(bus_addr_t *)arg = segs[0].ds_addr;
711286441Srpaulo}
712286441Srpaulo
713286441Srpaulostatic int
714286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma,
715286441Srpaulo    bus_size_t size, bus_size_t alignment)
716286441Srpaulo{
717286441Srpaulo	int error;
718286441Srpaulo
719286441Srpaulo	dma->tag = NULL;
720286441Srpaulo	dma->size = size;
721286441Srpaulo
722286441Srpaulo	error = bus_dma_tag_create(tag, alignment,
723286441Srpaulo            0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
724289679Skevlo            1, size, 0, NULL, NULL, &dma->tag);
725286441Srpaulo        if (error != 0)
726286441Srpaulo                goto fail;
727286441Srpaulo
728286441Srpaulo        error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
729286441Srpaulo            BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
730286441Srpaulo        if (error != 0)
731286441Srpaulo                goto fail;
732286441Srpaulo
733286441Srpaulo        error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
734286441Srpaulo            iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
735286441Srpaulo        if (error != 0)
736286441Srpaulo                goto fail;
737286441Srpaulo
738286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
739286441Srpaulo
740286441Srpaulo	return 0;
741286441Srpaulo
742286441Srpaulofail:	iwm_dma_contig_free(dma);
743286441Srpaulo	return error;
744286441Srpaulo}
745286441Srpaulo
746286441Srpaulostatic void
747286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma)
748286441Srpaulo{
749286441Srpaulo	if (dma->map != NULL) {
750286441Srpaulo		if (dma->vaddr != NULL) {
751286441Srpaulo			bus_dmamap_sync(dma->tag, dma->map,
752286441Srpaulo			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
753286441Srpaulo			bus_dmamap_unload(dma->tag, dma->map);
754286441Srpaulo			bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
755286441Srpaulo			dma->vaddr = NULL;
756286441Srpaulo		}
757286441Srpaulo		bus_dmamap_destroy(dma->tag, dma->map);
758286441Srpaulo		dma->map = NULL;
759286441Srpaulo	}
760286441Srpaulo	if (dma->tag != NULL) {
761286441Srpaulo		bus_dma_tag_destroy(dma->tag);
762286441Srpaulo		dma->tag = NULL;
763286441Srpaulo	}
764286441Srpaulo
765286441Srpaulo}
766286441Srpaulo
767286441Srpaulo/* fwmem is used to load firmware onto the card */
768286441Srpaulostatic int
769286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc)
770286441Srpaulo{
771286441Srpaulo	/* Must be aligned on a 16-byte boundary. */
772286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma,
773286441Srpaulo	    sc->sc_fwdmasegsz, 16);
774286441Srpaulo}
775286441Srpaulo
776286441Srpaulostatic void
777286441Srpauloiwm_free_fwmem(struct iwm_softc *sc)
778286441Srpaulo{
779286441Srpaulo	iwm_dma_contig_free(&sc->fw_dma);
780286441Srpaulo}
781286441Srpaulo
782286441Srpaulo/* tx scheduler rings.  not used? */
783286441Srpaulostatic int
784286441Srpauloiwm_alloc_sched(struct iwm_softc *sc)
785286441Srpaulo{
786286441Srpaulo	int rv;
787286441Srpaulo
788286441Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
789286441Srpaulo	rv = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma,
790286441Srpaulo	    nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024);
791286441Srpaulo	return rv;
792286441Srpaulo}
793286441Srpaulo
794286441Srpaulostatic void
795286441Srpauloiwm_free_sched(struct iwm_softc *sc)
796286441Srpaulo{
797286441Srpaulo	iwm_dma_contig_free(&sc->sched_dma);
798286441Srpaulo}
799286441Srpaulo
800286441Srpaulo/* keep-warm page is used internally by the card.  see iwl-fh.h for more info */
801286441Srpaulostatic int
802286441Srpauloiwm_alloc_kw(struct iwm_softc *sc)
803286441Srpaulo{
804286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096);
805286441Srpaulo}
806286441Srpaulo
807286441Srpaulostatic void
808286441Srpauloiwm_free_kw(struct iwm_softc *sc)
809286441Srpaulo{
810286441Srpaulo	iwm_dma_contig_free(&sc->kw_dma);
811286441Srpaulo}
812286441Srpaulo
813286441Srpaulo/* interrupt cause table */
814286441Srpaulostatic int
815286441Srpauloiwm_alloc_ict(struct iwm_softc *sc)
816286441Srpaulo{
817286441Srpaulo	return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma,
818286441Srpaulo	    IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT);
819286441Srpaulo}
820286441Srpaulo
821286441Srpaulostatic void
822286441Srpauloiwm_free_ict(struct iwm_softc *sc)
823286441Srpaulo{
824286441Srpaulo	iwm_dma_contig_free(&sc->ict_dma);
825286441Srpaulo}
826286441Srpaulo
827286441Srpaulostatic int
828286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
829286441Srpaulo{
830286441Srpaulo	bus_size_t size;
831286441Srpaulo	int i, error;
832286441Srpaulo
833286441Srpaulo	ring->cur = 0;
834286441Srpaulo
835286441Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
836286441Srpaulo	size = IWM_RX_RING_COUNT * sizeof(uint32_t);
837286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
838286441Srpaulo	if (error != 0) {
839286441Srpaulo		device_printf(sc->sc_dev,
840286441Srpaulo		    "could not allocate RX ring DMA memory\n");
841286441Srpaulo		goto fail;
842286441Srpaulo	}
843286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
844286441Srpaulo
845286441Srpaulo	/* Allocate RX status area (16-byte aligned). */
846286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma,
847286441Srpaulo	    sizeof(*ring->stat), 16);
848286441Srpaulo	if (error != 0) {
849286441Srpaulo		device_printf(sc->sc_dev,
850286441Srpaulo		    "could not allocate RX status DMA memory\n");
851286441Srpaulo		goto fail;
852286441Srpaulo	}
853286441Srpaulo	ring->stat = ring->stat_dma.vaddr;
854286441Srpaulo
855286441Srpaulo        /* Create RX buffer DMA tag. */
856286441Srpaulo        error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
857286441Srpaulo            BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
858289679Skevlo            IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat);
859286441Srpaulo        if (error != 0) {
860286441Srpaulo                device_printf(sc->sc_dev,
861286441Srpaulo                    "%s: could not create RX buf DMA tag, error %d\n",
862286441Srpaulo                    __func__, error);
863286441Srpaulo                goto fail;
864286441Srpaulo        }
865286441Srpaulo
866286441Srpaulo	/*
867286441Srpaulo	 * Allocate and map RX buffers.
868286441Srpaulo	 */
869286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
870286441Srpaulo		if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) {
871286441Srpaulo			goto fail;
872286441Srpaulo		}
873286441Srpaulo	}
874286441Srpaulo	return 0;
875286441Srpaulo
876286441Srpaulofail:	iwm_free_rx_ring(sc, ring);
877286441Srpaulo	return error;
878286441Srpaulo}
879286441Srpaulo
880286441Srpaulostatic void
881286441Srpauloiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
882286441Srpaulo{
883286441Srpaulo
884286441Srpaulo	/* XXX print out if we can't lock the NIC? */
885286441Srpaulo	if (iwm_nic_lock(sc)) {
886286441Srpaulo		/* XXX handle if RX stop doesn't finish? */
887286441Srpaulo		(void) iwm_pcie_rx_stop(sc);
888286441Srpaulo		iwm_nic_unlock(sc);
889286441Srpaulo	}
890287965Sadrian	/* Reset the ring state */
891286441Srpaulo	ring->cur = 0;
892287965Sadrian	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
893286441Srpaulo}
894286441Srpaulo
895286441Srpaulostatic void
896286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring)
897286441Srpaulo{
898286441Srpaulo	int i;
899286441Srpaulo
900286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
901286441Srpaulo	iwm_dma_contig_free(&ring->stat_dma);
902286441Srpaulo
903286441Srpaulo	for (i = 0; i < IWM_RX_RING_COUNT; i++) {
904286441Srpaulo		struct iwm_rx_data *data = &ring->data[i];
905286441Srpaulo
906286441Srpaulo		if (data->m != NULL) {
907286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
908286441Srpaulo			    BUS_DMASYNC_POSTREAD);
909286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
910286441Srpaulo			m_freem(data->m);
911286441Srpaulo			data->m = NULL;
912286441Srpaulo		}
913286441Srpaulo		if (data->map != NULL) {
914286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
915286441Srpaulo			data->map = NULL;
916286441Srpaulo		}
917286441Srpaulo	}
918286441Srpaulo	if (ring->data_dmat != NULL) {
919286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
920286441Srpaulo		ring->data_dmat = NULL;
921286441Srpaulo	}
922286441Srpaulo}
923286441Srpaulo
924286441Srpaulostatic int
925286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
926286441Srpaulo{
927286441Srpaulo	bus_addr_t paddr;
928286441Srpaulo	bus_size_t size;
929286441Srpaulo	int i, error;
930286441Srpaulo
931286441Srpaulo	ring->qid = qid;
932286441Srpaulo	ring->queued = 0;
933286441Srpaulo	ring->cur = 0;
934286441Srpaulo
935286441Srpaulo	/* Allocate TX descriptors (256-byte aligned). */
936286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd);
937286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
938286441Srpaulo	if (error != 0) {
939286441Srpaulo		device_printf(sc->sc_dev,
940286441Srpaulo		    "could not allocate TX ring DMA memory\n");
941286441Srpaulo		goto fail;
942286441Srpaulo	}
943286441Srpaulo	ring->desc = ring->desc_dma.vaddr;
944286441Srpaulo
945286441Srpaulo	/*
946286441Srpaulo	 * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need
947286441Srpaulo	 * to allocate commands space for other rings.
948286441Srpaulo	 */
949286441Srpaulo	if (qid > IWM_MVM_CMD_QUEUE)
950286441Srpaulo		return 0;
951286441Srpaulo
952286441Srpaulo	size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
953286441Srpaulo	error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4);
954286441Srpaulo	if (error != 0) {
955286441Srpaulo		device_printf(sc->sc_dev,
956286441Srpaulo		    "could not allocate TX cmd DMA memory\n");
957286441Srpaulo		goto fail;
958286441Srpaulo	}
959286441Srpaulo	ring->cmd = ring->cmd_dma.vaddr;
960286441Srpaulo
961286441Srpaulo	error = bus_dma_tag_create(sc->sc_dmat, 1, 0,
962286441Srpaulo	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
963293119Savos            IWM_MAX_SCATTER - 2, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
964286441Srpaulo	if (error != 0) {
965286441Srpaulo		device_printf(sc->sc_dev, "could not create TX buf DMA tag\n");
966286441Srpaulo		goto fail;
967286441Srpaulo	}
968286441Srpaulo
969286441Srpaulo	paddr = ring->cmd_dma.paddr;
970286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
971286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
972286441Srpaulo
973286441Srpaulo		data->cmd_paddr = paddr;
974286441Srpaulo		data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header)
975286441Srpaulo		    + offsetof(struct iwm_tx_cmd, scratch);
976286441Srpaulo		paddr += sizeof(struct iwm_device_cmd);
977286441Srpaulo
978286441Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
979286441Srpaulo		if (error != 0) {
980286441Srpaulo			device_printf(sc->sc_dev,
981286441Srpaulo			    "could not create TX buf DMA map\n");
982286441Srpaulo			goto fail;
983286441Srpaulo		}
984286441Srpaulo	}
985286441Srpaulo	KASSERT(paddr == ring->cmd_dma.paddr + size,
986286441Srpaulo	    ("invalid physical address"));
987286441Srpaulo	return 0;
988286441Srpaulo
989286441Srpaulofail:	iwm_free_tx_ring(sc, ring);
990286441Srpaulo	return error;
991286441Srpaulo}
992286441Srpaulo
993286441Srpaulostatic void
994286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
995286441Srpaulo{
996286441Srpaulo	int i;
997286441Srpaulo
998286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
999286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1000286441Srpaulo
1001286441Srpaulo		if (data->m != NULL) {
1002286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1003286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1004286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1005286441Srpaulo			m_freem(data->m);
1006286441Srpaulo			data->m = NULL;
1007286441Srpaulo		}
1008286441Srpaulo	}
1009286441Srpaulo	/* Clear TX descriptors. */
1010286441Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1011286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1012286441Srpaulo	    BUS_DMASYNC_PREWRITE);
1013286441Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1014286441Srpaulo	ring->queued = 0;
1015286441Srpaulo	ring->cur = 0;
1016286441Srpaulo}
1017286441Srpaulo
1018286441Srpaulostatic void
1019286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
1020286441Srpaulo{
1021286441Srpaulo	int i;
1022286441Srpaulo
1023286441Srpaulo	iwm_dma_contig_free(&ring->desc_dma);
1024286441Srpaulo	iwm_dma_contig_free(&ring->cmd_dma);
1025286441Srpaulo
1026286441Srpaulo	for (i = 0; i < IWM_TX_RING_COUNT; i++) {
1027286441Srpaulo		struct iwm_tx_data *data = &ring->data[i];
1028286441Srpaulo
1029286441Srpaulo		if (data->m != NULL) {
1030286441Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1031286441Srpaulo			    BUS_DMASYNC_POSTWRITE);
1032286441Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1033286441Srpaulo			m_freem(data->m);
1034286441Srpaulo			data->m = NULL;
1035286441Srpaulo		}
1036286441Srpaulo		if (data->map != NULL) {
1037286441Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1038286441Srpaulo			data->map = NULL;
1039286441Srpaulo		}
1040286441Srpaulo	}
1041286441Srpaulo	if (ring->data_dmat != NULL) {
1042286441Srpaulo		bus_dma_tag_destroy(ring->data_dmat);
1043286441Srpaulo		ring->data_dmat = NULL;
1044286441Srpaulo	}
1045286441Srpaulo}
1046286441Srpaulo
1047286441Srpaulo/*
1048286441Srpaulo * High-level hardware frobbing routines
1049286441Srpaulo */
1050286441Srpaulo
1051286441Srpaulostatic void
1052286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc)
1053286441Srpaulo{
1054286441Srpaulo	sc->sc_intmask = IWM_CSR_INI_SET_MASK;
1055286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1056286441Srpaulo}
1057286441Srpaulo
1058286441Srpaulostatic void
1059286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc)
1060286441Srpaulo{
1061286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
1062286441Srpaulo}
1063286441Srpaulo
1064286441Srpaulostatic void
1065286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc)
1066286441Srpaulo{
1067286441Srpaulo	/* disable interrupts */
1068286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
1069286441Srpaulo
1070286441Srpaulo	/* acknowledge all interrupts */
1071286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1072286441Srpaulo	IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
1073286441Srpaulo}
1074286441Srpaulo
1075286441Srpaulostatic void
1076286441Srpauloiwm_ict_reset(struct iwm_softc *sc)
1077286441Srpaulo{
1078286441Srpaulo	iwm_disable_interrupts(sc);
1079286441Srpaulo
1080286441Srpaulo	/* Reset ICT table. */
1081286441Srpaulo	memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE);
1082286441Srpaulo	sc->ict_cur = 0;
1083286441Srpaulo
1084286441Srpaulo	/* Set physical address of ICT table (4KB aligned). */
1085286441Srpaulo	IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG,
1086286441Srpaulo	    IWM_CSR_DRAM_INT_TBL_ENABLE
1087286441Srpaulo	    | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK
1088286441Srpaulo	    | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT);
1089286441Srpaulo
1090286441Srpaulo	/* Switch to ICT interrupt mode in driver. */
1091286441Srpaulo	sc->sc_flags |= IWM_FLAG_USE_ICT;
1092286441Srpaulo
1093286441Srpaulo	/* Re-enable interrupts. */
1094286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1095286441Srpaulo	iwm_enable_interrupts(sc);
1096286441Srpaulo}
1097286441Srpaulo
1098286441Srpaulo/* iwlwifi pcie/trans.c */
1099286441Srpaulo
1100286441Srpaulo/*
1101286441Srpaulo * Since this .. hard-resets things, it's time to actually
1102286441Srpaulo * mark the first vap (if any) as having no mac context.
1103286441Srpaulo * It's annoying, but since the driver is potentially being
1104286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we
1105286441Srpaulo * have to correctly track this.
1106286441Srpaulo */
1107286441Srpaulostatic void
1108286441Srpauloiwm_stop_device(struct iwm_softc *sc)
1109286441Srpaulo{
1110287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
1111286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1112286441Srpaulo	int chnl, ntries;
1113286441Srpaulo	int qid;
1114286441Srpaulo
1115286441Srpaulo	/* tell the device to stop sending interrupts */
1116286441Srpaulo	iwm_disable_interrupts(sc);
1117286441Srpaulo
1118286441Srpaulo	/*
1119286441Srpaulo	 * FreeBSD-local: mark the first vap as not-uploaded,
1120286441Srpaulo	 * so the next transition through auth/assoc
1121286441Srpaulo	 * will correctly populate the MAC context.
1122286441Srpaulo	 */
1123286441Srpaulo	if (vap) {
1124286441Srpaulo		struct iwm_vap *iv = IWM_VAP(vap);
1125286441Srpaulo		iv->is_uploaded = 0;
1126286441Srpaulo	}
1127286441Srpaulo
1128286441Srpaulo	/* device going down, Stop using ICT table */
1129286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_USE_ICT;
1130286441Srpaulo
1131286441Srpaulo	/* stop tx and rx.  tx and rx bits, as usual, are from if_iwn */
1132286441Srpaulo
1133286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1134286441Srpaulo
1135286441Srpaulo	/* Stop all DMA channels. */
1136286441Srpaulo	if (iwm_nic_lock(sc)) {
1137286441Srpaulo		for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1138286441Srpaulo			IWM_WRITE(sc,
1139286441Srpaulo			    IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0);
1140286441Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
1141286441Srpaulo				uint32_t r;
1142286441Srpaulo
1143286441Srpaulo				r = IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG);
1144286441Srpaulo				if (r & IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(
1145286441Srpaulo				    chnl))
1146286441Srpaulo					break;
1147286441Srpaulo				DELAY(20);
1148286441Srpaulo			}
1149286441Srpaulo		}
1150286441Srpaulo		iwm_nic_unlock(sc);
1151286441Srpaulo	}
1152286441Srpaulo
1153286441Srpaulo	/* Stop RX ring. */
1154286441Srpaulo	iwm_reset_rx_ring(sc, &sc->rxq);
1155286441Srpaulo
1156286441Srpaulo	/* Reset all TX rings. */
1157286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++)
1158286441Srpaulo		iwm_reset_tx_ring(sc, &sc->txq[qid]);
1159286441Srpaulo
1160286441Srpaulo	/*
1161286441Srpaulo	 * Power-down device's busmaster DMA clocks
1162286441Srpaulo	 */
1163286441Srpaulo	iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT);
1164286441Srpaulo	DELAY(5);
1165286441Srpaulo
1166286441Srpaulo	/* Make sure (redundant) we've released our request to stay awake */
1167286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
1168286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1169286441Srpaulo
1170286441Srpaulo	/* Stop the device, and put it in low power state */
1171286441Srpaulo	iwm_apm_stop(sc);
1172286441Srpaulo
1173286441Srpaulo	/* Upon stop, the APM issues an interrupt if HW RF kill is set.
1174286441Srpaulo	 * Clean again the interrupt here
1175286441Srpaulo	 */
1176286441Srpaulo	iwm_disable_interrupts(sc);
1177286441Srpaulo	/* stop and reset the on-board processor */
1178286441Srpaulo	IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_NEVO_RESET);
1179286441Srpaulo
1180286441Srpaulo	/*
1181286441Srpaulo	 * Even if we stop the HW, we still want the RF kill
1182286441Srpaulo	 * interrupt
1183286441Srpaulo	 */
1184286441Srpaulo	iwm_enable_rfkill_int(sc);
1185286441Srpaulo	iwm_check_rfkill(sc);
1186286441Srpaulo}
1187286441Srpaulo
1188286441Srpaulo/* iwlwifi: mvm/ops.c */
1189286441Srpaulostatic void
1190286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc)
1191286441Srpaulo{
1192286441Srpaulo	uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash;
1193286441Srpaulo	uint32_t reg_val = 0;
1194286441Srpaulo
1195286441Srpaulo	radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >>
1196286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_TYPE_POS;
1197286441Srpaulo	radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >>
1198286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_STEP_POS;
1199286441Srpaulo	radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >>
1200286441Srpaulo	    IWM_FW_PHY_CFG_RADIO_DASH_POS;
1201286441Srpaulo
1202286441Srpaulo	/* SKU control */
1203286441Srpaulo	reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) <<
1204286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
1205286441Srpaulo	reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) <<
1206286441Srpaulo	    IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
1207286441Srpaulo
1208286441Srpaulo	/* radio configuration */
1209286441Srpaulo	reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
1210286441Srpaulo	reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
1211286441Srpaulo	reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
1212286441Srpaulo
1213286441Srpaulo	IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val);
1214286441Srpaulo
1215286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1216286441Srpaulo	    "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
1217286441Srpaulo	    radio_cfg_step, radio_cfg_dash);
1218286441Srpaulo
1219286441Srpaulo	/*
1220286441Srpaulo	 * W/A : NIC is stuck in a reset state after Early PCIe power off
1221286441Srpaulo	 * (PCIe power is lost before PERST# is asserted), causing ME FW
1222286441Srpaulo	 * to lose ownership and not being able to obtain it back.
1223286441Srpaulo	 */
1224286441Srpaulo	iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG,
1225286441Srpaulo	    IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
1226286441Srpaulo	    ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
1227286441Srpaulo}
1228286441Srpaulo
1229286441Srpaulostatic int
1230286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc)
1231286441Srpaulo{
1232286441Srpaulo	if (!iwm_nic_lock(sc))
1233286441Srpaulo		return EBUSY;
1234286441Srpaulo
1235286441Srpaulo	/*
1236286441Srpaulo	 * Initialize RX ring.  This is from the iwn driver.
1237286441Srpaulo	 */
1238286441Srpaulo	memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat));
1239286441Srpaulo
1240286441Srpaulo	/* stop DMA */
1241286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
1242286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
1243286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
1244286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0);
1245286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
1246286441Srpaulo
1247286441Srpaulo	/* Set physical address of RX ring (256-byte aligned). */
1248286441Srpaulo	IWM_WRITE(sc,
1249286441Srpaulo	    IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8);
1250286441Srpaulo
1251286441Srpaulo	/* Set physical address of RX status (16-byte aligned). */
1252286441Srpaulo	IWM_WRITE(sc,
1253286441Srpaulo	    IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4);
1254286441Srpaulo
1255286441Srpaulo	/* Enable RX. */
1256286441Srpaulo	/*
1257286441Srpaulo	 * Note: Linux driver also sets this:
1258286441Srpaulo	 *  (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
1259286441Srpaulo	 *
1260286441Srpaulo	 * It causes weird behavior.  YMMV.
1261286441Srpaulo	 */
1262286441Srpaulo	IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG,
1263286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL		|
1264286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY		|  /* HW bug */
1265286441Srpaulo	    IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL	|
1266286441Srpaulo	    IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K		|
1267286441Srpaulo	    IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
1268286441Srpaulo
1269286441Srpaulo	IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF);
1270286441Srpaulo
1271286441Srpaulo	/* W/A for interrupt coalescing bug in 7260 and 3160 */
1272286441Srpaulo	if (sc->host_interrupt_operation_mode)
1273286441Srpaulo		IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE);
1274286441Srpaulo
1275286441Srpaulo	/*
1276286441Srpaulo	 * Thus sayeth el jefe (iwlwifi) via a comment:
1277286441Srpaulo	 *
1278286441Srpaulo	 * This value should initially be 0 (before preparing any
1279286441Srpaulo 	 * RBs), should be 8 after preparing the first 8 RBs (for example)
1280286441Srpaulo	 */
1281286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8);
1282286441Srpaulo
1283286441Srpaulo	iwm_nic_unlock(sc);
1284286441Srpaulo
1285286441Srpaulo	return 0;
1286286441Srpaulo}
1287286441Srpaulo
1288286441Srpaulostatic int
1289286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc)
1290286441Srpaulo{
1291286441Srpaulo	int qid;
1292286441Srpaulo
1293286441Srpaulo	if (!iwm_nic_lock(sc))
1294286441Srpaulo		return EBUSY;
1295286441Srpaulo
1296286441Srpaulo	/* Deactivate TX scheduler. */
1297286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0);
1298286441Srpaulo
1299286441Srpaulo	/* Set physical address of "keep warm" page (16-byte aligned). */
1300286441Srpaulo	IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4);
1301286441Srpaulo
1302286441Srpaulo	/* Initialize TX rings. */
1303286441Srpaulo	for (qid = 0; qid < nitems(sc->txq); qid++) {
1304286441Srpaulo		struct iwm_tx_ring *txq = &sc->txq[qid];
1305286441Srpaulo
1306286441Srpaulo		/* Set physical address of TX ring (256-byte aligned). */
1307286441Srpaulo		IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid),
1308286441Srpaulo		    txq->desc_dma.paddr >> 8);
1309286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
1310286441Srpaulo		    "%s: loading ring %d descriptors (%p) at %lx\n",
1311286441Srpaulo		    __func__,
1312286441Srpaulo		    qid, txq->desc,
1313286441Srpaulo		    (unsigned long) (txq->desc_dma.paddr >> 8));
1314286441Srpaulo	}
1315286441Srpaulo	iwm_nic_unlock(sc);
1316286441Srpaulo
1317286441Srpaulo	return 0;
1318286441Srpaulo}
1319286441Srpaulo
1320286441Srpaulostatic int
1321286441Srpauloiwm_nic_init(struct iwm_softc *sc)
1322286441Srpaulo{
1323286441Srpaulo	int error;
1324286441Srpaulo
1325286441Srpaulo	iwm_apm_init(sc);
1326286441Srpaulo	iwm_set_pwr(sc);
1327286441Srpaulo
1328286441Srpaulo	iwm_mvm_nic_config(sc);
1329286441Srpaulo
1330286441Srpaulo	if ((error = iwm_nic_rx_init(sc)) != 0)
1331286441Srpaulo		return error;
1332286441Srpaulo
1333286441Srpaulo	/*
1334286441Srpaulo	 * Ditto for TX, from iwn
1335286441Srpaulo	 */
1336286441Srpaulo	if ((error = iwm_nic_tx_init(sc)) != 0)
1337286441Srpaulo		return error;
1338286441Srpaulo
1339286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1340286441Srpaulo	    "%s: shadow registers enabled\n", __func__);
1341286441Srpaulo	IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff);
1342286441Srpaulo
1343286441Srpaulo	return 0;
1344286441Srpaulo}
1345286441Srpaulo
1346286441Srpauloenum iwm_mvm_tx_fifo {
1347286441Srpaulo	IWM_MVM_TX_FIFO_BK = 0,
1348286441Srpaulo	IWM_MVM_TX_FIFO_BE,
1349286441Srpaulo	IWM_MVM_TX_FIFO_VI,
1350286441Srpaulo	IWM_MVM_TX_FIFO_VO,
1351286441Srpaulo	IWM_MVM_TX_FIFO_MCAST = 5,
1352286441Srpaulo};
1353286441Srpaulo
1354286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = {
1355286441Srpaulo	IWM_MVM_TX_FIFO_VO,
1356286441Srpaulo	IWM_MVM_TX_FIFO_VI,
1357286441Srpaulo	IWM_MVM_TX_FIFO_BE,
1358286441Srpaulo	IWM_MVM_TX_FIFO_BK,
1359286441Srpaulo};
1360286441Srpaulo
1361286441Srpaulostatic void
1362286441Srpauloiwm_enable_txq(struct iwm_softc *sc, int qid, int fifo)
1363286441Srpaulo{
1364286441Srpaulo	if (!iwm_nic_lock(sc)) {
1365286441Srpaulo		device_printf(sc->sc_dev,
1366286441Srpaulo		    "%s: cannot enable txq %d\n",
1367286441Srpaulo		    __func__,
1368286441Srpaulo		    qid);
1369286441Srpaulo		return; /* XXX return EBUSY */
1370286441Srpaulo	}
1371286441Srpaulo
1372286441Srpaulo	/* unactivate before configuration */
1373286441Srpaulo	iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1374286441Srpaulo	    (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE)
1375286441Srpaulo	    | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
1376286441Srpaulo
1377286441Srpaulo	if (qid != IWM_MVM_CMD_QUEUE) {
1378286441Srpaulo		iwm_set_bits_prph(sc, IWM_SCD_QUEUECHAIN_SEL, (1 << qid));
1379286441Srpaulo	}
1380286441Srpaulo
1381286441Srpaulo	iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid));
1382286441Srpaulo
1383286441Srpaulo	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
1384286441Srpaulo	iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0);
1385286441Srpaulo
1386286441Srpaulo	iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0);
1387286441Srpaulo	/* Set scheduler window size and frame limit. */
1388286441Srpaulo	iwm_write_mem32(sc,
1389286441Srpaulo	    sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) +
1390286441Srpaulo	    sizeof(uint32_t),
1391286441Srpaulo	    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
1392286441Srpaulo	    IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
1393286441Srpaulo	    ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
1394286441Srpaulo	    IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
1395286441Srpaulo
1396286441Srpaulo	iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
1397286441Srpaulo	    (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
1398286441Srpaulo	    (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) |
1399286441Srpaulo	    (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) |
1400286441Srpaulo	    IWM_SCD_QUEUE_STTS_REG_MSK);
1401286441Srpaulo
1402286441Srpaulo	iwm_nic_unlock(sc);
1403286441Srpaulo
1404286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
1405286441Srpaulo	    "%s: enabled txq %d FIFO %d\n",
1406286441Srpaulo	    __func__, qid, fifo);
1407286441Srpaulo}
1408286441Srpaulo
1409286441Srpaulostatic int
1410286441Srpauloiwm_post_alive(struct iwm_softc *sc)
1411286441Srpaulo{
1412286441Srpaulo	int nwords;
1413286441Srpaulo	int error, chnl;
1414286441Srpaulo
1415286441Srpaulo	if (!iwm_nic_lock(sc))
1416286441Srpaulo		return EBUSY;
1417286441Srpaulo
1418286441Srpaulo	if (sc->sched_base != iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR)) {
1419286441Srpaulo		device_printf(sc->sc_dev,
1420286441Srpaulo		    "%s: sched addr mismatch",
1421286441Srpaulo		    __func__);
1422286441Srpaulo		error = EINVAL;
1423286441Srpaulo		goto out;
1424286441Srpaulo	}
1425286441Srpaulo
1426286441Srpaulo	iwm_ict_reset(sc);
1427286441Srpaulo
1428286441Srpaulo	/* Clear TX scheduler state in SRAM. */
1429286441Srpaulo	nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND -
1430286441Srpaulo	    IWM_SCD_CONTEXT_MEM_LOWER_BOUND)
1431286441Srpaulo	    / sizeof(uint32_t);
1432286441Srpaulo	error = iwm_write_mem(sc,
1433286441Srpaulo	    sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND,
1434286441Srpaulo	    NULL, nwords);
1435286441Srpaulo	if (error)
1436286441Srpaulo		goto out;
1437286441Srpaulo
1438286441Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned). */
1439286441Srpaulo	iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10);
1440286441Srpaulo
1441286441Srpaulo	iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0);
1442286441Srpaulo
1443286441Srpaulo	/* enable command channel */
1444286441Srpaulo	iwm_enable_txq(sc, IWM_MVM_CMD_QUEUE, 7);
1445286441Srpaulo
1446286441Srpaulo	iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff);
1447286441Srpaulo
1448286441Srpaulo	/* Enable DMA channels. */
1449286441Srpaulo	for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) {
1450286441Srpaulo		IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl),
1451286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
1452286441Srpaulo		    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
1453286441Srpaulo	}
1454286441Srpaulo
1455286441Srpaulo	IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG,
1456286441Srpaulo	    IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
1457286441Srpaulo
1458286441Srpaulo	/* Enable L1-Active */
1459286441Srpaulo	iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG,
1460286441Srpaulo	    IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
1461286441Srpaulo
1462286441Srpaulo out:
1463286441Srpaulo 	iwm_nic_unlock(sc);
1464286441Srpaulo	return error;
1465286441Srpaulo}
1466286441Srpaulo
1467286441Srpaulo/*
1468286441Srpaulo * NVM read access and content parsing.  We do not support
1469286441Srpaulo * external NVM or writing NVM.
1470286441Srpaulo * iwlwifi/mvm/nvm.c
1471286441Srpaulo */
1472286441Srpaulo
1473286441Srpaulo/* list of NVM sections we are allowed/need to read */
1474286441Srpauloconst int nvm_to_read[] = {
1475286441Srpaulo	IWM_NVM_SECTION_TYPE_HW,
1476286441Srpaulo	IWM_NVM_SECTION_TYPE_SW,
1477286441Srpaulo	IWM_NVM_SECTION_TYPE_CALIBRATION,
1478286441Srpaulo	IWM_NVM_SECTION_TYPE_PRODUCTION,
1479286441Srpaulo};
1480286441Srpaulo
1481286441Srpaulo/* Default NVM size to read */
1482286441Srpaulo#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024)
1483286441Srpaulo#define IWM_MAX_NVM_SECTION_SIZE 7000
1484286441Srpaulo
1485286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1
1486286441Srpaulo#define IWM_NVM_READ_OPCODE 0
1487286441Srpaulo
1488286441Srpaulostatic int
1489286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section,
1490286441Srpaulo	uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len)
1491286441Srpaulo{
1492286441Srpaulo	offset = 0;
1493286441Srpaulo	struct iwm_nvm_access_cmd nvm_access_cmd = {
1494286441Srpaulo		.offset = htole16(offset),
1495286441Srpaulo		.length = htole16(length),
1496286441Srpaulo		.type = htole16(section),
1497286441Srpaulo		.op_code = IWM_NVM_READ_OPCODE,
1498286441Srpaulo	};
1499286441Srpaulo	struct iwm_nvm_access_resp *nvm_resp;
1500286441Srpaulo	struct iwm_rx_packet *pkt;
1501286441Srpaulo	struct iwm_host_cmd cmd = {
1502286441Srpaulo		.id = IWM_NVM_ACCESS_CMD,
1503286441Srpaulo		.flags = IWM_CMD_SYNC | IWM_CMD_WANT_SKB |
1504286441Srpaulo		    IWM_CMD_SEND_IN_RFKILL,
1505286441Srpaulo		.data = { &nvm_access_cmd, },
1506286441Srpaulo	};
1507286441Srpaulo	int ret, bytes_read, offset_read;
1508286441Srpaulo	uint8_t *resp_data;
1509286441Srpaulo
1510286441Srpaulo	cmd.len[0] = sizeof(struct iwm_nvm_access_cmd);
1511286441Srpaulo
1512286441Srpaulo	ret = iwm_send_cmd(sc, &cmd);
1513286441Srpaulo	if (ret)
1514286441Srpaulo		return ret;
1515286441Srpaulo
1516286441Srpaulo	pkt = cmd.resp_pkt;
1517286441Srpaulo	if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) {
1518286441Srpaulo		device_printf(sc->sc_dev,
1519286441Srpaulo		    "%s: Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n",
1520286441Srpaulo		    __func__, pkt->hdr.flags);
1521286441Srpaulo		ret = EIO;
1522286441Srpaulo		goto exit;
1523286441Srpaulo	}
1524286441Srpaulo
1525286441Srpaulo	/* Extract NVM response */
1526286441Srpaulo	nvm_resp = (void *)pkt->data;
1527286441Srpaulo
1528286441Srpaulo	ret = le16toh(nvm_resp->status);
1529286441Srpaulo	bytes_read = le16toh(nvm_resp->length);
1530286441Srpaulo	offset_read = le16toh(nvm_resp->offset);
1531286441Srpaulo	resp_data = nvm_resp->data;
1532286441Srpaulo	if (ret) {
1533286441Srpaulo		device_printf(sc->sc_dev,
1534286441Srpaulo		    "%s: NVM access command failed with status %d\n",
1535286441Srpaulo		    __func__, ret);
1536286441Srpaulo		ret = EINVAL;
1537286441Srpaulo		goto exit;
1538286441Srpaulo	}
1539286441Srpaulo
1540286441Srpaulo	if (offset_read != offset) {
1541286441Srpaulo		device_printf(sc->sc_dev,
1542286441Srpaulo		    "%s: NVM ACCESS response with invalid offset %d\n",
1543286441Srpaulo		    __func__, offset_read);
1544286441Srpaulo		ret = EINVAL;
1545286441Srpaulo		goto exit;
1546286441Srpaulo	}
1547286441Srpaulo
1548286441Srpaulo	memcpy(data + offset, resp_data, bytes_read);
1549286441Srpaulo	*len = bytes_read;
1550286441Srpaulo
1551286441Srpaulo exit:
1552286441Srpaulo	iwm_free_resp(sc, &cmd);
1553286441Srpaulo	return ret;
1554286441Srpaulo}
1555286441Srpaulo
1556286441Srpaulo/*
1557286441Srpaulo * Reads an NVM section completely.
1558286441Srpaulo * NICs prior to 7000 family doesn't have a real NVM, but just read
1559286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
1560286441Srpaulo * by uCode, we need to manually check in this case that we don't
1561286441Srpaulo * overflow and try to read more than the EEPROM size.
1562286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and
1563286441Srpaulo * the uCode fills the response with as much data as we can,
1564286441Srpaulo * without overflowing, so no check is needed.
1565286441Srpaulo */
1566286441Srpaulostatic int
1567286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc,
1568286441Srpaulo	uint16_t section, uint8_t *data, uint16_t *len)
1569286441Srpaulo{
1570286441Srpaulo	uint16_t length, seglen;
1571286441Srpaulo	int error;
1572286441Srpaulo
1573286441Srpaulo	/* Set nvm section read length */
1574286441Srpaulo	length = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE;
1575286441Srpaulo	*len = 0;
1576286441Srpaulo
1577286441Srpaulo	/* Read the NVM until exhausted (reading less than requested) */
1578286441Srpaulo	while (seglen == length) {
1579286441Srpaulo		error = iwm_nvm_read_chunk(sc,
1580286441Srpaulo		    section, *len, length, data, &seglen);
1581286441Srpaulo		if (error) {
1582286441Srpaulo			device_printf(sc->sc_dev,
1583286441Srpaulo			    "Cannot read NVM from section "
1584286441Srpaulo			    "%d offset %d, length %d\n",
1585286441Srpaulo			    section, *len, length);
1586286441Srpaulo			return error;
1587286441Srpaulo		}
1588286441Srpaulo		*len += seglen;
1589286441Srpaulo	}
1590286441Srpaulo
1591286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET,
1592286441Srpaulo	    "NVM section %d read completed\n", section);
1593286441Srpaulo	return 0;
1594286441Srpaulo}
1595286441Srpaulo
1596286441Srpaulo/*
1597286441Srpaulo * BEGIN IWM_NVM_PARSE
1598286441Srpaulo */
1599286441Srpaulo
1600286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */
1601286441Srpaulo
1602286441Srpaulo/* NVM offsets (in words) definitions */
1603286441Srpauloenum wkp_nvm_offsets {
1604286441Srpaulo	/* NVM HW-Section offset (in words) definitions */
1605286441Srpaulo	IWM_HW_ADDR = 0x15,
1606286441Srpaulo
1607286441Srpaulo/* NVM SW-Section offset (in words) definitions */
1608286441Srpaulo	IWM_NVM_SW_SECTION = 0x1C0,
1609286441Srpaulo	IWM_NVM_VERSION = 0,
1610286441Srpaulo	IWM_RADIO_CFG = 1,
1611286441Srpaulo	IWM_SKU = 2,
1612286441Srpaulo	IWM_N_HW_ADDRS = 3,
1613286441Srpaulo	IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION,
1614286441Srpaulo
1615286441Srpaulo/* NVM calibration section offset (in words) definitions */
1616286441Srpaulo	IWM_NVM_CALIB_SECTION = 0x2B8,
1617286441Srpaulo	IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION
1618286441Srpaulo};
1619286441Srpaulo
1620286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */
1621286441Srpauloenum nvm_sku_bits {
1622286441Srpaulo	IWM_NVM_SKU_CAP_BAND_24GHZ	= (1 << 0),
1623286441Srpaulo	IWM_NVM_SKU_CAP_BAND_52GHZ	= (1 << 1),
1624286441Srpaulo	IWM_NVM_SKU_CAP_11N_ENABLE	= (1 << 2),
1625286441Srpaulo	IWM_NVM_SKU_CAP_11AC_ENABLE	= (1 << 3),
1626286441Srpaulo};
1627286441Srpaulo
1628286441Srpaulo/* radio config bits (actual values from NVM definition) */
1629286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
1630286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
1631286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
1632286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
1633286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
1634286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
1635286441Srpaulo
1636286441Srpaulo#define DEFAULT_MAX_TX_POWER 16
1637286441Srpaulo
1638286441Srpaulo/**
1639286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM
1640286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo
1641286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel
1642286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed
1643286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required
1644298877Savos * XXX cannot find this (DFS) flag in iwl-nvm-parse.c
1645286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate
1646286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
1647286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
1648286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
1649286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
1650286441Srpaulo */
1651286441Srpauloenum iwm_nvm_channel_flags {
1652286441Srpaulo	IWM_NVM_CHANNEL_VALID = (1 << 0),
1653286441Srpaulo	IWM_NVM_CHANNEL_IBSS = (1 << 1),
1654286441Srpaulo	IWM_NVM_CHANNEL_ACTIVE = (1 << 3),
1655286441Srpaulo	IWM_NVM_CHANNEL_RADAR = (1 << 4),
1656286441Srpaulo	IWM_NVM_CHANNEL_DFS = (1 << 7),
1657286441Srpaulo	IWM_NVM_CHANNEL_WIDE = (1 << 8),
1658286441Srpaulo	IWM_NVM_CHANNEL_40MHZ = (1 << 9),
1659286441Srpaulo	IWM_NVM_CHANNEL_80MHZ = (1 << 10),
1660286441Srpaulo	IWM_NVM_CHANNEL_160MHZ = (1 << 11),
1661286441Srpaulo};
1662286441Srpaulo
1663286441Srpaulo/*
1664298877Savos * Translate EEPROM flags to net80211.
1665286441Srpaulo */
1666298877Savosstatic uint32_t
1667298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags)
1668286441Srpaulo{
1669298877Savos	uint32_t nflags;
1670286441Srpaulo
1671298877Savos	nflags = 0;
1672298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0)
1673298877Savos		nflags |= IEEE80211_CHAN_PASSIVE;
1674298877Savos	if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0)
1675298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1676298877Savos	if (ch_flags & IWM_NVM_CHANNEL_RADAR) {
1677298877Savos		nflags |= IEEE80211_CHAN_DFS;
1678298877Savos		/* Just in case. */
1679298877Savos		nflags |= IEEE80211_CHAN_NOADHOC;
1680286441Srpaulo	}
1681286441Srpaulo
1682298877Savos	return (nflags);
1683286441Srpaulo}
1684286441Srpaulo
1685286441Srpaulostatic void
1686298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[],
1687298877Savos    int maxchans, int *nchans, int ch_idx, int ch_num, const uint8_t bands[])
1688286441Srpaulo{
1689298877Savos	const uint16_t * const nvm_ch_flags = sc->sc_nvm.nvm_ch_flags;
1690298877Savos	uint32_t nflags;
1691286441Srpaulo	uint16_t ch_flags;
1692298877Savos	uint8_t ieee;
1693298877Savos	int error;
1694286441Srpaulo
1695298877Savos	for (; ch_idx < ch_num; ch_idx++) {
1696286441Srpaulo		ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx);
1697298877Savos		ieee = iwm_nvm_channels[ch_idx];
1698286441Srpaulo
1699286441Srpaulo		if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) {
1700286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1701286441Srpaulo			    "Ch. %d Flags %x [%sGHz] - No traffic\n",
1702298877Savos			    ieee, ch_flags,
1703286441Srpaulo			    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1704286441Srpaulo			    "5.2" : "2.4");
1705286441Srpaulo			continue;
1706286441Srpaulo		}
1707286441Srpaulo
1708298877Savos		nflags = iwm_eeprom_channel_flags(ch_flags);
1709298877Savos		error = ieee80211_add_channel(chans, maxchans, nchans,
1710298877Savos		    ieee, 0, 0, nflags, bands);
1711298877Savos		if (error != 0)
1712298877Savos			break;
1713286441Srpaulo
1714286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1715286441Srpaulo		    "Ch. %d Flags %x [%sGHz] - Added\n",
1716298877Savos		    ieee, ch_flags,
1717286441Srpaulo		    (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ?
1718286441Srpaulo		    "5.2" : "2.4");
1719286441Srpaulo	}
1720286441Srpaulo}
1721286441Srpaulo
1722298877Savosstatic void
1723298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans,
1724298877Savos    struct ieee80211_channel chans[])
1725298877Savos{
1726298877Savos	struct iwm_softc *sc = ic->ic_softc;
1727298877Savos	struct iwm_nvm_data *data = &sc->sc_nvm;
1728299883Skevlo	uint8_t bands[IEEE80211_MODE_BYTES];
1729298877Savos
1730298877Savos	memset(bands, 0, sizeof(bands));
1731298877Savos	/* 1-13: 11b/g channels. */
1732298877Savos	setbit(bands, IEEE80211_MODE_11B);
1733298877Savos	setbit(bands, IEEE80211_MODE_11G);
1734298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans, 0,
1735298877Savos	    IWM_NUM_2GHZ_CHANNELS - 1, bands);
1736298877Savos
1737298877Savos	/* 14: 11b channel only. */
1738298877Savos	clrbit(bands, IEEE80211_MODE_11G);
1739298877Savos	iwm_add_channel_band(sc, chans, maxchans, nchans,
1740298936Savos	    IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands);
1741298877Savos
1742298877Savos	if (data->sku_cap_band_52GHz_enable) {
1743298877Savos		memset(bands, 0, sizeof(bands));
1744298877Savos		setbit(bands, IEEE80211_MODE_11A);
1745298877Savos		iwm_add_channel_band(sc, chans, maxchans, nchans,
1746298936Savos		    IWM_NUM_2GHZ_CHANNELS, nitems(iwm_nvm_channels), bands);
1747298877Savos	}
1748298877Savos}
1749298877Savos
1750286441Srpaulostatic int
1751286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc,
1752286441Srpaulo	const uint16_t *nvm_hw, const uint16_t *nvm_sw,
1753286441Srpaulo	const uint16_t *nvm_calib, uint8_t tx_chains, uint8_t rx_chains)
1754286441Srpaulo{
1755286441Srpaulo	struct iwm_nvm_data *data = &sc->sc_nvm;
1756286441Srpaulo	uint8_t hw_addr[IEEE80211_ADDR_LEN];
1757286441Srpaulo	uint16_t radio_cfg, sku;
1758286441Srpaulo
1759286441Srpaulo	data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION);
1760286441Srpaulo
1761286441Srpaulo	radio_cfg = le16_to_cpup(nvm_sw + IWM_RADIO_CFG);
1762286441Srpaulo	data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg);
1763286441Srpaulo	data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg);
1764286441Srpaulo	data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg);
1765286441Srpaulo	data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg);
1766286441Srpaulo	data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK(radio_cfg);
1767286441Srpaulo	data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK(radio_cfg);
1768286441Srpaulo
1769286441Srpaulo	sku = le16_to_cpup(nvm_sw + IWM_SKU);
1770286441Srpaulo	data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ;
1771286441Srpaulo	data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ;
1772286441Srpaulo	data->sku_cap_11n_enable = 0;
1773286441Srpaulo
1774286441Srpaulo	if (!data->valid_tx_ant || !data->valid_rx_ant) {
1775286441Srpaulo		device_printf(sc->sc_dev,
1776286441Srpaulo		    "%s: invalid antennas (0x%x, 0x%x)\n",
1777286441Srpaulo		    __func__, data->valid_tx_ant,
1778286441Srpaulo		    data->valid_rx_ant);
1779286441Srpaulo		return EINVAL;
1780286441Srpaulo	}
1781286441Srpaulo
1782286441Srpaulo	data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS);
1783286441Srpaulo
1784286441Srpaulo	data->xtal_calib[0] = *(nvm_calib + IWM_XTAL_CALIB);
1785286441Srpaulo	data->xtal_calib[1] = *(nvm_calib + IWM_XTAL_CALIB + 1);
1786286441Srpaulo
1787286441Srpaulo	/* The byte order is little endian 16 bit, meaning 214365 */
1788286441Srpaulo	IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR);
1789286441Srpaulo	data->hw_addr[0] = hw_addr[1];
1790286441Srpaulo	data->hw_addr[1] = hw_addr[0];
1791286441Srpaulo	data->hw_addr[2] = hw_addr[3];
1792286441Srpaulo	data->hw_addr[3] = hw_addr[2];
1793286441Srpaulo	data->hw_addr[4] = hw_addr[5];
1794286441Srpaulo	data->hw_addr[5] = hw_addr[4];
1795286441Srpaulo
1796298877Savos	memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS],
1797298877Savos	    sizeof(data->nvm_ch_flags));
1798286441Srpaulo	data->calib_version = 255;   /* TODO:
1799286441Srpaulo					this value will prevent some checks from
1800286441Srpaulo					failing, we need to check if this
1801286441Srpaulo					field is still needed, and if it does,
1802286441Srpaulo					where is it in the NVM */
1803286441Srpaulo
1804286441Srpaulo	return 0;
1805286441Srpaulo}
1806286441Srpaulo
1807286441Srpaulo/*
1808286441Srpaulo * END NVM PARSE
1809286441Srpaulo */
1810286441Srpaulo
1811286441Srpaulostruct iwm_nvm_section {
1812286441Srpaulo	uint16_t length;
1813286441Srpaulo	const uint8_t *data;
1814286441Srpaulo};
1815286441Srpaulo
1816286441Srpaulostatic int
1817286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections)
1818286441Srpaulo{
1819286441Srpaulo	const uint16_t *hw, *sw, *calib;
1820286441Srpaulo
1821286441Srpaulo	/* Checking for required sections */
1822286441Srpaulo	if (!sections[IWM_NVM_SECTION_TYPE_SW].data ||
1823286441Srpaulo	    !sections[IWM_NVM_SECTION_TYPE_HW].data) {
1824286441Srpaulo		device_printf(sc->sc_dev,
1825286441Srpaulo		    "%s: Can't parse empty NVM sections\n",
1826286441Srpaulo		    __func__);
1827286441Srpaulo		return ENOENT;
1828286441Srpaulo	}
1829286441Srpaulo
1830286441Srpaulo	hw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_HW].data;
1831286441Srpaulo	sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data;
1832286441Srpaulo	calib = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data;
1833286441Srpaulo	return iwm_parse_nvm_data(sc, hw, sw, calib,
1834286441Srpaulo	    IWM_FW_VALID_TX_ANT(sc), IWM_FW_VALID_RX_ANT(sc));
1835286441Srpaulo}
1836286441Srpaulo
1837286441Srpaulostatic int
1838286441Srpauloiwm_nvm_init(struct iwm_softc *sc)
1839286441Srpaulo{
1840286441Srpaulo	struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS];
1841286441Srpaulo	int i, section, error;
1842286441Srpaulo	uint16_t len;
1843286441Srpaulo	uint8_t *nvm_buffer, *temp;
1844286441Srpaulo
1845286441Srpaulo	/* Read From FW NVM */
1846286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_EEPROM,
1847286441Srpaulo	    "%s: Read NVM\n",
1848286441Srpaulo	    __func__);
1849286441Srpaulo
1850286441Srpaulo	/* TODO: find correct NVM max size for a section */
1851286441Srpaulo	nvm_buffer = malloc(IWM_OTP_LOW_IMAGE_SIZE, M_DEVBUF, M_NOWAIT);
1852286441Srpaulo	if (nvm_buffer == NULL)
1853286441Srpaulo		return (ENOMEM);
1854286441Srpaulo	for (i = 0; i < nitems(nvm_to_read); i++) {
1855286441Srpaulo		section = nvm_to_read[i];
1856286441Srpaulo		KASSERT(section <= nitems(nvm_sections),
1857286441Srpaulo		    ("too many sections"));
1858286441Srpaulo
1859286441Srpaulo		error = iwm_nvm_read_section(sc, section, nvm_buffer, &len);
1860286441Srpaulo		if (error)
1861286441Srpaulo			break;
1862286441Srpaulo
1863286441Srpaulo		temp = malloc(len, M_DEVBUF, M_NOWAIT);
1864286441Srpaulo		if (temp == NULL) {
1865286441Srpaulo			error = ENOMEM;
1866286441Srpaulo			break;
1867286441Srpaulo		}
1868286441Srpaulo		memcpy(temp, nvm_buffer, len);
1869286441Srpaulo		nvm_sections[section].data = temp;
1870286441Srpaulo		nvm_sections[section].length = len;
1871286441Srpaulo	}
1872286441Srpaulo	free(nvm_buffer, M_DEVBUF);
1873286441Srpaulo	if (error)
1874286441Srpaulo		return error;
1875286441Srpaulo
1876286441Srpaulo	return iwm_parse_nvm_sections(sc, nvm_sections);
1877286441Srpaulo}
1878286441Srpaulo
1879286441Srpaulo/*
1880286441Srpaulo * Firmware loading gunk.  This is kind of a weird hybrid between the
1881286441Srpaulo * iwn driver and the Linux iwlwifi driver.
1882286441Srpaulo */
1883286441Srpaulo
1884286441Srpaulostatic int
1885286441Srpauloiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr,
1886286441Srpaulo	const uint8_t *section, uint32_t byte_cnt)
1887286441Srpaulo{
1888286441Srpaulo	struct iwm_dma_info *dma = &sc->fw_dma;
1889286441Srpaulo	int error;
1890286441Srpaulo
1891286441Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
1892286441Srpaulo	memcpy(dma->vaddr, section, byte_cnt);
1893286441Srpaulo	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1894286441Srpaulo
1895286441Srpaulo	if (!iwm_nic_lock(sc))
1896286441Srpaulo		return EBUSY;
1897286441Srpaulo
1898286441Srpaulo	sc->sc_fw_chunk_done = 0;
1899286441Srpaulo
1900286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
1901286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
1902286441Srpaulo	IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL),
1903286441Srpaulo	    dst_addr);
1904286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL),
1905286441Srpaulo	    dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
1906286441Srpaulo	IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL),
1907286441Srpaulo	    (iwm_get_dma_hi_addr(dma->paddr)
1908286441Srpaulo	      << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
1909286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL),
1910286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
1911286441Srpaulo	    1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
1912286441Srpaulo	    IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
1913286441Srpaulo	IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL),
1914286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE    |
1915286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
1916286441Srpaulo	    IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
1917286441Srpaulo
1918286441Srpaulo	iwm_nic_unlock(sc);
1919286441Srpaulo
1920286441Srpaulo	/* wait 1s for this segment to load */
1921286441Srpaulo	while (!sc->sc_fw_chunk_done)
1922286441Srpaulo		if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0)
1923286441Srpaulo			break;
1924286441Srpaulo
1925286441Srpaulo	return error;
1926286441Srpaulo}
1927286441Srpaulo
1928286441Srpaulostatic int
1929286441Srpauloiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
1930286441Srpaulo{
1931286441Srpaulo	struct iwm_fw_sects *fws;
1932286441Srpaulo	int error, i, w;
1933286441Srpaulo	const void *data;
1934286441Srpaulo	uint32_t dlen;
1935286441Srpaulo	uint32_t offset;
1936286441Srpaulo
1937286441Srpaulo	sc->sc_uc.uc_intr = 0;
1938286441Srpaulo
1939286441Srpaulo	fws = &sc->sc_fw.fw_sects[ucode_type];
1940286441Srpaulo	for (i = 0; i < fws->fw_count; i++) {
1941286441Srpaulo		data = fws->fw_sect[i].fws_data;
1942286441Srpaulo		dlen = fws->fw_sect[i].fws_len;
1943286441Srpaulo		offset = fws->fw_sect[i].fws_devoff;
1944286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV,
1945286441Srpaulo		    "LOAD FIRMWARE type %d offset %u len %d\n",
1946286441Srpaulo		    ucode_type, offset, dlen);
1947286441Srpaulo		error = iwm_firmware_load_chunk(sc, offset, data, dlen);
1948286441Srpaulo		if (error) {
1949286441Srpaulo			device_printf(sc->sc_dev,
1950286441Srpaulo			    "%s: chunk %u of %u returned error %02d\n",
1951286441Srpaulo			    __func__, i, fws->fw_count, error);
1952286441Srpaulo			return error;
1953286441Srpaulo		}
1954286441Srpaulo	}
1955286441Srpaulo
1956286441Srpaulo	/* wait for the firmware to load */
1957286441Srpaulo	IWM_WRITE(sc, IWM_CSR_RESET, 0);
1958286441Srpaulo
1959286441Srpaulo	for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) {
1960286441Srpaulo		error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10);
1961286441Srpaulo	}
1962286441Srpaulo
1963286441Srpaulo	return error;
1964286441Srpaulo}
1965286441Srpaulo
1966286441Srpaulo/* iwlwifi: pcie/trans.c */
1967286441Srpaulostatic int
1968286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
1969286441Srpaulo{
1970286441Srpaulo	int error;
1971286441Srpaulo
1972286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1973286441Srpaulo
1974286441Srpaulo	if ((error = iwm_nic_init(sc)) != 0) {
1975286441Srpaulo		device_printf(sc->sc_dev, "unable to init nic\n");
1976286441Srpaulo		return error;
1977286441Srpaulo	}
1978286441Srpaulo
1979286441Srpaulo	/* make sure rfkill handshake bits are cleared */
1980286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
1981286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR,
1982286441Srpaulo	    IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
1983286441Srpaulo
1984286441Srpaulo	/* clear (again), then enable host interrupts */
1985286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, ~0);
1986286441Srpaulo	iwm_enable_interrupts(sc);
1987286441Srpaulo
1988286441Srpaulo	/* really make sure rfkill handshake bits are cleared */
1989286441Srpaulo	/* maybe we should write a few times more?  just to make sure */
1990286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
1991286441Srpaulo	IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL);
1992286441Srpaulo
1993286441Srpaulo	/* Load the given image to the HW */
1994286441Srpaulo	return iwm_load_firmware(sc, ucode_type);
1995286441Srpaulo}
1996286441Srpaulo
1997286441Srpaulostatic int
1998286441Srpauloiwm_fw_alive(struct iwm_softc *sc, uint32_t sched_base)
1999286441Srpaulo{
2000286441Srpaulo	return iwm_post_alive(sc);
2001286441Srpaulo}
2002286441Srpaulo
2003286441Srpaulostatic int
2004286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant)
2005286441Srpaulo{
2006286441Srpaulo	struct iwm_tx_ant_cfg_cmd tx_ant_cmd = {
2007286441Srpaulo		.valid = htole32(valid_tx_ant),
2008286441Srpaulo	};
2009286441Srpaulo
2010286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD,
2011286441Srpaulo	    IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd);
2012286441Srpaulo}
2013286441Srpaulo
2014286441Srpaulo/* iwlwifi: mvm/fw.c */
2015286441Srpaulostatic int
2016286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc)
2017286441Srpaulo{
2018286441Srpaulo	struct iwm_phy_cfg_cmd phy_cfg_cmd;
2019286441Srpaulo	enum iwm_ucode_type ucode_type = sc->sc_uc_current;
2020286441Srpaulo
2021286441Srpaulo	/* Set parameters */
2022286441Srpaulo	phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config);
2023286441Srpaulo	phy_cfg_cmd.calib_control.event_trigger =
2024286441Srpaulo	    sc->sc_default_calib[ucode_type].event_trigger;
2025286441Srpaulo	phy_cfg_cmd.calib_control.flow_trigger =
2026286441Srpaulo	    sc->sc_default_calib[ucode_type].flow_trigger;
2027286441Srpaulo
2028286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
2029286441Srpaulo	    "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg);
2030286441Srpaulo	return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC,
2031286441Srpaulo	    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
2032286441Srpaulo}
2033286441Srpaulo
2034286441Srpaulostatic int
2035286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc,
2036286441Srpaulo	enum iwm_ucode_type ucode_type)
2037286441Srpaulo{
2038286441Srpaulo	enum iwm_ucode_type old_type = sc->sc_uc_current;
2039286441Srpaulo	int error;
2040286441Srpaulo
2041286441Srpaulo	if ((error = iwm_read_firmware(sc, ucode_type)) != 0)
2042286441Srpaulo		return error;
2043286441Srpaulo
2044286441Srpaulo	sc->sc_uc_current = ucode_type;
2045286441Srpaulo	error = iwm_start_fw(sc, ucode_type);
2046286441Srpaulo	if (error) {
2047286441Srpaulo		sc->sc_uc_current = old_type;
2048286441Srpaulo		return error;
2049286441Srpaulo	}
2050286441Srpaulo
2051286441Srpaulo	return iwm_fw_alive(sc, sc->sched_base);
2052286441Srpaulo}
2053286441Srpaulo
2054286441Srpaulo/*
2055286441Srpaulo * mvm misc bits
2056286441Srpaulo */
2057286441Srpaulo
2058286441Srpaulo/*
2059286441Srpaulo * follows iwlwifi/fw.c
2060286441Srpaulo */
2061286441Srpaulostatic int
2062286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm)
2063286441Srpaulo{
2064286441Srpaulo	int error;
2065286441Srpaulo
2066286441Srpaulo	/* do not operate with rfkill switch turned on */
2067286441Srpaulo	if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) {
2068286441Srpaulo		device_printf(sc->sc_dev,
2069286441Srpaulo		    "radio is disabled by hardware switch\n");
2070286441Srpaulo		return EPERM;
2071286441Srpaulo	}
2072286441Srpaulo
2073286441Srpaulo	sc->sc_init_complete = 0;
2074286441Srpaulo	if ((error = iwm_mvm_load_ucode_wait_alive(sc,
2075286441Srpaulo	    IWM_UCODE_TYPE_INIT)) != 0)
2076286441Srpaulo		return error;
2077286441Srpaulo
2078286441Srpaulo	if (justnvm) {
2079286441Srpaulo		if ((error = iwm_nvm_init(sc)) != 0) {
2080286441Srpaulo			device_printf(sc->sc_dev, "failed to read nvm\n");
2081286441Srpaulo			return error;
2082286441Srpaulo		}
2083289729Skevlo		IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->sc_nvm.hw_addr);
2084286441Srpaulo
2085286441Srpaulo		sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd)
2086286441Srpaulo		    + sc->sc_capa_max_probe_len
2087286441Srpaulo		    + IWM_MAX_NUM_SCAN_CHANNELS
2088286441Srpaulo		    * sizeof(struct iwm_scan_channel);
2089286441Srpaulo		sc->sc_scan_cmd = malloc(sc->sc_scan_cmd_len, M_DEVBUF,
2090286441Srpaulo		    M_NOWAIT);
2091286441Srpaulo		if (sc->sc_scan_cmd == NULL)
2092286441Srpaulo			return (ENOMEM);
2093286441Srpaulo
2094286441Srpaulo		return 0;
2095286441Srpaulo	}
2096286441Srpaulo
2097286441Srpaulo	/* Send TX valid antennas before triggering calibrations */
2098286441Srpaulo	if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0)
2099286441Srpaulo		return error;
2100286441Srpaulo
2101286441Srpaulo	/*
2102286441Srpaulo	* Send phy configurations command to init uCode
2103286441Srpaulo	* to start the 16.0 uCode init image internal calibrations.
2104286441Srpaulo	*/
2105286441Srpaulo	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) {
2106286441Srpaulo		device_printf(sc->sc_dev,
2107286441Srpaulo		    "%s: failed to run internal calibration: %d\n",
2108286441Srpaulo		    __func__, error);
2109286441Srpaulo		return error;
2110286441Srpaulo	}
2111286441Srpaulo
2112286441Srpaulo	/*
2113286441Srpaulo	 * Nothing to do but wait for the init complete notification
2114286441Srpaulo	 * from the firmware
2115286441Srpaulo	 */
2116286441Srpaulo	while (!sc->sc_init_complete)
2117286441Srpaulo		if ((error = msleep(&sc->sc_init_complete, &sc->sc_mtx,
2118286441Srpaulo		    0, "iwminit", 2*hz)) != 0)
2119286441Srpaulo			break;
2120286441Srpaulo
2121286441Srpaulo	return error;
2122286441Srpaulo}
2123286441Srpaulo
2124286441Srpaulo/*
2125286441Srpaulo * receive side
2126286441Srpaulo */
2127286441Srpaulo
2128286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */
2129286441Srpaulostatic int
2130286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx)
2131286441Srpaulo{
2132286441Srpaulo	struct iwm_rx_ring *ring = &sc->rxq;
2133286441Srpaulo	struct iwm_rx_data *data = &ring->data[idx];
2134286441Srpaulo	struct mbuf *m;
2135286441Srpaulo	int error;
2136286441Srpaulo	bus_addr_t paddr;
2137286441Srpaulo
2138286441Srpaulo	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE);
2139286441Srpaulo	if (m == NULL)
2140286441Srpaulo		return ENOBUFS;
2141286441Srpaulo
2142286441Srpaulo	if (data->m != NULL)
2143286441Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2144286441Srpaulo
2145286441Srpaulo	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
2146286441Srpaulo	error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
2147286441Srpaulo	if (error != 0) {
2148286441Srpaulo		device_printf(sc->sc_dev,
2149286441Srpaulo		    "%s: could not create RX buf DMA map, error %d\n",
2150286441Srpaulo		    __func__, error);
2151286441Srpaulo		goto fail;
2152286441Srpaulo	}
2153286441Srpaulo	data->m = m;
2154286441Srpaulo	error = bus_dmamap_load(ring->data_dmat, data->map,
2155286441Srpaulo	    mtod(data->m, void *), IWM_RBUF_SIZE, iwm_dma_map_addr,
2156286441Srpaulo	    &paddr, BUS_DMA_NOWAIT);
2157286441Srpaulo	if (error != 0 && error != EFBIG) {
2158286441Srpaulo		device_printf(sc->sc_dev,
2159286441Srpaulo		    "%s: can't not map mbuf, error %d\n", __func__,
2160286441Srpaulo		    error);
2161286441Srpaulo		goto fail;
2162286441Srpaulo	}
2163286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
2164286441Srpaulo
2165286441Srpaulo	/* Update RX descriptor. */
2166286441Srpaulo	ring->desc[idx] = htole32(paddr >> 8);
2167286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2168286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2169286441Srpaulo
2170286441Srpaulo	return 0;
2171286441Srpaulofail:
2172286441Srpaulo	return error;
2173286441Srpaulo}
2174286441Srpaulo
2175286441Srpaulo/* iwlwifi: mvm/rx.c */
2176286441Srpaulo#define IWM_RSSI_OFFSET 50
2177286441Srpaulostatic int
2178286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2179286441Srpaulo{
2180286441Srpaulo	int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
2181286441Srpaulo	uint32_t agc_a, agc_b;
2182286441Srpaulo	uint32_t val;
2183286441Srpaulo
2184286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]);
2185286441Srpaulo	agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS;
2186286441Srpaulo	agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS;
2187286441Srpaulo
2188286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]);
2189286441Srpaulo	rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS;
2190286441Srpaulo	rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS;
2191286441Srpaulo
2192286441Srpaulo	/*
2193286441Srpaulo	 * dBm = rssi dB - agc dB - constant.
2194286441Srpaulo	 * Higher AGC (higher radio gain) means lower signal.
2195286441Srpaulo	 */
2196286441Srpaulo	rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a;
2197286441Srpaulo	rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b;
2198286441Srpaulo	max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm);
2199286441Srpaulo
2200286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2201286441Srpaulo	    "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
2202286441Srpaulo	    rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
2203286441Srpaulo
2204286441Srpaulo	return max_rssi_dbm;
2205286441Srpaulo}
2206286441Srpaulo
2207286441Srpaulo/* iwlwifi: mvm/rx.c */
2208286441Srpaulo/*
2209286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API
2210286441Srpaulo * values are reported by the fw as positive values - need to negate
2211286441Srpaulo * to obtain their dBM.  Account for missing antennas by replacing 0
2212286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
2213286441Srpaulo */
2214286441Srpaulostatic int
2215286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info)
2216286441Srpaulo{
2217286441Srpaulo	int energy_a, energy_b, energy_c, max_energy;
2218286441Srpaulo	uint32_t val;
2219286441Srpaulo
2220286441Srpaulo	val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]);
2221286441Srpaulo	energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >>
2222286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_A_POS;
2223286441Srpaulo	energy_a = energy_a ? -energy_a : -256;
2224286441Srpaulo	energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >>
2225286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_B_POS;
2226286441Srpaulo	energy_b = energy_b ? -energy_b : -256;
2227286441Srpaulo	energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >>
2228286441Srpaulo	    IWM_RX_INFO_ENERGY_ANT_C_POS;
2229286441Srpaulo	energy_c = energy_c ? -energy_c : -256;
2230286441Srpaulo	max_energy = MAX(energy_a, energy_b);
2231286441Srpaulo	max_energy = MAX(max_energy, energy_c);
2232286441Srpaulo
2233286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2234286441Srpaulo	    "energy In A %d B %d C %d , and max %d\n",
2235286441Srpaulo	    energy_a, energy_b, energy_c, max_energy);
2236286441Srpaulo
2237286441Srpaulo	return max_energy;
2238286441Srpaulo}
2239286441Srpaulo
2240286441Srpaulostatic void
2241286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc,
2242286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2243286441Srpaulo{
2244286441Srpaulo	struct iwm_rx_phy_info *phy_info = (void *)pkt->data;
2245286441Srpaulo
2246286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n");
2247286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2248286441Srpaulo
2249286441Srpaulo	memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info));
2250286441Srpaulo}
2251286441Srpaulo
2252286441Srpaulo/*
2253286441Srpaulo * Retrieve the average noise (in dBm) among receivers.
2254286441Srpaulo */
2255286441Srpaulostatic int
2256286441Srpauloiwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *stats)
2257286441Srpaulo{
2258286441Srpaulo	int i, total, nbant, noise;
2259286441Srpaulo
2260286441Srpaulo	total = nbant = noise = 0;
2261286441Srpaulo	for (i = 0; i < 3; i++) {
2262286441Srpaulo		noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff;
2263286441Srpaulo		if (noise) {
2264286441Srpaulo			total += noise;
2265286441Srpaulo			nbant++;
2266286441Srpaulo		}
2267286441Srpaulo	}
2268286441Srpaulo
2269286441Srpaulo	/* There should be at least one antenna but check anyway. */
2270286441Srpaulo	return (nbant == 0) ? -127 : (total / nbant) - 107;
2271286441Srpaulo}
2272286441Srpaulo
2273286441Srpaulo/*
2274286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler
2275286441Srpaulo *
2276286441Srpaulo * Handles the actual data of the Rx packet from the fw
2277286441Srpaulo */
2278286441Srpaulostatic void
2279286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc,
2280286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2281286441Srpaulo{
2282287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
2283286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2284286441Srpaulo	struct ieee80211_frame *wh;
2285286441Srpaulo	struct ieee80211_node *ni;
2286286441Srpaulo	struct ieee80211_rx_stats rxs;
2287286441Srpaulo	struct mbuf *m;
2288286441Srpaulo	struct iwm_rx_phy_info *phy_info;
2289286441Srpaulo	struct iwm_rx_mpdu_res_start *rx_res;
2290286441Srpaulo	uint32_t len;
2291286441Srpaulo	uint32_t rx_pkt_status;
2292286441Srpaulo	int rssi;
2293286441Srpaulo
2294286441Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2295286441Srpaulo
2296286441Srpaulo	phy_info = &sc->sc_last_phy_info;
2297286441Srpaulo	rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
2298286441Srpaulo	wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
2299286441Srpaulo	len = le16toh(rx_res->byte_count);
2300286441Srpaulo	rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len));
2301286441Srpaulo
2302286441Srpaulo	m = data->m;
2303286441Srpaulo	m->m_data = pkt->data + sizeof(*rx_res);
2304286441Srpaulo	m->m_pkthdr.len = m->m_len = len;
2305286441Srpaulo
2306286441Srpaulo	if (__predict_false(phy_info->cfg_phy_cnt > 20)) {
2307286441Srpaulo		device_printf(sc->sc_dev,
2308286441Srpaulo		    "dsp size out of range [0,20]: %d\n",
2309286441Srpaulo		    phy_info->cfg_phy_cnt);
2310286441Srpaulo		return;
2311286441Srpaulo	}
2312286441Srpaulo
2313286441Srpaulo	if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
2314286441Srpaulo	    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) {
2315286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2316286441Srpaulo		    "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
2317286441Srpaulo		return; /* drop */
2318286441Srpaulo	}
2319286441Srpaulo
2320286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
2321286441Srpaulo		rssi = iwm_mvm_get_signal_strength(sc, phy_info);
2322286441Srpaulo	} else {
2323286441Srpaulo		rssi = iwm_mvm_calc_rssi(sc, phy_info);
2324286441Srpaulo	}
2325286441Srpaulo	rssi = (0 - IWM_MIN_DBM) + rssi;	/* normalize */
2326286441Srpaulo	rssi = MIN(rssi, sc->sc_max_rssi);	/* clip to max. 100% */
2327286441Srpaulo
2328286441Srpaulo	/* replenish ring for the buffer we're going to feed to the sharks */
2329286441Srpaulo	if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) {
2330286441Srpaulo		device_printf(sc->sc_dev, "%s: unable to add more buffers\n",
2331286441Srpaulo		    __func__);
2332286441Srpaulo		return;
2333286441Srpaulo	}
2334286441Srpaulo
2335286441Srpaulo	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2336286441Srpaulo
2337286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RECV,
2338286441Srpaulo	    "%s: phy_info: channel=%d, flags=0x%08x\n",
2339286441Srpaulo	    __func__,
2340286441Srpaulo	    le16toh(phy_info->channel),
2341286441Srpaulo	    le16toh(phy_info->phy_flags));
2342286441Srpaulo
2343286441Srpaulo	/*
2344286441Srpaulo	 * Populate an RX state struct with the provided information.
2345286441Srpaulo	 */
2346286441Srpaulo	bzero(&rxs, sizeof(rxs));
2347286441Srpaulo	rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
2348286441Srpaulo	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
2349286441Srpaulo	rxs.c_ieee = le16toh(phy_info->channel);
2350286441Srpaulo	if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) {
2351286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ);
2352286441Srpaulo	} else {
2353286441Srpaulo		rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ);
2354286441Srpaulo	}
2355286441Srpaulo	rxs.rssi = rssi - sc->sc_noise;
2356286441Srpaulo	rxs.nf = sc->sc_noise;
2357286441Srpaulo
2358286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
2359286441Srpaulo		struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap;
2360286441Srpaulo
2361286441Srpaulo		tap->wr_flags = 0;
2362286441Srpaulo		if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE))
2363286441Srpaulo			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2364286441Srpaulo		tap->wr_chan_freq = htole16(rxs.c_freq);
2365286441Srpaulo		/* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */
2366286441Srpaulo		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
2367286441Srpaulo		tap->wr_dbm_antsignal = (int8_t)rssi;
2368286441Srpaulo		tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
2369286441Srpaulo		tap->wr_tsft = phy_info->system_timestamp;
2370286441Srpaulo		switch (phy_info->rate) {
2371286441Srpaulo		/* CCK rates. */
2372286441Srpaulo		case  10: tap->wr_rate =   2; break;
2373286441Srpaulo		case  20: tap->wr_rate =   4; break;
2374286441Srpaulo		case  55: tap->wr_rate =  11; break;
2375286441Srpaulo		case 110: tap->wr_rate =  22; break;
2376286441Srpaulo		/* OFDM rates. */
2377286441Srpaulo		case 0xd: tap->wr_rate =  12; break;
2378286441Srpaulo		case 0xf: tap->wr_rate =  18; break;
2379286441Srpaulo		case 0x5: tap->wr_rate =  24; break;
2380286441Srpaulo		case 0x7: tap->wr_rate =  36; break;
2381286441Srpaulo		case 0x9: tap->wr_rate =  48; break;
2382286441Srpaulo		case 0xb: tap->wr_rate =  72; break;
2383286441Srpaulo		case 0x1: tap->wr_rate =  96; break;
2384286441Srpaulo		case 0x3: tap->wr_rate = 108; break;
2385286441Srpaulo		/* Unknown rate: should not happen. */
2386286441Srpaulo		default:  tap->wr_rate =   0;
2387286441Srpaulo		}
2388286441Srpaulo	}
2389286441Srpaulo
2390286441Srpaulo	IWM_UNLOCK(sc);
2391286441Srpaulo	if (ni != NULL) {
2392286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m);
2393286441Srpaulo		ieee80211_input_mimo(ni, m, &rxs);
2394286441Srpaulo		ieee80211_free_node(ni);
2395286441Srpaulo	} else {
2396286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m);
2397286441Srpaulo		ieee80211_input_mimo_all(ic, m, &rxs);
2398286441Srpaulo	}
2399286441Srpaulo	IWM_LOCK(sc);
2400286441Srpaulo}
2401286441Srpaulo
2402293100Savosstatic int
2403286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
2404286441Srpaulo	struct iwm_node *in)
2405286441Srpaulo{
2406286441Srpaulo	struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data;
2407293100Savos	struct ieee80211_node *ni = &in->in_ni;
2408293100Savos	struct ieee80211vap *vap = ni->ni_vap;
2409286441Srpaulo	int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
2410286441Srpaulo	int failack = tx_resp->failure_frame;
2411286441Srpaulo
2412286441Srpaulo	KASSERT(tx_resp->frame_count == 1, ("too many frames"));
2413286441Srpaulo
2414286441Srpaulo	/* Update rate control statistics. */
2415298611Sadrian	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",
2416298611Sadrian	    __func__,
2417298611Sadrian	    (int) le16toh(tx_resp->status.status),
2418298611Sadrian	    (int) le16toh(tx_resp->status.sequence),
2419298611Sadrian	    tx_resp->frame_count,
2420298611Sadrian	    tx_resp->bt_kill_count,
2421298611Sadrian	    tx_resp->failure_rts,
2422298611Sadrian	    tx_resp->failure_frame,
2423298611Sadrian	    le32toh(tx_resp->initial_rate),
2424298611Sadrian	    (int) le16toh(tx_resp->wireless_media_time));
2425298611Sadrian
2426286441Srpaulo	if (status != IWM_TX_STATUS_SUCCESS &&
2427286441Srpaulo	    status != IWM_TX_STATUS_DIRECT_DONE) {
2428293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
2429286441Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &failack, NULL);
2430293100Savos		return (1);
2431286441Srpaulo	} else {
2432293100Savos		ieee80211_ratectl_tx_complete(vap, ni,
2433286441Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL);
2434293100Savos		return (0);
2435286441Srpaulo	}
2436286441Srpaulo}
2437286441Srpaulo
2438286441Srpaulostatic void
2439286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc,
2440286441Srpaulo	struct iwm_rx_packet *pkt, struct iwm_rx_data *data)
2441286441Srpaulo{
2442286441Srpaulo	struct iwm_cmd_header *cmd_hdr = &pkt->hdr;
2443286441Srpaulo	int idx = cmd_hdr->idx;
2444286441Srpaulo	int qid = cmd_hdr->qid;
2445286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[qid];
2446286441Srpaulo	struct iwm_tx_data *txd = &ring->data[idx];
2447286441Srpaulo	struct iwm_node *in = txd->in;
2448293100Savos	struct mbuf *m = txd->m;
2449293100Savos	int status;
2450286441Srpaulo
2451293100Savos	KASSERT(txd->done == 0, ("txd not done"));
2452293100Savos	KASSERT(txd->in != NULL, ("txd without node"));
2453293100Savos	KASSERT(txd->m != NULL, ("txd without mbuf"));
2454293100Savos
2455286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2456286441Srpaulo
2457286441Srpaulo	sc->sc_tx_timer = 0;
2458286441Srpaulo
2459293100Savos	status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in);
2460286441Srpaulo
2461286441Srpaulo	/* Unmap and free mbuf. */
2462286441Srpaulo	bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE);
2463286441Srpaulo	bus_dmamap_unload(ring->data_dmat, txd->map);
2464286441Srpaulo
2465286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
2466286441Srpaulo	    "free txd %p, in %p\n", txd, txd->in);
2467286441Srpaulo	txd->done = 1;
2468286441Srpaulo	txd->m = NULL;
2469286441Srpaulo	txd->in = NULL;
2470286441Srpaulo
2471293100Savos	ieee80211_tx_complete(&in->in_ni, m, status);
2472293100Savos
2473286441Srpaulo	if (--ring->queued < IWM_TX_RING_LOMARK) {
2474286441Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2475287197Sglebius		if (sc->qfullmsk == 0) {
2476286441Srpaulo			/*
2477286441Srpaulo			 * Well, we're in interrupt context, but then again
2478286441Srpaulo			 * I guess net80211 does all sorts of stunts in
2479286441Srpaulo			 * interrupt context, so maybe this is no biggie.
2480286441Srpaulo			 */
2481287197Sglebius			iwm_start(sc);
2482286441Srpaulo		}
2483286441Srpaulo	}
2484286441Srpaulo}
2485286441Srpaulo
2486286441Srpaulo/*
2487286441Srpaulo * transmit side
2488286441Srpaulo */
2489286441Srpaulo
2490286441Srpaulo/*
2491286441Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2492286441Srpaulo * processes waiting for a synchronous command completion.
2493286441Srpaulo * from if_iwn
2494286441Srpaulo */
2495286441Srpaulostatic void
2496286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
2497286441Srpaulo{
2498286441Srpaulo	struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE];
2499286441Srpaulo	struct iwm_tx_data *data;
2500286441Srpaulo
2501286441Srpaulo	if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) {
2502286441Srpaulo		return;	/* Not a command ack. */
2503286441Srpaulo	}
2504286441Srpaulo
2505286441Srpaulo	data = &ring->data[pkt->hdr.idx];
2506286441Srpaulo
2507286441Srpaulo	/* If the command was mapped in an mbuf, free it. */
2508286441Srpaulo	if (data->m != NULL) {
2509286441Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
2510286441Srpaulo		    BUS_DMASYNC_POSTWRITE);
2511286441Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2512286441Srpaulo		m_freem(data->m);
2513286441Srpaulo		data->m = NULL;
2514286441Srpaulo	}
2515286441Srpaulo	wakeup(&ring->desc[pkt->hdr.idx]);
2516286441Srpaulo}
2517286441Srpaulo
2518286441Srpaulo#if 0
2519286441Srpaulo/*
2520286441Srpaulo * necessary only for block ack mode
2521286441Srpaulo */
2522286441Srpaulovoid
2523286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id,
2524286441Srpaulo	uint16_t len)
2525286441Srpaulo{
2526286441Srpaulo	struct iwm_agn_scd_bc_tbl *scd_bc_tbl;
2527286441Srpaulo	uint16_t w_val;
2528286441Srpaulo
2529286441Srpaulo	scd_bc_tbl = sc->sched_dma.vaddr;
2530286441Srpaulo
2531286441Srpaulo	len += 8; /* magic numbers came naturally from paris */
2532286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE)
2533286441Srpaulo		len = roundup(len, 4) / 4;
2534286441Srpaulo
2535286441Srpaulo	w_val = htole16(sta_id << 12 | len);
2536286441Srpaulo
2537286441Srpaulo	/* Update TX scheduler. */
2538286441Srpaulo	scd_bc_tbl[qid].tfd_offset[idx] = w_val;
2539286441Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2540286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2541286441Srpaulo
2542286441Srpaulo	/* I really wonder what this is ?!? */
2543286441Srpaulo	if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) {
2544286441Srpaulo		scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val;
2545286441Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2546286441Srpaulo		    BUS_DMASYNC_PREWRITE);
2547286441Srpaulo	}
2548286441Srpaulo}
2549286441Srpaulo#endif
2550286441Srpaulo
2551286441Srpaulo/*
2552286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate
2553286441Srpaulo * table entry.  return the index into in_ridx[].
2554286441Srpaulo *
2555286441Srpaulo * The caller then uses that index back into in_ridx
2556286441Srpaulo * to figure out the rate index programmed /into/
2557286441Srpaulo * the firmware for this given node.
2558286441Srpaulo */
2559286441Srpaulostatic int
2560286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in,
2561286441Srpaulo    uint8_t rate)
2562286441Srpaulo{
2563286441Srpaulo	int i;
2564286441Srpaulo	uint8_t r;
2565286441Srpaulo
2566286441Srpaulo	for (i = 0; i < nitems(in->in_ridx); i++) {
2567286441Srpaulo		r = iwm_rates[in->in_ridx[i]].rate;
2568286441Srpaulo		if (rate == r)
2569286441Srpaulo			return (i);
2570286441Srpaulo	}
2571286441Srpaulo	/* XXX Return the first */
2572286441Srpaulo	/* XXX TODO: have it return the /lowest/ */
2573286441Srpaulo	return (0);
2574286441Srpaulo}
2575286441Srpaulo
2576286441Srpaulo/*
2577298875Sadrian * Fill in the rate related information for a transmit command.
2578286441Srpaulo */
2579286441Srpaulostatic const struct iwm_rate *
2580286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in,
2581286441Srpaulo	struct ieee80211_frame *wh, struct iwm_tx_cmd *tx)
2582286441Srpaulo{
2583287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
2584286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
2585286441Srpaulo	const struct iwm_rate *rinfo;
2586286441Srpaulo	int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
2587286441Srpaulo	int ridx, rate_flags;
2588286441Srpaulo
2589286441Srpaulo	tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT;
2590286441Srpaulo	tx->data_retry_limit = IWM_DEFAULT_TX_RETRY;
2591286441Srpaulo
2592286441Srpaulo	/*
2593286441Srpaulo	 * XXX TODO: everything about the rate selection here is terrible!
2594286441Srpaulo	 */
2595286441Srpaulo
2596286441Srpaulo	if (type == IEEE80211_FC0_TYPE_DATA) {
2597286441Srpaulo		int i;
2598286441Srpaulo		/* for data frames, use RS table */
2599286441Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
2600286441Srpaulo		i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate);
2601286441Srpaulo		ridx = in->in_ridx[i];
2602286441Srpaulo
2603286441Srpaulo		/* This is the index into the programmed table */
2604286441Srpaulo		tx->initial_rate_index = i;
2605286441Srpaulo		tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
2606286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
2607286441Srpaulo		    "%s: start with i=%d, txrate %d\n",
2608286441Srpaulo		    __func__, i, iwm_rates[ridx].rate);
2609298875Sadrian	} else {
2610286441Srpaulo		/*
2611298875Sadrian		 * For non-data, use the lowest supported rate for the given
2612298875Sadrian		 * operational mode.
2613298875Sadrian		 *
2614298875Sadrian		 * Note: there may not be any rate control information available.
2615298875Sadrian		 * This driver currently assumes if we're transmitting data
2616298875Sadrian		 * frames, use the rate control table.  Grr.
2617298875Sadrian		 *
2618298875Sadrian		 * XXX TODO: use the configured rate for the traffic type!
2619298875Sadrian		 * XXX TODO: this should be per-vap, not curmode; as we later
2620298875Sadrian		 * on we'll want to handle off-channel stuff (eg TDLS).
2621286441Srpaulo		 */
2622298875Sadrian		if (ic->ic_curmode == IEEE80211_MODE_11A) {
2623298875Sadrian			/*
2624298875Sadrian			 * XXX this assumes the mode is either 11a or not 11a;
2625298875Sadrian			 * definitely won't work for 11n.
2626298875Sadrian			 */
2627298875Sadrian			ridx = IWM_RIDX_OFDM;
2628298875Sadrian		} else {
2629298875Sadrian			ridx = IWM_RIDX_CCK;
2630298875Sadrian		}
2631286441Srpaulo	}
2632286441Srpaulo
2633286441Srpaulo	rinfo = &iwm_rates[ridx];
2634286441Srpaulo
2635286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n",
2636286441Srpaulo	    __func__, ridx,
2637286441Srpaulo	    rinfo->rate,
2638286441Srpaulo	    !! (IWM_RIDX_IS_CCK(ridx))
2639286441Srpaulo	    );
2640286441Srpaulo
2641286441Srpaulo	/* XXX TODO: hard-coded TX antenna? */
2642286441Srpaulo	rate_flags = 1 << IWM_RATE_MCS_ANT_POS;
2643286441Srpaulo	if (IWM_RIDX_IS_CCK(ridx))
2644286441Srpaulo		rate_flags |= IWM_RATE_MCS_CCK_MSK;
2645286441Srpaulo	tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
2646286441Srpaulo
2647286441Srpaulo	return rinfo;
2648286441Srpaulo}
2649286441Srpaulo
2650286441Srpaulo#define TB0_SIZE 16
2651286441Srpaulostatic int
2652286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac)
2653286441Srpaulo{
2654287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
2655286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2656293099Savos	struct iwm_node *in = IWM_NODE(ni);
2657286441Srpaulo	struct iwm_tx_ring *ring;
2658286441Srpaulo	struct iwm_tx_data *data;
2659286441Srpaulo	struct iwm_tfd *desc;
2660286441Srpaulo	struct iwm_device_cmd *cmd;
2661286441Srpaulo	struct iwm_tx_cmd *tx;
2662286441Srpaulo	struct ieee80211_frame *wh;
2663286441Srpaulo	struct ieee80211_key *k = NULL;
2664286441Srpaulo	struct mbuf *m1;
2665286441Srpaulo	const struct iwm_rate *rinfo;
2666286441Srpaulo	uint32_t flags;
2667286441Srpaulo	u_int hdrlen;
2668286441Srpaulo	bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
2669286441Srpaulo	int nsegs;
2670286441Srpaulo	uint8_t tid, type;
2671286441Srpaulo	int i, totlen, error, pad;
2672286441Srpaulo
2673286441Srpaulo	wh = mtod(m, struct ieee80211_frame *);
2674286441Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
2675286441Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
2676286441Srpaulo	tid = 0;
2677286441Srpaulo	ring = &sc->txq[ac];
2678286441Srpaulo	desc = &ring->desc[ring->cur];
2679286441Srpaulo	memset(desc, 0, sizeof(*desc));
2680286441Srpaulo	data = &ring->data[ring->cur];
2681286441Srpaulo
2682286441Srpaulo	/* Fill out iwm_tx_cmd to send to the firmware */
2683286441Srpaulo	cmd = &ring->cmd[ring->cur];
2684286441Srpaulo	cmd->hdr.code = IWM_TX_CMD;
2685286441Srpaulo	cmd->hdr.flags = 0;
2686286441Srpaulo	cmd->hdr.qid = ring->qid;
2687286441Srpaulo	cmd->hdr.idx = ring->cur;
2688286441Srpaulo
2689286441Srpaulo	tx = (void *)cmd->data;
2690286441Srpaulo	memset(tx, 0, sizeof(*tx));
2691286441Srpaulo
2692286441Srpaulo	rinfo = iwm_tx_fill_cmd(sc, in, wh, tx);
2693286441Srpaulo
2694286441Srpaulo	/* Encrypt the frame if need be. */
2695286441Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
2696286441Srpaulo		/* Retrieve key for TX && do software encryption. */
2697286441Srpaulo		k = ieee80211_crypto_encap(ni, m);
2698286441Srpaulo		if (k == NULL) {
2699286441Srpaulo			m_freem(m);
2700286441Srpaulo			return (ENOBUFS);
2701286441Srpaulo		}
2702286441Srpaulo		/* 802.11 header may have moved. */
2703286441Srpaulo		wh = mtod(m, struct ieee80211_frame *);
2704286441Srpaulo	}
2705286441Srpaulo
2706286441Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
2707286441Srpaulo		struct iwm_tx_radiotap_header *tap = &sc->sc_txtap;
2708286441Srpaulo
2709286441Srpaulo		tap->wt_flags = 0;
2710286441Srpaulo		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
2711286441Srpaulo		tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
2712286441Srpaulo		tap->wt_rate = rinfo->rate;
2713286441Srpaulo		if (k != NULL)
2714286441Srpaulo			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
2715286441Srpaulo		ieee80211_radiotap_tx(vap, m);
2716286441Srpaulo	}
2717286441Srpaulo
2718286441Srpaulo
2719286441Srpaulo	totlen = m->m_pkthdr.len;
2720286441Srpaulo
2721286441Srpaulo	flags = 0;
2722286441Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2723286441Srpaulo		flags |= IWM_TX_CMD_FLG_ACK;
2724286441Srpaulo	}
2725286441Srpaulo
2726286441Srpaulo	if (type != IEEE80211_FC0_TYPE_DATA
2727286441Srpaulo	    && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
2728286441Srpaulo	    && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2729286441Srpaulo		flags |= IWM_TX_CMD_FLG_PROT_REQUIRE;
2730286441Srpaulo	}
2731286441Srpaulo
2732286441Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
2733286441Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
2734286441Srpaulo		tx->sta_id = sc->sc_aux_sta.sta_id;
2735286441Srpaulo	else
2736286441Srpaulo		tx->sta_id = IWM_STATION_ID;
2737286441Srpaulo
2738286441Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
2739286441Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2740286441Srpaulo
2741286441Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
2742286441Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
2743286441Srpaulo			tx->pm_frame_timeout = htole16(3);
2744286441Srpaulo		else
2745286441Srpaulo			tx->pm_frame_timeout = htole16(2);
2746286441Srpaulo	} else {
2747286441Srpaulo		tx->pm_frame_timeout = htole16(0);
2748286441Srpaulo	}
2749286441Srpaulo
2750286441Srpaulo	if (hdrlen & 3) {
2751286441Srpaulo		/* First segment length must be a multiple of 4. */
2752286441Srpaulo		flags |= IWM_TX_CMD_FLG_MH_PAD;
2753286441Srpaulo		pad = 4 - (hdrlen & 3);
2754286441Srpaulo	} else
2755286441Srpaulo		pad = 0;
2756286441Srpaulo
2757286441Srpaulo	tx->driver_txop = 0;
2758286441Srpaulo	tx->next_frame_len = 0;
2759286441Srpaulo
2760286441Srpaulo	tx->len = htole16(totlen);
2761286441Srpaulo	tx->tid_tspec = tid;
2762286441Srpaulo	tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE);
2763286441Srpaulo
2764286441Srpaulo	/* Set physical address of "scratch area". */
2765286441Srpaulo	tx->dram_lsb_ptr = htole32(data->scratch_paddr);
2766286441Srpaulo	tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr);
2767286441Srpaulo
2768286441Srpaulo	/* Copy 802.11 header in TX command. */
2769286441Srpaulo	memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen);
2770286441Srpaulo
2771286441Srpaulo	flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL;
2772286441Srpaulo
2773286441Srpaulo	tx->sec_ctl = 0;
2774286441Srpaulo	tx->tx_flags |= htole32(flags);
2775286441Srpaulo
2776286441Srpaulo	/* Trim 802.11 header. */
2777286441Srpaulo	m_adj(m, hdrlen);
2778286441Srpaulo	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
2779286441Srpaulo	    segs, &nsegs, BUS_DMA_NOWAIT);
2780286441Srpaulo	if (error != 0) {
2781286441Srpaulo		if (error != EFBIG) {
2782286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
2783286441Srpaulo			    error);
2784286441Srpaulo			m_freem(m);
2785286441Srpaulo			return error;
2786286441Srpaulo		}
2787286441Srpaulo		/* Too many DMA segments, linearize mbuf. */
2788293119Savos		m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2);
2789286441Srpaulo		if (m1 == NULL) {
2790293119Savos			device_printf(sc->sc_dev,
2791293119Savos			    "%s: could not defrag mbuf\n", __func__);
2792286441Srpaulo			m_freem(m);
2793293119Savos			return (ENOBUFS);
2794286441Srpaulo		}
2795286441Srpaulo		m = m1;
2796293119Savos
2797286441Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
2798286441Srpaulo		    segs, &nsegs, BUS_DMA_NOWAIT);
2799286441Srpaulo		if (error != 0) {
2800286441Srpaulo			device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
2801286441Srpaulo			    error);
2802286441Srpaulo			m_freem(m);
2803286441Srpaulo			return error;
2804286441Srpaulo		}
2805286441Srpaulo	}
2806286441Srpaulo	data->m = m;
2807286441Srpaulo	data->in = in;
2808286441Srpaulo	data->done = 0;
2809286441Srpaulo
2810286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
2811286441Srpaulo	    "sending txd %p, in %p\n", data, data->in);
2812286441Srpaulo	KASSERT(data->in != NULL, ("node is NULL"));
2813286441Srpaulo
2814286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
2815298611Sadrian	    "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%d\n",
2816298611Sadrian	    ring->qid, ring->cur, totlen, nsegs,
2817298611Sadrian	    le32toh(tx->tx_flags),
2818298611Sadrian	    le32toh(tx->rate_n_flags),
2819298611Sadrian	    (int) tx->initial_rate_index
2820298611Sadrian	    );
2821286441Srpaulo
2822286441Srpaulo	/* Fill TX descriptor. */
2823286441Srpaulo	desc->num_tbs = 2 + nsegs;
2824286441Srpaulo
2825286441Srpaulo	desc->tbs[0].lo = htole32(data->cmd_paddr);
2826286441Srpaulo	desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
2827286441Srpaulo	    (TB0_SIZE << 4);
2828286441Srpaulo	desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE);
2829286441Srpaulo	desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) |
2830286441Srpaulo	    ((sizeof(struct iwm_cmd_header) + sizeof(*tx)
2831286441Srpaulo	      + hdrlen + pad - TB0_SIZE) << 4);
2832286441Srpaulo
2833286441Srpaulo	/* Other DMA segments are for data payload. */
2834286441Srpaulo	for (i = 0; i < nsegs; i++) {
2835286441Srpaulo		seg = &segs[i];
2836286441Srpaulo		desc->tbs[i+2].lo = htole32(seg->ds_addr);
2837286441Srpaulo		desc->tbs[i+2].hi_n_len = \
2838286441Srpaulo		    htole16(iwm_get_dma_hi_addr(seg->ds_addr))
2839286441Srpaulo		    | ((seg->ds_len) << 4);
2840286441Srpaulo	}
2841286441Srpaulo
2842286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map,
2843286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2844286441Srpaulo	bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
2845286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2846286441Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2847286441Srpaulo	    BUS_DMASYNC_PREWRITE);
2848286441Srpaulo
2849286441Srpaulo#if 0
2850286441Srpaulo	iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len));
2851286441Srpaulo#endif
2852286441Srpaulo
2853286441Srpaulo	/* Kick TX ring. */
2854286441Srpaulo	ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT;
2855286441Srpaulo	IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
2856286441Srpaulo
2857286441Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
2858286441Srpaulo	if (++ring->queued > IWM_TX_RING_HIMARK) {
2859286441Srpaulo		sc->qfullmsk |= 1 << ring->qid;
2860286441Srpaulo	}
2861286441Srpaulo
2862286441Srpaulo	return 0;
2863286441Srpaulo}
2864286441Srpaulo
2865286441Srpaulostatic int
2866286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
2867286441Srpaulo    const struct ieee80211_bpf_params *params)
2868286441Srpaulo{
2869286441Srpaulo	struct ieee80211com *ic = ni->ni_ic;
2870286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
2871286441Srpaulo	int error = 0;
2872286441Srpaulo
2873286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
2874286441Srpaulo	    "->%s begin\n", __func__);
2875286441Srpaulo
2876287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
2877286441Srpaulo		m_freem(m);
2878286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_XMIT,
2879286441Srpaulo		    "<-%s not RUNNING\n", __func__);
2880286441Srpaulo		return (ENETDOWN);
2881286441Srpaulo        }
2882286441Srpaulo
2883286441Srpaulo	IWM_LOCK(sc);
2884286441Srpaulo	/* XXX fix this */
2885286441Srpaulo        if (params == NULL) {
2886286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
2887286441Srpaulo	} else {
2888286441Srpaulo		error = iwm_tx(sc, m, ni, 0);
2889286441Srpaulo	}
2890286441Srpaulo	sc->sc_tx_timer = 5;
2891286441Srpaulo	IWM_UNLOCK(sc);
2892286441Srpaulo
2893286441Srpaulo        return (error);
2894286441Srpaulo}
2895286441Srpaulo
2896286441Srpaulo/*
2897286441Srpaulo * mvm/tx.c
2898286441Srpaulo */
2899286441Srpaulo
2900286441Srpaulo#if 0
2901286441Srpaulo/*
2902286441Srpaulo * Note that there are transports that buffer frames before they reach
2903286441Srpaulo * the firmware. This means that after flush_tx_path is called, the
2904286441Srpaulo * queue might not be empty. The race-free way to handle this is to:
2905286441Srpaulo * 1) set the station as draining
2906286441Srpaulo * 2) flush the Tx path
2907286441Srpaulo * 3) wait for the transport queues to be empty
2908286441Srpaulo */
2909286441Srpauloint
2910286441Srpauloiwm_mvm_flush_tx_path(struct iwm_softc *sc, int tfd_msk, int sync)
2911286441Srpaulo{
2912286441Srpaulo	struct iwm_tx_path_flush_cmd flush_cmd = {
2913286441Srpaulo		.queues_ctl = htole32(tfd_msk),
2914286441Srpaulo		.flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH),
2915286441Srpaulo	};
2916286441Srpaulo	int ret;
2917286441Srpaulo
2918286441Srpaulo	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH,
2919286441Srpaulo	    sync ? IWM_CMD_SYNC : IWM_CMD_ASYNC,
2920286441Srpaulo	    sizeof(flush_cmd), &flush_cmd);
2921286441Srpaulo	if (ret)
2922286441Srpaulo                device_printf(sc->sc_dev,
2923286441Srpaulo		    "Flushing tx queue failed: %d\n", ret);
2924286441Srpaulo	return ret;
2925286441Srpaulo}
2926286441Srpaulo#endif
2927286441Srpaulo
2928286441Srpaulo/*
2929286441Srpaulo * BEGIN mvm/sta.c
2930286441Srpaulo */
2931286441Srpaulo
2932286441Srpaulostatic void
2933286441Srpauloiwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6,
2934286441Srpaulo	struct iwm_mvm_add_sta_cmd_v5 *cmd_v5)
2935286441Srpaulo{
2936286441Srpaulo	memset(cmd_v5, 0, sizeof(*cmd_v5));
2937286441Srpaulo
2938286441Srpaulo	cmd_v5->add_modify = cmd_v6->add_modify;
2939286441Srpaulo	cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
2940286441Srpaulo	cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
2941286441Srpaulo	IEEE80211_ADDR_COPY(cmd_v5->addr, cmd_v6->addr);
2942286441Srpaulo	cmd_v5->sta_id = cmd_v6->sta_id;
2943286441Srpaulo	cmd_v5->modify_mask = cmd_v6->modify_mask;
2944286441Srpaulo	cmd_v5->station_flags = cmd_v6->station_flags;
2945286441Srpaulo	cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
2946286441Srpaulo	cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
2947286441Srpaulo	cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
2948286441Srpaulo	cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
2949286441Srpaulo	cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
2950286441Srpaulo	cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
2951286441Srpaulo	cmd_v5->assoc_id = cmd_v6->assoc_id;
2952286441Srpaulo	cmd_v5->beamform_flags = cmd_v6->beamform_flags;
2953286441Srpaulo	cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
2954286441Srpaulo}
2955286441Srpaulo
2956286441Srpaulostatic int
2957286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc,
2958286441Srpaulo	struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status)
2959286441Srpaulo{
2960286441Srpaulo	struct iwm_mvm_add_sta_cmd_v5 cmd_v5;
2961286441Srpaulo
2962286441Srpaulo	if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) {
2963286441Srpaulo		return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
2964286441Srpaulo		    sizeof(*cmd), cmd, status);
2965286441Srpaulo	}
2966286441Srpaulo
2967286441Srpaulo	iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
2968286441Srpaulo
2969286441Srpaulo	return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5),
2970286441Srpaulo	    &cmd_v5, status);
2971286441Srpaulo}
2972286441Srpaulo
2973286441Srpaulo/* send station add/update command to firmware */
2974286441Srpaulostatic int
2975286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update)
2976286441Srpaulo{
2977286441Srpaulo	struct iwm_mvm_add_sta_cmd_v6 add_sta_cmd;
2978286441Srpaulo	int ret;
2979286441Srpaulo	uint32_t status;
2980286441Srpaulo
2981286441Srpaulo	memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
2982286441Srpaulo
2983286441Srpaulo	add_sta_cmd.sta_id = IWM_STATION_ID;
2984286441Srpaulo	add_sta_cmd.mac_id_n_color
2985286441Srpaulo	    = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID,
2986286441Srpaulo	        IWM_DEFAULT_COLOR));
2987286441Srpaulo	if (!update) {
2988286441Srpaulo		add_sta_cmd.tfd_queue_msk = htole32(0xf);
2989286441Srpaulo		IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
2990286441Srpaulo	}
2991286441Srpaulo	add_sta_cmd.add_modify = update ? 1 : 0;
2992286441Srpaulo	add_sta_cmd.station_flags_msk
2993286441Srpaulo	    |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK);
2994286441Srpaulo
2995286441Srpaulo	status = IWM_ADD_STA_SUCCESS;
2996286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status);
2997286441Srpaulo	if (ret)
2998286441Srpaulo		return ret;
2999286441Srpaulo
3000286441Srpaulo	switch (status) {
3001286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3002286441Srpaulo		break;
3003286441Srpaulo	default:
3004286441Srpaulo		ret = EIO;
3005286441Srpaulo		device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
3006286441Srpaulo		break;
3007286441Srpaulo	}
3008286441Srpaulo
3009286441Srpaulo	return ret;
3010286441Srpaulo}
3011286441Srpaulo
3012286441Srpaulostatic int
3013286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
3014286441Srpaulo{
3015286441Srpaulo	int ret;
3016286441Srpaulo
3017286441Srpaulo	ret = iwm_mvm_sta_send_to_fw(sc, in, 0);
3018286441Srpaulo	if (ret)
3019286441Srpaulo		return ret;
3020286441Srpaulo
3021286441Srpaulo	return 0;
3022286441Srpaulo}
3023286441Srpaulo
3024286441Srpaulostatic int
3025286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
3026286441Srpaulo{
3027286441Srpaulo	return iwm_mvm_sta_send_to_fw(sc, in, 1);
3028286441Srpaulo}
3029286441Srpaulo
3030286441Srpaulostatic int
3031286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta,
3032286441Srpaulo	const uint8_t *addr, uint16_t mac_id, uint16_t color)
3033286441Srpaulo{
3034286441Srpaulo	struct iwm_mvm_add_sta_cmd_v6 cmd;
3035286441Srpaulo	int ret;
3036286441Srpaulo	uint32_t status;
3037286441Srpaulo
3038286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3039286441Srpaulo	cmd.sta_id = sta->sta_id;
3040286441Srpaulo	cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color));
3041286441Srpaulo
3042286441Srpaulo	cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk);
3043286441Srpaulo
3044286441Srpaulo	if (addr)
3045286441Srpaulo		IEEE80211_ADDR_COPY(cmd.addr, addr);
3046286441Srpaulo
3047286441Srpaulo	ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status);
3048286441Srpaulo	if (ret)
3049286441Srpaulo		return ret;
3050286441Srpaulo
3051286441Srpaulo	switch (status) {
3052286441Srpaulo	case IWM_ADD_STA_SUCCESS:
3053286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_RESET,
3054286441Srpaulo		    "%s: Internal station added.\n", __func__);
3055286441Srpaulo		return 0;
3056286441Srpaulo	default:
3057286441Srpaulo		device_printf(sc->sc_dev,
3058286441Srpaulo		    "%s: Add internal station failed, status=0x%x\n",
3059286441Srpaulo		    __func__, status);
3060286441Srpaulo		ret = EIO;
3061286441Srpaulo		break;
3062286441Srpaulo	}
3063286441Srpaulo	return ret;
3064286441Srpaulo}
3065286441Srpaulo
3066286441Srpaulostatic int
3067286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc)
3068286441Srpaulo{
3069286441Srpaulo	int ret;
3070286441Srpaulo
3071286441Srpaulo	sc->sc_aux_sta.sta_id = 3;
3072286441Srpaulo	sc->sc_aux_sta.tfd_queue_msk = 0;
3073286441Srpaulo
3074286441Srpaulo	ret = iwm_mvm_add_int_sta_common(sc,
3075286441Srpaulo	    &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0);
3076286441Srpaulo
3077286441Srpaulo	if (ret)
3078286441Srpaulo		memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
3079286441Srpaulo	return ret;
3080286441Srpaulo}
3081286441Srpaulo
3082286441Srpaulo/*
3083286441Srpaulo * END mvm/sta.c
3084286441Srpaulo */
3085286441Srpaulo
3086286441Srpaulo/*
3087286441Srpaulo * BEGIN mvm/quota.c
3088286441Srpaulo */
3089286441Srpaulo
3090286441Srpaulostatic int
3091286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in)
3092286441Srpaulo{
3093286441Srpaulo	struct iwm_time_quota_cmd cmd;
3094286441Srpaulo	int i, idx, ret, num_active_macs, quota, quota_rem;
3095286441Srpaulo	int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, };
3096286441Srpaulo	int n_ifs[IWM_MAX_BINDINGS] = {0, };
3097286441Srpaulo	uint16_t id;
3098286441Srpaulo
3099286441Srpaulo	memset(&cmd, 0, sizeof(cmd));
3100286441Srpaulo
3101286441Srpaulo	/* currently, PHY ID == binding ID */
3102286441Srpaulo	if (in) {
3103286441Srpaulo		id = in->in_phyctxt->id;
3104286441Srpaulo		KASSERT(id < IWM_MAX_BINDINGS, ("invalid id"));
3105286441Srpaulo		colors[id] = in->in_phyctxt->color;
3106286441Srpaulo
3107286441Srpaulo		if (1)
3108286441Srpaulo			n_ifs[id] = 1;
3109286441Srpaulo	}
3110286441Srpaulo
3111286441Srpaulo	/*
3112286441Srpaulo	 * The FW's scheduling session consists of
3113286441Srpaulo	 * IWM_MVM_MAX_QUOTA fragments. Divide these fragments
3114286441Srpaulo	 * equally between all the bindings that require quota
3115286441Srpaulo	 */
3116286441Srpaulo	num_active_macs = 0;
3117286441Srpaulo	for (i = 0; i < IWM_MAX_BINDINGS; i++) {
3118286441Srpaulo		cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID);
3119286441Srpaulo		num_active_macs += n_ifs[i];
3120286441Srpaulo	}
3121286441Srpaulo
3122286441Srpaulo	quota = 0;
3123286441Srpaulo	quota_rem = 0;
3124286441Srpaulo	if (num_active_macs) {
3125286441Srpaulo		quota = IWM_MVM_MAX_QUOTA / num_active_macs;
3126286441Srpaulo		quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs;
3127286441Srpaulo	}
3128286441Srpaulo
3129286441Srpaulo	for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) {
3130286441Srpaulo		if (colors[i] < 0)
3131286441Srpaulo			continue;
3132286441Srpaulo
3133286441Srpaulo		cmd.quotas[idx].id_and_color =
3134286441Srpaulo			htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i]));
3135286441Srpaulo
3136286441Srpaulo		if (n_ifs[i] <= 0) {
3137286441Srpaulo			cmd.quotas[idx].quota = htole32(0);
3138286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3139286441Srpaulo		} else {
3140286441Srpaulo			cmd.quotas[idx].quota = htole32(quota * n_ifs[i]);
3141286441Srpaulo			cmd.quotas[idx].max_duration = htole32(0);
3142286441Srpaulo		}
3143286441Srpaulo		idx++;
3144286441Srpaulo	}
3145286441Srpaulo
3146286441Srpaulo	/* Give the remainder of the session to the first binding */
3147286441Srpaulo	cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem);
3148286441Srpaulo
3149286441Srpaulo	ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC,
3150286441Srpaulo	    sizeof(cmd), &cmd);
3151286441Srpaulo	if (ret)
3152286441Srpaulo		device_printf(sc->sc_dev,
3153286441Srpaulo		    "%s: Failed to send quota: %d\n", __func__, ret);
3154286441Srpaulo	return ret;
3155286441Srpaulo}
3156286441Srpaulo
3157286441Srpaulo/*
3158286441Srpaulo * END mvm/quota.c
3159286441Srpaulo */
3160286441Srpaulo
3161286441Srpaulo/*
3162286441Srpaulo * ieee80211 routines
3163286441Srpaulo */
3164286441Srpaulo
3165286441Srpaulo/*
3166286441Srpaulo * Change to AUTH state in 80211 state machine.  Roughly matches what
3167286441Srpaulo * Linux does in bss_info_changed().
3168286441Srpaulo */
3169286441Srpaulostatic int
3170286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc)
3171286441Srpaulo{
3172286441Srpaulo	struct ieee80211_node *ni;
3173286441Srpaulo	struct iwm_node *in;
3174286441Srpaulo	struct iwm_vap *iv = IWM_VAP(vap);
3175286441Srpaulo	uint32_t duration;
3176286441Srpaulo	int error;
3177286441Srpaulo
3178286441Srpaulo	/*
3179286441Srpaulo	 * XXX i have a feeling that the vap node is being
3180286441Srpaulo	 * freed from underneath us. Grr.
3181286441Srpaulo	 */
3182286441Srpaulo	ni = ieee80211_ref_node(vap->iv_bss);
3183293099Savos	in = IWM_NODE(ni);
3184286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE,
3185286441Srpaulo	    "%s: called; vap=%p, bss ni=%p\n",
3186286441Srpaulo	    __func__,
3187286441Srpaulo	    vap,
3188286441Srpaulo	    ni);
3189286441Srpaulo
3190286441Srpaulo	in->in_assoc = 0;
3191286441Srpaulo
3192286441Srpaulo	error = iwm_allow_mcast(vap, sc);
3193286441Srpaulo	if (error) {
3194286441Srpaulo		device_printf(sc->sc_dev,
3195286441Srpaulo		    "%s: failed to set multicast\n", __func__);
3196286441Srpaulo		goto out;
3197286441Srpaulo	}
3198286441Srpaulo
3199286441Srpaulo	/*
3200286441Srpaulo	 * This is where it deviates from what Linux does.
3201286441Srpaulo	 *
3202286441Srpaulo	 * Linux iwlwifi doesn't reset the nic each time, nor does it
3203286441Srpaulo	 * call ctxt_add() here.  Instead, it adds it during vap creation,
3204286441Srpaulo	 * and always does does a mac_ctx_changed().
3205286441Srpaulo	 *
3206286441Srpaulo	 * The openbsd port doesn't attempt to do that - it reset things
3207286441Srpaulo	 * at odd states and does the add here.
3208286441Srpaulo	 *
3209286441Srpaulo	 * So, until the state handling is fixed (ie, we never reset
3210286441Srpaulo	 * the NIC except for a firmware failure, which should drag
3211286441Srpaulo	 * the NIC back to IDLE, re-setup and re-add all the mac/phy
3212286441Srpaulo	 * contexts that are required), let's do a dirty hack here.
3213286441Srpaulo	 */
3214286441Srpaulo	if (iv->is_uploaded) {
3215286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
3216286441Srpaulo			device_printf(sc->sc_dev,
3217298582Sadrian			    "%s: failed to update MAC\n", __func__);
3218286441Srpaulo			goto out;
3219286441Srpaulo		}
3220298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3221298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3222298582Sadrian			device_printf(sc->sc_dev,
3223298582Sadrian			    "%s: failed update phy ctxt\n", __func__);
3224298582Sadrian			goto out;
3225298582Sadrian		}
3226298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3227298582Sadrian
3228298582Sadrian		if ((error = iwm_mvm_binding_update(sc, in)) != 0) {
3229298582Sadrian			device_printf(sc->sc_dev,
3230298582Sadrian			    "%s: binding update cmd\n", __func__);
3231298582Sadrian			goto out;
3232298582Sadrian		}
3233298582Sadrian		if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
3234298582Sadrian			device_printf(sc->sc_dev,
3235298582Sadrian			    "%s: failed to update sta\n", __func__);
3236298582Sadrian			goto out;
3237298582Sadrian		}
3238286441Srpaulo	} else {
3239286441Srpaulo		if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) {
3240286441Srpaulo			device_printf(sc->sc_dev,
3241286441Srpaulo			    "%s: failed to add MAC\n", __func__);
3242286441Srpaulo			goto out;
3243286441Srpaulo		}
3244298582Sadrian		if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0],
3245298582Sadrian		    in->in_ni.ni_chan, 1, 1)) != 0) {
3246286441Srpaulo			device_printf(sc->sc_dev,
3247298582Sadrian			    "%s: failed add phy ctxt!\n", __func__);
3248286441Srpaulo			error = ETIMEDOUT;
3249286441Srpaulo			goto out;
3250298582Sadrian		}
3251298582Sadrian		in->in_phyctxt = &sc->sc_phyctxt[0];
3252298582Sadrian
3253298582Sadrian		if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) {
3254286441Srpaulo			device_printf(sc->sc_dev,
3255298582Sadrian			    "%s: binding add cmd\n", __func__);
3256286441Srpaulo			goto out;
3257286441Srpaulo		}
3258298582Sadrian		if ((error = iwm_mvm_add_sta(sc, in)) != 0) {
3259298582Sadrian			device_printf(sc->sc_dev,
3260298582Sadrian			    "%s: failed to add sta\n", __func__);
3261298582Sadrian			goto out;
3262298582Sadrian		}
3263286441Srpaulo	}
3264298582Sadrian
3265298582Sadrian	/*
3266298582Sadrian	 * Prevent the FW from wandering off channel during association
3267298582Sadrian	 * by "protecting" the session with a time event.
3268298582Sadrian	 */
3269298582Sadrian	/* XXX duration is in units of TU, not MS */
3270298582Sadrian	duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
3271298582Sadrian	iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */);
3272298582Sadrian	DELAY(100);
3273298582Sadrian
3274286441Srpaulo	error = 0;
3275286441Srpauloout:
3276286441Srpaulo	ieee80211_free_node(ni);
3277286441Srpaulo	return (error);
3278286441Srpaulo}
3279286441Srpaulo
3280286441Srpaulostatic int
3281286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc)
3282286441Srpaulo{
3283293099Savos	struct iwm_node *in = IWM_NODE(vap->iv_bss);
3284286441Srpaulo	int error;
3285286441Srpaulo
3286286441Srpaulo	if ((error = iwm_mvm_update_sta(sc, in)) != 0) {
3287286441Srpaulo		device_printf(sc->sc_dev,
3288286441Srpaulo		    "%s: failed to update STA\n", __func__);
3289286441Srpaulo		return error;
3290286441Srpaulo	}
3291286441Srpaulo
3292286441Srpaulo	in->in_assoc = 1;
3293286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) {
3294286441Srpaulo		device_printf(sc->sc_dev,
3295286441Srpaulo		    "%s: failed to update MAC\n", __func__);
3296286441Srpaulo		return error;
3297286441Srpaulo	}
3298286441Srpaulo
3299286441Srpaulo	return 0;
3300286441Srpaulo}
3301286441Srpaulo
3302286441Srpaulostatic int
3303286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in)
3304286441Srpaulo{
3305286441Srpaulo	/*
3306286441Srpaulo	 * Ok, so *technically* the proper set of calls for going
3307286441Srpaulo	 * from RUN back to SCAN is:
3308286441Srpaulo	 *
3309286441Srpaulo	 * iwm_mvm_power_mac_disable(sc, in);
3310286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
3311286441Srpaulo	 * iwm_mvm_rm_sta(sc, in);
3312286441Srpaulo	 * iwm_mvm_update_quotas(sc, NULL);
3313286441Srpaulo	 * iwm_mvm_mac_ctxt_changed(sc, in);
3314286441Srpaulo	 * iwm_mvm_binding_remove_vif(sc, in);
3315286441Srpaulo	 * iwm_mvm_mac_ctxt_remove(sc, in);
3316286441Srpaulo	 *
3317286441Srpaulo	 * However, that freezes the device not matter which permutations
3318286441Srpaulo	 * and modifications are attempted.  Obviously, this driver is missing
3319286441Srpaulo	 * something since it works in the Linux driver, but figuring out what
3320286441Srpaulo	 * is missing is a little more complicated.  Now, since we're going
3321286441Srpaulo	 * back to nothing anyway, we'll just do a complete device reset.
3322286441Srpaulo	 * Up your's, device!
3323286441Srpaulo	 */
3324286441Srpaulo	//iwm_mvm_flush_tx_path(sc, 0xf, 1);
3325286441Srpaulo	iwm_stop_device(sc);
3326286441Srpaulo	iwm_init_hw(sc);
3327286441Srpaulo	if (in)
3328286441Srpaulo		in->in_assoc = 0;
3329286441Srpaulo	return 0;
3330286441Srpaulo
3331286441Srpaulo#if 0
3332286441Srpaulo	int error;
3333286441Srpaulo
3334286441Srpaulo	iwm_mvm_power_mac_disable(sc, in);
3335286441Srpaulo
3336286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
3337286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error);
3338286441Srpaulo		return error;
3339286441Srpaulo	}
3340286441Srpaulo
3341286441Srpaulo	if ((error = iwm_mvm_rm_sta(sc, in)) != 0) {
3342286441Srpaulo		device_printf(sc->sc_dev, "sta remove fail %d\n", error);
3343286441Srpaulo		return error;
3344286441Srpaulo	}
3345286441Srpaulo	error = iwm_mvm_rm_sta(sc, in);
3346286441Srpaulo	in->in_assoc = 0;
3347286441Srpaulo	iwm_mvm_update_quotas(sc, NULL);
3348286441Srpaulo	if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) {
3349286441Srpaulo		device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error);
3350286441Srpaulo		return error;
3351286441Srpaulo	}
3352286441Srpaulo	iwm_mvm_binding_remove_vif(sc, in);
3353286441Srpaulo
3354286441Srpaulo	iwm_mvm_mac_ctxt_remove(sc, in);
3355286441Srpaulo
3356286441Srpaulo	return error;
3357286441Srpaulo#endif
3358286441Srpaulo}
3359286441Srpaulo
3360286441Srpaulostatic struct ieee80211_node *
3361286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
3362286441Srpaulo{
3363286441Srpaulo	return malloc(sizeof (struct iwm_node), M_80211_NODE,
3364286441Srpaulo	    M_NOWAIT | M_ZERO);
3365286441Srpaulo}
3366286441Srpaulo
3367286441Srpaulostatic void
3368286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
3369286441Srpaulo{
3370286441Srpaulo	struct ieee80211_node *ni = &in->in_ni;
3371286441Srpaulo	struct iwm_lq_cmd *lq = &in->in_lq;
3372286441Srpaulo	int nrates = ni->ni_rates.rs_nrates;
3373286441Srpaulo	int i, ridx, tab = 0;
3374286441Srpaulo	int txant = 0;
3375286441Srpaulo
3376286441Srpaulo	if (nrates > nitems(lq->rs_table)) {
3377286441Srpaulo		device_printf(sc->sc_dev,
3378286441Srpaulo		    "%s: node supports %d rates, driver handles "
3379286441Srpaulo		    "only %zu\n", __func__, nrates, nitems(lq->rs_table));
3380286441Srpaulo		return;
3381286441Srpaulo	}
3382294248Sadrian	if (nrates == 0) {
3383294248Sadrian		device_printf(sc->sc_dev,
3384294248Sadrian		    "%s: node supports 0 rates, odd!\n", __func__);
3385294248Sadrian		return;
3386294248Sadrian	}
3387286441Srpaulo
3388286441Srpaulo	/*
3389286441Srpaulo	 * XXX .. and most of iwm_node is not initialised explicitly;
3390286441Srpaulo	 * it's all just 0x0 passed to the firmware.
3391286441Srpaulo	 */
3392286441Srpaulo
3393286441Srpaulo	/* first figure out which rates we should support */
3394286441Srpaulo	/* XXX TODO: this isn't 11n aware /at all/ */
3395286441Srpaulo	memset(&in->in_ridx, -1, sizeof(in->in_ridx));
3396286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3397286441Srpaulo	    "%s: nrates=%d\n", __func__, nrates);
3398286441Srpaulo
3399294248Sadrian	/*
3400294248Sadrian	 * Loop over nrates and populate in_ridx from the highest
3401294248Sadrian	 * rate to the lowest rate.  Remember, in_ridx[] has
3402294248Sadrian	 * IEEE80211_RATE_MAXSIZE entries!
3403294248Sadrian	 */
3404294248Sadrian	for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) {
3405294248Sadrian		int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL;
3406294248Sadrian
3407286441Srpaulo		/* Map 802.11 rate to HW rate index. */
3408286441Srpaulo		for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
3409286441Srpaulo			if (iwm_rates[ridx].rate == rate)
3410286441Srpaulo				break;
3411286441Srpaulo		if (ridx > IWM_RIDX_MAX) {
3412286441Srpaulo			device_printf(sc->sc_dev,
3413286441Srpaulo			    "%s: WARNING: device rate for %d not found!\n",
3414286441Srpaulo			    __func__, rate);
3415286441Srpaulo		} else {
3416286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3417286441Srpaulo			    "%s: rate: i: %d, rate=%d, ridx=%d\n",
3418286441Srpaulo			    __func__,
3419286441Srpaulo			    i,
3420286441Srpaulo			    rate,
3421286441Srpaulo			    ridx);
3422286441Srpaulo			in->in_ridx[i] = ridx;
3423286441Srpaulo		}
3424286441Srpaulo	}
3425286441Srpaulo
3426286441Srpaulo	/* then construct a lq_cmd based on those */
3427286441Srpaulo	memset(lq, 0, sizeof(*lq));
3428286441Srpaulo	lq->sta_id = IWM_STATION_ID;
3429286441Srpaulo
3430286441Srpaulo	/*
3431286441Srpaulo	 * are these used? (we don't do SISO or MIMO)
3432286441Srpaulo	 * need to set them to non-zero, though, or we get an error.
3433286441Srpaulo	 */
3434286441Srpaulo	lq->single_stream_ant_msk = 1;
3435286441Srpaulo	lq->dual_stream_ant_msk = 1;
3436286441Srpaulo
3437286441Srpaulo	/*
3438286441Srpaulo	 * Build the actual rate selection table.
3439286441Srpaulo	 * The lowest bits are the rates.  Additionally,
3440286441Srpaulo	 * CCK needs bit 9 to be set.  The rest of the bits
3441286441Srpaulo	 * we add to the table select the tx antenna
3442286441Srpaulo	 * Note that we add the rates in the highest rate first
3443286441Srpaulo	 * (opposite of ni_rates).
3444286441Srpaulo	 */
3445286441Srpaulo	/*
3446286441Srpaulo	 * XXX TODO: this should be looping over the min of nrates
3447286441Srpaulo	 * and LQ_MAX_RETRY_NUM.  Sigh.
3448286441Srpaulo	 */
3449286441Srpaulo	for (i = 0; i < nrates; i++) {
3450286441Srpaulo		int nextant;
3451286441Srpaulo
3452286441Srpaulo		if (txant == 0)
3453286441Srpaulo			txant = IWM_FW_VALID_TX_ANT(sc);
3454286441Srpaulo		nextant = 1<<(ffs(txant)-1);
3455286441Srpaulo		txant &= ~nextant;
3456286441Srpaulo
3457286441Srpaulo		/*
3458286441Srpaulo		 * Map the rate id into a rate index into
3459286441Srpaulo		 * our hardware table containing the
3460286441Srpaulo		 * configuration to use for this rate.
3461286441Srpaulo		 */
3462294248Sadrian		ridx = in->in_ridx[i];
3463286441Srpaulo		tab = iwm_rates[ridx].plcp;
3464286441Srpaulo		tab |= nextant << IWM_RATE_MCS_ANT_POS;
3465286441Srpaulo		if (IWM_RIDX_IS_CCK(ridx))
3466286441Srpaulo			tab |= IWM_RATE_MCS_CCK_MSK;
3467286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
3468286441Srpaulo		    "station rate i=%d, rate=%d, hw=%x\n",
3469286441Srpaulo		    i, iwm_rates[ridx].rate, tab);
3470286441Srpaulo		lq->rs_table[i] = htole32(tab);
3471286441Srpaulo	}
3472286441Srpaulo	/* then fill the rest with the lowest possible rate */
3473286441Srpaulo	for (i = nrates; i < nitems(lq->rs_table); i++) {
3474286441Srpaulo		KASSERT(tab != 0, ("invalid tab"));
3475286441Srpaulo		lq->rs_table[i] = htole32(tab);
3476286441Srpaulo	}
3477286441Srpaulo}
3478286441Srpaulo
3479286441Srpaulostatic int
3480286441Srpauloiwm_media_change(struct ifnet *ifp)
3481286441Srpaulo{
3482287197Sglebius	struct ieee80211vap *vap = ifp->if_softc;
3483287197Sglebius	struct ieee80211com *ic = vap->iv_ic;
3484287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
3485286441Srpaulo	int error;
3486286441Srpaulo
3487286441Srpaulo	error = ieee80211_media_change(ifp);
3488286441Srpaulo	if (error != ENETRESET)
3489286441Srpaulo		return error;
3490286441Srpaulo
3491287197Sglebius	IWM_LOCK(sc);
3492287197Sglebius	if (ic->ic_nrunning > 0) {
3493287197Sglebius		iwm_stop(sc);
3494286441Srpaulo		iwm_init(sc);
3495286441Srpaulo	}
3496287197Sglebius	IWM_UNLOCK(sc);
3497286441Srpaulo	return error;
3498286441Srpaulo}
3499286441Srpaulo
3500286441Srpaulo
3501286441Srpaulostatic int
3502286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
3503286441Srpaulo{
3504286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
3505286441Srpaulo	struct ieee80211com *ic = vap->iv_ic;
3506286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
3507286441Srpaulo	struct iwm_node *in;
3508286441Srpaulo	int error;
3509286441Srpaulo
3510286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_STATE,
3511286441Srpaulo	    "switching state %s -> %s\n",
3512286441Srpaulo	    ieee80211_state_name[vap->iv_state],
3513286441Srpaulo	    ieee80211_state_name[nstate]);
3514286441Srpaulo	IEEE80211_UNLOCK(ic);
3515286441Srpaulo	IWM_LOCK(sc);
3516286441Srpaulo	/* disable beacon filtering if we're hopping out of RUN */
3517286441Srpaulo	if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) {
3518286441Srpaulo		iwm_mvm_disable_beacon_filter(sc);
3519286441Srpaulo
3520293099Savos		if (((in = IWM_NODE(vap->iv_bss)) != NULL))
3521286441Srpaulo			in->in_assoc = 0;
3522286441Srpaulo
3523286441Srpaulo		iwm_release(sc, NULL);
3524286441Srpaulo
3525286441Srpaulo		/*
3526286441Srpaulo		 * It's impossible to directly go RUN->SCAN. If we iwm_release()
3527286441Srpaulo		 * above then the card will be completely reinitialized,
3528286441Srpaulo		 * so the driver must do everything necessary to bring the card
3529286441Srpaulo		 * from INIT to SCAN.
3530286441Srpaulo		 *
3531286441Srpaulo		 * Additionally, upon receiving deauth frame from AP,
3532286441Srpaulo		 * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH
3533286441Srpaulo		 * state. This will also fail with this driver, so bring the FSM
3534286441Srpaulo		 * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well.
3535286441Srpaulo		 *
3536286441Srpaulo		 * XXX TODO: fix this for FreeBSD!
3537286441Srpaulo		 */
3538286441Srpaulo		if (nstate == IEEE80211_S_SCAN ||
3539286441Srpaulo		    nstate == IEEE80211_S_AUTH ||
3540286441Srpaulo		    nstate == IEEE80211_S_ASSOC) {
3541286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
3542286441Srpaulo			    "Force transition to INIT; MGT=%d\n", arg);
3543286441Srpaulo			IWM_UNLOCK(sc);
3544286441Srpaulo			IEEE80211_LOCK(ic);
3545286441Srpaulo			vap->iv_newstate(vap, IEEE80211_S_INIT, arg);
3546286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_STATE,
3547286441Srpaulo			    "Going INIT->SCAN\n");
3548286441Srpaulo			nstate = IEEE80211_S_SCAN;
3549286441Srpaulo			IEEE80211_UNLOCK(ic);
3550286441Srpaulo			IWM_LOCK(sc);
3551286441Srpaulo		}
3552286441Srpaulo	}
3553286441Srpaulo
3554286441Srpaulo	switch (nstate) {
3555286441Srpaulo	case IEEE80211_S_INIT:
3556286441Srpaulo		sc->sc_scanband = 0;
3557286441Srpaulo		break;
3558286441Srpaulo
3559286441Srpaulo	case IEEE80211_S_AUTH:
3560286441Srpaulo		if ((error = iwm_auth(vap, sc)) != 0) {
3561286441Srpaulo			device_printf(sc->sc_dev,
3562286441Srpaulo			    "%s: could not move to auth state: %d\n",
3563286441Srpaulo			    __func__, error);
3564286441Srpaulo			break;
3565286441Srpaulo		}
3566286441Srpaulo		break;
3567286441Srpaulo
3568286441Srpaulo	case IEEE80211_S_ASSOC:
3569286441Srpaulo		if ((error = iwm_assoc(vap, sc)) != 0) {
3570286441Srpaulo			device_printf(sc->sc_dev,
3571286441Srpaulo			    "%s: failed to associate: %d\n", __func__,
3572286441Srpaulo			    error);
3573286441Srpaulo			break;
3574286441Srpaulo		}
3575286441Srpaulo		break;
3576286441Srpaulo
3577286441Srpaulo	case IEEE80211_S_RUN:
3578286441Srpaulo	{
3579286441Srpaulo		struct iwm_host_cmd cmd = {
3580286441Srpaulo			.id = IWM_LQ_CMD,
3581286441Srpaulo			.len = { sizeof(in->in_lq), },
3582286441Srpaulo			.flags = IWM_CMD_SYNC,
3583286441Srpaulo		};
3584286441Srpaulo
3585286441Srpaulo		/* Update the association state, now we have it all */
3586286441Srpaulo		/* (eg associd comes in at this point */
3587286441Srpaulo		error = iwm_assoc(vap, sc);
3588286441Srpaulo		if (error != 0) {
3589286441Srpaulo			device_printf(sc->sc_dev,
3590286441Srpaulo			    "%s: failed to update association state: %d\n",
3591286441Srpaulo			    __func__,
3592286441Srpaulo			    error);
3593286441Srpaulo			break;
3594286441Srpaulo		}
3595286441Srpaulo
3596293099Savos		in = IWM_NODE(vap->iv_bss);
3597286441Srpaulo		iwm_mvm_power_mac_update_mode(sc, in);
3598286441Srpaulo		iwm_mvm_enable_beacon_filter(sc, in);
3599286441Srpaulo		iwm_mvm_update_quotas(sc, in);
3600286441Srpaulo		iwm_setrates(sc, in);
3601286441Srpaulo
3602286441Srpaulo		cmd.data[0] = &in->in_lq;
3603286441Srpaulo		if ((error = iwm_send_cmd(sc, &cmd)) != 0) {
3604286441Srpaulo			device_printf(sc->sc_dev,
3605286441Srpaulo			    "%s: IWM_LQ_CMD failed\n", __func__);
3606286441Srpaulo		}
3607286441Srpaulo
3608286441Srpaulo		break;
3609286441Srpaulo	}
3610286441Srpaulo
3611286441Srpaulo	default:
3612286441Srpaulo		break;
3613286441Srpaulo	}
3614286441Srpaulo	IWM_UNLOCK(sc);
3615286441Srpaulo	IEEE80211_LOCK(ic);
3616286441Srpaulo
3617286441Srpaulo	return (ivp->iv_newstate(vap, nstate, arg));
3618286441Srpaulo}
3619286441Srpaulo
3620286441Srpaulovoid
3621286441Srpauloiwm_endscan_cb(void *arg, int pending)
3622286441Srpaulo{
3623286441Srpaulo	struct iwm_softc *sc = arg;
3624287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3625286441Srpaulo	int done;
3626286441Srpaulo	int error;
3627286441Srpaulo
3628286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE,
3629286441Srpaulo	    "%s: scan ended\n",
3630286441Srpaulo	    __func__);
3631286441Srpaulo
3632286441Srpaulo	IWM_LOCK(sc);
3633286441Srpaulo	if (sc->sc_scanband == IEEE80211_CHAN_2GHZ &&
3634286441Srpaulo	    sc->sc_nvm.sku_cap_band_52GHz_enable) {
3635286441Srpaulo		done = 0;
3636286441Srpaulo		if ((error = iwm_mvm_scan_request(sc,
3637286441Srpaulo		    IEEE80211_CHAN_5GHZ, 0, NULL, 0)) != 0) {
3638286441Srpaulo			device_printf(sc->sc_dev, "could not initiate scan\n");
3639286441Srpaulo			done = 1;
3640286441Srpaulo		}
3641286441Srpaulo	} else {
3642286441Srpaulo		done = 1;
3643286441Srpaulo	}
3644286441Srpaulo
3645286441Srpaulo	if (done) {
3646286441Srpaulo		IWM_UNLOCK(sc);
3647286441Srpaulo		ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
3648286441Srpaulo		IWM_LOCK(sc);
3649286441Srpaulo		sc->sc_scanband = 0;
3650286441Srpaulo	}
3651286441Srpaulo	IWM_UNLOCK(sc);
3652286441Srpaulo}
3653286441Srpaulo
3654286441Srpaulostatic int
3655286441Srpauloiwm_init_hw(struct iwm_softc *sc)
3656286441Srpaulo{
3657287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
3658286441Srpaulo	int error, i, qid;
3659286441Srpaulo
3660286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0)
3661286441Srpaulo		return error;
3662286441Srpaulo
3663286441Srpaulo	if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) {
3664286441Srpaulo		return error;
3665286441Srpaulo	}
3666286441Srpaulo
3667286441Srpaulo	/*
3668286441Srpaulo	 * should stop and start HW since that INIT
3669286441Srpaulo	 * image just loaded
3670286441Srpaulo	 */
3671286441Srpaulo	iwm_stop_device(sc);
3672286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
3673286441Srpaulo		device_printf(sc->sc_dev, "could not initialize hardware\n");
3674286441Srpaulo		return error;
3675286441Srpaulo	}
3676286441Srpaulo
3677286441Srpaulo	/* omstart, this time with the regular firmware */
3678286441Srpaulo	error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR);
3679286441Srpaulo	if (error) {
3680286441Srpaulo		device_printf(sc->sc_dev, "could not load firmware\n");
3681286441Srpaulo		goto error;
3682286441Srpaulo	}
3683286441Srpaulo
3684286441Srpaulo	if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0)
3685286441Srpaulo		goto error;
3686286441Srpaulo
3687286441Srpaulo	/* Send phy db control command and then phy db calibration*/
3688286441Srpaulo	if ((error = iwm_send_phy_db_data(sc)) != 0)
3689286441Srpaulo		goto error;
3690286441Srpaulo
3691286441Srpaulo	if ((error = iwm_send_phy_cfg_cmd(sc)) != 0)
3692286441Srpaulo		goto error;
3693286441Srpaulo
3694286441Srpaulo	/* Add auxiliary station for scanning */
3695286441Srpaulo	if ((error = iwm_mvm_add_aux_sta(sc)) != 0)
3696286441Srpaulo		goto error;
3697286441Srpaulo
3698286441Srpaulo	for (i = 0; i < IWM_NUM_PHY_CTX; i++) {
3699286441Srpaulo		/*
3700286441Srpaulo		 * The channel used here isn't relevant as it's
3701286441Srpaulo		 * going to be overwritten in the other flows.
3702286441Srpaulo		 * For now use the first channel we have.
3703286441Srpaulo		 */
3704286441Srpaulo		if ((error = iwm_mvm_phy_ctxt_add(sc,
3705286441Srpaulo		    &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0)
3706286441Srpaulo			goto error;
3707286441Srpaulo	}
3708286441Srpaulo
3709286441Srpaulo	error = iwm_mvm_power_update_device(sc);
3710286441Srpaulo	if (error)
3711286441Srpaulo		goto error;
3712286441Srpaulo
3713286441Srpaulo	/* Mark TX rings as active. */
3714286441Srpaulo	for (qid = 0; qid < 4; qid++) {
3715286441Srpaulo		iwm_enable_txq(sc, qid, qid);
3716286441Srpaulo	}
3717286441Srpaulo
3718286441Srpaulo	return 0;
3719286441Srpaulo
3720286441Srpaulo error:
3721286441Srpaulo	iwm_stop_device(sc);
3722286441Srpaulo	return error;
3723286441Srpaulo}
3724286441Srpaulo
3725286441Srpaulo/* Allow multicast from our BSSID. */
3726286441Srpaulostatic int
3727286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc)
3728286441Srpaulo{
3729286441Srpaulo	struct ieee80211_node *ni = vap->iv_bss;
3730286441Srpaulo	struct iwm_mcast_filter_cmd *cmd;
3731286441Srpaulo	size_t size;
3732286441Srpaulo	int error;
3733286441Srpaulo
3734286441Srpaulo	size = roundup(sizeof(*cmd), 4);
3735286441Srpaulo	cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
3736286441Srpaulo	if (cmd == NULL)
3737286441Srpaulo		return ENOMEM;
3738286441Srpaulo	cmd->filter_own = 1;
3739286441Srpaulo	cmd->port_id = 0;
3740286441Srpaulo	cmd->count = 0;
3741286441Srpaulo	cmd->pass_all = 1;
3742286441Srpaulo	IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid);
3743286441Srpaulo
3744286441Srpaulo	error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD,
3745286441Srpaulo	    IWM_CMD_SYNC, size, cmd);
3746286441Srpaulo	free(cmd, M_DEVBUF);
3747286441Srpaulo
3748286441Srpaulo	return (error);
3749286441Srpaulo}
3750286441Srpaulo
3751286441Srpaulostatic void
3752287197Sglebiusiwm_init(struct iwm_softc *sc)
3753286441Srpaulo{
3754286441Srpaulo	int error;
3755286441Srpaulo
3756286441Srpaulo	if (sc->sc_flags & IWM_FLAG_HW_INITED) {
3757286441Srpaulo		return;
3758286441Srpaulo	}
3759286441Srpaulo	sc->sc_generation++;
3760286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_STOPPED;
3761286441Srpaulo
3762286441Srpaulo	if ((error = iwm_init_hw(sc)) != 0) {
3763287197Sglebius		iwm_stop(sc);
3764286441Srpaulo		return;
3765286441Srpaulo	}
3766286441Srpaulo
3767286441Srpaulo	/*
3768286441Srpaulo 	 * Ok, firmware loaded and we are jogging
3769286441Srpaulo	 */
3770286441Srpaulo	sc->sc_flags |= IWM_FLAG_HW_INITED;
3771286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
3772286441Srpaulo}
3773286441Srpaulo
3774287197Sglebiusstatic int
3775287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m)
3776286441Srpaulo{
3777287197Sglebius	struct iwm_softc *sc;
3778287197Sglebius	int error;
3779286441Srpaulo
3780287197Sglebius	sc = ic->ic_softc;
3781287197Sglebius
3782286441Srpaulo	IWM_LOCK(sc);
3783287197Sglebius	if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) {
3784287197Sglebius		IWM_UNLOCK(sc);
3785287197Sglebius		return (ENXIO);
3786287197Sglebius	}
3787287197Sglebius	error = mbufq_enqueue(&sc->sc_snd, m);
3788287197Sglebius	if (error) {
3789287197Sglebius		IWM_UNLOCK(sc);
3790287197Sglebius		return (error);
3791287197Sglebius	}
3792287197Sglebius	iwm_start(sc);
3793286441Srpaulo	IWM_UNLOCK(sc);
3794287197Sglebius	return (0);
3795286441Srpaulo}
3796286441Srpaulo
3797287197Sglebius/*
3798287197Sglebius * Dequeue packets from sendq and call send.
3799287197Sglebius */
3800286441Srpaulostatic void
3801287197Sglebiusiwm_start(struct iwm_softc *sc)
3802286441Srpaulo{
3803286441Srpaulo	struct ieee80211_node *ni;
3804286441Srpaulo	struct mbuf *m;
3805286441Srpaulo	int ac = 0;
3806286441Srpaulo
3807286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__);
3808287197Sglebius	while (sc->qfullmsk == 0 &&
3809287197Sglebius		(m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
3810286441Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
3811286441Srpaulo		if (iwm_tx(sc, m, ni, ac) != 0) {
3812287197Sglebius			if_inc_counter(ni->ni_vap->iv_ifp,
3813287197Sglebius			    IFCOUNTER_OERRORS, 1);
3814286441Srpaulo			ieee80211_free_node(ni);
3815286441Srpaulo			continue;
3816286441Srpaulo		}
3817287197Sglebius		sc->sc_tx_timer = 15;
3818286441Srpaulo	}
3819286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__);
3820286441Srpaulo}
3821286441Srpaulo
3822286441Srpaulostatic void
3823287197Sglebiusiwm_stop(struct iwm_softc *sc)
3824286441Srpaulo{
3825286441Srpaulo
3826286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_HW_INITED;
3827286441Srpaulo	sc->sc_flags |= IWM_FLAG_STOPPED;
3828286441Srpaulo	sc->sc_generation++;
3829286441Srpaulo	sc->sc_scanband = 0;
3830286441Srpaulo	sc->sc_auth_prot = 0;
3831286441Srpaulo	sc->sc_tx_timer = 0;
3832286441Srpaulo	iwm_stop_device(sc);
3833286441Srpaulo}
3834286441Srpaulo
3835286441Srpaulostatic void
3836286441Srpauloiwm_watchdog(void *arg)
3837286441Srpaulo{
3838286441Srpaulo	struct iwm_softc *sc = arg;
3839286441Srpaulo
3840286441Srpaulo	if (sc->sc_tx_timer > 0) {
3841286441Srpaulo		if (--sc->sc_tx_timer == 0) {
3842286441Srpaulo			device_printf(sc->sc_dev, "device timeout\n");
3843286441Srpaulo#ifdef IWM_DEBUG
3844286441Srpaulo			iwm_nic_error(sc);
3845286441Srpaulo#endif
3846287197Sglebius			iwm_stop(sc);
3847287197Sglebius			counter_u64_add(sc->sc_ic.ic_oerrors, 1);
3848286441Srpaulo			return;
3849286441Srpaulo		}
3850286441Srpaulo	}
3851286441Srpaulo	callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc);
3852286441Srpaulo}
3853286441Srpaulo
3854287197Sglebiusstatic void
3855287197Sglebiusiwm_parent(struct ieee80211com *ic)
3856286441Srpaulo{
3857287197Sglebius	struct iwm_softc *sc = ic->ic_softc;
3858287197Sglebius	int startall = 0;
3859286441Srpaulo
3860287197Sglebius	IWM_LOCK(sc);
3861287197Sglebius	if (ic->ic_nrunning > 0) {
3862287197Sglebius		if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) {
3863287197Sglebius			iwm_init(sc);
3864287197Sglebius			startall = 1;
3865286441Srpaulo		}
3866287197Sglebius	} else if (sc->sc_flags & IWM_FLAG_HW_INITED)
3867287197Sglebius		iwm_stop(sc);
3868287197Sglebius	IWM_UNLOCK(sc);
3869287197Sglebius	if (startall)
3870287197Sglebius		ieee80211_start_all(ic);
3871286441Srpaulo}
3872286441Srpaulo
3873286441Srpaulo/*
3874286441Srpaulo * The interrupt side of things
3875286441Srpaulo */
3876286441Srpaulo
3877286441Srpaulo/*
3878286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c
3879286441Srpaulo */
3880286441Srpaulo
3881286441Srpaulo/*
3882286441Srpaulo * Note: This structure is read from the device with IO accesses,
3883286441Srpaulo * and the reading already does the endian conversion. As it is
3884286441Srpaulo * read with uint32_t-sized accesses, any members with a different size
3885286441Srpaulo * need to be ordered correctly though!
3886286441Srpaulo */
3887286441Srpaulostruct iwm_error_event_table {
3888286441Srpaulo	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
3889286441Srpaulo	uint32_t error_id;		/* type of error */
3890286441Srpaulo	uint32_t pc;			/* program counter */
3891286441Srpaulo	uint32_t blink1;		/* branch link */
3892286441Srpaulo	uint32_t blink2;		/* branch link */
3893286441Srpaulo	uint32_t ilink1;		/* interrupt link */
3894286441Srpaulo	uint32_t ilink2;		/* interrupt link */
3895286441Srpaulo	uint32_t data1;		/* error-specific data */
3896286441Srpaulo	uint32_t data2;		/* error-specific data */
3897286441Srpaulo	uint32_t data3;		/* error-specific data */
3898286441Srpaulo	uint32_t bcon_time;		/* beacon timer */
3899286441Srpaulo	uint32_t tsf_low;		/* network timestamp function timer */
3900286441Srpaulo	uint32_t tsf_hi;		/* network timestamp function timer */
3901286441Srpaulo	uint32_t gp1;		/* GP1 timer register */
3902286441Srpaulo	uint32_t gp2;		/* GP2 timer register */
3903286441Srpaulo	uint32_t gp3;		/* GP3 timer register */
3904286441Srpaulo	uint32_t ucode_ver;		/* uCode version */
3905286441Srpaulo	uint32_t hw_ver;		/* HW Silicon version */
3906286441Srpaulo	uint32_t brd_ver;		/* HW board version */
3907286441Srpaulo	uint32_t log_pc;		/* log program counter */
3908286441Srpaulo	uint32_t frame_ptr;		/* frame pointer */
3909286441Srpaulo	uint32_t stack_ptr;		/* stack pointer */
3910286441Srpaulo	uint32_t hcmd;		/* last host command header */
3911286441Srpaulo	uint32_t isr0;		/* isr status register LMPM_NIC_ISR0:
3912286441Srpaulo				 * rxtx_flag */
3913286441Srpaulo	uint32_t isr1;		/* isr status register LMPM_NIC_ISR1:
3914286441Srpaulo				 * host_flag */
3915286441Srpaulo	uint32_t isr2;		/* isr status register LMPM_NIC_ISR2:
3916286441Srpaulo				 * enc_flag */
3917286441Srpaulo	uint32_t isr3;		/* isr status register LMPM_NIC_ISR3:
3918286441Srpaulo				 * time_flag */
3919286441Srpaulo	uint32_t isr4;		/* isr status register LMPM_NIC_ISR4:
3920286441Srpaulo				 * wico interrupt */
3921286441Srpaulo	uint32_t isr_pref;		/* isr status register LMPM_NIC_PREF_STAT */
3922286441Srpaulo	uint32_t wait_event;		/* wait event() caller address */
3923286441Srpaulo	uint32_t l2p_control;	/* L2pControlField */
3924286441Srpaulo	uint32_t l2p_duration;	/* L2pDurationField */
3925286441Srpaulo	uint32_t l2p_mhvalid;	/* L2pMhValidBits */
3926286441Srpaulo	uint32_t l2p_addr_match;	/* L2pAddrMatchStat */
3927286441Srpaulo	uint32_t lmpm_pmg_sel;	/* indicate which clocks are turned on
3928286441Srpaulo				 * (LMPM_PMG_SEL) */
3929286441Srpaulo	uint32_t u_timestamp;	/* indicate when the date and time of the
3930286441Srpaulo				 * compilation */
3931286441Srpaulo	uint32_t flow_handler;	/* FH read/write pointers, RX credit */
3932286441Srpaulo} __packed;
3933286441Srpaulo
3934286441Srpaulo#define ERROR_START_OFFSET  (1 * sizeof(uint32_t))
3935286441Srpaulo#define ERROR_ELEM_SIZE     (7 * sizeof(uint32_t))
3936286441Srpaulo
3937286441Srpaulo#ifdef IWM_DEBUG
3938286441Srpaulostruct {
3939286441Srpaulo	const char *name;
3940286441Srpaulo	uint8_t num;
3941286441Srpaulo} advanced_lookup[] = {
3942286441Srpaulo	{ "NMI_INTERRUPT_WDG", 0x34 },
3943286441Srpaulo	{ "SYSASSERT", 0x35 },
3944286441Srpaulo	{ "UCODE_VERSION_MISMATCH", 0x37 },
3945286441Srpaulo	{ "BAD_COMMAND", 0x38 },
3946286441Srpaulo	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
3947286441Srpaulo	{ "FATAL_ERROR", 0x3D },
3948286441Srpaulo	{ "NMI_TRM_HW_ERR", 0x46 },
3949286441Srpaulo	{ "NMI_INTERRUPT_TRM", 0x4C },
3950286441Srpaulo	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
3951286441Srpaulo	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
3952286441Srpaulo	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
3953286441Srpaulo	{ "NMI_INTERRUPT_HOST", 0x66 },
3954286441Srpaulo	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
3955286441Srpaulo	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
3956286441Srpaulo	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
3957286441Srpaulo	{ "ADVANCED_SYSASSERT", 0 },
3958286441Srpaulo};
3959286441Srpaulo
3960286441Srpaulostatic const char *
3961286441Srpauloiwm_desc_lookup(uint32_t num)
3962286441Srpaulo{
3963286441Srpaulo	int i;
3964286441Srpaulo
3965286441Srpaulo	for (i = 0; i < nitems(advanced_lookup) - 1; i++)
3966286441Srpaulo		if (advanced_lookup[i].num == num)
3967286441Srpaulo			return advanced_lookup[i].name;
3968286441Srpaulo
3969286441Srpaulo	/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
3970286441Srpaulo	return advanced_lookup[i].name;
3971286441Srpaulo}
3972286441Srpaulo
3973286441Srpaulo/*
3974286441Srpaulo * Support for dumping the error log seemed like a good idea ...
3975286441Srpaulo * but it's mostly hex junk and the only sensible thing is the
3976286441Srpaulo * hw/ucode revision (which we know anyway).  Since it's here,
3977286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to
3978286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later.
3979286441Srpaulo */
3980286441Srpaulostatic void
3981286441Srpauloiwm_nic_error(struct iwm_softc *sc)
3982286441Srpaulo{
3983286441Srpaulo	struct iwm_error_event_table table;
3984286441Srpaulo	uint32_t base;
3985286441Srpaulo
3986286441Srpaulo	device_printf(sc->sc_dev, "dumping device error log\n");
3987286441Srpaulo	base = sc->sc_uc.uc_error_event_table;
3988286441Srpaulo	if (base < 0x800000 || base >= 0x80C000) {
3989286441Srpaulo		device_printf(sc->sc_dev,
3990286441Srpaulo		    "Not valid error log pointer 0x%08x\n", base);
3991286441Srpaulo		return;
3992286441Srpaulo	}
3993286441Srpaulo
3994286441Srpaulo	if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t)) != 0) {
3995286441Srpaulo		device_printf(sc->sc_dev, "reading errlog failed\n");
3996286441Srpaulo		return;
3997286441Srpaulo	}
3998286441Srpaulo
3999286441Srpaulo	if (!table.valid) {
4000286441Srpaulo		device_printf(sc->sc_dev, "errlog not found, skipping\n");
4001286441Srpaulo		return;
4002286441Srpaulo	}
4003286441Srpaulo
4004286441Srpaulo	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
4005286441Srpaulo		device_printf(sc->sc_dev, "Start IWL Error Log Dump:\n");
4006286441Srpaulo		device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n",
4007286441Srpaulo		    sc->sc_flags, table.valid);
4008286441Srpaulo	}
4009286441Srpaulo
4010286441Srpaulo	device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id,
4011286441Srpaulo		iwm_desc_lookup(table.error_id));
4012286441Srpaulo	device_printf(sc->sc_dev, "%08X | uPc\n", table.pc);
4013286441Srpaulo	device_printf(sc->sc_dev, "%08X | branchlink1\n", table.blink1);
4014286441Srpaulo	device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2);
4015286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1);
4016286441Srpaulo	device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2);
4017286441Srpaulo	device_printf(sc->sc_dev, "%08X | data1\n", table.data1);
4018286441Srpaulo	device_printf(sc->sc_dev, "%08X | data2\n", table.data2);
4019286441Srpaulo	device_printf(sc->sc_dev, "%08X | data3\n", table.data3);
4020286441Srpaulo	device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time);
4021286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low);
4022286441Srpaulo	device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi);
4023286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1);
4024286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2);
4025286441Srpaulo	device_printf(sc->sc_dev, "%08X | time gp3\n", table.gp3);
4026286441Srpaulo	device_printf(sc->sc_dev, "%08X | uCode version\n", table.ucode_ver);
4027286441Srpaulo	device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver);
4028286441Srpaulo	device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver);
4029286441Srpaulo	device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd);
4030286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0);
4031286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1);
4032286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2);
4033286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3);
4034286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4);
4035286441Srpaulo	device_printf(sc->sc_dev, "%08X | isr_pref\n", table.isr_pref);
4036286441Srpaulo	device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event);
4037286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control);
4038286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration);
4039286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid);
4040286441Srpaulo	device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match);
4041286441Srpaulo	device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
4042286441Srpaulo	device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp);
4043286441Srpaulo	device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler);
4044286441Srpaulo}
4045286441Srpaulo#endif
4046286441Srpaulo
4047286441Srpaulo#define SYNC_RESP_STRUCT(_var_, _pkt_)					\
4048286441Srpaulodo {									\
4049286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\
4050286441Srpaulo	_var_ = (void *)((_pkt_)+1);					\
4051286441Srpaulo} while (/*CONSTCOND*/0)
4052286441Srpaulo
4053286441Srpaulo#define SYNC_RESP_PTR(_ptr_, _len_, _pkt_)				\
4054286441Srpaulodo {									\
4055286441Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\
4056286441Srpaulo	_ptr_ = (void *)((_pkt_)+1);					\
4057286441Srpaulo} while (/*CONSTCOND*/0)
4058286441Srpaulo
4059286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
4060286441Srpaulo
4061286441Srpaulo/*
4062286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt.
4063286441Srpaulo * Basic structure from if_iwn
4064286441Srpaulo */
4065286441Srpaulostatic void
4066286441Srpauloiwm_notif_intr(struct iwm_softc *sc)
4067286441Srpaulo{
4068286441Srpaulo	uint16_t hw;
4069286441Srpaulo
4070286441Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
4071286441Srpaulo	    BUS_DMASYNC_POSTREAD);
4072286441Srpaulo
4073286441Srpaulo	hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
4074286441Srpaulo	while (sc->rxq.cur != hw) {
4075286441Srpaulo		struct iwm_rx_ring *ring = &sc->rxq;
4076286441Srpaulo		struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
4077286441Srpaulo		struct iwm_rx_packet *pkt;
4078286441Srpaulo		struct iwm_cmd_response *cresp;
4079286441Srpaulo		int qid, idx;
4080286441Srpaulo
4081286441Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
4082286441Srpaulo		    BUS_DMASYNC_POSTREAD);
4083286441Srpaulo		pkt = mtod(data->m, struct iwm_rx_packet *);
4084286441Srpaulo
4085286441Srpaulo		qid = pkt->hdr.qid & ~0x80;
4086286441Srpaulo		idx = pkt->hdr.idx;
4087286441Srpaulo
4088286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
4089286441Srpaulo		    "rx packet qid=%d idx=%d flags=%x type=%x %d %d\n",
4090286441Srpaulo		    pkt->hdr.qid & ~0x80, pkt->hdr.idx, pkt->hdr.flags,
4091286441Srpaulo		    pkt->hdr.code, sc->rxq.cur, hw);
4092286441Srpaulo
4093286441Srpaulo		/*
4094286441Srpaulo		 * randomly get these from the firmware, no idea why.
4095286441Srpaulo		 * they at least seem harmless, so just ignore them for now
4096286441Srpaulo		 */
4097286441Srpaulo		if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
4098286441Srpaulo		    || pkt->len_n_flags == htole32(0x55550000))) {
4099286441Srpaulo			ADVANCE_RXQ(sc);
4100286441Srpaulo			continue;
4101286441Srpaulo		}
4102286441Srpaulo
4103286441Srpaulo		switch (pkt->hdr.code) {
4104286441Srpaulo		case IWM_REPLY_RX_PHY_CMD:
4105286441Srpaulo			iwm_mvm_rx_rx_phy_cmd(sc, pkt, data);
4106286441Srpaulo			break;
4107286441Srpaulo
4108286441Srpaulo		case IWM_REPLY_RX_MPDU_CMD:
4109286441Srpaulo			iwm_mvm_rx_rx_mpdu(sc, pkt, data);
4110286441Srpaulo			break;
4111286441Srpaulo
4112286441Srpaulo		case IWM_TX_CMD:
4113286441Srpaulo			iwm_mvm_rx_tx_cmd(sc, pkt, data);
4114286441Srpaulo			break;
4115286441Srpaulo
4116286441Srpaulo		case IWM_MISSED_BEACONS_NOTIFICATION: {
4117286441Srpaulo			struct iwm_missed_beacons_notif *resp;
4118286441Srpaulo			int missed;
4119286441Srpaulo
4120286441Srpaulo			/* XXX look at mac_id to determine interface ID */
4121287197Sglebius			struct ieee80211com *ic = &sc->sc_ic;
4122286441Srpaulo			struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4123286441Srpaulo
4124286441Srpaulo			SYNC_RESP_STRUCT(resp, pkt);
4125286441Srpaulo			missed = le32toh(resp->consec_missed_beacons);
4126286441Srpaulo
4127286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE,
4128286441Srpaulo			    "%s: MISSED_BEACON: mac_id=%d, "
4129286441Srpaulo			    "consec_since_last_rx=%d, consec=%d, num_expect=%d "
4130286441Srpaulo			    "num_rx=%d\n",
4131286441Srpaulo			    __func__,
4132286441Srpaulo			    le32toh(resp->mac_id),
4133286441Srpaulo			    le32toh(resp->consec_missed_beacons_since_last_rx),
4134286441Srpaulo			    le32toh(resp->consec_missed_beacons),
4135286441Srpaulo			    le32toh(resp->num_expected_beacons),
4136286441Srpaulo			    le32toh(resp->num_recvd_beacons));
4137286441Srpaulo
4138286441Srpaulo			/* Be paranoid */
4139286441Srpaulo			if (vap == NULL)
4140286441Srpaulo				break;
4141286441Srpaulo
4142286441Srpaulo			/* XXX no net80211 locking? */
4143286441Srpaulo			if (vap->iv_state == IEEE80211_S_RUN &&
4144286441Srpaulo			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
4145286441Srpaulo				if (missed > vap->iv_bmissthreshold) {
4146286441Srpaulo					/* XXX bad locking; turn into task */
4147286441Srpaulo					IWM_UNLOCK(sc);
4148286441Srpaulo					ieee80211_beacon_miss(ic);
4149286441Srpaulo					IWM_LOCK(sc);
4150286441Srpaulo				}
4151286441Srpaulo			}
4152286441Srpaulo
4153286441Srpaulo			break; }
4154286441Srpaulo
4155286441Srpaulo		case IWM_MVM_ALIVE: {
4156286441Srpaulo			struct iwm_mvm_alive_resp *resp;
4157286441Srpaulo			SYNC_RESP_STRUCT(resp, pkt);
4158286441Srpaulo
4159286441Srpaulo			sc->sc_uc.uc_error_event_table
4160286441Srpaulo			    = le32toh(resp->error_event_table_ptr);
4161286441Srpaulo			sc->sc_uc.uc_log_event_table
4162286441Srpaulo			    = le32toh(resp->log_event_table_ptr);
4163286441Srpaulo			sc->sched_base = le32toh(resp->scd_base_ptr);
4164286441Srpaulo			sc->sc_uc.uc_ok = resp->status == IWM_ALIVE_STATUS_OK;
4165286441Srpaulo
4166286441Srpaulo			sc->sc_uc.uc_intr = 1;
4167286441Srpaulo			wakeup(&sc->sc_uc);
4168286441Srpaulo			break; }
4169286441Srpaulo
4170286441Srpaulo		case IWM_CALIB_RES_NOTIF_PHY_DB: {
4171286441Srpaulo			struct iwm_calib_res_notif_phy_db *phy_db_notif;
4172286441Srpaulo			SYNC_RESP_STRUCT(phy_db_notif, pkt);
4173286441Srpaulo
4174286441Srpaulo			iwm_phy_db_set_section(sc, phy_db_notif);
4175286441Srpaulo
4176286441Srpaulo			break; }
4177286441Srpaulo
4178286441Srpaulo		case IWM_STATISTICS_NOTIFICATION: {
4179286441Srpaulo			struct iwm_notif_statistics *stats;
4180286441Srpaulo			SYNC_RESP_STRUCT(stats, pkt);
4181286441Srpaulo			memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
4182286441Srpaulo			sc->sc_noise = iwm_get_noise(&stats->rx.general);
4183286441Srpaulo			break; }
4184286441Srpaulo
4185286441Srpaulo		case IWM_NVM_ACCESS_CMD:
4186286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
4187286441Srpaulo				bus_dmamap_sync(sc->rxq.data_dmat, data->map,
4188286441Srpaulo				    BUS_DMASYNC_POSTREAD);
4189286441Srpaulo				memcpy(sc->sc_cmd_resp,
4190286441Srpaulo				    pkt, sizeof(sc->sc_cmd_resp));
4191286441Srpaulo			}
4192286441Srpaulo			break;
4193286441Srpaulo
4194286441Srpaulo		case IWM_PHY_CONFIGURATION_CMD:
4195286441Srpaulo		case IWM_TX_ANT_CONFIGURATION_CMD:
4196286441Srpaulo		case IWM_ADD_STA:
4197286441Srpaulo		case IWM_MAC_CONTEXT_CMD:
4198286441Srpaulo		case IWM_REPLY_SF_CFG_CMD:
4199286441Srpaulo		case IWM_POWER_TABLE_CMD:
4200286441Srpaulo		case IWM_PHY_CONTEXT_CMD:
4201286441Srpaulo		case IWM_BINDING_CONTEXT_CMD:
4202286441Srpaulo		case IWM_TIME_EVENT_CMD:
4203286441Srpaulo		case IWM_SCAN_REQUEST_CMD:
4204286441Srpaulo		case IWM_REPLY_BEACON_FILTERING_CMD:
4205286441Srpaulo		case IWM_MAC_PM_POWER_TABLE:
4206286441Srpaulo		case IWM_TIME_QUOTA_CMD:
4207286441Srpaulo		case IWM_REMOVE_STA:
4208286441Srpaulo		case IWM_TXPATH_FLUSH:
4209286441Srpaulo		case IWM_LQ_CMD:
4210286441Srpaulo			SYNC_RESP_STRUCT(cresp, pkt);
4211286441Srpaulo			if (sc->sc_wantresp == ((qid << 16) | idx)) {
4212286441Srpaulo				memcpy(sc->sc_cmd_resp,
4213286441Srpaulo				    pkt, sizeof(*pkt)+sizeof(*cresp));
4214286441Srpaulo			}
4215286441Srpaulo			break;
4216286441Srpaulo
4217286441Srpaulo		/* ignore */
4218286441Srpaulo		case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */
4219286441Srpaulo			break;
4220286441Srpaulo
4221286441Srpaulo		case IWM_INIT_COMPLETE_NOTIF:
4222286441Srpaulo			sc->sc_init_complete = 1;
4223286441Srpaulo			wakeup(&sc->sc_init_complete);
4224286441Srpaulo			break;
4225286441Srpaulo
4226286441Srpaulo		case IWM_SCAN_COMPLETE_NOTIFICATION: {
4227286441Srpaulo			struct iwm_scan_complete_notif *notif;
4228286441Srpaulo			SYNC_RESP_STRUCT(notif, pkt);
4229286441Srpaulo			taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task);
4230286441Srpaulo			break; }
4231286441Srpaulo
4232286441Srpaulo		case IWM_REPLY_ERROR: {
4233286441Srpaulo			struct iwm_error_resp *resp;
4234286441Srpaulo			SYNC_RESP_STRUCT(resp, pkt);
4235286441Srpaulo
4236286441Srpaulo			device_printf(sc->sc_dev,
4237286441Srpaulo			    "firmware error 0x%x, cmd 0x%x\n",
4238286441Srpaulo			    le32toh(resp->error_type),
4239286441Srpaulo			    resp->cmd_id);
4240286441Srpaulo			break; }
4241286441Srpaulo
4242286441Srpaulo		case IWM_TIME_EVENT_NOTIFICATION: {
4243286441Srpaulo			struct iwm_time_event_notif *notif;
4244286441Srpaulo			SYNC_RESP_STRUCT(notif, pkt);
4245286441Srpaulo
4246286441Srpaulo			if (notif->status) {
4247286441Srpaulo				if (le32toh(notif->action) &
4248286441Srpaulo				    IWM_TE_V2_NOTIF_HOST_EVENT_START)
4249286441Srpaulo					sc->sc_auth_prot = 2;
4250286441Srpaulo				else
4251286441Srpaulo					sc->sc_auth_prot = 0;
4252286441Srpaulo			} else {
4253286441Srpaulo				sc->sc_auth_prot = -1;
4254286441Srpaulo			}
4255286441Srpaulo			IWM_DPRINTF(sc, IWM_DEBUG_INTR,
4256286441Srpaulo			    "%s: time event notification auth_prot=%d\n",
4257286441Srpaulo				__func__, sc->sc_auth_prot);
4258286441Srpaulo
4259286441Srpaulo			wakeup(&sc->sc_auth_prot);
4260286441Srpaulo			break; }
4261286441Srpaulo
4262286441Srpaulo		case IWM_MCAST_FILTER_CMD:
4263286441Srpaulo			break;
4264286441Srpaulo
4265286441Srpaulo		default:
4266286441Srpaulo			device_printf(sc->sc_dev,
4267286441Srpaulo			    "frame %d/%d %x UNHANDLED (this should "
4268286441Srpaulo			    "not happen)\n", qid, idx,
4269286441Srpaulo			    pkt->len_n_flags);
4270286441Srpaulo			break;
4271286441Srpaulo		}
4272286441Srpaulo
4273286441Srpaulo		/*
4274286441Srpaulo		 * Why test bit 0x80?  The Linux driver:
4275286441Srpaulo		 *
4276286441Srpaulo		 * There is one exception:  uCode sets bit 15 when it
4277286441Srpaulo		 * originates the response/notification, i.e. when the
4278286441Srpaulo		 * response/notification is not a direct response to a
4279286441Srpaulo		 * command sent by the driver.  For example, uCode issues
4280286441Srpaulo		 * IWM_REPLY_RX when it sends a received frame to the driver;
4281286441Srpaulo		 * it is not a direct response to any driver command.
4282286441Srpaulo		 *
4283286441Srpaulo		 * Ok, so since when is 7 == 15?  Well, the Linux driver
4284286441Srpaulo		 * uses a slightly different format for pkt->hdr, and "qid"
4285286441Srpaulo		 * is actually the upper byte of a two-byte field.
4286286441Srpaulo		 */
4287286441Srpaulo		if (!(pkt->hdr.qid & (1 << 7))) {
4288286441Srpaulo			iwm_cmd_done(sc, pkt);
4289286441Srpaulo		}
4290286441Srpaulo
4291286441Srpaulo		ADVANCE_RXQ(sc);
4292286441Srpaulo	}
4293286441Srpaulo
4294286441Srpaulo	IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
4295286441Srpaulo	    IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
4296286441Srpaulo
4297286441Srpaulo	/*
4298286441Srpaulo	 * Tell the firmware what we have processed.
4299286441Srpaulo	 * Seems like the hardware gets upset unless we align
4300286441Srpaulo	 * the write by 8??
4301286441Srpaulo	 */
4302286441Srpaulo	hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1;
4303286441Srpaulo	IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7);
4304286441Srpaulo}
4305286441Srpaulo
4306286441Srpaulostatic void
4307286441Srpauloiwm_intr(void *arg)
4308286441Srpaulo{
4309286441Srpaulo	struct iwm_softc *sc = arg;
4310286441Srpaulo	int handled = 0;
4311286441Srpaulo	int r1, r2, rv = 0;
4312286441Srpaulo	int isperiodic = 0;
4313286441Srpaulo
4314286441Srpaulo	IWM_LOCK(sc);
4315286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
4316286441Srpaulo
4317286441Srpaulo	if (sc->sc_flags & IWM_FLAG_USE_ICT) {
4318286441Srpaulo		uint32_t *ict = sc->ict_dma.vaddr;
4319286441Srpaulo		int tmp;
4320286441Srpaulo
4321286441Srpaulo		tmp = htole32(ict[sc->ict_cur]);
4322286441Srpaulo		if (!tmp)
4323286441Srpaulo			goto out_ena;
4324286441Srpaulo
4325286441Srpaulo		/*
4326286441Srpaulo		 * ok, there was something.  keep plowing until we have all.
4327286441Srpaulo		 */
4328286441Srpaulo		r1 = r2 = 0;
4329286441Srpaulo		while (tmp) {
4330286441Srpaulo			r1 |= tmp;
4331286441Srpaulo			ict[sc->ict_cur] = 0;
4332286441Srpaulo			sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT;
4333286441Srpaulo			tmp = htole32(ict[sc->ict_cur]);
4334286441Srpaulo		}
4335286441Srpaulo
4336286441Srpaulo		/* this is where the fun begins.  don't ask */
4337286441Srpaulo		if (r1 == 0xffffffff)
4338286441Srpaulo			r1 = 0;
4339286441Srpaulo
4340286441Srpaulo		/* i am not expected to understand this */
4341286441Srpaulo		if (r1 & 0xc0000)
4342286441Srpaulo			r1 |= 0x8000;
4343286441Srpaulo		r1 = (0xff & r1) | ((0xff00 & r1) << 16);
4344286441Srpaulo	} else {
4345286441Srpaulo		r1 = IWM_READ(sc, IWM_CSR_INT);
4346286441Srpaulo		/* "hardware gone" (where, fishing?) */
4347286441Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
4348286441Srpaulo			goto out;
4349286441Srpaulo		r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS);
4350286441Srpaulo	}
4351286441Srpaulo	if (r1 == 0 && r2 == 0) {
4352286441Srpaulo		goto out_ena;
4353286441Srpaulo	}
4354286441Srpaulo
4355286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask);
4356286441Srpaulo
4357286441Srpaulo	/* ignored */
4358286441Srpaulo	handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/));
4359286441Srpaulo
4360286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_SW_ERR) {
4361286441Srpaulo		int i;
4362287197Sglebius		struct ieee80211com *ic = &sc->sc_ic;
4363286441Srpaulo		struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4364286441Srpaulo
4365298793Sdchagin#ifdef IWM_DEBUG
4366286441Srpaulo		iwm_nic_error(sc);
4367298793Sdchagin#endif
4368286441Srpaulo		/* Dump driver status (TX and RX rings) while we're here. */
4369286441Srpaulo		device_printf(sc->sc_dev, "driver status:\n");
4370286441Srpaulo		for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) {
4371286441Srpaulo			struct iwm_tx_ring *ring = &sc->txq[i];
4372286441Srpaulo			device_printf(sc->sc_dev,
4373286441Srpaulo			    "  tx ring %2d: qid=%-2d cur=%-3d "
4374286441Srpaulo			    "queued=%-3d\n",
4375286441Srpaulo			    i, ring->qid, ring->cur, ring->queued);
4376286441Srpaulo		}
4377286441Srpaulo		device_printf(sc->sc_dev,
4378286441Srpaulo		    "  rx ring: cur=%d\n", sc->rxq.cur);
4379286441Srpaulo		device_printf(sc->sc_dev,
4380298659Scem		    "  802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state);
4381286441Srpaulo
4382298594Sadrian		/* Don't stop the device; just do a VAP restart */
4383298594Sadrian		IWM_UNLOCK(sc);
4384286441Srpaulo
4385298594Sadrian		if (vap == NULL) {
4386298594Sadrian			printf("%s: null vap\n", __func__);
4387298594Sadrian			return;
4388298594Sadrian		}
4389298594Sadrian
4390298594Sadrian		device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; "
4391298594Sadrian		    "restarting\n", __func__, vap->iv_state);
4392298594Sadrian
4393298594Sadrian		/* XXX TODO: turn this into a callout/taskqueue */
4394298594Sadrian		ieee80211_restart_all(ic);
4395298594Sadrian		return;
4396286441Srpaulo	}
4397286441Srpaulo
4398286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_HW_ERR) {
4399286441Srpaulo		handled |= IWM_CSR_INT_BIT_HW_ERR;
4400286441Srpaulo		device_printf(sc->sc_dev, "hardware error, stopping device\n");
4401287197Sglebius		iwm_stop(sc);
4402286441Srpaulo		rv = 1;
4403286441Srpaulo		goto out;
4404286441Srpaulo	}
4405286441Srpaulo
4406286441Srpaulo	/* firmware chunk loaded */
4407286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_FH_TX) {
4408286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK);
4409286441Srpaulo		handled |= IWM_CSR_INT_BIT_FH_TX;
4410286441Srpaulo		sc->sc_fw_chunk_done = 1;
4411286441Srpaulo		wakeup(&sc->sc_fw);
4412286441Srpaulo	}
4413286441Srpaulo
4414286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RF_KILL) {
4415286441Srpaulo		handled |= IWM_CSR_INT_BIT_RF_KILL;
4416287197Sglebius		if (iwm_check_rfkill(sc)) {
4417286441Srpaulo			device_printf(sc->sc_dev,
4418286441Srpaulo			    "%s: rfkill switch, disabling interface\n",
4419286441Srpaulo			    __func__);
4420287197Sglebius			iwm_stop(sc);
4421286441Srpaulo		}
4422286441Srpaulo	}
4423286441Srpaulo
4424286441Srpaulo	/*
4425286441Srpaulo	 * The Linux driver uses periodic interrupts to avoid races.
4426286441Srpaulo	 * We cargo-cult like it's going out of fashion.
4427286441Srpaulo	 */
4428286441Srpaulo	if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
4429286441Srpaulo		handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
4430286441Srpaulo		IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
4431286441Srpaulo		if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0)
4432286441Srpaulo			IWM_WRITE_1(sc,
4433286441Srpaulo			    IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
4434286441Srpaulo		isperiodic = 1;
4435286441Srpaulo	}
4436286441Srpaulo
4437286441Srpaulo	if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) {
4438286441Srpaulo		handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
4439286441Srpaulo		IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
4440286441Srpaulo
4441286441Srpaulo		iwm_notif_intr(sc);
4442286441Srpaulo
4443286441Srpaulo		/* enable periodic interrupt, see above */
4444286441Srpaulo		if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic)
4445286441Srpaulo			IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG,
4446286441Srpaulo			    IWM_CSR_INT_PERIODIC_ENA);
4447286441Srpaulo	}
4448286441Srpaulo
4449286441Srpaulo	if (__predict_false(r1 & ~handled))
4450286441Srpaulo		IWM_DPRINTF(sc, IWM_DEBUG_INTR,
4451286441Srpaulo		    "%s: unhandled interrupts: %x\n", __func__, r1);
4452286441Srpaulo	rv = 1;
4453286441Srpaulo
4454286441Srpaulo out_ena:
4455286441Srpaulo	iwm_restore_interrupts(sc);
4456286441Srpaulo out:
4457286441Srpaulo	IWM_UNLOCK(sc);
4458286441Srpaulo	return;
4459286441Srpaulo}
4460286441Srpaulo
4461286441Srpaulo/*
4462286441Srpaulo * Autoconf glue-sniffing
4463286441Srpaulo */
4464286441Srpaulo#define	PCI_VENDOR_INTEL		0x8086
4465286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_1	0x08b3
4466286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_3160_2	0x08b4
4467286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_1	0x08b1
4468286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7260_2	0x08b2
4469286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_1	0x095a
4470286441Srpaulo#define	PCI_PRODUCT_INTEL_WL_7265_2	0x095b
4471286441Srpaulo
4472286441Srpaulostatic const struct iwm_devices {
4473286441Srpaulo	uint16_t	device;
4474286441Srpaulo	const char	*name;
4475286441Srpaulo} iwm_devices[] = {
4476286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" },
4477286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" },
4478286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" },
4479286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" },
4480286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" },
4481286441Srpaulo	{ PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" },
4482286441Srpaulo};
4483286441Srpaulo
4484286441Srpaulostatic int
4485286441Srpauloiwm_probe(device_t dev)
4486286441Srpaulo{
4487286441Srpaulo	int i;
4488286441Srpaulo
4489286441Srpaulo	for (i = 0; i < nitems(iwm_devices); i++)
4490286441Srpaulo		if (pci_get_vendor(dev) == PCI_VENDOR_INTEL &&
4491286441Srpaulo		    pci_get_device(dev) == iwm_devices[i].device) {
4492286441Srpaulo			device_set_desc(dev, iwm_devices[i].name);
4493286441Srpaulo			return (BUS_PROBE_DEFAULT);
4494286441Srpaulo		}
4495286441Srpaulo
4496286441Srpaulo	return (ENXIO);
4497286441Srpaulo}
4498286441Srpaulo
4499286441Srpaulostatic int
4500286441Srpauloiwm_dev_check(device_t dev)
4501286441Srpaulo{
4502286441Srpaulo	struct iwm_softc *sc;
4503286441Srpaulo
4504286441Srpaulo	sc = device_get_softc(dev);
4505286441Srpaulo
4506286441Srpaulo	switch (pci_get_device(dev)) {
4507286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_1:
4508286441Srpaulo	case PCI_PRODUCT_INTEL_WL_3160_2:
4509286441Srpaulo		sc->sc_fwname = "iwm3160fw";
4510286441Srpaulo		sc->host_interrupt_operation_mode = 1;
4511286441Srpaulo		return (0);
4512286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_1:
4513286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7260_2:
4514286441Srpaulo		sc->sc_fwname = "iwm7260fw";
4515286441Srpaulo		sc->host_interrupt_operation_mode = 1;
4516286441Srpaulo		return (0);
4517286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_1:
4518286441Srpaulo	case PCI_PRODUCT_INTEL_WL_7265_2:
4519286441Srpaulo		sc->sc_fwname = "iwm7265fw";
4520286441Srpaulo		sc->host_interrupt_operation_mode = 0;
4521286441Srpaulo		return (0);
4522286441Srpaulo	default:
4523286441Srpaulo		device_printf(dev, "unknown adapter type\n");
4524286441Srpaulo		return ENXIO;
4525286441Srpaulo	}
4526286441Srpaulo}
4527286441Srpaulo
4528286441Srpaulostatic int
4529286441Srpauloiwm_pci_attach(device_t dev)
4530286441Srpaulo{
4531286441Srpaulo	struct iwm_softc *sc;
4532286441Srpaulo	int count, error, rid;
4533286441Srpaulo	uint16_t reg;
4534286441Srpaulo
4535286441Srpaulo	sc = device_get_softc(dev);
4536286441Srpaulo
4537286441Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
4538286441Srpaulo	reg = pci_read_config(dev, 0x40, sizeof(reg));
4539286441Srpaulo	pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg));
4540286441Srpaulo
4541286441Srpaulo	/* Enable bus-mastering and hardware bug workaround. */
4542286441Srpaulo	pci_enable_busmaster(dev);
4543286441Srpaulo	reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg));
4544286441Srpaulo	/* if !MSI */
4545286441Srpaulo	if (reg & PCIM_STATUS_INTxSTATE) {
4546286441Srpaulo		reg &= ~PCIM_STATUS_INTxSTATE;
4547286441Srpaulo	}
4548286441Srpaulo	pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg));
4549286441Srpaulo
4550286441Srpaulo	rid = PCIR_BAR(0);
4551286441Srpaulo	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
4552286441Srpaulo	    RF_ACTIVE);
4553286441Srpaulo	if (sc->sc_mem == NULL) {
4554286441Srpaulo		device_printf(sc->sc_dev, "can't map mem space\n");
4555286441Srpaulo		return (ENXIO);
4556286441Srpaulo	}
4557286441Srpaulo	sc->sc_st = rman_get_bustag(sc->sc_mem);
4558286441Srpaulo	sc->sc_sh = rman_get_bushandle(sc->sc_mem);
4559286441Srpaulo
4560286441Srpaulo	/* Install interrupt handler. */
4561286441Srpaulo	count = 1;
4562286441Srpaulo	rid = 0;
4563286441Srpaulo	if (pci_alloc_msi(dev, &count) == 0)
4564286441Srpaulo		rid = 1;
4565286441Srpaulo	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
4566286441Srpaulo	    (rid != 0 ? 0 : RF_SHAREABLE));
4567286441Srpaulo	if (sc->sc_irq == NULL) {
4568286441Srpaulo		device_printf(dev, "can't map interrupt\n");
4569286441Srpaulo			return (ENXIO);
4570286441Srpaulo	}
4571286441Srpaulo	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
4572286441Srpaulo	    NULL, iwm_intr, sc, &sc->sc_ih);
4573286441Srpaulo	if (sc->sc_ih == NULL) {
4574286441Srpaulo		device_printf(dev, "can't establish interrupt");
4575286441Srpaulo			return (ENXIO);
4576286441Srpaulo	}
4577286441Srpaulo	sc->sc_dmat = bus_get_dma_tag(sc->sc_dev);
4578286441Srpaulo
4579286441Srpaulo	return (0);
4580286441Srpaulo}
4581286441Srpaulo
4582286441Srpaulostatic void
4583286441Srpauloiwm_pci_detach(device_t dev)
4584286441Srpaulo{
4585286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
4586286441Srpaulo
4587286441Srpaulo	if (sc->sc_irq != NULL) {
4588286441Srpaulo		bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
4589286441Srpaulo		bus_release_resource(dev, SYS_RES_IRQ,
4590286441Srpaulo		    rman_get_rid(sc->sc_irq), sc->sc_irq);
4591286441Srpaulo		pci_release_msi(dev);
4592286441Srpaulo        }
4593286441Srpaulo	if (sc->sc_mem != NULL)
4594286441Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY,
4595286441Srpaulo		    rman_get_rid(sc->sc_mem), sc->sc_mem);
4596286441Srpaulo}
4597286441Srpaulo
4598286441Srpaulo
4599286441Srpaulo
4600286441Srpaulostatic int
4601286441Srpauloiwm_attach(device_t dev)
4602286441Srpaulo{
4603287197Sglebius	struct iwm_softc *sc = device_get_softc(dev);
4604287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4605286441Srpaulo	int error;
4606286441Srpaulo	int txq_i, i;
4607286441Srpaulo
4608286441Srpaulo	sc->sc_dev = dev;
4609293099Savos	IWM_LOCK_INIT(sc);
4610287197Sglebius	mbufq_init(&sc->sc_snd, ifqmaxlen);
4611286441Srpaulo	callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0);
4612286441Srpaulo	TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc);
4613286441Srpaulo	sc->sc_tq = taskqueue_create("iwm_taskq", M_WAITOK,
4614286441Srpaulo            taskqueue_thread_enqueue, &sc->sc_tq);
4615286441Srpaulo        error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwm_taskq");
4616286441Srpaulo        if (error != 0) {
4617286441Srpaulo                device_printf(dev, "can't start threads, error %d\n",
4618286441Srpaulo		    error);
4619286441Srpaulo		goto fail;
4620286441Srpaulo        }
4621286441Srpaulo
4622286441Srpaulo	/* PCI attach */
4623286441Srpaulo	error = iwm_pci_attach(dev);
4624286441Srpaulo	if (error != 0)
4625286441Srpaulo		goto fail;
4626286441Srpaulo
4627286441Srpaulo	sc->sc_wantresp = -1;
4628286441Srpaulo
4629286441Srpaulo	/* Check device type */
4630286441Srpaulo	error = iwm_dev_check(dev);
4631286441Srpaulo	if (error != 0)
4632286441Srpaulo		goto fail;
4633286441Srpaulo
4634286441Srpaulo	sc->sc_fwdmasegsz = IWM_FWDMASEGSZ;
4635286441Srpaulo
4636286441Srpaulo	/*
4637286441Srpaulo	 * We now start fiddling with the hardware
4638286441Srpaulo	 */
4639286441Srpaulo	sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV);
4640286441Srpaulo	if (iwm_prepare_card_hw(sc) != 0) {
4641286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
4642286441Srpaulo		goto fail;
4643286441Srpaulo	}
4644286441Srpaulo
4645286441Srpaulo	/* Allocate DMA memory for firmware transfers. */
4646286441Srpaulo	if ((error = iwm_alloc_fwmem(sc)) != 0) {
4647286441Srpaulo		device_printf(dev, "could not allocate memory for firmware\n");
4648286441Srpaulo		goto fail;
4649286441Srpaulo	}
4650286441Srpaulo
4651286441Srpaulo	/* Allocate "Keep Warm" page. */
4652286441Srpaulo	if ((error = iwm_alloc_kw(sc)) != 0) {
4653286441Srpaulo		device_printf(dev, "could not allocate keep warm page\n");
4654286441Srpaulo		goto fail;
4655286441Srpaulo	}
4656286441Srpaulo
4657286441Srpaulo	/* We use ICT interrupts */
4658286441Srpaulo	if ((error = iwm_alloc_ict(sc)) != 0) {
4659286441Srpaulo		device_printf(dev, "could not allocate ICT table\n");
4660286441Srpaulo		goto fail;
4661286441Srpaulo	}
4662286441Srpaulo
4663286441Srpaulo	/* Allocate TX scheduler "rings". */
4664286441Srpaulo	if ((error = iwm_alloc_sched(sc)) != 0) {
4665286441Srpaulo		device_printf(dev, "could not allocate TX scheduler rings\n");
4666286441Srpaulo		goto fail;
4667286441Srpaulo	}
4668286441Srpaulo
4669286441Srpaulo	/* Allocate TX rings */
4670286441Srpaulo	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) {
4671286441Srpaulo		if ((error = iwm_alloc_tx_ring(sc,
4672286441Srpaulo		    &sc->txq[txq_i], txq_i)) != 0) {
4673286441Srpaulo			device_printf(dev,
4674286441Srpaulo			    "could not allocate TX ring %d\n",
4675286441Srpaulo			    txq_i);
4676286441Srpaulo			goto fail;
4677286441Srpaulo		}
4678286441Srpaulo	}
4679286441Srpaulo
4680286441Srpaulo	/* Allocate RX ring. */
4681286441Srpaulo	if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) {
4682286441Srpaulo		device_printf(dev, "could not allocate RX ring\n");
4683286441Srpaulo		goto fail;
4684286441Srpaulo	}
4685286441Srpaulo
4686286441Srpaulo	/* Clear pending interrupts. */
4687286441Srpaulo	IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff);
4688286441Srpaulo
4689286441Srpaulo	ic->ic_softc = sc;
4690286441Srpaulo	ic->ic_name = device_get_nameunit(sc->sc_dev);
4691286441Srpaulo	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
4692286441Srpaulo	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
4693286441Srpaulo
4694286441Srpaulo	/* Set device capabilities. */
4695286441Srpaulo	ic->ic_caps =
4696286441Srpaulo	    IEEE80211_C_STA |
4697286441Srpaulo	    IEEE80211_C_WPA |		/* WPA/RSN */
4698286441Srpaulo	    IEEE80211_C_WME |
4699286441Srpaulo	    IEEE80211_C_SHSLOT |	/* short slot time supported */
4700286441Srpaulo	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
4701286441Srpaulo//	    IEEE80211_C_BGSCAN		/* capable of bg scanning */
4702286441Srpaulo	    ;
4703286441Srpaulo	for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
4704286441Srpaulo		sc->sc_phyctxt[i].id = i;
4705286441Srpaulo		sc->sc_phyctxt[i].color = 0;
4706286441Srpaulo		sc->sc_phyctxt[i].ref = 0;
4707286441Srpaulo		sc->sc_phyctxt[i].channel = NULL;
4708286441Srpaulo	}
4709286441Srpaulo
4710286441Srpaulo	/* Max RSSI */
4711286441Srpaulo	sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM;
4712286441Srpaulo	sc->sc_preinit_hook.ich_func = iwm_preinit;
4713286441Srpaulo	sc->sc_preinit_hook.ich_arg = sc;
4714286441Srpaulo	if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
4715286441Srpaulo		device_printf(dev, "config_intrhook_establish failed\n");
4716286441Srpaulo		goto fail;
4717286441Srpaulo	}
4718286441Srpaulo
4719286441Srpaulo#ifdef IWM_DEBUG
4720286441Srpaulo	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
4721286441Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug",
4722286441Srpaulo	    CTLFLAG_RW, &sc->sc_debug, 0, "control debugging");
4723286441Srpaulo#endif
4724286441Srpaulo
4725286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
4726286441Srpaulo	    "<-%s\n", __func__);
4727286441Srpaulo
4728286441Srpaulo	return 0;
4729286441Srpaulo
4730286441Srpaulo	/* Free allocated memory if something failed during attachment. */
4731286441Srpaulofail:
4732286441Srpaulo	iwm_detach_local(sc, 0);
4733286441Srpaulo
4734286441Srpaulo	return ENXIO;
4735286441Srpaulo}
4736286441Srpaulo
4737286441Srpaulostatic int
4738286441Srpauloiwm_update_edca(struct ieee80211com *ic)
4739286441Srpaulo{
4740286865Sadrian	struct iwm_softc *sc = ic->ic_softc;
4741286441Srpaulo
4742286441Srpaulo	device_printf(sc->sc_dev, "%s: called\n", __func__);
4743286441Srpaulo	return (0);
4744286441Srpaulo}
4745286441Srpaulo
4746286441Srpaulostatic void
4747286441Srpauloiwm_preinit(void *arg)
4748286441Srpaulo{
4749286441Srpaulo	struct iwm_softc *sc = arg;
4750286441Srpaulo	device_t dev = sc->sc_dev;
4751287197Sglebius	struct ieee80211com *ic = &sc->sc_ic;
4752286441Srpaulo	int error;
4753286441Srpaulo
4754286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
4755286441Srpaulo	    "->%s\n", __func__);
4756286441Srpaulo
4757286441Srpaulo	IWM_LOCK(sc);
4758286441Srpaulo	if ((error = iwm_start_hw(sc)) != 0) {
4759286441Srpaulo		device_printf(dev, "could not initialize hardware\n");
4760286441Srpaulo		IWM_UNLOCK(sc);
4761286441Srpaulo		goto fail;
4762286441Srpaulo	}
4763286441Srpaulo
4764286441Srpaulo	error = iwm_run_init_mvm_ucode(sc, 1);
4765286441Srpaulo	iwm_stop_device(sc);
4766286441Srpaulo	if (error) {
4767286441Srpaulo		IWM_UNLOCK(sc);
4768286441Srpaulo		goto fail;
4769286441Srpaulo	}
4770286441Srpaulo	device_printf(dev,
4771286441Srpaulo	    "revision: 0x%x, firmware %d.%d (API ver. %d)\n",
4772286441Srpaulo	    sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK,
4773286441Srpaulo	    IWM_UCODE_MAJOR(sc->sc_fwver),
4774286441Srpaulo	    IWM_UCODE_MINOR(sc->sc_fwver),
4775286441Srpaulo	    IWM_UCODE_API(sc->sc_fwver));
4776286441Srpaulo
4777286441Srpaulo	/* not all hardware can do 5GHz band */
4778286441Srpaulo	if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
4779286441Srpaulo		memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
4780286441Srpaulo		    sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A]));
4781286441Srpaulo	IWM_UNLOCK(sc);
4782286441Srpaulo
4783298877Savos	iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
4784298877Savos	    ic->ic_channels);
4785298877Savos
4786286441Srpaulo	/*
4787286441Srpaulo	 * At this point we've committed - if we fail to do setup,
4788286441Srpaulo	 * we now also have to tear down the net80211 state.
4789286441Srpaulo	 */
4790287197Sglebius	ieee80211_ifattach(ic);
4791286441Srpaulo	ic->ic_vap_create = iwm_vap_create;
4792286441Srpaulo	ic->ic_vap_delete = iwm_vap_delete;
4793286441Srpaulo	ic->ic_raw_xmit = iwm_raw_xmit;
4794286441Srpaulo	ic->ic_node_alloc = iwm_node_alloc;
4795286441Srpaulo	ic->ic_scan_start = iwm_scan_start;
4796286441Srpaulo	ic->ic_scan_end = iwm_scan_end;
4797286441Srpaulo	ic->ic_update_mcast = iwm_update_mcast;
4798298877Savos	ic->ic_getradiocaps = iwm_init_channel_map;
4799286441Srpaulo	ic->ic_set_channel = iwm_set_channel;
4800286441Srpaulo	ic->ic_scan_curchan = iwm_scan_curchan;
4801286441Srpaulo	ic->ic_scan_mindwell = iwm_scan_mindwell;
4802286441Srpaulo	ic->ic_wme.wme_update = iwm_update_edca;
4803287197Sglebius	ic->ic_parent = iwm_parent;
4804287197Sglebius	ic->ic_transmit = iwm_transmit;
4805286441Srpaulo	iwm_radiotap_attach(sc);
4806286441Srpaulo	if (bootverbose)
4807286441Srpaulo		ieee80211_announce(ic);
4808286441Srpaulo
4809286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
4810286441Srpaulo	    "<-%s\n", __func__);
4811286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
4812286441Srpaulo
4813286441Srpaulo	return;
4814286441Srpaulofail:
4815286441Srpaulo	config_intrhook_disestablish(&sc->sc_preinit_hook);
4816286441Srpaulo	iwm_detach_local(sc, 0);
4817286441Srpaulo}
4818286441Srpaulo
4819286441Srpaulo/*
4820286441Srpaulo * Attach the interface to 802.11 radiotap.
4821286441Srpaulo */
4822286441Srpaulostatic void
4823286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc)
4824286441Srpaulo{
4825287197Sglebius        struct ieee80211com *ic = &sc->sc_ic;
4826286441Srpaulo
4827286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
4828286441Srpaulo	    "->%s begin\n", __func__);
4829286441Srpaulo        ieee80211_radiotap_attach(ic,
4830286441Srpaulo            &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
4831286441Srpaulo                IWM_TX_RADIOTAP_PRESENT,
4832286441Srpaulo            &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
4833286441Srpaulo                IWM_RX_RADIOTAP_PRESENT);
4834286441Srpaulo	IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE,
4835286441Srpaulo	    "->%s end\n", __func__);
4836286441Srpaulo}
4837286441Srpaulo
4838286441Srpaulostatic struct ieee80211vap *
4839286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
4840286441Srpaulo    enum ieee80211_opmode opmode, int flags,
4841286441Srpaulo    const uint8_t bssid[IEEE80211_ADDR_LEN],
4842286441Srpaulo    const uint8_t mac[IEEE80211_ADDR_LEN])
4843286441Srpaulo{
4844286441Srpaulo	struct iwm_vap *ivp;
4845286441Srpaulo	struct ieee80211vap *vap;
4846286441Srpaulo
4847286441Srpaulo	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
4848286441Srpaulo		return NULL;
4849287197Sglebius	ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO);
4850286441Srpaulo	vap = &ivp->iv_vap;
4851287197Sglebius	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
4852286441Srpaulo	vap->iv_bmissthreshold = 10;            /* override default */
4853286441Srpaulo	/* Override with driver methods. */
4854286441Srpaulo	ivp->iv_newstate = vap->iv_newstate;
4855286441Srpaulo	vap->iv_newstate = iwm_newstate;
4856286441Srpaulo
4857286441Srpaulo	ieee80211_ratectl_init(vap);
4858286441Srpaulo	/* Complete setup. */
4859287197Sglebius	ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status,
4860287197Sglebius	    mac);
4861286441Srpaulo	ic->ic_opmode = opmode;
4862286441Srpaulo
4863286441Srpaulo	return vap;
4864286441Srpaulo}
4865286441Srpaulo
4866286441Srpaulostatic void
4867286441Srpauloiwm_vap_delete(struct ieee80211vap *vap)
4868286441Srpaulo{
4869286441Srpaulo	struct iwm_vap *ivp = IWM_VAP(vap);
4870286441Srpaulo
4871286441Srpaulo	ieee80211_ratectl_deinit(vap);
4872286441Srpaulo	ieee80211_vap_detach(vap);
4873286441Srpaulo	free(ivp, M_80211_VAP);
4874286441Srpaulo}
4875286441Srpaulo
4876286441Srpaulostatic void
4877286441Srpauloiwm_scan_start(struct ieee80211com *ic)
4878286441Srpaulo{
4879286441Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4880286865Sadrian        struct iwm_softc *sc = ic->ic_softc;
4881286441Srpaulo	int error;
4882286441Srpaulo
4883286441Srpaulo	if (sc->sc_scanband)
4884286441Srpaulo		return;
4885286441Srpaulo	IWM_LOCK(sc);
4886286441Srpaulo	error = iwm_mvm_scan_request(sc, IEEE80211_CHAN_2GHZ, 0, NULL, 0);
4887286441Srpaulo	if (error) {
4888286441Srpaulo		device_printf(sc->sc_dev, "could not initiate scan\n");
4889286441Srpaulo		IWM_UNLOCK(sc);
4890286441Srpaulo		ieee80211_cancel_scan(vap);
4891286441Srpaulo	} else
4892286441Srpaulo		IWM_UNLOCK(sc);
4893286441Srpaulo}
4894286441Srpaulo
4895286441Srpaulostatic void
4896286441Srpauloiwm_scan_end(struct ieee80211com *ic)
4897286441Srpaulo{
4898286441Srpaulo}
4899286441Srpaulo
4900286441Srpaulostatic void
4901286441Srpauloiwm_update_mcast(struct ieee80211com *ic)
4902286441Srpaulo{
4903286441Srpaulo}
4904286441Srpaulo
4905286441Srpaulostatic void
4906286441Srpauloiwm_set_channel(struct ieee80211com *ic)
4907286441Srpaulo{
4908286441Srpaulo}
4909286441Srpaulo
4910286441Srpaulostatic void
4911286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
4912286441Srpaulo{
4913286441Srpaulo}
4914286441Srpaulo
4915286441Srpaulostatic void
4916286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss)
4917286441Srpaulo{
4918286441Srpaulo	return;
4919286441Srpaulo}
4920286441Srpaulo
4921286441Srpaulovoid
4922286441Srpauloiwm_init_task(void *arg1)
4923286441Srpaulo{
4924286441Srpaulo	struct iwm_softc *sc = arg1;
4925286441Srpaulo
4926286441Srpaulo	IWM_LOCK(sc);
4927286441Srpaulo	while (sc->sc_flags & IWM_FLAG_BUSY)
4928286441Srpaulo		msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0);
4929286441Srpaulo	sc->sc_flags |= IWM_FLAG_BUSY;
4930287197Sglebius	iwm_stop(sc);
4931287197Sglebius	if (sc->sc_ic.ic_nrunning > 0)
4932286441Srpaulo		iwm_init(sc);
4933286441Srpaulo	sc->sc_flags &= ~IWM_FLAG_BUSY;
4934286441Srpaulo	wakeup(&sc->sc_flags);
4935286441Srpaulo	IWM_UNLOCK(sc);
4936286441Srpaulo}
4937286441Srpaulo
4938286441Srpaulostatic int
4939286441Srpauloiwm_resume(device_t dev)
4940286441Srpaulo{
4941298612Sadrian	struct iwm_softc *sc = device_get_softc(dev);
4942298612Sadrian	int do_reinit = 0;
4943286441Srpaulo	uint16_t reg;
4944286441Srpaulo
4945286441Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
4946286441Srpaulo	reg = pci_read_config(dev, 0x40, sizeof(reg));
4947286441Srpaulo	pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg));
4948286441Srpaulo	iwm_init_task(device_get_softc(dev));
4949286441Srpaulo
4950298612Sadrian	IWM_LOCK(sc);
4951298612Sadrian	if (sc->sc_flags & IWM_FLAG_DORESUME) {
4952298612Sadrian		sc->sc_flags &= ~IWM_FLAG_DORESUME;
4953298612Sadrian		do_reinit = 1;
4954298612Sadrian	}
4955298612Sadrian	IWM_UNLOCK(sc);
4956298612Sadrian
4957298612Sadrian	if (do_reinit)
4958298612Sadrian		ieee80211_resume_all(&sc->sc_ic);
4959298612Sadrian
4960286441Srpaulo	return 0;
4961286441Srpaulo}
4962286441Srpaulo
4963286441Srpaulostatic int
4964286441Srpauloiwm_suspend(device_t dev)
4965286441Srpaulo{
4966298612Sadrian	int do_stop = 0;
4967286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
4968286441Srpaulo
4969298612Sadrian	do_stop = !! (sc->sc_ic.ic_nrunning > 0);
4970298612Sadrian
4971298612Sadrian	ieee80211_suspend_all(&sc->sc_ic);
4972298612Sadrian
4973298612Sadrian	if (do_stop) {
4974287197Sglebius		IWM_LOCK(sc);
4975287197Sglebius		iwm_stop(sc);
4976298612Sadrian		sc->sc_flags |= IWM_FLAG_DORESUME;
4977287197Sglebius		IWM_UNLOCK(sc);
4978287197Sglebius	}
4979286441Srpaulo
4980286441Srpaulo	return (0);
4981286441Srpaulo}
4982286441Srpaulo
4983286441Srpaulostatic int
4984286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211)
4985286441Srpaulo{
4986293219Savos	struct iwm_fw_info *fw = &sc->sc_fw;
4987286441Srpaulo	device_t dev = sc->sc_dev;
4988286441Srpaulo	int i;
4989286441Srpaulo
4990286441Srpaulo	if (sc->sc_tq) {
4991286441Srpaulo		taskqueue_drain_all(sc->sc_tq);
4992286441Srpaulo		taskqueue_free(sc->sc_tq);
4993286441Srpaulo	}
4994287197Sglebius	callout_drain(&sc->sc_watchdog_to);
4995287197Sglebius	iwm_stop_device(sc);
4996287197Sglebius	if (do_net80211)
4997287197Sglebius		ieee80211_ifdetach(&sc->sc_ic);
4998286441Srpaulo
4999286441Srpaulo	/* Free descriptor rings */
5000286441Srpaulo	for (i = 0; i < nitems(sc->txq); i++)
5001286441Srpaulo		iwm_free_tx_ring(sc, &sc->txq[i]);
5002286441Srpaulo
5003293219Savos	/* Free firmware */
5004293219Savos	if (fw->fw_fp != NULL)
5005293219Savos		iwm_fw_info_free(fw);
5006293219Savos
5007293178Savos	/* Free scheduler */
5008286441Srpaulo	iwm_free_sched(sc);
5009286441Srpaulo	if (sc->ict_dma.vaddr != NULL)
5010286441Srpaulo		iwm_free_ict(sc);
5011286441Srpaulo	if (sc->kw_dma.vaddr != NULL)
5012286441Srpaulo		iwm_free_kw(sc);
5013286441Srpaulo	if (sc->fw_dma.vaddr != NULL)
5014286441Srpaulo		iwm_free_fwmem(sc);
5015286441Srpaulo
5016286441Srpaulo	/* Finished with the hardware - detach things */
5017286441Srpaulo	iwm_pci_detach(dev);
5018286441Srpaulo
5019287197Sglebius	mbufq_drain(&sc->sc_snd);
5020293099Savos	IWM_LOCK_DESTROY(sc);
5021286441Srpaulo
5022286441Srpaulo	return (0);
5023286441Srpaulo}
5024286441Srpaulo
5025286441Srpaulostatic int
5026286441Srpauloiwm_detach(device_t dev)
5027286441Srpaulo{
5028286441Srpaulo	struct iwm_softc *sc = device_get_softc(dev);
5029286441Srpaulo
5030286441Srpaulo	return (iwm_detach_local(sc, 1));
5031286441Srpaulo}
5032286441Srpaulo
5033286441Srpaulostatic device_method_t iwm_pci_methods[] = {
5034286441Srpaulo        /* Device interface */
5035286441Srpaulo        DEVMETHOD(device_probe,         iwm_probe),
5036286441Srpaulo        DEVMETHOD(device_attach,        iwm_attach),
5037286441Srpaulo        DEVMETHOD(device_detach,        iwm_detach),
5038286441Srpaulo        DEVMETHOD(device_suspend,       iwm_suspend),
5039286441Srpaulo        DEVMETHOD(device_resume,        iwm_resume),
5040286441Srpaulo
5041286441Srpaulo        DEVMETHOD_END
5042286441Srpaulo};
5043286441Srpaulo
5044286441Srpaulostatic driver_t iwm_pci_driver = {
5045286441Srpaulo        "iwm",
5046286441Srpaulo        iwm_pci_methods,
5047286441Srpaulo        sizeof (struct iwm_softc)
5048286441Srpaulo};
5049286441Srpaulo
5050286441Srpaulostatic devclass_t iwm_devclass;
5051286441Srpaulo
5052286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL);
5053286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1);
5054286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1);
5055286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1);
5056