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