xref: /illumos-gate/usr/src/uts/common/io/iwn/if_iwn.c (revision 35060cea)
1fd43cf6eSHans Rosenfeld /*	$NetBSD: if_iwn.c,v 1.78 2016/06/10 13:27:14 ozaki-r Exp $	*/
2fd43cf6eSHans Rosenfeld /*	$OpenBSD: if_iwn.c,v 1.135 2014/09/10 07:22:09 dcoppa Exp $	*/
3fd43cf6eSHans Rosenfeld 
4fd43cf6eSHans Rosenfeld /*-
5fd43cf6eSHans Rosenfeld  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
6fd43cf6eSHans Rosenfeld  *
7fd43cf6eSHans Rosenfeld  * Permission to use, copy, modify, and distribute this software for any
8fd43cf6eSHans Rosenfeld  * purpose with or without fee is hereby granted, provided that the above
9fd43cf6eSHans Rosenfeld  * copyright notice and this permission notice appear in all copies.
10fd43cf6eSHans Rosenfeld  *
11fd43cf6eSHans Rosenfeld  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12fd43cf6eSHans Rosenfeld  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13fd43cf6eSHans Rosenfeld  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14fd43cf6eSHans Rosenfeld  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15fd43cf6eSHans Rosenfeld  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16fd43cf6eSHans Rosenfeld  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17fd43cf6eSHans Rosenfeld  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18fd43cf6eSHans Rosenfeld  */
19fd43cf6eSHans Rosenfeld 
20fd43cf6eSHans Rosenfeld /*
21fd43cf6eSHans Rosenfeld  * Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
22fd43cf6eSHans Rosenfeld  */
23fd43cf6eSHans Rosenfeld 
24fd43cf6eSHans Rosenfeld /*
25fd43cf6eSHans Rosenfeld  * Driver for Intel WiFi Link 4965 and 100/1000/2000/5000/6000 Series 802.11
26fd43cf6eSHans Rosenfeld  * network adapters.
27fd43cf6eSHans Rosenfeld  */
28fd43cf6eSHans Rosenfeld 
29fd43cf6eSHans Rosenfeld /*
30fd43cf6eSHans Rosenfeld  * TODO:
31fd43cf6eSHans Rosenfeld  * - turn tunables into driver properties
32fd43cf6eSHans Rosenfeld  */
33fd43cf6eSHans Rosenfeld 
34fd43cf6eSHans Rosenfeld #undef IWN_HWCRYPTO	/* XXX does not even compile yet */
35fd43cf6eSHans Rosenfeld 
36fd43cf6eSHans Rosenfeld #include <sys/modctl.h>
37fd43cf6eSHans Rosenfeld #include <sys/ddi.h>
38fd43cf6eSHans Rosenfeld #include <sys/sunddi.h>
39fd43cf6eSHans Rosenfeld #include <sys/stat.h>
40fd43cf6eSHans Rosenfeld 
41fd43cf6eSHans Rosenfeld #include <sys/param.h>
42fd43cf6eSHans Rosenfeld #include <sys/sockio.h>
43fd43cf6eSHans Rosenfeld #include <sys/proc.h>
44fd43cf6eSHans Rosenfeld #include <sys/socket.h>
45fd43cf6eSHans Rosenfeld #include <sys/systm.h>
46fd43cf6eSHans Rosenfeld #include <sys/mutex.h>
47fd43cf6eSHans Rosenfeld #include <sys/conf.h>
48fd43cf6eSHans Rosenfeld 
49fd43cf6eSHans Rosenfeld #include <sys/pci.h>
50fd43cf6eSHans Rosenfeld #include <sys/pcie.h>
51fd43cf6eSHans Rosenfeld 
52fd43cf6eSHans Rosenfeld #include <net/if.h>
53fd43cf6eSHans Rosenfeld #include <net/if_arp.h>
54fd43cf6eSHans Rosenfeld #include <net/if_dl.h>
55fd43cf6eSHans Rosenfeld #include <net/if_types.h>
56fd43cf6eSHans Rosenfeld 
57fd43cf6eSHans Rosenfeld #include <netinet/in.h>
58fd43cf6eSHans Rosenfeld #include <netinet/in_systm.h>
59fd43cf6eSHans Rosenfeld #include <netinet/in_var.h>
60fd43cf6eSHans Rosenfeld #include <netinet/ip.h>
61fd43cf6eSHans Rosenfeld 
62fd43cf6eSHans Rosenfeld #include <sys/dlpi.h>
63fd43cf6eSHans Rosenfeld #include <sys/mac_provider.h>
64fd43cf6eSHans Rosenfeld #include <sys/mac_wifi.h>
65fd43cf6eSHans Rosenfeld #include <sys/net80211.h>
66fd43cf6eSHans Rosenfeld #include <sys/firmload.h>
67fd43cf6eSHans Rosenfeld #include <sys/queue.h>
68fd43cf6eSHans Rosenfeld #include <sys/strsun.h>
69fd43cf6eSHans Rosenfeld #include <sys/strsubr.h>
70fd43cf6eSHans Rosenfeld #include <sys/sysmacros.h>
71fd43cf6eSHans Rosenfeld #include <sys/types.h>
72fd43cf6eSHans Rosenfeld #include <sys/kstat.h>
73fd43cf6eSHans Rosenfeld 
74fd43cf6eSHans Rosenfeld #include <sys/sdt.h>
75fd43cf6eSHans Rosenfeld 
76fd43cf6eSHans Rosenfeld #include "if_iwncompat.h"
77fd43cf6eSHans Rosenfeld #include "if_iwnreg.h"
78fd43cf6eSHans Rosenfeld #include "if_iwnvar.h"
79fd43cf6eSHans Rosenfeld #include <inet/wifi_ioctl.h>
80fd43cf6eSHans Rosenfeld 
81fd43cf6eSHans Rosenfeld #ifdef DEBUG
82fd43cf6eSHans Rosenfeld #define IWN_DEBUG
83fd43cf6eSHans Rosenfeld #endif
84fd43cf6eSHans Rosenfeld 
85fd43cf6eSHans Rosenfeld /*
86fd43cf6eSHans Rosenfeld  * regs access attributes
87fd43cf6eSHans Rosenfeld  */
88fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_reg_accattr = {
89fd43cf6eSHans Rosenfeld 	.devacc_attr_version	= DDI_DEVICE_ATTR_V0,
90fd43cf6eSHans Rosenfeld 	.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC,
91fd43cf6eSHans Rosenfeld 	.devacc_attr_dataorder	= DDI_STRICTORDER_ACC,
92fd43cf6eSHans Rosenfeld 	.devacc_attr_access	= DDI_DEFAULT_ACC
93fd43cf6eSHans Rosenfeld };
94fd43cf6eSHans Rosenfeld 
95fd43cf6eSHans Rosenfeld /*
96fd43cf6eSHans Rosenfeld  * DMA access attributes for descriptor
97fd43cf6eSHans Rosenfeld  */
98fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_dma_descattr = {
99fd43cf6eSHans Rosenfeld 	.devacc_attr_version	= DDI_DEVICE_ATTR_V0,
100fd43cf6eSHans Rosenfeld 	.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC,
101fd43cf6eSHans Rosenfeld 	.devacc_attr_dataorder	= DDI_STRICTORDER_ACC,
102fd43cf6eSHans Rosenfeld 	.devacc_attr_access	= DDI_DEFAULT_ACC
103fd43cf6eSHans Rosenfeld };
104fd43cf6eSHans Rosenfeld 
105fd43cf6eSHans Rosenfeld /*
106fd43cf6eSHans Rosenfeld  * DMA access attributes
107fd43cf6eSHans Rosenfeld  */
108fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_dma_accattr = {
109fd43cf6eSHans Rosenfeld 	.devacc_attr_version	= DDI_DEVICE_ATTR_V0,
110fd43cf6eSHans Rosenfeld 	.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC,
111fd43cf6eSHans Rosenfeld 	.devacc_attr_dataorder	= DDI_STRICTORDER_ACC,
112fd43cf6eSHans Rosenfeld 	.devacc_attr_access	= DDI_DEFAULT_ACC
113fd43cf6eSHans Rosenfeld };
114fd43cf6eSHans Rosenfeld 
115fd43cf6eSHans Rosenfeld 
116fd43cf6eSHans Rosenfeld /*
117fd43cf6eSHans Rosenfeld  * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
118fd43cf6eSHans Rosenfeld  */
119fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11a =
120fd43cf6eSHans Rosenfeld 	{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
121fd43cf6eSHans Rosenfeld 
122fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11b =
123fd43cf6eSHans Rosenfeld 	{ 4, { 2, 4, 11, 22 } };
124fd43cf6eSHans Rosenfeld 
125fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11g =
126fd43cf6eSHans Rosenfeld 	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
127fd43cf6eSHans Rosenfeld 
128fd43cf6eSHans Rosenfeld static void	iwn_kstat_create(struct iwn_softc *, const char *, size_t,
129fd43cf6eSHans Rosenfeld     kstat_t **, void **);
130fd43cf6eSHans Rosenfeld static void	iwn_kstat_free(kstat_t *, void *, size_t);
131fd43cf6eSHans Rosenfeld static void	iwn_kstat_init(struct iwn_softc *);
132fd43cf6eSHans Rosenfeld static void	iwn_kstat_init_2000(struct iwn_softc *);
133fd43cf6eSHans Rosenfeld static void	iwn_kstat_init_4965(struct iwn_softc *);
134fd43cf6eSHans Rosenfeld static void	iwn_kstat_init_6000(struct iwn_softc *);
135fd43cf6eSHans Rosenfeld static void	iwn_intr_teardown(struct iwn_softc *);
136fd43cf6eSHans Rosenfeld static int	iwn_intr_add(struct iwn_softc *, int);
137fd43cf6eSHans Rosenfeld static int	iwn_intr_setup(struct iwn_softc *);
138fd43cf6eSHans Rosenfeld static int	iwn_attach(dev_info_t *, ddi_attach_cmd_t);
139fd43cf6eSHans Rosenfeld static int	iwn4965_attach(struct iwn_softc *);
140fd43cf6eSHans Rosenfeld static int	iwn5000_attach(struct iwn_softc *, uint16_t);
141fd43cf6eSHans Rosenfeld static int	iwn_detach(dev_info_t *, ddi_detach_cmd_t);
142fd43cf6eSHans Rosenfeld static int	iwn_quiesce(dev_info_t *);
143fd43cf6eSHans Rosenfeld static int	iwn_nic_lock(struct iwn_softc *);
144fd43cf6eSHans Rosenfeld static int	iwn_eeprom_lock(struct iwn_softc *);
145fd43cf6eSHans Rosenfeld static int	iwn_init_otprom(struct iwn_softc *);
146fd43cf6eSHans Rosenfeld static int	iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
147fd43cf6eSHans Rosenfeld static int	iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
148fd43cf6eSHans Rosenfeld     uint_t, uint_t, void **, ddi_device_acc_attr_t *, uint_t);
149fd43cf6eSHans Rosenfeld static void	iwn_dma_contig_free(struct iwn_dma_info *);
150fd43cf6eSHans Rosenfeld static int	iwn_alloc_sched(struct iwn_softc *);
151fd43cf6eSHans Rosenfeld static void	iwn_free_sched(struct iwn_softc *);
152fd43cf6eSHans Rosenfeld static int	iwn_alloc_kw(struct iwn_softc *);
153fd43cf6eSHans Rosenfeld static void	iwn_free_kw(struct iwn_softc *);
154fd43cf6eSHans Rosenfeld static int	iwn_alloc_ict(struct iwn_softc *);
155fd43cf6eSHans Rosenfeld static void	iwn_free_ict(struct iwn_softc *);
156fd43cf6eSHans Rosenfeld static int	iwn_alloc_fwmem(struct iwn_softc *);
157fd43cf6eSHans Rosenfeld static void	iwn_free_fwmem(struct iwn_softc *);
158fd43cf6eSHans Rosenfeld static int	iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
159fd43cf6eSHans Rosenfeld static void	iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
160fd43cf6eSHans Rosenfeld static void	iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
161fd43cf6eSHans Rosenfeld static int	iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
162fd43cf6eSHans Rosenfeld 		    int);
163fd43cf6eSHans Rosenfeld static void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
164fd43cf6eSHans Rosenfeld static void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
165fd43cf6eSHans Rosenfeld static void	iwn5000_ict_reset(struct iwn_softc *);
166fd43cf6eSHans Rosenfeld static int	iwn_read_eeprom(struct iwn_softc *);
167fd43cf6eSHans Rosenfeld static void	iwn4965_read_eeprom(struct iwn_softc *);
168fd43cf6eSHans Rosenfeld 
169fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG
170fd43cf6eSHans Rosenfeld static void	iwn4965_print_power_group(struct iwn_softc *, int);
171fd43cf6eSHans Rosenfeld #endif
172fd43cf6eSHans Rosenfeld static void	iwn5000_read_eeprom(struct iwn_softc *);
173fd43cf6eSHans Rosenfeld static void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
174fd43cf6eSHans Rosenfeld static void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
175fd43cf6eSHans Rosenfeld static struct	ieee80211_node *iwn_node_alloc(ieee80211com_t *);
176fd43cf6eSHans Rosenfeld static void	iwn_node_free(ieee80211_node_t *);
177fd43cf6eSHans Rosenfeld static void	iwn_newassoc(struct ieee80211_node *, int);
178fd43cf6eSHans Rosenfeld static int	iwn_newstate(struct ieee80211com *, enum ieee80211_state, int);
179fd43cf6eSHans Rosenfeld static void	iwn_iter_func(void *, struct ieee80211_node *);
180fd43cf6eSHans Rosenfeld static void	iwn_calib_timeout(void *);
181fd43cf6eSHans Rosenfeld static void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
182fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
183fd43cf6eSHans Rosenfeld static void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
184fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
185fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
186fd43cf6eSHans Rosenfeld static void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
187fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
188fd43cf6eSHans Rosenfeld #endif
189fd43cf6eSHans Rosenfeld static void	iwn5000_rx_calib_results(struct iwn_softc *,
190fd43cf6eSHans Rosenfeld 		    struct iwn_rx_desc *, struct iwn_rx_data *);
191fd43cf6eSHans Rosenfeld static void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
192fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
193fd43cf6eSHans Rosenfeld static void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
194fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
195fd43cf6eSHans Rosenfeld static void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
196fd43cf6eSHans Rosenfeld 		    struct iwn_rx_data *);
197fd43cf6eSHans Rosenfeld static void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
198fd43cf6eSHans Rosenfeld 		    uint8_t);
199fd43cf6eSHans Rosenfeld static void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
200fd43cf6eSHans Rosenfeld static void	iwn_notif_intr(struct iwn_softc *);
201fd43cf6eSHans Rosenfeld static void	iwn_wakeup_intr(struct iwn_softc *);
202fd43cf6eSHans Rosenfeld static void	iwn_fatal_intr(struct iwn_softc *);
203fd43cf6eSHans Rosenfeld static uint_t	iwn_intr(caddr_t, caddr_t);
204fd43cf6eSHans Rosenfeld static void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
205fd43cf6eSHans Rosenfeld 		    uint16_t);
206fd43cf6eSHans Rosenfeld static void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
207fd43cf6eSHans Rosenfeld 		    uint16_t);
208fd43cf6eSHans Rosenfeld #ifdef notyet
209fd43cf6eSHans Rosenfeld static void	iwn5000_reset_sched(struct iwn_softc *, int, int);
210fd43cf6eSHans Rosenfeld #endif
211fd43cf6eSHans Rosenfeld static int	iwn_send(ieee80211com_t *, mblk_t *, uint8_t);
212fd43cf6eSHans Rosenfeld static void	iwn_watchdog(void *);
213fd43cf6eSHans Rosenfeld static int	iwn_cmd(struct iwn_softc *, uint8_t, void *, int, int);
214fd43cf6eSHans Rosenfeld static int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
215fd43cf6eSHans Rosenfeld 		    int);
216fd43cf6eSHans Rosenfeld static int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
217fd43cf6eSHans Rosenfeld 		    int);
218fd43cf6eSHans Rosenfeld static int	iwn_set_link_quality(struct iwn_softc *,
219fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *);
220fd43cf6eSHans Rosenfeld static int	iwn_add_broadcast_node(struct iwn_softc *, int);
221fd43cf6eSHans Rosenfeld static void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
222fd43cf6eSHans Rosenfeld static int	iwn_set_critical_temp(struct iwn_softc *);
223fd43cf6eSHans Rosenfeld static int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
224fd43cf6eSHans Rosenfeld static void	iwn4965_power_calibration(struct iwn_softc *, int);
225fd43cf6eSHans Rosenfeld static int	iwn4965_set_txpower(struct iwn_softc *, int);
226fd43cf6eSHans Rosenfeld static int	iwn5000_set_txpower(struct iwn_softc *, int);
227fd43cf6eSHans Rosenfeld static int	iwn4965_get_rssi(const struct iwn_rx_stat *);
228fd43cf6eSHans Rosenfeld static int	iwn5000_get_rssi(const struct iwn_rx_stat *);
229fd43cf6eSHans Rosenfeld static int	iwn_get_noise(const struct iwn_rx_general_stats *);
230fd43cf6eSHans Rosenfeld static int	iwn4965_get_temperature(struct iwn_softc *);
231fd43cf6eSHans Rosenfeld static int	iwn5000_get_temperature(struct iwn_softc *);
232fd43cf6eSHans Rosenfeld static int	iwn_init_sensitivity(struct iwn_softc *);
233fd43cf6eSHans Rosenfeld static void	iwn_collect_noise(struct iwn_softc *,
234fd43cf6eSHans Rosenfeld 		    const struct iwn_rx_general_stats *);
235fd43cf6eSHans Rosenfeld static int	iwn4965_init_gains(struct iwn_softc *);
236fd43cf6eSHans Rosenfeld static int	iwn5000_init_gains(struct iwn_softc *);
237fd43cf6eSHans Rosenfeld static int	iwn4965_set_gains(struct iwn_softc *);
238fd43cf6eSHans Rosenfeld static int	iwn5000_set_gains(struct iwn_softc *);
239fd43cf6eSHans Rosenfeld static void	iwn_tune_sensitivity(struct iwn_softc *,
240fd43cf6eSHans Rosenfeld 		    const struct iwn_rx_stats *);
241fd43cf6eSHans Rosenfeld static int	iwn_send_sensitivity(struct iwn_softc *);
242fd43cf6eSHans Rosenfeld static int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
243fd43cf6eSHans Rosenfeld static int	iwn5000_runtime_calib(struct iwn_softc *);
244fd43cf6eSHans Rosenfeld 
245fd43cf6eSHans Rosenfeld static int	iwn_config_bt_coex_bluetooth(struct iwn_softc *);
246fd43cf6eSHans Rosenfeld static int	iwn_config_bt_coex_prio_table(struct iwn_softc *);
247fd43cf6eSHans Rosenfeld static int	iwn_config_bt_coex_adv1(struct iwn_softc *);
248fd43cf6eSHans Rosenfeld static int	iwn_config_bt_coex_adv2(struct iwn_softc *);
249fd43cf6eSHans Rosenfeld 
250fd43cf6eSHans Rosenfeld static int	iwn_config(struct iwn_softc *);
251fd43cf6eSHans Rosenfeld static uint16_t	iwn_get_active_dwell_time(struct iwn_softc *, uint16_t,
252fd43cf6eSHans Rosenfeld 		    uint8_t);
253fd43cf6eSHans Rosenfeld static uint16_t	iwn_limit_dwell(struct iwn_softc *, uint16_t);
254fd43cf6eSHans Rosenfeld static uint16_t	iwn_get_passive_dwell_time(struct iwn_softc *, uint16_t);
255fd43cf6eSHans Rosenfeld static int	iwn_scan(struct iwn_softc *, uint16_t);
256fd43cf6eSHans Rosenfeld static int	iwn_auth(struct iwn_softc *);
257fd43cf6eSHans Rosenfeld static int	iwn_run(struct iwn_softc *);
258fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO
259fd43cf6eSHans Rosenfeld static int	iwn_set_key(struct ieee80211com *, struct ieee80211_node *,
260fd43cf6eSHans Rosenfeld 		    struct ieee80211_key *);
261fd43cf6eSHans Rosenfeld static void	iwn_delete_key(struct ieee80211com *, struct ieee80211_node *,
262fd43cf6eSHans Rosenfeld 		    struct ieee80211_key *);
263fd43cf6eSHans Rosenfeld #endif
264fd43cf6eSHans Rosenfeld static int	iwn_wme_update(struct ieee80211com *);
265fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
266fd43cf6eSHans Rosenfeld static int	iwn_ampdu_rx_start(struct ieee80211com *,
267fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t);
268fd43cf6eSHans Rosenfeld static void	iwn_ampdu_rx_stop(struct ieee80211com *,
269fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t);
270fd43cf6eSHans Rosenfeld static int	iwn_ampdu_tx_start(struct ieee80211com *,
271fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t);
272fd43cf6eSHans Rosenfeld static void	iwn_ampdu_tx_stop(struct ieee80211com *,
273fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t);
274fd43cf6eSHans Rosenfeld static void	iwn4965_ampdu_tx_start(struct iwn_softc *,
275fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t, uint16_t);
276fd43cf6eSHans Rosenfeld static void	iwn4965_ampdu_tx_stop(struct iwn_softc *,
277fd43cf6eSHans Rosenfeld 		    uint8_t, uint16_t);
278fd43cf6eSHans Rosenfeld static void	iwn5000_ampdu_tx_start(struct iwn_softc *,
279fd43cf6eSHans Rosenfeld 		    struct ieee80211_node *, uint8_t, uint16_t);
280fd43cf6eSHans Rosenfeld static void	iwn5000_ampdu_tx_stop(struct iwn_softc *,
281fd43cf6eSHans Rosenfeld 		    uint8_t, uint16_t);
282fd43cf6eSHans Rosenfeld #endif
283fd43cf6eSHans Rosenfeld static int	iwn5000_query_calibration(struct iwn_softc *);
284fd43cf6eSHans Rosenfeld static int	iwn5000_send_calibration(struct iwn_softc *);
285fd43cf6eSHans Rosenfeld static int	iwn5000_send_wimax_coex(struct iwn_softc *);
286fd43cf6eSHans Rosenfeld static int	iwn6000_temp_offset_calib(struct iwn_softc *);
287fd43cf6eSHans Rosenfeld static int	iwn2000_temp_offset_calib(struct iwn_softc *);
288fd43cf6eSHans Rosenfeld static int	iwn4965_post_alive(struct iwn_softc *);
289fd43cf6eSHans Rosenfeld static int	iwn5000_post_alive(struct iwn_softc *);
290fd43cf6eSHans Rosenfeld static int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
291fd43cf6eSHans Rosenfeld 		    int);
292fd43cf6eSHans Rosenfeld static int	iwn4965_load_firmware(struct iwn_softc *);
293fd43cf6eSHans Rosenfeld static int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
294fd43cf6eSHans Rosenfeld 		    const uint8_t *, int);
295fd43cf6eSHans Rosenfeld static int	iwn5000_load_firmware(struct iwn_softc *);
296fd43cf6eSHans Rosenfeld static int	iwn_read_firmware_leg(struct iwn_softc *,
297fd43cf6eSHans Rosenfeld 		    struct iwn_fw_info *);
298fd43cf6eSHans Rosenfeld static int	iwn_read_firmware_tlv(struct iwn_softc *,
299fd43cf6eSHans Rosenfeld 		    struct iwn_fw_info *, uint16_t);
300fd43cf6eSHans Rosenfeld static int	iwn_read_firmware(struct iwn_softc *);
301fd43cf6eSHans Rosenfeld static int	iwn_clock_wait(struct iwn_softc *);
302fd43cf6eSHans Rosenfeld static int	iwn_apm_init(struct iwn_softc *);
303fd43cf6eSHans Rosenfeld static void	iwn_apm_stop_master(struct iwn_softc *);
304fd43cf6eSHans Rosenfeld static void	iwn_apm_stop(struct iwn_softc *);
305fd43cf6eSHans Rosenfeld static int	iwn4965_nic_config(struct iwn_softc *);
306fd43cf6eSHans Rosenfeld static int	iwn5000_nic_config(struct iwn_softc *);
307fd43cf6eSHans Rosenfeld static int	iwn_hw_prepare(struct iwn_softc *);
308fd43cf6eSHans Rosenfeld static int	iwn_hw_init(struct iwn_softc *);
309fd43cf6eSHans Rosenfeld static void	iwn_hw_stop(struct iwn_softc *, boolean_t);
310fd43cf6eSHans Rosenfeld static int	iwn_init(struct iwn_softc *);
311fd43cf6eSHans Rosenfeld static void	iwn_abort_scan(void *);
312fd43cf6eSHans Rosenfeld static void	iwn_periodic(void *);
313fd43cf6eSHans Rosenfeld static int	iwn_fast_recover(struct iwn_softc *);
314fd43cf6eSHans Rosenfeld 
315fd43cf6eSHans Rosenfeld static uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, uint32_t);
316fd43cf6eSHans Rosenfeld static uint8_t	*ieee80211_add_rates(uint8_t *,
317fd43cf6eSHans Rosenfeld     const struct ieee80211_rateset *);
318fd43cf6eSHans Rosenfeld static uint8_t	*ieee80211_add_xrates(uint8_t *,
319fd43cf6eSHans Rosenfeld     const struct ieee80211_rateset *);
320fd43cf6eSHans Rosenfeld 
321fd43cf6eSHans Rosenfeld static void	iwn_fix_channel(struct iwn_softc *, mblk_t *,
322fd43cf6eSHans Rosenfeld 		    struct iwn_rx_stat *);
323fd43cf6eSHans Rosenfeld 
324fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG
325fd43cf6eSHans Rosenfeld 
326fd43cf6eSHans Rosenfeld #define	IWN_DBG(...)	iwn_dbg("?" __VA_ARGS__)
327fd43cf6eSHans Rosenfeld 
328fd43cf6eSHans Rosenfeld static int iwn_dbg_print = 0;
329fd43cf6eSHans Rosenfeld 
330fd43cf6eSHans Rosenfeld static void
iwn_dbg(const char * fmt,...)331fd43cf6eSHans Rosenfeld iwn_dbg(const char *fmt, ...)
332fd43cf6eSHans Rosenfeld {
333fd43cf6eSHans Rosenfeld 	va_list	ap;
334fd43cf6eSHans Rosenfeld 
335fd43cf6eSHans Rosenfeld 	if (iwn_dbg_print != 0) {
336fd43cf6eSHans Rosenfeld 		va_start(ap, fmt);
337fd43cf6eSHans Rosenfeld 		vcmn_err(CE_CONT, fmt, ap);
338fd43cf6eSHans Rosenfeld 		va_end(ap);
339fd43cf6eSHans Rosenfeld 	}
340fd43cf6eSHans Rosenfeld }
341fd43cf6eSHans Rosenfeld 
342fd43cf6eSHans Rosenfeld #else
343fd43cf6eSHans Rosenfeld #define	IWN_DBG(...)
344fd43cf6eSHans Rosenfeld #endif
345fd43cf6eSHans Rosenfeld 
346fd43cf6eSHans Rosenfeld /*
347fd43cf6eSHans Rosenfeld  * tunables
348fd43cf6eSHans Rosenfeld  */
349fd43cf6eSHans Rosenfeld 
350fd43cf6eSHans Rosenfeld /*
351fd43cf6eSHans Rosenfeld  * enable 5GHz scanning
352fd43cf6eSHans Rosenfeld  */
353fd43cf6eSHans Rosenfeld int iwn_enable_5ghz = 1;
354fd43cf6eSHans Rosenfeld 
355fd43cf6eSHans Rosenfeld /*
356fd43cf6eSHans Rosenfeld  * If more than 50 consecutive beacons are missed,
357fd43cf6eSHans Rosenfeld  * we've probably lost our connection.
358fd43cf6eSHans Rosenfeld  * If more than 5 consecutive beacons are missed,
359fd43cf6eSHans Rosenfeld  * reinitialize the sensitivity state machine.
360fd43cf6eSHans Rosenfeld  */
361fd43cf6eSHans Rosenfeld int iwn_beacons_missed_disconnect = 50;
362fd43cf6eSHans Rosenfeld int iwn_beacons_missed_sensitivity = 5;
363fd43cf6eSHans Rosenfeld 
364fd43cf6eSHans Rosenfeld /*
365fd43cf6eSHans Rosenfeld  * iwn_periodic interval, in units of msec
366fd43cf6eSHans Rosenfeld  */
367fd43cf6eSHans Rosenfeld int iwn_periodic_interval = 100;
368fd43cf6eSHans Rosenfeld 
369fd43cf6eSHans Rosenfeld /*
370fd43cf6eSHans Rosenfeld  * scan timeout in sec
371fd43cf6eSHans Rosenfeld  */
372fd43cf6eSHans Rosenfeld int iwn_scan_timeout = 20;
373fd43cf6eSHans Rosenfeld 
374fd43cf6eSHans Rosenfeld static ether_addr_t etherbroadcastaddr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
375fd43cf6eSHans Rosenfeld 
376fd43cf6eSHans Rosenfeld static void *iwn_state = NULL;
377fd43cf6eSHans Rosenfeld 
378fd43cf6eSHans Rosenfeld /*
379fd43cf6eSHans Rosenfeld  * Mac Call Back entries
380fd43cf6eSHans Rosenfeld  */
381fd43cf6eSHans Rosenfeld static int	iwn_m_stat(void *, uint_t, uint64_t *);
382fd43cf6eSHans Rosenfeld static int	iwn_m_start(void *);
383fd43cf6eSHans Rosenfeld static void	iwn_m_stop(void *);
384fd43cf6eSHans Rosenfeld static int	iwn_m_unicst(void *, const uint8_t *);
385fd43cf6eSHans Rosenfeld static int	iwn_m_multicst(void *, boolean_t, const uint8_t *);
386fd43cf6eSHans Rosenfeld static int	iwn_m_promisc(void *, boolean_t);
387fd43cf6eSHans Rosenfeld static mblk_t	*iwn_m_tx(void *, mblk_t *);
388fd43cf6eSHans Rosenfeld static void	iwn_m_ioctl(void *, queue_t *, mblk_t *);
389fd43cf6eSHans Rosenfeld static int	iwn_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
390fd43cf6eSHans Rosenfeld     const void *);
391fd43cf6eSHans Rosenfeld static int	iwn_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
392fd43cf6eSHans Rosenfeld     void *);
393fd43cf6eSHans Rosenfeld static void	iwn_m_propinfo(void *, const char *, mac_prop_id_t,
394fd43cf6eSHans Rosenfeld     mac_prop_info_handle_t);
395fd43cf6eSHans Rosenfeld 
396fd43cf6eSHans Rosenfeld mac_callbacks_t	iwn_m_callbacks = {
397fd43cf6eSHans Rosenfeld 	.mc_callbacks	= MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
398fd43cf6eSHans Rosenfeld 	.mc_getstat	= iwn_m_stat,
399fd43cf6eSHans Rosenfeld 	.mc_start	= iwn_m_start,
400fd43cf6eSHans Rosenfeld 	.mc_stop	= iwn_m_stop,
401fd43cf6eSHans Rosenfeld 	.mc_setpromisc	= iwn_m_promisc,
402fd43cf6eSHans Rosenfeld 	.mc_multicst	= iwn_m_multicst,
403fd43cf6eSHans Rosenfeld 	.mc_unicst	= iwn_m_unicst,
404fd43cf6eSHans Rosenfeld 	.mc_tx		= iwn_m_tx,
405fd43cf6eSHans Rosenfeld 	.mc_reserved	= NULL,
406fd43cf6eSHans Rosenfeld 	.mc_ioctl	= iwn_m_ioctl,
407fd43cf6eSHans Rosenfeld 	.mc_getcapab	= NULL,
408fd43cf6eSHans Rosenfeld 	.mc_open	= NULL,
409fd43cf6eSHans Rosenfeld 	.mc_close	= NULL,
410fd43cf6eSHans Rosenfeld 	.mc_setprop	= iwn_m_setprop,
411fd43cf6eSHans Rosenfeld 	.mc_getprop	= iwn_m_getprop,
412fd43cf6eSHans Rosenfeld 	.mc_propinfo	= iwn_m_propinfo
413fd43cf6eSHans Rosenfeld };
414fd43cf6eSHans Rosenfeld 
415fd43cf6eSHans Rosenfeld static inline uint32_t
iwn_read(struct iwn_softc * sc,int reg)416fd43cf6eSHans Rosenfeld iwn_read(struct iwn_softc *sc, int reg)
417fd43cf6eSHans Rosenfeld {
418fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
419fd43cf6eSHans Rosenfeld 	return (ddi_get32(sc->sc_regh, (uint32_t *)(sc->sc_base + reg)));
420fd43cf6eSHans Rosenfeld }
421fd43cf6eSHans Rosenfeld 
422fd43cf6eSHans Rosenfeld static inline void
iwn_write(struct iwn_softc * sc,int reg,uint32_t val)423fd43cf6eSHans Rosenfeld iwn_write(struct iwn_softc *sc, int reg, uint32_t val)
424fd43cf6eSHans Rosenfeld {
425fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
426fd43cf6eSHans Rosenfeld 	ddi_put32(sc->sc_regh, (uint32_t *)(sc->sc_base + reg), val);
427fd43cf6eSHans Rosenfeld }
428fd43cf6eSHans Rosenfeld 
429fd43cf6eSHans Rosenfeld static inline void
iwn_write_1(struct iwn_softc * sc,int reg,uint8_t val)430fd43cf6eSHans Rosenfeld iwn_write_1(struct iwn_softc *sc, int reg, uint8_t val)
431fd43cf6eSHans Rosenfeld {
432fd43cf6eSHans Rosenfeld 	ddi_put8(sc->sc_regh, (uint8_t *)(sc->sc_base + reg), val);
433fd43cf6eSHans Rosenfeld }
434fd43cf6eSHans Rosenfeld 
435fd43cf6eSHans Rosenfeld static void
iwn_kstat_create(struct iwn_softc * sc,const char * name,size_t size,kstat_t ** ks,void ** data)436fd43cf6eSHans Rosenfeld iwn_kstat_create(struct iwn_softc *sc, const char *name, size_t size,
437fd43cf6eSHans Rosenfeld     kstat_t **ks, void **data)
438fd43cf6eSHans Rosenfeld {
439fd43cf6eSHans Rosenfeld 	*ks = kstat_create(ddi_driver_name(sc->sc_dip),
440fd43cf6eSHans Rosenfeld 	    ddi_get_instance(sc->sc_dip), name, "misc", KSTAT_TYPE_NAMED,
441fd43cf6eSHans Rosenfeld 	    size / sizeof (kstat_named_t), 0);
442fd43cf6eSHans Rosenfeld 	if (*ks == NULL)
443fd43cf6eSHans Rosenfeld 		*data = kmem_zalloc(size, KM_SLEEP);
444fd43cf6eSHans Rosenfeld 	else
445fd43cf6eSHans Rosenfeld 		*data = (*ks)->ks_data;
446fd43cf6eSHans Rosenfeld }
447fd43cf6eSHans Rosenfeld 
448fd43cf6eSHans Rosenfeld static void
iwn_kstat_free(kstat_t * ks,void * data,size_t size)449fd43cf6eSHans Rosenfeld iwn_kstat_free(kstat_t *ks, void *data, size_t size)
450fd43cf6eSHans Rosenfeld {
451fd43cf6eSHans Rosenfeld 	if (ks != NULL)
452fd43cf6eSHans Rosenfeld 		kstat_delete(ks);
453fd43cf6eSHans Rosenfeld 	else if (data != NULL)
454fd43cf6eSHans Rosenfeld 		kmem_free(data, size);
455fd43cf6eSHans Rosenfeld }
456fd43cf6eSHans Rosenfeld 
457fd43cf6eSHans Rosenfeld static void
iwn_kstat_init(struct iwn_softc * sc)458fd43cf6eSHans Rosenfeld iwn_kstat_init(struct iwn_softc *sc)
459fd43cf6eSHans Rosenfeld {
460fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_misc != NULL)
461fd43cf6eSHans Rosenfeld 		sc->sc_ks_misc->ks_lock = &sc->sc_mtx;
462fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_ant != NULL)
463fd43cf6eSHans Rosenfeld 		sc->sc_ks_ant->ks_lock = &sc->sc_mtx;
464fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_sens != NULL)
465fd43cf6eSHans Rosenfeld 		sc->sc_ks_sens->ks_lock = &sc->sc_mtx;
466fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_timing != NULL)
467fd43cf6eSHans Rosenfeld 		sc->sc_ks_timing->ks_lock = &sc->sc_mtx;
468fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_edca != NULL)
469fd43cf6eSHans Rosenfeld 		sc->sc_ks_edca->ks_lock = &sc->sc_mtx;
470fd43cf6eSHans Rosenfeld 
471fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_misc->temp,
472fd43cf6eSHans Rosenfeld 	    "temperature", KSTAT_DATA_ULONG);
473fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_misc->crit_temp,
474fd43cf6eSHans Rosenfeld 	    "critical temperature", KSTAT_DATA_ULONG);
475fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_misc->pslevel,
476fd43cf6eSHans Rosenfeld 	    "power saving level", KSTAT_DATA_ULONG);
477fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_misc->noise,
478fd43cf6eSHans Rosenfeld 	    "noise", KSTAT_DATA_LONG);
479fd43cf6eSHans Rosenfeld 
480fd43cf6eSHans Rosenfeld 
481fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->tx_ant,
482fd43cf6eSHans Rosenfeld 	    "TX mask", KSTAT_DATA_ULONG);
483fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->rx_ant,
484fd43cf6eSHans Rosenfeld 	    "RX mask", KSTAT_DATA_ULONG);
485fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->conn_ant,
486fd43cf6eSHans Rosenfeld 	    "connected mask", KSTAT_DATA_ULONG);
487fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->gain[0],
488fd43cf6eSHans Rosenfeld 	    "gain A", KSTAT_DATA_ULONG);
489fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->gain[1],
490fd43cf6eSHans Rosenfeld 	    "gain B", KSTAT_DATA_ULONG);
491fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_ant->gain[2],
492fd43cf6eSHans Rosenfeld 	    "gain C", KSTAT_DATA_ULONG);
493fd43cf6eSHans Rosenfeld 
494fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->ofdm_x1,
495fd43cf6eSHans Rosenfeld 	    "OFDM X1", KSTAT_DATA_ULONG);
496fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->ofdm_mrc_x1,
497fd43cf6eSHans Rosenfeld 	    "OFDM MRC X1", KSTAT_DATA_ULONG);
498fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->ofdm_x4,
499fd43cf6eSHans Rosenfeld 	    "OFDM X4", KSTAT_DATA_ULONG);
500fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->ofdm_mrc_x4,
501fd43cf6eSHans Rosenfeld 	    "OFDM MRC X4", KSTAT_DATA_ULONG);
502fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->cck_x4,
503fd43cf6eSHans Rosenfeld 	    "CCK X4", KSTAT_DATA_ULONG);
504fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->cck_mrc_x4,
505fd43cf6eSHans Rosenfeld 	    "CCK MRC X4", KSTAT_DATA_ULONG);
506fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_sens->energy_cck,
507fd43cf6eSHans Rosenfeld 	    "energy CCK", KSTAT_DATA_ULONG);
508fd43cf6eSHans Rosenfeld 
509fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_timing->bintval,
510fd43cf6eSHans Rosenfeld 	    "bintval", KSTAT_DATA_ULONG);
511fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_timing->tstamp,
512fd43cf6eSHans Rosenfeld 	    "timestamp", KSTAT_DATA_ULONGLONG);
513fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_timing->init,
514fd43cf6eSHans Rosenfeld 	    "init", KSTAT_DATA_ULONG);
515fd43cf6eSHans Rosenfeld 
516fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[0].cwmin,
517fd43cf6eSHans Rosenfeld 	    "background cwmin", KSTAT_DATA_ULONG);
518fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[0].cwmax,
519fd43cf6eSHans Rosenfeld 	    "background cwmax", KSTAT_DATA_ULONG);
520fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[0].aifsn,
521fd43cf6eSHans Rosenfeld 	    "background aifsn", KSTAT_DATA_ULONG);
522fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[0].txop,
523fd43cf6eSHans Rosenfeld 	    "background txop", KSTAT_DATA_ULONG);
524fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[1].cwmin,
525fd43cf6eSHans Rosenfeld 	    "best effort cwmin", KSTAT_DATA_ULONG);
526fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[1].cwmax,
527fd43cf6eSHans Rosenfeld 	    "best effort cwmax", KSTAT_DATA_ULONG);
528fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[1].aifsn,
529fd43cf6eSHans Rosenfeld 	    "best effort aifsn", KSTAT_DATA_ULONG);
530fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[1].txop,
531fd43cf6eSHans Rosenfeld 	    "best effort txop", KSTAT_DATA_ULONG);
532fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[2].cwmin,
533fd43cf6eSHans Rosenfeld 	    "video cwmin", KSTAT_DATA_ULONG);
534fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[2].cwmax,
535fd43cf6eSHans Rosenfeld 	    "video cwmax", KSTAT_DATA_ULONG);
536fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[2].aifsn,
537fd43cf6eSHans Rosenfeld 	    "video aifsn", KSTAT_DATA_ULONG);
538fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[2].txop,
539fd43cf6eSHans Rosenfeld 	    "video txop", KSTAT_DATA_ULONG);
540fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[3].cwmin,
541fd43cf6eSHans Rosenfeld 	    "voice cwmin", KSTAT_DATA_ULONG);
542fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[3].cwmax,
543fd43cf6eSHans Rosenfeld 	    "voice cwmax", KSTAT_DATA_ULONG);
544fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[3].aifsn,
545fd43cf6eSHans Rosenfeld 	    "voice aifsn", KSTAT_DATA_ULONG);
546fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_edca->ac[3].txop,
547fd43cf6eSHans Rosenfeld 	    "voice txop", KSTAT_DATA_ULONG);
548fd43cf6eSHans Rosenfeld }
549fd43cf6eSHans Rosenfeld 
550fd43cf6eSHans Rosenfeld static void
iwn_kstat_init_2000(struct iwn_softc * sc)551fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(struct iwn_softc *sc)
552fd43cf6eSHans Rosenfeld {
553fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_toff != NULL)
554fd43cf6eSHans Rosenfeld 		sc->sc_ks_toff->ks_lock = &sc->sc_mtx;
555fd43cf6eSHans Rosenfeld 
556fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_toff.t2000->toff_lo,
557fd43cf6eSHans Rosenfeld 	    "temperature offset low", KSTAT_DATA_LONG);
558fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_toff.t2000->toff_hi,
559fd43cf6eSHans Rosenfeld 	    "temperature offset high", KSTAT_DATA_LONG);
560fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_toff.t2000->volt,
561fd43cf6eSHans Rosenfeld 	    "reference voltage", KSTAT_DATA_LONG);
562fd43cf6eSHans Rosenfeld }
563fd43cf6eSHans Rosenfeld 
564fd43cf6eSHans Rosenfeld static void
iwn_kstat_init_4965(struct iwn_softc * sc)565fd43cf6eSHans Rosenfeld iwn_kstat_init_4965(struct iwn_softc *sc)
566fd43cf6eSHans Rosenfeld {
567fd43cf6eSHans Rosenfeld 	int i, r;
568fd43cf6eSHans Rosenfeld 
569fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_txpower != NULL)
570fd43cf6eSHans Rosenfeld 		sc->sc_ks_txpower->ks_lock = &sc->sc_mtx;
571fd43cf6eSHans Rosenfeld 
572fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_txpower->vdiff,
573fd43cf6eSHans Rosenfeld 	    "voltage comp", KSTAT_DATA_LONG);
574fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_txpower->chan,
575fd43cf6eSHans Rosenfeld 	    "channel", KSTAT_DATA_LONG);
576fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_txpower->group,
577fd43cf6eSHans Rosenfeld 	    "attenuation group", KSTAT_DATA_LONG);
578fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_txpower->subband,
579fd43cf6eSHans Rosenfeld 	    "sub-band", KSTAT_DATA_LONG);
580fd43cf6eSHans Rosenfeld 	for (i = 0; i != 2; i++) {
581fd43cf6eSHans Rosenfeld 		char tmp[KSTAT_STRLEN];
582fd43cf6eSHans Rosenfeld 
583fd43cf6eSHans Rosenfeld 		(void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d power", i);
584fd43cf6eSHans Rosenfeld 		kstat_named_init(&sc->sc_txpower->txchain[i].power,
585fd43cf6eSHans Rosenfeld 		    tmp, KSTAT_DATA_LONG);
586fd43cf6eSHans Rosenfeld 
587fd43cf6eSHans Rosenfeld 		(void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d gain", i);
588fd43cf6eSHans Rosenfeld 		kstat_named_init(&sc->sc_txpower->txchain[i].gain,
589fd43cf6eSHans Rosenfeld 		    tmp, KSTAT_DATA_LONG);
590fd43cf6eSHans Rosenfeld 
591fd43cf6eSHans Rosenfeld 		(void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d temperature", i);
592fd43cf6eSHans Rosenfeld 		kstat_named_init(&sc->sc_txpower->txchain[i].temp,
593fd43cf6eSHans Rosenfeld 		    tmp, KSTAT_DATA_LONG);
594fd43cf6eSHans Rosenfeld 
595fd43cf6eSHans Rosenfeld 		(void) snprintf(tmp, KSTAT_STRLEN - 1,
596fd43cf6eSHans Rosenfeld 		    "Ant %d temperature compensation", i);
597fd43cf6eSHans Rosenfeld 		kstat_named_init(&sc->sc_txpower->txchain[i].tcomp,
598fd43cf6eSHans Rosenfeld 		    tmp, KSTAT_DATA_LONG);
599fd43cf6eSHans Rosenfeld 
600843ead08SRichard Lowe 		for (r = 0; r <= IWN_RIDX_MAX; r++) {
601fd43cf6eSHans Rosenfeld 			(void) snprintf(tmp, KSTAT_STRLEN - 1,
602fd43cf6eSHans Rosenfeld 			    "Ant %d Rate %d RF gain", i, r);
603fd43cf6eSHans Rosenfeld 			kstat_named_init(
604fd43cf6eSHans Rosenfeld 			    &sc->sc_txpower->txchain[i].rate[r].rf_gain,
605fd43cf6eSHans Rosenfeld 			    tmp, KSTAT_DATA_LONG);
606fd43cf6eSHans Rosenfeld 
607fd43cf6eSHans Rosenfeld 			(void) snprintf(tmp, KSTAT_STRLEN - 1,
608fd43cf6eSHans Rosenfeld 			    "Ant %d Rate %d DSP gain", i, r);
609fd43cf6eSHans Rosenfeld 			kstat_named_init(
610fd43cf6eSHans Rosenfeld 			    &sc->sc_txpower->txchain[0].rate[0].dsp_gain,
611fd43cf6eSHans Rosenfeld 			    tmp, KSTAT_DATA_LONG);
612fd43cf6eSHans Rosenfeld 		}
613fd43cf6eSHans Rosenfeld 	}
614fd43cf6eSHans Rosenfeld }
615fd43cf6eSHans Rosenfeld 
616fd43cf6eSHans Rosenfeld static void
iwn_kstat_init_6000(struct iwn_softc * sc)617fd43cf6eSHans Rosenfeld iwn_kstat_init_6000(struct iwn_softc *sc)
618fd43cf6eSHans Rosenfeld {
619fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_toff != NULL)
620fd43cf6eSHans Rosenfeld 		sc->sc_ks_toff->ks_lock = &sc->sc_mtx;
621fd43cf6eSHans Rosenfeld 
622fd43cf6eSHans Rosenfeld 	kstat_named_init(&sc->sc_toff.t6000->toff,
623fd43cf6eSHans Rosenfeld 	    "temperature offset", KSTAT_DATA_LONG);
624fd43cf6eSHans Rosenfeld }
625fd43cf6eSHans Rosenfeld 
626fd43cf6eSHans Rosenfeld static void
iwn_intr_teardown(struct iwn_softc * sc)627fd43cf6eSHans Rosenfeld iwn_intr_teardown(struct iwn_softc *sc)
628fd43cf6eSHans Rosenfeld {
629fd43cf6eSHans Rosenfeld 	if (sc->sc_intr_htable != NULL) {
630fd43cf6eSHans Rosenfeld 		if ((sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) != 0) {
631fd43cf6eSHans Rosenfeld 			(void) ddi_intr_block_disable(sc->sc_intr_htable,
632fd43cf6eSHans Rosenfeld 			    sc->sc_intr_count);
633fd43cf6eSHans Rosenfeld 		} else {
634fd43cf6eSHans Rosenfeld 			(void) ddi_intr_disable(sc->sc_intr_htable[0]);
635fd43cf6eSHans Rosenfeld 		}
636fd43cf6eSHans Rosenfeld 		(void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
637fd43cf6eSHans Rosenfeld 		(void) ddi_intr_free(sc->sc_intr_htable[0]);
638fd43cf6eSHans Rosenfeld 		sc->sc_intr_htable[0] = NULL;
639fd43cf6eSHans Rosenfeld 
640fd43cf6eSHans Rosenfeld 		kmem_free(sc->sc_intr_htable, sc->sc_intr_size);
641fd43cf6eSHans Rosenfeld 		sc->sc_intr_size = 0;
642fd43cf6eSHans Rosenfeld 		sc->sc_intr_htable = NULL;
643fd43cf6eSHans Rosenfeld 	}
644fd43cf6eSHans Rosenfeld }
645fd43cf6eSHans Rosenfeld 
646fd43cf6eSHans Rosenfeld static int
iwn_intr_add(struct iwn_softc * sc,int intr_type)647fd43cf6eSHans Rosenfeld iwn_intr_add(struct iwn_softc *sc, int intr_type)
648fd43cf6eSHans Rosenfeld {
649fd43cf6eSHans Rosenfeld 	int ni, na;
650fd43cf6eSHans Rosenfeld 	int ret;
651fd43cf6eSHans Rosenfeld 	char *func;
652fd43cf6eSHans Rosenfeld 
653fd43cf6eSHans Rosenfeld 	if (ddi_intr_get_nintrs(sc->sc_dip, intr_type, &ni) != DDI_SUCCESS)
654fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
655fd43cf6eSHans Rosenfeld 
656fd43cf6eSHans Rosenfeld 
657fd43cf6eSHans Rosenfeld 	if (ddi_intr_get_navail(sc->sc_dip, intr_type, &na) != DDI_SUCCESS)
658fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
659fd43cf6eSHans Rosenfeld 
660fd43cf6eSHans Rosenfeld 	sc->sc_intr_size = sizeof (ddi_intr_handle_t);
661fd43cf6eSHans Rosenfeld 	sc->sc_intr_htable = kmem_zalloc(sc->sc_intr_size, KM_SLEEP);
662fd43cf6eSHans Rosenfeld 
663fd43cf6eSHans Rosenfeld 	ret = ddi_intr_alloc(sc->sc_dip, sc->sc_intr_htable, intr_type, 0, 1,
664fd43cf6eSHans Rosenfeld 	    &sc->sc_intr_count, DDI_INTR_ALLOC_STRICT);
665fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
666fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_alloc() failed");
667fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
668fd43cf6eSHans Rosenfeld 	}
669fd43cf6eSHans Rosenfeld 
670fd43cf6eSHans Rosenfeld 	ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri);
671fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
672fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_get_pri() failed");
673fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
674fd43cf6eSHans Rosenfeld 	}
675fd43cf6eSHans Rosenfeld 
676fd43cf6eSHans Rosenfeld 	ret = ddi_intr_add_handler(sc->sc_intr_htable[0], iwn_intr, (caddr_t)sc,
677fd43cf6eSHans Rosenfeld 	    NULL);
678fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
679fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_add_handler() failed");
680fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
681fd43cf6eSHans Rosenfeld 	}
682fd43cf6eSHans Rosenfeld 
683fd43cf6eSHans Rosenfeld 	ret = ddi_intr_get_cap(sc->sc_intr_htable[0], &sc->sc_intr_cap);
684fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
685fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_get_cap() failed");
686fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
687fd43cf6eSHans Rosenfeld 	}
688fd43cf6eSHans Rosenfeld 
689fd43cf6eSHans Rosenfeld 	if ((sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) != 0) {
690fd43cf6eSHans Rosenfeld 		ret = ddi_intr_block_enable(sc->sc_intr_htable,
691fd43cf6eSHans Rosenfeld 		    sc->sc_intr_count);
692fd43cf6eSHans Rosenfeld 		func = "ddi_intr_enable_block";
693fd43cf6eSHans Rosenfeld 	} else {
694fd43cf6eSHans Rosenfeld 		ret = ddi_intr_enable(sc->sc_intr_htable[0]);
695fd43cf6eSHans Rosenfeld 		func = "ddi_intr_enable";
696fd43cf6eSHans Rosenfeld 	}
697fd43cf6eSHans Rosenfeld 
698fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
699fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!%s() failed", func);
700fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
701fd43cf6eSHans Rosenfeld 	}
702fd43cf6eSHans Rosenfeld 
703fd43cf6eSHans Rosenfeld 	return (DDI_SUCCESS);
704fd43cf6eSHans Rosenfeld }
705fd43cf6eSHans Rosenfeld 
706fd43cf6eSHans Rosenfeld static int
iwn_intr_setup(struct iwn_softc * sc)707fd43cf6eSHans Rosenfeld iwn_intr_setup(struct iwn_softc *sc)
708fd43cf6eSHans Rosenfeld {
709fd43cf6eSHans Rosenfeld 	int intr_type;
710fd43cf6eSHans Rosenfeld 	int ret;
711fd43cf6eSHans Rosenfeld 
712fd43cf6eSHans Rosenfeld 	ret = ddi_intr_get_supported_types(sc->sc_dip, &intr_type);
713fd43cf6eSHans Rosenfeld 	if (ret != DDI_SUCCESS) {
714fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
715fd43cf6eSHans Rosenfeld 		    "!ddi_intr_get_supported_types() failed");
716fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
717fd43cf6eSHans Rosenfeld 	}
718fd43cf6eSHans Rosenfeld 
719fd43cf6eSHans Rosenfeld 	if ((intr_type & DDI_INTR_TYPE_MSIX)) {
720fd43cf6eSHans Rosenfeld 		if (iwn_intr_add(sc, DDI_INTR_TYPE_MSIX) == DDI_SUCCESS)
721fd43cf6eSHans Rosenfeld 			return (DDI_SUCCESS);
722fd43cf6eSHans Rosenfeld 		iwn_intr_teardown(sc);
723fd43cf6eSHans Rosenfeld 	}
724fd43cf6eSHans Rosenfeld 
725fd43cf6eSHans Rosenfeld 	if ((intr_type & DDI_INTR_TYPE_MSI)) {
726fd43cf6eSHans Rosenfeld 		if (iwn_intr_add(sc, DDI_INTR_TYPE_MSI) == DDI_SUCCESS)
727fd43cf6eSHans Rosenfeld 			return (DDI_SUCCESS);
728fd43cf6eSHans Rosenfeld 		iwn_intr_teardown(sc);
729fd43cf6eSHans Rosenfeld 	}
730fd43cf6eSHans Rosenfeld 
731fd43cf6eSHans Rosenfeld 	if ((intr_type & DDI_INTR_TYPE_FIXED)) {
732fd43cf6eSHans Rosenfeld 		if (iwn_intr_add(sc, DDI_INTR_TYPE_FIXED) == DDI_SUCCESS)
733fd43cf6eSHans Rosenfeld 			return (DDI_SUCCESS);
734fd43cf6eSHans Rosenfeld 		iwn_intr_teardown(sc);
735fd43cf6eSHans Rosenfeld 	}
736fd43cf6eSHans Rosenfeld 
737fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN, "!iwn_intr_setup() failed");
738fd43cf6eSHans Rosenfeld 	return (DDI_FAILURE);
739fd43cf6eSHans Rosenfeld }
740fd43cf6eSHans Rosenfeld 
741fd43cf6eSHans Rosenfeld static int
iwn_pci_get_capability(ddi_acc_handle_t pcih,int cap,int * cap_off)742fd43cf6eSHans Rosenfeld iwn_pci_get_capability(ddi_acc_handle_t pcih, int cap, int *cap_off)
743fd43cf6eSHans Rosenfeld {
744fd43cf6eSHans Rosenfeld 	uint8_t ptr;
745fd43cf6eSHans Rosenfeld 	uint8_t val;
746fd43cf6eSHans Rosenfeld 
747fd43cf6eSHans Rosenfeld 	for (ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
748fd43cf6eSHans Rosenfeld 	    ptr != 0 && ptr != 0xff;
749fd43cf6eSHans Rosenfeld 	    ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR)) {
750fd43cf6eSHans Rosenfeld 		val = pci_config_get8(pcih, ptr + PCIE_CAP_ID);
751fd43cf6eSHans Rosenfeld 		if (val == 0xff)
752fd43cf6eSHans Rosenfeld 			return (DDI_FAILURE);
753fd43cf6eSHans Rosenfeld 
754fd43cf6eSHans Rosenfeld 		if (cap != val)
755fd43cf6eSHans Rosenfeld 			continue;
756