xref: /illumos-gate/usr/src/uts/common/io/iwn/if_iwn.c (revision 35060ceaa548c2d6fa63812e06a1a0f8dc4a06b0)
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;
756fd43cf6eSHans Rosenfeld 
757fd43cf6eSHans Rosenfeld 		*cap_off = ptr;
758fd43cf6eSHans Rosenfeld 		return (DDI_SUCCESS);
759fd43cf6eSHans Rosenfeld 	}
760fd43cf6eSHans Rosenfeld 
761fd43cf6eSHans Rosenfeld 	return (DDI_FAILURE);
762fd43cf6eSHans Rosenfeld }
763fd43cf6eSHans Rosenfeld 
764fd43cf6eSHans Rosenfeld static int
iwn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)765fd43cf6eSHans Rosenfeld iwn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
766fd43cf6eSHans Rosenfeld {
767fd43cf6eSHans Rosenfeld 	int instance;
768fd43cf6eSHans Rosenfeld 
769fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
770fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic;
771fd43cf6eSHans Rosenfeld 	char strbuf[32];
772fd43cf6eSHans Rosenfeld 	wifi_data_t wd = { 0 };
773fd43cf6eSHans Rosenfeld 	mac_register_t *macp;
774fd43cf6eSHans Rosenfeld 	uint32_t reg;
775fd43cf6eSHans Rosenfeld 	int i, error;
776fd43cf6eSHans Rosenfeld 
777fd43cf6eSHans Rosenfeld 	switch (cmd) {
778fd43cf6eSHans Rosenfeld 	case DDI_ATTACH:
779fd43cf6eSHans Rosenfeld 		break;
780fd43cf6eSHans Rosenfeld 
781fd43cf6eSHans Rosenfeld 	case DDI_RESUME:
782fd43cf6eSHans Rosenfeld 		instance = ddi_get_instance(dip);
783fd43cf6eSHans Rosenfeld 		sc = ddi_get_soft_state(iwn_state,
784fd43cf6eSHans Rosenfeld 		    instance);
785fd43cf6eSHans Rosenfeld 		ASSERT(sc != NULL);
786fd43cf6eSHans Rosenfeld 
787fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_RUNNING) {
788fd43cf6eSHans Rosenfeld 			(void) iwn_init(sc);
789fd43cf6eSHans Rosenfeld 		}
790fd43cf6eSHans Rosenfeld 
791fd43cf6eSHans Rosenfeld 		sc->sc_flags &= ~IWN_FLAG_SUSPEND;
792fd43cf6eSHans Rosenfeld 
793fd43cf6eSHans Rosenfeld 		return (DDI_SUCCESS);
794fd43cf6eSHans Rosenfeld 	default:
795fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
796fd43cf6eSHans Rosenfeld 	}
797fd43cf6eSHans Rosenfeld 
798fd43cf6eSHans Rosenfeld 	instance = ddi_get_instance(dip);
799fd43cf6eSHans Rosenfeld 
800fd43cf6eSHans Rosenfeld 	if (ddi_soft_state_zalloc(iwn_state, instance) != DDI_SUCCESS) {
801fd43cf6eSHans Rosenfeld 		dev_err(dip, CE_WARN, "!ddi_soft_state_zalloc() failed");
802fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
803fd43cf6eSHans Rosenfeld 	}
804fd43cf6eSHans Rosenfeld 
805fd43cf6eSHans Rosenfeld 	sc = ddi_get_soft_state(iwn_state, instance);
806fd43cf6eSHans Rosenfeld 	ddi_set_driver_private(dip, (caddr_t)sc);
807fd43cf6eSHans Rosenfeld 
808fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
809fd43cf6eSHans Rosenfeld 
810fd43cf6eSHans Rosenfeld 	sc->sc_dip = dip;
811fd43cf6eSHans Rosenfeld 
812fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "hw_state", sizeof (struct iwn_ks_misc),
813fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_misc, (void **)&sc->sc_misc);
814fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "antennas", sizeof (struct iwn_ks_ant),
815fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_ant, (void **)&sc->sc_ant);
816fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "sensitivity", sizeof (struct iwn_ks_sens),
817fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_sens, (void **)&sc->sc_sens);
818fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "timing", sizeof (struct iwn_ks_timing),
819fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_timing, (void **)&sc->sc_timing);
820fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "edca", sizeof (struct iwn_ks_edca),
821fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_edca, (void **)&sc->sc_edca);
822fd43cf6eSHans Rosenfeld 
823fd43cf6eSHans Rosenfeld 	if (pci_config_setup(dip, &sc->sc_pcih) != DDI_SUCCESS) {
824fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!pci_config_setup() failed");
825fd43cf6eSHans Rosenfeld 		goto fail_pci_config;
826fd43cf6eSHans Rosenfeld 	}
827fd43cf6eSHans Rosenfeld 
828fd43cf6eSHans Rosenfeld 	/*
829fd43cf6eSHans Rosenfeld 	 * Get the offset of the PCI Express Capability Structure in PCI
830fd43cf6eSHans Rosenfeld 	 * Configuration Space.
831fd43cf6eSHans Rosenfeld 	 */
832fd43cf6eSHans Rosenfeld 	error = iwn_pci_get_capability(sc->sc_pcih, PCI_CAP_ID_PCI_E,
833fd43cf6eSHans Rosenfeld 	    &sc->sc_cap_off);
834fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
835fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
836fd43cf6eSHans Rosenfeld 		    "!PCIe capability structure not found!");
837fd43cf6eSHans Rosenfeld 		goto fail_pci_capab;
838fd43cf6eSHans Rosenfeld 	}
839fd43cf6eSHans Rosenfeld 
840fd43cf6eSHans Rosenfeld 	/* Clear device-specific "PCI retry timeout" register (41h). */
841fd43cf6eSHans Rosenfeld 	reg = pci_config_get8(sc->sc_pcih, 0x41);
842fd43cf6eSHans Rosenfeld 	if (reg)
843fd43cf6eSHans Rosenfeld 		pci_config_put8(sc->sc_pcih, 0x41, 0);
844fd43cf6eSHans Rosenfeld 
845fd43cf6eSHans Rosenfeld 	error = ddi_regs_map_setup(dip, 1, &sc->sc_base, 0, 0, &iwn_reg_accattr,
846fd43cf6eSHans Rosenfeld 	    &sc->sc_regh);
847fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
848fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_regs_map_setup() failed");
849fd43cf6eSHans Rosenfeld 		goto fail_regs_map;
850fd43cf6eSHans Rosenfeld 	}
851fd43cf6eSHans Rosenfeld 
852fd43cf6eSHans Rosenfeld 	/* Clear pending interrupts. */
853fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
854fd43cf6eSHans Rosenfeld 
855fd43cf6eSHans Rosenfeld 	/* Disable all interrupts. */
856fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, 0);
857fd43cf6eSHans Rosenfeld 
858fd43cf6eSHans Rosenfeld 	/* Install interrupt handler. */
859fd43cf6eSHans Rosenfeld 	if (iwn_intr_setup(sc) != DDI_SUCCESS)
860fd43cf6eSHans Rosenfeld 		goto fail_intr;
861fd43cf6eSHans Rosenfeld 
862fd43cf6eSHans Rosenfeld 	mutex_init(&sc->sc_mtx, NULL, MUTEX_DRIVER,
863fd43cf6eSHans Rosenfeld 	    DDI_INTR_PRI(sc->sc_intr_pri));
864fd43cf6eSHans Rosenfeld 	mutex_init(&sc->sc_tx_mtx, NULL, MUTEX_DRIVER,
865fd43cf6eSHans Rosenfeld 	    DDI_INTR_PRI(sc->sc_intr_pri));
866fd43cf6eSHans Rosenfeld 	mutex_init(&sc->sc_mt_mtx, NULL, MUTEX_DRIVER,
867fd43cf6eSHans Rosenfeld 	    DDI_INTR_PRI(sc->sc_intr_pri));
868fd43cf6eSHans Rosenfeld 
869fd43cf6eSHans Rosenfeld 	cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL);
870fd43cf6eSHans Rosenfeld 	cv_init(&sc->sc_scan_cv, NULL, CV_DRIVER, NULL);
871fd43cf6eSHans Rosenfeld 	cv_init(&sc->sc_fhdma_cv, NULL, CV_DRIVER, NULL);
872fd43cf6eSHans Rosenfeld 	cv_init(&sc->sc_alive_cv, NULL, CV_DRIVER, NULL);
873fd43cf6eSHans Rosenfeld 	cv_init(&sc->sc_calib_cv, NULL, CV_DRIVER, NULL);
874fd43cf6eSHans Rosenfeld 
875fd43cf6eSHans Rosenfeld 	iwn_kstat_init(sc);
876fd43cf6eSHans Rosenfeld 
877fd43cf6eSHans Rosenfeld 	/* Read hardware revision and attach. */
878fd43cf6eSHans Rosenfeld 	sc->hw_type =
879fd43cf6eSHans Rosenfeld 	    (IWN_READ(sc, IWN_HW_REV) & IWN_HW_REV_TYPE_MASK)
880fd43cf6eSHans Rosenfeld 	      >> IWN_HW_REV_TYPE_SHIFT;
881fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
882fd43cf6eSHans Rosenfeld 		error = iwn4965_attach(sc);
883fd43cf6eSHans Rosenfeld 	else
884fd43cf6eSHans Rosenfeld 		error = iwn5000_attach(sc, sc->sc_devid);
885fd43cf6eSHans Rosenfeld 	if (error != 0) {
886fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not attach device");
887fd43cf6eSHans Rosenfeld 		goto fail_hw;
888fd43cf6eSHans Rosenfeld 	}
889fd43cf6eSHans Rosenfeld 
890fd43cf6eSHans Rosenfeld 	if ((error = iwn_hw_prepare(sc)) != 0) {
891fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!hardware not ready");
892fd43cf6eSHans Rosenfeld 		goto fail_hw;
893fd43cf6eSHans Rosenfeld 	}
894fd43cf6eSHans Rosenfeld 
895fd43cf6eSHans Rosenfeld 	/* Read MAC address, channels, etc from EEPROM. */
896fd43cf6eSHans Rosenfeld 	if ((error = iwn_read_eeprom(sc)) != 0) {
897fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not read EEPROM");
898fd43cf6eSHans Rosenfeld 		goto fail_hw;
899fd43cf6eSHans Rosenfeld 	}
900fd43cf6eSHans Rosenfeld 
901fd43cf6eSHans Rosenfeld 	/* Allocate DMA memory for firmware transfers. */
902fd43cf6eSHans Rosenfeld 	if ((error = iwn_alloc_fwmem(sc)) != 0) {
903fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
904fd43cf6eSHans Rosenfeld 		    "!could not allocate memory for firmware");
905fd43cf6eSHans Rosenfeld 		goto fail_fwmem;
906fd43cf6eSHans Rosenfeld 	}
907fd43cf6eSHans Rosenfeld 
908fd43cf6eSHans Rosenfeld 	/* Allocate "Keep Warm" page. */
909fd43cf6eSHans Rosenfeld 	if ((error = iwn_alloc_kw(sc)) != 0) {
910fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
911fd43cf6eSHans Rosenfeld 		    "!could not allocate keep warm page");
912fd43cf6eSHans Rosenfeld 		goto fail_kw;
913fd43cf6eSHans Rosenfeld 	}
914fd43cf6eSHans Rosenfeld 
915fd43cf6eSHans Rosenfeld 	/* Allocate ICT table for 5000 Series. */
916fd43cf6eSHans Rosenfeld 	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
917fd43cf6eSHans Rosenfeld 	    (error = iwn_alloc_ict(sc)) != 0) {
918fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not allocate ICT table");
919fd43cf6eSHans Rosenfeld 		goto fail_ict;
920fd43cf6eSHans Rosenfeld 	}
921fd43cf6eSHans Rosenfeld 
922fd43cf6eSHans Rosenfeld 	/* Allocate TX scheduler "rings". */
923fd43cf6eSHans Rosenfeld 	if ((error = iwn_alloc_sched(sc)) != 0) {
924fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
925fd43cf6eSHans Rosenfeld 		    "!could not allocate TX scheduler rings");
926fd43cf6eSHans Rosenfeld 		goto fail_sched;
927fd43cf6eSHans Rosenfeld 	}
928fd43cf6eSHans Rosenfeld 
929fd43cf6eSHans Rosenfeld 	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
930fd43cf6eSHans Rosenfeld 	for (i = 0; i < sc->ntxqs; i++) {
931fd43cf6eSHans Rosenfeld 		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
932fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
933fd43cf6eSHans Rosenfeld 			    "!could not allocate TX ring %d", i);
934fd43cf6eSHans Rosenfeld 			while (--i >= 0)
935fd43cf6eSHans Rosenfeld 				iwn_free_tx_ring(sc, &sc->txq[i]);
936fd43cf6eSHans Rosenfeld 			goto fail_txring;
937fd43cf6eSHans Rosenfeld 		}
938fd43cf6eSHans Rosenfeld 	}
939fd43cf6eSHans Rosenfeld 
940fd43cf6eSHans Rosenfeld 	/* Allocate RX ring. */
941fd43cf6eSHans Rosenfeld 	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
942fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not allocate RX ring");
943fd43cf6eSHans Rosenfeld 		goto fail_rxring;
944fd43cf6eSHans Rosenfeld 	}
945fd43cf6eSHans Rosenfeld 
946fd43cf6eSHans Rosenfeld 	/* Clear pending interrupts. */
947fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
948fd43cf6eSHans Rosenfeld 
949fd43cf6eSHans Rosenfeld 	/* Count the number of available chains. */
950fd43cf6eSHans Rosenfeld 	sc->ntxchains =
951fd43cf6eSHans Rosenfeld 	    ((sc->txchainmask >> 2) & 1) +
952fd43cf6eSHans Rosenfeld 	    ((sc->txchainmask >> 1) & 1) +
953fd43cf6eSHans Rosenfeld 	    ((sc->txchainmask >> 0) & 1);
954fd43cf6eSHans Rosenfeld 	sc->nrxchains =
955fd43cf6eSHans Rosenfeld 	    ((sc->rxchainmask >> 2) & 1) +
956fd43cf6eSHans Rosenfeld 	    ((sc->rxchainmask >> 1) & 1) +
957fd43cf6eSHans Rosenfeld 	    ((sc->rxchainmask >> 0) & 1);
958fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!MIMO %dT%dR, %s, address %s",
959fd43cf6eSHans Rosenfeld 	    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
960fd43cf6eSHans Rosenfeld 	    ieee80211_macaddr_sprintf(ic->ic_macaddr));
961fd43cf6eSHans Rosenfeld 
962fd43cf6eSHans Rosenfeld 	sc->sc_ant->tx_ant.value.ul = sc->txchainmask;
963fd43cf6eSHans Rosenfeld 	sc->sc_ant->rx_ant.value.ul = sc->rxchainmask;
964fd43cf6eSHans Rosenfeld 
965fd43cf6eSHans Rosenfeld 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
966fd43cf6eSHans Rosenfeld 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
967fd43cf6eSHans Rosenfeld 	ic->ic_state = IEEE80211_S_INIT;
968fd43cf6eSHans Rosenfeld 
969fd43cf6eSHans Rosenfeld 	/* Set device capabilities. */
970fd43cf6eSHans Rosenfeld 	/* XXX OpenBSD has IEEE80211_C_WEP, IEEE80211_C_RSN,
971fd43cf6eSHans Rosenfeld 	 * and IEEE80211_C_PMGT too. */
972fd43cf6eSHans Rosenfeld 	ic->ic_caps =
973fd43cf6eSHans Rosenfeld 	    IEEE80211_C_IBSS |		/* IBSS mode support */
974fd43cf6eSHans Rosenfeld 	    IEEE80211_C_WPA |		/* 802.11i */
975fd43cf6eSHans Rosenfeld 	    IEEE80211_C_MONITOR |	/* monitor mode supported */
976fd43cf6eSHans Rosenfeld 	    IEEE80211_C_TXPMGT |	/* tx power management */
977fd43cf6eSHans Rosenfeld 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
978fd43cf6eSHans Rosenfeld 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
979fd43cf6eSHans Rosenfeld 	    IEEE80211_C_WME;		/* 802.11e */
980fd43cf6eSHans Rosenfeld 
981fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
982fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
983fd43cf6eSHans Rosenfeld 		/* Set HT capabilities. */
984fd43cf6eSHans Rosenfeld 		ic->ic_htcaps =
985fd43cf6eSHans Rosenfeld #if IWN_RBUF_SIZE == 8192
986fd43cf6eSHans Rosenfeld 		    IEEE80211_HTCAP_AMSDU7935 |
987fd43cf6eSHans Rosenfeld #endif
988fd43cf6eSHans Rosenfeld 		    IEEE80211_HTCAP_CBW20_40 |
989fd43cf6eSHans Rosenfeld 		    IEEE80211_HTCAP_SGI20 |
990fd43cf6eSHans Rosenfeld 		    IEEE80211_HTCAP_SGI40;
991fd43cf6eSHans Rosenfeld 		if (sc->hw_type != IWN_HW_REV_TYPE_4965)
992fd43cf6eSHans Rosenfeld 			ic->ic_htcaps |= IEEE80211_HTCAP_GF;
993fd43cf6eSHans Rosenfeld 		if (sc->hw_type == IWN_HW_REV_TYPE_6050)
994fd43cf6eSHans Rosenfeld 			ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN;
995fd43cf6eSHans Rosenfeld 		else
996fd43cf6eSHans Rosenfeld 			ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
997fd43cf6eSHans Rosenfeld 	}
998fd43cf6eSHans Rosenfeld #endif	/* !IEEE80211_NO_HT */
999fd43cf6eSHans Rosenfeld 
1000fd43cf6eSHans Rosenfeld 	/* Set supported legacy rates. */
1001fd43cf6eSHans Rosenfeld 	ic->ic_sup_rates[IEEE80211_MODE_11B] = iwn_rateset_11b;
1002fd43cf6eSHans Rosenfeld 	ic->ic_sup_rates[IEEE80211_MODE_11G] = iwn_rateset_11g;
1003fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_HAS_5GHZ) {
1004fd43cf6eSHans Rosenfeld 		ic->ic_sup_rates[IEEE80211_MODE_11A] = iwn_rateset_11a;
1005fd43cf6eSHans Rosenfeld 	}
1006fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
1007fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
1008fd43cf6eSHans Rosenfeld 		/* Set supported HT rates. */
1009fd43cf6eSHans Rosenfeld 		ic->ic_sup_mcs[0] = 0xff;		/* MCS 0-7 */
1010fd43cf6eSHans Rosenfeld 		if (sc->nrxchains > 1)
1011fd43cf6eSHans Rosenfeld 			ic->ic_sup_mcs[1] = 0xff;	/* MCS 7-15 */
1012fd43cf6eSHans Rosenfeld 		if (sc->nrxchains > 2)
1013fd43cf6eSHans Rosenfeld 			ic->ic_sup_mcs[2] = 0xff;	/* MCS 16-23 */
1014fd43cf6eSHans Rosenfeld 	}
1015fd43cf6eSHans Rosenfeld #endif
1016fd43cf6eSHans Rosenfeld 
1017fd43cf6eSHans Rosenfeld 	/* IBSS channel undefined for now. */
1018fd43cf6eSHans Rosenfeld 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
1019fd43cf6eSHans Rosenfeld 
1020fd43cf6eSHans Rosenfeld 	ic->ic_node_newassoc = iwn_newassoc;
1021fd43cf6eSHans Rosenfeld 	ic->ic_xmit = iwn_send;
1022fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO
1023fd43cf6eSHans Rosenfeld 	ic->ic_crypto.cs_key_set = iwn_set_key;
1024fd43cf6eSHans Rosenfeld 	ic->ic_crypto.cs_key_delete = iwn_delete_key;
1025fd43cf6eSHans Rosenfeld #endif
1026fd43cf6eSHans Rosenfeld 	ic->ic_wme.wme_update = iwn_wme_update;
1027fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
1028fd43cf6eSHans Rosenfeld 	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
1029fd43cf6eSHans Rosenfeld 	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
1030fd43cf6eSHans Rosenfeld 	ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
1031fd43cf6eSHans Rosenfeld 	ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
1032fd43cf6eSHans Rosenfeld #endif
1033fd43cf6eSHans Rosenfeld 	/*
1034fd43cf6eSHans Rosenfeld 	 * attach to 802.11 module
1035fd43cf6eSHans Rosenfeld 	 */
1036fd43cf6eSHans Rosenfeld 	ieee80211_attach(ic);
1037fd43cf6eSHans Rosenfeld 
1038fd43cf6eSHans Rosenfeld 	ieee80211_register_door(ic, ddi_driver_name(dip), ddi_get_instance(dip));
1039fd43cf6eSHans Rosenfeld 
1040fd43cf6eSHans Rosenfeld 	/* Override 802.11 state transition machine. */
1041fd43cf6eSHans Rosenfeld 	sc->sc_newstate = ic->ic_newstate;
1042fd43cf6eSHans Rosenfeld 	ic->ic_newstate = iwn_newstate;
1043fd43cf6eSHans Rosenfeld 	ic->ic_watchdog = iwn_watchdog;
1044fd43cf6eSHans Rosenfeld 
1045fd43cf6eSHans Rosenfeld 	ic->ic_node_alloc = iwn_node_alloc;
1046fd43cf6eSHans Rosenfeld 	ic->ic_node_free = iwn_node_free;
1047fd43cf6eSHans Rosenfeld 
1048fd43cf6eSHans Rosenfeld 	ieee80211_media_init(ic);
1049fd43cf6eSHans Rosenfeld 
1050fd43cf6eSHans Rosenfeld 	/*
1051fd43cf6eSHans Rosenfeld 	 * initialize default tx key
1052fd43cf6eSHans Rosenfeld 	 */
1053fd43cf6eSHans Rosenfeld 	ic->ic_def_txkey = 0;
1054fd43cf6eSHans Rosenfeld 
1055fd43cf6eSHans Rosenfeld 	sc->amrr.amrr_min_success_threshold =  1;
1056fd43cf6eSHans Rosenfeld 	sc->amrr.amrr_max_success_threshold = 15;
1057fd43cf6eSHans Rosenfeld 
1058fd43cf6eSHans Rosenfeld 	/*
1059fd43cf6eSHans Rosenfeld 	 * Initialize pointer to device specific functions
1060fd43cf6eSHans Rosenfeld 	 */
1061fd43cf6eSHans Rosenfeld 	wd.wd_secalloc = WIFI_SEC_NONE;
1062fd43cf6eSHans Rosenfeld 	wd.wd_opmode = ic->ic_opmode;
1063fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
1064fd43cf6eSHans Rosenfeld 
1065fd43cf6eSHans Rosenfeld 	/*
1066fd43cf6eSHans Rosenfeld 	 * create relation to GLD
1067fd43cf6eSHans Rosenfeld 	 */
1068fd43cf6eSHans Rosenfeld 	macp = mac_alloc(MAC_VERSION);
1069fd43cf6eSHans Rosenfeld 	if (NULL == macp) {
1070fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!mac_alloc() failed");
1071fd43cf6eSHans Rosenfeld 		goto fail_mac_alloc;
1072fd43cf6eSHans Rosenfeld 	}
1073fd43cf6eSHans Rosenfeld 
1074fd43cf6eSHans Rosenfeld 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
1075fd43cf6eSHans Rosenfeld 	macp->m_driver		= sc;
1076fd43cf6eSHans Rosenfeld 	macp->m_dip		= dip;
1077fd43cf6eSHans Rosenfeld 	macp->m_src_addr	= ic->ic_macaddr;
1078fd43cf6eSHans Rosenfeld 	macp->m_callbacks	= &iwn_m_callbacks;
1079fd43cf6eSHans Rosenfeld 	macp->m_min_sdu		= 0;
1080fd43cf6eSHans Rosenfeld 	macp->m_max_sdu		= IEEE80211_MTU;
1081fd43cf6eSHans Rosenfeld 	macp->m_pdata		= &wd;
1082fd43cf6eSHans Rosenfeld 	macp->m_pdata_size	= sizeof (wd);
1083fd43cf6eSHans Rosenfeld 
1084fd43cf6eSHans Rosenfeld 	/*
1085fd43cf6eSHans Rosenfeld 	 * Register the macp to mac
1086fd43cf6eSHans Rosenfeld 	 */
1087fd43cf6eSHans Rosenfeld 	error = mac_register(macp, &ic->ic_mach);
1088fd43cf6eSHans Rosenfeld 	mac_free(macp);
1089fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1090fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!mac_register() failed");
1091fd43cf6eSHans Rosenfeld 		goto fail_mac_alloc;
1092fd43cf6eSHans Rosenfeld 	}
1093fd43cf6eSHans Rosenfeld 
1094fd43cf6eSHans Rosenfeld 	/*
1095fd43cf6eSHans Rosenfeld 	 * Create minor node of type DDI_NT_NET_WIFI
1096fd43cf6eSHans Rosenfeld 	 */
1097fd43cf6eSHans Rosenfeld 	(void) snprintf(strbuf, sizeof (strbuf), "iwn%d", instance);
1098fd43cf6eSHans Rosenfeld 	error = ddi_create_minor_node(dip, strbuf, S_IFCHR,
1099fd43cf6eSHans Rosenfeld 	    instance + 1, DDI_NT_NET_WIFI, 0);
1100fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1101fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!ddi_create_minor_node() failed");
1102fd43cf6eSHans Rosenfeld 		goto fail_minor;
1103fd43cf6eSHans Rosenfeld 	}
1104fd43cf6eSHans Rosenfeld 
1105fd43cf6eSHans Rosenfeld 	/*
1106fd43cf6eSHans Rosenfeld 	 * Notify link is down now
1107fd43cf6eSHans Rosenfeld 	 */
1108fd43cf6eSHans Rosenfeld 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
1109fd43cf6eSHans Rosenfeld 
1110fd43cf6eSHans Rosenfeld 	sc->sc_periodic = ddi_periodic_add(iwn_periodic, sc,
1111fd43cf6eSHans Rosenfeld 	    iwn_periodic_interval * MICROSEC, 0);
1112fd43cf6eSHans Rosenfeld 
1113fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_misc)
1114fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_misc);
1115fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_ant)
1116fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_ant);
1117fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_sens)
1118fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_sens);
1119fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_timing)
1120fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_timing);
1121fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_edca)
1122fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_edca);
1123fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_txpower)
1124fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_txpower);
1125fd43cf6eSHans Rosenfeld 	if (sc->sc_ks_toff)
1126fd43cf6eSHans Rosenfeld 		kstat_install(sc->sc_ks_toff);
1127fd43cf6eSHans Rosenfeld 
1128fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_ATTACHED;
1129fd43cf6eSHans Rosenfeld 
1130fd43cf6eSHans Rosenfeld 	return (DDI_SUCCESS);
1131fd43cf6eSHans Rosenfeld 
1132fd43cf6eSHans Rosenfeld 	/* Free allocated memory if something failed during attachment. */
1133fd43cf6eSHans Rosenfeld fail_minor:
1134fd43cf6eSHans Rosenfeld 	mac_unregister(ic->ic_mach);
1135fd43cf6eSHans Rosenfeld 
1136fd43cf6eSHans Rosenfeld fail_mac_alloc:
1137fd43cf6eSHans Rosenfeld 	ieee80211_detach(ic);
1138fd43cf6eSHans Rosenfeld 	iwn_free_rx_ring(sc, &sc->rxq);
1139fd43cf6eSHans Rosenfeld 
1140fd43cf6eSHans Rosenfeld fail_rxring:
1141fd43cf6eSHans Rosenfeld 	for (i = 0; i < sc->ntxqs; i++)
1142fd43cf6eSHans Rosenfeld 		iwn_free_tx_ring(sc, &sc->txq[i]);
1143fd43cf6eSHans Rosenfeld 
1144fd43cf6eSHans Rosenfeld fail_txring:
1145fd43cf6eSHans Rosenfeld 	iwn_free_sched(sc);
1146fd43cf6eSHans Rosenfeld 
1147fd43cf6eSHans Rosenfeld fail_sched:
1148fd43cf6eSHans Rosenfeld 	if (sc->ict != NULL)
1149fd43cf6eSHans Rosenfeld 		iwn_free_ict(sc);
1150fd43cf6eSHans Rosenfeld 
1151fd43cf6eSHans Rosenfeld fail_ict:
1152fd43cf6eSHans Rosenfeld 	iwn_free_kw(sc);
1153fd43cf6eSHans Rosenfeld 
1154fd43cf6eSHans Rosenfeld fail_kw:
1155fd43cf6eSHans Rosenfeld 	iwn_free_fwmem(sc);
1156fd43cf6eSHans Rosenfeld 
1157fd43cf6eSHans Rosenfeld fail_fwmem:
1158fd43cf6eSHans Rosenfeld fail_hw:
1159fd43cf6eSHans Rosenfeld 	iwn_intr_teardown(sc);
1160fd43cf6eSHans Rosenfeld 
1161fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_txpower, sc->sc_txpower,
1162fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_txpower));
1163fd43cf6eSHans Rosenfeld 
1164fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
1165fd43cf6eSHans Rosenfeld 		iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t6000,
1166fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_6000));
1167fd43cf6eSHans Rosenfeld 	else
1168fd43cf6eSHans Rosenfeld 		iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t2000,
1169fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000));
1170fd43cf6eSHans Rosenfeld 
1171fd43cf6eSHans Rosenfeld fail_intr:
1172fd43cf6eSHans Rosenfeld 	ddi_regs_map_free(&sc->sc_regh);
1173fd43cf6eSHans Rosenfeld 
1174fd43cf6eSHans Rosenfeld fail_regs_map:
1175fd43cf6eSHans Rosenfeld fail_pci_capab:
1176fd43cf6eSHans Rosenfeld 	pci_config_teardown(&sc->sc_pcih);
1177fd43cf6eSHans Rosenfeld 
1178fd43cf6eSHans Rosenfeld fail_pci_config:
1179fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_misc, sc->sc_misc,
1180fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_misc));
1181fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_ant, sc->sc_ant,
1182fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_ant));
1183fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_sens, sc->sc_sens,
1184fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_sens));
1185fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_timing, sc->sc_timing,
1186fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_timing));
1187fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_edca, sc->sc_edca,
1188fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_edca));
1189fd43cf6eSHans Rosenfeld 
1190fd43cf6eSHans Rosenfeld 	ddi_soft_state_free(iwn_state, instance);
1191fd43cf6eSHans Rosenfeld 
1192fd43cf6eSHans Rosenfeld 	return (DDI_FAILURE);
1193fd43cf6eSHans Rosenfeld }
1194fd43cf6eSHans Rosenfeld 
1195fd43cf6eSHans Rosenfeld int
iwn4965_attach(struct iwn_softc * sc)1196fd43cf6eSHans Rosenfeld iwn4965_attach(struct iwn_softc *sc)
1197fd43cf6eSHans Rosenfeld {
1198fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
1199fd43cf6eSHans Rosenfeld 
1200fd43cf6eSHans Rosenfeld 	ops->load_firmware = iwn4965_load_firmware;
1201fd43cf6eSHans Rosenfeld 	ops->read_eeprom = iwn4965_read_eeprom;
1202fd43cf6eSHans Rosenfeld 	ops->post_alive = iwn4965_post_alive;
1203fd43cf6eSHans Rosenfeld 	ops->nic_config = iwn4965_nic_config;
1204fd43cf6eSHans Rosenfeld 	ops->config_bt_coex = iwn_config_bt_coex_bluetooth;
1205fd43cf6eSHans Rosenfeld 	ops->update_sched = iwn4965_update_sched;
1206fd43cf6eSHans Rosenfeld 	ops->get_temperature = iwn4965_get_temperature;
1207fd43cf6eSHans Rosenfeld 	ops->get_rssi = iwn4965_get_rssi;
1208fd43cf6eSHans Rosenfeld 	ops->set_txpower = iwn4965_set_txpower;
1209fd43cf6eSHans Rosenfeld 	ops->init_gains = iwn4965_init_gains;
1210fd43cf6eSHans Rosenfeld 	ops->set_gains = iwn4965_set_gains;
1211fd43cf6eSHans Rosenfeld 	ops->add_node = iwn4965_add_node;
1212fd43cf6eSHans Rosenfeld 	ops->tx_done = iwn4965_tx_done;
1213fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
1214fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
1215fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
1216fd43cf6eSHans Rosenfeld #endif
1217fd43cf6eSHans Rosenfeld 	sc->ntxqs = IWN4965_NTXQUEUES;
1218fd43cf6eSHans Rosenfeld 	sc->ndmachnls = IWN4965_NDMACHNLS;
1219fd43cf6eSHans Rosenfeld 	sc->broadcast_id = IWN4965_ID_BROADCAST;
1220fd43cf6eSHans Rosenfeld 	sc->rxonsz = IWN4965_RXONSZ;
1221fd43cf6eSHans Rosenfeld 	sc->schedsz = IWN4965_SCHEDSZ;
1222fd43cf6eSHans Rosenfeld 	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
1223fd43cf6eSHans Rosenfeld 	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
1224fd43cf6eSHans Rosenfeld 	sc->fwsz = IWN4965_FWSZ;
1225fd43cf6eSHans Rosenfeld 	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
1226fd43cf6eSHans Rosenfeld 	sc->limits = &iwn4965_sensitivity_limits;
1227fd43cf6eSHans Rosenfeld 	sc->fwname = "iwlwifi-4965-2.ucode";
1228fd43cf6eSHans Rosenfeld 	/* Override chains masks, ROM is known to be broken. */
1229fd43cf6eSHans Rosenfeld 	sc->txchainmask = IWN_ANT_AB;
1230fd43cf6eSHans Rosenfeld 	sc->rxchainmask = IWN_ANT_ABC;
1231fd43cf6eSHans Rosenfeld 
1232fd43cf6eSHans Rosenfeld 	iwn_kstat_create(sc, "txpower", sizeof (struct iwn_ks_txpower),
1233fd43cf6eSHans Rosenfeld 	    &sc->sc_ks_txpower, (void **)&sc->sc_txpower);
1234fd43cf6eSHans Rosenfeld 	iwn_kstat_init_4965(sc);
1235fd43cf6eSHans Rosenfeld 
1236fd43cf6eSHans Rosenfeld 	return 0;
1237fd43cf6eSHans Rosenfeld }
1238fd43cf6eSHans Rosenfeld 
1239fd43cf6eSHans Rosenfeld int
iwn5000_attach(struct iwn_softc * sc,uint16_t pid)1240fd43cf6eSHans Rosenfeld iwn5000_attach(struct iwn_softc *sc, uint16_t pid)
1241fd43cf6eSHans Rosenfeld {
1242fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
1243fd43cf6eSHans Rosenfeld 
1244fd43cf6eSHans Rosenfeld 	ops->load_firmware = iwn5000_load_firmware;
1245fd43cf6eSHans Rosenfeld 	ops->read_eeprom = iwn5000_read_eeprom;
1246fd43cf6eSHans Rosenfeld 	ops->post_alive = iwn5000_post_alive;
1247fd43cf6eSHans Rosenfeld 	ops->nic_config = iwn5000_nic_config;
1248fd43cf6eSHans Rosenfeld 	ops->config_bt_coex = iwn_config_bt_coex_bluetooth;
1249fd43cf6eSHans Rosenfeld 	ops->update_sched = iwn5000_update_sched;
1250fd43cf6eSHans Rosenfeld 	ops->get_temperature = iwn5000_get_temperature;
1251fd43cf6eSHans Rosenfeld 	ops->get_rssi = iwn5000_get_rssi;
1252fd43cf6eSHans Rosenfeld 	ops->set_txpower = iwn5000_set_txpower;
1253fd43cf6eSHans Rosenfeld 	ops->init_gains = iwn5000_init_gains;
1254fd43cf6eSHans Rosenfeld 	ops->set_gains = iwn5000_set_gains;
1255fd43cf6eSHans Rosenfeld 	ops->add_node = iwn5000_add_node;
1256fd43cf6eSHans Rosenfeld 	ops->tx_done = iwn5000_tx_done;
1257fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
1258fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
1259fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
1260fd43cf6eSHans Rosenfeld #endif
1261fd43cf6eSHans Rosenfeld 	sc->ntxqs = IWN5000_NTXQUEUES;
1262fd43cf6eSHans Rosenfeld 	sc->ndmachnls = IWN5000_NDMACHNLS;
1263fd43cf6eSHans Rosenfeld 	sc->broadcast_id = IWN5000_ID_BROADCAST;
1264fd43cf6eSHans Rosenfeld 	sc->rxonsz = IWN5000_RXONSZ;
1265fd43cf6eSHans Rosenfeld 	sc->schedsz = IWN5000_SCHEDSZ;
1266fd43cf6eSHans Rosenfeld 	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
1267fd43cf6eSHans Rosenfeld 	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
1268fd43cf6eSHans Rosenfeld 	sc->fwsz = IWN5000_FWSZ;
1269fd43cf6eSHans Rosenfeld 	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
1270fd43cf6eSHans Rosenfeld 
1271fd43cf6eSHans Rosenfeld 	switch (sc->hw_type) {
1272fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_5100:
1273fd43cf6eSHans Rosenfeld 		sc->limits = &iwn5000_sensitivity_limits;
1274fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-5000-2.ucode";
1275fd43cf6eSHans Rosenfeld 		/* Override chains masks, ROM is known to be broken. */
1276fd43cf6eSHans Rosenfeld 		sc->txchainmask = IWN_ANT_B;
1277fd43cf6eSHans Rosenfeld 		sc->rxchainmask = IWN_ANT_AB;
1278fd43cf6eSHans Rosenfeld 		break;
1279fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_5150:
1280fd43cf6eSHans Rosenfeld 		sc->limits = &iwn5150_sensitivity_limits;
1281fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-5150-2.ucode";
1282fd43cf6eSHans Rosenfeld 		break;
1283fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_5300:
1284fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_5350:
1285fd43cf6eSHans Rosenfeld 		sc->limits = &iwn5000_sensitivity_limits;
1286fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-5000-2.ucode";
1287fd43cf6eSHans Rosenfeld 		break;
1288fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_1000:
1289fd43cf6eSHans Rosenfeld 		sc->limits = &iwn1000_sensitivity_limits;
1290fd43cf6eSHans Rosenfeld 		if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_100_1 ||
1291fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_100_2)
1292fd43cf6eSHans Rosenfeld 			sc->fwname = "iwlwifi-100-5.ucode";
1293fd43cf6eSHans Rosenfeld 		else
1294fd43cf6eSHans Rosenfeld 			sc->fwname = "iwlwifi-1000-3.ucode";
1295fd43cf6eSHans Rosenfeld 		break;
1296fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_6000:
1297fd43cf6eSHans Rosenfeld 		sc->limits = &iwn6000_sensitivity_limits;
1298fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-6000-4.ucode";
1299fd43cf6eSHans Rosenfeld 		if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_1 ||
1300fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_2) {
1301fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
1302fd43cf6eSHans Rosenfeld 			/* Override chains masks, ROM is known to be broken. */
1303fd43cf6eSHans Rosenfeld 			sc->txchainmask = IWN_ANT_BC;
1304fd43cf6eSHans Rosenfeld 			sc->rxchainmask = IWN_ANT_BC;
1305fd43cf6eSHans Rosenfeld 		}
1306fd43cf6eSHans Rosenfeld 		break;
1307fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_6050:
1308fd43cf6eSHans Rosenfeld 		sc->limits = &iwn6000_sensitivity_limits;
1309fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-6050-5.ucode";
1310fd43cf6eSHans Rosenfeld 		break;
1311fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_6005:
1312fd43cf6eSHans Rosenfeld 		sc->limits = &iwn6000_sensitivity_limits;
1313fd43cf6eSHans Rosenfeld 		/* Type 6030 cards return IWN_HW_REV_TYPE_6005 */
1314fd43cf6eSHans Rosenfeld 		if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_1030_1 ||
1315fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_1030_2 ||
1316fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_6230_1 ||
1317fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_6230_2 ||
1318fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_6235   ||
1319fd43cf6eSHans Rosenfeld 		    pid == PCI_PRODUCT_INTEL_WIFI_LINK_6235_2) {
1320fd43cf6eSHans Rosenfeld 			sc->fwname = "iwlwifi-6000g2b-6.ucode";
1321fd43cf6eSHans Rosenfeld 			ops->config_bt_coex = iwn_config_bt_coex_adv1;
1322fd43cf6eSHans Rosenfeld 		}
1323fd43cf6eSHans Rosenfeld 		else
1324e5b103bbSMarcel Telka 			sc->fwname = "iwlwifi-6000g2a-6.ucode";
1325fd43cf6eSHans Rosenfeld 
1326fd43cf6eSHans Rosenfeld 		iwn_kstat_create(sc, "temp_offset",
1327fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_6000),
1328fd43cf6eSHans Rosenfeld 		    &sc->sc_ks_toff, (void **)&sc->sc_toff.t6000);
1329fd43cf6eSHans Rosenfeld 		iwn_kstat_init_6000(sc);
1330fd43cf6eSHans Rosenfeld 		break;
1331fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_2030:
1332fd43cf6eSHans Rosenfeld 		sc->limits = &iwn2000_sensitivity_limits;
1333fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-2030-6.ucode";
1334fd43cf6eSHans Rosenfeld 		ops->config_bt_coex = iwn_config_bt_coex_adv2;
1335fd43cf6eSHans Rosenfeld 
1336fd43cf6eSHans Rosenfeld 		iwn_kstat_create(sc, "temp_offset",
1337fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000),
1338fd43cf6eSHans Rosenfeld 		    &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000);
1339fd43cf6eSHans Rosenfeld 		iwn_kstat_init_2000(sc);
1340fd43cf6eSHans Rosenfeld 		break;
1341fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_2000:
1342fd43cf6eSHans Rosenfeld 		sc->limits = &iwn2000_sensitivity_limits;
1343fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-2000-6.ucode";
1344fd43cf6eSHans Rosenfeld 
1345fd43cf6eSHans Rosenfeld 		iwn_kstat_create(sc, "temp_offset",
1346fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000),
1347fd43cf6eSHans Rosenfeld 		    &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000);
1348fd43cf6eSHans Rosenfeld 		iwn_kstat_init_2000(sc);
1349fd43cf6eSHans Rosenfeld 		break;
1350fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_135:
1351fd43cf6eSHans Rosenfeld 		sc->limits = &iwn2000_sensitivity_limits;
1352fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-135-6.ucode";
1353fd43cf6eSHans Rosenfeld 		ops->config_bt_coex = iwn_config_bt_coex_adv2;
1354fd43cf6eSHans Rosenfeld 
1355fd43cf6eSHans Rosenfeld 		iwn_kstat_create(sc, "temp_offset",
1356fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000),
1357fd43cf6eSHans Rosenfeld 		    &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000);
1358fd43cf6eSHans Rosenfeld 		iwn_kstat_init_2000(sc);
1359fd43cf6eSHans Rosenfeld 		break;
1360fd43cf6eSHans Rosenfeld 	case IWN_HW_REV_TYPE_105:
1361fd43cf6eSHans Rosenfeld 		sc->limits = &iwn2000_sensitivity_limits;
1362fd43cf6eSHans Rosenfeld 		sc->fwname = "iwlwifi-105-6.ucode";
1363fd43cf6eSHans Rosenfeld 
1364fd43cf6eSHans Rosenfeld 		iwn_kstat_create(sc, "temp_offset",
1365fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000),
1366fd43cf6eSHans Rosenfeld 		    &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000);
1367fd43cf6eSHans Rosenfeld 		iwn_kstat_init_2000(sc);
1368fd43cf6eSHans Rosenfeld 		break;
1369fd43cf6eSHans Rosenfeld 	default:
1370fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!adapter type %d not supported",
1371fd43cf6eSHans Rosenfeld 		    sc->hw_type);
1372fd43cf6eSHans Rosenfeld 		return ENOTSUP;
1373fd43cf6eSHans Rosenfeld 	}
1374fd43cf6eSHans Rosenfeld 	return 0;
1375fd43cf6eSHans Rosenfeld }
1376fd43cf6eSHans Rosenfeld 
1377fd43cf6eSHans Rosenfeld static int
iwn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1378fd43cf6eSHans Rosenfeld iwn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1379fd43cf6eSHans Rosenfeld {
1380fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ddi_get_driver_private(dip);
1381fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic = &sc->sc_ic;
1382fd43cf6eSHans Rosenfeld 	int qid, error;
1383fd43cf6eSHans Rosenfeld 
1384fd43cf6eSHans Rosenfeld 	switch (cmd) {
1385fd43cf6eSHans Rosenfeld 	case DDI_DETACH:
1386fd43cf6eSHans Rosenfeld 		break;
1387fd43cf6eSHans Rosenfeld 	case DDI_SUSPEND:
1388fd43cf6eSHans Rosenfeld 		sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER;
1389fd43cf6eSHans Rosenfeld 		sc->sc_flags &= ~IWN_FLAG_RATE_AUTO_CTL;
1390fd43cf6eSHans Rosenfeld 
1391fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_SUSPEND;
1392fd43cf6eSHans Rosenfeld 
1393fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_RUNNING) {
1394fd43cf6eSHans Rosenfeld 			iwn_hw_stop(sc, B_TRUE);
1395fd43cf6eSHans Rosenfeld 			ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1396fd43cf6eSHans Rosenfeld 
1397fd43cf6eSHans Rosenfeld 		}
1398fd43cf6eSHans Rosenfeld 
1399fd43cf6eSHans Rosenfeld 		return (DDI_SUCCESS);
1400fd43cf6eSHans Rosenfeld 	default:
1401fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
1402fd43cf6eSHans Rosenfeld 	}
1403fd43cf6eSHans Rosenfeld 
1404fd43cf6eSHans Rosenfeld 	if (!(sc->sc_flags & IWN_FLAG_ATTACHED)) {
1405fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
1406fd43cf6eSHans Rosenfeld 	}
1407fd43cf6eSHans Rosenfeld 
1408fd43cf6eSHans Rosenfeld 	error = mac_disable(ic->ic_mach);
1409fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS)
1410fd43cf6eSHans Rosenfeld 		return (error);
1411fd43cf6eSHans Rosenfeld 
1412fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
1413fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO;
1414fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
1415fd43cf6eSHans Rosenfeld 
1416fd43cf6eSHans Rosenfeld 	if (sc->calib_to != 0)
1417fd43cf6eSHans Rosenfeld 		(void) untimeout(sc->calib_to);
1418fd43cf6eSHans Rosenfeld 	sc->calib_to = 0;
1419fd43cf6eSHans Rosenfeld 
1420fd43cf6eSHans Rosenfeld 	if (sc->scan_to != 0)
1421fd43cf6eSHans Rosenfeld 		(void) untimeout(sc->scan_to);
1422fd43cf6eSHans Rosenfeld 	sc->scan_to = 0;
1423fd43cf6eSHans Rosenfeld 
1424fd43cf6eSHans Rosenfeld 	ddi_periodic_delete(sc->sc_periodic);
1425fd43cf6eSHans Rosenfeld 
1426fd43cf6eSHans Rosenfeld 	/*
1427fd43cf6eSHans Rosenfeld 	 * stop chipset
1428fd43cf6eSHans Rosenfeld 	 */
1429fd43cf6eSHans Rosenfeld 	iwn_hw_stop(sc, B_TRUE);
1430fd43cf6eSHans Rosenfeld 
1431fd43cf6eSHans Rosenfeld 	/*
1432fd43cf6eSHans Rosenfeld 	 * Unregister from GLD
1433fd43cf6eSHans Rosenfeld 	 */
1434fd43cf6eSHans Rosenfeld 	(void) mac_unregister(ic->ic_mach);
1435fd43cf6eSHans Rosenfeld 	ieee80211_detach(ic);
1436fd43cf6eSHans Rosenfeld 
1437fd43cf6eSHans Rosenfeld 	/* Uninstall interrupt handler. */
1438fd43cf6eSHans Rosenfeld 	iwn_intr_teardown(sc);
1439fd43cf6eSHans Rosenfeld 
1440fd43cf6eSHans Rosenfeld 	/* Free DMA resources. */
1441fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
1442fd43cf6eSHans Rosenfeld 	iwn_free_rx_ring(sc, &sc->rxq);
1443fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < sc->ntxqs; qid++)
1444fd43cf6eSHans Rosenfeld 		iwn_free_tx_ring(sc, &sc->txq[qid]);
1445fd43cf6eSHans Rosenfeld 	iwn_free_sched(sc);
1446fd43cf6eSHans Rosenfeld 	iwn_free_kw(sc);
1447fd43cf6eSHans Rosenfeld 	if (sc->ict != NULL)
1448fd43cf6eSHans Rosenfeld 		iwn_free_ict(sc);
1449fd43cf6eSHans Rosenfeld 	iwn_free_fwmem(sc);
1450fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
1451fd43cf6eSHans Rosenfeld 
1452fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_misc, sc->sc_misc,
1453fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_misc));
1454fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_ant, sc->sc_ant,
1455fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_ant));
1456fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_sens, sc->sc_sens,
1457fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_sens));
1458fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_timing, sc->sc_timing,
1459fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_timing));
1460fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_edca, sc->sc_edca,
1461fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_edca));
1462fd43cf6eSHans Rosenfeld 	iwn_kstat_free(sc->sc_ks_txpower, sc->sc_txpower,
1463fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_ks_txpower));
1464fd43cf6eSHans Rosenfeld 
1465fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
1466fd43cf6eSHans Rosenfeld 		iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t6000,
1467fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_6000));
1468fd43cf6eSHans Rosenfeld 	else
1469fd43cf6eSHans Rosenfeld 		iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t2000,
1470fd43cf6eSHans Rosenfeld 		    sizeof (struct iwn_ks_toff_2000));
1471fd43cf6eSHans Rosenfeld 
1472fd43cf6eSHans Rosenfeld 	ddi_regs_map_free(&sc->sc_regh);
1473fd43cf6eSHans Rosenfeld 	pci_config_teardown(&sc->sc_pcih);
1474fd43cf6eSHans Rosenfeld 	ddi_remove_minor_node(dip, NULL);
1475fd43cf6eSHans Rosenfeld 	ddi_soft_state_free(iwn_state, ddi_get_instance(dip));
1476fd43cf6eSHans Rosenfeld 
1477fd43cf6eSHans Rosenfeld 	return 0;
1478fd43cf6eSHans Rosenfeld }
1479fd43cf6eSHans Rosenfeld 
1480fd43cf6eSHans Rosenfeld static int
iwn_quiesce(dev_info_t * dip)1481fd43cf6eSHans Rosenfeld iwn_quiesce(dev_info_t *dip)
1482fd43cf6eSHans Rosenfeld {
1483fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
1484fd43cf6eSHans Rosenfeld 
1485fd43cf6eSHans Rosenfeld 	sc = ddi_get_soft_state(iwn_state, ddi_get_instance(dip));
1486fd43cf6eSHans Rosenfeld 	if (sc == NULL)
1487fd43cf6eSHans Rosenfeld 		return (DDI_FAILURE);
1488fd43cf6eSHans Rosenfeld 
1489fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG
1490fd43cf6eSHans Rosenfeld 	/* bypass any messages */
1491fd43cf6eSHans Rosenfeld 	iwn_dbg_print = 0;
1492fd43cf6eSHans Rosenfeld #endif
1493fd43cf6eSHans Rosenfeld 
1494fd43cf6eSHans Rosenfeld 	/*
1495fd43cf6eSHans Rosenfeld 	 * No more blocking is allowed while we are in the
1496fd43cf6eSHans Rosenfeld 	 * quiesce(9E) entry point.
1497fd43cf6eSHans Rosenfeld 	 */
1498fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_QUIESCED;
1499fd43cf6eSHans Rosenfeld 
1500fd43cf6eSHans Rosenfeld 	/*
1501fd43cf6eSHans Rosenfeld 	 * Disable and mask all interrupts.
1502fd43cf6eSHans Rosenfeld 	 */
1503fd43cf6eSHans Rosenfeld 	iwn_hw_stop(sc, B_FALSE);
1504fd43cf6eSHans Rosenfeld 
1505fd43cf6eSHans Rosenfeld 	return (DDI_SUCCESS);
1506fd43cf6eSHans Rosenfeld }
1507fd43cf6eSHans Rosenfeld 
1508fd43cf6eSHans Rosenfeld static int
iwn_nic_lock(struct iwn_softc * sc)1509fd43cf6eSHans Rosenfeld iwn_nic_lock(struct iwn_softc *sc)
1510fd43cf6eSHans Rosenfeld {
1511fd43cf6eSHans Rosenfeld 	int ntries;
1512fd43cf6eSHans Rosenfeld 
1513fd43cf6eSHans Rosenfeld 	/* Request exclusive access to NIC. */
1514fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1515fd43cf6eSHans Rosenfeld 
1516fd43cf6eSHans Rosenfeld 	/* Spin until we actually get the lock. */
1517fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 1000; ntries++) {
1518fd43cf6eSHans Rosenfeld 		if ((IWN_READ(sc, IWN_GP_CNTRL) &
1519fd43cf6eSHans Rosenfeld 		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
1520fd43cf6eSHans Rosenfeld 		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
1521fd43cf6eSHans Rosenfeld 			return 0;
1522fd43cf6eSHans Rosenfeld 		DELAY(10);
1523fd43cf6eSHans Rosenfeld 	}
1524fd43cf6eSHans Rosenfeld 	return ETIMEDOUT;
1525fd43cf6eSHans Rosenfeld }
1526fd43cf6eSHans Rosenfeld 
1527fd43cf6eSHans Rosenfeld static __inline void
iwn_nic_unlock(struct iwn_softc * sc)1528fd43cf6eSHans Rosenfeld iwn_nic_unlock(struct iwn_softc *sc)
1529fd43cf6eSHans Rosenfeld {
1530fd43cf6eSHans Rosenfeld 	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1531fd43cf6eSHans Rosenfeld }
1532fd43cf6eSHans Rosenfeld 
1533fd43cf6eSHans Rosenfeld static __inline uint32_t
iwn_prph_read(struct iwn_softc * sc,uint32_t addr)1534fd43cf6eSHans Rosenfeld iwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1535fd43cf6eSHans Rosenfeld {
1536fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1537fd43cf6eSHans Rosenfeld 	IWN_BARRIER_READ_WRITE(sc);
1538fd43cf6eSHans Rosenfeld 	return IWN_READ(sc, IWN_PRPH_RDATA);
1539fd43cf6eSHans Rosenfeld }
1540fd43cf6eSHans Rosenfeld 
1541fd43cf6eSHans Rosenfeld static __inline void
iwn_prph_write(struct iwn_softc * sc,uint32_t addr,uint32_t data)1542fd43cf6eSHans Rosenfeld iwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1543fd43cf6eSHans Rosenfeld {
1544fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1545fd43cf6eSHans Rosenfeld 	IWN_BARRIER_WRITE(sc);
1546fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1547fd43cf6eSHans Rosenfeld }
1548fd43cf6eSHans Rosenfeld 
1549fd43cf6eSHans Rosenfeld static __inline void
iwn_prph_setbits(struct iwn_softc * sc,uint32_t addr,uint32_t mask)1550fd43cf6eSHans Rosenfeld iwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1551fd43cf6eSHans Rosenfeld {
1552fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1553fd43cf6eSHans Rosenfeld }
1554fd43cf6eSHans Rosenfeld 
1555fd43cf6eSHans Rosenfeld static __inline void
iwn_prph_clrbits(struct iwn_softc * sc,uint32_t addr,uint32_t mask)1556fd43cf6eSHans Rosenfeld iwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1557fd43cf6eSHans Rosenfeld {
1558fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1559fd43cf6eSHans Rosenfeld }
1560fd43cf6eSHans Rosenfeld 
1561fd43cf6eSHans Rosenfeld static __inline void
iwn_prph_write_region_4(struct iwn_softc * sc,uint32_t addr,const uint32_t * data,int count)1562fd43cf6eSHans Rosenfeld iwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1563fd43cf6eSHans Rosenfeld     const uint32_t *data, int count)
1564fd43cf6eSHans Rosenfeld {
1565fd43cf6eSHans Rosenfeld 	for (; count > 0; count--, data++, addr += 4)
1566fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, addr, *data);
1567fd43cf6eSHans Rosenfeld }
1568fd43cf6eSHans Rosenfeld 
1569fd43cf6eSHans Rosenfeld static __inline uint32_t
iwn_mem_read(struct iwn_softc * sc,uint32_t addr)1570fd43cf6eSHans Rosenfeld iwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1571fd43cf6eSHans Rosenfeld {
1572fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1573fd43cf6eSHans Rosenfeld 	IWN_BARRIER_READ_WRITE(sc);
1574fd43cf6eSHans Rosenfeld 	return IWN_READ(sc, IWN_MEM_RDATA);
1575fd43cf6eSHans Rosenfeld }
1576fd43cf6eSHans Rosenfeld 
1577fd43cf6eSHans Rosenfeld static __inline void
iwn_mem_write(struct iwn_softc * sc,uint32_t addr,uint32_t data)1578fd43cf6eSHans Rosenfeld iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1579fd43cf6eSHans Rosenfeld {
1580fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1581fd43cf6eSHans Rosenfeld 	IWN_BARRIER_WRITE(sc);
1582fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1583fd43cf6eSHans Rosenfeld }
1584fd43cf6eSHans Rosenfeld 
1585fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
1586fd43cf6eSHans Rosenfeld static __inline void
iwn_mem_write_2(struct iwn_softc * sc,uint32_t addr,uint16_t data)1587fd43cf6eSHans Rosenfeld iwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1588fd43cf6eSHans Rosenfeld {
1589fd43cf6eSHans Rosenfeld 	uint32_t tmp;
1590fd43cf6eSHans Rosenfeld 
1591fd43cf6eSHans Rosenfeld 	tmp = iwn_mem_read(sc, addr & ~3);
1592fd43cf6eSHans Rosenfeld 	if (addr & 3)
1593fd43cf6eSHans Rosenfeld 		tmp = (tmp & 0x0000ffff) | data << 16;
1594fd43cf6eSHans Rosenfeld 	else
1595fd43cf6eSHans Rosenfeld 		tmp = (tmp & 0xffff0000) | data;
1596fd43cf6eSHans Rosenfeld 	iwn_mem_write(sc, addr & ~3, tmp);
1597fd43cf6eSHans Rosenfeld }
1598fd43cf6eSHans Rosenfeld #endif
1599fd43cf6eSHans Rosenfeld 
1600fd43cf6eSHans Rosenfeld static __inline void
iwn_mem_read_region_4(struct iwn_softc * sc,uint32_t addr,uint32_t * data,int count)1601fd43cf6eSHans Rosenfeld iwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1602fd43cf6eSHans Rosenfeld     int count)
1603fd43cf6eSHans Rosenfeld {
1604fd43cf6eSHans Rosenfeld 	for (; count > 0; count--, addr += 4)
1605fd43cf6eSHans Rosenfeld 		*data++ = iwn_mem_read(sc, addr);
1606fd43cf6eSHans Rosenfeld }
1607fd43cf6eSHans Rosenfeld 
1608fd43cf6eSHans Rosenfeld static __inline void
iwn_mem_set_region_4(struct iwn_softc * sc,uint32_t addr,uint32_t val,int count)1609fd43cf6eSHans Rosenfeld iwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1610fd43cf6eSHans Rosenfeld     int count)
1611fd43cf6eSHans Rosenfeld {
1612fd43cf6eSHans Rosenfeld 	for (; count > 0; count--, addr += 4)
1613fd43cf6eSHans Rosenfeld 		iwn_mem_write(sc, addr, val);
1614fd43cf6eSHans Rosenfeld }
1615fd43cf6eSHans Rosenfeld 
1616fd43cf6eSHans Rosenfeld static int
iwn_eeprom_lock(struct iwn_softc * sc)1617fd43cf6eSHans Rosenfeld iwn_eeprom_lock(struct iwn_softc *sc)
1618fd43cf6eSHans Rosenfeld {
1619fd43cf6eSHans Rosenfeld 	int i, ntries;
1620fd43cf6eSHans Rosenfeld 
1621fd43cf6eSHans Rosenfeld 	for (i = 0; i < 100; i++) {
1622fd43cf6eSHans Rosenfeld 		/* Request exclusive access to EEPROM. */
1623fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1624fd43cf6eSHans Rosenfeld 		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1625fd43cf6eSHans Rosenfeld 
1626fd43cf6eSHans Rosenfeld 		/* Spin until we actually get the lock. */
1627fd43cf6eSHans Rosenfeld 		for (ntries = 0; ntries < 100; ntries++) {
1628fd43cf6eSHans Rosenfeld 			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1629fd43cf6eSHans Rosenfeld 			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1630fd43cf6eSHans Rosenfeld 				return 0;
1631fd43cf6eSHans Rosenfeld 			DELAY(10);
1632fd43cf6eSHans Rosenfeld 		}
1633fd43cf6eSHans Rosenfeld 	}
1634fd43cf6eSHans Rosenfeld 	return ETIMEDOUT;
1635fd43cf6eSHans Rosenfeld }
1636fd43cf6eSHans Rosenfeld 
1637fd43cf6eSHans Rosenfeld static __inline void
iwn_eeprom_unlock(struct iwn_softc * sc)1638fd43cf6eSHans Rosenfeld iwn_eeprom_unlock(struct iwn_softc *sc)
1639fd43cf6eSHans Rosenfeld {
1640fd43cf6eSHans Rosenfeld 	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1641fd43cf6eSHans Rosenfeld }
1642fd43cf6eSHans Rosenfeld 
1643fd43cf6eSHans Rosenfeld /*
1644fd43cf6eSHans Rosenfeld  * Initialize access by host to One Time Programmable ROM.
1645fd43cf6eSHans Rosenfeld  * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1646fd43cf6eSHans Rosenfeld  */
1647fd43cf6eSHans Rosenfeld static int
iwn_init_otprom(struct iwn_softc * sc)1648fd43cf6eSHans Rosenfeld iwn_init_otprom(struct iwn_softc *sc)
1649fd43cf6eSHans Rosenfeld {
1650fd43cf6eSHans Rosenfeld 	uint16_t prev = 0, base, next;
1651fd43cf6eSHans Rosenfeld 	int count, error;
1652fd43cf6eSHans Rosenfeld 
1653fd43cf6eSHans Rosenfeld 	/* Wait for clock stabilization before accessing prph. */
1654fd43cf6eSHans Rosenfeld 	if ((error = iwn_clock_wait(sc)) != 0)
1655fd43cf6eSHans Rosenfeld 		return error;
1656fd43cf6eSHans Rosenfeld 
1657fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
1658fd43cf6eSHans Rosenfeld 		return error;
1659fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1660fd43cf6eSHans Rosenfeld 	DELAY(5);
1661fd43cf6eSHans Rosenfeld 	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1662fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
1663fd43cf6eSHans Rosenfeld 
1664fd43cf6eSHans Rosenfeld 	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1665fd43cf6eSHans Rosenfeld 	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1666fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1667fd43cf6eSHans Rosenfeld 		    IWN_RESET_LINK_PWR_MGMT_DIS);
1668fd43cf6eSHans Rosenfeld 	}
1669fd43cf6eSHans Rosenfeld 	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1670fd43cf6eSHans Rosenfeld 	/* Clear ECC status. */
1671fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_OTP_GP,
1672fd43cf6eSHans Rosenfeld 	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1673fd43cf6eSHans Rosenfeld 
1674fd43cf6eSHans Rosenfeld 	/*
1675fd43cf6eSHans Rosenfeld 	 * Find the block before last block (contains the EEPROM image)
1676fd43cf6eSHans Rosenfeld 	 * for HW without OTP shadow RAM.
1677fd43cf6eSHans Rosenfeld 	 */
1678fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1679fd43cf6eSHans Rosenfeld 		/* Switch to absolute addressing mode. */
1680fd43cf6eSHans Rosenfeld 		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1681fd43cf6eSHans Rosenfeld 		base = 0;
1682fd43cf6eSHans Rosenfeld 		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1683fd43cf6eSHans Rosenfeld 			error = iwn_read_prom_data(sc, base, &next, 2);
1684fd43cf6eSHans Rosenfeld 			if (error != 0)
1685fd43cf6eSHans Rosenfeld 				return error;
1686fd43cf6eSHans Rosenfeld 			if (next == 0)	/* End of linked-list. */
1687fd43cf6eSHans Rosenfeld 				break;
1688fd43cf6eSHans Rosenfeld 			prev = base;
1689fd43cf6eSHans Rosenfeld 			base = le16toh(next);
1690fd43cf6eSHans Rosenfeld 		}
1691fd43cf6eSHans Rosenfeld 		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1692fd43cf6eSHans Rosenfeld 			return EIO;
1693fd43cf6eSHans Rosenfeld 		/* Skip "next" word. */
1694fd43cf6eSHans Rosenfeld 		sc->prom_base = prev + 1;
1695fd43cf6eSHans Rosenfeld 	}
1696fd43cf6eSHans Rosenfeld 	return 0;
1697fd43cf6eSHans Rosenfeld }
1698fd43cf6eSHans Rosenfeld 
1699fd43cf6eSHans Rosenfeld static int
iwn_read_prom_data(struct iwn_softc * sc,uint32_t addr,void * data,int count)1700fd43cf6eSHans Rosenfeld iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1701fd43cf6eSHans Rosenfeld {
1702fd43cf6eSHans Rosenfeld 	uint8_t *out = data;
1703fd43cf6eSHans Rosenfeld 	uint32_t val, tmp;
1704fd43cf6eSHans Rosenfeld 	int ntries;
1705fd43cf6eSHans Rosenfeld 
1706fd43cf6eSHans Rosenfeld 	addr += sc->prom_base;
1707fd43cf6eSHans Rosenfeld 	for (; count > 0; count -= 2, addr++) {
1708fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1709fd43cf6eSHans Rosenfeld 		for (ntries = 0; ntries < 10; ntries++) {
1710fd43cf6eSHans Rosenfeld 			val = IWN_READ(sc, IWN_EEPROM);
1711fd43cf6eSHans Rosenfeld 			if (val & IWN_EEPROM_READ_VALID)
1712fd43cf6eSHans Rosenfeld 				break;
1713fd43cf6eSHans Rosenfeld 			DELAY(5);
1714fd43cf6eSHans Rosenfeld 		}
1715fd43cf6eSHans Rosenfeld 		if (ntries == 10) {
1716fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
1717fd43cf6eSHans Rosenfeld 			    "!timeout reading ROM at 0x%x", addr);
1718fd43cf6eSHans Rosenfeld 			return ETIMEDOUT;
1719fd43cf6eSHans Rosenfeld 		}
1720fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1721fd43cf6eSHans Rosenfeld 			/* OTPROM, check for ECC errors. */
1722fd43cf6eSHans Rosenfeld 			tmp = IWN_READ(sc, IWN_OTP_GP);
1723fd43cf6eSHans Rosenfeld 			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1724fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN,
1725fd43cf6eSHans Rosenfeld 				    "!OTPROM ECC error at 0x%x", addr);
1726fd43cf6eSHans Rosenfeld 				return EIO;
1727fd43cf6eSHans Rosenfeld 			}
1728fd43cf6eSHans Rosenfeld 			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1729fd43cf6eSHans Rosenfeld 				/* Correctable ECC error, clear bit. */
1730fd43cf6eSHans Rosenfeld 				IWN_SETBITS(sc, IWN_OTP_GP,
1731fd43cf6eSHans Rosenfeld 				    IWN_OTP_GP_ECC_CORR_STTS);
1732fd43cf6eSHans Rosenfeld 			}
1733fd43cf6eSHans Rosenfeld 		}
1734fd43cf6eSHans Rosenfeld 		*out++ = val >> 16;
1735fd43cf6eSHans Rosenfeld 		if (count > 1)
1736fd43cf6eSHans Rosenfeld 			*out++ = val >> 24;
1737fd43cf6eSHans Rosenfeld 	}
1738fd43cf6eSHans Rosenfeld 	return 0;
1739fd43cf6eSHans Rosenfeld }
1740fd43cf6eSHans Rosenfeld 
1741fd43cf6eSHans Rosenfeld static int
iwn_dma_contig_alloc(struct iwn_softc * sc,struct iwn_dma_info * dma,uint_t size,uint_t flags,void ** kvap,ddi_device_acc_attr_t * acc_attr,uint_t align)1742fd43cf6eSHans Rosenfeld iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1743fd43cf6eSHans Rosenfeld     uint_t size, uint_t flags, void **kvap, ddi_device_acc_attr_t *acc_attr,
1744fd43cf6eSHans Rosenfeld     uint_t align)
1745fd43cf6eSHans Rosenfeld {
1746fd43cf6eSHans Rosenfeld 	ddi_dma_attr_t dma_attr = {
1747fd43cf6eSHans Rosenfeld 		.dma_attr_version	= DMA_ATTR_V0,
1748fd43cf6eSHans Rosenfeld 		.dma_attr_addr_lo	= 0,
1749fd43cf6eSHans Rosenfeld 		.dma_attr_addr_hi	= 0xfffffffffULL,
1750fd43cf6eSHans Rosenfeld 		.dma_attr_count_max	= 0xfffffffffULL,
1751fd43cf6eSHans Rosenfeld 		.dma_attr_align		= align,
1752fd43cf6eSHans Rosenfeld 		.dma_attr_burstsizes	= 0x7ff,
1753fd43cf6eSHans Rosenfeld 		.dma_attr_minxfer	= 1,
1754fd43cf6eSHans Rosenfeld 		.dma_attr_maxxfer	= 0xfffffffffULL,
1755fd43cf6eSHans Rosenfeld 		.dma_attr_seg		= 0xfffffffffULL,
1756fd43cf6eSHans Rosenfeld 		.dma_attr_sgllen	= 1,
1757fd43cf6eSHans Rosenfeld 		.dma_attr_granular	= 1,
1758fd43cf6eSHans Rosenfeld 		.dma_attr_flags		= 0,
1759fd43cf6eSHans Rosenfeld 	};
1760fd43cf6eSHans Rosenfeld 	int error;
1761fd43cf6eSHans Rosenfeld 
1762fd43cf6eSHans Rosenfeld 	error = ddi_dma_alloc_handle(sc->sc_dip, &dma_attr, DDI_DMA_SLEEP, NULL,
1763fd43cf6eSHans Rosenfeld 	    &dma->dma_hdl);
1764fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1765fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1766fd43cf6eSHans Rosenfeld 		    "ddi_dma_alloc_handle() failed, error = %d", error);
1767fd43cf6eSHans Rosenfeld 		goto fail;
1768fd43cf6eSHans Rosenfeld 	}
1769fd43cf6eSHans Rosenfeld 
1770fd43cf6eSHans Rosenfeld 	error = ddi_dma_mem_alloc(dma->dma_hdl, size, acc_attr,
1771fd43cf6eSHans Rosenfeld 	    flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING), DDI_DMA_SLEEP, 0,
1772fd43cf6eSHans Rosenfeld 	    &dma->vaddr, &dma->length, &dma->acc_hdl);
1773fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1774fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1775fd43cf6eSHans Rosenfeld 		    "ddi_dma_mem_alloc() failed, error = %d", error);
1776fd43cf6eSHans Rosenfeld 		goto fail2;
1777fd43cf6eSHans Rosenfeld 	}
1778fd43cf6eSHans Rosenfeld 
1779fd43cf6eSHans Rosenfeld 	bzero(dma->vaddr, dma->length);
1780fd43cf6eSHans Rosenfeld 
1781fd43cf6eSHans Rosenfeld 	error = ddi_dma_addr_bind_handle(dma->dma_hdl, NULL, dma->vaddr,
1782fd43cf6eSHans Rosenfeld 	    dma->length, flags, DDI_DMA_SLEEP, NULL, &dma->cookie,
1783fd43cf6eSHans Rosenfeld 	    &dma->ncookies);
1784fd43cf6eSHans Rosenfeld 	if (error != DDI_DMA_MAPPED) {
1785fd43cf6eSHans Rosenfeld 		dma->ncookies = 0;
1786fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1787fd43cf6eSHans Rosenfeld 		    "ddi_dma_addr_bind_handle() failed, error = %d", error);
1788fd43cf6eSHans Rosenfeld 		goto fail3;
1789fd43cf6eSHans Rosenfeld 	}
1790fd43cf6eSHans Rosenfeld 
1791fd43cf6eSHans Rosenfeld 	dma->size = size;
1792fd43cf6eSHans Rosenfeld 	dma->paddr = dma->cookie.dmac_laddress;
1793fd43cf6eSHans Rosenfeld 
1794fd43cf6eSHans Rosenfeld 	if (kvap != NULL)
1795fd43cf6eSHans Rosenfeld 		*kvap = (void *)dma->vaddr;
1796fd43cf6eSHans Rosenfeld 
1797fd43cf6eSHans Rosenfeld 	return (DDI_SUCCESS);
1798fd43cf6eSHans Rosenfeld 
1799fd43cf6eSHans Rosenfeld fail3:
1800fd43cf6eSHans Rosenfeld 	ddi_dma_mem_free(&dma->acc_hdl);
1801fd43cf6eSHans Rosenfeld fail2:
1802fd43cf6eSHans Rosenfeld 	ddi_dma_free_handle(&dma->dma_hdl);
1803fd43cf6eSHans Rosenfeld fail:
1804fd43cf6eSHans Rosenfeld 	bzero(dma, sizeof (struct iwn_dma_info));
1805fd43cf6eSHans Rosenfeld 	return (DDI_FAILURE);
1806fd43cf6eSHans Rosenfeld }
1807fd43cf6eSHans Rosenfeld 
1808fd43cf6eSHans Rosenfeld static void
iwn_dma_contig_free(struct iwn_dma_info * dma)1809fd43cf6eSHans Rosenfeld iwn_dma_contig_free(struct iwn_dma_info *dma)
1810fd43cf6eSHans Rosenfeld {
1811fd43cf6eSHans Rosenfeld 	if (dma->dma_hdl != NULL) {
1812fd43cf6eSHans Rosenfeld 		if (dma->ncookies)
1813fd43cf6eSHans Rosenfeld 			(void) ddi_dma_unbind_handle(dma->dma_hdl);
1814fd43cf6eSHans Rosenfeld 		ddi_dma_free_handle(&dma->dma_hdl);
1815fd43cf6eSHans Rosenfeld 	}
1816fd43cf6eSHans Rosenfeld 
1817fd43cf6eSHans Rosenfeld 	if (dma->acc_hdl != NULL)
1818fd43cf6eSHans Rosenfeld 		ddi_dma_mem_free(&dma->acc_hdl);
1819fd43cf6eSHans Rosenfeld 
1820fd43cf6eSHans Rosenfeld 	bzero(dma, sizeof (struct iwn_dma_info));
1821fd43cf6eSHans Rosenfeld }
1822fd43cf6eSHans Rosenfeld 
1823fd43cf6eSHans Rosenfeld static int
iwn_alloc_sched(struct iwn_softc * sc)1824fd43cf6eSHans Rosenfeld iwn_alloc_sched(struct iwn_softc *sc)
1825fd43cf6eSHans Rosenfeld {
1826fd43cf6eSHans Rosenfeld 	/* TX scheduler rings must be aligned on a 1KB boundary. */
1827fd43cf6eSHans Rosenfeld 
1828fd43cf6eSHans Rosenfeld 	return iwn_dma_contig_alloc(sc, &sc->sched_dma, sc->schedsz,
1829fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&sc->sched,
1830fd43cf6eSHans Rosenfeld 	    &iwn_dma_accattr, 1024);
1831fd43cf6eSHans Rosenfeld }
1832fd43cf6eSHans Rosenfeld 
1833fd43cf6eSHans Rosenfeld static void
iwn_free_sched(struct iwn_softc * sc)1834fd43cf6eSHans Rosenfeld iwn_free_sched(struct iwn_softc *sc)
1835fd43cf6eSHans Rosenfeld {
1836fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&sc->sched_dma);
1837fd43cf6eSHans Rosenfeld }
1838fd43cf6eSHans Rosenfeld 
1839fd43cf6eSHans Rosenfeld static int
iwn_alloc_kw(struct iwn_softc * sc)1840fd43cf6eSHans Rosenfeld iwn_alloc_kw(struct iwn_softc *sc)
1841fd43cf6eSHans Rosenfeld {
1842fd43cf6eSHans Rosenfeld 	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1843fd43cf6eSHans Rosenfeld 
1844fd43cf6eSHans Rosenfeld 	return iwn_dma_contig_alloc(sc, &sc->kw_dma, IWN_KW_SIZE,
1845fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, NULL, &iwn_dma_accattr, 4096);
1846fd43cf6eSHans Rosenfeld }
1847fd43cf6eSHans Rosenfeld 
1848fd43cf6eSHans Rosenfeld static void
iwn_free_kw(struct iwn_softc * sc)1849fd43cf6eSHans Rosenfeld iwn_free_kw(struct iwn_softc *sc)
1850fd43cf6eSHans Rosenfeld {
1851fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&sc->kw_dma);
1852fd43cf6eSHans Rosenfeld }
1853fd43cf6eSHans Rosenfeld 
1854fd43cf6eSHans Rosenfeld static int
iwn_alloc_ict(struct iwn_softc * sc)1855fd43cf6eSHans Rosenfeld iwn_alloc_ict(struct iwn_softc *sc)
1856fd43cf6eSHans Rosenfeld {
1857fd43cf6eSHans Rosenfeld 	/* ICT table must be aligned on a 4KB boundary. */
1858fd43cf6eSHans Rosenfeld 
1859fd43cf6eSHans Rosenfeld 	return iwn_dma_contig_alloc(sc, &sc->ict_dma, IWN_ICT_SIZE,
1860fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&sc->ict,
1861fd43cf6eSHans Rosenfeld 	    &iwn_dma_descattr, 4096);
1862fd43cf6eSHans Rosenfeld }
1863fd43cf6eSHans Rosenfeld 
1864fd43cf6eSHans Rosenfeld static void
iwn_free_ict(struct iwn_softc * sc)1865fd43cf6eSHans Rosenfeld iwn_free_ict(struct iwn_softc *sc)
1866fd43cf6eSHans Rosenfeld {
1867fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&sc->ict_dma);
1868fd43cf6eSHans Rosenfeld }
1869fd43cf6eSHans Rosenfeld 
1870fd43cf6eSHans Rosenfeld static int
iwn_alloc_fwmem(struct iwn_softc * sc)1871fd43cf6eSHans Rosenfeld iwn_alloc_fwmem(struct iwn_softc *sc)
1872fd43cf6eSHans Rosenfeld {
1873fd43cf6eSHans Rosenfeld 	/* Must be aligned on a 16-byte boundary. */
1874fd43cf6eSHans Rosenfeld 	return iwn_dma_contig_alloc(sc, &sc->fw_dma, sc->fwsz,
1875fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, NULL, &iwn_dma_accattr, 16);
1876fd43cf6eSHans Rosenfeld }
1877fd43cf6eSHans Rosenfeld 
1878fd43cf6eSHans Rosenfeld static void
iwn_free_fwmem(struct iwn_softc * sc)1879fd43cf6eSHans Rosenfeld iwn_free_fwmem(struct iwn_softc *sc)
1880fd43cf6eSHans Rosenfeld {
1881fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&sc->fw_dma);
1882fd43cf6eSHans Rosenfeld }
1883fd43cf6eSHans Rosenfeld 
1884fd43cf6eSHans Rosenfeld static int
iwn_alloc_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)1885fd43cf6eSHans Rosenfeld iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1886fd43cf6eSHans Rosenfeld {
1887fd43cf6eSHans Rosenfeld 	size_t size;
1888fd43cf6eSHans Rosenfeld 	int i, error;
1889fd43cf6eSHans Rosenfeld 
1890fd43cf6eSHans Rosenfeld 	ring->cur = 0;
1891fd43cf6eSHans Rosenfeld 
1892fd43cf6eSHans Rosenfeld 	/* Allocate RX descriptors (256-byte aligned). */
1893fd43cf6eSHans Rosenfeld 	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1894fd43cf6eSHans Rosenfeld 	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, size,
1895fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&ring->desc,
1896fd43cf6eSHans Rosenfeld 	    &iwn_dma_descattr, 256);
1897fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1898fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1899fd43cf6eSHans Rosenfeld 		    "!could not allocate RX ring DMA memory");
1900fd43cf6eSHans Rosenfeld 		goto fail;
1901fd43cf6eSHans Rosenfeld 	}
1902fd43cf6eSHans Rosenfeld 
1903fd43cf6eSHans Rosenfeld 	/* Allocate RX status area (16-byte aligned). */
1904fd43cf6eSHans Rosenfeld 	error = iwn_dma_contig_alloc(sc, &ring->stat_dma,
1905fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_rx_status), DDI_DMA_CONSISTENT | DDI_DMA_RDWR,
1906fd43cf6eSHans Rosenfeld 	    (void **)&ring->stat, &iwn_dma_descattr, 16);
1907fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1908fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1909fd43cf6eSHans Rosenfeld 		    "!could not allocate RX status DMA memory");
1910fd43cf6eSHans Rosenfeld 		goto fail;
1911fd43cf6eSHans Rosenfeld 	}
1912fd43cf6eSHans Rosenfeld 
1913fd43cf6eSHans Rosenfeld 	/*
1914fd43cf6eSHans Rosenfeld 	 * Allocate and map RX buffers.
1915fd43cf6eSHans Rosenfeld 	 */
1916fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1917fd43cf6eSHans Rosenfeld 		struct iwn_rx_data *data = &ring->data[i];
1918fd43cf6eSHans Rosenfeld 
1919fd43cf6eSHans Rosenfeld 		error = iwn_dma_contig_alloc(sc, &data->dma_data, IWN_RBUF_SIZE,
1920fd43cf6eSHans Rosenfeld 		    DDI_DMA_CONSISTENT | DDI_DMA_READ, NULL, &iwn_dma_accattr,
1921fd43cf6eSHans Rosenfeld 		    256);
1922fd43cf6eSHans Rosenfeld 		if (error != DDI_SUCCESS) {
1923fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
1924fd43cf6eSHans Rosenfeld 			    "!could not create RX buf DMA map");
1925fd43cf6eSHans Rosenfeld 			goto fail;
1926fd43cf6eSHans Rosenfeld 		}
1927fd43cf6eSHans Rosenfeld 
1928fd43cf6eSHans Rosenfeld 		/* Set physical address of RX buffer (256-byte aligned). */
1929fd43cf6eSHans Rosenfeld 		ring->desc[i] = htole32(data->dma_data.paddr >> 8);
1930fd43cf6eSHans Rosenfeld 	}
1931fd43cf6eSHans Rosenfeld 
1932fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->desc_dma.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
1933fd43cf6eSHans Rosenfeld 
1934fd43cf6eSHans Rosenfeld 	return 0;
1935fd43cf6eSHans Rosenfeld 
1936fd43cf6eSHans Rosenfeld fail:	iwn_free_rx_ring(sc, ring);
1937fd43cf6eSHans Rosenfeld 	return error;
1938fd43cf6eSHans Rosenfeld }
1939fd43cf6eSHans Rosenfeld 
1940fd43cf6eSHans Rosenfeld static void
iwn_reset_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)1941fd43cf6eSHans Rosenfeld iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1942fd43cf6eSHans Rosenfeld {
1943fd43cf6eSHans Rosenfeld 	int ntries;
1944fd43cf6eSHans Rosenfeld 
1945fd43cf6eSHans Rosenfeld 	if (iwn_nic_lock(sc) == 0) {
1946fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1947fd43cf6eSHans Rosenfeld 		for (ntries = 0; ntries < 1000; ntries++) {
1948fd43cf6eSHans Rosenfeld 			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1949fd43cf6eSHans Rosenfeld 			    IWN_FH_RX_STATUS_IDLE)
1950fd43cf6eSHans Rosenfeld 				break;
1951fd43cf6eSHans Rosenfeld 			DELAY(10);
1952fd43cf6eSHans Rosenfeld 		}
1953fd43cf6eSHans Rosenfeld 		iwn_nic_unlock(sc);
1954fd43cf6eSHans Rosenfeld 	}
1955fd43cf6eSHans Rosenfeld 	ring->cur = 0;
1956fd43cf6eSHans Rosenfeld 	sc->last_rx_valid = 0;
1957fd43cf6eSHans Rosenfeld }
1958fd43cf6eSHans Rosenfeld 
1959fd43cf6eSHans Rosenfeld static void
iwn_free_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)1960fd43cf6eSHans Rosenfeld iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1961fd43cf6eSHans Rosenfeld {
1962fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(sc));
1963fd43cf6eSHans Rosenfeld 	int i;
1964fd43cf6eSHans Rosenfeld 
1965fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&ring->desc_dma);
1966fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&ring->stat_dma);
1967fd43cf6eSHans Rosenfeld 
1968fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1969fd43cf6eSHans Rosenfeld 		struct iwn_rx_data *data = &ring->data[i];
1970fd43cf6eSHans Rosenfeld 
1971fd43cf6eSHans Rosenfeld 		if (data->dma_data.dma_hdl)
1972fd43cf6eSHans Rosenfeld 			iwn_dma_contig_free(&data->dma_data);
1973fd43cf6eSHans Rosenfeld 	}
1974fd43cf6eSHans Rosenfeld }
1975fd43cf6eSHans Rosenfeld 
1976fd43cf6eSHans Rosenfeld static int
iwn_alloc_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring,int qid)1977fd43cf6eSHans Rosenfeld iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1978fd43cf6eSHans Rosenfeld {
1979fd43cf6eSHans Rosenfeld 	uintptr_t paddr;
1980fd43cf6eSHans Rosenfeld 	size_t size;
1981fd43cf6eSHans Rosenfeld 	int i, error;
1982fd43cf6eSHans Rosenfeld 
1983fd43cf6eSHans Rosenfeld 	ring->qid = qid;
1984fd43cf6eSHans Rosenfeld 	ring->queued = 0;
1985fd43cf6eSHans Rosenfeld 	ring->cur = 0;
1986fd43cf6eSHans Rosenfeld 
1987fd43cf6eSHans Rosenfeld 	/* Allocate TX descriptors (256-byte aligned). */
1988fd43cf6eSHans Rosenfeld 	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1989fd43cf6eSHans Rosenfeld 	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, size,
1990fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_WRITE, (void **)&ring->desc,
1991fd43cf6eSHans Rosenfeld 	    &iwn_dma_descattr, 256);
1992fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
1993fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
1994fd43cf6eSHans Rosenfeld 		    "!could not allocate TX ring DMA memory");
1995fd43cf6eSHans Rosenfeld 		goto fail;
1996fd43cf6eSHans Rosenfeld 	}
1997fd43cf6eSHans Rosenfeld 	/*
1998fd43cf6eSHans Rosenfeld 	 * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need
1999fd43cf6eSHans Rosenfeld 	 * to allocate commands space for other rings.
2000fd43cf6eSHans Rosenfeld 	 * XXX Do we really need to allocate descriptors for other rings?
2001fd43cf6eSHans Rosenfeld 	 */
2002fd43cf6eSHans Rosenfeld 	if (qid > 4)
2003fd43cf6eSHans Rosenfeld 		return 0;
2004fd43cf6eSHans Rosenfeld 
2005fd43cf6eSHans Rosenfeld 	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
2006fd43cf6eSHans Rosenfeld 	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, size,
2007fd43cf6eSHans Rosenfeld 	    DDI_DMA_CONSISTENT | DDI_DMA_WRITE, (void **)&ring->cmd,
2008fd43cf6eSHans Rosenfeld 	    &iwn_dma_accattr, 4);
2009fd43cf6eSHans Rosenfeld 	if (error != DDI_SUCCESS) {
2010fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
2011fd43cf6eSHans Rosenfeld 		    "!could not allocate TX cmd DMA memory");
2012fd43cf6eSHans Rosenfeld 		goto fail;
2013fd43cf6eSHans Rosenfeld 	}
2014fd43cf6eSHans Rosenfeld 
2015fd43cf6eSHans Rosenfeld 	paddr = ring->cmd_dma.paddr;
2016fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2017fd43cf6eSHans Rosenfeld 		struct iwn_tx_data *data = &ring->data[i];
2018fd43cf6eSHans Rosenfeld 
2019fd43cf6eSHans Rosenfeld 		data->cmd_paddr = paddr;
2020fd43cf6eSHans Rosenfeld 		data->scratch_paddr = paddr + 12;
2021fd43cf6eSHans Rosenfeld 		paddr += sizeof (struct iwn_tx_cmd);
2022fd43cf6eSHans Rosenfeld 
2023fd43cf6eSHans Rosenfeld 		error = iwn_dma_contig_alloc(sc, &data->dma_data, IWN_TBUF_SIZE,
2024fd43cf6eSHans Rosenfeld 		    DDI_DMA_CONSISTENT | DDI_DMA_WRITE, NULL, &iwn_dma_accattr,
2025fd43cf6eSHans Rosenfeld 		    256);
2026fd43cf6eSHans Rosenfeld 		if (error != DDI_SUCCESS) {
2027fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2028fd43cf6eSHans Rosenfeld 			    "!could not create TX buf DMA map");
2029fd43cf6eSHans Rosenfeld 			goto fail;
2030fd43cf6eSHans Rosenfeld 		}
2031fd43cf6eSHans Rosenfeld 	}
2032fd43cf6eSHans Rosenfeld 	return 0;
2033fd43cf6eSHans Rosenfeld 
2034fd43cf6eSHans Rosenfeld fail:	iwn_free_tx_ring(sc, ring);
2035fd43cf6eSHans Rosenfeld 	return error;
2036fd43cf6eSHans Rosenfeld }
2037fd43cf6eSHans Rosenfeld 
2038fd43cf6eSHans Rosenfeld static void
iwn_reset_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring)2039fd43cf6eSHans Rosenfeld iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
2040fd43cf6eSHans Rosenfeld {
2041fd43cf6eSHans Rosenfeld 	int i;
2042fd43cf6eSHans Rosenfeld 
2043fd43cf6eSHans Rosenfeld 	if (ring->qid < 4)
2044fd43cf6eSHans Rosenfeld 		for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2045fd43cf6eSHans Rosenfeld 			struct iwn_tx_data *data = &ring->data[i];
2046fd43cf6eSHans Rosenfeld 
2047fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0,
2048fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORDEV);
2049fd43cf6eSHans Rosenfeld 		}
2050fd43cf6eSHans Rosenfeld 
2051fd43cf6eSHans Rosenfeld 	/* Clear TX descriptors. */
2052fd43cf6eSHans Rosenfeld 	memset(ring->desc, 0, ring->desc_dma.size);
2053fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->desc_dma.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
2054fd43cf6eSHans Rosenfeld 	sc->qfullmsk &= ~(1 << ring->qid);
2055fd43cf6eSHans Rosenfeld 	ring->queued = 0;
2056fd43cf6eSHans Rosenfeld 	ring->cur = 0;
2057fd43cf6eSHans Rosenfeld }
2058fd43cf6eSHans Rosenfeld 
2059fd43cf6eSHans Rosenfeld static void
iwn_free_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring)2060fd43cf6eSHans Rosenfeld iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
2061fd43cf6eSHans Rosenfeld {
2062fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(sc));
2063fd43cf6eSHans Rosenfeld 	int i;
2064fd43cf6eSHans Rosenfeld 
2065fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&ring->desc_dma);
2066fd43cf6eSHans Rosenfeld 	iwn_dma_contig_free(&ring->cmd_dma);
2067fd43cf6eSHans Rosenfeld 
2068fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2069fd43cf6eSHans Rosenfeld 		struct iwn_tx_data *data = &ring->data[i];
2070fd43cf6eSHans Rosenfeld 
2071fd43cf6eSHans Rosenfeld 		if (data->dma_data.dma_hdl)
2072fd43cf6eSHans Rosenfeld 			iwn_dma_contig_free(&data->dma_data);
2073fd43cf6eSHans Rosenfeld 	}
2074fd43cf6eSHans Rosenfeld }
2075fd43cf6eSHans Rosenfeld 
2076fd43cf6eSHans Rosenfeld static void
iwn5000_ict_reset(struct iwn_softc * sc)2077fd43cf6eSHans Rosenfeld iwn5000_ict_reset(struct iwn_softc *sc)
2078fd43cf6eSHans Rosenfeld {
2079fd43cf6eSHans Rosenfeld 	/* Disable interrupts. */
2080fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, 0);
2081fd43cf6eSHans Rosenfeld 
2082fd43cf6eSHans Rosenfeld 	/* Reset ICT table. */
2083fd43cf6eSHans Rosenfeld 	memset(sc->ict, 0, IWN_ICT_SIZE);
2084fd43cf6eSHans Rosenfeld 	sc->ict_cur = 0;
2085fd43cf6eSHans Rosenfeld 
2086fd43cf6eSHans Rosenfeld 	/* Set physical address of ICT table (4KB aligned). */
2087fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
2088fd43cf6eSHans Rosenfeld 	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
2089fd43cf6eSHans Rosenfeld 
2090fd43cf6eSHans Rosenfeld 	/* Enable periodic RX interrupt. */
2091fd43cf6eSHans Rosenfeld 	sc->int_mask |= IWN_INT_RX_PERIODIC;
2092fd43cf6eSHans Rosenfeld 	/* Switch to ICT interrupt mode in driver. */
2093fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_USE_ICT;
2094fd43cf6eSHans Rosenfeld 
2095fd43cf6eSHans Rosenfeld 	/* Re-enable interrupts. */
2096fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
2097fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
2098fd43cf6eSHans Rosenfeld }
2099fd43cf6eSHans Rosenfeld 
2100fd43cf6eSHans Rosenfeld static int
iwn_read_eeprom(struct iwn_softc * sc)2101fd43cf6eSHans Rosenfeld iwn_read_eeprom(struct iwn_softc *sc)
2102fd43cf6eSHans Rosenfeld {
2103fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
2104fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2105fd43cf6eSHans Rosenfeld 	uint16_t val;
2106fd43cf6eSHans Rosenfeld 	int error;
2107fd43cf6eSHans Rosenfeld 
2108fd43cf6eSHans Rosenfeld 	/* Check whether adapter has an EEPROM or an OTPROM. */
2109fd43cf6eSHans Rosenfeld 	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
2110fd43cf6eSHans Rosenfeld 	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
2111fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
2112fd43cf6eSHans Rosenfeld 	IWN_DBG("%s found",
2113fd43cf6eSHans Rosenfeld 	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
2114fd43cf6eSHans Rosenfeld 
2115fd43cf6eSHans Rosenfeld 	/* Adapter has to be powered on for EEPROM access to work. */
2116fd43cf6eSHans Rosenfeld 	if ((error = iwn_apm_init(sc)) != 0) {
2117fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
2118fd43cf6eSHans Rosenfeld 		    "!could not power ON adapter");
2119fd43cf6eSHans Rosenfeld 		return error;
2120fd43cf6eSHans Rosenfeld 	}
2121fd43cf6eSHans Rosenfeld 
2122fd43cf6eSHans Rosenfeld 	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
2123fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
2124fd43cf6eSHans Rosenfeld 		    "!bad ROM signature");
2125fd43cf6eSHans Rosenfeld 		return EIO;
2126fd43cf6eSHans Rosenfeld 	}
2127fd43cf6eSHans Rosenfeld 	if ((error = iwn_eeprom_lock(sc)) != 0) {
2128fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
2129fd43cf6eSHans Rosenfeld 		    "!could not lock ROM (error=%d)", error);
2130fd43cf6eSHans Rosenfeld 		return error;
2131fd43cf6eSHans Rosenfeld 	}
2132fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
2133fd43cf6eSHans Rosenfeld 		if ((error = iwn_init_otprom(sc)) != 0) {
2134fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2135fd43cf6eSHans Rosenfeld 			    "!could not initialize OTPROM");
2136fd43cf6eSHans Rosenfeld 			return error;
2137fd43cf6eSHans Rosenfeld 		}
2138fd43cf6eSHans Rosenfeld 	}
2139fd43cf6eSHans Rosenfeld 
2140fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
2141fd43cf6eSHans Rosenfeld 	IWN_DBG("SKU capabilities=0x%04x", le16toh(val));
2142fd43cf6eSHans Rosenfeld 	/* Check if HT support is bonded out. */
2143fd43cf6eSHans Rosenfeld 	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
2144fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_HAS_11N;
2145fd43cf6eSHans Rosenfeld 
2146fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
2147fd43cf6eSHans Rosenfeld 	sc->rfcfg = le16toh(val);
2148fd43cf6eSHans Rosenfeld 	IWN_DBG("radio config=0x%04x", sc->rfcfg);
2149fd43cf6eSHans Rosenfeld 	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
2150fd43cf6eSHans Rosenfeld 	if (sc->txchainmask == 0)
2151fd43cf6eSHans Rosenfeld 		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
2152fd43cf6eSHans Rosenfeld 	if (sc->rxchainmask == 0)
2153fd43cf6eSHans Rosenfeld 		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
2154fd43cf6eSHans Rosenfeld 
2155fd43cf6eSHans Rosenfeld 	/* Read MAC address. */
2156fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_macaddr, 6);
2157fd43cf6eSHans Rosenfeld 
2158fd43cf6eSHans Rosenfeld 	/* Read adapter-specific information from EEPROM. */
2159fd43cf6eSHans Rosenfeld 	ops->read_eeprom(sc);
2160fd43cf6eSHans Rosenfeld 
2161fd43cf6eSHans Rosenfeld 	iwn_apm_stop(sc);	/* Power OFF adapter. */
2162fd43cf6eSHans Rosenfeld 
2163fd43cf6eSHans Rosenfeld 	iwn_eeprom_unlock(sc);
2164fd43cf6eSHans Rosenfeld 	return 0;
2165fd43cf6eSHans Rosenfeld }
2166fd43cf6eSHans Rosenfeld 
2167fd43cf6eSHans Rosenfeld static void
iwn4965_read_eeprom(struct iwn_softc * sc)2168fd43cf6eSHans Rosenfeld iwn4965_read_eeprom(struct iwn_softc *sc)
2169fd43cf6eSHans Rosenfeld {
2170fd43cf6eSHans Rosenfeld 	uint32_t addr;
2171fd43cf6eSHans Rosenfeld 	uint16_t val;
2172fd43cf6eSHans Rosenfeld 	int i;
2173fd43cf6eSHans Rosenfeld 
2174fd43cf6eSHans Rosenfeld 	/* Read regulatory domain (4 ASCII characters). */
2175fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
2176fd43cf6eSHans Rosenfeld 
2177fd43cf6eSHans Rosenfeld 	/* Read the list of authorized channels (20MHz ones only). */
2178fd43cf6eSHans Rosenfeld 	for (i = 0; i < 5; i++) {
2179fd43cf6eSHans Rosenfeld 		addr = iwn4965_regulatory_bands[i];
2180fd43cf6eSHans Rosenfeld 		iwn_read_eeprom_channels(sc, i, addr);
2181fd43cf6eSHans Rosenfeld 	}
2182fd43cf6eSHans Rosenfeld 
2183fd43cf6eSHans Rosenfeld 	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
2184fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
2185fd43cf6eSHans Rosenfeld 	sc->maxpwr2GHz = val & 0xff;
2186fd43cf6eSHans Rosenfeld 	sc->maxpwr5GHz = val >> 8;
2187fd43cf6eSHans Rosenfeld 	/* Check that EEPROM values are within valid range. */
2188fd43cf6eSHans Rosenfeld 	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
2189fd43cf6eSHans Rosenfeld 		sc->maxpwr5GHz = 38;
2190fd43cf6eSHans Rosenfeld 	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
2191fd43cf6eSHans Rosenfeld 		sc->maxpwr2GHz = 38;
2192fd43cf6eSHans Rosenfeld 	IWN_DBG("maxpwr 2GHz=%d 5GHz=%d", sc->maxpwr2GHz, sc->maxpwr5GHz);
2193fd43cf6eSHans Rosenfeld 
2194fd43cf6eSHans Rosenfeld 	/* Read samples for each TX power group. */
2195fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
2196fd43cf6eSHans Rosenfeld 	    sizeof sc->bands);
2197fd43cf6eSHans Rosenfeld 
2198fd43cf6eSHans Rosenfeld 	/* Read voltage at which samples were taken. */
2199fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
2200fd43cf6eSHans Rosenfeld 	sc->eeprom_voltage = (int16_t)le16toh(val);
2201fd43cf6eSHans Rosenfeld 	IWN_DBG("voltage=%d (in 0.3V)", sc->eeprom_voltage);
2202fd43cf6eSHans Rosenfeld 
2203fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG
2204fd43cf6eSHans Rosenfeld 	/* Print samples. */
2205fd43cf6eSHans Rosenfeld 	if (iwn_dbg_print != 0) {
2206fd43cf6eSHans Rosenfeld 		for (i = 0; i < IWN_NBANDS; i++)
2207fd43cf6eSHans Rosenfeld 			iwn4965_print_power_group(sc, i);
2208fd43cf6eSHans Rosenfeld 	}
2209fd43cf6eSHans Rosenfeld #endif
2210fd43cf6eSHans Rosenfeld }
2211fd43cf6eSHans Rosenfeld 
2212fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG
2213fd43cf6eSHans Rosenfeld static void
iwn4965_print_power_group(struct iwn_softc * sc,int i)2214fd43cf6eSHans Rosenfeld iwn4965_print_power_group(struct iwn_softc *sc, int i)
2215fd43cf6eSHans Rosenfeld {
2216fd43cf6eSHans Rosenfeld 	struct iwn4965_eeprom_band *band = &sc->bands[i];
2217fd43cf6eSHans Rosenfeld 	struct iwn4965_eeprom_chan_samples *chans = band->chans;
2218fd43cf6eSHans Rosenfeld 	int j, c;
2219fd43cf6eSHans Rosenfeld 
2220fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!===band %d===", i);
2221fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!chan lo=%d, chan hi=%d", band->lo,
2222fd43cf6eSHans Rosenfeld 	    band->hi);
2223fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT,  "!chan1 num=%d", chans[0].num);
2224fd43cf6eSHans Rosenfeld 	for (c = 0; c < 2; c++) {
2225fd43cf6eSHans Rosenfeld 		for (j = 0; j < IWN_NSAMPLES; j++) {
2226fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_CONT, "!chain %d, sample %d: "
2227fd43cf6eSHans Rosenfeld 			    "temp=%d gain=%d power=%d pa_det=%d", c, j,
2228fd43cf6eSHans Rosenfeld 			    chans[0].samples[c][j].temp,
2229fd43cf6eSHans Rosenfeld 			    chans[0].samples[c][j].gain,
2230fd43cf6eSHans Rosenfeld 			    chans[0].samples[c][j].power,
2231fd43cf6eSHans Rosenfeld 			    chans[0].samples[c][j].pa_det);
2232fd43cf6eSHans Rosenfeld 		}
2233fd43cf6eSHans Rosenfeld 	}
2234fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!chan2 num=%d", chans[1].num);
2235fd43cf6eSHans Rosenfeld 	for (c = 0; c < 2; c++) {
2236fd43cf6eSHans Rosenfeld 		for (j = 0; j < IWN_NSAMPLES; j++) {
2237fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_CONT, "!chain %d, sample %d: "
2238fd43cf6eSHans Rosenfeld 			    "temp=%d gain=%d power=%d pa_det=%d", c, j,
2239fd43cf6eSHans Rosenfeld 			    chans[1].samples[c][j].temp,
2240fd43cf6eSHans Rosenfeld 			    chans[1].samples[c][j].gain,
2241fd43cf6eSHans Rosenfeld 			    chans[1].samples[c][j].power,
2242fd43cf6eSHans Rosenfeld 			    chans[1].samples[c][j].pa_det);
2243fd43cf6eSHans Rosenfeld 		}
2244fd43cf6eSHans Rosenfeld 	}
2245fd43cf6eSHans Rosenfeld }
2246fd43cf6eSHans Rosenfeld #endif
2247fd43cf6eSHans Rosenfeld 
2248fd43cf6eSHans Rosenfeld static void
iwn5000_read_eeprom(struct iwn_softc * sc)2249fd43cf6eSHans Rosenfeld iwn5000_read_eeprom(struct iwn_softc *sc)
2250fd43cf6eSHans Rosenfeld {
2251fd43cf6eSHans Rosenfeld 	struct iwn5000_eeprom_calib_hdr hdr;
2252fd43cf6eSHans Rosenfeld 	int32_t volt;
2253fd43cf6eSHans Rosenfeld 	uint32_t base, addr;
2254fd43cf6eSHans Rosenfeld 	uint16_t val;
2255fd43cf6eSHans Rosenfeld 	int i;
2256fd43cf6eSHans Rosenfeld 
2257fd43cf6eSHans Rosenfeld 	/* Read regulatory domain (4 ASCII characters). */
2258fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2259fd43cf6eSHans Rosenfeld 	base = le16toh(val);
2260fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
2261fd43cf6eSHans Rosenfeld 	    sc->eeprom_domain, 4);
2262fd43cf6eSHans Rosenfeld 
2263fd43cf6eSHans Rosenfeld 	/* Read the list of authorized channels (20MHz ones only). */
2264fd43cf6eSHans Rosenfeld 	for (i = 0; i < 5; i++) {
2265fd43cf6eSHans Rosenfeld 		addr = base + iwn5000_regulatory_bands[i];
2266fd43cf6eSHans Rosenfeld 		iwn_read_eeprom_channels(sc, i, addr);
2267fd43cf6eSHans Rosenfeld 	}
2268fd43cf6eSHans Rosenfeld 
2269fd43cf6eSHans Rosenfeld 	/* Read enhanced TX power information for 6000 Series. */
2270fd43cf6eSHans Rosenfeld 	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
2271fd43cf6eSHans Rosenfeld 		iwn_read_eeprom_enhinfo(sc);
2272fd43cf6eSHans Rosenfeld 
2273fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
2274fd43cf6eSHans Rosenfeld 	base = le16toh(val);
2275fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
2276fd43cf6eSHans Rosenfeld 	IWN_DBG("calib version=%u pa type=%u voltage=%u",
2277fd43cf6eSHans Rosenfeld 	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
2278fd43cf6eSHans Rosenfeld 	sc->calib_ver = hdr.version;
2279fd43cf6eSHans Rosenfeld 
2280fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_2030 ||
2281fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_2000 ||
2282fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_135  ||
2283fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_105) {
2284fd43cf6eSHans Rosenfeld 		sc->eeprom_voltage = le16toh(hdr.volt);
2285fd43cf6eSHans Rosenfeld 		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
2286fd43cf6eSHans Rosenfeld 		sc->eeprom_temp = le16toh(val);
2287fd43cf6eSHans Rosenfeld 		iwn_read_prom_data(sc, base + IWN2000_EEPROM_RAWTEMP, &val, 2);
2288fd43cf6eSHans Rosenfeld 		sc->eeprom_rawtemp = le16toh(val);
2289fd43cf6eSHans Rosenfeld 	}
2290fd43cf6eSHans Rosenfeld 
2291fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
2292fd43cf6eSHans Rosenfeld 		/* Compute temperature offset. */
2293fd43cf6eSHans Rosenfeld 		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
2294fd43cf6eSHans Rosenfeld 		sc->eeprom_temp = le16toh(val);
2295fd43cf6eSHans Rosenfeld 		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
2296fd43cf6eSHans Rosenfeld 		volt = le16toh(val);
2297fd43cf6eSHans Rosenfeld 		sc->temp_off = sc->eeprom_temp - (volt / -5);
2298fd43cf6eSHans Rosenfeld 		IWN_DBG("temp=%d volt=%d offset=%dK",
2299fd43cf6eSHans Rosenfeld 		    sc->eeprom_temp, volt, sc->temp_off);
2300fd43cf6eSHans Rosenfeld 	} else {
2301fd43cf6eSHans Rosenfeld 		/* Read crystal calibration. */
2302fd43cf6eSHans Rosenfeld 		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
2303fd43cf6eSHans Rosenfeld 		    &sc->eeprom_crystal, sizeof (uint32_t));
2304fd43cf6eSHans Rosenfeld 		IWN_DBG("crystal calibration 0x%08x",
2305fd43cf6eSHans Rosenfeld 		    le32toh(sc->eeprom_crystal));
2306fd43cf6eSHans Rosenfeld 	}
2307fd43cf6eSHans Rosenfeld }
2308fd43cf6eSHans Rosenfeld 
2309fd43cf6eSHans Rosenfeld static void
iwn_read_eeprom_channels(struct iwn_softc * sc,int n,uint32_t addr)2310fd43cf6eSHans Rosenfeld iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
2311fd43cf6eSHans Rosenfeld {
2312fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2313fd43cf6eSHans Rosenfeld 	const struct iwn_chan_band *band = &iwn_bands[n];
2314fd43cf6eSHans Rosenfeld 	struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND];
2315fd43cf6eSHans Rosenfeld 	uint8_t chan;
2316fd43cf6eSHans Rosenfeld 	int i;
2317fd43cf6eSHans Rosenfeld 
2318fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, addr, channels,
2319fd43cf6eSHans Rosenfeld 	    band->nchan * sizeof (struct iwn_eeprom_chan));
2320fd43cf6eSHans Rosenfeld 
2321fd43cf6eSHans Rosenfeld 	for (i = 0; i < band->nchan; i++) {
2322fd43cf6eSHans Rosenfeld 		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID))
2323fd43cf6eSHans Rosenfeld 			continue;
2324fd43cf6eSHans Rosenfeld 
2325fd43cf6eSHans Rosenfeld 		chan = band->chan[i];
2326fd43cf6eSHans Rosenfeld 
2327fd43cf6eSHans Rosenfeld 		if (n == 0) {	/* 2GHz band */
2328fd43cf6eSHans Rosenfeld 			ic->ic_sup_channels[chan].ich_freq =
2329fd43cf6eSHans Rosenfeld 			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
2330fd43cf6eSHans Rosenfeld 			ic->ic_sup_channels[chan].ich_flags =
2331fd43cf6eSHans Rosenfeld 			    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
2332fd43cf6eSHans Rosenfeld 			    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
2333fd43cf6eSHans Rosenfeld 
2334fd43cf6eSHans Rosenfeld 		} else {	/* 5GHz band */
2335fd43cf6eSHans Rosenfeld 			/*
2336fd43cf6eSHans Rosenfeld 			 * Some adapters support channels 7, 8, 11 and 12
2337fd43cf6eSHans Rosenfeld 			 * both in the 2GHz and 4.9GHz bands.
2338fd43cf6eSHans Rosenfeld 			 * Because of limitations in our net80211 layer,
2339fd43cf6eSHans Rosenfeld 			 * we don't support them in the 4.9GHz band.
2340fd43cf6eSHans Rosenfeld 			 */
2341fd43cf6eSHans Rosenfeld 			if (chan <= 14)
2342fd43cf6eSHans Rosenfeld 				continue;
2343fd43cf6eSHans Rosenfeld 
2344fd43cf6eSHans Rosenfeld 			ic->ic_sup_channels[chan].ich_freq =
2345fd43cf6eSHans Rosenfeld 			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
2346fd43cf6eSHans Rosenfeld 			ic->ic_sup_channels[chan].ich_flags =
2347fd43cf6eSHans Rosenfeld 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
2348fd43cf6eSHans Rosenfeld 			/* We have at least one valid 5GHz channel. */
2349fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_HAS_5GHZ;
2350fd43cf6eSHans Rosenfeld 		}
2351fd43cf6eSHans Rosenfeld 
2352fd43cf6eSHans Rosenfeld 		/* Is active scan allowed on this channel? */
2353fd43cf6eSHans Rosenfeld 		if (!(channels[i].flags & IWN_EEPROM_CHAN_ACTIVE)) {
2354fd43cf6eSHans Rosenfeld 			ic->ic_sup_channels[chan].ich_flags |=
2355fd43cf6eSHans Rosenfeld 			    IEEE80211_CHAN_PASSIVE;
2356fd43cf6eSHans Rosenfeld 		}
2357fd43cf6eSHans Rosenfeld 
2358fd43cf6eSHans Rosenfeld 		/* Save maximum allowed TX power for this channel. */
2359fd43cf6eSHans Rosenfeld 		sc->maxpwr[chan] = channels[i].maxpwr;
2360fd43cf6eSHans Rosenfeld 
2361fd43cf6eSHans Rosenfeld 		IWN_DBG("adding chan %d flags=0x%x maxpwr=%d",
2362fd43cf6eSHans Rosenfeld 		    chan, channels[i].flags, sc->maxpwr[chan]);
2363fd43cf6eSHans Rosenfeld 	}
2364fd43cf6eSHans Rosenfeld }
2365fd43cf6eSHans Rosenfeld 
2366fd43cf6eSHans Rosenfeld static void
iwn_read_eeprom_enhinfo(struct iwn_softc * sc)2367fd43cf6eSHans Rosenfeld iwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2368fd43cf6eSHans Rosenfeld {
2369fd43cf6eSHans Rosenfeld 	struct iwn_eeprom_enhinfo enhinfo[35];
2370fd43cf6eSHans Rosenfeld 	uint16_t val, base;
2371fd43cf6eSHans Rosenfeld 	int8_t maxpwr;
2372fd43cf6eSHans Rosenfeld 	int i;
2373fd43cf6eSHans Rosenfeld 
2374fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2375fd43cf6eSHans Rosenfeld 	base = le16toh(val);
2376fd43cf6eSHans Rosenfeld 	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2377fd43cf6eSHans Rosenfeld 	    enhinfo, sizeof enhinfo);
2378fd43cf6eSHans Rosenfeld 
2379fd43cf6eSHans Rosenfeld 	memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr);
2380fd43cf6eSHans Rosenfeld 	for (i = 0; i < __arraycount(enhinfo); i++) {
2381fd43cf6eSHans Rosenfeld 		if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0)
2382fd43cf6eSHans Rosenfeld 			continue;	/* Skip invalid entries. */
2383fd43cf6eSHans Rosenfeld 
2384fd43cf6eSHans Rosenfeld 		maxpwr = 0;
2385fd43cf6eSHans Rosenfeld 		if (sc->txchainmask & IWN_ANT_A)
2386fd43cf6eSHans Rosenfeld 			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2387fd43cf6eSHans Rosenfeld 		if (sc->txchainmask & IWN_ANT_B)
2388fd43cf6eSHans Rosenfeld 			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2389fd43cf6eSHans Rosenfeld 		if (sc->txchainmask & IWN_ANT_C)
2390fd43cf6eSHans Rosenfeld 			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2391fd43cf6eSHans Rosenfeld 		if (sc->ntxchains == 2)
2392fd43cf6eSHans Rosenfeld 			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2393fd43cf6eSHans Rosenfeld 		else if (sc->ntxchains == 3)
2394fd43cf6eSHans Rosenfeld 			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2395fd43cf6eSHans Rosenfeld 		maxpwr /= 2;	/* Convert half-dBm to dBm. */
2396fd43cf6eSHans Rosenfeld 
2397fd43cf6eSHans Rosenfeld 		IWN_DBG("enhinfo %d, maxpwr=%d", i, maxpwr);
2398fd43cf6eSHans Rosenfeld 		sc->enh_maxpwr[i] = maxpwr;
2399fd43cf6eSHans Rosenfeld 	}
2400fd43cf6eSHans Rosenfeld }
2401fd43cf6eSHans Rosenfeld 
2402fd43cf6eSHans Rosenfeld static struct ieee80211_node *
iwn_node_alloc(ieee80211com_t * ic)2403fd43cf6eSHans Rosenfeld iwn_node_alloc(ieee80211com_t *ic)
2404fd43cf6eSHans Rosenfeld {
2405fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(ic));
2406fd43cf6eSHans Rosenfeld 	return (kmem_zalloc(sizeof (struct iwn_node), KM_NOSLEEP));
2407fd43cf6eSHans Rosenfeld }
2408fd43cf6eSHans Rosenfeld 
2409fd43cf6eSHans Rosenfeld static void
iwn_node_free(ieee80211_node_t * in)2410fd43cf6eSHans Rosenfeld iwn_node_free(ieee80211_node_t *in)
2411fd43cf6eSHans Rosenfeld {
2412fd43cf6eSHans Rosenfeld 	ASSERT(in != NULL);
2413fd43cf6eSHans Rosenfeld 	ASSERT(in->in_ic != NULL);
2414fd43cf6eSHans Rosenfeld 
2415fd43cf6eSHans Rosenfeld 	if (in->in_wpa_ie != NULL)
2416fd43cf6eSHans Rosenfeld 		ieee80211_free(in->in_wpa_ie);
2417fd43cf6eSHans Rosenfeld 
2418fd43cf6eSHans Rosenfeld 	if (in->in_wme_ie != NULL)
2419fd43cf6eSHans Rosenfeld 		ieee80211_free(in->in_wme_ie);
2420fd43cf6eSHans Rosenfeld 
2421fd43cf6eSHans Rosenfeld 	if (in->in_htcap_ie != NULL)
2422fd43cf6eSHans Rosenfeld 		ieee80211_free(in->in_htcap_ie);
2423fd43cf6eSHans Rosenfeld 
2424fd43cf6eSHans Rosenfeld 	kmem_free(in, sizeof (struct iwn_node));
2425fd43cf6eSHans Rosenfeld }
2426fd43cf6eSHans Rosenfeld 
2427fd43cf6eSHans Rosenfeld static void
iwn_newassoc(struct ieee80211_node * ni,int isnew)2428fd43cf6eSHans Rosenfeld iwn_newassoc(struct ieee80211_node *ni, int isnew)
2429fd43cf6eSHans Rosenfeld {
2430fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(isnew));
2431fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)&ni->in_ic;
2432fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
2433fd43cf6eSHans Rosenfeld 	uint8_t rate, ridx;
2434fd43cf6eSHans Rosenfeld 	int i;
2435fd43cf6eSHans Rosenfeld 
2436fd43cf6eSHans Rosenfeld 	ieee80211_amrr_node_init(&sc->amrr, &wn->amn);
2437fd43cf6eSHans Rosenfeld 	/*
2438fd43cf6eSHans Rosenfeld 	 * Select a medium rate and depend on AMRR to raise/lower it.
2439fd43cf6eSHans Rosenfeld 	 */
2440fd43cf6eSHans Rosenfeld 	ni->in_txrate = ni->in_rates.ir_nrates / 2;
2441fd43cf6eSHans Rosenfeld 
2442fd43cf6eSHans Rosenfeld 	for (i = 0; i < ni->in_rates.ir_nrates; i++) {
2443fd43cf6eSHans Rosenfeld 		rate = ni->in_rates.ir_rates[i] & IEEE80211_RATE_VAL;
2444fd43cf6eSHans Rosenfeld 		/* Map 802.11 rate to HW rate index. */
2445fd43cf6eSHans Rosenfeld 		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++)
2446fd43cf6eSHans Rosenfeld 			if (iwn_rates[ridx].rate == rate)
2447fd43cf6eSHans Rosenfeld 				break;
2448fd43cf6eSHans Rosenfeld 		wn->ridx[i] = ridx;
2449fd43cf6eSHans Rosenfeld 	}
2450fd43cf6eSHans Rosenfeld }
2451fd43cf6eSHans Rosenfeld 
2452fd43cf6eSHans Rosenfeld static int
iwn_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)2453fd43cf6eSHans Rosenfeld iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
2454fd43cf6eSHans Rosenfeld {
2455fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)ic;
2456fd43cf6eSHans Rosenfeld 	enum ieee80211_state ostate;
2457fd43cf6eSHans Rosenfeld 	int error;
2458fd43cf6eSHans Rosenfeld 
2459fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
2460fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO;
2461fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
2462fd43cf6eSHans Rosenfeld 
2463fd43cf6eSHans Rosenfeld 	(void) untimeout(sc->calib_to);
2464fd43cf6eSHans Rosenfeld 	sc->calib_to = 0;
2465fd43cf6eSHans Rosenfeld 
2466fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
2467fd43cf6eSHans Rosenfeld 	ostate = ic->ic_state;
2468fd43cf6eSHans Rosenfeld 
2469fd43cf6eSHans Rosenfeld 	DTRACE_PROBE5(new__state, int, sc->sc_flags,
2470fd43cf6eSHans Rosenfeld 	    enum ieee80211_state, ostate,
2471fd43cf6eSHans Rosenfeld 	    const char *, ieee80211_state_name[ostate],
2472fd43cf6eSHans Rosenfeld 	    enum ieee80211_state, nstate,
2473fd43cf6eSHans Rosenfeld 	    const char *, ieee80211_state_name[nstate]);
2474fd43cf6eSHans Rosenfeld 
2475fd43cf6eSHans Rosenfeld 	if ((sc->sc_flags & IWN_FLAG_RADIO_OFF) && nstate != IEEE80211_S_INIT) {
2476fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2477fd43cf6eSHans Rosenfeld 		return (IWN_FAIL);
2478fd43cf6eSHans Rosenfeld 	}
2479fd43cf6eSHans Rosenfeld 
2480fd43cf6eSHans Rosenfeld 	if (!(sc->sc_flags & IWN_FLAG_HW_INITED) &&
2481fd43cf6eSHans Rosenfeld 	    nstate != IEEE80211_S_INIT) {
2482fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2483fd43cf6eSHans Rosenfeld 		return (IWN_FAIL);
2484fd43cf6eSHans Rosenfeld 	}
2485fd43cf6eSHans Rosenfeld 
2486fd43cf6eSHans Rosenfeld 	switch (nstate) {
2487fd43cf6eSHans Rosenfeld 	case IEEE80211_S_SCAN:
2488fd43cf6eSHans Rosenfeld 		/* XXX Do not abort a running scan. */
2489fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_SCANNING) {
2490fd43cf6eSHans Rosenfeld 			if (ostate != nstate)
2491fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN, "!scan request(%d)"
2492fd43cf6eSHans Rosenfeld 				    " while scanning(%d) ignored", nstate,
2493fd43cf6eSHans Rosenfeld 				    ostate);
2494fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
2495fd43cf6eSHans Rosenfeld 			return (0);
2496fd43cf6eSHans Rosenfeld 		}
2497fd43cf6eSHans Rosenfeld 
2498fd43cf6eSHans Rosenfeld 		bcopy(&sc->rxon, &sc->rxon_save, sizeof (sc->rxon));
2499fd43cf6eSHans Rosenfeld 		sc->sc_ostate = ostate;
2500fd43cf6eSHans Rosenfeld 
2501fd43cf6eSHans Rosenfeld 		/* XXX Not sure if call and flags are needed. */
2502fd43cf6eSHans Rosenfeld 		ieee80211_node_table_reset(&ic->ic_scan);
2503fd43cf6eSHans Rosenfeld 		ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
2504fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_SCANNING_2GHZ;
2505fd43cf6eSHans Rosenfeld 
2506fd43cf6eSHans Rosenfeld 		/* Make the link LED blink while we're scanning. */
2507fd43cf6eSHans Rosenfeld 		iwn_set_led(sc, IWN_LED_LINK, 10, 10);
2508fd43cf6eSHans Rosenfeld 
2509fd43cf6eSHans Rosenfeld 		ic->ic_state = nstate;
2510fd43cf6eSHans Rosenfeld 
2511fd43cf6eSHans Rosenfeld 		error = iwn_scan(sc, IEEE80211_CHAN_2GHZ);
2512fd43cf6eSHans Rosenfeld 		if (error != 0) {
2513fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2514fd43cf6eSHans Rosenfeld 			    "!could not initiate scan");
2515fd43cf6eSHans Rosenfeld 			sc->sc_flags &= ~IWN_FLAG_SCANNING;
2516fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
2517fd43cf6eSHans Rosenfeld 			return (error);
2518fd43cf6eSHans Rosenfeld 		}
2519fd43cf6eSHans Rosenfeld 
2520fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2521fd43cf6eSHans Rosenfeld 		sc->scan_to = timeout(iwn_abort_scan, sc, iwn_scan_timeout *
2522fd43cf6eSHans Rosenfeld 		    drv_usectohz(MICROSEC));
2523fd43cf6eSHans Rosenfeld 		return (error);
2524fd43cf6eSHans Rosenfeld 
2525fd43cf6eSHans Rosenfeld 	case IEEE80211_S_ASSOC:
2526fd43cf6eSHans Rosenfeld 		if (ostate != IEEE80211_S_RUN) {
2527fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
2528fd43cf6eSHans Rosenfeld 			break;
2529fd43cf6eSHans Rosenfeld 		}
2530fd43cf6eSHans Rosenfeld 		/* FALLTHROUGH */
2531fd43cf6eSHans Rosenfeld 	case IEEE80211_S_AUTH:
2532fd43cf6eSHans Rosenfeld 		/* Reset state to handle reassociations correctly. */
2533fd43cf6eSHans Rosenfeld 		sc->rxon.associd = 0;
2534fd43cf6eSHans Rosenfeld 		sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
2535fd43cf6eSHans Rosenfeld 		sc->calib.state = IWN_CALIB_STATE_INIT;
2536fd43cf6eSHans Rosenfeld 
2537fd43cf6eSHans Rosenfeld 		if ((error = iwn_auth(sc)) != 0) {
2538fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
2539fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2540fd43cf6eSHans Rosenfeld 			    "!could not move to auth state");
2541fd43cf6eSHans Rosenfeld 			return error;
2542fd43cf6eSHans Rosenfeld 		}
2543fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2544fd43cf6eSHans Rosenfeld 		break;
2545fd43cf6eSHans Rosenfeld 
2546fd43cf6eSHans Rosenfeld 	case IEEE80211_S_RUN:
2547fd43cf6eSHans Rosenfeld 		if ((error = iwn_run(sc)) != 0) {
2548fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
2549fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2550fd43cf6eSHans Rosenfeld 			    "!could not move to run state");
2551fd43cf6eSHans Rosenfeld 			return error;
2552fd43cf6eSHans Rosenfeld 		}
2553fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2554fd43cf6eSHans Rosenfeld 		break;
2555fd43cf6eSHans Rosenfeld 
2556fd43cf6eSHans Rosenfeld 	case IEEE80211_S_INIT:
2557fd43cf6eSHans Rosenfeld 		sc->sc_flags &= ~IWN_FLAG_SCANNING;
2558fd43cf6eSHans Rosenfeld 		sc->calib.state = IWN_CALIB_STATE_INIT;
2559fd43cf6eSHans Rosenfeld 
2560fd43cf6eSHans Rosenfeld 		/*
2561fd43cf6eSHans Rosenfeld 		 * set LED off after init
2562fd43cf6eSHans Rosenfeld 		 */
2563fd43cf6eSHans Rosenfeld 		iwn_set_led(sc, IWN_LED_LINK, 1, 0);
2564fd43cf6eSHans Rosenfeld 
2565fd43cf6eSHans Rosenfeld 		cv_signal(&sc->sc_scan_cv);
2566fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
2567fd43cf6eSHans Rosenfeld 		if (sc->scan_to != 0)
2568fd43cf6eSHans Rosenfeld 			(void) untimeout(sc->scan_to);
2569fd43cf6eSHans Rosenfeld 		sc->scan_to = 0;
2570fd43cf6eSHans Rosenfeld 		break;
2571fd43cf6eSHans Rosenfeld 	}
2572fd43cf6eSHans Rosenfeld 
2573fd43cf6eSHans Rosenfeld 	error = sc->sc_newstate(ic, nstate, arg);
2574fd43cf6eSHans Rosenfeld 
2575fd43cf6eSHans Rosenfeld 	if (nstate == IEEE80211_S_RUN)
2576fd43cf6eSHans Rosenfeld 		ieee80211_start_watchdog(ic, 1);
2577fd43cf6eSHans Rosenfeld 
2578fd43cf6eSHans Rosenfeld 	return (error);
2579fd43cf6eSHans Rosenfeld }
2580fd43cf6eSHans Rosenfeld 
2581fd43cf6eSHans Rosenfeld static void
iwn_iter_func(void * arg,struct ieee80211_node * ni)2582fd43cf6eSHans Rosenfeld iwn_iter_func(void *arg, struct ieee80211_node *ni)
2583fd43cf6eSHans Rosenfeld {
2584fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = arg;
2585fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (struct iwn_node *)ni;
2586fd43cf6eSHans Rosenfeld 
2587fd43cf6eSHans Rosenfeld 	ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn);
2588fd43cf6eSHans Rosenfeld }
2589fd43cf6eSHans Rosenfeld 
2590fd43cf6eSHans Rosenfeld static void
iwn_calib_timeout(void * arg)2591fd43cf6eSHans Rosenfeld iwn_calib_timeout(void *arg)
2592fd43cf6eSHans Rosenfeld {
2593fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = arg;
2594fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2595fd43cf6eSHans Rosenfeld 
2596fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
2597fd43cf6eSHans Rosenfeld 
2598fd43cf6eSHans Rosenfeld 	if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
2599fd43cf6eSHans Rosenfeld 		if (ic->ic_opmode == IEEE80211_M_STA)
2600fd43cf6eSHans Rosenfeld 			iwn_iter_func(sc, ic->ic_bss);
2601fd43cf6eSHans Rosenfeld 		else
2602fd43cf6eSHans Rosenfeld 			ieee80211_iterate_nodes(&ic->ic_sta, iwn_iter_func, sc);
2603fd43cf6eSHans Rosenfeld 	}
2604fd43cf6eSHans Rosenfeld 	/* Force automatic TX power calibration every 60 secs. */
2605fd43cf6eSHans Rosenfeld 	if (++sc->calib_cnt >= 120) {
2606fd43cf6eSHans Rosenfeld 		uint32_t flags = 0;
2607fd43cf6eSHans Rosenfeld 
2608fd43cf6eSHans Rosenfeld 		DTRACE_PROBE(get__statistics);
2609fd43cf6eSHans Rosenfeld 		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2610fd43cf6eSHans Rosenfeld 		    sizeof flags, 1);
2611fd43cf6eSHans Rosenfeld 		sc->calib_cnt = 0;
2612fd43cf6eSHans Rosenfeld 	}
2613fd43cf6eSHans Rosenfeld 
2614fd43cf6eSHans Rosenfeld 	/* Automatic rate control triggered every 500ms. */
2615fd43cf6eSHans Rosenfeld 	if ((sc->sc_flags & IWN_FLAG_STOP_CALIB_TO) == 0)
2616fd43cf6eSHans Rosenfeld 		sc->calib_to = timeout(iwn_calib_timeout, sc,
2617fd43cf6eSHans Rosenfeld 		    drv_usectohz(500000));
2618fd43cf6eSHans Rosenfeld 
2619fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
2620fd43cf6eSHans Rosenfeld }
2621fd43cf6eSHans Rosenfeld 
2622fd43cf6eSHans Rosenfeld /*
2623fd43cf6eSHans Rosenfeld  * Process an RX_PHY firmware notification.  This is usually immediately
2624fd43cf6eSHans Rosenfeld  * followed by an MPDU_RX_DONE notification.
2625fd43cf6eSHans Rosenfeld  */
2626fd43cf6eSHans Rosenfeld static void
iwn_rx_phy(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2627fd43cf6eSHans Rosenfeld iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2628fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2629fd43cf6eSHans Rosenfeld {
2630fd43cf6eSHans Rosenfeld 	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2631fd43cf6eSHans Rosenfeld 
2632fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc),
2633fd43cf6eSHans Rosenfeld 	    sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
2634fd43cf6eSHans Rosenfeld 
2635fd43cf6eSHans Rosenfeld 	DTRACE_PROBE1(rx__phy, struct iwn_rx_stat *, stat);
2636fd43cf6eSHans Rosenfeld 
2637fd43cf6eSHans Rosenfeld 	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2638fd43cf6eSHans Rosenfeld 	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2639fd43cf6eSHans Rosenfeld 	sc->last_rx_valid = 1;
2640fd43cf6eSHans Rosenfeld }
2641fd43cf6eSHans Rosenfeld 
2642fd43cf6eSHans Rosenfeld /*
2643fd43cf6eSHans Rosenfeld  * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2644fd43cf6eSHans Rosenfeld  * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2645fd43cf6eSHans Rosenfeld  */
2646fd43cf6eSHans Rosenfeld static void
iwn_rx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2647fd43cf6eSHans Rosenfeld iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2648fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2649fd43cf6eSHans Rosenfeld {
2650fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
2651fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2652fd43cf6eSHans Rosenfeld 	struct iwn_rx_ring *ring = &sc->rxq;
2653fd43cf6eSHans Rosenfeld 	struct ieee80211_frame *wh;
2654fd43cf6eSHans Rosenfeld 	struct ieee80211_node *ni;
2655fd43cf6eSHans Rosenfeld 	mblk_t *m;
2656fd43cf6eSHans Rosenfeld 	struct iwn_rx_stat *stat;
2657fd43cf6eSHans Rosenfeld 	char	*head;
2658fd43cf6eSHans Rosenfeld 	uint32_t flags;
2659fd43cf6eSHans Rosenfeld 	int len, rssi;
2660fd43cf6eSHans Rosenfeld 
2661fd43cf6eSHans Rosenfeld 	if (desc->type == IWN_MPDU_RX_DONE) {
2662fd43cf6eSHans Rosenfeld 		/* Check for prior RX_PHY notification. */
2663fd43cf6eSHans Rosenfeld 		if (!sc->last_rx_valid) {
2664fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
2665fd43cf6eSHans Rosenfeld 			    "missing RX_PHY");
2666fd43cf6eSHans Rosenfeld 			return;
2667fd43cf6eSHans Rosenfeld 		}
2668fd43cf6eSHans Rosenfeld 		sc->last_rx_valid = 0;
2669fd43cf6eSHans Rosenfeld 		stat = &sc->last_rx_stat;
2670fd43cf6eSHans Rosenfeld 	} else
2671fd43cf6eSHans Rosenfeld 		stat = (struct iwn_rx_stat *)(desc + 1);
2672fd43cf6eSHans Rosenfeld 
2673fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0,
2674fd43cf6eSHans Rosenfeld 	    DDI_DMA_SYNC_FORKERNEL);
2675fd43cf6eSHans Rosenfeld 
2676fd43cf6eSHans Rosenfeld 	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2677fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
2678fd43cf6eSHans Rosenfeld 		    "!invalid RX statistic header");
2679fd43cf6eSHans Rosenfeld 		return;
2680fd43cf6eSHans Rosenfeld 	}
2681fd43cf6eSHans Rosenfeld 	if (desc->type == IWN_MPDU_RX_DONE) {
2682fd43cf6eSHans Rosenfeld 		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2683fd43cf6eSHans Rosenfeld 		head = (char *)(mpdu + 1);
2684fd43cf6eSHans Rosenfeld 		len = le16toh(mpdu->len);
2685fd43cf6eSHans Rosenfeld 	} else {
2686fd43cf6eSHans Rosenfeld 		head = (char *)(stat + 1) + stat->cfg_phy_len;
2687fd43cf6eSHans Rosenfeld 		len = le16toh(stat->len);
2688fd43cf6eSHans Rosenfeld 	}
2689fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
2690fd43cf6eSHans Rosenfeld 	flags = le32toh(*(uint32_t *)(head + len));
2691fd43cf6eSHans Rosenfeld 
2692fd43cf6eSHans Rosenfeld 	/* Discard frames with a bad FCS early. */
2693fd43cf6eSHans Rosenfeld 	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2694fd43cf6eSHans Rosenfeld 		sc->sc_rx_err++;
2695fd43cf6eSHans Rosenfeld 		ic->ic_stats.is_fcs_errors++;
2696fd43cf6eSHans Rosenfeld 		return;
2697fd43cf6eSHans Rosenfeld 	}
2698fd43cf6eSHans Rosenfeld 	/* Discard frames that are too short. */
2699fd43cf6eSHans Rosenfeld 	if (len < sizeof (*wh)) {
2700fd43cf6eSHans Rosenfeld 		sc->sc_rx_err++;
2701fd43cf6eSHans Rosenfeld 		return;
2702fd43cf6eSHans Rosenfeld 	}
2703fd43cf6eSHans Rosenfeld 
2704fd43cf6eSHans Rosenfeld 	m = allocb(len, BPRI_MED);
2705fd43cf6eSHans Rosenfeld 	if (m == NULL) {
2706fd43cf6eSHans Rosenfeld 		sc->sc_rx_nobuf++;
2707fd43cf6eSHans Rosenfeld 		return;
2708fd43cf6eSHans Rosenfeld 	}
2709fd43cf6eSHans Rosenfeld 
2710fd43cf6eSHans Rosenfeld 	/* Update RX descriptor. */
2711fd43cf6eSHans Rosenfeld 	ring->desc[ring->cur] =
2712fd43cf6eSHans Rosenfeld 	    htole32(data->dma_data.paddr >> 8);
2713fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->desc_dma.dma_hdl,
2714fd43cf6eSHans Rosenfeld 	    ring->cur * sizeof (uint32_t), sizeof (uint32_t),
2715fd43cf6eSHans Rosenfeld 	    DDI_DMA_SYNC_FORDEV);
2716fd43cf6eSHans Rosenfeld 
2717fd43cf6eSHans Rosenfeld 	/* Grab a reference to the source node. */
2718fd43cf6eSHans Rosenfeld 	wh = (struct ieee80211_frame*)head;
2719fd43cf6eSHans Rosenfeld 	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame *)wh);
2720fd43cf6eSHans Rosenfeld 
2721fd43cf6eSHans Rosenfeld 	/* XXX OpenBSD adds decryption here (see also comments in iwn_tx). */
2722fd43cf6eSHans Rosenfeld 	/* NetBSD does decryption in ieee80211_input. */
2723fd43cf6eSHans Rosenfeld 
2724fd43cf6eSHans Rosenfeld 	rssi = ops->get_rssi(stat);
2725fd43cf6eSHans Rosenfeld 
2726fd43cf6eSHans Rosenfeld 	/*
2727fd43cf6eSHans Rosenfeld 	 * convert dBm to percentage
2728fd43cf6eSHans Rosenfeld 	 */
2729fd43cf6eSHans Rosenfeld 	rssi = (100 * 75 * 75 - (-20 - rssi) * (15 * 75 + 62 * (-20 - rssi)))
2730fd43cf6eSHans Rosenfeld 	    / (75 * 75);
2731fd43cf6eSHans Rosenfeld 	if (rssi > 100)
2732fd43cf6eSHans Rosenfeld 		rssi = 100;
2733fd43cf6eSHans Rosenfeld 	else if (rssi < 1)
2734fd43cf6eSHans Rosenfeld 		rssi = 1;
2735fd43cf6eSHans Rosenfeld 
2736fd43cf6eSHans Rosenfeld 	bcopy(wh, m->b_wptr, len);
2737fd43cf6eSHans Rosenfeld 	m->b_wptr += len;
2738fd43cf6eSHans Rosenfeld 
2739fd43cf6eSHans Rosenfeld 	/* XXX Added for NetBSD: scans never stop without it */
2740fd43cf6eSHans Rosenfeld 	if (ic->ic_state == IEEE80211_S_SCAN)
2741fd43cf6eSHans Rosenfeld 		iwn_fix_channel(sc, m, stat);
2742fd43cf6eSHans Rosenfeld 
2743fd43cf6eSHans Rosenfeld 	/* Send the frame to the 802.11 layer. */
2744fd43cf6eSHans Rosenfeld 	ieee80211_input(ic, m, ni, rssi, 0);
2745fd43cf6eSHans Rosenfeld 
2746fd43cf6eSHans Rosenfeld 	/* Node is no longer needed. */
2747fd43cf6eSHans Rosenfeld 	ieee80211_free_node(ni);
2748fd43cf6eSHans Rosenfeld }
2749fd43cf6eSHans Rosenfeld 
2750fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
2751fd43cf6eSHans Rosenfeld /* Process an incoming Compressed BlockAck. */
2752fd43cf6eSHans Rosenfeld static void
iwn_rx_compressed_ba(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2753fd43cf6eSHans Rosenfeld iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2754fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2755fd43cf6eSHans Rosenfeld {
2756fd43cf6eSHans Rosenfeld 	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2757fd43cf6eSHans Rosenfeld 	struct iwn_tx_ring *txq;
2758fd43cf6eSHans Rosenfeld 
2759fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc),
2760fd43cf6eSHans Rosenfeld 	    sizeof (*ba), DDI_DMA_SYNC_FORKERNEL);
2761fd43cf6eSHans Rosenfeld 
2762fd43cf6eSHans Rosenfeld 	txq = &sc->txq[le16toh(ba->qid)];
2763fd43cf6eSHans Rosenfeld 	/* XXX TBD */
2764fd43cf6eSHans Rosenfeld }
2765fd43cf6eSHans Rosenfeld #endif
2766fd43cf6eSHans Rosenfeld 
2767fd43cf6eSHans Rosenfeld /*
2768fd43cf6eSHans Rosenfeld  * Process a CALIBRATION_RESULT notification sent by the initialization
2769fd43cf6eSHans Rosenfeld  * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2770fd43cf6eSHans Rosenfeld  */
2771fd43cf6eSHans Rosenfeld static void
iwn5000_rx_calib_results(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2772fd43cf6eSHans Rosenfeld iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2773fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2774fd43cf6eSHans Rosenfeld {
2775fd43cf6eSHans Rosenfeld 	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2776fd43cf6eSHans Rosenfeld 	int len, idx = -1;
2777fd43cf6eSHans Rosenfeld 
2778fd43cf6eSHans Rosenfeld 	/* Runtime firmware should not send such a notification. */
2779fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_CALIB_DONE)
2780fd43cf6eSHans Rosenfeld 		return;
2781fd43cf6eSHans Rosenfeld 
2782fd43cf6eSHans Rosenfeld 	len = (le32toh(desc->len) & 0x3fff) - 4;
2783fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), len,
2784fd43cf6eSHans Rosenfeld 	    DDI_DMA_SYNC_FORKERNEL);
2785fd43cf6eSHans Rosenfeld 
2786fd43cf6eSHans Rosenfeld 	switch (calib->code) {
2787fd43cf6eSHans Rosenfeld 	case IWN5000_PHY_CALIB_DC:
2788fd43cf6eSHans Rosenfeld 		if (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2789fd43cf6eSHans Rosenfeld 		    sc->hw_type == IWN_HW_REV_TYPE_2030 ||
2790fd43cf6eSHans Rosenfeld 		    sc->hw_type == IWN_HW_REV_TYPE_2000 ||
2791fd43cf6eSHans Rosenfeld 		    sc->hw_type == IWN_HW_REV_TYPE_135  ||
2792fd43cf6eSHans Rosenfeld 		    sc->hw_type == IWN_HW_REV_TYPE_105)
2793fd43cf6eSHans Rosenfeld 			idx = 0;
2794fd43cf6eSHans Rosenfeld 		break;
2795fd43cf6eSHans Rosenfeld 	case IWN5000_PHY_CALIB_LO:
2796fd43cf6eSHans Rosenfeld 		idx = 1;
2797fd43cf6eSHans Rosenfeld 		break;
2798fd43cf6eSHans Rosenfeld 	case IWN5000_PHY_CALIB_TX_IQ:
2799fd43cf6eSHans Rosenfeld 		idx = 2;
2800fd43cf6eSHans Rosenfeld 		break;
2801fd43cf6eSHans Rosenfeld 	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2802fd43cf6eSHans Rosenfeld 		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2803fd43cf6eSHans Rosenfeld 		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2804fd43cf6eSHans Rosenfeld 			idx = 3;
2805fd43cf6eSHans Rosenfeld 		break;
2806fd43cf6eSHans Rosenfeld 	case IWN5000_PHY_CALIB_BASE_BAND:
2807fd43cf6eSHans Rosenfeld 		idx = 4;
2808fd43cf6eSHans Rosenfeld 		break;
2809fd43cf6eSHans Rosenfeld 	}
2810fd43cf6eSHans Rosenfeld 	if (idx == -1)	/* Ignore other results. */
2811fd43cf6eSHans Rosenfeld 		return;
2812fd43cf6eSHans Rosenfeld 
2813fd43cf6eSHans Rosenfeld 	/* Save calibration result. */
2814fd43cf6eSHans Rosenfeld 	if (sc->calibcmd[idx].buf != NULL)
2815fd43cf6eSHans Rosenfeld 		kmem_free(sc->calibcmd[idx].buf, sc->calibcmd[idx].len);
2816fd43cf6eSHans Rosenfeld 	sc->calibcmd[idx].buf = kmem_zalloc(len, KM_NOSLEEP);
2817fd43cf6eSHans Rosenfeld 	if (sc->calibcmd[idx].buf == NULL) {
2818fd43cf6eSHans Rosenfeld 		return;
2819fd43cf6eSHans Rosenfeld 	}
2820fd43cf6eSHans Rosenfeld 	sc->calibcmd[idx].len = len;
2821fd43cf6eSHans Rosenfeld 	memcpy(sc->calibcmd[idx].buf, calib, len);
2822fd43cf6eSHans Rosenfeld }
2823fd43cf6eSHans Rosenfeld 
2824fd43cf6eSHans Rosenfeld /*
2825fd43cf6eSHans Rosenfeld  * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2826fd43cf6eSHans Rosenfeld  * The latter is sent by the firmware after each received beacon.
2827fd43cf6eSHans Rosenfeld  */
2828fd43cf6eSHans Rosenfeld static void
iwn_rx_statistics(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2829fd43cf6eSHans Rosenfeld iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2830fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2831fd43cf6eSHans Rosenfeld {
2832fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
2833fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2834fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
2835fd43cf6eSHans Rosenfeld 	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2836fd43cf6eSHans Rosenfeld 	int temp = 0;
2837fd43cf6eSHans Rosenfeld 
2838fd43cf6eSHans Rosenfeld 	/* Ignore statistics received during a scan. */
2839fd43cf6eSHans Rosenfeld 	if (ic->ic_state != IEEE80211_S_RUN)
2840fd43cf6eSHans Rosenfeld 		return;
2841fd43cf6eSHans Rosenfeld 
2842fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc),
2843fd43cf6eSHans Rosenfeld 	    sizeof (*stats), DDI_DMA_SYNC_FORKERNEL);
2844fd43cf6eSHans Rosenfeld 
2845fd43cf6eSHans Rosenfeld 	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2846fd43cf6eSHans Rosenfeld 
2847fd43cf6eSHans Rosenfeld 	/* Test if temperature has changed. */
2848fd43cf6eSHans Rosenfeld 	if (stats->general.temp != sc->rawtemp) {
2849fd43cf6eSHans Rosenfeld 		/* Convert "raw" temperature to degC. */
2850fd43cf6eSHans Rosenfeld 		sc->rawtemp = stats->general.temp;
2851fd43cf6eSHans Rosenfeld 		temp = ops->get_temperature(sc);
2852fd43cf6eSHans Rosenfeld 		sc->sc_misc->temp.value.ul = temp;
2853fd43cf6eSHans Rosenfeld 
2854fd43cf6eSHans Rosenfeld 		/* Update TX power if need be (4965AGN only). */
2855fd43cf6eSHans Rosenfeld 		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2856fd43cf6eSHans Rosenfeld 			iwn4965_power_calibration(sc, temp);
2857fd43cf6eSHans Rosenfeld 	}
2858fd43cf6eSHans Rosenfeld 
2859fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(rx__statistics, struct iwn_stats *, stats, int, temp);
2860fd43cf6eSHans Rosenfeld 
2861fd43cf6eSHans Rosenfeld 	if (desc->type != IWN_BEACON_STATISTICS)
2862fd43cf6eSHans Rosenfeld 		return;	/* Reply to a statistics request. */
2863fd43cf6eSHans Rosenfeld 
2864fd43cf6eSHans Rosenfeld 	sc->noise = iwn_get_noise(&stats->rx.general);
2865fd43cf6eSHans Rosenfeld 	sc->sc_misc->noise.value.l = sc->noise;
2866fd43cf6eSHans Rosenfeld 
2867fd43cf6eSHans Rosenfeld 	/* Test that RSSI and noise are present in stats report. */
2868fd43cf6eSHans Rosenfeld 	if (le32toh(stats->rx.general.flags) != 1) {
2869fd43cf6eSHans Rosenfeld 		return;
2870fd43cf6eSHans Rosenfeld 	}
2871fd43cf6eSHans Rosenfeld 
2872fd43cf6eSHans Rosenfeld 	/*
2873fd43cf6eSHans Rosenfeld 	 * XXX Differential gain calibration makes the 6005 firmware
2874fd43cf6eSHans Rosenfeld 	 * crap out, so skip it for now.  This effectively disables
2875fd43cf6eSHans Rosenfeld 	 * sensitivity tuning as well.
2876fd43cf6eSHans Rosenfeld 	 */
2877fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
2878fd43cf6eSHans Rosenfeld 		return;
2879fd43cf6eSHans Rosenfeld 
2880fd43cf6eSHans Rosenfeld 	if (calib->state == IWN_CALIB_STATE_ASSOC)
2881fd43cf6eSHans Rosenfeld 		iwn_collect_noise(sc, &stats->rx.general);
2882fd43cf6eSHans Rosenfeld 	else if (calib->state == IWN_CALIB_STATE_RUN)
2883fd43cf6eSHans Rosenfeld 		iwn_tune_sensitivity(sc, &stats->rx);
2884fd43cf6eSHans Rosenfeld }
2885fd43cf6eSHans Rosenfeld 
2886fd43cf6eSHans Rosenfeld /*
2887fd43cf6eSHans Rosenfeld  * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2888fd43cf6eSHans Rosenfeld  * and 5000 adapters have different incompatible TX status formats.
2889fd43cf6eSHans Rosenfeld  */
2890fd43cf6eSHans Rosenfeld static void
iwn4965_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2891fd43cf6eSHans Rosenfeld iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2892fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2893fd43cf6eSHans Rosenfeld {
2894fd43cf6eSHans Rosenfeld 	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2895fd43cf6eSHans Rosenfeld 
2896fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc),
2897fd43cf6eSHans Rosenfeld 	    sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
2898fd43cf6eSHans Rosenfeld 	iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff);
2899fd43cf6eSHans Rosenfeld }
2900fd43cf6eSHans Rosenfeld 
2901fd43cf6eSHans Rosenfeld static void
iwn5000_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)2902fd43cf6eSHans Rosenfeld iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2903fd43cf6eSHans Rosenfeld     struct iwn_rx_data *data)
2904fd43cf6eSHans Rosenfeld {
2905fd43cf6eSHans Rosenfeld 	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2906fd43cf6eSHans Rosenfeld 
2907fd43cf6eSHans Rosenfeld #ifdef notyet
2908fd43cf6eSHans Rosenfeld 	/* Reset TX scheduler slot. */
2909fd43cf6eSHans Rosenfeld 	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2910fd43cf6eSHans Rosenfeld #endif
2911fd43cf6eSHans Rosenfeld 
2912fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc),
2913fd43cf6eSHans Rosenfeld 	    sizeof (*stat), DDI_DMA_SYNC_FORKERNEL);
2914fd43cf6eSHans Rosenfeld 	iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff);
2915fd43cf6eSHans Rosenfeld }
2916fd43cf6eSHans Rosenfeld 
2917fd43cf6eSHans Rosenfeld /*
2918fd43cf6eSHans Rosenfeld  * Adapter-independent backend for TX_DONE firmware notifications.
2919fd43cf6eSHans Rosenfeld  */
2920fd43cf6eSHans Rosenfeld static void
iwn_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,int ackfailcnt,uint8_t status)2921fd43cf6eSHans Rosenfeld iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2922fd43cf6eSHans Rosenfeld     uint8_t status)
2923fd43cf6eSHans Rosenfeld {
2924fd43cf6eSHans Rosenfeld 	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2925fd43cf6eSHans Rosenfeld 	struct iwn_tx_data *data = &ring->data[desc->idx];
2926fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (struct iwn_node *)data->ni;
2927fd43cf6eSHans Rosenfeld 
2928fd43cf6eSHans Rosenfeld 	/* Update rate control statistics. */
2929fd43cf6eSHans Rosenfeld 	wn->amn.amn_txcnt++;
2930fd43cf6eSHans Rosenfeld 	if (ackfailcnt > 0)
2931fd43cf6eSHans Rosenfeld 		wn->amn.amn_retrycnt++;
2932fd43cf6eSHans Rosenfeld 
2933fd43cf6eSHans Rosenfeld 	if (status != 1 && status != 2)
2934fd43cf6eSHans Rosenfeld 		sc->sc_tx_err++;
2935fd43cf6eSHans Rosenfeld 	else
2936fd43cf6eSHans Rosenfeld 		sc->sc_ic.ic_stats.is_tx_frags++;
2937fd43cf6eSHans Rosenfeld 
2938fd43cf6eSHans Rosenfeld 	ieee80211_free_node(data->ni);
2939fd43cf6eSHans Rosenfeld 	data->ni = NULL;
2940fd43cf6eSHans Rosenfeld 
2941fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_tx_mtx);
2942fd43cf6eSHans Rosenfeld 	sc->sc_tx_timer = 0;
2943fd43cf6eSHans Rosenfeld 	if (--ring->queued < IWN_TX_RING_LOMARK) {
2944fd43cf6eSHans Rosenfeld 		sc->qfullmsk &= ~(1 << ring->qid);
2945fd43cf6eSHans Rosenfeld 	}
2946fd43cf6eSHans Rosenfeld 	mac_tx_update(sc->sc_ic.ic_mach);
2947fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_tx_mtx);
2948fd43cf6eSHans Rosenfeld }
2949fd43cf6eSHans Rosenfeld 
2950fd43cf6eSHans Rosenfeld /*
2951fd43cf6eSHans Rosenfeld  * Process a "command done" firmware notification.  This is where we wakeup
2952fd43cf6eSHans Rosenfeld  * processes waiting for a synchronous command completion.
2953fd43cf6eSHans Rosenfeld  */
2954fd43cf6eSHans Rosenfeld static void
iwn_cmd_done(struct iwn_softc * sc,struct iwn_rx_desc * desc)2955fd43cf6eSHans Rosenfeld iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2956fd43cf6eSHans Rosenfeld {
2957fd43cf6eSHans Rosenfeld 	struct iwn_tx_ring *ring = &sc->txq[IWN_CMD_QUEUE_NUM];
2958fd43cf6eSHans Rosenfeld 	struct iwn_tx_data *data;
2959fd43cf6eSHans Rosenfeld 
2960fd43cf6eSHans Rosenfeld 	if ((desc->qid & 0xf) != IWN_CMD_QUEUE_NUM)
2961fd43cf6eSHans Rosenfeld 		return;	/* Not a command ack. */
2962fd43cf6eSHans Rosenfeld 
2963fd43cf6eSHans Rosenfeld 	data = &ring->data[desc->idx];
2964fd43cf6eSHans Rosenfeld 
2965fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
2966fd43cf6eSHans Rosenfeld 
2967fd43cf6eSHans Rosenfeld 	/* If the command was mapped in an extra buffer, free it. */
2968fd43cf6eSHans Rosenfeld 	if (data->cmd_dma.dma_hdl) {
2969fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(data->cmd_dma.dma_hdl, 0, 0,
2970fd43cf6eSHans Rosenfeld 		    DDI_DMA_SYNC_FORDEV);
2971fd43cf6eSHans Rosenfeld 		iwn_dma_contig_free(&data->cmd_dma);
2972fd43cf6eSHans Rosenfeld 	}
2973fd43cf6eSHans Rosenfeld 
2974fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
2975fd43cf6eSHans Rosenfeld 	sc->sc_cmd_flag = SC_CMD_FLG_DONE;
2976fd43cf6eSHans Rosenfeld 	cv_signal(&sc->sc_cmd_cv);
2977fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
2978fd43cf6eSHans Rosenfeld }
2979fd43cf6eSHans Rosenfeld 
2980fd43cf6eSHans Rosenfeld /*
2981fd43cf6eSHans Rosenfeld  * Process an INT_FH_RX or INT_SW_RX interrupt.
2982fd43cf6eSHans Rosenfeld  */
2983fd43cf6eSHans Rosenfeld static void
iwn_notif_intr(struct iwn_softc * sc)2984fd43cf6eSHans Rosenfeld iwn_notif_intr(struct iwn_softc *sc)
2985fd43cf6eSHans Rosenfeld {
2986fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
2987fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
2988fd43cf6eSHans Rosenfeld 	uint16_t hw;
2989fd43cf6eSHans Rosenfeld 
2990fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
2991fd43cf6eSHans Rosenfeld 
2992fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(sc->rxq.stat_dma.dma_hdl, 0, 0,
2993fd43cf6eSHans Rosenfeld 	    DDI_DMA_SYNC_FORKERNEL);
2994fd43cf6eSHans Rosenfeld 
2995fd43cf6eSHans Rosenfeld 	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
2996fd43cf6eSHans Rosenfeld 	while (sc->rxq.cur != hw) {
2997fd43cf6eSHans Rosenfeld 		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
2998fd43cf6eSHans Rosenfeld 		struct iwn_rx_desc *desc;
2999fd43cf6eSHans Rosenfeld 
3000fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(data->dma_data.dma_hdl, 0, sizeof (*desc),
3001fd43cf6eSHans Rosenfeld 		    DDI_DMA_SYNC_FORKERNEL);
3002fd43cf6eSHans Rosenfeld 		desc = (struct iwn_rx_desc *)data->dma_data.vaddr;
3003fd43cf6eSHans Rosenfeld 
3004fd43cf6eSHans Rosenfeld 		DTRACE_PROBE1(notification__intr, struct iwn_rx_desc *, desc);
3005fd43cf6eSHans Rosenfeld 
3006fd43cf6eSHans Rosenfeld 		if (!(desc->qid & 0x80))	/* Reply to a command. */
3007fd43cf6eSHans Rosenfeld 			iwn_cmd_done(sc, desc);
3008fd43cf6eSHans Rosenfeld 
3009fd43cf6eSHans Rosenfeld 		switch (desc->type) {
3010fd43cf6eSHans Rosenfeld 		case IWN_RX_PHY:
3011fd43cf6eSHans Rosenfeld 			iwn_rx_phy(sc, desc, data);
3012fd43cf6eSHans Rosenfeld 			break;
3013fd43cf6eSHans Rosenfeld 
3014fd43cf6eSHans Rosenfeld 		case IWN_RX_DONE:		/* 4965AGN only. */
3015fd43cf6eSHans Rosenfeld 		case IWN_MPDU_RX_DONE:
3016fd43cf6eSHans Rosenfeld 			/* An 802.11 frame has been received. */
3017fd43cf6eSHans Rosenfeld 			iwn_rx_done(sc, desc, data);
3018fd43cf6eSHans Rosenfeld 			break;
3019fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
3020fd43cf6eSHans Rosenfeld 		case IWN_RX_COMPRESSED_BA:
3021fd43cf6eSHans Rosenfeld 			/* A Compressed BlockAck has been received. */
3022fd43cf6eSHans Rosenfeld 			iwn_rx_compressed_ba(sc, desc, data);
3023fd43cf6eSHans Rosenfeld 			break;
3024fd43cf6eSHans Rosenfeld #endif
3025fd43cf6eSHans Rosenfeld 		case IWN_TX_DONE:
3026fd43cf6eSHans Rosenfeld 			/* An 802.11 frame has been transmitted. */
3027fd43cf6eSHans Rosenfeld 			ops->tx_done(sc, desc, data);
3028fd43cf6eSHans Rosenfeld 			break;
3029fd43cf6eSHans Rosenfeld 
3030fd43cf6eSHans Rosenfeld 		case IWN_RX_STATISTICS:
3031fd43cf6eSHans Rosenfeld 		case IWN_BEACON_STATISTICS:
3032fd43cf6eSHans Rosenfeld 			mutex_enter(&sc->sc_mtx);
3033fd43cf6eSHans Rosenfeld 			iwn_rx_statistics(sc, desc, data);
3034fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
3035fd43cf6eSHans Rosenfeld 			break;
3036fd43cf6eSHans Rosenfeld 
3037fd43cf6eSHans Rosenfeld 		case IWN_BEACON_MISSED:
3038fd43cf6eSHans Rosenfeld 		{
3039fd43cf6eSHans Rosenfeld 			struct iwn_beacon_missed *miss =
3040fd43cf6eSHans Rosenfeld 			    (struct iwn_beacon_missed *)(desc + 1);
3041fd43cf6eSHans Rosenfeld 
3042fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl,
3043fd43cf6eSHans Rosenfeld 			    sizeof (*desc), sizeof (*miss),
3044fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORKERNEL);
3045fd43cf6eSHans Rosenfeld 			/*
3046fd43cf6eSHans Rosenfeld 			 * If more than iwn_beacons_missed_disconnect
3047fd43cf6eSHans Rosenfeld 			 * consecutive beacons are missed, we've probably lost
3048fd43cf6eSHans Rosenfeld 			 * our connection.
3049fd43cf6eSHans Rosenfeld 			 * If more than iwn_beacons_missed_sensitivity
3050fd43cf6eSHans Rosenfeld 			 * consecutive beacons are missed, reinitialize the
3051fd43cf6eSHans Rosenfeld 			 * sensitivity state machine.
3052fd43cf6eSHans Rosenfeld 			 */
3053fd43cf6eSHans Rosenfeld 			DTRACE_PROBE1(beacons__missed,
3054fd43cf6eSHans Rosenfeld 			    struct iwn_beacon_missed *, miss);
3055fd43cf6eSHans Rosenfeld 			if (ic->ic_state == IEEE80211_S_RUN) {
3056fd43cf6eSHans Rosenfeld 				if (le32toh(miss->consecutive)
3057fd43cf6eSHans Rosenfeld 				    > iwn_beacons_missed_disconnect) {
3058fd43cf6eSHans Rosenfeld 					dev_err(sc->sc_dip, CE_WARN,
3059fd43cf6eSHans Rosenfeld 					    "!iwn_notif_intr(): %d consecutive "
3060fd43cf6eSHans Rosenfeld 					    "beacons missed, disconnecting",
3061fd43cf6eSHans Rosenfeld 					    le32toh(miss->consecutive));
3062fd43cf6eSHans Rosenfeld 					ieee80211_new_state(ic,
3063fd43cf6eSHans Rosenfeld 					    IEEE80211_S_INIT, -1);
3064fd43cf6eSHans Rosenfeld 				} else if (le32toh(miss->consecutive)
3065fd43cf6eSHans Rosenfeld 				    > iwn_beacons_missed_sensitivity) {
3066fd43cf6eSHans Rosenfeld 					mutex_enter(&sc->sc_mtx);
3067fd43cf6eSHans Rosenfeld 					(void)iwn_init_sensitivity(sc);
3068fd43cf6eSHans Rosenfeld 					mutex_exit(&sc->sc_mtx);
3069fd43cf6eSHans Rosenfeld 				}
3070fd43cf6eSHans Rosenfeld 			}
3071fd43cf6eSHans Rosenfeld 			break;
3072fd43cf6eSHans Rosenfeld 		}
3073fd43cf6eSHans Rosenfeld 		case IWN_UC_READY:
3074fd43cf6eSHans Rosenfeld 		{
3075fd43cf6eSHans Rosenfeld 			struct iwn_ucode_info *uc =
3076fd43cf6eSHans Rosenfeld 			    (struct iwn_ucode_info *)(desc + 1);
3077fd43cf6eSHans Rosenfeld 
3078fd43cf6eSHans Rosenfeld 			/* The microcontroller is ready. */
3079fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl,
3080fd43cf6eSHans Rosenfeld 			    sizeof (*desc), sizeof (*uc),
3081fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORKERNEL);
3082fd43cf6eSHans Rosenfeld 			DTRACE_PROBE1(uc__ready, struct iwn_ucode_info *, uc)
3083fd43cf6eSHans Rosenfeld 
3084fd43cf6eSHans Rosenfeld 			if (le32toh(uc->valid) != 1) {
3085fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN,
3086fd43cf6eSHans Rosenfeld 				    "!microcontroller initialization failed");
3087fd43cf6eSHans Rosenfeld 				break;
3088fd43cf6eSHans Rosenfeld 			}
3089fd43cf6eSHans Rosenfeld 			if (uc->subtype == IWN_UCODE_INIT) {
3090fd43cf6eSHans Rosenfeld 				/* Save microcontroller report. */
3091fd43cf6eSHans Rosenfeld 				memcpy(&sc->ucode_info, uc, sizeof (*uc));
3092fd43cf6eSHans Rosenfeld 			}
3093fd43cf6eSHans Rosenfeld 			/* Save the address of the error log in SRAM. */
3094fd43cf6eSHans Rosenfeld 			sc->errptr = le32toh(uc->errptr);
3095fd43cf6eSHans Rosenfeld 			break;
3096fd43cf6eSHans Rosenfeld 		}
3097fd43cf6eSHans Rosenfeld 		case IWN_STATE_CHANGED:
3098fd43cf6eSHans Rosenfeld 		{
3099fd43cf6eSHans Rosenfeld 			/*LINTED: E_PTR_BAD_CAST_ALIGN*/
3100fd43cf6eSHans Rosenfeld 			uint32_t *status = (uint32_t *)(desc + 1);
3101fd43cf6eSHans Rosenfeld 
3102fd43cf6eSHans Rosenfeld 			/* Enabled/disabled notification. */
3103fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl,
3104fd43cf6eSHans Rosenfeld 			    sizeof (*desc), sizeof (*status),
3105fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORKERNEL);
3106fd43cf6eSHans Rosenfeld 			DTRACE_PROBE1(state__changed, uint32_t, *status);
3107fd43cf6eSHans Rosenfeld 
3108fd43cf6eSHans Rosenfeld 			if (le32toh(*status) & 1) {
3109fd43cf6eSHans Rosenfeld 				/* The radio button has to be pushed. */
3110fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN,
3111fd43cf6eSHans Rosenfeld 				    "!Radio transmitter is off");
3112fd43cf6eSHans Rosenfeld 				/* Turn the interface down. */
3113fd43cf6eSHans Rosenfeld 				mutex_enter(&sc->sc_mtx);
3114fd43cf6eSHans Rosenfeld 				sc->sc_flags |=
3115fd43cf6eSHans Rosenfeld 				    IWN_FLAG_HW_ERR_RECOVER |
3116fd43cf6eSHans Rosenfeld 				    IWN_FLAG_RADIO_OFF;
3117fd43cf6eSHans Rosenfeld 				mutex_exit(&sc->sc_mtx);
3118fd43cf6eSHans Rosenfeld 				ieee80211_new_state(&sc->sc_ic,
3119fd43cf6eSHans Rosenfeld 				    IEEE80211_S_INIT, -1);
3120fd43cf6eSHans Rosenfeld 
3121fd43cf6eSHans Rosenfeld 				return;	/* No further processing. */
3122fd43cf6eSHans Rosenfeld 			}
3123fd43cf6eSHans Rosenfeld 			break;
3124fd43cf6eSHans Rosenfeld 		}
3125fd43cf6eSHans Rosenfeld 		case IWN_START_SCAN:
3126fd43cf6eSHans Rosenfeld 		{
3127fd43cf6eSHans Rosenfeld 			struct iwn_start_scan *scan =
3128fd43cf6eSHans Rosenfeld 			    (struct iwn_start_scan *)(desc + 1);
3129fd43cf6eSHans Rosenfeld 
3130fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl,
3131fd43cf6eSHans Rosenfeld 			    sizeof (*desc), sizeof (*scan),
3132fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORKERNEL);
3133fd43cf6eSHans Rosenfeld 			DTRACE_PROBE2(start__scan, uint8_t, scan->chan,
3134fd43cf6eSHans Rosenfeld 			    uint32_t, le32toh(scan->status));
3135fd43cf6eSHans Rosenfeld 
3136fd43cf6eSHans Rosenfeld 			/* Fix current channel. */
3137fd43cf6eSHans Rosenfeld 			ic->ic_curchan = ic->ic_bss->in_chan =
3138fd43cf6eSHans Rosenfeld 			    &ic->ic_sup_channels[scan->chan];
3139fd43cf6eSHans Rosenfeld 			break;
3140fd43cf6eSHans Rosenfeld 		}
3141fd43cf6eSHans Rosenfeld 		case IWN_STOP_SCAN:
3142fd43cf6eSHans Rosenfeld 		{
3143fd43cf6eSHans Rosenfeld 			struct iwn_stop_scan *scan =
3144fd43cf6eSHans Rosenfeld 			    (struct iwn_stop_scan *)(desc + 1);
3145fd43cf6eSHans Rosenfeld 
3146fd43cf6eSHans Rosenfeld 			(void) ddi_dma_sync(data->dma_data.dma_hdl,
3147fd43cf6eSHans Rosenfeld 			    sizeof (*desc), sizeof (*scan),
3148fd43cf6eSHans Rosenfeld 			    DDI_DMA_SYNC_FORKERNEL);
3149fd43cf6eSHans Rosenfeld 			DTRACE_PROBE3(stop__scan, uint8_t, scan->chan,
3150fd43cf6eSHans Rosenfeld 			    uint32_t, le32toh(scan->status),
3151fd43cf6eSHans Rosenfeld 			    uint8_t, scan->nchan);
3152fd43cf6eSHans Rosenfeld 
3153fd43cf6eSHans Rosenfeld 			if (iwn_enable_5ghz != 0 &&
3154fd43cf6eSHans Rosenfeld 			    (sc->sc_flags & IWN_FLAG_SCANNING_2GHZ) &&
3155fd43cf6eSHans Rosenfeld 			    (sc->sc_flags & IWN_FLAG_HAS_5GHZ)) {
3156fd43cf6eSHans Rosenfeld 				/*
3157fd43cf6eSHans Rosenfeld 				 * We just finished scanning 2GHz channels,
3158fd43cf6eSHans Rosenfeld 				 * start scanning 5GHz ones.
3159fd43cf6eSHans Rosenfeld 				 */
3160fd43cf6eSHans Rosenfeld 				mutex_enter(&sc->sc_mtx);
3161fd43cf6eSHans Rosenfeld 				sc->sc_flags |= IWN_FLAG_SCANNING_5GHZ;
3162fd43cf6eSHans Rosenfeld 				sc->sc_flags &= ~IWN_FLAG_SCANNING_2GHZ;
3163fd43cf6eSHans Rosenfeld 				if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0) {
3164fd43cf6eSHans Rosenfeld 					mutex_exit(&sc->sc_mtx);
3165fd43cf6eSHans Rosenfeld 					break;
3166fd43cf6eSHans Rosenfeld 				}
3167fd43cf6eSHans Rosenfeld 				mutex_exit(&sc->sc_mtx);
3168fd43cf6eSHans Rosenfeld 			}
3169fd43cf6eSHans Rosenfeld 			ieee80211_end_scan(ic);
3170fd43cf6eSHans Rosenfeld 			mutex_enter(&sc->sc_mtx);
3171fd43cf6eSHans Rosenfeld 			sc->sc_flags &= ~IWN_FLAG_SCANNING;
3172fd43cf6eSHans Rosenfeld 			cv_signal(&sc->sc_scan_cv);
3173fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
3174fd43cf6eSHans Rosenfeld 			(void) untimeout(sc->scan_to);
3175fd43cf6eSHans Rosenfeld 			sc->scan_to = 0;
3176fd43cf6eSHans Rosenfeld 			break;
3177fd43cf6eSHans Rosenfeld 		}
3178fd43cf6eSHans Rosenfeld 		case IWN5000_CALIBRATION_RESULT:
3179fd43cf6eSHans Rosenfeld 			iwn5000_rx_calib_results(sc, desc, data);
3180fd43cf6eSHans Rosenfeld 			break;
3181fd43cf6eSHans Rosenfeld 
3182fd43cf6eSHans Rosenfeld 		case IWN5000_CALIBRATION_DONE:
3183fd43cf6eSHans Rosenfeld 			mutex_enter(&sc->sc_mtx);
3184fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
3185fd43cf6eSHans Rosenfeld 			cv_signal(&sc->sc_calib_cv);
3186fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
3187fd43cf6eSHans Rosenfeld 			break;
3188fd43cf6eSHans Rosenfeld 		}
3189fd43cf6eSHans Rosenfeld 
3190fd43cf6eSHans Rosenfeld 		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
3191fd43cf6eSHans Rosenfeld 	}
3192fd43cf6eSHans Rosenfeld 
3193fd43cf6eSHans Rosenfeld 	/* Tell the firmware what we have processed. */
3194fd43cf6eSHans Rosenfeld 	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
3195fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
3196fd43cf6eSHans Rosenfeld }
3197fd43cf6eSHans Rosenfeld 
3198fd43cf6eSHans Rosenfeld /*
3199fd43cf6eSHans Rosenfeld  * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
3200fd43cf6eSHans Rosenfeld  * from power-down sleep mode.
3201fd43cf6eSHans Rosenfeld  */
3202fd43cf6eSHans Rosenfeld static void
iwn_wakeup_intr(struct iwn_softc * sc)3203fd43cf6eSHans Rosenfeld iwn_wakeup_intr(struct iwn_softc *sc)
3204fd43cf6eSHans Rosenfeld {
3205fd43cf6eSHans Rosenfeld 	int qid;
3206fd43cf6eSHans Rosenfeld 
3207fd43cf6eSHans Rosenfeld 	DTRACE_PROBE(wakeup__intr);
3208fd43cf6eSHans Rosenfeld 
3209fd43cf6eSHans Rosenfeld 	/* Wakeup RX and TX rings. */
3210fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
3211fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < sc->ntxqs; qid++) {
3212fd43cf6eSHans Rosenfeld 		struct iwn_tx_ring *ring = &sc->txq[qid];
3213fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
3214fd43cf6eSHans Rosenfeld 	}
3215fd43cf6eSHans Rosenfeld }
3216fd43cf6eSHans Rosenfeld 
3217fd43cf6eSHans Rosenfeld /*
3218fd43cf6eSHans Rosenfeld  * Dump the error log of the firmware when a firmware panic occurs.  Although
3219fd43cf6eSHans Rosenfeld  * we can't debug the firmware because it is neither open source nor free, it
3220fd43cf6eSHans Rosenfeld  * can help us to identify certain classes of problems.
3221fd43cf6eSHans Rosenfeld  */
3222fd43cf6eSHans Rosenfeld static void
iwn_fatal_intr(struct iwn_softc * sc)3223fd43cf6eSHans Rosenfeld iwn_fatal_intr(struct iwn_softc *sc)
3224fd43cf6eSHans Rosenfeld {
3225*35060ceaSToomas Soome 	struct iwn_fw_dump *dump;
3226*35060ceaSToomas Soome 	uint32_t buf[P2ROUNDUP(sizeof (*dump), sizeof (uint32_t)) /
3227*35060ceaSToomas Soome 	    sizeof (uint32_t)];
3228fd43cf6eSHans Rosenfeld 	int i;
3229fd43cf6eSHans Rosenfeld 
3230fd43cf6eSHans Rosenfeld 	/* Force a complete recalibration on next init. */
3231fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
3232fd43cf6eSHans Rosenfeld 
3233fd43cf6eSHans Rosenfeld 	/* Check that the error log address is valid. */
3234fd43cf6eSHans Rosenfeld 	if (sc->errptr < IWN_FW_DATA_BASE ||
3235*35060ceaSToomas Soome 	    sc->errptr + sizeof (*dump) >
3236fd43cf6eSHans Rosenfeld 	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
3237fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3238fd43cf6eSHans Rosenfeld 		    "!bad firmware error log address 0x%08x", sc->errptr);
3239fd43cf6eSHans Rosenfeld 		return;
3240fd43cf6eSHans Rosenfeld 	}
3241fd43cf6eSHans Rosenfeld 	if (iwn_nic_lock(sc) != 0) {
3242fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3243fd43cf6eSHans Rosenfeld 		    "!could not read firmware error log");
3244fd43cf6eSHans Rosenfeld 		return;
3245fd43cf6eSHans Rosenfeld 	}
3246fd43cf6eSHans Rosenfeld 	/* Read firmware error log from SRAM. */
3247*35060ceaSToomas Soome 	dump = (struct iwn_fw_dump *)buf;
3248*35060ceaSToomas Soome 	iwn_mem_read_region_4(sc, sc->errptr, buf, ARRAY_SIZE(buf));
3249fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
3250fd43cf6eSHans Rosenfeld 
3251*35060ceaSToomas Soome 	if (dump->valid == 0) {
3252fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3253fd43cf6eSHans Rosenfeld 		    "!firmware error log is empty");
3254fd43cf6eSHans Rosenfeld 		return;
3255fd43cf6eSHans Rosenfeld 	}
3256fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN, "!firmware error log:");
3257fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!  error type      = \"%s\" (0x%08X)",
3258*35060ceaSToomas Soome 	    (dump->id < __arraycount(iwn_fw_errmsg)) ?
3259*35060ceaSToomas Soome 		iwn_fw_errmsg[dump->id] : "UNKNOWN",
3260*35060ceaSToomas Soome 	    dump->id);
3261*35060ceaSToomas Soome 	dev_err(sc->sc_dip, CE_CONT, "!  program counter = 0x%08X", dump->pc);
3262fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!  source line     = 0x%08X",
3263*35060ceaSToomas Soome 	    dump->src_line);
3264fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!  error data      = 0x%08X%08X",
3265*35060ceaSToomas Soome 	    dump->error_data[0], dump->error_data[1]);
3266fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!  branch link     = 0x%08X%08X",
3267*35060ceaSToomas Soome 	    dump->branch_link[0], dump->branch_link[1]);
3268fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_CONT, "!  interrupt link  = 0x%08X%08X",
3269*35060ceaSToomas Soome 	    dump->interrupt_link[0], dump->interrupt_link[1]);
3270*35060ceaSToomas Soome 	dev_err(sc->sc_dip, CE_CONT, "!  time            = %u", dump->time[0]);
3271fd43cf6eSHans Rosenfeld 
3272fd43cf6eSHans Rosenfeld 	/* Dump driver status (TX and RX rings) while we're here. */
3273fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN, "!driver status:");
3274fd43cf6eSHans Rosenfeld 	for (i = 0; i < sc->ntxqs; i++) {
3275fd43cf6eSHans Rosenfeld 		struct iwn_tx_ring *ring = &sc->txq[i];
3276fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3277fd43cf6eSHans Rosenfeld 		    "!  tx ring %2d: qid=%2d cur=%3d queued=%3d",
3278fd43cf6eSHans Rosenfeld 		    i, ring->qid, ring->cur, ring->queued);
3279fd43cf6eSHans Rosenfeld 	}
3280fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN, "!  rx ring: cur=%d", sc->rxq.cur);
3281fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN, "!  802.11 state %d", sc->sc_ic.ic_state);
3282fd43cf6eSHans Rosenfeld }
3283fd43cf6eSHans Rosenfeld 
3284fd43cf6eSHans Rosenfeld /*ARGSUSED1*/
3285fd43cf6eSHans Rosenfeld static uint_t
iwn_intr(caddr_t arg,caddr_t unused)3286fd43cf6eSHans Rosenfeld iwn_intr(caddr_t arg, caddr_t unused)
3287fd43cf6eSHans Rosenfeld {
3288fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(unused));
3289fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
3290fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)arg;
3291fd43cf6eSHans Rosenfeld 	uint32_t r1, r2, tmp;
3292fd43cf6eSHans Rosenfeld 
3293fd43cf6eSHans Rosenfeld 	if (sc == NULL)
3294fd43cf6eSHans Rosenfeld 		return (DDI_INTR_UNCLAIMED);
3295fd43cf6eSHans Rosenfeld 
3296fd43cf6eSHans Rosenfeld 	/* Disable interrupts. */
3297fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, 0);
3298fd43cf6eSHans Rosenfeld 
3299fd43cf6eSHans Rosenfeld 	/* Read interrupts from ICT (fast) or from registers (slow). */
3300fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3301fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(sc->ict_dma.dma_hdl, 0, 0,
3302fd43cf6eSHans Rosenfeld 		    DDI_DMA_SYNC_FORKERNEL);
3303fd43cf6eSHans Rosenfeld 		tmp = 0;
3304fd43cf6eSHans Rosenfeld 		while (sc->ict[sc->ict_cur] != 0) {
3305fd43cf6eSHans Rosenfeld 			tmp |= sc->ict[sc->ict_cur];
3306fd43cf6eSHans Rosenfeld 			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
3307fd43cf6eSHans Rosenfeld 			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
3308fd43cf6eSHans Rosenfeld 		}
3309fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(sc->ict_dma.dma_hdl, 0, 0,
3310fd43cf6eSHans Rosenfeld 		    DDI_DMA_SYNC_FORDEV);
3311fd43cf6eSHans Rosenfeld 		tmp = le32toh(tmp);
3312fd43cf6eSHans Rosenfeld 		if (tmp == 0xffffffff)	/* Shouldn't happen. */
3313fd43cf6eSHans Rosenfeld 			tmp = 0;
3314fd43cf6eSHans Rosenfeld 		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
3315fd43cf6eSHans Rosenfeld 			tmp |= 0x8000;
3316fd43cf6eSHans Rosenfeld 		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
3317fd43cf6eSHans Rosenfeld 		r2 = 0;	/* Unused. */
3318fd43cf6eSHans Rosenfeld 	} else {
3319fd43cf6eSHans Rosenfeld 		r1 = IWN_READ(sc, IWN_INT);
3320fd43cf6eSHans Rosenfeld 		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
3321fd43cf6eSHans Rosenfeld 			return (DDI_INTR_UNCLAIMED);	/* Hardware gone! */
3322fd43cf6eSHans Rosenfeld 		r2 = IWN_READ(sc, IWN_FH_INT);
3323fd43cf6eSHans Rosenfeld 	}
3324fd43cf6eSHans Rosenfeld 	if (r1 == 0 && r2 == 0) {
3325fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3326fd43cf6eSHans Rosenfeld 		return (DDI_INTR_UNCLAIMED);	/* Interrupt not for us. */
3327fd43cf6eSHans Rosenfeld 	}
3328fd43cf6eSHans Rosenfeld 
3329fd43cf6eSHans Rosenfeld 	/* Acknowledge interrupts. */
3330fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, r1);
3331fd43cf6eSHans Rosenfeld 	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
3332fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_FH_INT, r2);
3333fd43cf6eSHans Rosenfeld 
3334fd43cf6eSHans Rosenfeld 	if (r1 & IWN_INT_RF_TOGGLED) {
3335fd43cf6eSHans Rosenfeld 		tmp = IWN_READ(sc, IWN_GP_CNTRL);
3336fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_NOTE,
3337fd43cf6eSHans Rosenfeld 		    "!RF switch: radio %s",
3338fd43cf6eSHans Rosenfeld 		    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
3339fd43cf6eSHans Rosenfeld 	}
3340fd43cf6eSHans Rosenfeld 	if (r1 & IWN_INT_CT_REACHED) {
3341fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3342fd43cf6eSHans Rosenfeld 		    "!critical temperature reached!");
3343fd43cf6eSHans Rosenfeld 	}
3344fd43cf6eSHans Rosenfeld 	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
3345fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
3346fd43cf6eSHans Rosenfeld 		    "!fatal firmware error");
3347fd43cf6eSHans Rosenfeld 		/* Dump firmware error log and stop. */
3348fd43cf6eSHans Rosenfeld 		iwn_fatal_intr(sc);
3349fd43cf6eSHans Rosenfeld 		iwn_hw_stop(sc, B_TRUE);
3350fd43cf6eSHans Rosenfeld 		if (!IWN_CHK_FAST_RECOVER(sc))
3351fd43cf6eSHans Rosenfeld 			ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
3352fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
3353fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER;
3354fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
3355fd43cf6eSHans Rosenfeld 
3356fd43cf6eSHans Rosenfeld 		return (DDI_INTR_CLAIMED);
3357fd43cf6eSHans Rosenfeld 	}
3358fd43cf6eSHans Rosenfeld 	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
3359fd43cf6eSHans Rosenfeld 	    (r2 & IWN_FH_INT_RX)) {
3360fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3361fd43cf6eSHans Rosenfeld 			int ena = (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX));
3362fd43cf6eSHans Rosenfeld 
3363fd43cf6eSHans Rosenfeld 			if (ena)
3364fd43cf6eSHans Rosenfeld 				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
3365fd43cf6eSHans Rosenfeld 			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3366fd43cf6eSHans Rosenfeld 			    IWN_INT_PERIODIC_DIS);
3367fd43cf6eSHans Rosenfeld 			iwn_notif_intr(sc);
3368fd43cf6eSHans Rosenfeld 			if (ena)
3369fd43cf6eSHans Rosenfeld 				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3370fd43cf6eSHans Rosenfeld 				    IWN_INT_PERIODIC_ENA);
3371fd43cf6eSHans Rosenfeld 		} else {
3372fd43cf6eSHans Rosenfeld 			iwn_notif_intr(sc);
3373fd43cf6eSHans Rosenfeld 		}
3374fd43cf6eSHans Rosenfeld 	}
3375fd43cf6eSHans Rosenfeld 
3376fd43cf6eSHans Rosenfeld 	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
3377fd43cf6eSHans Rosenfeld 		if (sc->sc_flags & IWN_FLAG_USE_ICT)
3378fd43cf6eSHans Rosenfeld 			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
3379fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
3380fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_FW_DMA;
3381fd43cf6eSHans Rosenfeld 		cv_signal(&sc->sc_fhdma_cv);
3382fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
3383fd43cf6eSHans Rosenfeld 	}
3384fd43cf6eSHans Rosenfeld 
3385fd43cf6eSHans Rosenfeld 	if (r1 & IWN_INT_ALIVE) {
3386fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
3387fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_FW_ALIVE;
3388fd43cf6eSHans Rosenfeld 		cv_signal(&sc->sc_alive_cv);
3389fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
3390fd43cf6eSHans Rosenfeld 	}
3391fd43cf6eSHans Rosenfeld 
3392fd43cf6eSHans Rosenfeld 	if (r1 & IWN_INT_WAKEUP)
3393fd43cf6eSHans Rosenfeld 		iwn_wakeup_intr(sc);
3394fd43cf6eSHans Rosenfeld 
3395fd43cf6eSHans Rosenfeld 	/* Re-enable interrupts. */
3396fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3397fd43cf6eSHans Rosenfeld 	return (DDI_INTR_CLAIMED);
3398fd43cf6eSHans Rosenfeld }
3399fd43cf6eSHans Rosenfeld 
3400fd43cf6eSHans Rosenfeld /*
3401fd43cf6eSHans Rosenfeld  * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3402fd43cf6eSHans Rosenfeld  * 5000 adapters use a slightly different format).
3403fd43cf6eSHans Rosenfeld  */
3404fd43cf6eSHans Rosenfeld static void
iwn4965_update_sched(struct iwn_softc * sc,int qid,int idx,uint8_t id,uint16_t len)3405fd43cf6eSHans Rosenfeld iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3406fd43cf6eSHans Rosenfeld     uint16_t len)
3407fd43cf6eSHans Rosenfeld {
3408fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(id));
3409fd43cf6eSHans Rosenfeld 	int w_idx = qid * IWN4965_SCHED_COUNT + idx;
3410fd43cf6eSHans Rosenfeld 	uint16_t *w = &sc->sched[w_idx];
3411fd43cf6eSHans Rosenfeld 
3412fd43cf6eSHans Rosenfeld 	*w = htole16(len + 8);
3413fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t),
3414fd43cf6eSHans Rosenfeld 	    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3415fd43cf6eSHans Rosenfeld 	if (idx < IWN_SCHED_WINSZ) {
3416fd43cf6eSHans Rosenfeld 		*(w + IWN_TX_RING_COUNT) = *w;
3417fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(sc->sched_dma.dma_hdl,
3418fd43cf6eSHans Rosenfeld 		    (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t),
3419fd43cf6eSHans Rosenfeld 		    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3420fd43cf6eSHans Rosenfeld 	}
3421fd43cf6eSHans Rosenfeld }
3422fd43cf6eSHans Rosenfeld 
3423fd43cf6eSHans Rosenfeld static void
iwn5000_update_sched(struct iwn_softc * sc,int qid,int idx,uint8_t id,uint16_t len)3424fd43cf6eSHans Rosenfeld iwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3425fd43cf6eSHans Rosenfeld     uint16_t len)
3426fd43cf6eSHans Rosenfeld {
3427fd43cf6eSHans Rosenfeld 	int w_idx = qid * IWN5000_SCHED_COUNT + idx;
3428fd43cf6eSHans Rosenfeld 	uint16_t *w = &sc->sched[w_idx];
3429fd43cf6eSHans Rosenfeld 
3430fd43cf6eSHans Rosenfeld 	*w = htole16(id << 12 | (len + 8));
3431fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t),
3432fd43cf6eSHans Rosenfeld 	    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3433fd43cf6eSHans Rosenfeld 	if (idx < IWN_SCHED_WINSZ) {
3434fd43cf6eSHans Rosenfeld 		*(w + IWN_TX_RING_COUNT) = *w;
3435fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(sc->sched_dma.dma_hdl,
3436fd43cf6eSHans Rosenfeld 		    (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t),
3437fd43cf6eSHans Rosenfeld 		    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3438fd43cf6eSHans Rosenfeld 	}
3439fd43cf6eSHans Rosenfeld }
3440fd43cf6eSHans Rosenfeld 
3441fd43cf6eSHans Rosenfeld #ifdef notyet
3442fd43cf6eSHans Rosenfeld static void
iwn5000_reset_sched(struct iwn_softc * sc,int qid,int idx)3443fd43cf6eSHans Rosenfeld iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3444fd43cf6eSHans Rosenfeld {
3445fd43cf6eSHans Rosenfeld 	int w_idx = qid * IWN5000_SCHED_COUNT + idx;
3446fd43cf6eSHans Rosenfeld 	uint16_t *w = &sc->sched[w_idx];
3447fd43cf6eSHans Rosenfeld 
3448fd43cf6eSHans Rosenfeld 	*w = (*w & htole16(0xf000)) | htole16(1);
3449fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t),
3450fd43cf6eSHans Rosenfeld 	    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3451fd43cf6eSHans Rosenfeld 	if (idx < IWN_SCHED_WINSZ) {
3452fd43cf6eSHans Rosenfeld 		*(w + IWN_TX_RING_COUNT) = *w;
3453fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(sc->sched_dma.dma_hdl,
3454fd43cf6eSHans Rosenfeld 		    (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t),
3455fd43cf6eSHans Rosenfeld 		    sizeof (uint16_t), DDI_DMA_SYNC_FORDEV);
3456fd43cf6eSHans Rosenfeld 	}
3457fd43cf6eSHans Rosenfeld }
3458fd43cf6eSHans Rosenfeld #endif
3459fd43cf6eSHans Rosenfeld 
3460fd43cf6eSHans Rosenfeld /*
3461fd43cf6eSHans Rosenfeld  * This function is only for compatibility with Net80211 module.
3462fd43cf6eSHans Rosenfeld  * iwn_qosparam_to_hw() is the actual function updating EDCA
3463fd43cf6eSHans Rosenfeld  * parameters to hardware.
3464fd43cf6eSHans Rosenfeld  */
3465fd43cf6eSHans Rosenfeld static int
iwn_wme_update(struct ieee80211com * ic)3466fd43cf6eSHans Rosenfeld iwn_wme_update(struct ieee80211com *ic)
3467fd43cf6eSHans Rosenfeld {
3468fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(ic));
3469fd43cf6eSHans Rosenfeld 	return (0);
3470fd43cf6eSHans Rosenfeld }
3471fd43cf6eSHans Rosenfeld 
3472fd43cf6eSHans Rosenfeld static int
iwn_wme_to_qos_ac(struct iwn_softc * sc,int wme_ac)3473fd43cf6eSHans Rosenfeld iwn_wme_to_qos_ac(struct iwn_softc *sc, int wme_ac)
3474fd43cf6eSHans Rosenfeld {
3475fd43cf6eSHans Rosenfeld 	int qos_ac;
3476fd43cf6eSHans Rosenfeld 
3477fd43cf6eSHans Rosenfeld 	switch (wme_ac) {
3478fd43cf6eSHans Rosenfeld 	case WME_AC_BE:
3479fd43cf6eSHans Rosenfeld 		qos_ac = QOS_AC_BK;
3480fd43cf6eSHans Rosenfeld 		break;
3481fd43cf6eSHans Rosenfeld 	case WME_AC_BK:
3482fd43cf6eSHans Rosenfeld 		qos_ac = QOS_AC_BE;
3483fd43cf6eSHans Rosenfeld 		break;
3484fd43cf6eSHans Rosenfeld 	case WME_AC_VI:
3485fd43cf6eSHans Rosenfeld 		qos_ac = QOS_AC_VI;
3486fd43cf6eSHans Rosenfeld 		break;
3487fd43cf6eSHans Rosenfeld 	case WME_AC_VO:
3488fd43cf6eSHans Rosenfeld 		qos_ac = QOS_AC_VO;
3489fd43cf6eSHans Rosenfeld 		break;
3490fd43cf6eSHans Rosenfeld 	default:
3491fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_wme_to_qos_ac(): "
3492fd43cf6eSHans Rosenfeld 		    "WME AC index is not in suitable range.\n");
3493fd43cf6eSHans Rosenfeld 		qos_ac = QOS_AC_INVALID;
3494fd43cf6eSHans Rosenfeld 		break;
3495fd43cf6eSHans Rosenfeld 	}
3496fd43cf6eSHans Rosenfeld 
3497fd43cf6eSHans Rosenfeld 	return (qos_ac);
3498fd43cf6eSHans Rosenfeld }
3499fd43cf6eSHans Rosenfeld 
3500fd43cf6eSHans Rosenfeld static uint16_t
iwn_cw_e_to_cw(uint8_t cw_e)3501fd43cf6eSHans Rosenfeld iwn_cw_e_to_cw(uint8_t cw_e)
3502fd43cf6eSHans Rosenfeld {
3503fd43cf6eSHans Rosenfeld 	uint16_t cw = 1;
3504fd43cf6eSHans Rosenfeld 
3505fd43cf6eSHans Rosenfeld 	while (cw_e > 0) {
3506fd43cf6eSHans Rosenfeld 		cw <<= 1;
3507fd43cf6eSHans Rosenfeld 		cw_e--;
3508fd43cf6eSHans Rosenfeld 	}
3509fd43cf6eSHans Rosenfeld 
3510fd43cf6eSHans Rosenfeld 	cw -= 1;
3511fd43cf6eSHans Rosenfeld 	return (cw);
3512fd43cf6eSHans Rosenfeld }
3513fd43cf6eSHans Rosenfeld 
3514fd43cf6eSHans Rosenfeld static int
iwn_wmeparam_check(struct iwn_softc * sc,struct wmeParams * wmeparam)3515fd43cf6eSHans Rosenfeld iwn_wmeparam_check(struct iwn_softc *sc, struct wmeParams *wmeparam)
3516fd43cf6eSHans Rosenfeld {
3517fd43cf6eSHans Rosenfeld 	int i;
3518fd43cf6eSHans Rosenfeld 
3519fd43cf6eSHans Rosenfeld 	for (i = 0; i < WME_NUM_AC; i++) {
3520fd43cf6eSHans Rosenfeld 
3521fd43cf6eSHans Rosenfeld 		if ((wmeparam[i].wmep_logcwmax > QOS_CW_RANGE_MAX) ||
3522fd43cf6eSHans Rosenfeld 		    (wmeparam[i].wmep_logcwmin >= wmeparam[i].wmep_logcwmax)) {
3523fd43cf6eSHans Rosenfeld 			cmn_err(CE_WARN, "iwn_wmeparam_check(): "
3524fd43cf6eSHans Rosenfeld 			    "Contention window is not in suitable range.\n");
3525fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
3526fd43cf6eSHans Rosenfeld 		}
3527fd43cf6eSHans Rosenfeld 
3528fd43cf6eSHans Rosenfeld 		if ((wmeparam[i].wmep_aifsn < QOS_AIFSN_MIN) ||
3529fd43cf6eSHans Rosenfeld 		    (wmeparam[i].wmep_aifsn > QOS_AIFSN_MAX)) {
3530fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN, "!iwn_wmeparam_check(): "
3531fd43cf6eSHans Rosenfeld 			    "Arbitration interframe space number"
3532fd43cf6eSHans Rosenfeld 			    "is not in suitable range.\n");
3533fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
3534fd43cf6eSHans Rosenfeld 		}
3535fd43cf6eSHans Rosenfeld 	}
3536fd43cf6eSHans Rosenfeld 
3537fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
3538fd43cf6eSHans Rosenfeld }
3539fd43cf6eSHans Rosenfeld 
3540fd43cf6eSHans Rosenfeld /*
3541fd43cf6eSHans Rosenfeld  * This function updates EDCA parameters into hardware.
3542fd43cf6eSHans Rosenfeld  * FIFO0-background, FIFO1-best effort, FIFO2-video, FIFO3-voice.
3543fd43cf6eSHans Rosenfeld  */
3544fd43cf6eSHans Rosenfeld static int
iwn_qosparam_to_hw(struct iwn_softc * sc,int async)3545fd43cf6eSHans Rosenfeld iwn_qosparam_to_hw(struct iwn_softc *sc, int async)
3546fd43cf6eSHans Rosenfeld {
3547fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic = &sc->sc_ic;
3548fd43cf6eSHans Rosenfeld 	ieee80211_node_t *in = ic->ic_bss;
3549fd43cf6eSHans Rosenfeld 	struct wmeParams *wmeparam;
3550fd43cf6eSHans Rosenfeld 	struct iwn_edca_params edcaparam;
3551fd43cf6eSHans Rosenfeld 	int i, j;
3552fd43cf6eSHans Rosenfeld 	int err = IWN_FAIL;
3553fd43cf6eSHans Rosenfeld 
3554fd43cf6eSHans Rosenfeld 	if ((in->in_flags & IEEE80211_NODE_QOS) &&
3555fd43cf6eSHans Rosenfeld 	    (IEEE80211_M_STA == ic->ic_opmode)) {
3556fd43cf6eSHans Rosenfeld 		wmeparam = ic->ic_wme.wme_chanParams.cap_wmeParams;
3557fd43cf6eSHans Rosenfeld 	} else {
3558fd43cf6eSHans Rosenfeld 		return (IWN_SUCCESS);
3559fd43cf6eSHans Rosenfeld 	}
3560fd43cf6eSHans Rosenfeld 
3561fd43cf6eSHans Rosenfeld 	(void) memset(&edcaparam, 0, sizeof (edcaparam));
3562fd43cf6eSHans Rosenfeld 
3563fd43cf6eSHans Rosenfeld 	err = iwn_wmeparam_check(sc, wmeparam);
3564fd43cf6eSHans Rosenfeld 	if (err != IWN_SUCCESS) {
3565fd43cf6eSHans Rosenfeld 		return (err);
3566fd43cf6eSHans Rosenfeld 	}
3567fd43cf6eSHans Rosenfeld 
3568fd43cf6eSHans Rosenfeld 	if (in->in_flags & IEEE80211_NODE_QOS) {
3569fd43cf6eSHans Rosenfeld 		edcaparam.flags |= QOS_PARAM_FLG_UPDATE_EDCA;
3570fd43cf6eSHans Rosenfeld 	}
3571fd43cf6eSHans Rosenfeld 
3572fd43cf6eSHans Rosenfeld 	if (in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT)) {
3573fd43cf6eSHans Rosenfeld 		edcaparam.flags |= QOS_PARAM_FLG_TGN;
3574fd43cf6eSHans Rosenfeld 	}
3575fd43cf6eSHans Rosenfeld 
3576fd43cf6eSHans Rosenfeld 	for (i = 0; i < WME_NUM_AC; i++) {
3577fd43cf6eSHans Rosenfeld 
3578fd43cf6eSHans Rosenfeld 		j = iwn_wme_to_qos_ac(sc, i);
3579fd43cf6eSHans Rosenfeld 		if (j < QOS_AC_BK || j > QOS_AC_VO) {
3580fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
3581fd43cf6eSHans Rosenfeld 		}
3582fd43cf6eSHans Rosenfeld 
3583fd43cf6eSHans Rosenfeld 		sc->sc_edca->ac[j].cwmin.value.ul = edcaparam.ac[j].cwmin =
3584fd43cf6eSHans Rosenfeld 		    iwn_cw_e_to_cw(wmeparam[i].wmep_logcwmin);
3585fd43cf6eSHans Rosenfeld 		sc->sc_edca->ac[j].cwmax.value.ul = edcaparam.ac[j].cwmax =
3586fd43cf6eSHans Rosenfeld 		    iwn_cw_e_to_cw(wmeparam[i].wmep_logcwmax);
3587fd43cf6eSHans Rosenfeld 		sc->sc_edca->ac[j].aifsn.value.ul = edcaparam.ac[j].aifsn =
3588fd43cf6eSHans Rosenfeld 		    wmeparam[i].wmep_aifsn;
3589fd43cf6eSHans Rosenfeld 		sc->sc_edca->ac[j].txop.value.ul = edcaparam.ac[j].txoplimit =
3590fd43cf6eSHans Rosenfeld 		    (uint16_t)(wmeparam[i].wmep_txopLimit * 32);
3591fd43cf6eSHans Rosenfeld 	}
3592fd43cf6eSHans Rosenfeld 
3593fd43cf6eSHans Rosenfeld 	err = iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &edcaparam,
3594fd43cf6eSHans Rosenfeld 	    sizeof (edcaparam), async);
3595fd43cf6eSHans Rosenfeld 	if (err != IWN_SUCCESS) {
3596fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_qosparam_to_hw(): "
3597fd43cf6eSHans Rosenfeld 		    "failed to update QoS parameters into hardware.");
3598fd43cf6eSHans Rosenfeld 		return (err);
3599fd43cf6eSHans Rosenfeld 	}
3600fd43cf6eSHans Rosenfeld 
3601fd43cf6eSHans Rosenfeld 	return (err);
3602fd43cf6eSHans Rosenfeld }
3603fd43cf6eSHans Rosenfeld 
3604fd43cf6eSHans Rosenfeld static inline int
iwn_wme_tid_qos_ac(int tid)3605fd43cf6eSHans Rosenfeld iwn_wme_tid_qos_ac(int tid)
3606fd43cf6eSHans Rosenfeld {
3607fd43cf6eSHans Rosenfeld 	switch (tid) {
3608fd43cf6eSHans Rosenfeld 	case 1:
3609fd43cf6eSHans Rosenfeld 	case 2:
3610fd43cf6eSHans Rosenfeld 		return (QOS_AC_BK);
3611fd43cf6eSHans Rosenfeld 	case 0:
3612fd43cf6eSHans Rosenfeld 	case 3:
3613fd43cf6eSHans Rosenfeld 		return (QOS_AC_BE);
3614fd43cf6eSHans Rosenfeld 	case 4:
3615fd43cf6eSHans Rosenfeld 	case 5:
3616fd43cf6eSHans Rosenfeld 		return (QOS_AC_VI);
3617fd43cf6eSHans Rosenfeld 	case 6:
3618fd43cf6eSHans Rosenfeld 	case 7:
3619fd43cf6eSHans Rosenfeld 		return (QOS_AC_VO);
3620fd43cf6eSHans Rosenfeld 	}
3621fd43cf6eSHans Rosenfeld 
3622fd43cf6eSHans Rosenfeld 	return (QOS_AC_BE);
3623fd43cf6eSHans Rosenfeld }
3624fd43cf6eSHans Rosenfeld 
3625fd43cf6eSHans Rosenfeld static inline int
iwn_qos_ac_to_txq(int qos_ac)3626fd43cf6eSHans Rosenfeld iwn_qos_ac_to_txq(int qos_ac)
3627fd43cf6eSHans Rosenfeld {
3628fd43cf6eSHans Rosenfeld 	switch (qos_ac) {
3629fd43cf6eSHans Rosenfeld 	case QOS_AC_BK:
3630fd43cf6eSHans Rosenfeld 		return (QOS_AC_BK_TO_TXQ);
3631fd43cf6eSHans Rosenfeld 	case QOS_AC_BE:
3632fd43cf6eSHans Rosenfeld 		return (QOS_AC_BE_TO_TXQ);
3633fd43cf6eSHans Rosenfeld 	case QOS_AC_VI:
3634fd43cf6eSHans Rosenfeld 		return (QOS_AC_VI_TO_TXQ);
3635fd43cf6eSHans Rosenfeld 	case QOS_AC_VO:
3636fd43cf6eSHans Rosenfeld 		return (QOS_AC_VO_TO_TXQ);
3637fd43cf6eSHans Rosenfeld 	}
3638fd43cf6eSHans Rosenfeld 
3639fd43cf6eSHans Rosenfeld 	return (QOS_AC_BE_TO_TXQ);
3640fd43cf6eSHans Rosenfeld }
3641fd43cf6eSHans Rosenfeld 
3642fd43cf6eSHans Rosenfeld static int
iwn_wme_tid_to_txq(struct iwn_softc * sc,int tid)3643fd43cf6eSHans Rosenfeld iwn_wme_tid_to_txq(struct iwn_softc *sc, int tid)
3644fd43cf6eSHans Rosenfeld {
3645fd43cf6eSHans Rosenfeld 	int queue_n = TXQ_FOR_AC_INVALID;
3646fd43cf6eSHans Rosenfeld 	int qos_ac;
3647fd43cf6eSHans Rosenfeld 
3648fd43cf6eSHans Rosenfeld 	if (tid < WME_TID_MIN ||
3649fd43cf6eSHans Rosenfeld 	    tid > WME_TID_MAX) {
3650fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!wme_tid_to_txq(): "
3651fd43cf6eSHans Rosenfeld 		    "TID is not in suitable range.");
3652fd43cf6eSHans Rosenfeld 		return (queue_n);
3653fd43cf6eSHans Rosenfeld 	}
3654fd43cf6eSHans Rosenfeld 
3655fd43cf6eSHans Rosenfeld 	qos_ac = iwn_wme_tid_qos_ac(tid);
3656fd43cf6eSHans Rosenfeld 	queue_n = iwn_qos_ac_to_txq(qos_ac);
3657fd43cf6eSHans Rosenfeld 
3658fd43cf6eSHans Rosenfeld 	return (queue_n);
3659fd43cf6eSHans Rosenfeld }
3660fd43cf6eSHans Rosenfeld 
3661fd43cf6eSHans Rosenfeld static int
iwn_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)3662fd43cf6eSHans Rosenfeld iwn_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
3663fd43cf6eSHans Rosenfeld {
3664fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)ic;
3665fd43cf6eSHans Rosenfeld 	struct iwn_node *wn;
3666fd43cf6eSHans Rosenfeld 	struct iwn_tx_ring *ring;
3667fd43cf6eSHans Rosenfeld 	struct iwn_tx_desc *desc;
3668fd43cf6eSHans Rosenfeld 	struct iwn_tx_data *data;
3669fd43cf6eSHans Rosenfeld 	struct iwn_tx_cmd *cmd;
3670fd43cf6eSHans Rosenfeld 	struct iwn_cmd_data *tx;
3671fd43cf6eSHans Rosenfeld 	ieee80211_node_t *in;
3672fd43cf6eSHans Rosenfeld 	const struct iwn_rate *rinfo;
3673fd43cf6eSHans Rosenfeld 	struct ieee80211_frame *wh;
3674fd43cf6eSHans Rosenfeld 	struct ieee80211_key *k = NULL;
3675fd43cf6eSHans Rosenfeld 	uint32_t flags;
3676fd43cf6eSHans Rosenfeld 	uint_t hdrlen;
3677fd43cf6eSHans Rosenfeld 	uint8_t ridx, txant;
3678fd43cf6eSHans Rosenfeld 	int i, totlen, seglen, pad;
3679fd43cf6eSHans Rosenfeld 	int txq_id = NON_QOS_TXQ;
3680fd43cf6eSHans Rosenfeld 	struct ieee80211_qosframe *qwh = NULL;
3681fd43cf6eSHans Rosenfeld 	uint8_t tid = WME_TID_INVALID;
3682fd43cf6eSHans Rosenfeld 	ddi_dma_cookie_t cookie;
3683fd43cf6eSHans Rosenfeld 	mblk_t *m0, *m;
3684fd43cf6eSHans Rosenfeld 	int mblen, off;
3685fd43cf6eSHans Rosenfeld 
3686fd43cf6eSHans Rosenfeld 	int noack = 0;
3687fd43cf6eSHans Rosenfeld 
3688fd43cf6eSHans Rosenfeld 	if (ic == NULL)
3689fd43cf6eSHans Rosenfeld 		return (EIO);
3690fd43cf6eSHans Rosenfeld 
3691fd43cf6eSHans Rosenfeld 	if ((mp == NULL) || (MBLKL(mp) <= 0))
3692fd43cf6eSHans Rosenfeld 		return (EIO);
3693fd43cf6eSHans Rosenfeld 
3694fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_SUSPEND) {
3695fd43cf6eSHans Rosenfeld 		freemsg(mp);
3696fd43cf6eSHans Rosenfeld 		sc->sc_tx_err++;
3697fd43cf6eSHans Rosenfeld 		return(EIO);
3698fd43cf6eSHans Rosenfeld 	}
3699fd43cf6eSHans Rosenfeld 
3700fd43cf6eSHans Rosenfeld 	wh = (struct ieee80211_frame *)mp->b_rptr;
3701fd43cf6eSHans Rosenfeld 
3702fd43cf6eSHans Rosenfeld 	hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
3703fd43cf6eSHans Rosenfeld 
3704fd43cf6eSHans Rosenfeld 	/*
3705fd43cf6eSHans Rosenfeld 	 * determine send which AP or station in IBSS
3706fd43cf6eSHans Rosenfeld 	 */
3707fd43cf6eSHans Rosenfeld 	in = ieee80211_find_txnode(ic, wh->i_addr1);
3708fd43cf6eSHans Rosenfeld 	if (in == NULL) {
3709fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_send(): "
3710fd43cf6eSHans Rosenfeld 		    "failed to find tx node");
3711fd43cf6eSHans Rosenfeld 		freemsg(mp);
3712fd43cf6eSHans Rosenfeld 		sc->sc_tx_err++;
3713fd43cf6eSHans Rosenfeld 		return(EIO);
3714fd43cf6eSHans Rosenfeld 	}
3715fd43cf6eSHans Rosenfeld 
3716fd43cf6eSHans Rosenfeld 	wn = (struct iwn_node *)in;
3717fd43cf6eSHans Rosenfeld 
3718fd43cf6eSHans Rosenfeld 	/*
3719fd43cf6eSHans Rosenfeld 	 * Determine TX queue according to traffic ID in frame
3720fd43cf6eSHans Rosenfeld 	 * if working in QoS mode.
3721fd43cf6eSHans Rosenfeld 	 */
3722fd43cf6eSHans Rosenfeld 	if (in->in_flags & IEEE80211_NODE_QOS) {
3723fd43cf6eSHans Rosenfeld 		if ((type & IEEE80211_FC0_TYPE_MASK) ==
3724fd43cf6eSHans Rosenfeld 		    IEEE80211_FC0_TYPE_DATA) {
3725fd43cf6eSHans Rosenfeld 			if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
3726fd43cf6eSHans Rosenfeld 				qwh = (struct ieee80211_qosframe *)wh;
3727fd43cf6eSHans Rosenfeld 
3728fd43cf6eSHans Rosenfeld 				tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
3729fd43cf6eSHans Rosenfeld 				txq_id = iwn_wme_tid_to_txq(sc, tid);
3730fd43cf6eSHans Rosenfeld 
3731fd43cf6eSHans Rosenfeld 				if (txq_id < TXQ_FOR_AC_MIN ||
3732fd43cf6eSHans Rosenfeld 				    (txq_id > TXQ_FOR_AC_MAX)) {
3733fd43cf6eSHans Rosenfeld 					freemsg(mp);
3734fd43cf6eSHans Rosenfeld 					sc->sc_tx_err++;
3735fd43cf6eSHans Rosenfeld 					return(EIO);
3736fd43cf6eSHans Rosenfeld 				}
3737fd43cf6eSHans Rosenfeld 			} else {
3738fd43cf6eSHans Rosenfeld 				txq_id = NON_QOS_TXQ;
3739fd43cf6eSHans Rosenfeld 			}
3740fd43cf6eSHans Rosenfeld 		} else if ((type & IEEE80211_FC0_TYPE_MASK) ==
3741fd43cf6eSHans Rosenfeld 		    IEEE80211_FC0_TYPE_MGT) {
3742fd43cf6eSHans Rosenfeld 			txq_id = QOS_TXQ_FOR_MGT;
3743fd43cf6eSHans Rosenfeld 		} else {
3744fd43cf6eSHans Rosenfeld 			txq_id = NON_QOS_TXQ;
3745fd43cf6eSHans Rosenfeld 		}
3746fd43cf6eSHans Rosenfeld 	} else {
3747fd43cf6eSHans Rosenfeld 		txq_id = NON_QOS_TXQ;
3748fd43cf6eSHans Rosenfeld 	}
3749fd43cf6eSHans Rosenfeld 
3750fd43cf6eSHans Rosenfeld 	if (sc->qfullmsk & (1 << txq_id)) {
3751fd43cf6eSHans Rosenfeld 		sc->sc_tx_err++;
3752fd43cf6eSHans Rosenfeld 		/* net80211-initiated send */
3753fd43cf6eSHans Rosenfeld 		if ((type & IEEE80211_FC0_TYPE_MASK) !=
3754fd43cf6eSHans Rosenfeld 		    IEEE80211_FC0_TYPE_DATA)
3755fd43cf6eSHans Rosenfeld 			freemsg(mp);
3756fd43cf6eSHans Rosenfeld 		return (EAGAIN);
3757fd43cf6eSHans Rosenfeld 	}
3758fd43cf6eSHans Rosenfeld 
3759fd43cf6eSHans Rosenfeld 	/* Choose a TX rate index. */
3760fd43cf6eSHans Rosenfeld 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3761fd43cf6eSHans Rosenfeld 	    type != IEEE80211_FC0_TYPE_DATA) {
3762fd43cf6eSHans Rosenfeld 		ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
3763fd43cf6eSHans Rosenfeld 		    IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
3764fd43cf6eSHans Rosenfeld 	} else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
3765fd43cf6eSHans Rosenfeld 		ridx = sc->fixed_ridx;
3766fd43cf6eSHans Rosenfeld 	} else
3767fd43cf6eSHans Rosenfeld 		ridx = wn->ridx[in->in_txrate];
3768fd43cf6eSHans Rosenfeld 	rinfo = &iwn_rates[ridx];
3769fd43cf6eSHans Rosenfeld 
3770fd43cf6eSHans Rosenfeld 	m = allocb(msgdsize(mp) + 32, BPRI_MED);
3771fd43cf6eSHans Rosenfeld 	if (m) {
3772fd43cf6eSHans Rosenfeld 		for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
3773fd43cf6eSHans Rosenfeld 			mblen = MBLKL(m0);
3774fd43cf6eSHans Rosenfeld 			bcopy(m0->b_rptr, m->b_rptr + off, mblen);
3775fd43cf6eSHans Rosenfeld 			off += mblen;
3776fd43cf6eSHans Rosenfeld 		}
3777fd43cf6eSHans Rosenfeld 
3778fd43cf6eSHans Rosenfeld 		m->b_wptr += off;
3779fd43cf6eSHans Rosenfeld 
3780fd43cf6eSHans Rosenfeld 		freemsg(mp);
3781fd43cf6eSHans Rosenfeld 		mp = m;
3782fd43cf6eSHans Rosenfeld 
3783fd43cf6eSHans Rosenfeld 		wh = (struct ieee80211_frame *)mp->b_rptr;
3784fd43cf6eSHans Rosenfeld 	} else {
3785fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_send(): can't copy");
3786fd43cf6eSHans Rosenfeld 		/* net80211-initiated send */
3787fd43cf6eSHans Rosenfeld 		if ((type & IEEE80211_FC0_TYPE_MASK) !=
3788fd43cf6eSHans Rosenfeld 		    IEEE80211_FC0_TYPE_DATA)
3789fd43cf6eSHans Rosenfeld 			freemsg(mp);
3790fd43cf6eSHans Rosenfeld 		return (EAGAIN);
3791fd43cf6eSHans Rosenfeld 	}
3792fd43cf6eSHans Rosenfeld 
3793fd43cf6eSHans Rosenfeld 
3794fd43cf6eSHans Rosenfeld 	/*
3795fd43cf6eSHans Rosenfeld 	 * Net80211 module encapsulate outbound data frames.
3796fd43cf6eSHans Rosenfeld 	 * Add some fields of 80211 frame.
3797fd43cf6eSHans Rosenfeld 	 */
3798fd43cf6eSHans Rosenfeld 	if ((type & IEEE80211_FC0_TYPE_MASK) ==
3799fd43cf6eSHans Rosenfeld 	    IEEE80211_FC0_TYPE_DATA)
3800fd43cf6eSHans Rosenfeld 		(void) ieee80211_encap(ic, mp, in);
3801fd43cf6eSHans Rosenfeld 
3802fd43cf6eSHans Rosenfeld 	/* Encrypt the frame if need be. */
3803fd43cf6eSHans Rosenfeld 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3804fd43cf6eSHans Rosenfeld 		k = ieee80211_crypto_encap(ic, mp);
3805fd43cf6eSHans Rosenfeld 		if (k == NULL) {
3806fd43cf6eSHans Rosenfeld 			freemsg(mp);
3807fd43cf6eSHans Rosenfeld 			return(EIO);
3808fd43cf6eSHans Rosenfeld 		}
3809fd43cf6eSHans Rosenfeld 		/* Packet header may have moved, reset our local pointer. */
3810fd43cf6eSHans Rosenfeld 		wh = (struct ieee80211_frame *)mp->b_rptr;
3811fd43cf6eSHans Rosenfeld 	}
3812fd43cf6eSHans Rosenfeld 	totlen = msgdsize(mp);
3813fd43cf6eSHans Rosenfeld 
3814fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_tx_mtx);
3815fd43cf6eSHans Rosenfeld 	ring = &sc->txq[txq_id];
3816fd43cf6eSHans Rosenfeld 	desc = &ring->desc[ring->cur];
3817fd43cf6eSHans Rosenfeld 	data = &ring->data[ring->cur];
3818fd43cf6eSHans Rosenfeld 
3819fd43cf6eSHans Rosenfeld 	/* Prepare TX firmware command. */
3820fd43cf6eSHans Rosenfeld 	cmd = &ring->cmd[ring->cur];
3821fd43cf6eSHans Rosenfeld 	cmd->code = IWN_CMD_TX_DATA;
3822fd43cf6eSHans Rosenfeld 	cmd->flags = 0;
3823fd43cf6eSHans Rosenfeld 	cmd->qid = ring->qid;
3824fd43cf6eSHans Rosenfeld 	cmd->idx = ring->cur;
3825fd43cf6eSHans Rosenfeld 
3826fd43cf6eSHans Rosenfeld 	tx = (struct iwn_cmd_data *)cmd->data;
3827fd43cf6eSHans Rosenfeld 	/* NB: No need to clear tx, all fields are reinitialized here. */
3828fd43cf6eSHans Rosenfeld 	tx->scratch = 0;	/* clear "scratch" area */
3829fd43cf6eSHans Rosenfeld 
3830fd43cf6eSHans Rosenfeld 	flags = 0;
3831fd43cf6eSHans Rosenfeld 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3832fd43cf6eSHans Rosenfeld 		/* Unicast frame, check if an ACK is expected. */
3833fd43cf6eSHans Rosenfeld 		if (!noack)
3834fd43cf6eSHans Rosenfeld 			flags |= IWN_TX_NEED_ACK;
3835fd43cf6eSHans Rosenfeld 	}
3836fd43cf6eSHans Rosenfeld 
3837fd43cf6eSHans Rosenfeld 	if ((wh->i_fc[0] &
3838fd43cf6eSHans Rosenfeld 	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3839fd43cf6eSHans Rosenfeld 	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3840fd43cf6eSHans Rosenfeld 		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3841fd43cf6eSHans Rosenfeld 
3842fd43cf6eSHans Rosenfeld 	ASSERT((flags & IWN_TX_IMM_BA) == 0);
3843fd43cf6eSHans Rosenfeld 
3844fd43cf6eSHans Rosenfeld 	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3845fd43cf6eSHans Rosenfeld 		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3846fd43cf6eSHans Rosenfeld 
3847fd43cf6eSHans Rosenfeld 	ASSERT((flags & IWN_TX_MORE_FRAG) == 0);
3848fd43cf6eSHans Rosenfeld 
3849fd43cf6eSHans Rosenfeld 	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3850fd43cf6eSHans Rosenfeld 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3851fd43cf6eSHans Rosenfeld 		/* NB: Group frames are sent using CCK in 802.11b/g. */
3852fd43cf6eSHans Rosenfeld 		if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
3853fd43cf6eSHans Rosenfeld 			flags |= IWN_TX_NEED_RTS;
3854fd43cf6eSHans Rosenfeld 		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3855fd43cf6eSHans Rosenfeld 		    ridx >= IWN_RIDX_OFDM6) {
3856fd43cf6eSHans Rosenfeld 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3857fd43cf6eSHans Rosenfeld 				flags |= IWN_TX_NEED_CTS;
3858fd43cf6eSHans Rosenfeld 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3859fd43cf6eSHans Rosenfeld 				flags |= IWN_TX_NEED_RTS;
3860fd43cf6eSHans Rosenfeld 		}
3861fd43cf6eSHans Rosenfeld 		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3862fd43cf6eSHans Rosenfeld 			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3863fd43cf6eSHans Rosenfeld 				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3864fd43cf6eSHans Rosenfeld 				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3865fd43cf6eSHans Rosenfeld 				flags |= IWN_TX_NEED_PROTECTION;
3866fd43cf6eSHans Rosenfeld 			} else
3867fd43cf6eSHans Rosenfeld 				flags |= IWN_TX_FULL_TXOP;
3868fd43cf6eSHans Rosenfeld 		}
3869fd43cf6eSHans Rosenfeld 	}
3870fd43cf6eSHans Rosenfeld 
3871fd43cf6eSHans Rosenfeld 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3872fd43cf6eSHans Rosenfeld 	    type != IEEE80211_FC0_TYPE_DATA)
3873fd43cf6eSHans Rosenfeld 		tx->id = sc->broadcast_id;
3874fd43cf6eSHans Rosenfeld 	else
3875fd43cf6eSHans Rosenfeld 		tx->id = wn->id;
3876fd43cf6eSHans Rosenfeld 
3877fd43cf6eSHans Rosenfeld 	if (type == IEEE80211_FC0_TYPE_MGT) {
3878fd43cf6eSHans Rosenfeld 		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3879fd43cf6eSHans Rosenfeld 
3880fd43cf6eSHans Rosenfeld #ifndef IEEE80211_STA_ONLY
3881fd43cf6eSHans Rosenfeld 		/* Tell HW to set timestamp in probe responses. */
3882fd43cf6eSHans Rosenfeld 		/* XXX NetBSD rev 1.11 added probe requests here but */
3883fd43cf6eSHans Rosenfeld 		/* probe requests do not take timestamps (from Bergamini). */
3884fd43cf6eSHans Rosenfeld 		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3885fd43cf6eSHans Rosenfeld 			flags |= IWN_TX_INSERT_TSTAMP;
3886fd43cf6eSHans Rosenfeld #endif
3887fd43cf6eSHans Rosenfeld 		/* XXX NetBSD rev 1.11 and 1.20 added AUTH/DAUTH and RTS/CTS */
3888fd43cf6eSHans Rosenfeld 		/* changes here. These are not needed (from Bergamini). */
3889fd43cf6eSHans Rosenfeld 		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3890fd43cf6eSHans Rosenfeld 		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3891fd43cf6eSHans Rosenfeld 			tx->timeout = htole16(3);
3892fd43cf6eSHans Rosenfeld 		else
3893fd43cf6eSHans Rosenfeld 			tx->timeout = htole16(2);
3894fd43cf6eSHans Rosenfeld 	} else
3895fd43cf6eSHans Rosenfeld 		tx->timeout = htole16(0);
3896fd43cf6eSHans Rosenfeld 
3897fd43cf6eSHans Rosenfeld 	if (hdrlen & 3) {
3898fd43cf6eSHans Rosenfeld 		/* First segment length must be a multiple of 4. */
3899fd43cf6eSHans Rosenfeld 		flags |= IWN_TX_NEED_PADDING;
3900fd43cf6eSHans Rosenfeld 		pad = 4 - (hdrlen & 3);
3901fd43cf6eSHans Rosenfeld 	} else
3902fd43cf6eSHans Rosenfeld 		pad = 0;
3903fd43cf6eSHans Rosenfeld 
3904fd43cf6eSHans Rosenfeld 	if (tid != WME_TID_INVALID) {
3905fd43cf6eSHans Rosenfeld 		flags &= ~IWN_TX_AUTO_SEQ;
3906fd43cf6eSHans Rosenfeld 	} else {
3907fd43cf6eSHans Rosenfeld 		flags |= IWN_TX_AUTO_SEQ;
3908fd43cf6eSHans Rosenfeld 		tid = 0;
3909fd43cf6eSHans Rosenfeld 	}
3910fd43cf6eSHans Rosenfeld 
3911fd43cf6eSHans Rosenfeld 	tx->len = htole16(totlen);
3912fd43cf6eSHans Rosenfeld 	tx->tid = tid;
3913fd43cf6eSHans Rosenfeld 	tx->rts_ntries = 60;
3914fd43cf6eSHans Rosenfeld 	tx->data_ntries = 15;
3915fd43cf6eSHans Rosenfeld 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3916fd43cf6eSHans Rosenfeld 	tx->plcp = rinfo->plcp;
3917fd43cf6eSHans Rosenfeld 	tx->rflags = rinfo->flags;
3918fd43cf6eSHans Rosenfeld 	if (tx->id == sc->broadcast_id) {
3919fd43cf6eSHans Rosenfeld 		/* Group or management frame. */
3920fd43cf6eSHans Rosenfeld 		tx->linkq = 0;
3921fd43cf6eSHans Rosenfeld 		/* XXX Alternate between antenna A and B? */
3922fd43cf6eSHans Rosenfeld 		txant = IWN_LSB(sc->txchainmask);
3923fd43cf6eSHans Rosenfeld 		tx->rflags |= IWN_RFLAG_ANT(txant);
3924fd43cf6eSHans Rosenfeld 	} else {
3925fd43cf6eSHans Rosenfeld 		tx->linkq = in->in_rates.ir_nrates - in->in_txrate - 1;
3926fd43cf6eSHans Rosenfeld 		flags |= IWN_TX_LINKQ;	/* enable MRR */
3927fd43cf6eSHans Rosenfeld 	}
3928fd43cf6eSHans Rosenfeld 	/* Set physical address of "scratch area". */
3929fd43cf6eSHans Rosenfeld 	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3930fd43cf6eSHans Rosenfeld 	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3931fd43cf6eSHans Rosenfeld 
3932fd43cf6eSHans Rosenfeld 	/* Copy 802.11 header in TX command. */
3933fd43cf6eSHans Rosenfeld 	/* XXX NetBSD changed this in rev 1.20 */
3934fd43cf6eSHans Rosenfeld 	memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen);
3935fd43cf6eSHans Rosenfeld 	mp->b_rptr += hdrlen;
3936fd43cf6eSHans Rosenfeld 
3937fd43cf6eSHans Rosenfeld 	bcopy(mp->b_rptr, data->dma_data.vaddr, totlen - hdrlen);
3938fd43cf6eSHans Rosenfeld 	tx->security = 0;
3939fd43cf6eSHans Rosenfeld 	tx->flags = htole32(flags);
3940fd43cf6eSHans Rosenfeld 
3941fd43cf6eSHans Rosenfeld 	data->ni = in;
3942fd43cf6eSHans Rosenfeld 
3943fd43cf6eSHans Rosenfeld 	DTRACE_PROBE4(tx, int, ring->qid, int, ring->cur, size_t, MBLKL(mp),
3944fd43cf6eSHans Rosenfeld 	    int, data->dma_data.ncookies);
3945fd43cf6eSHans Rosenfeld 
3946fd43cf6eSHans Rosenfeld 	/* Fill TX descriptor. */
3947fd43cf6eSHans Rosenfeld 	desc->nsegs = 1 + data->dma_data.ncookies;
3948fd43cf6eSHans Rosenfeld 	/* First DMA segment is used by the TX command. */
3949fd43cf6eSHans Rosenfeld 	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3950fd43cf6eSHans Rosenfeld 	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3951fd43cf6eSHans Rosenfeld 	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3952fd43cf6eSHans Rosenfeld 
3953fd43cf6eSHans Rosenfeld 	/* Other DMA segments are for data payload. */
3954fd43cf6eSHans Rosenfeld 	cookie = data->dma_data.cookie;
3955fd43cf6eSHans Rosenfeld 	for (i = 1, seglen = totlen - hdrlen;
3956fd43cf6eSHans Rosenfeld 	     i <= data->dma_data.ncookies;
3957fd43cf6eSHans Rosenfeld 	     i++, seglen -= cookie.dmac_size) {
3958fd43cf6eSHans Rosenfeld 		desc->segs[i].addr = htole32(IWN_LOADDR(cookie.dmac_laddress));
3959fd43cf6eSHans Rosenfeld 		desc->segs[i].len  = htole16(IWN_HIADDR(cookie.dmac_laddress) |
3960fd43cf6eSHans Rosenfeld 		    seglen << 4);
3961fd43cf6eSHans Rosenfeld 		if (i < data->dma_data.ncookies)
3962fd43cf6eSHans Rosenfeld 			ddi_dma_nextcookie(data->dma_data.dma_hdl, &cookie);
3963fd43cf6eSHans Rosenfeld 	}
3964fd43cf6eSHans Rosenfeld 
3965fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
3966fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->cmd_dma.dma_hdl, ring->cur * sizeof (*cmd),
3967fd43cf6eSHans Rosenfeld 	    sizeof (*cmd), DDI_DMA_SYNC_FORDEV);
3968fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->desc_dma.dma_hdl, ring->cur * sizeof (*desc),
3969fd43cf6eSHans Rosenfeld 	    sizeof (*desc), DDI_DMA_SYNC_FORDEV);
3970fd43cf6eSHans Rosenfeld 
3971fd43cf6eSHans Rosenfeld 	/* Update TX scheduler. */
3972fd43cf6eSHans Rosenfeld 	sc->ops.update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3973fd43cf6eSHans Rosenfeld 
3974fd43cf6eSHans Rosenfeld 	/* Kick TX ring. */
3975fd43cf6eSHans Rosenfeld 	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3976fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3977fd43cf6eSHans Rosenfeld 
3978fd43cf6eSHans Rosenfeld 	/* Mark TX ring as full if we reach a certain threshold. */
3979fd43cf6eSHans Rosenfeld 	if (++ring->queued > IWN_TX_RING_HIMARK)
3980fd43cf6eSHans Rosenfeld 		sc->qfullmsk |= 1 << ring->qid;
3981fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_tx_mtx);
3982fd43cf6eSHans Rosenfeld 	freemsg(mp);
3983fd43cf6eSHans Rosenfeld 
3984fd43cf6eSHans Rosenfeld 	ic->ic_stats.is_tx_bytes += totlen;
3985fd43cf6eSHans Rosenfeld 
3986fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mt_mtx);
3987fd43cf6eSHans Rosenfeld 	if (sc->sc_tx_timer == 0)
3988fd43cf6eSHans Rosenfeld 		sc->sc_tx_timer = 5;
3989fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mt_mtx);
3990fd43cf6eSHans Rosenfeld 
3991fd43cf6eSHans Rosenfeld 	return 0;
3992fd43cf6eSHans Rosenfeld }
3993fd43cf6eSHans Rosenfeld 
3994fd43cf6eSHans Rosenfeld static mblk_t *
iwn_m_tx(void * arg,mblk_t * mp)3995fd43cf6eSHans Rosenfeld iwn_m_tx(void *arg, mblk_t *mp)
3996fd43cf6eSHans Rosenfeld {
3997fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
3998fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic;
3999fd43cf6eSHans Rosenfeld 	mblk_t *next;
4000fd43cf6eSHans Rosenfeld 
4001fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4002fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4003fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
4004fd43cf6eSHans Rosenfeld 
4005fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_SUSPEND) {
4006fd43cf6eSHans Rosenfeld 		freemsgchain(mp);
4007fd43cf6eSHans Rosenfeld 		return (NULL);
4008fd43cf6eSHans Rosenfeld 	}
4009fd43cf6eSHans Rosenfeld 
4010fd43cf6eSHans Rosenfeld 	if (ic->ic_state != IEEE80211_S_RUN) {
4011fd43cf6eSHans Rosenfeld 		freemsgchain(mp);
4012fd43cf6eSHans Rosenfeld 		return (NULL);
4013fd43cf6eSHans Rosenfeld 	}
4014fd43cf6eSHans Rosenfeld 
4015fd43cf6eSHans Rosenfeld 	if ((sc->sc_flags & IWN_FLAG_HW_ERR_RECOVER)) {
4016fd43cf6eSHans Rosenfeld 		freemsgchain(mp);
4017fd43cf6eSHans Rosenfeld 		return (NULL);
4018fd43cf6eSHans Rosenfeld 	}
4019fd43cf6eSHans Rosenfeld 
4020fd43cf6eSHans Rosenfeld 	while (mp != NULL) {
4021fd43cf6eSHans Rosenfeld 		next = mp->b_next;
4022fd43cf6eSHans Rosenfeld 		mp->b_next = NULL;
4023fd43cf6eSHans Rosenfeld 		if (iwn_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == EAGAIN) {
4024fd43cf6eSHans Rosenfeld 			mp->b_next = next;
4025fd43cf6eSHans Rosenfeld 			break;
4026fd43cf6eSHans Rosenfeld 		}
4027fd43cf6eSHans Rosenfeld 		mp = next;
4028fd43cf6eSHans Rosenfeld 	}
4029fd43cf6eSHans Rosenfeld 
4030fd43cf6eSHans Rosenfeld 	return (mp);
4031fd43cf6eSHans Rosenfeld }
4032fd43cf6eSHans Rosenfeld 
4033fd43cf6eSHans Rosenfeld static void
iwn_watchdog(void * arg)4034fd43cf6eSHans Rosenfeld iwn_watchdog(void *arg)
4035fd43cf6eSHans Rosenfeld {
4036fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)arg;
4037fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic = &sc->sc_ic;
4038fd43cf6eSHans Rosenfeld 	timeout_id_t timeout_id = ic->ic_watchdog_timer;
4039fd43cf6eSHans Rosenfeld 
4040fd43cf6eSHans Rosenfeld 	ieee80211_stop_watchdog(ic);
4041fd43cf6eSHans Rosenfeld 
4042fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mt_mtx);
4043fd43cf6eSHans Rosenfeld 	if (sc->sc_tx_timer > 0) {
4044fd43cf6eSHans Rosenfeld 		if (--sc->sc_tx_timer == 0) {
4045fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN, "!device timeout");
4046fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER;
4047fd43cf6eSHans Rosenfeld 			sc->sc_ostate = IEEE80211_S_RUN;
4048fd43cf6eSHans Rosenfeld 			DTRACE_PROBE(recover__send__fail);
4049fd43cf6eSHans Rosenfeld 		}
4050fd43cf6eSHans Rosenfeld 	}
4051fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mt_mtx);
4052fd43cf6eSHans Rosenfeld 
4053fd43cf6eSHans Rosenfeld 	if ((ic->ic_state != IEEE80211_S_AUTH) &&
4054fd43cf6eSHans Rosenfeld 	    (ic->ic_state != IEEE80211_S_ASSOC))
4055fd43cf6eSHans Rosenfeld 		return;
4056fd43cf6eSHans Rosenfeld 
4057fd43cf6eSHans Rosenfeld 	if (ic->ic_bss->in_fails > 10) {
4058fd43cf6eSHans Rosenfeld 		DTRACE_PROBE2(watchdog__reset, timeout_id_t, timeout_id,
4059fd43cf6eSHans Rosenfeld 		    struct ieee80211node *, ic->ic_bss);
4060fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_watchdog reset");
4061fd43cf6eSHans Rosenfeld 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
4062fd43cf6eSHans Rosenfeld 	} else {
4063fd43cf6eSHans Rosenfeld 		ic->ic_bss->in_fails++;
4064fd43cf6eSHans Rosenfeld 
4065fd43cf6eSHans Rosenfeld 		DTRACE_PROBE2(watchdog__timeout, timeout_id_t, timeout_id,
4066fd43cf6eSHans Rosenfeld 		    struct ieee80211node *, ic->ic_bss);
4067fd43cf6eSHans Rosenfeld 
4068fd43cf6eSHans Rosenfeld 		ieee80211_watchdog(ic);
4069fd43cf6eSHans Rosenfeld 	}
4070fd43cf6eSHans Rosenfeld }
4071fd43cf6eSHans Rosenfeld 
4072fd43cf6eSHans Rosenfeld static void
iwn_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)4073fd43cf6eSHans Rosenfeld iwn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
4074fd43cf6eSHans Rosenfeld {
4075fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4076fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic;
4077fd43cf6eSHans Rosenfeld 	int  error = 0;
4078fd43cf6eSHans Rosenfeld 
4079fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4080fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4081fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
4082fd43cf6eSHans Rosenfeld 
4083fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
4084fd43cf6eSHans Rosenfeld 	while (sc->sc_flags & IWN_FLAG_SCANNING)
4085fd43cf6eSHans Rosenfeld 		cv_wait(&sc->sc_scan_cv, &sc->sc_mtx);
4086fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
4087fd43cf6eSHans Rosenfeld 
4088fd43cf6eSHans Rosenfeld 	error = ieee80211_ioctl(ic, wq, mp);
4089fd43cf6eSHans Rosenfeld 	if (error == ENETRESET) {
4090fd43cf6eSHans Rosenfeld 		/*
4091fd43cf6eSHans Rosenfeld 		 * This is special for the hidden AP connection.
4092fd43cf6eSHans Rosenfeld 		 * In any case, we should make sure only one 'scan'
4093fd43cf6eSHans Rosenfeld 		 * in the driver for a 'connect' CLI command. So
4094fd43cf6eSHans Rosenfeld 		 * when connecting to a hidden AP, the scan is just
4095fd43cf6eSHans Rosenfeld 		 * sent out to the air when we know the desired
4096fd43cf6eSHans Rosenfeld 		 * essid of the AP we want to connect.
4097fd43cf6eSHans Rosenfeld 		 */
4098fd43cf6eSHans Rosenfeld 		if (ic->ic_des_esslen) {
4099fd43cf6eSHans Rosenfeld 			if (sc->sc_flags & IWN_FLAG_RUNNING) {
4100fd43cf6eSHans Rosenfeld 				DTRACE_PROBE(netreset);
4101fd43cf6eSHans Rosenfeld 				iwn_m_stop(sc);
4102fd43cf6eSHans Rosenfeld 				(void) iwn_m_start(sc);
4103fd43cf6eSHans Rosenfeld 				(void) ieee80211_new_state(ic,
4104fd43cf6eSHans Rosenfeld 				    IEEE80211_S_SCAN, -1);
4105fd43cf6eSHans Rosenfeld 			}
4106fd43cf6eSHans Rosenfeld 		}
4107fd43cf6eSHans Rosenfeld 	}
4108fd43cf6eSHans Rosenfeld }
4109fd43cf6eSHans Rosenfeld 
4110fd43cf6eSHans Rosenfeld /*
4111fd43cf6eSHans Rosenfeld  * Call back functions for get/set property
4112fd43cf6eSHans Rosenfeld  */
4113fd43cf6eSHans Rosenfeld static int
iwn_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)4114fd43cf6eSHans Rosenfeld iwn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
4115fd43cf6eSHans Rosenfeld     uint_t wldp_length, void *wldp_buf)
4116fd43cf6eSHans Rosenfeld {
4117fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4118fd43cf6eSHans Rosenfeld 
4119fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4120fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4121fd43cf6eSHans Rosenfeld 
4122fd43cf6eSHans Rosenfeld 	return (ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
4123fd43cf6eSHans Rosenfeld 	    wldp_length, wldp_buf));
4124fd43cf6eSHans Rosenfeld }
4125fd43cf6eSHans Rosenfeld 
4126fd43cf6eSHans Rosenfeld static void
iwn_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)4127fd43cf6eSHans Rosenfeld iwn_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
4128fd43cf6eSHans Rosenfeld     mac_prop_info_handle_t prh)
4129fd43cf6eSHans Rosenfeld {
4130fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4131fd43cf6eSHans Rosenfeld 
4132fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4133fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4134fd43cf6eSHans Rosenfeld 
4135fd43cf6eSHans Rosenfeld 	ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, prh);
4136fd43cf6eSHans Rosenfeld }
4137fd43cf6eSHans Rosenfeld 
4138fd43cf6eSHans Rosenfeld static int
iwn_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)4139fd43cf6eSHans Rosenfeld iwn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
4140fd43cf6eSHans Rosenfeld     uint_t wldp_length, const void *wldp_buf)
4141fd43cf6eSHans Rosenfeld {
4142fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4143fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic;
4144fd43cf6eSHans Rosenfeld 	int err = EINVAL;
4145fd43cf6eSHans Rosenfeld 
4146fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4147fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4148fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
4149fd43cf6eSHans Rosenfeld 
4150fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
4151fd43cf6eSHans Rosenfeld 	while (sc->sc_flags & IWN_FLAG_SCANNING)
4152fd43cf6eSHans Rosenfeld 		cv_wait(&sc->sc_scan_cv, &sc->sc_mtx);
4153fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
4154fd43cf6eSHans Rosenfeld 
4155fd43cf6eSHans Rosenfeld 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
4156fd43cf6eSHans Rosenfeld 	    wldp_buf);
4157fd43cf6eSHans Rosenfeld 
4158fd43cf6eSHans Rosenfeld 	if (err == ENETRESET) {
4159fd43cf6eSHans Rosenfeld 		if (ic->ic_des_esslen) {
4160fd43cf6eSHans Rosenfeld 			if (sc->sc_flags & IWN_FLAG_RUNNING) {
4161fd43cf6eSHans Rosenfeld 				DTRACE_PROBE(netreset);
4162fd43cf6eSHans Rosenfeld 				iwn_m_stop(sc);
4163fd43cf6eSHans Rosenfeld 				(void) iwn_m_start(sc);
4164fd43cf6eSHans Rosenfeld 				(void) ieee80211_new_state(ic,
4165fd43cf6eSHans Rosenfeld 				    IEEE80211_S_SCAN, -1);
4166fd43cf6eSHans Rosenfeld 			}
4167fd43cf6eSHans Rosenfeld 		}
4168fd43cf6eSHans Rosenfeld 		err = 0;
4169fd43cf6eSHans Rosenfeld 	}
4170fd43cf6eSHans Rosenfeld 
4171fd43cf6eSHans Rosenfeld 	return (err);
4172fd43cf6eSHans Rosenfeld }
4173fd43cf6eSHans Rosenfeld 
4174fd43cf6eSHans Rosenfeld /*
4175fd43cf6eSHans Rosenfeld  * invoked by GLD get statistics from NIC and driver
4176fd43cf6eSHans Rosenfeld  */
4177fd43cf6eSHans Rosenfeld static int
iwn_m_stat(void * arg,uint_t stat,uint64_t * val)4178fd43cf6eSHans Rosenfeld iwn_m_stat(void *arg, uint_t stat, uint64_t *val)
4179fd43cf6eSHans Rosenfeld {
4180fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4181fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic;
4182fd43cf6eSHans Rosenfeld 	ieee80211_node_t *in;
4183fd43cf6eSHans Rosenfeld 
4184fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4185fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4186fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
4187fd43cf6eSHans Rosenfeld 
4188fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
4189fd43cf6eSHans Rosenfeld 
4190fd43cf6eSHans Rosenfeld 	switch (stat) {
4191fd43cf6eSHans Rosenfeld 	case MAC_STAT_IFSPEED:
4192fd43cf6eSHans Rosenfeld 		in = ic->ic_bss;
4193fd43cf6eSHans Rosenfeld 		*val = ((IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) ?
4194fd43cf6eSHans Rosenfeld 		    IEEE80211_RATE(in->in_txrate) :
4195fd43cf6eSHans Rosenfeld 		    ic->ic_fixed_rate) / 2 * 1000000;
4196fd43cf6eSHans Rosenfeld 		break;
4197fd43cf6eSHans Rosenfeld 	case MAC_STAT_NOXMTBUF:
4198fd43cf6eSHans Rosenfeld 		*val = sc->sc_tx_nobuf;
4199fd43cf6eSHans Rosenfeld 		break;
4200fd43cf6eSHans Rosenfeld 	case MAC_STAT_NORCVBUF:
4201fd43cf6eSHans Rosenfeld 		*val = sc->sc_rx_nobuf;
4202fd43cf6eSHans Rosenfeld 		break;
4203fd43cf6eSHans Rosenfeld 	case MAC_STAT_IERRORS:
4204fd43cf6eSHans Rosenfeld 		*val = sc->sc_rx_err;
4205fd43cf6eSHans Rosenfeld 		break;
4206fd43cf6eSHans Rosenfeld 	case MAC_STAT_RBYTES:
4207fd43cf6eSHans Rosenfeld 		*val = ic->ic_stats.is_rx_bytes;
4208fd43cf6eSHans Rosenfeld 		break;
4209fd43cf6eSHans Rosenfeld 	case MAC_STAT_IPACKETS:
4210fd43cf6eSHans Rosenfeld 		*val = ic->ic_stats.is_rx_frags;
4211fd43cf6eSHans Rosenfeld 		break;
4212fd43cf6eSHans Rosenfeld 	case MAC_STAT_OBYTES:
4213fd43cf6eSHans Rosenfeld 		*val = ic->ic_stats.is_tx_bytes;
4214fd43cf6eSHans Rosenfeld 		break;
4215fd43cf6eSHans Rosenfeld 	case MAC_STAT_OPACKETS:
4216fd43cf6eSHans Rosenfeld 		*val = ic->ic_stats.is_tx_frags;
4217fd43cf6eSHans Rosenfeld 		break;
4218fd43cf6eSHans Rosenfeld 	case MAC_STAT_OERRORS:
4219fd43cf6eSHans Rosenfeld 	case WIFI_STAT_TX_FAILED:
4220fd43cf6eSHans Rosenfeld 		*val = sc->sc_tx_err;
4221fd43cf6eSHans Rosenfeld 		break;
4222fd43cf6eSHans Rosenfeld 	case WIFI_STAT_TX_RETRANS:
4223fd43cf6eSHans Rosenfeld 		*val = sc->sc_tx_retries;
4224fd43cf6eSHans Rosenfeld 		break;
4225fd43cf6eSHans Rosenfeld 	case WIFI_STAT_FCS_ERRORS:
4226fd43cf6eSHans Rosenfeld 	case WIFI_STAT_WEP_ERRORS:
4227fd43cf6eSHans Rosenfeld 	case WIFI_STAT_TX_FRAGS:
4228fd43cf6eSHans Rosenfeld 	case WIFI_STAT_MCAST_TX:
4229fd43cf6eSHans Rosenfeld 	case WIFI_STAT_RTS_SUCCESS:
4230fd43cf6eSHans Rosenfeld 	case WIFI_STAT_RTS_FAILURE:
4231fd43cf6eSHans Rosenfeld 	case WIFI_STAT_ACK_FAILURE:
4232fd43cf6eSHans Rosenfeld 	case WIFI_STAT_RX_FRAGS:
4233fd43cf6eSHans Rosenfeld 	case WIFI_STAT_MCAST_RX:
4234fd43cf6eSHans Rosenfeld 	case WIFI_STAT_RX_DUPS:
4235fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4236fd43cf6eSHans Rosenfeld 		return (ieee80211_stat(ic, stat, val));
4237fd43cf6eSHans Rosenfeld 	default:
4238fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4239fd43cf6eSHans Rosenfeld 		return (ENOTSUP);
4240fd43cf6eSHans Rosenfeld 	}
4241fd43cf6eSHans Rosenfeld 
4242fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
4243fd43cf6eSHans Rosenfeld 
4244fd43cf6eSHans Rosenfeld 	return (0);
4245fd43cf6eSHans Rosenfeld 
4246fd43cf6eSHans Rosenfeld }
4247fd43cf6eSHans Rosenfeld 
4248fd43cf6eSHans Rosenfeld /*
4249fd43cf6eSHans Rosenfeld  * invoked by GLD to configure NIC
4250fd43cf6eSHans Rosenfeld  */
4251fd43cf6eSHans Rosenfeld static int
iwn_m_unicst(void * arg,const uint8_t * macaddr)4252fd43cf6eSHans Rosenfeld iwn_m_unicst(void *arg, const uint8_t *macaddr)
4253fd43cf6eSHans Rosenfeld {
4254fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
4255fd43cf6eSHans Rosenfeld 	ieee80211com_t	*ic;
4256fd43cf6eSHans Rosenfeld 	int err = IWN_SUCCESS;
4257fd43cf6eSHans Rosenfeld 
4258fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
4259fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
4260fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
4261fd43cf6eSHans Rosenfeld 
4262fd43cf6eSHans Rosenfeld 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
4263fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
4264fd43cf6eSHans Rosenfeld 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
4265fd43cf6eSHans Rosenfeld 		err = iwn_config(sc);
4266fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4267fd43cf6eSHans Rosenfeld 		if (err != IWN_SUCCESS) {
4268fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN, "!iwn_m_unicst(): "
4269fd43cf6eSHans Rosenfeld 			    "failed to configure device");
4270fd43cf6eSHans Rosenfeld 			goto fail;
4271fd43cf6eSHans Rosenfeld 		}
4272fd43cf6eSHans Rosenfeld 	}
4273fd43cf6eSHans Rosenfeld 
4274fd43cf6eSHans Rosenfeld 	return (err);
4275fd43cf6eSHans Rosenfeld 
4276fd43cf6eSHans Rosenfeld fail:
4277fd43cf6eSHans Rosenfeld 	return (err);
4278fd43cf6eSHans Rosenfeld }
4279fd43cf6eSHans Rosenfeld 
4280fd43cf6eSHans Rosenfeld /*ARGSUSED*/
4281fd43cf6eSHans Rosenfeld static int
iwn_m_multicst(void * arg,boolean_t add,const uint8_t * m)4282fd43cf6eSHans Rosenfeld iwn_m_multicst(void *arg, boolean_t add, const uint8_t *m)
4283fd43cf6eSHans Rosenfeld {
4284fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
4285fd43cf6eSHans Rosenfeld }
4286fd43cf6eSHans Rosenfeld 
4287fd43cf6eSHans Rosenfeld /*ARGSUSED*/
4288fd43cf6eSHans Rosenfeld static int
iwn_m_promisc(void * arg,boolean_t on)4289fd43cf6eSHans Rosenfeld iwn_m_promisc(void *arg, boolean_t on)
4290fd43cf6eSHans Rosenfeld {
4291fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(on));
4292fd43cf6eSHans Rosenfeld 
4293fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
4294fd43cf6eSHans Rosenfeld }
4295fd43cf6eSHans Rosenfeld 
4296fd43cf6eSHans Rosenfeld static void
iwn_abort_scan(void * arg)4297fd43cf6eSHans Rosenfeld iwn_abort_scan(void *arg)
4298fd43cf6eSHans Rosenfeld {
4299fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)arg;
4300fd43cf6eSHans Rosenfeld 	ieee80211com_t *ic = &sc->sc_ic;
4301fd43cf6eSHans Rosenfeld 
4302fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
43038d548c7aSHans Rosenfeld 	if ((sc->sc_flags & IWN_FLAG_SCANNING) == 0) {
43048d548c7aSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4305fd43cf6eSHans Rosenfeld 		return;
43068d548c7aSHans Rosenfeld 	}
4307fd43cf6eSHans Rosenfeld 
4308fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN,
4309fd43cf6eSHans Rosenfeld 	    "!aborting scan, flags = %x, state = %s",
4310fd43cf6eSHans Rosenfeld 	    sc->sc_flags, ieee80211_state_name[ic->ic_state]);
4311fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_SCANNING;
4312fd43cf6eSHans Rosenfeld 	iwn_hw_stop(sc, B_FALSE);
4313fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
4314fd43cf6eSHans Rosenfeld 
4315fd43cf6eSHans Rosenfeld 	sc->scan_to = 0;
4316fd43cf6eSHans Rosenfeld 	(void) iwn_init(sc);
4317fd43cf6eSHans Rosenfeld 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
4318fd43cf6eSHans Rosenfeld }
4319fd43cf6eSHans Rosenfeld 
4320fd43cf6eSHans Rosenfeld /*
4321fd43cf6eSHans Rosenfeld  * periodic function to deal with RF switch and HW error recovery
4322fd43cf6eSHans Rosenfeld  */
4323fd43cf6eSHans Rosenfeld static void
iwn_periodic(void * arg)4324fd43cf6eSHans Rosenfeld iwn_periodic(void *arg)
4325fd43cf6eSHans Rosenfeld {
4326fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = (struct iwn_softc *)arg;
4327fd43cf6eSHans Rosenfeld 	ieee80211com_t	*ic = &sc->sc_ic;
4328fd43cf6eSHans Rosenfeld 	int err;
4329fd43cf6eSHans Rosenfeld 	uint32_t tmp;
4330fd43cf6eSHans Rosenfeld 
4331fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
4332fd43cf6eSHans Rosenfeld 	tmp = IWN_READ(sc, IWN_GP_CNTRL);
4333fd43cf6eSHans Rosenfeld 	if (tmp & IWN_GP_CNTRL_RFKILL) {
4334fd43cf6eSHans Rosenfeld 		sc->sc_flags &= ~IWN_FLAG_RADIO_OFF;
4335fd43cf6eSHans Rosenfeld 	} else {
4336fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_RADIO_OFF;
4337fd43cf6eSHans Rosenfeld 	}
4338fd43cf6eSHans Rosenfeld 
4339fd43cf6eSHans Rosenfeld 	/*
4340fd43cf6eSHans Rosenfeld 	 * If the RF is OFF, do nothing.
4341fd43cf6eSHans Rosenfeld 	 */
4342fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_RADIO_OFF) {
4343fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4344fd43cf6eSHans Rosenfeld 		return;
4345fd43cf6eSHans Rosenfeld 	}
4346fd43cf6eSHans Rosenfeld 
4347fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
4348fd43cf6eSHans Rosenfeld 
4349fd43cf6eSHans Rosenfeld 	/*
4350fd43cf6eSHans Rosenfeld 	 * recovery fatal error
4351fd43cf6eSHans Rosenfeld 	 */
4352fd43cf6eSHans Rosenfeld 	if (ic->ic_mach &&
4353fd43cf6eSHans Rosenfeld 	    (sc->sc_flags & IWN_FLAG_HW_ERR_RECOVER)) {
4354fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
4355fd43cf6eSHans Rosenfeld 		    "!trying to restore previous state");
4356fd43cf6eSHans Rosenfeld 
4357fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
4358fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO;
4359fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4360fd43cf6eSHans Rosenfeld 
4361fd43cf6eSHans Rosenfeld 		if (sc->calib_to != 0)
4362fd43cf6eSHans Rosenfeld 			(void) untimeout(sc->calib_to);
4363fd43cf6eSHans Rosenfeld 		sc->calib_to = 0;
4364fd43cf6eSHans Rosenfeld 
4365fd43cf6eSHans Rosenfeld 		if (sc->scan_to != 0)
4366fd43cf6eSHans Rosenfeld 			(void) untimeout(sc->scan_to);
4367fd43cf6eSHans Rosenfeld 		sc->scan_to = 0;
4368fd43cf6eSHans Rosenfeld 
4369fd43cf6eSHans Rosenfeld 		iwn_hw_stop(sc, B_TRUE);
4370fd43cf6eSHans Rosenfeld 
4371fd43cf6eSHans Rosenfeld 		if (IWN_CHK_FAST_RECOVER(sc)) {
4372fd43cf6eSHans Rosenfeld 			/* save runtime configuration */
4373fd43cf6eSHans Rosenfeld 			bcopy(&sc->rxon, &sc->rxon_save, sizeof (sc->rxon));
4374fd43cf6eSHans Rosenfeld 		} else {
4375fd43cf6eSHans Rosenfeld 			ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
4376fd43cf6eSHans Rosenfeld 		}
4377fd43cf6eSHans Rosenfeld 
4378fd43cf6eSHans Rosenfeld 		err = iwn_init(sc);
4379fd43cf6eSHans Rosenfeld 		if (err != IWN_SUCCESS)
4380fd43cf6eSHans Rosenfeld 			return;
4381fd43cf6eSHans Rosenfeld 
4382fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
4383fd43cf6eSHans Rosenfeld 		sc->sc_flags |= IWN_FLAG_RUNNING;
4384fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
4385fd43cf6eSHans Rosenfeld 
4386fd43cf6eSHans Rosenfeld 		if (!IWN_CHK_FAST_RECOVER(sc) ||
4387fd43cf6eSHans Rosenfeld 		    iwn_fast_recover(sc) != IWN_SUCCESS) {
4388fd43cf6eSHans Rosenfeld 			mutex_enter(&sc->sc_mtx);
4389fd43cf6eSHans Rosenfeld 			sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER;
4390fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
4391fd43cf6eSHans Rosenfeld 			if (sc->sc_ostate != IEEE80211_S_INIT) {
4392fd43cf6eSHans Rosenfeld 				ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
4393fd43cf6eSHans Rosenfeld 			}
4394fd43cf6eSHans Rosenfeld 		}
4395fd43cf6eSHans Rosenfeld 	}
4396fd43cf6eSHans Rosenfeld }
4397fd43cf6eSHans Rosenfeld 
4398fd43cf6eSHans Rosenfeld /*
4399fd43cf6eSHans Rosenfeld  * Send a command to the firmware.
4400fd43cf6eSHans Rosenfeld  */
4401fd43cf6eSHans Rosenfeld static int
iwn_cmd(struct iwn_softc * sc,uint8_t code,void * buf,int size,int async)4402fd43cf6eSHans Rosenfeld iwn_cmd(struct iwn_softc *sc, uint8_t code, void *buf, int size, int async)
4403fd43cf6eSHans Rosenfeld {
4404fd43cf6eSHans Rosenfeld 	struct iwn_tx_ring *ring = &sc->txq[IWN_CMD_QUEUE_NUM];
4405fd43cf6eSHans Rosenfeld 	struct iwn_tx_desc *desc;
4406fd43cf6eSHans Rosenfeld 	struct iwn_tx_data *data;
4407fd43cf6eSHans Rosenfeld 	struct iwn_tx_cmd *cmd;
4408fd43cf6eSHans Rosenfeld 	clock_t clk;
4409fd43cf6eSHans Rosenfeld 	uintptr_t paddr;
4410fd43cf6eSHans Rosenfeld 	int totlen, ret;
4411fd43cf6eSHans Rosenfeld 
4412fd43cf6eSHans Rosenfeld 	ASSERT(mutex_owned(&sc->sc_mtx));
4413fd43cf6eSHans Rosenfeld 
4414fd43cf6eSHans Rosenfeld 	desc = &ring->desc[ring->cur];
4415fd43cf6eSHans Rosenfeld 	data = &ring->data[ring->cur];
4416fd43cf6eSHans Rosenfeld 	totlen = 4 + size;
4417fd43cf6eSHans Rosenfeld 
4418fd43cf6eSHans Rosenfeld 	if (size > sizeof (cmd->data)) {
4419fd43cf6eSHans Rosenfeld 		/* Command is too large to fit in a descriptor. */
4420fd43cf6eSHans Rosenfeld 		if (iwn_dma_contig_alloc(sc, &data->cmd_dma, totlen,
4421fd43cf6eSHans Rosenfeld 		    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&cmd,
4422fd43cf6eSHans Rosenfeld 		    &iwn_dma_accattr, 1) != DDI_SUCCESS)
4423fd43cf6eSHans Rosenfeld 			return ENOBUFS;
4424fd43cf6eSHans Rosenfeld 		paddr = data->cmd_dma.paddr;
4425fd43cf6eSHans Rosenfeld 	} else {
4426fd43cf6eSHans Rosenfeld 		cmd = &ring->cmd[ring->cur];
4427fd43cf6eSHans Rosenfeld 		paddr = data->cmd_paddr;
4428fd43cf6eSHans Rosenfeld 	}
4429fd43cf6eSHans Rosenfeld 
4430fd43cf6eSHans Rosenfeld 	cmd->code = code;
4431fd43cf6eSHans Rosenfeld 	cmd->flags = 0;
4432fd43cf6eSHans Rosenfeld 	cmd->qid = ring->qid;
4433fd43cf6eSHans Rosenfeld 	cmd->idx = ring->cur;
4434fd43cf6eSHans Rosenfeld 	bzero(cmd->data, size);
4435fd43cf6eSHans Rosenfeld 	memcpy(cmd->data, buf, size);
4436fd43cf6eSHans Rosenfeld 
4437fd43cf6eSHans Rosenfeld 	bzero(desc, sizeof(*desc));
4438fd43cf6eSHans Rosenfeld 	desc->nsegs = 1;
4439fd43cf6eSHans Rosenfeld 	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
4440fd43cf6eSHans Rosenfeld 	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
4441fd43cf6eSHans Rosenfeld 
4442fd43cf6eSHans Rosenfeld 	if (size > sizeof cmd->data) {
4443fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(data->cmd_dma.dma_hdl, 0, totlen,
4444fd43cf6eSHans Rosenfeld 		    DDI_DMA_SYNC_FORDEV);
4445fd43cf6eSHans Rosenfeld 	} else {
4446fd43cf6eSHans Rosenfeld 		(void) ddi_dma_sync(ring->cmd_dma.dma_hdl,
4447fd43cf6eSHans Rosenfeld 		    ring->cur * sizeof (*cmd),
4448fd43cf6eSHans Rosenfeld 		    totlen, DDI_DMA_SYNC_FORDEV);
4449fd43cf6eSHans Rosenfeld 	}
4450fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(ring->desc_dma.dma_hdl,
4451fd43cf6eSHans Rosenfeld 	    ring->cur * sizeof (*desc),
4452fd43cf6eSHans Rosenfeld 	    sizeof (*desc), DDI_DMA_SYNC_FORDEV);
4453fd43cf6eSHans Rosenfeld 
4454fd43cf6eSHans Rosenfeld 	/* Update TX scheduler. */
4455fd43cf6eSHans Rosenfeld 	sc->ops.update_sched(sc, ring->qid, ring->cur, 0, 0);
4456fd43cf6eSHans Rosenfeld 
4457fd43cf6eSHans Rosenfeld 	/* Kick command ring. */
4458fd43cf6eSHans Rosenfeld 	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4459fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4460fd43cf6eSHans Rosenfeld 
4461fd43cf6eSHans Rosenfeld 	if (async)
4462fd43cf6eSHans Rosenfeld 		return (IWN_SUCCESS);
4463fd43cf6eSHans Rosenfeld 
4464fd43cf6eSHans Rosenfeld 	sc->sc_cmd_flag = SC_CMD_FLG_NONE;
4465fd43cf6eSHans Rosenfeld 	clk = ddi_get_lbolt() + drv_usectohz(2000000);
4466fd43cf6eSHans Rosenfeld 	while (sc->sc_cmd_flag != SC_CMD_FLG_DONE)
4467fd43cf6eSHans Rosenfeld 		if (cv_timedwait(&sc->sc_cmd_cv, &sc->sc_mtx, clk) < 0)
4468fd43cf6eSHans Rosenfeld 			break;
4469fd43cf6eSHans Rosenfeld 
4470fd43cf6eSHans Rosenfeld 	ret = (sc->sc_cmd_flag == SC_CMD_FLG_DONE) ? IWN_SUCCESS : IWN_FAIL;
4471fd43cf6eSHans Rosenfeld 	sc->sc_cmd_flag = SC_CMD_FLG_NONE;
4472fd43cf6eSHans Rosenfeld 
4473fd43cf6eSHans Rosenfeld 	return (ret);
4474fd43cf6eSHans Rosenfeld }
4475fd43cf6eSHans Rosenfeld 
4476fd43cf6eSHans Rosenfeld static int
iwn4965_add_node(struct iwn_softc * sc,struct iwn_node_info * node,int async)4477fd43cf6eSHans Rosenfeld iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4478fd43cf6eSHans Rosenfeld {
4479fd43cf6eSHans Rosenfeld 	struct iwn4965_node_info hnode;
4480fd43cf6eSHans Rosenfeld 	char *src, *dst;
4481fd43cf6eSHans Rosenfeld 
4482fd43cf6eSHans Rosenfeld 	/*
4483fd43cf6eSHans Rosenfeld 	 * We use the node structure for 5000 Series internally (it is
4484fd43cf6eSHans Rosenfeld 	 * a superset of the one for 4965AGN). We thus copy the common
4485fd43cf6eSHans Rosenfeld 	 * fields before sending the command.
4486fd43cf6eSHans Rosenfeld 	 */
4487fd43cf6eSHans Rosenfeld 	src = (char *)node;
4488fd43cf6eSHans Rosenfeld 	dst = (char *)&hnode;
4489fd43cf6eSHans Rosenfeld 	memcpy(dst, src, 48);
4490fd43cf6eSHans Rosenfeld 	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
4491fd43cf6eSHans Rosenfeld 	memcpy(dst + 48, src + 72, 20);
4492fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
4493fd43cf6eSHans Rosenfeld }
4494fd43cf6eSHans Rosenfeld 
4495fd43cf6eSHans Rosenfeld static int
iwn5000_add_node(struct iwn_softc * sc,struct iwn_node_info * node,int async)4496fd43cf6eSHans Rosenfeld iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4497fd43cf6eSHans Rosenfeld {
4498fd43cf6eSHans Rosenfeld 	/* Direct mapping. */
4499fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
4500fd43cf6eSHans Rosenfeld }
4501fd43cf6eSHans Rosenfeld 
4502fd43cf6eSHans Rosenfeld static int
iwn_set_link_quality(struct iwn_softc * sc,struct ieee80211_node * ni)4503fd43cf6eSHans Rosenfeld iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
4504fd43cf6eSHans Rosenfeld {
4505fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
4506fd43cf6eSHans Rosenfeld 	struct ieee80211_rateset *rs = &ni->in_rates;
4507fd43cf6eSHans Rosenfeld 	struct iwn_cmd_link_quality linkq;
4508fd43cf6eSHans Rosenfeld 	const struct iwn_rate *rinfo;
4509fd43cf6eSHans Rosenfeld 	uint8_t txant;
4510fd43cf6eSHans Rosenfeld 	int i, txrate;
4511fd43cf6eSHans Rosenfeld 
4512fd43cf6eSHans Rosenfeld 	/* Use the first valid TX antenna. */
4513fd43cf6eSHans Rosenfeld 	txant = IWN_LSB(sc->txchainmask);
4514fd43cf6eSHans Rosenfeld 
4515fd43cf6eSHans Rosenfeld 	memset(&linkq, 0, sizeof linkq);
4516fd43cf6eSHans Rosenfeld 	linkq.id = wn->id;
4517fd43cf6eSHans Rosenfeld 	linkq.antmsk_1stream = txant;
4518fd43cf6eSHans Rosenfeld 	linkq.antmsk_2stream = IWN_ANT_AB;
4519fd43cf6eSHans Rosenfeld 	linkq.ampdu_max = 31;
4520fd43cf6eSHans Rosenfeld 	linkq.ampdu_threshold = 3;
4521fd43cf6eSHans Rosenfeld 	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4522fd43cf6eSHans Rosenfeld 
4523fd43cf6eSHans Rosenfeld 	/* Start at highest available bit-rate. */
4524fd43cf6eSHans Rosenfeld 	txrate = rs->ir_nrates - 1;
4525fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
4526fd43cf6eSHans Rosenfeld 		rinfo = &iwn_rates[wn->ridx[txrate]];
4527fd43cf6eSHans Rosenfeld 		linkq.retry[i].plcp = rinfo->plcp;
4528fd43cf6eSHans Rosenfeld 		linkq.retry[i].rflags = rinfo->flags;
4529fd43cf6eSHans Rosenfeld 		linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
4530fd43cf6eSHans Rosenfeld 		/* Next retry at immediate lower bit-rate. */
4531fd43cf6eSHans Rosenfeld 		if (txrate > 0)
4532fd43cf6eSHans Rosenfeld 			txrate--;
4533fd43cf6eSHans Rosenfeld 	}
4534fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
4535fd43cf6eSHans Rosenfeld }
4536fd43cf6eSHans Rosenfeld 
4537fd43cf6eSHans Rosenfeld /*
4538fd43cf6eSHans Rosenfeld  * Broadcast node is used to send group-addressed and management frames.
4539fd43cf6eSHans Rosenfeld  */
4540fd43cf6eSHans Rosenfeld static int
iwn_add_broadcast_node(struct iwn_softc * sc,int async)4541fd43cf6eSHans Rosenfeld iwn_add_broadcast_node(struct iwn_softc *sc, int async)
4542fd43cf6eSHans Rosenfeld {
4543fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
4544fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
4545fd43cf6eSHans Rosenfeld 	struct iwn_cmd_link_quality linkq;
4546fd43cf6eSHans Rosenfeld 	const struct iwn_rate *rinfo;
4547fd43cf6eSHans Rosenfeld 	uint8_t txant;
4548fd43cf6eSHans Rosenfeld 	int i, error;
4549fd43cf6eSHans Rosenfeld 
4550fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
4551fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr);
4552fd43cf6eSHans Rosenfeld 	node.id = sc->broadcast_id;
4553fd43cf6eSHans Rosenfeld 	DTRACE_PROBE(add__broadcast__node);
4554fd43cf6eSHans Rosenfeld 	if ((error = ops->add_node(sc, &node, async)) != 0)
4555fd43cf6eSHans Rosenfeld 		return error;
4556fd43cf6eSHans Rosenfeld 
4557fd43cf6eSHans Rosenfeld 	/* Use the first valid TX antenna. */
4558fd43cf6eSHans Rosenfeld 	txant = IWN_LSB(sc->txchainmask);
4559fd43cf6eSHans Rosenfeld 
4560fd43cf6eSHans Rosenfeld 	memset(&linkq, 0, sizeof linkq);
4561fd43cf6eSHans Rosenfeld 	linkq.id = sc->broadcast_id;
4562fd43cf6eSHans Rosenfeld 	linkq.antmsk_1stream = txant;
4563fd43cf6eSHans Rosenfeld 	linkq.antmsk_2stream = IWN_ANT_AB;
4564fd43cf6eSHans Rosenfeld 	linkq.ampdu_max = 64;
4565fd43cf6eSHans Rosenfeld 	linkq.ampdu_threshold = 3;
4566fd43cf6eSHans Rosenfeld 	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4567fd43cf6eSHans Rosenfeld 
4568fd43cf6eSHans Rosenfeld 	/* Use lowest mandatory bit-rate. */
4569fd43cf6eSHans Rosenfeld 	rinfo = (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) ?
4570fd43cf6eSHans Rosenfeld 	    &iwn_rates[IWN_RIDX_CCK1] : &iwn_rates[IWN_RIDX_OFDM6];
4571fd43cf6eSHans Rosenfeld 	linkq.retry[0].plcp = rinfo->plcp;
4572fd43cf6eSHans Rosenfeld 	linkq.retry[0].rflags = rinfo->flags;
4573fd43cf6eSHans Rosenfeld 	linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant);
4574fd43cf6eSHans Rosenfeld 	/* Use same bit-rate for all TX retries. */
4575fd43cf6eSHans Rosenfeld 	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
4576fd43cf6eSHans Rosenfeld 		linkq.retry[i].plcp = linkq.retry[0].plcp;
4577fd43cf6eSHans Rosenfeld 		linkq.retry[i].rflags = linkq.retry[0].rflags;
4578fd43cf6eSHans Rosenfeld 	}
4579fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
4580fd43cf6eSHans Rosenfeld }
4581fd43cf6eSHans Rosenfeld 
4582fd43cf6eSHans Rosenfeld static void
iwn_set_led(struct iwn_softc * sc,uint8_t which,uint8_t off,uint8_t on)4583fd43cf6eSHans Rosenfeld iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
4584fd43cf6eSHans Rosenfeld {
4585fd43cf6eSHans Rosenfeld 	struct iwn_cmd_led led;
4586fd43cf6eSHans Rosenfeld 
4587fd43cf6eSHans Rosenfeld 	/* Clear microcode LED ownership. */
4588fd43cf6eSHans Rosenfeld 	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
4589fd43cf6eSHans Rosenfeld 
4590fd43cf6eSHans Rosenfeld 	led.which = which;
4591fd43cf6eSHans Rosenfeld 	led.unit = htole32(10000);	/* on/off in unit of 100ms */
4592fd43cf6eSHans Rosenfeld 	led.off = off;
4593fd43cf6eSHans Rosenfeld 	led.on = on;
4594fd43cf6eSHans Rosenfeld 	DTRACE_PROBE1(led__change, const char *,
4595fd43cf6eSHans Rosenfeld 	    (off != 0 && on != 0) ? "blinking" :
4596fd43cf6eSHans Rosenfeld 	    (off != 0) ? "off" : "on");
4597fd43cf6eSHans Rosenfeld 	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
4598fd43cf6eSHans Rosenfeld }
4599fd43cf6eSHans Rosenfeld 
4600fd43cf6eSHans Rosenfeld /*
4601fd43cf6eSHans Rosenfeld  * Set the critical temperature at which the firmware will stop the radio
4602fd43cf6eSHans Rosenfeld  * and notify us.
4603fd43cf6eSHans Rosenfeld  */
4604fd43cf6eSHans Rosenfeld static int
iwn_set_critical_temp(struct iwn_softc * sc)4605fd43cf6eSHans Rosenfeld iwn_set_critical_temp(struct iwn_softc *sc)
4606fd43cf6eSHans Rosenfeld {
4607fd43cf6eSHans Rosenfeld 	struct iwn_critical_temp crit;
4608fd43cf6eSHans Rosenfeld 	int32_t temp;
4609fd43cf6eSHans Rosenfeld 
4610fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
4611fd43cf6eSHans Rosenfeld 
4612fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
4613fd43cf6eSHans Rosenfeld 		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
4614fd43cf6eSHans Rosenfeld 	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
4615fd43cf6eSHans Rosenfeld 		temp = IWN_CTOK(110);
4616fd43cf6eSHans Rosenfeld 	else
4617fd43cf6eSHans Rosenfeld 		temp = 110;
4618fd43cf6eSHans Rosenfeld 
4619fd43cf6eSHans Rosenfeld 	sc->sc_misc->crit_temp.value.ul = temp;
4620fd43cf6eSHans Rosenfeld 
4621fd43cf6eSHans Rosenfeld 	memset(&crit, 0, sizeof crit);
4622fd43cf6eSHans Rosenfeld 	crit.tempR = htole32(temp);
4623fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
4624fd43cf6eSHans Rosenfeld }
4625fd43cf6eSHans Rosenfeld 
4626fd43cf6eSHans Rosenfeld static int
iwn_set_timing(struct iwn_softc * sc,struct ieee80211_node * ni)4627fd43cf6eSHans Rosenfeld iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
4628fd43cf6eSHans Rosenfeld {
4629fd43cf6eSHans Rosenfeld 	struct iwn_cmd_timing cmd;
4630fd43cf6eSHans Rosenfeld 	uint64_t val, mod;
4631fd43cf6eSHans Rosenfeld 
4632fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
4633fd43cf6eSHans Rosenfeld 	memcpy(&cmd.tstamp, ni->in_tstamp.data, sizeof (uint64_t));
4634fd43cf6eSHans Rosenfeld 	cmd.bintval = htole16(ni->in_intval);
4635fd43cf6eSHans Rosenfeld 	cmd.lintval = htole16(10);
4636fd43cf6eSHans Rosenfeld 
4637fd43cf6eSHans Rosenfeld 	/* Compute remaining time until next beacon. */
4638fd43cf6eSHans Rosenfeld 	val = (uint64_t)ni->in_intval * 1024;	/* msecs -> usecs */
4639fd43cf6eSHans Rosenfeld 	mod = le64toh(cmd.tstamp) % val;
4640fd43cf6eSHans Rosenfeld 	cmd.binitval = htole32((uint32_t)(val - mod));
4641fd43cf6eSHans Rosenfeld 
4642fd43cf6eSHans Rosenfeld 	sc->sc_timing->bintval.value.ul = ni->in_intval;
4643fd43cf6eSHans Rosenfeld 	sc->sc_timing->tstamp.value.ul = ni->in_tstamp.tsf;
4644fd43cf6eSHans Rosenfeld 	sc->sc_timing->init.value.ul = (uint32_t)(val - mod);
4645fd43cf6eSHans Rosenfeld 
4646fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
4647fd43cf6eSHans Rosenfeld }
4648fd43cf6eSHans Rosenfeld 
4649fd43cf6eSHans Rosenfeld static void
iwn4965_power_calibration(struct iwn_softc * sc,int temp)4650fd43cf6eSHans Rosenfeld iwn4965_power_calibration(struct iwn_softc *sc, int temp)
4651fd43cf6eSHans Rosenfeld {
4652fd43cf6eSHans Rosenfeld 	/* Adjust TX power if need be (delta >= 3 degC). */
4653fd43cf6eSHans Rosenfeld 	IWN_DBG("temperature %d->%d", sc->temp, temp);
4654fd43cf6eSHans Rosenfeld 	if (abs(temp - sc->temp) >= 3) {
4655fd43cf6eSHans Rosenfeld 		/* Record temperature of last calibration. */
4656fd43cf6eSHans Rosenfeld 		sc->temp = temp;
4657fd43cf6eSHans Rosenfeld 		(void)iwn4965_set_txpower(sc, 1);
4658fd43cf6eSHans Rosenfeld 	}
4659fd43cf6eSHans Rosenfeld }
4660fd43cf6eSHans Rosenfeld 
4661fd43cf6eSHans Rosenfeld /*
4662fd43cf6eSHans Rosenfeld  * Set TX power for current channel (each rate has its own power settings).
4663fd43cf6eSHans Rosenfeld  * This function takes into account the regulatory information from EEPROM,
4664fd43cf6eSHans Rosenfeld  * the current temperature and the current voltage.
4665fd43cf6eSHans Rosenfeld  */
4666fd43cf6eSHans Rosenfeld static int
iwn4965_set_txpower(struct iwn_softc * sc,int async)4667fd43cf6eSHans Rosenfeld iwn4965_set_txpower(struct iwn_softc *sc, int async)
4668fd43cf6eSHans Rosenfeld {
4669fd43cf6eSHans Rosenfeld /* Fixed-point arithmetic division using a n-bit fractional part. */
4670fd43cf6eSHans Rosenfeld #define fdivround(a, b, n)	\
4671fd43cf6eSHans Rosenfeld 	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
4672fd43cf6eSHans Rosenfeld /* Linear interpolation. */
4673fd43cf6eSHans Rosenfeld #define interpolate(x, x1, y1, x2, y2, n)	\
4674fd43cf6eSHans Rosenfeld 	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
4675fd43cf6eSHans Rosenfeld 
4676fd43cf6eSHans Rosenfeld 	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
4677fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
4678fd43cf6eSHans Rosenfeld 	struct iwn_ucode_info *uc = &sc->ucode_info;
4679fd43cf6eSHans Rosenfeld 	struct ieee80211_channel *ch;
4680fd43cf6eSHans Rosenfeld 	struct iwn4965_cmd_txpower cmd;
4681fd43cf6eSHans Rosenfeld 	struct iwn4965_eeprom_chan_samples *chans;
4682fd43cf6eSHans Rosenfeld 	const uint8_t *rf_gain, *dsp_gain;
4683fd43cf6eSHans Rosenfeld 	int32_t vdiff, tdiff;
4684fd43cf6eSHans Rosenfeld 	int i, c, grp, maxpwr;
4685fd43cf6eSHans Rosenfeld 	uint8_t chan;
4686fd43cf6eSHans Rosenfeld 
4687fd43cf6eSHans Rosenfeld 	/* Retrieve current channel from last RXON. */
4688fd43cf6eSHans Rosenfeld 	chan = sc->rxon.chan;
4689fd43cf6eSHans Rosenfeld 	sc->sc_txpower->chan.value.l = chan;
4690fd43cf6eSHans Rosenfeld 	ch = &ic->ic_sup_channels[chan];
4691fd43cf6eSHans Rosenfeld 
4692fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
4693fd43cf6eSHans Rosenfeld 	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4694fd43cf6eSHans Rosenfeld 	cmd.chan = chan;
4695fd43cf6eSHans Rosenfeld 
4696fd43cf6eSHans Rosenfeld 	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4697fd43cf6eSHans Rosenfeld 		maxpwr   = sc->maxpwr5GHz;
4698fd43cf6eSHans Rosenfeld 		rf_gain  = iwn4965_rf_gain_5ghz;
4699fd43cf6eSHans Rosenfeld 		dsp_gain = iwn4965_dsp_gain_5ghz;
4700fd43cf6eSHans Rosenfeld 	} else {
4701fd43cf6eSHans Rosenfeld 		maxpwr   = sc->maxpwr2GHz;
4702fd43cf6eSHans Rosenfeld 		rf_gain  = iwn4965_rf_gain_2ghz;
4703fd43cf6eSHans Rosenfeld 		dsp_gain = iwn4965_dsp_gain_2ghz;
4704fd43cf6eSHans Rosenfeld 	}
4705fd43cf6eSHans Rosenfeld 
4706fd43cf6eSHans Rosenfeld 	/* Compute voltage compensation. */
4707fd43cf6eSHans Rosenfeld 	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4708fd43cf6eSHans Rosenfeld 	if (vdiff > 0)
4709fd43cf6eSHans Rosenfeld 		vdiff *= 2;
4710fd43cf6eSHans Rosenfeld 	if (abs(vdiff) > 2)
4711fd43cf6eSHans Rosenfeld 		vdiff = 0;
4712fd43cf6eSHans Rosenfeld 	sc->sc_txpower->vdiff.value.l = vdiff;
4713fd43cf6eSHans Rosenfeld 
4714fd43cf6eSHans Rosenfeld 	/* Get channel attenuation group. */
4715fd43cf6eSHans Rosenfeld 	if (chan <= 20)		/* 1-20 */
4716fd43cf6eSHans Rosenfeld 		grp = 4;
4717fd43cf6eSHans Rosenfeld 	else if (chan <= 43)	/* 34-43 */
4718fd43cf6eSHans Rosenfeld 		grp = 0;
4719fd43cf6eSHans Rosenfeld 	else if (chan <= 70)	/* 44-70 */
4720fd43cf6eSHans Rosenfeld 		grp = 1;
4721fd43cf6eSHans Rosenfeld 	else if (chan <= 124)	/* 71-124 */
4722fd43cf6eSHans Rosenfeld 		grp = 2;
4723fd43cf6eSHans Rosenfeld 	else			/* 125-200 */
4724fd43cf6eSHans Rosenfeld 		grp = 3;
4725fd43cf6eSHans Rosenfeld 	sc->sc_txpower->group.value.l = grp;
4726fd43cf6eSHans Rosenfeld 
4727fd43cf6eSHans Rosenfeld 	/* Get channel sub-band. */
4728fd43cf6eSHans Rosenfeld 	for (i = 0; i < IWN_NBANDS; i++)
4729fd43cf6eSHans Rosenfeld 		if (sc->bands[i].lo != 0 &&
4730fd43cf6eSHans Rosenfeld 		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4731fd43cf6eSHans Rosenfeld 			break;
4732fd43cf6eSHans Rosenfeld 	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4733fd43cf6eSHans Rosenfeld 		return EINVAL;
4734fd43cf6eSHans Rosenfeld 	chans = sc->bands[i].chans;
4735fd43cf6eSHans Rosenfeld 	sc->sc_txpower->subband.value.l = i;
4736fd43cf6eSHans Rosenfeld 
4737fd43cf6eSHans Rosenfeld 	for (c = 0; c < 2; c++) {
4738fd43cf6eSHans Rosenfeld 		uint8_t power, gain, temp;
4739fd43cf6eSHans Rosenfeld 		int maxchpwr, pwr, ridx, idx;
4740fd43cf6eSHans Rosenfeld 
4741fd43cf6eSHans Rosenfeld 		power = interpolate(chan,
4742fd43cf6eSHans Rosenfeld 		    chans[0].num, chans[0].samples[c][1].power,
4743fd43cf6eSHans Rosenfeld 		    chans[1].num, chans[1].samples[c][1].power, 1);
4744fd43cf6eSHans Rosenfeld 		gain  = interpolate(chan,
4745fd43cf6eSHans Rosenfeld 		    chans[0].num, chans[0].samples[c][1].gain,
4746fd43cf6eSHans Rosenfeld 		    chans[1].num, chans[1].samples[c][1].gain, 1);
4747fd43cf6eSHans Rosenfeld 		temp  = interpolate(chan,
4748fd43cf6eSHans Rosenfeld 		    chans[0].num, chans[0].samples[c][1].temp,
4749fd43cf6eSHans Rosenfeld 		    chans[1].num, chans[1].samples[c][1].temp, 1);
4750fd43cf6eSHans Rosenfeld 		sc->sc_txpower->txchain[c].power.value.l = power;
4751fd43cf6eSHans Rosenfeld 		sc->sc_txpower->txchain[c].gain.value.l = gain;
4752fd43cf6eSHans Rosenfeld 		sc->sc_txpower->txchain[c].temp.value.l = temp;
4753fd43cf6eSHans Rosenfeld 
4754fd43cf6eSHans Rosenfeld 		/* Compute temperature compensation. */
4755fd43cf6eSHans Rosenfeld 		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4756fd43cf6eSHans Rosenfeld 		sc->sc_txpower->txchain[c].tcomp.value.l = tdiff;
4757fd43cf6eSHans Rosenfeld 
4758fd43cf6eSHans Rosenfeld 		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4759fd43cf6eSHans Rosenfeld 			/* Convert dBm to half-dBm. */
4760fd43cf6eSHans Rosenfeld 			maxchpwr = sc->maxpwr[chan] * 2;
4761fd43cf6eSHans Rosenfeld 			if ((ridx / 8) & 1)
4762fd43cf6eSHans Rosenfeld 				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4763fd43cf6eSHans Rosenfeld 
4764fd43cf6eSHans Rosenfeld 			pwr = maxpwr;
4765fd43cf6eSHans Rosenfeld 
4766fd43cf6eSHans Rosenfeld 			/* Adjust TX power based on rate. */
4767fd43cf6eSHans Rosenfeld 			if ((ridx % 8) == 5)
4768fd43cf6eSHans Rosenfeld 				pwr -= 15;	/* OFDM48: -7.5dB */
4769fd43cf6eSHans Rosenfeld 			else if ((ridx % 8) == 6)
4770fd43cf6eSHans Rosenfeld 				pwr -= 17;	/* OFDM54: -8.5dB */
4771fd43cf6eSHans Rosenfeld 			else if ((ridx % 8) == 7)
4772fd43cf6eSHans Rosenfeld 				pwr -= 20;	/* OFDM60: -10dB */
4773fd43cf6eSHans Rosenfeld 			else
4774fd43cf6eSHans Rosenfeld 				pwr -= 10;	/* Others: -5dB */
4775fd43cf6eSHans Rosenfeld 
4776fd43cf6eSHans Rosenfeld 			/* Do not exceed channel max TX power. */
4777fd43cf6eSHans Rosenfeld 			if (pwr > maxchpwr)
4778fd43cf6eSHans Rosenfeld 				pwr = maxchpwr;
4779fd43cf6eSHans Rosenfeld 
4780fd43cf6eSHans Rosenfeld 			idx = gain - (pwr - power) - tdiff - vdiff;
4781fd43cf6eSHans Rosenfeld 			if ((ridx / 8) & 1)	/* MIMO */
4782fd43cf6eSHans Rosenfeld 				idx += (int32_t)le32toh(uc->atten[grp][c]);
4783fd43cf6eSHans Rosenfeld 
4784fd43cf6eSHans Rosenfeld 			if (cmd.band == 0)
4785fd43cf6eSHans Rosenfeld 				idx += 9;	/* 5GHz */
4786fd43cf6eSHans Rosenfeld 			if (ridx == IWN_RIDX_MAX)
4787fd43cf6eSHans Rosenfeld 				idx += 5;	/* CCK */
4788fd43cf6eSHans Rosenfeld 
4789fd43cf6eSHans Rosenfeld 			/* Make sure idx stays in a valid range. */
4790fd43cf6eSHans Rosenfeld 			if (idx < 0)
4791fd43cf6eSHans Rosenfeld 				idx = 0;
4792fd43cf6eSHans Rosenfeld 			else if (idx > IWN4965_MAX_PWR_INDEX)
4793fd43cf6eSHans Rosenfeld 				idx = IWN4965_MAX_PWR_INDEX;
4794fd43cf6eSHans Rosenfeld 
4795fd43cf6eSHans Rosenfeld 			sc->sc_txpower->txchain[c].rate[ridx].rf_gain.value.l =
4796fd43cf6eSHans Rosenfeld 			    cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4797fd43cf6eSHans Rosenfeld 			sc->sc_txpower->txchain[c].rate[ridx].dsp_gain.value.l =
4798fd43cf6eSHans Rosenfeld 			    cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4799fd43cf6eSHans Rosenfeld 		}
4800fd43cf6eSHans Rosenfeld 	}
4801fd43cf6eSHans Rosenfeld 
4802fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4803fd43cf6eSHans Rosenfeld 
4804fd43cf6eSHans Rosenfeld #undef interpolate
4805fd43cf6eSHans Rosenfeld #undef fdivround
4806fd43cf6eSHans Rosenfeld }
4807fd43cf6eSHans Rosenfeld 
4808fd43cf6eSHans Rosenfeld static int
iwn5000_set_txpower(struct iwn_softc * sc,int async)4809fd43cf6eSHans Rosenfeld iwn5000_set_txpower(struct iwn_softc *sc, int async)
4810fd43cf6eSHans Rosenfeld {
4811fd43cf6eSHans Rosenfeld 	struct iwn5000_cmd_txpower cmd;
4812fd43cf6eSHans Rosenfeld 
4813fd43cf6eSHans Rosenfeld 	/*
4814fd43cf6eSHans Rosenfeld 	 * TX power calibration is handled automatically by the firmware
4815fd43cf6eSHans Rosenfeld 	 * for 5000 Series.
4816fd43cf6eSHans Rosenfeld 	 */
4817fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
4818fd43cf6eSHans Rosenfeld 	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4819fd43cf6eSHans Rosenfeld 	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4820fd43cf6eSHans Rosenfeld 	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4821fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4822fd43cf6eSHans Rosenfeld }
4823fd43cf6eSHans Rosenfeld 
4824fd43cf6eSHans Rosenfeld /*
4825fd43cf6eSHans Rosenfeld  * Retrieve the maximum RSSI (in dBm) among receivers.
4826fd43cf6eSHans Rosenfeld  */
4827fd43cf6eSHans Rosenfeld static int
iwn4965_get_rssi(const struct iwn_rx_stat * stat)4828fd43cf6eSHans Rosenfeld iwn4965_get_rssi(const struct iwn_rx_stat *stat)
4829fd43cf6eSHans Rosenfeld {
4830fd43cf6eSHans Rosenfeld 	const struct iwn4965_rx_phystat *phy = (const void *)stat->phybuf;
4831fd43cf6eSHans Rosenfeld 	uint8_t mask, agc;
4832fd43cf6eSHans Rosenfeld 	int rssi;
4833fd43cf6eSHans Rosenfeld 
4834fd43cf6eSHans Rosenfeld 	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4835fd43cf6eSHans Rosenfeld 	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4836fd43cf6eSHans Rosenfeld 
4837fd43cf6eSHans Rosenfeld 	rssi = 0;
4838fd43cf6eSHans Rosenfeld 	if (mask & IWN_ANT_A)
4839fd43cf6eSHans Rosenfeld 		rssi = MAX(rssi, phy->rssi[0]);
4840fd43cf6eSHans Rosenfeld 	if (mask & IWN_ANT_B)
4841fd43cf6eSHans Rosenfeld 		rssi = MAX(rssi, phy->rssi[2]);
4842fd43cf6eSHans Rosenfeld 	if (mask & IWN_ANT_C)
4843fd43cf6eSHans Rosenfeld 		rssi = MAX(rssi, phy->rssi[4]);
4844fd43cf6eSHans Rosenfeld 
4845fd43cf6eSHans Rosenfeld 	return rssi - agc - IWN_RSSI_TO_DBM;
4846fd43cf6eSHans Rosenfeld }
4847fd43cf6eSHans Rosenfeld 
4848fd43cf6eSHans Rosenfeld static int
iwn5000_get_rssi(const struct iwn_rx_stat * stat)4849fd43cf6eSHans Rosenfeld iwn5000_get_rssi(const struct iwn_rx_stat *stat)
4850fd43cf6eSHans Rosenfeld {
4851fd43cf6eSHans Rosenfeld 	const struct iwn5000_rx_phystat *phy = (const void *)stat->phybuf;
4852fd43cf6eSHans Rosenfeld 	uint8_t agc;
4853fd43cf6eSHans Rosenfeld 	int rssi;
4854fd43cf6eSHans Rosenfeld 
4855fd43cf6eSHans Rosenfeld 	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4856fd43cf6eSHans Rosenfeld 
4857fd43cf6eSHans Rosenfeld 	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4858fd43cf6eSHans Rosenfeld 		   le16toh(phy->rssi[1]) & 0xff);
4859fd43cf6eSHans Rosenfeld 	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4860fd43cf6eSHans Rosenfeld 
4861fd43cf6eSHans Rosenfeld 	return rssi - agc - IWN_RSSI_TO_DBM;
4862fd43cf6eSHans Rosenfeld }
4863fd43cf6eSHans Rosenfeld 
4864fd43cf6eSHans Rosenfeld /*
4865fd43cf6eSHans Rosenfeld  * Retrieve the average noise (in dBm) among receivers.
4866fd43cf6eSHans Rosenfeld  */
4867fd43cf6eSHans Rosenfeld static int
iwn_get_noise(const struct iwn_rx_general_stats * stats)4868fd43cf6eSHans Rosenfeld iwn_get_noise(const struct iwn_rx_general_stats *stats)
4869fd43cf6eSHans Rosenfeld {
4870fd43cf6eSHans Rosenfeld 	int i, total, nbant, noise;
4871fd43cf6eSHans Rosenfeld 
4872fd43cf6eSHans Rosenfeld 	total = nbant = 0;
4873fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++) {
4874fd43cf6eSHans Rosenfeld 		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4875fd43cf6eSHans Rosenfeld 			continue;
4876fd43cf6eSHans Rosenfeld 		total += noise;
4877fd43cf6eSHans Rosenfeld 		nbant++;
4878fd43cf6eSHans Rosenfeld 	}
4879fd43cf6eSHans Rosenfeld 	/* There should be at least one antenna but check anyway. */
4880fd43cf6eSHans Rosenfeld 	return (nbant == 0) ? -127 : (total / nbant) - 107;
4881fd43cf6eSHans Rosenfeld }
4882fd43cf6eSHans Rosenfeld 
4883fd43cf6eSHans Rosenfeld /*
4884fd43cf6eSHans Rosenfeld  * Compute temperature (in degC) from last received statistics.
4885fd43cf6eSHans Rosenfeld  */
4886fd43cf6eSHans Rosenfeld static int
iwn4965_get_temperature(struct iwn_softc * sc)4887fd43cf6eSHans Rosenfeld iwn4965_get_temperature(struct iwn_softc *sc)
4888fd43cf6eSHans Rosenfeld {
4889fd43cf6eSHans Rosenfeld 	struct iwn_ucode_info *uc = &sc->ucode_info;
4890fd43cf6eSHans Rosenfeld 	int32_t r1, r2, r3, r4, temp;
4891fd43cf6eSHans Rosenfeld 
4892fd43cf6eSHans Rosenfeld 	r1 = le32toh(uc->temp[0].chan20MHz);
4893fd43cf6eSHans Rosenfeld 	r2 = le32toh(uc->temp[1].chan20MHz);
4894fd43cf6eSHans Rosenfeld 	r3 = le32toh(uc->temp[2].chan20MHz);
4895fd43cf6eSHans Rosenfeld 	r4 = le32toh(sc->rawtemp);
4896fd43cf6eSHans Rosenfeld 
4897fd43cf6eSHans Rosenfeld 	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4898fd43cf6eSHans Rosenfeld 		return 0;
4899fd43cf6eSHans Rosenfeld 
4900fd43cf6eSHans Rosenfeld 	/* Sign-extend 23-bit R4 value to 32-bit. */
4901fd43cf6eSHans Rosenfeld 	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4902fd43cf6eSHans Rosenfeld 	/* Compute temperature in Kelvin. */
4903fd43cf6eSHans Rosenfeld 	temp = (259 * (r4 - r2)) / (r3 - r1);
4904fd43cf6eSHans Rosenfeld 	temp = (temp * 97) / 100 + 8;
4905fd43cf6eSHans Rosenfeld 
4906fd43cf6eSHans Rosenfeld 	return IWN_KTOC(temp);
4907fd43cf6eSHans Rosenfeld }
4908fd43cf6eSHans Rosenfeld 
4909fd43cf6eSHans Rosenfeld static int
iwn5000_get_temperature(struct iwn_softc * sc)4910fd43cf6eSHans Rosenfeld iwn5000_get_temperature(struct iwn_softc *sc)
4911fd43cf6eSHans Rosenfeld {
4912fd43cf6eSHans Rosenfeld 	int32_t temp;
4913fd43cf6eSHans Rosenfeld 
4914fd43cf6eSHans Rosenfeld 	/*
4915fd43cf6eSHans Rosenfeld 	 * Temperature is not used by the driver for 5000 Series because
4916fd43cf6eSHans Rosenfeld 	 * TX power calibration is handled by firmware.  We export it to
4917fd43cf6eSHans Rosenfeld 	 * users through a kstat though.
4918fd43cf6eSHans Rosenfeld 	 */
4919fd43cf6eSHans Rosenfeld 	temp = le32toh(sc->rawtemp);
4920fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4921fd43cf6eSHans Rosenfeld 		temp = (temp / -5) + sc->temp_off;
4922fd43cf6eSHans Rosenfeld 		temp = IWN_KTOC(temp);
4923fd43cf6eSHans Rosenfeld 	}
4924fd43cf6eSHans Rosenfeld 	return temp;
4925fd43cf6eSHans Rosenfeld }
4926fd43cf6eSHans Rosenfeld 
4927fd43cf6eSHans Rosenfeld /*
4928fd43cf6eSHans Rosenfeld  * Initialize sensitivity calibration state machine.
4929fd43cf6eSHans Rosenfeld  */
4930fd43cf6eSHans Rosenfeld static int
iwn_init_sensitivity(struct iwn_softc * sc)4931fd43cf6eSHans Rosenfeld iwn_init_sensitivity(struct iwn_softc *sc)
4932fd43cf6eSHans Rosenfeld {
4933fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
4934fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
4935fd43cf6eSHans Rosenfeld 	uint32_t flags;
4936fd43cf6eSHans Rosenfeld 	int error;
4937fd43cf6eSHans Rosenfeld 
4938fd43cf6eSHans Rosenfeld 	/* Reset calibration state machine. */
4939fd43cf6eSHans Rosenfeld 	memset(calib, 0, sizeof (*calib));
4940fd43cf6eSHans Rosenfeld 	calib->state = IWN_CALIB_STATE_INIT;
4941fd43cf6eSHans Rosenfeld 	calib->cck_state = IWN_CCK_STATE_HIFA;
4942fd43cf6eSHans Rosenfeld 	/* Set initial correlation values. */
4943fd43cf6eSHans Rosenfeld 	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4944fd43cf6eSHans Rosenfeld 	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4945fd43cf6eSHans Rosenfeld 	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4946fd43cf6eSHans Rosenfeld 	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4947fd43cf6eSHans Rosenfeld 	calib->cck_x4      = 125;
4948fd43cf6eSHans Rosenfeld 	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4949fd43cf6eSHans Rosenfeld 	calib->energy_cck  = sc->limits->energy_cck;
4950fd43cf6eSHans Rosenfeld 
4951fd43cf6eSHans Rosenfeld 	/* Write initial sensitivity. */
4952fd43cf6eSHans Rosenfeld 	if ((error = iwn_send_sensitivity(sc)) != 0)
4953fd43cf6eSHans Rosenfeld 		return error;
4954fd43cf6eSHans Rosenfeld 
4955fd43cf6eSHans Rosenfeld 	/* Write initial gains. */
4956fd43cf6eSHans Rosenfeld 	if ((error = ops->init_gains(sc)) != 0)
4957fd43cf6eSHans Rosenfeld 		return error;
4958fd43cf6eSHans Rosenfeld 
4959fd43cf6eSHans Rosenfeld 	/* Request statistics at each beacon interval. */
4960fd43cf6eSHans Rosenfeld 	flags = 0;
4961fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4962fd43cf6eSHans Rosenfeld }
4963fd43cf6eSHans Rosenfeld 
4964fd43cf6eSHans Rosenfeld /*
4965fd43cf6eSHans Rosenfeld  * Collect noise and RSSI statistics for the first 20 beacons received
4966fd43cf6eSHans Rosenfeld  * after association and use them to determine connected antennas and
4967fd43cf6eSHans Rosenfeld  * to set differential gains.
4968fd43cf6eSHans Rosenfeld  */
4969fd43cf6eSHans Rosenfeld static void
iwn_collect_noise(struct iwn_softc * sc,const struct iwn_rx_general_stats * stats)4970fd43cf6eSHans Rosenfeld iwn_collect_noise(struct iwn_softc *sc,
4971fd43cf6eSHans Rosenfeld     const struct iwn_rx_general_stats *stats)
4972fd43cf6eSHans Rosenfeld {
4973fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
4974fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
4975fd43cf6eSHans Rosenfeld 	uint32_t val;
4976fd43cf6eSHans Rosenfeld 	int i;
4977fd43cf6eSHans Rosenfeld 
4978fd43cf6eSHans Rosenfeld 	/* Accumulate RSSI and noise for all 3 antennas. */
4979fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++) {
4980fd43cf6eSHans Rosenfeld 		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4981fd43cf6eSHans Rosenfeld 		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4982fd43cf6eSHans Rosenfeld 	}
4983fd43cf6eSHans Rosenfeld 	/* NB: We update differential gains only once after 20 beacons. */
4984fd43cf6eSHans Rosenfeld 	if (++calib->nbeacons < 20)
4985fd43cf6eSHans Rosenfeld 		return;
4986fd43cf6eSHans Rosenfeld 
4987fd43cf6eSHans Rosenfeld 	/* Determine highest average RSSI. */
4988fd43cf6eSHans Rosenfeld 	val = MAX(calib->rssi[0], calib->rssi[1]);
4989fd43cf6eSHans Rosenfeld 	val = MAX(calib->rssi[2], val);
4990fd43cf6eSHans Rosenfeld 
4991fd43cf6eSHans Rosenfeld 	/* Determine which antennas are connected. */
4992fd43cf6eSHans Rosenfeld 	sc->chainmask = sc->rxchainmask;
4993fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++)
4994fd43cf6eSHans Rosenfeld 		if (val - calib->rssi[i] > 15 * 20)
4995fd43cf6eSHans Rosenfeld 			sc->chainmask &= ~(1 << i);
4996fd43cf6eSHans Rosenfeld 
4997fd43cf6eSHans Rosenfeld 	sc->sc_ant->conn_ant.value.ul = sc->chainmask;
4998fd43cf6eSHans Rosenfeld 
4999fd43cf6eSHans Rosenfeld 	/* If none of the TX antennas are connected, keep at least one. */
5000fd43cf6eSHans Rosenfeld 	if ((sc->chainmask & sc->txchainmask) == 0)
5001fd43cf6eSHans Rosenfeld 		sc->chainmask |= IWN_LSB(sc->txchainmask);
5002fd43cf6eSHans Rosenfeld 
5003fd43cf6eSHans Rosenfeld 	(void)ops->set_gains(sc);
5004fd43cf6eSHans Rosenfeld 	calib->state = IWN_CALIB_STATE_RUN;
5005fd43cf6eSHans Rosenfeld 
5006fd43cf6eSHans Rosenfeld #ifdef notyet
5007fd43cf6eSHans Rosenfeld 	/* XXX Disable RX chains with no antennas connected. */
5008fd43cf6eSHans Rosenfeld 	sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
5009fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz);
5010fd43cf6eSHans Rosenfeld 	(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5011fd43cf6eSHans Rosenfeld #endif
5012fd43cf6eSHans Rosenfeld 
5013fd43cf6eSHans Rosenfeld 	/* Enable power-saving mode if requested by user. */
5014fd43cf6eSHans Rosenfeld 	if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
5015fd43cf6eSHans Rosenfeld 		(void)iwn_set_pslevel(sc, 0, 3, 1);
5016fd43cf6eSHans Rosenfeld }
5017fd43cf6eSHans Rosenfeld 
5018fd43cf6eSHans Rosenfeld static int
iwn4965_init_gains(struct iwn_softc * sc)5019fd43cf6eSHans Rosenfeld iwn4965_init_gains(struct iwn_softc *sc)
5020fd43cf6eSHans Rosenfeld {
5021fd43cf6eSHans Rosenfeld 	struct iwn_phy_calib_gain cmd;
5022fd43cf6eSHans Rosenfeld 
5023fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5024fd43cf6eSHans Rosenfeld 	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
5025fd43cf6eSHans Rosenfeld 	/* Differential gains initially set to 0 for all 3 antennas. */
5026fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5027fd43cf6eSHans Rosenfeld }
5028fd43cf6eSHans Rosenfeld 
5029fd43cf6eSHans Rosenfeld static int
iwn5000_init_gains(struct iwn_softc * sc)5030fd43cf6eSHans Rosenfeld iwn5000_init_gains(struct iwn_softc *sc)
5031fd43cf6eSHans Rosenfeld {
5032fd43cf6eSHans Rosenfeld 	struct iwn_phy_calib cmd;
5033fd43cf6eSHans Rosenfeld 
5034fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5035fd43cf6eSHans Rosenfeld 	cmd.code = sc->reset_noise_gain;
5036fd43cf6eSHans Rosenfeld 	cmd.ngroups = 1;
5037fd43cf6eSHans Rosenfeld 	cmd.isvalid = 1;
5038fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5039fd43cf6eSHans Rosenfeld }
5040fd43cf6eSHans Rosenfeld 
5041fd43cf6eSHans Rosenfeld static int
iwn4965_set_gains(struct iwn_softc * sc)5042fd43cf6eSHans Rosenfeld iwn4965_set_gains(struct iwn_softc *sc)
5043fd43cf6eSHans Rosenfeld {
5044fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
5045fd43cf6eSHans Rosenfeld 	struct iwn_phy_calib_gain cmd;
5046fd43cf6eSHans Rosenfeld 	int i, delta, noise;
5047fd43cf6eSHans Rosenfeld 
5048fd43cf6eSHans Rosenfeld 	/* Get minimal noise among connected antennas. */
5049fd43cf6eSHans Rosenfeld 	noise = INT_MAX;	/* NB: There's at least one antenna. */
5050fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++)
5051fd43cf6eSHans Rosenfeld 		if (sc->chainmask & (1 << i))
5052fd43cf6eSHans Rosenfeld 			noise = MIN(calib->noise[i], noise);
5053fd43cf6eSHans Rosenfeld 
5054fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5055fd43cf6eSHans Rosenfeld 	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
5056fd43cf6eSHans Rosenfeld 	/* Set differential gains for connected antennas. */
5057fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++) {
5058fd43cf6eSHans Rosenfeld 		if (sc->chainmask & (1 << i)) {
5059fd43cf6eSHans Rosenfeld 			/* Compute attenuation (in unit of 1.5dB). */
5060fd43cf6eSHans Rosenfeld 			delta = (noise - calib->noise[i]) / 30;
5061fd43cf6eSHans Rosenfeld 			/* NB: delta <= 0 */
5062fd43cf6eSHans Rosenfeld 			/* Limit to [-4.5dB,0]. */
5063fd43cf6eSHans Rosenfeld 			cmd.gain[i] = (uint8_t)MIN(abs(delta), 3);
5064fd43cf6eSHans Rosenfeld 			if (delta < 0)
5065fd43cf6eSHans Rosenfeld 				cmd.gain[i] |= 1 << 2;	/* sign bit */
5066fd43cf6eSHans Rosenfeld 			sc->sc_ant->gain[i].value.ul = cmd.gain[i];
5067fd43cf6eSHans Rosenfeld 		}
5068fd43cf6eSHans Rosenfeld 	}
5069fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5070fd43cf6eSHans Rosenfeld }
5071fd43cf6eSHans Rosenfeld 
5072fd43cf6eSHans Rosenfeld static int
iwn5000_set_gains(struct iwn_softc * sc)5073fd43cf6eSHans Rosenfeld iwn5000_set_gains(struct iwn_softc *sc)
5074fd43cf6eSHans Rosenfeld {
5075fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
5076fd43cf6eSHans Rosenfeld 	struct iwn_phy_calib_gain cmd;
5077fd43cf6eSHans Rosenfeld 	int i, ant, div, delta;
5078fd43cf6eSHans Rosenfeld 
5079fd43cf6eSHans Rosenfeld 	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
5080fd43cf6eSHans Rosenfeld 	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
5081fd43cf6eSHans Rosenfeld 
5082fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5083fd43cf6eSHans Rosenfeld 	cmd.code = sc->noise_gain;
5084fd43cf6eSHans Rosenfeld 	cmd.ngroups = 1;
5085fd43cf6eSHans Rosenfeld 	cmd.isvalid = 1;
5086fd43cf6eSHans Rosenfeld 	/* Get first available RX antenna as referential. */
5087fd43cf6eSHans Rosenfeld 	ant = IWN_LSB(sc->rxchainmask);
5088fd43cf6eSHans Rosenfeld 	/* Set differential gains for other antennas. */
5089fd43cf6eSHans Rosenfeld 	for (i = ant + 1; i < 3; i++) {
5090fd43cf6eSHans Rosenfeld 		if (sc->chainmask & (1 << i)) {
5091fd43cf6eSHans Rosenfeld 			/* The delta is relative to antenna "ant". */
5092fd43cf6eSHans Rosenfeld 			delta = (calib->noise[ant] - calib->noise[i]) / div;
5093fd43cf6eSHans Rosenfeld 			/* Limit to [-4.5dB,+4.5dB]. */
5094fd43cf6eSHans Rosenfeld 			cmd.gain[i - 1] = (uint8_t)MIN(abs(delta), 3);
5095fd43cf6eSHans Rosenfeld 			if (delta < 0)
5096fd43cf6eSHans Rosenfeld 				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
5097fd43cf6eSHans Rosenfeld 			sc->sc_ant->gain[i - 1].value.ul
5098fd43cf6eSHans Rosenfeld 			    = cmd.gain[i - 1];
5099fd43cf6eSHans Rosenfeld 		}
5100fd43cf6eSHans Rosenfeld 	}
5101fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5102fd43cf6eSHans Rosenfeld }
5103fd43cf6eSHans Rosenfeld 
5104fd43cf6eSHans Rosenfeld /*
5105fd43cf6eSHans Rosenfeld  * Tune RF RX sensitivity based on the number of false alarms detected
5106fd43cf6eSHans Rosenfeld  * during the last beacon period.
5107fd43cf6eSHans Rosenfeld  */
5108fd43cf6eSHans Rosenfeld static void
iwn_tune_sensitivity(struct iwn_softc * sc,const struct iwn_rx_stats * stats)5109fd43cf6eSHans Rosenfeld iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
5110fd43cf6eSHans Rosenfeld {
5111fd43cf6eSHans Rosenfeld #define inc(val, inc, max)			\
5112fd43cf6eSHans Rosenfeld 	if ((val) < (max)) {			\
5113fd43cf6eSHans Rosenfeld 		if ((val) < (max) - (inc))	\
5114fd43cf6eSHans Rosenfeld 			(val) += (inc);		\
5115fd43cf6eSHans Rosenfeld 		else				\
5116fd43cf6eSHans Rosenfeld 			(val) = (max);		\
5117fd43cf6eSHans Rosenfeld 		needs_update = 1;		\
5118fd43cf6eSHans Rosenfeld 	}
5119fd43cf6eSHans Rosenfeld #define dec(val, dec, min)			\
5120fd43cf6eSHans Rosenfeld 	if ((val) > (min)) {			\
5121fd43cf6eSHans Rosenfeld 		if ((val) > (min) + (dec))	\
5122fd43cf6eSHans Rosenfeld 			(val) -= (dec);		\
5123fd43cf6eSHans Rosenfeld 		else				\
5124fd43cf6eSHans Rosenfeld 			(val) = (min);		\
5125fd43cf6eSHans Rosenfeld 		needs_update = 1;		\
5126fd43cf6eSHans Rosenfeld 	}
5127fd43cf6eSHans Rosenfeld 
5128fd43cf6eSHans Rosenfeld 	const struct iwn_sensitivity_limits *limits = sc->limits;
5129fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
5130fd43cf6eSHans Rosenfeld 	uint32_t val, rxena, fa;
5131fd43cf6eSHans Rosenfeld 	uint32_t energy[3], energy_min;
5132fd43cf6eSHans Rosenfeld 	uint8_t noise[3], noise_ref;
5133fd43cf6eSHans Rosenfeld 	int i, needs_update = 0;
5134fd43cf6eSHans Rosenfeld 
5135fd43cf6eSHans Rosenfeld 	/* Check that we've been enabled long enough. */
5136fd43cf6eSHans Rosenfeld 	if ((rxena = le32toh(stats->general.load)) == 0)
5137fd43cf6eSHans Rosenfeld 		return;
5138fd43cf6eSHans Rosenfeld 
5139fd43cf6eSHans Rosenfeld 	/* Compute number of false alarms since last call for OFDM. */
5140fd43cf6eSHans Rosenfeld 	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
5141fd43cf6eSHans Rosenfeld 	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
5142fd43cf6eSHans Rosenfeld 	fa *= 200 * 1024;	/* 200TU */
5143fd43cf6eSHans Rosenfeld 
5144fd43cf6eSHans Rosenfeld 	/* Save counters values for next call. */
5145fd43cf6eSHans Rosenfeld 	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
5146fd43cf6eSHans Rosenfeld 	calib->fa_ofdm = le32toh(stats->ofdm.fa);
5147fd43cf6eSHans Rosenfeld 
5148fd43cf6eSHans Rosenfeld 	if (fa > 50 * rxena) {
5149fd43cf6eSHans Rosenfeld 		/* High false alarm count, decrease sensitivity. */
5150fd43cf6eSHans Rosenfeld 		IWN_DBG("OFDM high false alarm count: %u", fa);
5151fd43cf6eSHans Rosenfeld 		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
5152fd43cf6eSHans Rosenfeld 		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
5153fd43cf6eSHans Rosenfeld 		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
5154fd43cf6eSHans Rosenfeld 		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
5155fd43cf6eSHans Rosenfeld 
5156fd43cf6eSHans Rosenfeld 	} else if (fa < 5 * rxena) {
5157fd43cf6eSHans Rosenfeld 		/* Low false alarm count, increase sensitivity. */
5158fd43cf6eSHans Rosenfeld 		IWN_DBG("OFDM low false alarm count: %u", fa);
5159fd43cf6eSHans Rosenfeld 		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
5160fd43cf6eSHans Rosenfeld 		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
5161fd43cf6eSHans Rosenfeld 		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
5162fd43cf6eSHans Rosenfeld 		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
5163fd43cf6eSHans Rosenfeld 	}
5164fd43cf6eSHans Rosenfeld 
5165fd43cf6eSHans Rosenfeld 	/* Compute maximum noise among 3 receivers. */
5166fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++)
5167fd43cf6eSHans Rosenfeld 		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
5168fd43cf6eSHans Rosenfeld 	val = MAX(noise[0], noise[1]);
5169fd43cf6eSHans Rosenfeld 	val = MAX(noise[2], val);
5170fd43cf6eSHans Rosenfeld 	/* Insert it into our samples table. */
5171fd43cf6eSHans Rosenfeld 	calib->noise_samples[calib->cur_noise_sample] = (uint8_t)val;
5172fd43cf6eSHans Rosenfeld 	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
5173fd43cf6eSHans Rosenfeld 
5174fd43cf6eSHans Rosenfeld 	/* Compute maximum noise among last 20 samples. */
5175fd43cf6eSHans Rosenfeld 	noise_ref = calib->noise_samples[0];
5176fd43cf6eSHans Rosenfeld 	for (i = 1; i < 20; i++)
5177fd43cf6eSHans Rosenfeld 		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
5178fd43cf6eSHans Rosenfeld 
5179fd43cf6eSHans Rosenfeld 	/* Compute maximum energy among 3 receivers. */
5180fd43cf6eSHans Rosenfeld 	for (i = 0; i < 3; i++)
5181fd43cf6eSHans Rosenfeld 		energy[i] = le32toh(stats->general.energy[i]);
5182fd43cf6eSHans Rosenfeld 	val = MIN(energy[0], energy[1]);
5183fd43cf6eSHans Rosenfeld 	val = MIN(energy[2], val);
5184fd43cf6eSHans Rosenfeld 	/* Insert it into our samples table. */
5185fd43cf6eSHans Rosenfeld 	calib->energy_samples[calib->cur_energy_sample] = val;
5186fd43cf6eSHans Rosenfeld 	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
5187fd43cf6eSHans Rosenfeld 
5188fd43cf6eSHans Rosenfeld 	/* Compute minimum energy among last 10 samples. */
5189fd43cf6eSHans Rosenfeld 	energy_min = calib->energy_samples[0];
5190fd43cf6eSHans Rosenfeld 	for (i = 1; i < 10; i++)
5191fd43cf6eSHans Rosenfeld 		energy_min = MAX(energy_min, calib->energy_samples[i]);
5192fd43cf6eSHans Rosenfeld 	energy_min += 6;
5193fd43cf6eSHans Rosenfeld 
5194fd43cf6eSHans Rosenfeld 	/* Compute number of false alarms since last call for CCK. */
5195fd43cf6eSHans Rosenfeld 	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
5196fd43cf6eSHans Rosenfeld 	fa += le32toh(stats->cck.fa) - calib->fa_cck;
5197fd43cf6eSHans Rosenfeld 	fa *= 200 * 1024;	/* 200TU */
5198fd43cf6eSHans Rosenfeld 
5199fd43cf6eSHans Rosenfeld 	/* Save counters values for next call. */
5200fd43cf6eSHans Rosenfeld 	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
5201fd43cf6eSHans Rosenfeld 	calib->fa_cck = le32toh(stats->cck.fa);
5202fd43cf6eSHans Rosenfeld 
5203fd43cf6eSHans Rosenfeld 	if (fa > 50 * rxena) {
5204fd43cf6eSHans Rosenfeld 		/* High false alarm count, decrease sensitivity. */
5205fd43cf6eSHans Rosenfeld 		IWN_DBG("CCK high false alarm count: %u", fa);
5206fd43cf6eSHans Rosenfeld 		calib->cck_state = IWN_CCK_STATE_HIFA;
5207fd43cf6eSHans Rosenfeld 		calib->low_fa = 0;
5208fd43cf6eSHans Rosenfeld 
5209fd43cf6eSHans Rosenfeld 		if (calib->cck_x4 > 160) {
5210fd43cf6eSHans Rosenfeld 			calib->noise_ref = noise_ref;
5211fd43cf6eSHans Rosenfeld 			if (calib->energy_cck > 2)
5212fd43cf6eSHans Rosenfeld 				dec(calib->energy_cck, 2, energy_min);
5213fd43cf6eSHans Rosenfeld 		}
5214fd43cf6eSHans Rosenfeld 		if (calib->cck_x4 < 160) {
5215fd43cf6eSHans Rosenfeld 			calib->cck_x4 = 161;
5216fd43cf6eSHans Rosenfeld 			needs_update = 1;
5217fd43cf6eSHans Rosenfeld 		} else
5218fd43cf6eSHans Rosenfeld 			inc(calib->cck_x4, 3, limits->max_cck_x4);
5219fd43cf6eSHans Rosenfeld 
5220fd43cf6eSHans Rosenfeld 		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
5221fd43cf6eSHans Rosenfeld 
5222fd43cf6eSHans Rosenfeld 	} else if (fa < 5 * rxena) {
5223fd43cf6eSHans Rosenfeld 		/* Low false alarm count, increase sensitivity. */
5224fd43cf6eSHans Rosenfeld 		IWN_DBG("CCK low false alarm count: %u", fa);
5225fd43cf6eSHans Rosenfeld 		calib->cck_state = IWN_CCK_STATE_LOFA;
5226fd43cf6eSHans Rosenfeld 		calib->low_fa++;
5227fd43cf6eSHans Rosenfeld 
5228fd43cf6eSHans Rosenfeld 		if (calib->cck_state != IWN_CCK_STATE_INIT &&
5229fd43cf6eSHans Rosenfeld 		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
5230fd43cf6eSHans Rosenfeld 		     calib->low_fa > 100)) {
5231fd43cf6eSHans Rosenfeld 			inc(calib->energy_cck, 2, limits->min_energy_cck);
5232fd43cf6eSHans Rosenfeld 			dec(calib->cck_x4,     3, limits->min_cck_x4);
5233fd43cf6eSHans Rosenfeld 			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
5234fd43cf6eSHans Rosenfeld 		}
5235fd43cf6eSHans Rosenfeld 	} else {
5236fd43cf6eSHans Rosenfeld 		/* Not worth to increase or decrease sensitivity. */
5237fd43cf6eSHans Rosenfeld 		IWN_DBG("CCK normal false alarm count: %u", fa);
5238fd43cf6eSHans Rosenfeld 		calib->low_fa = 0;
5239fd43cf6eSHans Rosenfeld 		calib->noise_ref = noise_ref;
5240fd43cf6eSHans Rosenfeld 
5241fd43cf6eSHans Rosenfeld 		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
5242fd43cf6eSHans Rosenfeld 			/* Previous interval had many false alarms. */
5243fd43cf6eSHans Rosenfeld 			dec(calib->energy_cck, 8, energy_min);
5244fd43cf6eSHans Rosenfeld 		}
5245fd43cf6eSHans Rosenfeld 		calib->cck_state = IWN_CCK_STATE_INIT;
5246fd43cf6eSHans Rosenfeld 	}
5247fd43cf6eSHans Rosenfeld 
5248fd43cf6eSHans Rosenfeld 	if (needs_update)
5249fd43cf6eSHans Rosenfeld 		(void)iwn_send_sensitivity(sc);
5250fd43cf6eSHans Rosenfeld #undef dec
5251fd43cf6eSHans Rosenfeld #undef inc
5252fd43cf6eSHans Rosenfeld }
5253fd43cf6eSHans Rosenfeld 
5254fd43cf6eSHans Rosenfeld static int
iwn_send_sensitivity(struct iwn_softc * sc)5255fd43cf6eSHans Rosenfeld iwn_send_sensitivity(struct iwn_softc *sc)
5256fd43cf6eSHans Rosenfeld {
5257fd43cf6eSHans Rosenfeld 	struct iwn_calib_state *calib = &sc->calib;
5258fd43cf6eSHans Rosenfeld 	struct iwn_enhanced_sensitivity_cmd cmd;
5259fd43cf6eSHans Rosenfeld 	int len;
5260fd43cf6eSHans Rosenfeld 
5261fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5262fd43cf6eSHans Rosenfeld 	len = sizeof (struct iwn_sensitivity_cmd);
5263fd43cf6eSHans Rosenfeld 	cmd.which = IWN_SENSITIVITY_WORKTBL;
5264fd43cf6eSHans Rosenfeld 	/* OFDM modulation. */
5265fd43cf6eSHans Rosenfeld 	cmd.corr_ofdm_x1     = htole16(calib->ofdm_x1);
5266fd43cf6eSHans Rosenfeld 	cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1);
5267fd43cf6eSHans Rosenfeld 	cmd.corr_ofdm_x4     = htole16(calib->ofdm_x4);
5268fd43cf6eSHans Rosenfeld 	cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4);
5269fd43cf6eSHans Rosenfeld 	cmd.energy_ofdm      = htole16(sc->limits->energy_ofdm);
5270fd43cf6eSHans Rosenfeld 	cmd.energy_ofdm_th   = htole16(62);
5271fd43cf6eSHans Rosenfeld 	/* CCK modulation. */
5272fd43cf6eSHans Rosenfeld 	cmd.corr_cck_x4      = htole16(calib->cck_x4);
5273fd43cf6eSHans Rosenfeld 	cmd.corr_cck_mrc_x4  = htole16(calib->cck_mrc_x4);
5274fd43cf6eSHans Rosenfeld 	cmd.energy_cck       = htole16(calib->energy_cck);
5275fd43cf6eSHans Rosenfeld 	/* Barker modulation: use default values. */
5276fd43cf6eSHans Rosenfeld 	cmd.corr_barker      = htole16(190);
5277fd43cf6eSHans Rosenfeld 	cmd.corr_barker_mrc  = htole16(390);
5278fd43cf6eSHans Rosenfeld 	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
5279fd43cf6eSHans Rosenfeld 		goto send;
5280fd43cf6eSHans Rosenfeld 	/* Enhanced sensitivity settings. */
5281fd43cf6eSHans Rosenfeld 	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
5282fd43cf6eSHans Rosenfeld 	cmd.ofdm_det_slope_mrc = htole16(668);
5283fd43cf6eSHans Rosenfeld 	cmd.ofdm_det_icept_mrc = htole16(4);
5284fd43cf6eSHans Rosenfeld 	cmd.ofdm_det_slope     = htole16(486);
5285fd43cf6eSHans Rosenfeld 	cmd.ofdm_det_icept     = htole16(37);
5286fd43cf6eSHans Rosenfeld 	cmd.cck_det_slope_mrc  = htole16(853);
5287fd43cf6eSHans Rosenfeld 	cmd.cck_det_icept_mrc  = htole16(4);
5288fd43cf6eSHans Rosenfeld 	cmd.cck_det_slope      = htole16(476);
5289fd43cf6eSHans Rosenfeld 	cmd.cck_det_icept      = htole16(99);
5290fd43cf6eSHans Rosenfeld send:
5291fd43cf6eSHans Rosenfeld 
5292fd43cf6eSHans Rosenfeld 	sc->sc_sens->ofdm_x1.value.ul = calib->ofdm_x1;
5293fd43cf6eSHans Rosenfeld 	sc->sc_sens->ofdm_mrc_x1.value.ul = calib->ofdm_mrc_x1;
5294fd43cf6eSHans Rosenfeld 	sc->sc_sens->ofdm_x4.value.ul = calib->ofdm_x4;
5295fd43cf6eSHans Rosenfeld 	sc->sc_sens->ofdm_mrc_x4.value.ul = calib->ofdm_mrc_x4;
5296fd43cf6eSHans Rosenfeld 	sc->sc_sens->cck_x4.value.ul = calib->cck_x4;
5297fd43cf6eSHans Rosenfeld 	sc->sc_sens->cck_mrc_x4.value.ul = calib->cck_mrc_x4;
5298fd43cf6eSHans Rosenfeld 	sc->sc_sens->energy_cck.value.ul = calib->energy_cck;
5299fd43cf6eSHans Rosenfeld 
5300fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
5301fd43cf6eSHans Rosenfeld }
5302fd43cf6eSHans Rosenfeld 
5303fd43cf6eSHans Rosenfeld /*
5304fd43cf6eSHans Rosenfeld  * Set STA mode power saving level (between 0 and 5).
5305fd43cf6eSHans Rosenfeld  * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
5306fd43cf6eSHans Rosenfeld  */
5307fd43cf6eSHans Rosenfeld static int
iwn_set_pslevel(struct iwn_softc * sc,int dtim,int level,int async)5308fd43cf6eSHans Rosenfeld iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
5309fd43cf6eSHans Rosenfeld {
5310fd43cf6eSHans Rosenfeld 	struct iwn_pmgt_cmd cmd;
5311fd43cf6eSHans Rosenfeld 	const struct iwn_pmgt *pmgt;
5312fd43cf6eSHans Rosenfeld 	uint32_t maxp, skip_dtim;
5313fd43cf6eSHans Rosenfeld 	uint32_t reg;
5314fd43cf6eSHans Rosenfeld 	int i;
5315fd43cf6eSHans Rosenfeld 
5316fd43cf6eSHans Rosenfeld 	/* Select which PS parameters to use. */
5317fd43cf6eSHans Rosenfeld 	if (dtim <= 2)
5318fd43cf6eSHans Rosenfeld 		pmgt = &iwn_pmgt[0][level];
5319fd43cf6eSHans Rosenfeld 	else if (dtim <= 10)
5320fd43cf6eSHans Rosenfeld 		pmgt = &iwn_pmgt[1][level];
5321fd43cf6eSHans Rosenfeld 	else
5322fd43cf6eSHans Rosenfeld 		pmgt = &iwn_pmgt[2][level];
5323fd43cf6eSHans Rosenfeld 
5324fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5325fd43cf6eSHans Rosenfeld 	if (level != 0)	/* not CAM */
5326fd43cf6eSHans Rosenfeld 		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
5327fd43cf6eSHans Rosenfeld 	if (level == 5)
5328fd43cf6eSHans Rosenfeld 		cmd.flags |= htole16(IWN_PS_FAST_PD);
5329fd43cf6eSHans Rosenfeld 	/* Retrieve PCIe Active State Power Management (ASPM). */
5330fd43cf6eSHans Rosenfeld 	reg = pci_config_get32(sc->sc_pcih,
5331fd43cf6eSHans Rosenfeld 	    sc->sc_cap_off + PCIE_LINKCTL);
5332fd43cf6eSHans Rosenfeld 	if (!(reg & PCIE_LINKCTL_ASPM_CTL_L0S)) /* L0s Entry disabled. */
5333fd43cf6eSHans Rosenfeld 		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
5334fd43cf6eSHans Rosenfeld 	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
5335fd43cf6eSHans Rosenfeld 	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
5336fd43cf6eSHans Rosenfeld 
5337fd43cf6eSHans Rosenfeld 	if (dtim == 0) {
5338fd43cf6eSHans Rosenfeld 		dtim = 1;
5339fd43cf6eSHans Rosenfeld 		skip_dtim = 0;
5340fd43cf6eSHans Rosenfeld 	} else
5341fd43cf6eSHans Rosenfeld 		skip_dtim = pmgt->skip_dtim;
5342fd43cf6eSHans Rosenfeld 	if (skip_dtim != 0) {
5343fd43cf6eSHans Rosenfeld 		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
5344fd43cf6eSHans Rosenfeld 		maxp = pmgt->intval[4];
5345fd43cf6eSHans Rosenfeld 		if (maxp == (uint32_t)-1)
5346fd43cf6eSHans Rosenfeld 			maxp = dtim * (skip_dtim + 1);
5347fd43cf6eSHans Rosenfeld 		else if (maxp > dtim)
5348fd43cf6eSHans Rosenfeld 			maxp = (maxp / dtim) * dtim;
5349fd43cf6eSHans Rosenfeld 	} else
5350fd43cf6eSHans Rosenfeld 		maxp = dtim;
5351fd43cf6eSHans Rosenfeld 	for (i = 0; i < 5; i++)
5352fd43cf6eSHans Rosenfeld 		cmd.intval[i] = htole32(MIN(maxp, pmgt->intval[i]));
5353fd43cf6eSHans Rosenfeld 
5354fd43cf6eSHans Rosenfeld 	sc->sc_misc->pslevel.value.ul = level;
5355fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
5356fd43cf6eSHans Rosenfeld }
5357fd43cf6eSHans Rosenfeld 
5358fd43cf6eSHans Rosenfeld int
iwn5000_runtime_calib(struct iwn_softc * sc)5359fd43cf6eSHans Rosenfeld iwn5000_runtime_calib(struct iwn_softc *sc)
5360fd43cf6eSHans Rosenfeld {
5361fd43cf6eSHans Rosenfeld 	struct iwn5000_calib_config cmd;
5362fd43cf6eSHans Rosenfeld 
5363fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
5364fd43cf6eSHans Rosenfeld 	cmd.ucode.once.enable = 0xffffffff;
5365fd43cf6eSHans Rosenfeld 	cmd.ucode.once.start = IWN5000_CALIB_DC;
5366fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0);
5367fd43cf6eSHans Rosenfeld }
5368fd43cf6eSHans Rosenfeld 
5369fd43cf6eSHans Rosenfeld static int
iwn_config_bt_coex_bluetooth(struct iwn_softc * sc)5370fd43cf6eSHans Rosenfeld iwn_config_bt_coex_bluetooth(struct iwn_softc *sc)
5371fd43cf6eSHans Rosenfeld {
5372fd43cf6eSHans Rosenfeld 	struct iwn_bluetooth bluetooth;
5373fd43cf6eSHans Rosenfeld 
5374fd43cf6eSHans Rosenfeld 	memset(&bluetooth, 0, sizeof bluetooth);
5375fd43cf6eSHans Rosenfeld 	bluetooth.flags = IWN_BT_COEX_ENABLE;
5376fd43cf6eSHans Rosenfeld 	bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF;
5377fd43cf6eSHans Rosenfeld 	bluetooth.max_kill = IWN_BT_MAX_KILL_DEF;
5378fd43cf6eSHans Rosenfeld 
5379fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0);
5380fd43cf6eSHans Rosenfeld }
5381fd43cf6eSHans Rosenfeld 
5382fd43cf6eSHans Rosenfeld static int
iwn_config_bt_coex_prio_table(struct iwn_softc * sc)5383fd43cf6eSHans Rosenfeld iwn_config_bt_coex_prio_table(struct iwn_softc *sc)
5384fd43cf6eSHans Rosenfeld {
5385fd43cf6eSHans Rosenfeld 	uint8_t prio_table[16];
5386fd43cf6eSHans Rosenfeld 
5387fd43cf6eSHans Rosenfeld 	memset(&prio_table, 0, sizeof prio_table);
5388fd43cf6eSHans Rosenfeld 	prio_table[ 0] =  6;	/* init calibration 1		*/
5389fd43cf6eSHans Rosenfeld 	prio_table[ 1] =  7;	/* init calibration 2		*/
5390fd43cf6eSHans Rosenfeld 	prio_table[ 2] =  2;	/* periodic calib low 1		*/
5391fd43cf6eSHans Rosenfeld 	prio_table[ 3] =  3;	/* periodic calib low 2		*/
5392fd43cf6eSHans Rosenfeld 	prio_table[ 4] =  4;	/* periodic calib high 1	*/
5393fd43cf6eSHans Rosenfeld 	prio_table[ 5] =  5;	/* periodic calib high 2	*/
5394fd43cf6eSHans Rosenfeld 	prio_table[ 6] =  6;	/* dtim				*/
5395fd43cf6eSHans Rosenfeld 	prio_table[ 7] =  8;	/* scan52			*/
5396fd43cf6eSHans Rosenfeld 	prio_table[ 8] = 10;	/* scan24			*/
5397fd43cf6eSHans Rosenfeld 
5398fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_BT_COEX_PRIO_TABLE,
5399fd43cf6eSHans Rosenfeld 	               &prio_table, sizeof prio_table, 0);
5400fd43cf6eSHans Rosenfeld }
5401fd43cf6eSHans Rosenfeld 
5402fd43cf6eSHans Rosenfeld static int
iwn_config_bt_coex_adv_config(struct iwn_softc * sc,struct iwn_bt_basic * basic,size_t len)5403fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv_config(struct iwn_softc *sc, struct iwn_bt_basic *basic,
5404fd43cf6eSHans Rosenfeld     size_t len)
5405fd43cf6eSHans Rosenfeld {
5406fd43cf6eSHans Rosenfeld 	struct iwn_btcoex_prot btprot;
5407fd43cf6eSHans Rosenfeld 	int error;
5408fd43cf6eSHans Rosenfeld 
5409fd43cf6eSHans Rosenfeld 	basic->bt.flags = IWN_BT_COEX_ENABLE;
5410fd43cf6eSHans Rosenfeld 	basic->bt.lead_time = IWN_BT_LEAD_TIME_DEF;
5411fd43cf6eSHans Rosenfeld 	basic->bt.max_kill = IWN_BT_MAX_KILL_DEF;
5412fd43cf6eSHans Rosenfeld 	basic->bt.bt3_timer_t7_value = IWN_BT_BT3_T7_DEF;
5413fd43cf6eSHans Rosenfeld 	basic->bt.kill_ack_mask = IWN_BT_KILL_ACK_MASK_DEF;
5414fd43cf6eSHans Rosenfeld 	basic->bt.kill_cts_mask = IWN_BT_KILL_CTS_MASK_DEF;
5415fd43cf6eSHans Rosenfeld 	basic->bt3_prio_sample_time = IWN_BT_BT3_PRIO_SAMPLE_DEF;
5416fd43cf6eSHans Rosenfeld 	basic->bt3_timer_t2_value = IWN_BT_BT3_T2_DEF;
5417fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 0] = htole32(0xaaaaaaaa); /* Normal */
5418fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 1] = htole32(0xaaaaaaaa);
5419fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 2] = htole32(0xaeaaaaaa);
5420fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 3] = htole32(0xaaaaaaaa);
5421fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 4] = htole32(0xcc00ff28);
5422fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 5] = htole32(0x0000aaaa);
5423fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 6] = htole32(0xcc00aaaa);
5424fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 7] = htole32(0x0000aaaa);
5425fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 8] = htole32(0xc0004000);
5426fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[ 9] = htole32(0x00004000);
5427fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[10] = htole32(0xf0005000);
5428fd43cf6eSHans Rosenfeld 	basic->bt3_lookup_table[11] = htole32(0xf0005000);
5429fd43cf6eSHans Rosenfeld 	basic->reduce_txpower = 0; /* as not implemented */
5430fd43cf6eSHans Rosenfeld 	basic->valid = IWN_BT_ALL_VALID_MASK;
5431fd43cf6eSHans Rosenfeld 
5432fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &basic, len, 0);
5433fd43cf6eSHans Rosenfeld 	if (error != 0) {
5434fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5435fd43cf6eSHans Rosenfeld 		    "!could not configure advanced bluetooth coexistence");
5436fd43cf6eSHans Rosenfeld 		return error;
5437fd43cf6eSHans Rosenfeld 	}
5438fd43cf6eSHans Rosenfeld 
5439fd43cf6eSHans Rosenfeld 	error = iwn_config_bt_coex_prio_table(sc);
5440fd43cf6eSHans Rosenfeld 	if (error != 0) {
5441fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5442fd43cf6eSHans Rosenfeld 		    "!could not configure send BT priority table");
5443fd43cf6eSHans Rosenfeld 		return error;
5444fd43cf6eSHans Rosenfeld 	}
5445fd43cf6eSHans Rosenfeld 
5446fd43cf6eSHans Rosenfeld 	/* Force BT state machine change */
5447fd43cf6eSHans Rosenfeld 	memset(&btprot, 0, sizeof btprot);
5448fd43cf6eSHans Rosenfeld 	btprot.open = 1;
5449fd43cf6eSHans Rosenfeld 	btprot.type = 1;
5450fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof btprot, 1);
5451fd43cf6eSHans Rosenfeld 	if (error != 0) {
5452fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not open BT protcol");
5453fd43cf6eSHans Rosenfeld 		return error;
5454fd43cf6eSHans Rosenfeld 	}
5455fd43cf6eSHans Rosenfeld 
5456fd43cf6eSHans Rosenfeld 	btprot.open = 0;
5457fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof btprot, 1);
5458fd43cf6eSHans Rosenfeld 	if (error != 0) {
5459fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not close BT protcol");
5460fd43cf6eSHans Rosenfeld 		return error;
5461fd43cf6eSHans Rosenfeld 	}
5462fd43cf6eSHans Rosenfeld 	return 0;
5463fd43cf6eSHans Rosenfeld }
5464fd43cf6eSHans Rosenfeld 
5465fd43cf6eSHans Rosenfeld static int
iwn_config_bt_coex_adv1(struct iwn_softc * sc)5466fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv1(struct iwn_softc *sc)
5467fd43cf6eSHans Rosenfeld {
5468fd43cf6eSHans Rosenfeld 	struct iwn_bt_adv1 d;
5469fd43cf6eSHans Rosenfeld 
5470fd43cf6eSHans Rosenfeld 	memset(&d, 0, sizeof d);
5471fd43cf6eSHans Rosenfeld 	d.prio_boost = IWN_BT_PRIO_BOOST_DEF;
5472fd43cf6eSHans Rosenfeld 	d.tx_prio_boost = 0;
5473fd43cf6eSHans Rosenfeld 	d.rx_prio_boost = 0;
5474fd43cf6eSHans Rosenfeld 	return iwn_config_bt_coex_adv_config(sc, &d.basic, sizeof d);
5475fd43cf6eSHans Rosenfeld }
5476fd43cf6eSHans Rosenfeld 
5477fd43cf6eSHans Rosenfeld static int
iwn_config_bt_coex_adv2(struct iwn_softc * sc)5478fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv2(struct iwn_softc *sc)
5479fd43cf6eSHans Rosenfeld {
5480fd43cf6eSHans Rosenfeld 	struct iwn_bt_adv2 d;
5481fd43cf6eSHans Rosenfeld 
5482fd43cf6eSHans Rosenfeld 	memset(&d, 0, sizeof d);
5483fd43cf6eSHans Rosenfeld 	d.prio_boost = IWN_BT_PRIO_BOOST_DEF;
5484fd43cf6eSHans Rosenfeld 	d.tx_prio_boost = 0;
5485fd43cf6eSHans Rosenfeld 	d.rx_prio_boost = 0;
5486fd43cf6eSHans Rosenfeld 	return iwn_config_bt_coex_adv_config(sc, &d.basic, sizeof d);
5487fd43cf6eSHans Rosenfeld }
5488fd43cf6eSHans Rosenfeld 
5489fd43cf6eSHans Rosenfeld static int
iwn_config(struct iwn_softc * sc)5490fd43cf6eSHans Rosenfeld iwn_config(struct iwn_softc *sc)
5491fd43cf6eSHans Rosenfeld {
5492fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
5493fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
5494fd43cf6eSHans Rosenfeld 	uint32_t txmask;
5495fd43cf6eSHans Rosenfeld 	uint16_t rxchain;
5496fd43cf6eSHans Rosenfeld 	int error;
5497fd43cf6eSHans Rosenfeld 
5498fd43cf6eSHans Rosenfeld 	error = ops->config_bt_coex(sc);
5499fd43cf6eSHans Rosenfeld 	if (error != 0) {
5500fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5501fd43cf6eSHans Rosenfeld 		    "!could not configure bluetooth coexistence");
5502fd43cf6eSHans Rosenfeld 		return error;
5503fd43cf6eSHans Rosenfeld 	}
5504fd43cf6eSHans Rosenfeld 
5505fd43cf6eSHans Rosenfeld 	/* Set radio temperature sensor offset. */
5506fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
5507fd43cf6eSHans Rosenfeld 		error = iwn6000_temp_offset_calib(sc);
5508fd43cf6eSHans Rosenfeld 		if (error != 0) {
5509fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
5510fd43cf6eSHans Rosenfeld 			    "!could not set temperature offset");
5511fd43cf6eSHans Rosenfeld 			return error;
5512fd43cf6eSHans Rosenfeld 		}
5513fd43cf6eSHans Rosenfeld 	}
5514fd43cf6eSHans Rosenfeld 
5515fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_2030 ||
5516fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_2000 ||
5517fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_135  ||
5518fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_105) {
5519fd43cf6eSHans Rosenfeld 		error = iwn2000_temp_offset_calib(sc);
5520fd43cf6eSHans Rosenfeld 		if (error != 0) {
5521fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
5522fd43cf6eSHans Rosenfeld 			    "!could not set temperature offset");
5523fd43cf6eSHans Rosenfeld 			return error;
5524fd43cf6eSHans Rosenfeld 		}
5525fd43cf6eSHans Rosenfeld 	}
5526fd43cf6eSHans Rosenfeld 
5527fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6050 ||
5528fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_6005) {
5529fd43cf6eSHans Rosenfeld 		/* Configure runtime DC calibration. */
5530fd43cf6eSHans Rosenfeld 		error = iwn5000_runtime_calib(sc);
5531fd43cf6eSHans Rosenfeld 		if (error != 0) {
5532fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
5533fd43cf6eSHans Rosenfeld 			    "!could not configure runtime calibration");
5534fd43cf6eSHans Rosenfeld 			return error;
5535fd43cf6eSHans Rosenfeld 		}
5536fd43cf6eSHans Rosenfeld 	}
5537fd43cf6eSHans Rosenfeld 
5538fd43cf6eSHans Rosenfeld 	/* Configure valid TX chains for 5000 Series. */
5539fd43cf6eSHans Rosenfeld 	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
5540fd43cf6eSHans Rosenfeld 		txmask = htole32(sc->txchainmask);
5541fd43cf6eSHans Rosenfeld 		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
5542fd43cf6eSHans Rosenfeld 		    sizeof txmask, 0);
5543fd43cf6eSHans Rosenfeld 		if (error != 0) {
5544fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
5545fd43cf6eSHans Rosenfeld 			    "!could not configure valid TX chains");
5546fd43cf6eSHans Rosenfeld 			return error;
5547fd43cf6eSHans Rosenfeld 		}
5548fd43cf6eSHans Rosenfeld 	}
5549fd43cf6eSHans Rosenfeld 
5550fd43cf6eSHans Rosenfeld 	/* Set mode, channel, RX filter and enable RX. */
5551fd43cf6eSHans Rosenfeld 	memset(&sc->rxon, 0, sizeof (struct iwn_rxon));
5552fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(sc->rxon.myaddr, ic->ic_macaddr);
5553fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_macaddr);
5554fd43cf6eSHans Rosenfeld 	sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
5555fd43cf6eSHans Rosenfeld 	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5556fd43cf6eSHans Rosenfeld 	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan))
5557fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5558fd43cf6eSHans Rosenfeld 	switch (ic->ic_opmode) {
5559fd43cf6eSHans Rosenfeld 	case IEEE80211_M_IBSS:
5560fd43cf6eSHans Rosenfeld 		sc->rxon.mode = IWN_MODE_IBSS;
5561fd43cf6eSHans Rosenfeld 		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
5562fd43cf6eSHans Rosenfeld 		break;
5563fd43cf6eSHans Rosenfeld 	case IEEE80211_M_STA:
5564fd43cf6eSHans Rosenfeld 		sc->rxon.mode = IWN_MODE_STA;
5565fd43cf6eSHans Rosenfeld 		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
5566fd43cf6eSHans Rosenfeld 		break;
5567fd43cf6eSHans Rosenfeld 	case IEEE80211_M_MONITOR:
5568fd43cf6eSHans Rosenfeld 		sc->rxon.mode = IWN_MODE_MONITOR;
5569fd43cf6eSHans Rosenfeld 		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST |
5570fd43cf6eSHans Rosenfeld 		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
5571fd43cf6eSHans Rosenfeld 		break;
5572fd43cf6eSHans Rosenfeld 	default:
5573fd43cf6eSHans Rosenfeld 		/* Should not get there. */
5574fd43cf6eSHans Rosenfeld 		ASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
5575fd43cf6eSHans Rosenfeld 		    ic->ic_opmode == IEEE80211_M_STA ||
5576fd43cf6eSHans Rosenfeld 		    ic->ic_opmode == IEEE80211_M_MONITOR);
5577fd43cf6eSHans Rosenfeld 		break;
5578fd43cf6eSHans Rosenfeld 	}
5579fd43cf6eSHans Rosenfeld 	sc->rxon.cck_mask  = 0x0f;	/* not yet negotiated */
5580fd43cf6eSHans Rosenfeld 	sc->rxon.ofdm_mask = 0xff;	/* not yet negotiated */
5581fd43cf6eSHans Rosenfeld 	sc->rxon.ht_single_mask = 0xff;
5582fd43cf6eSHans Rosenfeld 	sc->rxon.ht_dual_mask = 0xff;
5583fd43cf6eSHans Rosenfeld 	sc->rxon.ht_triple_mask = 0xff;
5584fd43cf6eSHans Rosenfeld 	rxchain =
5585fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5586fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_MIMO_COUNT(2) |
5587fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_IDLE_COUNT(2);
5588fd43cf6eSHans Rosenfeld 	sc->rxon.rxchain = htole16(rxchain);
5589fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz);
5590fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0);
5591fd43cf6eSHans Rosenfeld 	if (error != 0) {
5592fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5593fd43cf6eSHans Rosenfeld 		    "!RXON command failed");
5594fd43cf6eSHans Rosenfeld 		return error;
5595fd43cf6eSHans Rosenfeld 	}
5596fd43cf6eSHans Rosenfeld 
5597fd43cf6eSHans Rosenfeld 	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
5598fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5599fd43cf6eSHans Rosenfeld 		    "!could not add broadcast node");
5600fd43cf6eSHans Rosenfeld 		return error;
5601fd43cf6eSHans Rosenfeld 	}
5602fd43cf6eSHans Rosenfeld 
5603fd43cf6eSHans Rosenfeld 	/* Configuration has changed, set TX power accordingly. */
5604fd43cf6eSHans Rosenfeld 	if ((error = ops->set_txpower(sc, 0)) != 0) {
5605fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5606fd43cf6eSHans Rosenfeld 		    "!could not set TX power");
5607fd43cf6eSHans Rosenfeld 		return error;
5608fd43cf6eSHans Rosenfeld 	}
5609fd43cf6eSHans Rosenfeld 
5610fd43cf6eSHans Rosenfeld 	if ((error = iwn_set_critical_temp(sc)) != 0) {
5611fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5612fd43cf6eSHans Rosenfeld 		    "!could not set critical temperature");
5613fd43cf6eSHans Rosenfeld 		return error;
5614fd43cf6eSHans Rosenfeld 	}
5615fd43cf6eSHans Rosenfeld 
5616fd43cf6eSHans Rosenfeld 	/* Set power saving level to CAM during initialization. */
5617fd43cf6eSHans Rosenfeld 	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
5618fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5619fd43cf6eSHans Rosenfeld 		    "!could not set power saving level");
5620fd43cf6eSHans Rosenfeld 		return error;
5621fd43cf6eSHans Rosenfeld 	}
5622fd43cf6eSHans Rosenfeld 	return 0;
5623fd43cf6eSHans Rosenfeld }
5624fd43cf6eSHans Rosenfeld 
5625fd43cf6eSHans Rosenfeld static uint16_t
iwn_get_active_dwell_time(struct iwn_softc * sc,uint16_t flags,uint8_t n_probes)5626fd43cf6eSHans Rosenfeld iwn_get_active_dwell_time(struct iwn_softc *sc, uint16_t flags,
5627fd43cf6eSHans Rosenfeld     uint8_t n_probes)
5628fd43cf6eSHans Rosenfeld {
5629fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(sc));
5630fd43cf6eSHans Rosenfeld 
5631fd43cf6eSHans Rosenfeld 	/* No channel? Default to 2GHz settings */
5632fd43cf6eSHans Rosenfeld 	if (flags & IEEE80211_CHAN_2GHZ)
5633fd43cf6eSHans Rosenfeld 		return IWN_ACTIVE_DWELL_TIME_2GHZ +
5634fd43cf6eSHans Rosenfeld 		    IWN_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1);
5635fd43cf6eSHans Rosenfeld 
5636fd43cf6eSHans Rosenfeld 	/* 5GHz dwell time */
5637fd43cf6eSHans Rosenfeld 	return IWN_ACTIVE_DWELL_TIME_5GHZ +
5638fd43cf6eSHans Rosenfeld 	    IWN_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1);
5639fd43cf6eSHans Rosenfeld }
5640fd43cf6eSHans Rosenfeld 
5641fd43cf6eSHans Rosenfeld /*
5642fd43cf6eSHans Rosenfeld  * Limit the total dwell time to 85% of the beacon interval.
5643fd43cf6eSHans Rosenfeld  *
5644fd43cf6eSHans Rosenfeld  * Returns the dwell time in milliseconds.
5645fd43cf6eSHans Rosenfeld  */
5646fd43cf6eSHans Rosenfeld static uint16_t
iwn_limit_dwell(struct iwn_softc * sc,uint16_t dwell_time)5647fd43cf6eSHans Rosenfeld iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time)
5648fd43cf6eSHans Rosenfeld {
5649fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(dwell_time));
5650fd43cf6eSHans Rosenfeld 
5651fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
5652fd43cf6eSHans Rosenfeld 	struct ieee80211_node *ni = ic->ic_bss;
5653fd43cf6eSHans Rosenfeld 	int bintval = 0;
5654fd43cf6eSHans Rosenfeld 
5655fd43cf6eSHans Rosenfeld 	/* bintval is in TU (1.024mS) */
5656fd43cf6eSHans Rosenfeld 	if (ni != NULL)
5657fd43cf6eSHans Rosenfeld 		bintval = ni->in_intval;
5658fd43cf6eSHans Rosenfeld 
5659fd43cf6eSHans Rosenfeld 	/*
5660fd43cf6eSHans Rosenfeld 	 * If it's non-zero, we should calculate the minimum of
5661fd43cf6eSHans Rosenfeld 	 * it and the DWELL_BASE.
5662fd43cf6eSHans Rosenfeld 	 *
5663fd43cf6eSHans Rosenfeld 	 * XXX Yes, the math should take into account that bintval
5664fd43cf6eSHans Rosenfeld 	 * is 1.024mS, not 1mS..
5665fd43cf6eSHans Rosenfeld 	 */
5666fd43cf6eSHans Rosenfeld 	if (bintval > 0)
5667fd43cf6eSHans Rosenfeld 		return MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100));
5668fd43cf6eSHans Rosenfeld 
5669fd43cf6eSHans Rosenfeld 	/* No association context? Default */
5670fd43cf6eSHans Rosenfeld 	return IWN_PASSIVE_DWELL_BASE;
5671fd43cf6eSHans Rosenfeld }
5672fd43cf6eSHans Rosenfeld 
5673fd43cf6eSHans Rosenfeld static uint16_t
iwn_get_passive_dwell_time(struct iwn_softc * sc,uint16_t flags)5674fd43cf6eSHans Rosenfeld iwn_get_passive_dwell_time(struct iwn_softc *sc, uint16_t flags)
5675fd43cf6eSHans Rosenfeld {
5676fd43cf6eSHans Rosenfeld 	uint16_t passive;
5677fd43cf6eSHans Rosenfeld 	if (flags & IEEE80211_CHAN_2GHZ)
5678fd43cf6eSHans Rosenfeld 		passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_2GHZ;
5679fd43cf6eSHans Rosenfeld 	else
5680fd43cf6eSHans Rosenfeld 		passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_5GHZ;
5681fd43cf6eSHans Rosenfeld 
5682fd43cf6eSHans Rosenfeld 	/* Clamp to the beacon interval if we're associated */
5683fd43cf6eSHans Rosenfeld 	return iwn_limit_dwell(sc, passive);
5684fd43cf6eSHans Rosenfeld }
5685fd43cf6eSHans Rosenfeld 
5686fd43cf6eSHans Rosenfeld static int
iwn_scan(struct iwn_softc * sc,uint16_t flags)5687fd43cf6eSHans Rosenfeld iwn_scan(struct iwn_softc *sc, uint16_t flags)
5688fd43cf6eSHans Rosenfeld {
5689fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
5690fd43cf6eSHans Rosenfeld 	struct iwn_scan_hdr *hdr;
5691fd43cf6eSHans Rosenfeld 	struct iwn_cmd_data *tx;
5692fd43cf6eSHans Rosenfeld 	struct iwn_scan_essid *essid;
5693fd43cf6eSHans Rosenfeld 	struct iwn_scan_chan *chan;
5694fd43cf6eSHans Rosenfeld 	struct ieee80211_frame *wh;
5695fd43cf6eSHans Rosenfeld 	struct ieee80211_rateset *rs;
5696fd43cf6eSHans Rosenfeld 	struct ieee80211_channel *c;
5697fd43cf6eSHans Rosenfeld 	uint8_t *buf, *frm;
5698fd43cf6eSHans Rosenfeld 	uint16_t rxchain, dwell_active, dwell_passive;
5699fd43cf6eSHans Rosenfeld 	uint8_t txant;
5700fd43cf6eSHans Rosenfeld 	int buflen, error, is_active;
5701fd43cf6eSHans Rosenfeld 
5702fd43cf6eSHans Rosenfeld 	buf = kmem_zalloc(IWN_SCAN_MAXSZ, KM_NOSLEEP);
5703fd43cf6eSHans Rosenfeld 	if (buf == NULL) {
5704fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5705fd43cf6eSHans Rosenfeld 		    "!could not allocate buffer for scan command");
5706fd43cf6eSHans Rosenfeld 		return ENOMEM;
5707fd43cf6eSHans Rosenfeld 	}
5708fd43cf6eSHans Rosenfeld 	hdr = (struct iwn_scan_hdr *)buf;
5709fd43cf6eSHans Rosenfeld 	/*
5710fd43cf6eSHans Rosenfeld 	 * Move to the next channel if no frames are received within 20ms
5711fd43cf6eSHans Rosenfeld 	 * after sending the probe request.
5712fd43cf6eSHans Rosenfeld 	 */
5713fd43cf6eSHans Rosenfeld 	hdr->quiet_time = htole16(20);		/* timeout in milliseconds */
5714fd43cf6eSHans Rosenfeld 	hdr->quiet_threshold = htole16(1);	/* min # of packets */
5715fd43cf6eSHans Rosenfeld 
5716fd43cf6eSHans Rosenfeld 	/* Select antennas for scanning. */
5717fd43cf6eSHans Rosenfeld 	rxchain =
5718fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5719fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
5720fd43cf6eSHans Rosenfeld 	    IWN_RXCHAIN_DRIVER_FORCE;
5721fd43cf6eSHans Rosenfeld 	if ((flags & IEEE80211_CHAN_5GHZ) &&
5722fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
5723fd43cf6eSHans Rosenfeld 		/* Ant A must be avoided in 5GHz because of an HW bug. */
5724fd43cf6eSHans Rosenfeld 		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC);
5725fd43cf6eSHans Rosenfeld 	} else	/* Use all available RX antennas. */
5726fd43cf6eSHans Rosenfeld 		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
5727fd43cf6eSHans Rosenfeld 	hdr->rxchain = htole16(rxchain);
5728fd43cf6eSHans Rosenfeld 	hdr->filter = htole32(IWN_FILTER_MULTICAST |  IWN_FILTER_BEACON);
5729fd43cf6eSHans Rosenfeld 
5730fd43cf6eSHans Rosenfeld 	tx = (struct iwn_cmd_data *)(hdr + 1);
5731fd43cf6eSHans Rosenfeld 	tx->flags = htole32(IWN_TX_AUTO_SEQ);
5732fd43cf6eSHans Rosenfeld 	tx->id = sc->broadcast_id;
5733fd43cf6eSHans Rosenfeld 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
5734fd43cf6eSHans Rosenfeld 
5735fd43cf6eSHans Rosenfeld 	if (flags & IEEE80211_CHAN_5GHZ) {
5736fd43cf6eSHans Rosenfeld 		/* Send probe requests at 6Mbps. */
5737fd43cf6eSHans Rosenfeld 		tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
5738fd43cf6eSHans Rosenfeld 		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
5739fd43cf6eSHans Rosenfeld 	} else {
5740fd43cf6eSHans Rosenfeld 		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
5741fd43cf6eSHans Rosenfeld 		/* Send probe requests at 1Mbps. */
5742fd43cf6eSHans Rosenfeld 		tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp;
5743fd43cf6eSHans Rosenfeld 		tx->rflags = IWN_RFLAG_CCK;
5744fd43cf6eSHans Rosenfeld 		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
5745fd43cf6eSHans Rosenfeld 	}
5746fd43cf6eSHans Rosenfeld 
5747fd43cf6eSHans Rosenfeld 	hdr->crc_threshold = 0xffff;
5748fd43cf6eSHans Rosenfeld 
5749fd43cf6eSHans Rosenfeld 	/* Use the first valid TX antenna. */
5750fd43cf6eSHans Rosenfeld 	txant = IWN_LSB(sc->txchainmask);
5751fd43cf6eSHans Rosenfeld 	tx->rflags |= IWN_RFLAG_ANT(txant);
5752fd43cf6eSHans Rosenfeld 
5753fd43cf6eSHans Rosenfeld 	/*
5754fd43cf6eSHans Rosenfeld 	 * Only do active scanning if we're announcing a probe request
5755fd43cf6eSHans Rosenfeld 	 * for a given SSID (or more, if we ever add it to the driver.)
5756fd43cf6eSHans Rosenfeld 	 */
5757fd43cf6eSHans Rosenfeld 	is_active = 0;
5758fd43cf6eSHans Rosenfeld 
5759fd43cf6eSHans Rosenfeld 	essid = (struct iwn_scan_essid *)(tx + 1);
5760fd43cf6eSHans Rosenfeld 	if (ic->ic_des_esslen != 0) {
5761fd43cf6eSHans Rosenfeld 		char essidstr[IEEE80211_NWID_LEN+1];
5762fd43cf6eSHans Rosenfeld 		memcpy(essidstr, ic->ic_des_essid, ic->ic_des_esslen);
5763fd43cf6eSHans Rosenfeld 		essidstr[ic->ic_des_esslen] = '\0';
5764fd43cf6eSHans Rosenfeld 
5765fd43cf6eSHans Rosenfeld 		DTRACE_PROBE1(scan__direct, char *, essidstr);
5766fd43cf6eSHans Rosenfeld 
5767fd43cf6eSHans Rosenfeld 		essid[0].id = IEEE80211_ELEMID_SSID;
5768fd43cf6eSHans Rosenfeld 		essid[0].len = ic->ic_des_esslen;
5769fd43cf6eSHans Rosenfeld 		memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen);
5770fd43cf6eSHans Rosenfeld 
5771fd43cf6eSHans Rosenfeld 		is_active = 1;
5772fd43cf6eSHans Rosenfeld 		/* hdr->crc_threshold = 0x1; */
5773fd43cf6eSHans Rosenfeld 		hdr->scan_flags = htole32(IWN_SCAN_PASSIVE2ACTIVE);
5774fd43cf6eSHans Rosenfeld 	}
5775fd43cf6eSHans Rosenfeld 	/*
5776fd43cf6eSHans Rosenfeld 	 * Build a probe request frame.  Most of the following code is a
5777fd43cf6eSHans Rosenfeld 	 * copy & paste of what is done in net80211.
5778fd43cf6eSHans Rosenfeld 	 */
5779fd43cf6eSHans Rosenfeld 	wh = (struct ieee80211_frame *)(essid + 20);
5780fd43cf6eSHans Rosenfeld 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
5781fd43cf6eSHans Rosenfeld 	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
5782fd43cf6eSHans Rosenfeld 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
5783fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
5784fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
5785fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr);
5786fd43cf6eSHans Rosenfeld 	wh->i_dur[0] = wh->i_dur[1] = 0; /* filled by HW */
5787fd43cf6eSHans Rosenfeld 	wh->i_seq[0] = wh->i_seq[1] = 0; /* filled by HW */
5788fd43cf6eSHans Rosenfeld 
5789fd43cf6eSHans Rosenfeld 	frm = (uint8_t *)(wh + 1);
5790fd43cf6eSHans Rosenfeld 	frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
5791fd43cf6eSHans Rosenfeld 	frm = ieee80211_add_rates(frm, rs);
5792fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
5793fd43cf6eSHans Rosenfeld 	if (ic->ic_flags & IEEE80211_F_HTON)
5794fd43cf6eSHans Rosenfeld 		frm = ieee80211_add_htcaps(frm, ic);
5795fd43cf6eSHans Rosenfeld #endif
5796fd43cf6eSHans Rosenfeld 	if (rs->ir_nrates > IEEE80211_RATE_SIZE)
5797fd43cf6eSHans Rosenfeld 		frm = ieee80211_add_xrates(frm, rs);
5798fd43cf6eSHans Rosenfeld 
5799fd43cf6eSHans Rosenfeld 	/* Set length of probe request. */
5800fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTRDIFF_OVERFLOW*/
5801fd43cf6eSHans Rosenfeld 	tx->len = htole16(frm - (uint8_t *)wh);
5802fd43cf6eSHans Rosenfeld 
5803fd43cf6eSHans Rosenfeld 
5804fd43cf6eSHans Rosenfeld 	/*
5805fd43cf6eSHans Rosenfeld 	 * If active scanning is requested but a certain channel is
5806fd43cf6eSHans Rosenfeld 	 * marked passive, we can do active scanning if we detect
5807fd43cf6eSHans Rosenfeld 	 * transmissions.
5808fd43cf6eSHans Rosenfeld 	 *
5809fd43cf6eSHans Rosenfeld 	 * There is an issue with some firmware versions that triggers
5810fd43cf6eSHans Rosenfeld 	 * a sysassert on a "good CRC threshold" of zero (== disabled),
5811fd43cf6eSHans Rosenfeld 	 * on a radar channel even though this means that we should NOT
5812fd43cf6eSHans Rosenfeld 	 * send probes.
5813fd43cf6eSHans Rosenfeld 	 *
5814fd43cf6eSHans Rosenfeld 	 * The "good CRC threshold" is the number of frames that we
5815fd43cf6eSHans Rosenfeld 	 * need to receive during our dwell time on a channel before
5816fd43cf6eSHans Rosenfeld 	 * sending out probes -- setting this to a huge value will
5817fd43cf6eSHans Rosenfeld 	 * mean we never reach it, but at the same time work around
5818fd43cf6eSHans Rosenfeld 	 * the aforementioned issue. Thus use IWN_GOOD_CRC_TH_NEVER
5819fd43cf6eSHans Rosenfeld 	 * here instead of IWN_GOOD_CRC_TH_DISABLED.
5820fd43cf6eSHans Rosenfeld 	 *
5821fd43cf6eSHans Rosenfeld 	 * This was fixed in later versions along with some other
5822fd43cf6eSHans Rosenfeld 	 * scan changes, and the threshold behaves as a flag in those
5823fd43cf6eSHans Rosenfeld 	 * versions.
5824fd43cf6eSHans Rosenfeld 	 */
5825fd43cf6eSHans Rosenfeld 
5826fd43cf6eSHans Rosenfeld 	/*
5827fd43cf6eSHans Rosenfeld 	 * If we're doing active scanning, set the crc_threshold
5828fd43cf6eSHans Rosenfeld 	 * to a suitable value.  This is different to active veruss
5829fd43cf6eSHans Rosenfeld 	 * passive scanning depending upon the channel flags; the
5830fd43cf6eSHans Rosenfeld 	 * firmware will obey that particular check for us.
5831fd43cf6eSHans Rosenfeld 	 */
5832fd43cf6eSHans Rosenfeld 	if (sc->tlv_feature_flags & IWN_UCODE_TLV_FLAGS_NEWSCAN)
5833fd43cf6eSHans Rosenfeld 		hdr->crc_threshold = is_active ?
5834fd43cf6eSHans Rosenfeld 		    IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_DISABLED;
5835fd43cf6eSHans Rosenfeld 	else
5836fd43cf6eSHans Rosenfeld 		hdr->crc_threshold = is_active ?
5837fd43cf6eSHans Rosenfeld 		    IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_NEVER;
5838fd43cf6eSHans Rosenfeld 
5839fd43cf6eSHans Rosenfeld 	chan = (struct iwn_scan_chan *)frm;
5840fd43cf6eSHans Rosenfeld 	for (c  = &ic->ic_sup_channels[1];
5841fd43cf6eSHans Rosenfeld 	     c <= &ic->ic_sup_channels[IEEE80211_CHAN_MAX]; c++) {
5842fd43cf6eSHans Rosenfeld 		if ((c->ich_flags & flags) != flags)
5843fd43cf6eSHans Rosenfeld 			continue;
5844fd43cf6eSHans Rosenfeld 		chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5845fd43cf6eSHans Rosenfeld 		chan->flags = 0;
5846fd43cf6eSHans Rosenfeld 		if (!(c->ich_flags & IEEE80211_CHAN_PASSIVE))
5847fd43cf6eSHans Rosenfeld 			chan->flags |= htole32(IWN_CHAN_ACTIVE);
5848fd43cf6eSHans Rosenfeld 		if (ic->ic_des_esslen != 0)
5849fd43cf6eSHans Rosenfeld 			chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5850fd43cf6eSHans Rosenfeld 
5851fd43cf6eSHans Rosenfeld 		/*
5852fd43cf6eSHans Rosenfeld 		 * Calculate the active/passive dwell times.
5853fd43cf6eSHans Rosenfeld 		 */
5854fd43cf6eSHans Rosenfeld 
5855fd43cf6eSHans Rosenfeld 		dwell_active = iwn_get_active_dwell_time(sc, flags, is_active);
5856fd43cf6eSHans Rosenfeld 		dwell_passive = iwn_get_passive_dwell_time(sc, flags);
5857fd43cf6eSHans Rosenfeld 
5858fd43cf6eSHans Rosenfeld 		/* Make sure they're valid */
5859fd43cf6eSHans Rosenfeld 		if (dwell_passive <= dwell_active)
5860fd43cf6eSHans Rosenfeld 			dwell_passive = dwell_active + 1;
5861fd43cf6eSHans Rosenfeld 
5862fd43cf6eSHans Rosenfeld 		chan->active = htole16(dwell_active);
5863fd43cf6eSHans Rosenfeld 		chan->passive = htole16(dwell_passive);
5864fd43cf6eSHans Rosenfeld 
5865fd43cf6eSHans Rosenfeld 		chan->dsp_gain = 0x6e;
5866fd43cf6eSHans Rosenfeld 		if (IEEE80211_IS_CHAN_5GHZ(c)) {
5867fd43cf6eSHans Rosenfeld 			chan->rf_gain = 0x3b;
5868fd43cf6eSHans Rosenfeld 		} else {
5869fd43cf6eSHans Rosenfeld 			chan->rf_gain = 0x28;
5870fd43cf6eSHans Rosenfeld 		}
5871fd43cf6eSHans Rosenfeld 		DTRACE_PROBE5(add__channel, uint8_t, chan->chan,
5872fd43cf6eSHans Rosenfeld 		    uint32_t, chan->flags, uint8_t, chan->rf_gain,
5873fd43cf6eSHans Rosenfeld 		    uint16_t, chan->active, uint16_t, chan->passive);
5874fd43cf6eSHans Rosenfeld 		hdr->nchan++;
5875fd43cf6eSHans Rosenfeld 		chan++;
5876fd43cf6eSHans Rosenfeld 	}
5877fd43cf6eSHans Rosenfeld 
5878fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTRDIFF_OVERFLOW*/
5879fd43cf6eSHans Rosenfeld 	buflen = (uint8_t *)chan - buf;
5880fd43cf6eSHans Rosenfeld 	hdr->len = htole16(buflen);
5881fd43cf6eSHans Rosenfeld 
5882fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5883fd43cf6eSHans Rosenfeld 	kmem_free(buf, IWN_SCAN_MAXSZ);
5884fd43cf6eSHans Rosenfeld 	return error;
5885fd43cf6eSHans Rosenfeld }
5886fd43cf6eSHans Rosenfeld 
5887fd43cf6eSHans Rosenfeld static int
iwn_auth(struct iwn_softc * sc)5888fd43cf6eSHans Rosenfeld iwn_auth(struct iwn_softc *sc)
5889fd43cf6eSHans Rosenfeld {
5890fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
5891fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
5892fd43cf6eSHans Rosenfeld 	struct ieee80211_node *ni = ic->ic_bss;
5893fd43cf6eSHans Rosenfeld 	int error;
5894fd43cf6eSHans Rosenfeld 
5895fd43cf6eSHans Rosenfeld 	ASSERT(ni->in_chan != NULL);
5896fd43cf6eSHans Rosenfeld 
5897fd43cf6eSHans Rosenfeld 	/* Update adapter configuration. */
5898fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->in_bssid);
5899fd43cf6eSHans Rosenfeld 	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->in_chan);
5900fd43cf6eSHans Rosenfeld 	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5901fd43cf6eSHans Rosenfeld 	if ((ni->in_chan != IEEE80211_CHAN_ANYC) &&
5902fd43cf6eSHans Rosenfeld 	    IEEE80211_IS_CHAN_2GHZ(ni->in_chan))
5903fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5904fd43cf6eSHans Rosenfeld 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5905fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5906fd43cf6eSHans Rosenfeld 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5907fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5908fd43cf6eSHans Rosenfeld 	switch (ic->ic_curmode) {
5909fd43cf6eSHans Rosenfeld 	case IEEE80211_MODE_11A:
5910fd43cf6eSHans Rosenfeld 		sc->rxon.cck_mask  = 0;
5911fd43cf6eSHans Rosenfeld 		sc->rxon.ofdm_mask = 0x15;
5912fd43cf6eSHans Rosenfeld 		break;
5913fd43cf6eSHans Rosenfeld 	case IEEE80211_MODE_11B:
5914fd43cf6eSHans Rosenfeld 		sc->rxon.cck_mask  = 0x03;
5915fd43cf6eSHans Rosenfeld 		sc->rxon.ofdm_mask = 0;
5916fd43cf6eSHans Rosenfeld 		break;
5917fd43cf6eSHans Rosenfeld 	default:	/* Assume 802.11b/g. */
5918fd43cf6eSHans Rosenfeld 		sc->rxon.cck_mask  = 0x0f;
5919fd43cf6eSHans Rosenfeld 		sc->rxon.ofdm_mask = 0x15;
5920fd43cf6eSHans Rosenfeld 	}
5921fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz);
5922fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5923fd43cf6eSHans Rosenfeld 	if (error != 0) {
5924fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5925fd43cf6eSHans Rosenfeld 		    "!RXON command failed");
5926fd43cf6eSHans Rosenfeld 		return error;
5927fd43cf6eSHans Rosenfeld 	}
5928fd43cf6eSHans Rosenfeld 
5929fd43cf6eSHans Rosenfeld 	/* Configuration has changed, set TX power accordingly. */
5930fd43cf6eSHans Rosenfeld 	if ((error = ops->set_txpower(sc, 1)) != 0) {
5931fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5932fd43cf6eSHans Rosenfeld 		    "!could not set TX power");
5933fd43cf6eSHans Rosenfeld 		return error;
5934fd43cf6eSHans Rosenfeld 	}
5935fd43cf6eSHans Rosenfeld 	/*
5936fd43cf6eSHans Rosenfeld 	 * Reconfiguring RXON clears the firmware nodes table so we must
5937fd43cf6eSHans Rosenfeld 	 * add the broadcast node again.
5938fd43cf6eSHans Rosenfeld 	 */
5939fd43cf6eSHans Rosenfeld 	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5940fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
5941fd43cf6eSHans Rosenfeld 		    "!could not add broadcast node");
5942fd43cf6eSHans Rosenfeld 		return error;
5943fd43cf6eSHans Rosenfeld 	}
5944fd43cf6eSHans Rosenfeld 	return 0;
5945fd43cf6eSHans Rosenfeld }
5946fd43cf6eSHans Rosenfeld 
5947fd43cf6eSHans Rosenfeld static int
iwn_fast_recover(struct iwn_softc * sc)5948fd43cf6eSHans Rosenfeld iwn_fast_recover(struct iwn_softc *sc)
5949fd43cf6eSHans Rosenfeld {
5950fd43cf6eSHans Rosenfeld 	int err = IWN_FAIL;
5951fd43cf6eSHans Rosenfeld 
5952fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
5953fd43cf6eSHans Rosenfeld 
5954fd43cf6eSHans Rosenfeld 	/* restore runtime configuration */
5955fd43cf6eSHans Rosenfeld 	bcopy(&sc->rxon_save, &sc->rxon,
5956fd43cf6eSHans Rosenfeld 	    sizeof (sc->rxon));
5957fd43cf6eSHans Rosenfeld 
5958fd43cf6eSHans Rosenfeld 	sc->rxon.associd = 0;
5959fd43cf6eSHans Rosenfeld 	sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
5960fd43cf6eSHans Rosenfeld 
5961fd43cf6eSHans Rosenfeld 	if ((err = iwn_auth(sc)) != IWN_SUCCESS) {
5962fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_fast_recover(): "
5963fd43cf6eSHans Rosenfeld 		    "could not setup authentication");
5964fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
5965fd43cf6eSHans Rosenfeld 		return (err);
5966fd43cf6eSHans Rosenfeld 	}
5967fd43cf6eSHans Rosenfeld 
5968fd43cf6eSHans Rosenfeld 	bcopy(&sc->rxon_save, &sc->rxon, sizeof (sc->rxon));
5969fd43cf6eSHans Rosenfeld 
5970fd43cf6eSHans Rosenfeld 	/* update adapter's configuration */
5971fd43cf6eSHans Rosenfeld 	err = iwn_run(sc);
5972fd43cf6eSHans Rosenfeld 	if (err != IWN_SUCCESS) {
5973fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!iwn_fast_recover(): "
5974fd43cf6eSHans Rosenfeld 		    "failed to setup association");
5975fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
5976fd43cf6eSHans Rosenfeld 		return (err);
5977fd43cf6eSHans Rosenfeld 	}
5978fd43cf6eSHans Rosenfeld 	/* set LED on */
5979fd43cf6eSHans Rosenfeld 	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5980fd43cf6eSHans Rosenfeld 
5981fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER;
5982fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
5983fd43cf6eSHans Rosenfeld 
5984fd43cf6eSHans Rosenfeld 	/* start queue */
5985fd43cf6eSHans Rosenfeld 	DTRACE_PROBE(resume__xmit);
5986fd43cf6eSHans Rosenfeld 
5987fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
5988fd43cf6eSHans Rosenfeld }
5989fd43cf6eSHans Rosenfeld 
5990fd43cf6eSHans Rosenfeld static int
iwn_run(struct iwn_softc * sc)5991fd43cf6eSHans Rosenfeld iwn_run(struct iwn_softc *sc)
5992fd43cf6eSHans Rosenfeld {
5993fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
5994fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
5995fd43cf6eSHans Rosenfeld 	struct ieee80211_node *ni = ic->ic_bss;
5996fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
5997fd43cf6eSHans Rosenfeld 	int error;
5998fd43cf6eSHans Rosenfeld 
5999fd43cf6eSHans Rosenfeld 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
6000fd43cf6eSHans Rosenfeld 		/* Link LED blinks while monitoring. */
6001fd43cf6eSHans Rosenfeld 		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
6002fd43cf6eSHans Rosenfeld 		return 0;
6003fd43cf6eSHans Rosenfeld 	}
6004fd43cf6eSHans Rosenfeld 	if ((error = iwn_set_timing(sc, ni)) != 0) {
6005fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6006fd43cf6eSHans Rosenfeld 		    "!could not set timing");
6007fd43cf6eSHans Rosenfeld 		return error;
6008fd43cf6eSHans Rosenfeld 	}
6009fd43cf6eSHans Rosenfeld 
6010fd43cf6eSHans Rosenfeld 	/* Update adapter configuration. */
6011fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->in_bssid);
6012fd43cf6eSHans Rosenfeld 	sc->rxon.associd = htole16(IEEE80211_AID(ni->in_associd));
6013fd43cf6eSHans Rosenfeld 	/* Short preamble and slot time are negotiated when associating. */
6014fd43cf6eSHans Rosenfeld 	sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE | IWN_RXON_SHSLOT);
6015fd43cf6eSHans Rosenfeld 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
6016fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
6017fd43cf6eSHans Rosenfeld 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
6018fd43cf6eSHans Rosenfeld 		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
6019fd43cf6eSHans Rosenfeld 	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
6020fd43cf6eSHans Rosenfeld 	if (ic->ic_opmode != IEEE80211_M_STA &&
6021fd43cf6eSHans Rosenfeld 	    ic->ic_opmode != IEEE80211_M_IBSS)
6022fd43cf6eSHans Rosenfeld 		sc->rxon.filter |= htole32(IWN_FILTER_BEACON);
6023fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz);
6024fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
6025fd43cf6eSHans Rosenfeld 	if (error != 0) {
6026fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6027fd43cf6eSHans Rosenfeld 		    "!could not update configuration");
6028fd43cf6eSHans Rosenfeld 		return error;
6029fd43cf6eSHans Rosenfeld 	}
6030fd43cf6eSHans Rosenfeld 
6031fd43cf6eSHans Rosenfeld 	/* Configuration has changed, set TX power accordingly. */
6032fd43cf6eSHans Rosenfeld 	if ((error = ops->set_txpower(sc, 1)) != 0) {
6033fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6034fd43cf6eSHans Rosenfeld 		    "!could not set TX power");
6035fd43cf6eSHans Rosenfeld 		return error;
6036fd43cf6eSHans Rosenfeld 	}
6037fd43cf6eSHans Rosenfeld 
6038fd43cf6eSHans Rosenfeld 	/* Fake a join to initialize the TX rate. */
6039fd43cf6eSHans Rosenfeld 	((struct iwn_node *)ni)->id = IWN_ID_BSS;
6040fd43cf6eSHans Rosenfeld 	iwn_newassoc(ni, 1);
6041fd43cf6eSHans Rosenfeld 
6042fd43cf6eSHans Rosenfeld 	/* Add BSS node. */
6043fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6044fd43cf6eSHans Rosenfeld 	IEEE80211_ADDR_COPY(node.macaddr, ni->in_macaddr);
6045fd43cf6eSHans Rosenfeld 	node.id = IWN_ID_BSS;
6046fd43cf6eSHans Rosenfeld #ifdef notyet
6047fd43cf6eSHans Rosenfeld 	node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) |
6048fd43cf6eSHans Rosenfeld 	    IWN_AMDPU_DENSITY(5));	/* 2us */
6049fd43cf6eSHans Rosenfeld #endif
6050fd43cf6eSHans Rosenfeld 	error = ops->add_node(sc, &node, 1);
6051fd43cf6eSHans Rosenfeld 	if (error != 0) {
6052fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6053fd43cf6eSHans Rosenfeld 		    "!could not add BSS node");
6054fd43cf6eSHans Rosenfeld 		return error;
6055fd43cf6eSHans Rosenfeld 	}
6056fd43cf6eSHans Rosenfeld 	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
6057fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6058fd43cf6eSHans Rosenfeld 		    "!could not setup link quality for node %d", node.id);
6059fd43cf6eSHans Rosenfeld 		return error;
6060fd43cf6eSHans Rosenfeld 	}
6061fd43cf6eSHans Rosenfeld 
6062fd43cf6eSHans Rosenfeld 	if ((error = iwn_init_sensitivity(sc)) != 0) {
6063fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6064fd43cf6eSHans Rosenfeld 		    "!could not set sensitivity");
6065fd43cf6eSHans Rosenfeld 		return error;
6066fd43cf6eSHans Rosenfeld 	}
6067fd43cf6eSHans Rosenfeld 
6068fd43cf6eSHans Rosenfeld 	if ((error = iwn_qosparam_to_hw(sc, 1)) != 0) {
6069fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6070fd43cf6eSHans Rosenfeld 		    "!could not set QoS params");
6071fd43cf6eSHans Rosenfeld 		return (error);
6072fd43cf6eSHans Rosenfeld 	}
6073fd43cf6eSHans Rosenfeld 
6074fd43cf6eSHans Rosenfeld 	/* Start periodic calibration timer. */
6075fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_STOP_CALIB_TO;
6076fd43cf6eSHans Rosenfeld 	sc->calib.state = IWN_CALIB_STATE_ASSOC;
6077fd43cf6eSHans Rosenfeld 	sc->calib_cnt = 0;
6078fd43cf6eSHans Rosenfeld 	sc->calib_to = timeout(iwn_calib_timeout, sc, drv_usectohz(500000));
6079fd43cf6eSHans Rosenfeld 
6080fd43cf6eSHans Rosenfeld 	/* Link LED always on while associated. */
6081fd43cf6eSHans Rosenfeld 	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
6082fd43cf6eSHans Rosenfeld 	return 0;
6083fd43cf6eSHans Rosenfeld }
6084fd43cf6eSHans Rosenfeld 
6085fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO
6086fd43cf6eSHans Rosenfeld /*
6087fd43cf6eSHans Rosenfeld  * We support CCMP hardware encryption/decryption of unicast frames only.
6088fd43cf6eSHans Rosenfeld  * HW support for TKIP really sucks.  We should let TKIP die anyway.
6089fd43cf6eSHans Rosenfeld  */
6090fd43cf6eSHans Rosenfeld static int
iwn_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)6091fd43cf6eSHans Rosenfeld iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
6092fd43cf6eSHans Rosenfeld     struct ieee80211_key *k)
6093fd43cf6eSHans Rosenfeld {
6094fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6095fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6096fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6097fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
6098fd43cf6eSHans Rosenfeld 	uint16_t kflags;
6099fd43cf6eSHans Rosenfeld 
6100fd43cf6eSHans Rosenfeld 	if ((k->k_flags & IEEE80211_KEY_GROUP) ||
6101fd43cf6eSHans Rosenfeld 	    k->k_cipher != IEEE80211_CIPHER_CCMP)
6102fd43cf6eSHans Rosenfeld 		return ieee80211_set_key(ic, ni, k);
6103fd43cf6eSHans Rosenfeld 
6104fd43cf6eSHans Rosenfeld 	kflags = IWN_KFLAG_CCMP | IWN_KFLAG_MAP | IWN_KFLAG_KID(k->k_id);
6105fd43cf6eSHans Rosenfeld 	if (k->k_flags & IEEE80211_KEY_GROUP)
6106fd43cf6eSHans Rosenfeld 		kflags |= IWN_KFLAG_GROUP;
6107fd43cf6eSHans Rosenfeld 
6108fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6109fd43cf6eSHans Rosenfeld 	node.id = (k->k_flags & IEEE80211_KEY_GROUP) ?
6110fd43cf6eSHans Rosenfeld 	    sc->broadcast_id : wn->id;
6111fd43cf6eSHans Rosenfeld 	node.control = IWN_NODE_UPDATE;
6112fd43cf6eSHans Rosenfeld 	node.flags = IWN_FLAG_SET_KEY;
6113fd43cf6eSHans Rosenfeld 	node.kflags = htole16(kflags);
6114fd43cf6eSHans Rosenfeld 	node.kid = k->k_id;
6115fd43cf6eSHans Rosenfeld 	memcpy(node.key, k->k_key, k->k_len);
6116fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(set__key, int, k->k_id, int, node.id);
6117fd43cf6eSHans Rosenfeld 	return ops->add_node(sc, &node, 1);
6118fd43cf6eSHans Rosenfeld }
6119fd43cf6eSHans Rosenfeld 
6120fd43cf6eSHans Rosenfeld static void
iwn_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)6121fd43cf6eSHans Rosenfeld iwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
6122fd43cf6eSHans Rosenfeld     struct ieee80211_key *k)
6123fd43cf6eSHans Rosenfeld {
6124fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6125fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6126fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6127fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
6128fd43cf6eSHans Rosenfeld 
6129fd43cf6eSHans Rosenfeld 	if ((k->k_flags & IEEE80211_KEY_GROUP) ||
6130fd43cf6eSHans Rosenfeld 	    k->k_cipher != IEEE80211_CIPHER_CCMP) {
6131fd43cf6eSHans Rosenfeld 		/* See comment about other ciphers above. */
6132fd43cf6eSHans Rosenfeld 		ieee80211_delete_key(ic, ni, k);
6133fd43cf6eSHans Rosenfeld 		return;
6134fd43cf6eSHans Rosenfeld 	}
6135fd43cf6eSHans Rosenfeld 	if (ic->ic_state != IEEE80211_S_RUN)
6136fd43cf6eSHans Rosenfeld 		return;	/* Nothing to do. */
6137fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6138fd43cf6eSHans Rosenfeld 	node.id = (k->k_flags & IEEE80211_KEY_GROUP) ?
6139fd43cf6eSHans Rosenfeld 	    sc->broadcast_id : wn->id;
6140fd43cf6eSHans Rosenfeld 	node.control = IWN_NODE_UPDATE;
6141fd43cf6eSHans Rosenfeld 	node.flags = IWN_FLAG_SET_KEY;
6142fd43cf6eSHans Rosenfeld 	node.kflags = htole16(IWN_KFLAG_INVALID);
6143fd43cf6eSHans Rosenfeld 	node.kid = 0xff;
6144fd43cf6eSHans Rosenfeld 	DTRACE_PROBE1(del__key, int, node.id);
6145fd43cf6eSHans Rosenfeld 	(void)ops->add_node(sc, &node, 1);
6146fd43cf6eSHans Rosenfeld }
6147fd43cf6eSHans Rosenfeld #endif
6148fd43cf6eSHans Rosenfeld 
6149fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT
6150fd43cf6eSHans Rosenfeld /*
6151fd43cf6eSHans Rosenfeld  * This function is called by upper layer when an ADDBA request is received
6152fd43cf6eSHans Rosenfeld  * from another STA and before the ADDBA response is sent.
6153fd43cf6eSHans Rosenfeld  */
6154fd43cf6eSHans Rosenfeld static int
iwn_ampdu_rx_start(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)6155fd43cf6eSHans Rosenfeld iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
6156fd43cf6eSHans Rosenfeld     uint8_t tid)
6157fd43cf6eSHans Rosenfeld {
6158fd43cf6eSHans Rosenfeld 	struct ieee80211_rx_ba *ba = &ni->in_rx_ba[tid];
6159fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6160fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6161fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6162fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
6163fd43cf6eSHans Rosenfeld 
6164fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6165fd43cf6eSHans Rosenfeld 	node.id = wn->id;
6166fd43cf6eSHans Rosenfeld 	node.control = IWN_NODE_UPDATE;
6167fd43cf6eSHans Rosenfeld 	node.flags = IWN_FLAG_SET_ADDBA;
6168fd43cf6eSHans Rosenfeld 	node.addba_tid = tid;
6169fd43cf6eSHans Rosenfeld 	node.addba_ssn = htole16(ba->ba_winstart);
6170fd43cf6eSHans Rosenfeld 	DTRACE_PROBE3(addba, uint8_t, wn->id, uint8_t, tid, int, ba->ba_winstart);
6171fd43cf6eSHans Rosenfeld 	return ops->add_node(sc, &node, 1);
6172fd43cf6eSHans Rosenfeld }
6173fd43cf6eSHans Rosenfeld 
6174fd43cf6eSHans Rosenfeld /*
6175fd43cf6eSHans Rosenfeld  * This function is called by upper layer on teardown of an HT-immediate
6176fd43cf6eSHans Rosenfeld  * Block Ack agreement (eg. uppon receipt of a DELBA frame).
6177fd43cf6eSHans Rosenfeld  */
6178fd43cf6eSHans Rosenfeld static void
iwn_ampdu_rx_stop(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)6179fd43cf6eSHans Rosenfeld iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
6180fd43cf6eSHans Rosenfeld     uint8_t tid)
6181fd43cf6eSHans Rosenfeld {
6182fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6183fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6184fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6185fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
6186fd43cf6eSHans Rosenfeld 
6187fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6188fd43cf6eSHans Rosenfeld 	node.id = wn->id;
6189fd43cf6eSHans Rosenfeld 	node.control = IWN_NODE_UPDATE;
6190fd43cf6eSHans Rosenfeld 	node.flags = IWN_FLAG_SET_DELBA;
6191fd43cf6eSHans Rosenfeld 	node.delba_tid = tid;
6192fd43cf6eSHans Rosenfeld 	DTRACE_PROBE2(delba, uint8_t, wn->id, uint8_t, tid);
6193fd43cf6eSHans Rosenfeld 	(void)ops->add_node(sc, &node, 1);
6194fd43cf6eSHans Rosenfeld }
6195fd43cf6eSHans Rosenfeld 
6196fd43cf6eSHans Rosenfeld /*
6197fd43cf6eSHans Rosenfeld  * This function is called by upper layer when an ADDBA response is received
6198fd43cf6eSHans Rosenfeld  * from another STA.
6199fd43cf6eSHans Rosenfeld  */
6200fd43cf6eSHans Rosenfeld static int
iwn_ampdu_tx_start(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)6201fd43cf6eSHans Rosenfeld iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
6202fd43cf6eSHans Rosenfeld     uint8_t tid)
6203fd43cf6eSHans Rosenfeld {
6204fd43cf6eSHans Rosenfeld 	struct ieee80211_tx_ba *ba = &ni->in_tx_ba[tid];
6205fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6206fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6207fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6208fd43cf6eSHans Rosenfeld 	struct iwn_node_info node;
6209fd43cf6eSHans Rosenfeld 	int error;
6210fd43cf6eSHans Rosenfeld 
6211fd43cf6eSHans Rosenfeld 	/* Enable TX for the specified RA/TID. */
6212fd43cf6eSHans Rosenfeld 	wn->disable_tid &= ~(1 << tid);
6213fd43cf6eSHans Rosenfeld 	memset(&node, 0, sizeof node);
6214fd43cf6eSHans Rosenfeld 	node.id = wn->id;
6215fd43cf6eSHans Rosenfeld 	node.control = IWN_NODE_UPDATE;
6216fd43cf6eSHans Rosenfeld 	node.flags = IWN_FLAG_SET_DISABLE_TID;
6217fd43cf6eSHans Rosenfeld 	node.disable_tid = htole16(wn->disable_tid);
6218fd43cf6eSHans Rosenfeld 	error = ops->add_node(sc, &node, 1);
6219fd43cf6eSHans Rosenfeld 	if (error != 0)
6220fd43cf6eSHans Rosenfeld 		return error;
6221fd43cf6eSHans Rosenfeld 
6222fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6223fd43cf6eSHans Rosenfeld 		return error;
6224fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_start(sc, ni, tid, ba->ba_winstart);
6225fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6226fd43cf6eSHans Rosenfeld 	return 0;
6227fd43cf6eSHans Rosenfeld }
6228fd43cf6eSHans Rosenfeld 
6229fd43cf6eSHans Rosenfeld static void
iwn_ampdu_tx_stop(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)6230fd43cf6eSHans Rosenfeld iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
6231fd43cf6eSHans Rosenfeld     uint8_t tid)
6232fd43cf6eSHans Rosenfeld {
6233fd43cf6eSHans Rosenfeld 	struct ieee80211_tx_ba *ba = &ni->in_tx_ba[tid];
6234fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc = ic->ic_softc;
6235fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
6236fd43cf6eSHans Rosenfeld 
6237fd43cf6eSHans Rosenfeld 	if (iwn_nic_lock(sc) != 0)
6238fd43cf6eSHans Rosenfeld 		return;
6239fd43cf6eSHans Rosenfeld 	ops->ampdu_tx_stop(sc, tid, ba->ba_winstart);
6240fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6241fd43cf6eSHans Rosenfeld }
6242fd43cf6eSHans Rosenfeld 
6243fd43cf6eSHans Rosenfeld static void
iwn4965_ampdu_tx_start(struct iwn_softc * sc,struct ieee80211_node * ni,uint8_t tid,uint16_t ssn)6244fd43cf6eSHans Rosenfeld iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6245fd43cf6eSHans Rosenfeld     uint8_t tid, uint16_t ssn)
6246fd43cf6eSHans Rosenfeld {
6247fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6248fd43cf6eSHans Rosenfeld 	int qid = 7 + tid;
6249fd43cf6eSHans Rosenfeld 
6250fd43cf6eSHans Rosenfeld 	/* Stop TX scheduler while we're changing its configuration. */
6251fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6252fd43cf6eSHans Rosenfeld 	    IWN4965_TXQ_STATUS_CHGACT);
6253fd43cf6eSHans Rosenfeld 
6254fd43cf6eSHans Rosenfeld 	/* Assign RA/TID translation to the queue. */
6255fd43cf6eSHans Rosenfeld 	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
6256fd43cf6eSHans Rosenfeld 	    wn->id << 4 | tid);
6257fd43cf6eSHans Rosenfeld 
6258fd43cf6eSHans Rosenfeld 	/* Enable chain-building mode for the queue. */
6259fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
6260fd43cf6eSHans Rosenfeld 
6261fd43cf6eSHans Rosenfeld 	/* Set starting sequence number from the ADDBA request. */
6262fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6263fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6264fd43cf6eSHans Rosenfeld 
6265fd43cf6eSHans Rosenfeld 	/* Set scheduler window size. */
6266fd43cf6eSHans Rosenfeld 	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
6267fd43cf6eSHans Rosenfeld 	    IWN_SCHED_WINSZ);
6268fd43cf6eSHans Rosenfeld 	/* Set scheduler frame limit. */
6269fd43cf6eSHans Rosenfeld 	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6270fd43cf6eSHans Rosenfeld 	    IWN_SCHED_LIMIT << 16);
6271fd43cf6eSHans Rosenfeld 
6272fd43cf6eSHans Rosenfeld 	/* Enable interrupts for the queue. */
6273fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6274fd43cf6eSHans Rosenfeld 
6275fd43cf6eSHans Rosenfeld 	/* Mark the queue as active. */
6276fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6277fd43cf6eSHans Rosenfeld 	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
6278fd43cf6eSHans Rosenfeld 	    iwn_tid2fifo[tid] << 1);
6279fd43cf6eSHans Rosenfeld }
6280fd43cf6eSHans Rosenfeld 
6281fd43cf6eSHans Rosenfeld static void
iwn4965_ampdu_tx_stop(struct iwn_softc * sc,uint8_t tid,uint16_t ssn)6282fd43cf6eSHans Rosenfeld iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
6283fd43cf6eSHans Rosenfeld {
6284fd43cf6eSHans Rosenfeld 	int qid = 7 + tid;
6285fd43cf6eSHans Rosenfeld 
6286fd43cf6eSHans Rosenfeld 	/* Stop TX scheduler while we're changing its configuration. */
6287fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6288fd43cf6eSHans Rosenfeld 	    IWN4965_TXQ_STATUS_CHGACT);
6289fd43cf6eSHans Rosenfeld 
6290fd43cf6eSHans Rosenfeld 	/* Set starting sequence number from the ADDBA request. */
6291fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6292fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6293fd43cf6eSHans Rosenfeld 
6294fd43cf6eSHans Rosenfeld 	/* Disable interrupts for the queue. */
6295fd43cf6eSHans Rosenfeld 	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6296fd43cf6eSHans Rosenfeld 
6297fd43cf6eSHans Rosenfeld 	/* Mark the queue as inactive. */
6298fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6299fd43cf6eSHans Rosenfeld 	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
6300fd43cf6eSHans Rosenfeld }
6301fd43cf6eSHans Rosenfeld 
6302fd43cf6eSHans Rosenfeld static void
iwn5000_ampdu_tx_start(struct iwn_softc * sc,struct ieee80211_node * ni,uint8_t tid,uint16_t ssn)6303fd43cf6eSHans Rosenfeld iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6304fd43cf6eSHans Rosenfeld     uint8_t tid, uint16_t ssn)
6305fd43cf6eSHans Rosenfeld {
6306fd43cf6eSHans Rosenfeld 	struct iwn_node *wn = (void *)ni;
6307fd43cf6eSHans Rosenfeld 	int qid = 10 + tid;
6308fd43cf6eSHans Rosenfeld 
6309fd43cf6eSHans Rosenfeld 	/* Stop TX scheduler while we're changing its configuration. */
6310fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6311fd43cf6eSHans Rosenfeld 	    IWN5000_TXQ_STATUS_CHGACT);
6312fd43cf6eSHans Rosenfeld 
6313fd43cf6eSHans Rosenfeld 	/* Assign RA/TID translation to the queue. */
6314fd43cf6eSHans Rosenfeld 	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
6315fd43cf6eSHans Rosenfeld 	    wn->id << 4 | tid);
6316fd43cf6eSHans Rosenfeld 
6317fd43cf6eSHans Rosenfeld 	/* Enable chain-building mode for the queue. */
6318fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
6319fd43cf6eSHans Rosenfeld 
6320fd43cf6eSHans Rosenfeld 	/* Enable aggregation for the queue. */
6321fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6322fd43cf6eSHans Rosenfeld 
6323fd43cf6eSHans Rosenfeld 	/* Set starting sequence number from the ADDBA request. */
6324fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6325fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6326fd43cf6eSHans Rosenfeld 
6327fd43cf6eSHans Rosenfeld 	/* Set scheduler window size and frame limit. */
6328fd43cf6eSHans Rosenfeld 	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6329fd43cf6eSHans Rosenfeld 	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6330fd43cf6eSHans Rosenfeld 
6331fd43cf6eSHans Rosenfeld 	/* Enable interrupts for the queue. */
6332fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6333fd43cf6eSHans Rosenfeld 
6334fd43cf6eSHans Rosenfeld 	/* Mark the queue as active. */
6335fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6336fd43cf6eSHans Rosenfeld 	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
6337fd43cf6eSHans Rosenfeld }
6338fd43cf6eSHans Rosenfeld 
6339fd43cf6eSHans Rosenfeld static void
iwn5000_ampdu_tx_stop(struct iwn_softc * sc,uint8_t tid,uint16_t ssn)6340fd43cf6eSHans Rosenfeld iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
6341fd43cf6eSHans Rosenfeld {
6342fd43cf6eSHans Rosenfeld 	int qid = 10 + tid;
6343fd43cf6eSHans Rosenfeld 
6344fd43cf6eSHans Rosenfeld 	/* Stop TX scheduler while we're changing its configuration. */
6345fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6346fd43cf6eSHans Rosenfeld 	    IWN5000_TXQ_STATUS_CHGACT);
6347fd43cf6eSHans Rosenfeld 
6348fd43cf6eSHans Rosenfeld 	/* Disable aggregation for the queue. */
6349fd43cf6eSHans Rosenfeld 	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6350fd43cf6eSHans Rosenfeld 
6351fd43cf6eSHans Rosenfeld 	/* Set starting sequence number from the ADDBA request. */
6352fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6353fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6354fd43cf6eSHans Rosenfeld 
6355fd43cf6eSHans Rosenfeld 	/* Disable interrupts for the queue. */
6356fd43cf6eSHans Rosenfeld 	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6357fd43cf6eSHans Rosenfeld 
6358fd43cf6eSHans Rosenfeld 	/* Mark the queue as inactive. */
6359fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6360fd43cf6eSHans Rosenfeld 	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
6361fd43cf6eSHans Rosenfeld }
6362fd43cf6eSHans Rosenfeld #endif	/* !IEEE80211_NO_HT */
6363fd43cf6eSHans Rosenfeld 
6364fd43cf6eSHans Rosenfeld /*
6365fd43cf6eSHans Rosenfeld  * Query calibration tables from the initialization firmware.  We do this
6366fd43cf6eSHans Rosenfeld  * only once at first boot.  Called from a process context.
6367fd43cf6eSHans Rosenfeld  */
6368fd43cf6eSHans Rosenfeld static int
iwn5000_query_calibration(struct iwn_softc * sc)6369fd43cf6eSHans Rosenfeld iwn5000_query_calibration(struct iwn_softc *sc)
6370fd43cf6eSHans Rosenfeld {
6371fd43cf6eSHans Rosenfeld 	struct iwn5000_calib_config cmd;
6372fd43cf6eSHans Rosenfeld 	int error;
6373fd43cf6eSHans Rosenfeld 	clock_t clk;
6374fd43cf6eSHans Rosenfeld 
6375fd43cf6eSHans Rosenfeld 	ASSERT(mutex_owned(&sc->sc_mtx));
6376fd43cf6eSHans Rosenfeld 
6377fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
6378fd43cf6eSHans Rosenfeld 	cmd.ucode.once.enable = 0xffffffff;
6379fd43cf6eSHans Rosenfeld 	cmd.ucode.once.start  = 0xffffffff;
6380fd43cf6eSHans Rosenfeld 	cmd.ucode.once.send   = 0xffffffff;
6381fd43cf6eSHans Rosenfeld 	cmd.ucode.flags       = 0xffffffff;
6382fd43cf6eSHans Rosenfeld 	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
6383fd43cf6eSHans Rosenfeld 	if (error != 0)
6384fd43cf6eSHans Rosenfeld 		return error;
6385fd43cf6eSHans Rosenfeld 
6386fd43cf6eSHans Rosenfeld 	/* Wait at most two seconds for calibration to complete. */
6387fd43cf6eSHans Rosenfeld 	clk = ddi_get_lbolt() + drv_usectohz(2000000);
6388fd43cf6eSHans Rosenfeld 	while (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
6389fd43cf6eSHans Rosenfeld 		if (cv_timedwait(&sc->sc_calib_cv, &sc->sc_mtx, clk) < 0)
6390fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
6391fd43cf6eSHans Rosenfeld 
6392fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
6393fd43cf6eSHans Rosenfeld }
6394fd43cf6eSHans Rosenfeld 
6395fd43cf6eSHans Rosenfeld /*
6396fd43cf6eSHans Rosenfeld  * Send calibration results to the runtime firmware.  These results were
6397fd43cf6eSHans Rosenfeld  * obtained on first boot from the initialization firmware.
6398fd43cf6eSHans Rosenfeld  */
6399fd43cf6eSHans Rosenfeld static int
iwn5000_send_calibration(struct iwn_softc * sc)6400fd43cf6eSHans Rosenfeld iwn5000_send_calibration(struct iwn_softc *sc)
6401fd43cf6eSHans Rosenfeld {
6402fd43cf6eSHans Rosenfeld 	int idx, error;
6403fd43cf6eSHans Rosenfeld 
6404fd43cf6eSHans Rosenfeld 	for (idx = 0; idx < 5; idx++) {
6405fd43cf6eSHans Rosenfeld 		if (sc->calibcmd[idx].buf == NULL)
6406fd43cf6eSHans Rosenfeld 			continue;	/* No results available. */
6407fd43cf6eSHans Rosenfeld 		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
6408fd43cf6eSHans Rosenfeld 		    sc->calibcmd[idx].len, 0);
6409fd43cf6eSHans Rosenfeld 		if (error != 0) {
6410fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
6411fd43cf6eSHans Rosenfeld 			    "!could not send calibration result");
6412fd43cf6eSHans Rosenfeld 			return error;
6413fd43cf6eSHans Rosenfeld 		}
6414fd43cf6eSHans Rosenfeld 	}
6415fd43cf6eSHans Rosenfeld 	return 0;
6416fd43cf6eSHans Rosenfeld }
6417fd43cf6eSHans Rosenfeld 
6418fd43cf6eSHans Rosenfeld static int
iwn5000_send_wimax_coex(struct iwn_softc * sc)6419fd43cf6eSHans Rosenfeld iwn5000_send_wimax_coex(struct iwn_softc *sc)
6420fd43cf6eSHans Rosenfeld {
6421fd43cf6eSHans Rosenfeld 	struct iwn5000_wimax_coex wimax;
6422fd43cf6eSHans Rosenfeld 
6423fd43cf6eSHans Rosenfeld #ifdef notyet
6424fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
6425fd43cf6eSHans Rosenfeld 		/* Enable WiMAX coexistence for combo adapters. */
6426fd43cf6eSHans Rosenfeld 		wimax.flags =
6427fd43cf6eSHans Rosenfeld 		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
6428fd43cf6eSHans Rosenfeld 		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
6429fd43cf6eSHans Rosenfeld 		    IWN_WIMAX_COEX_STA_TABLE_VALID |
6430fd43cf6eSHans Rosenfeld 		    IWN_WIMAX_COEX_ENABLE;
6431fd43cf6eSHans Rosenfeld 		memcpy(wimax.events, iwn6050_wimax_events,
6432fd43cf6eSHans Rosenfeld 		    sizeof iwn6050_wimax_events);
6433fd43cf6eSHans Rosenfeld 	} else
6434fd43cf6eSHans Rosenfeld #endif
6435fd43cf6eSHans Rosenfeld 	{
6436fd43cf6eSHans Rosenfeld 		/* Disable WiMAX coexistence. */
6437fd43cf6eSHans Rosenfeld 		wimax.flags = 0;
6438fd43cf6eSHans Rosenfeld 		memset(wimax.events, 0, sizeof wimax.events);
6439fd43cf6eSHans Rosenfeld 	}
6440fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
6441fd43cf6eSHans Rosenfeld }
6442fd43cf6eSHans Rosenfeld 
6443fd43cf6eSHans Rosenfeld static int
iwn6000_temp_offset_calib(struct iwn_softc * sc)6444fd43cf6eSHans Rosenfeld iwn6000_temp_offset_calib(struct iwn_softc *sc)
6445fd43cf6eSHans Rosenfeld {
6446fd43cf6eSHans Rosenfeld 	struct iwn6000_phy_calib_temp_offset cmd;
6447fd43cf6eSHans Rosenfeld 
6448fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
6449fd43cf6eSHans Rosenfeld 	cmd.code = IWN6000_PHY_CALIB_TEMP_OFFSET;
6450fd43cf6eSHans Rosenfeld 	cmd.ngroups = 1;
6451fd43cf6eSHans Rosenfeld 	cmd.isvalid = 1;
6452fd43cf6eSHans Rosenfeld 	if (sc->eeprom_temp != 0)
6453fd43cf6eSHans Rosenfeld 		cmd.offset = htole16(sc->eeprom_temp);
6454fd43cf6eSHans Rosenfeld 	else
6455fd43cf6eSHans Rosenfeld 		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
6456fd43cf6eSHans Rosenfeld 	sc->sc_toff.t6000->toff.value.l = le16toh(cmd.offset);
6457fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6458fd43cf6eSHans Rosenfeld }
6459fd43cf6eSHans Rosenfeld 
6460fd43cf6eSHans Rosenfeld static int
iwn2000_temp_offset_calib(struct iwn_softc * sc)6461fd43cf6eSHans Rosenfeld iwn2000_temp_offset_calib(struct iwn_softc *sc)
6462fd43cf6eSHans Rosenfeld {
6463fd43cf6eSHans Rosenfeld 	struct iwn2000_phy_calib_temp_offset cmd;
6464fd43cf6eSHans Rosenfeld 
6465fd43cf6eSHans Rosenfeld 	memset(&cmd, 0, sizeof cmd);
6466fd43cf6eSHans Rosenfeld 	cmd.code = IWN2000_PHY_CALIB_TEMP_OFFSET;
6467fd43cf6eSHans Rosenfeld 	cmd.ngroups = 1;
6468fd43cf6eSHans Rosenfeld 	cmd.isvalid = 1;
6469fd43cf6eSHans Rosenfeld 	if (sc->eeprom_rawtemp != 0) {
6470fd43cf6eSHans Rosenfeld 		cmd.offset_low = htole16(sc->eeprom_rawtemp);
6471fd43cf6eSHans Rosenfeld 		cmd.offset_high = htole16(sc->eeprom_temp);
6472fd43cf6eSHans Rosenfeld 	} else {
6473fd43cf6eSHans Rosenfeld 		cmd.offset_low = htole16(IWN_DEFAULT_TEMP_OFFSET);
6474fd43cf6eSHans Rosenfeld 		cmd.offset_high = htole16(IWN_DEFAULT_TEMP_OFFSET);
6475fd43cf6eSHans Rosenfeld 	}
6476fd43cf6eSHans Rosenfeld 	cmd.burnt_voltage_ref = htole16(sc->eeprom_voltage);
6477fd43cf6eSHans Rosenfeld 	sc->sc_toff.t2000->toff_lo.value.l = le16toh(cmd.offset_low);
6478fd43cf6eSHans Rosenfeld 	sc->sc_toff.t2000->toff_hi.value.l = le16toh(cmd.offset_high);
6479fd43cf6eSHans Rosenfeld 	sc->sc_toff.t2000->volt.value.l = le16toh(cmd.burnt_voltage_ref);
6480fd43cf6eSHans Rosenfeld 
6481fd43cf6eSHans Rosenfeld 	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6482fd43cf6eSHans Rosenfeld }
6483fd43cf6eSHans Rosenfeld 
6484fd43cf6eSHans Rosenfeld /*
6485fd43cf6eSHans Rosenfeld  * This function is called after the runtime firmware notifies us of its
6486fd43cf6eSHans Rosenfeld  * readiness (called in a process context).
6487fd43cf6eSHans Rosenfeld  */
6488fd43cf6eSHans Rosenfeld static int
iwn4965_post_alive(struct iwn_softc * sc)6489fd43cf6eSHans Rosenfeld iwn4965_post_alive(struct iwn_softc *sc)
6490fd43cf6eSHans Rosenfeld {
6491fd43cf6eSHans Rosenfeld 	int error, qid;
6492fd43cf6eSHans Rosenfeld 
6493fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6494fd43cf6eSHans Rosenfeld 		return error;
6495fd43cf6eSHans Rosenfeld 
6496fd43cf6eSHans Rosenfeld 	/* Clear TX scheduler state in SRAM. */
6497fd43cf6eSHans Rosenfeld 	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6498fd43cf6eSHans Rosenfeld 	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
6499fd43cf6eSHans Rosenfeld 	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
6500fd43cf6eSHans Rosenfeld 
6501fd43cf6eSHans Rosenfeld 	/* Set physical address of TX scheduler rings (1KB aligned). */
6502fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6503fd43cf6eSHans Rosenfeld 
6504fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6505fd43cf6eSHans Rosenfeld 
6506fd43cf6eSHans Rosenfeld 	/* Disable chain mode for all our 16 queues. */
6507fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
6508fd43cf6eSHans Rosenfeld 
6509fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
6510fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
6511fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6512fd43cf6eSHans Rosenfeld 
6513fd43cf6eSHans Rosenfeld 		/* Set scheduler window size. */
6514fd43cf6eSHans Rosenfeld 		iwn_mem_write(sc, sc->sched_base +
6515fd43cf6eSHans Rosenfeld 		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
6516fd43cf6eSHans Rosenfeld 		/* Set scheduler frame limit. */
6517fd43cf6eSHans Rosenfeld 		iwn_mem_write(sc, sc->sched_base +
6518fd43cf6eSHans Rosenfeld 		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6519fd43cf6eSHans Rosenfeld 		    IWN_SCHED_LIMIT << 16);
6520fd43cf6eSHans Rosenfeld 	}
6521fd43cf6eSHans Rosenfeld 
6522fd43cf6eSHans Rosenfeld 	/* Enable interrupts for all our 16 queues. */
6523fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
6524fd43cf6eSHans Rosenfeld 	/* Identify TX FIFO rings (0-7). */
6525fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
6526fd43cf6eSHans Rosenfeld 
6527fd43cf6eSHans Rosenfeld 	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6528fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < 7; qid++) {
6529fd43cf6eSHans Rosenfeld 		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
6530fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6531fd43cf6eSHans Rosenfeld 		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
6532fd43cf6eSHans Rosenfeld 	}
6533fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6534fd43cf6eSHans Rosenfeld 	return 0;
6535fd43cf6eSHans Rosenfeld }
6536fd43cf6eSHans Rosenfeld 
6537fd43cf6eSHans Rosenfeld /*
6538fd43cf6eSHans Rosenfeld  * This function is called after the initialization or runtime firmware
6539fd43cf6eSHans Rosenfeld  * notifies us of its readiness (called in a process context).
6540fd43cf6eSHans Rosenfeld  */
6541fd43cf6eSHans Rosenfeld static int
iwn5000_post_alive(struct iwn_softc * sc)6542fd43cf6eSHans Rosenfeld iwn5000_post_alive(struct iwn_softc *sc)
6543fd43cf6eSHans Rosenfeld {
6544fd43cf6eSHans Rosenfeld 	int error, qid;
6545fd43cf6eSHans Rosenfeld 
6546fd43cf6eSHans Rosenfeld 	/* Switch to using ICT interrupt mode. */
6547fd43cf6eSHans Rosenfeld 	iwn5000_ict_reset(sc);
6548fd43cf6eSHans Rosenfeld 
6549fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6550fd43cf6eSHans Rosenfeld 		return error;
6551fd43cf6eSHans Rosenfeld 
6552fd43cf6eSHans Rosenfeld 	/* Clear TX scheduler state in SRAM. */
6553fd43cf6eSHans Rosenfeld 	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6554fd43cf6eSHans Rosenfeld 	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
6555fd43cf6eSHans Rosenfeld 	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
6556fd43cf6eSHans Rosenfeld 
6557fd43cf6eSHans Rosenfeld 	/* Set physical address of TX scheduler rings (1KB aligned). */
6558fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6559fd43cf6eSHans Rosenfeld 
6560fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6561fd43cf6eSHans Rosenfeld 
6562fd43cf6eSHans Rosenfeld 	/* Enable chain mode for all queues, except command queue. */
6563fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
6564fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
6565fd43cf6eSHans Rosenfeld 
6566fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
6567fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
6568fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6569fd43cf6eSHans Rosenfeld 
6570fd43cf6eSHans Rosenfeld 		iwn_mem_write(sc, sc->sched_base +
6571fd43cf6eSHans Rosenfeld 		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
6572fd43cf6eSHans Rosenfeld 		/* Set scheduler window size and frame limit. */
6573fd43cf6eSHans Rosenfeld 		iwn_mem_write(sc, sc->sched_base +
6574fd43cf6eSHans Rosenfeld 		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6575fd43cf6eSHans Rosenfeld 		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6576fd43cf6eSHans Rosenfeld 	}
6577fd43cf6eSHans Rosenfeld 
6578fd43cf6eSHans Rosenfeld 	/* Enable interrupts for all our 20 queues. */
6579fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
6580fd43cf6eSHans Rosenfeld 	/* Identify TX FIFO rings (0-7). */
6581fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
6582fd43cf6eSHans Rosenfeld 
6583fd43cf6eSHans Rosenfeld 	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6584fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < 7; qid++) {
6585fd43cf6eSHans Rosenfeld 		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
6586fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6587fd43cf6eSHans Rosenfeld 		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
6588fd43cf6eSHans Rosenfeld 	}
6589fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6590fd43cf6eSHans Rosenfeld 
6591fd43cf6eSHans Rosenfeld 	/* Configure WiMAX coexistence for combo adapters. */
6592fd43cf6eSHans Rosenfeld 	error = iwn5000_send_wimax_coex(sc);
6593fd43cf6eSHans Rosenfeld 	if (error != 0) {
6594fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6595fd43cf6eSHans Rosenfeld 		    "!could not configure WiMAX coexistence");
6596fd43cf6eSHans Rosenfeld 		return error;
6597fd43cf6eSHans Rosenfeld 	}
6598fd43cf6eSHans Rosenfeld 	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
6599fd43cf6eSHans Rosenfeld 		struct iwn5000_phy_calib_crystal cmd;
6600fd43cf6eSHans Rosenfeld 
6601fd43cf6eSHans Rosenfeld 		/* Perform crystal calibration. */
6602fd43cf6eSHans Rosenfeld 		memset(&cmd, 0, sizeof cmd);
6603fd43cf6eSHans Rosenfeld 		cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
6604fd43cf6eSHans Rosenfeld 		cmd.ngroups = 1;
6605fd43cf6eSHans Rosenfeld 		cmd.isvalid = 1;
6606fd43cf6eSHans Rosenfeld 		cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
6607fd43cf6eSHans Rosenfeld 		cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
6608fd43cf6eSHans Rosenfeld 		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6609fd43cf6eSHans Rosenfeld 		if (error != 0) {
6610fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
6611fd43cf6eSHans Rosenfeld 			    "!crystal calibration failed");
6612fd43cf6eSHans Rosenfeld 			return error;
6613fd43cf6eSHans Rosenfeld 		}
6614fd43cf6eSHans Rosenfeld 	}
6615fd43cf6eSHans Rosenfeld 	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
6616fd43cf6eSHans Rosenfeld 		/* Query calibration from the initialization firmware. */
6617fd43cf6eSHans Rosenfeld 		if ((error = iwn5000_query_calibration(sc)) != 0) {
6618fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
6619fd43cf6eSHans Rosenfeld 			    "!could not query calibration");
6620fd43cf6eSHans Rosenfeld 			return error;
6621fd43cf6eSHans Rosenfeld 		}
6622fd43cf6eSHans Rosenfeld 		/*
6623fd43cf6eSHans Rosenfeld 		 * We have the calibration results now, reboot with the
6624fd43cf6eSHans Rosenfeld 		 * runtime firmware (call ourselves recursively!)
6625fd43cf6eSHans Rosenfeld 		 */
6626fd43cf6eSHans Rosenfeld 		iwn_hw_stop(sc, B_FALSE);
6627fd43cf6eSHans Rosenfeld 		error = iwn_hw_init(sc);
6628fd43cf6eSHans Rosenfeld 	} else {
6629fd43cf6eSHans Rosenfeld 		/* Send calibration results to runtime firmware. */
6630fd43cf6eSHans Rosenfeld 		error = iwn5000_send_calibration(sc);
6631fd43cf6eSHans Rosenfeld 	}
6632fd43cf6eSHans Rosenfeld 	return error;
6633fd43cf6eSHans Rosenfeld }
6634fd43cf6eSHans Rosenfeld 
6635fd43cf6eSHans Rosenfeld /*
6636fd43cf6eSHans Rosenfeld  * The firmware boot code is small and is intended to be copied directy into
6637fd43cf6eSHans Rosenfeld  * the NIC internal memory (no DMA transfer).
6638fd43cf6eSHans Rosenfeld  */
6639fd43cf6eSHans Rosenfeld static int
iwn4965_load_bootcode(struct iwn_softc * sc,const uint8_t * ucode,int size)6640fd43cf6eSHans Rosenfeld iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
6641fd43cf6eSHans Rosenfeld {
6642fd43cf6eSHans Rosenfeld 	int error, ntries;
6643fd43cf6eSHans Rosenfeld 
6644fd43cf6eSHans Rosenfeld 	size /= sizeof (uint32_t);
6645fd43cf6eSHans Rosenfeld 
6646fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6647fd43cf6eSHans Rosenfeld 		return error;
6648fd43cf6eSHans Rosenfeld 
6649fd43cf6eSHans Rosenfeld 	/* Copy microcode image into NIC memory. */
6650fd43cf6eSHans Rosenfeld 	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
6651fd43cf6eSHans Rosenfeld 	    /*LINTED: E_PTR_BAD_CAST_ALIGN*/
6652fd43cf6eSHans Rosenfeld 	    (const uint32_t *)ucode, size);
6653fd43cf6eSHans Rosenfeld 
6654fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
6655fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
6656fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
6657fd43cf6eSHans Rosenfeld 
6658fd43cf6eSHans Rosenfeld 	/* Start boot load now. */
6659fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
6660fd43cf6eSHans Rosenfeld 
6661fd43cf6eSHans Rosenfeld 	/* Wait for transfer to complete. */
6662fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 1000; ntries++) {
6663fd43cf6eSHans Rosenfeld 		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
6664fd43cf6eSHans Rosenfeld 		    IWN_BSM_WR_CTRL_START))
6665fd43cf6eSHans Rosenfeld 			break;
6666fd43cf6eSHans Rosenfeld 		DELAY(10);
6667fd43cf6eSHans Rosenfeld 	}
6668fd43cf6eSHans Rosenfeld 	if (ntries == 1000) {
6669fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6670fd43cf6eSHans Rosenfeld 		    "!could not load boot firmware");
6671fd43cf6eSHans Rosenfeld 		iwn_nic_unlock(sc);
6672fd43cf6eSHans Rosenfeld 		return ETIMEDOUT;
6673fd43cf6eSHans Rosenfeld 	}
6674fd43cf6eSHans Rosenfeld 
6675fd43cf6eSHans Rosenfeld 	/* Enable boot after power up. */
6676fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
6677fd43cf6eSHans Rosenfeld 
6678fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6679fd43cf6eSHans Rosenfeld 	return 0;
6680fd43cf6eSHans Rosenfeld }
6681fd43cf6eSHans Rosenfeld 
6682fd43cf6eSHans Rosenfeld static int
iwn4965_load_firmware(struct iwn_softc * sc)6683fd43cf6eSHans Rosenfeld iwn4965_load_firmware(struct iwn_softc *sc)
6684fd43cf6eSHans Rosenfeld {
6685fd43cf6eSHans Rosenfeld 	struct iwn_fw_info *fw = &sc->fw;
6686fd43cf6eSHans Rosenfeld 	struct iwn_dma_info *dma = &sc->fw_dma;
6687fd43cf6eSHans Rosenfeld 	int error;
6688fd43cf6eSHans Rosenfeld 	clock_t clk;
6689fd43cf6eSHans Rosenfeld 
6690fd43cf6eSHans Rosenfeld 	ASSERT(mutex_owned(&sc->sc_mtx));
6691fd43cf6eSHans Rosenfeld 
6692fd43cf6eSHans Rosenfeld 	/* Copy initialization sections into pre-allocated DMA-safe memory. */
6693fd43cf6eSHans Rosenfeld 	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
6694fd43cf6eSHans Rosenfeld 	memcpy((char *)dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6695fd43cf6eSHans Rosenfeld 	    fw->init.text, fw->init.textsz);
6696fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
6697fd43cf6eSHans Rosenfeld 
6698fd43cf6eSHans Rosenfeld 	/* Tell adapter where to find initialization sections. */
6699fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6700fd43cf6eSHans Rosenfeld 		return error;
6701fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6702fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
6703fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6704fd43cf6eSHans Rosenfeld 	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6705fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
6706fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6707fd43cf6eSHans Rosenfeld 
6708fd43cf6eSHans Rosenfeld 	/* Load firmware boot code. */
6709fd43cf6eSHans Rosenfeld 	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
6710fd43cf6eSHans Rosenfeld 	if (error != 0) {
6711fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6712fd43cf6eSHans Rosenfeld 		    "!could not load boot firmware");
6713fd43cf6eSHans Rosenfeld 		return error;
6714fd43cf6eSHans Rosenfeld 	}
6715fd43cf6eSHans Rosenfeld 	/* Now press "execute". */
6716fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_RESET, 0);
6717fd43cf6eSHans Rosenfeld 
6718fd43cf6eSHans Rosenfeld 	/* Wait at most one second for first alive notification. */
6719fd43cf6eSHans Rosenfeld 	clk = ddi_get_lbolt() + drv_usectohz(1000000);
6720fd43cf6eSHans Rosenfeld 	while ((sc->sc_flags & IWN_FLAG_FW_ALIVE) == 0) {
6721fd43cf6eSHans Rosenfeld 		if (cv_timedwait(&sc->sc_alive_cv, &sc->sc_mtx, clk) < 0) {
6722fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
6723fd43cf6eSHans Rosenfeld 			    "!timeout waiting for adapter to initialize");
6724fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
6725fd43cf6eSHans Rosenfeld 		}
6726fd43cf6eSHans Rosenfeld 	}
6727fd43cf6eSHans Rosenfeld 
6728fd43cf6eSHans Rosenfeld 	/* Retrieve current temperature for initial TX power calibration. */
6729fd43cf6eSHans Rosenfeld 	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
6730fd43cf6eSHans Rosenfeld 	sc->temp = iwn4965_get_temperature(sc);
6731fd43cf6eSHans Rosenfeld 	sc->sc_misc->temp.value.ul = sc->temp;
6732fd43cf6eSHans Rosenfeld 
6733fd43cf6eSHans Rosenfeld 	/* Copy runtime sections into pre-allocated DMA-safe memory. */
6734fd43cf6eSHans Rosenfeld 	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
6735fd43cf6eSHans Rosenfeld 	memcpy((char *)dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6736fd43cf6eSHans Rosenfeld 	    fw->main.text, fw->main.textsz);
6737fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
6738fd43cf6eSHans Rosenfeld 
6739fd43cf6eSHans Rosenfeld 	/* Tell adapter where to find runtime sections. */
6740fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6741fd43cf6eSHans Rosenfeld 		return error;
6742fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6743fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
6744fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6745fd43cf6eSHans Rosenfeld 	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6746fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
6747fd43cf6eSHans Rosenfeld 	    IWN_FW_UPDATED | fw->main.textsz);
6748fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6749fd43cf6eSHans Rosenfeld 
6750fd43cf6eSHans Rosenfeld 	return 0;
6751fd43cf6eSHans Rosenfeld }
6752fd43cf6eSHans Rosenfeld 
6753fd43cf6eSHans Rosenfeld static int
iwn5000_load_firmware_section(struct iwn_softc * sc,uint32_t dst,const uint8_t * section,int size)6754fd43cf6eSHans Rosenfeld iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
6755fd43cf6eSHans Rosenfeld     const uint8_t *section, int size)
6756fd43cf6eSHans Rosenfeld {
6757fd43cf6eSHans Rosenfeld 	struct iwn_dma_info *dma = &sc->fw_dma;
6758fd43cf6eSHans Rosenfeld 	int error;
6759fd43cf6eSHans Rosenfeld 	clock_t clk;
6760fd43cf6eSHans Rosenfeld 
6761fd43cf6eSHans Rosenfeld 	ASSERT(mutex_owned(&sc->sc_mtx));
6762fd43cf6eSHans Rosenfeld 
6763fd43cf6eSHans Rosenfeld 	/* Copy firmware section into pre-allocated DMA-safe memory. */
6764fd43cf6eSHans Rosenfeld 	memcpy(dma->vaddr, section, size);
6765fd43cf6eSHans Rosenfeld 	(void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV);
6766fd43cf6eSHans Rosenfeld 
6767fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
6768fd43cf6eSHans Rosenfeld 		return error;
6769fd43cf6eSHans Rosenfeld 
6770fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6771fd43cf6eSHans Rosenfeld 	    IWN_FH_TX_CONFIG_DMA_PAUSE);
6772fd43cf6eSHans Rosenfeld 
6773fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
6774fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
6775fd43cf6eSHans Rosenfeld 	    IWN_LOADDR(dma->paddr));
6776fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
6777fd43cf6eSHans Rosenfeld 	    IWN_HIADDR(dma->paddr) << 28 | size);
6778fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
6779fd43cf6eSHans Rosenfeld 	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
6780fd43cf6eSHans Rosenfeld 	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
6781fd43cf6eSHans Rosenfeld 	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
6782fd43cf6eSHans Rosenfeld 
6783fd43cf6eSHans Rosenfeld 	/* Kick Flow Handler to start DMA transfer. */
6784fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6785fd43cf6eSHans Rosenfeld 	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
6786fd43cf6eSHans Rosenfeld 
6787fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
6788fd43cf6eSHans Rosenfeld 
6789fd43cf6eSHans Rosenfeld 	/* Wait at most five seconds for FH DMA transfer to complete. */
6790fd43cf6eSHans Rosenfeld 	clk = ddi_get_lbolt() + drv_usectohz(5000000);
6791fd43cf6eSHans Rosenfeld 	while ((sc->sc_flags & IWN_FLAG_FW_DMA) == 0) {
6792fd43cf6eSHans Rosenfeld 		if (cv_timedwait(&sc->sc_fhdma_cv, &sc->sc_mtx, clk) < 0)
6793fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
6794fd43cf6eSHans Rosenfeld 	}
6795fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_FW_DMA;
6796fd43cf6eSHans Rosenfeld 
6797fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
6798fd43cf6eSHans Rosenfeld }
6799fd43cf6eSHans Rosenfeld 
6800fd43cf6eSHans Rosenfeld static int
iwn5000_load_firmware(struct iwn_softc * sc)6801fd43cf6eSHans Rosenfeld iwn5000_load_firmware(struct iwn_softc *sc)
6802fd43cf6eSHans Rosenfeld {
6803fd43cf6eSHans Rosenfeld 	struct iwn_fw_part *fw;
6804fd43cf6eSHans Rosenfeld 	int error;
6805fd43cf6eSHans Rosenfeld 
6806fd43cf6eSHans Rosenfeld 	/* Load the initialization firmware on first boot only. */
6807fd43cf6eSHans Rosenfeld 	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
6808fd43cf6eSHans Rosenfeld 	    &sc->fw.main : &sc->fw.init;
6809fd43cf6eSHans Rosenfeld 
6810fd43cf6eSHans Rosenfeld 	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
6811fd43cf6eSHans Rosenfeld 	    fw->text, fw->textsz);
6812fd43cf6eSHans Rosenfeld 	if (error != 0) {
6813fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6814fd43cf6eSHans Rosenfeld 		    "!could not load firmware %s section", ".text");
6815fd43cf6eSHans Rosenfeld 		return error;
6816fd43cf6eSHans Rosenfeld 	}
6817fd43cf6eSHans Rosenfeld 	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
6818fd43cf6eSHans Rosenfeld 	    fw->data, fw->datasz);
6819fd43cf6eSHans Rosenfeld 	if (error != 0) {
6820fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6821fd43cf6eSHans Rosenfeld 		    "!could not load firmware %s section", ".data");
6822fd43cf6eSHans Rosenfeld 		return error;
6823fd43cf6eSHans Rosenfeld 	}
6824fd43cf6eSHans Rosenfeld 
6825fd43cf6eSHans Rosenfeld 	/* Now press "execute". */
6826fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_RESET, 0);
6827fd43cf6eSHans Rosenfeld 	return 0;
6828fd43cf6eSHans Rosenfeld }
6829fd43cf6eSHans Rosenfeld 
6830fd43cf6eSHans Rosenfeld /*
6831fd43cf6eSHans Rosenfeld  * Extract text and data sections from a legacy firmware image.
6832fd43cf6eSHans Rosenfeld  */
6833fd43cf6eSHans Rosenfeld static int
iwn_read_firmware_leg(struct iwn_softc * sc,struct iwn_fw_info * fw)6834fd43cf6eSHans Rosenfeld iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
6835fd43cf6eSHans Rosenfeld {
6836fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(sc));
6837fd43cf6eSHans Rosenfeld 	const uint32_t *ptr;
6838fd43cf6eSHans Rosenfeld 	size_t hdrlen = 24;
6839fd43cf6eSHans Rosenfeld 	uint32_t rev;
6840fd43cf6eSHans Rosenfeld 
6841fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
6842fd43cf6eSHans Rosenfeld 	ptr = (const uint32_t *)fw->data;
6843fd43cf6eSHans Rosenfeld 	rev = le32toh(*ptr++);
6844fd43cf6eSHans Rosenfeld 
6845fd43cf6eSHans Rosenfeld 	/* Check firmware API version. */
6846fd43cf6eSHans Rosenfeld 	if (IWN_FW_API(rev) <= 1) {
6847fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6848fd43cf6eSHans Rosenfeld 		    "!bad firmware, need API version >=2");
6849fd43cf6eSHans Rosenfeld 		return EINVAL;
6850fd43cf6eSHans Rosenfeld 	}
6851fd43cf6eSHans Rosenfeld 	if (IWN_FW_API(rev) >= 3) {
6852fd43cf6eSHans Rosenfeld 		/* Skip build number (version 2 header). */
6853fd43cf6eSHans Rosenfeld 		hdrlen += 4;
6854fd43cf6eSHans Rosenfeld 		ptr++;
6855fd43cf6eSHans Rosenfeld 	}
6856fd43cf6eSHans Rosenfeld 	if (fw->size < hdrlen) {
6857fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6858fd43cf6eSHans Rosenfeld 		    "!firmware too short: %lld bytes", (longlong_t)fw->size);
6859fd43cf6eSHans Rosenfeld 		return EINVAL;
6860fd43cf6eSHans Rosenfeld 	}
6861fd43cf6eSHans Rosenfeld 	fw->main.textsz = le32toh(*ptr++);
6862fd43cf6eSHans Rosenfeld 	fw->main.datasz = le32toh(*ptr++);
6863fd43cf6eSHans Rosenfeld 	fw->init.textsz = le32toh(*ptr++);
6864fd43cf6eSHans Rosenfeld 	fw->init.datasz = le32toh(*ptr++);
6865fd43cf6eSHans Rosenfeld 	fw->boot.textsz = le32toh(*ptr++);
6866fd43cf6eSHans Rosenfeld 
6867fd43cf6eSHans Rosenfeld 	/* Check that all firmware sections fit. */
6868fd43cf6eSHans Rosenfeld 	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
6869fd43cf6eSHans Rosenfeld 	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
6870fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6871fd43cf6eSHans Rosenfeld 		    "!firmware too short: %lld bytes", (longlong_t)fw->size);
6872fd43cf6eSHans Rosenfeld 		return EINVAL;
6873fd43cf6eSHans Rosenfeld 	}
6874fd43cf6eSHans Rosenfeld 
6875fd43cf6eSHans Rosenfeld 	/* Get pointers to firmware sections. */
6876fd43cf6eSHans Rosenfeld 	fw->main.text = (const uint8_t *)ptr;
6877fd43cf6eSHans Rosenfeld 	fw->main.data = fw->main.text + fw->main.textsz;
6878fd43cf6eSHans Rosenfeld 	fw->init.text = fw->main.data + fw->main.datasz;
6879fd43cf6eSHans Rosenfeld 	fw->init.data = fw->init.text + fw->init.textsz;
6880fd43cf6eSHans Rosenfeld 	fw->boot.text = fw->init.data + fw->init.datasz;
6881fd43cf6eSHans Rosenfeld 	return 0;
6882fd43cf6eSHans Rosenfeld }
6883fd43cf6eSHans Rosenfeld 
6884fd43cf6eSHans Rosenfeld /*
6885fd43cf6eSHans Rosenfeld  * Extract text and data sections from a TLV firmware image.
6886fd43cf6eSHans Rosenfeld  */
6887fd43cf6eSHans Rosenfeld static int
iwn_read_firmware_tlv(struct iwn_softc * sc,struct iwn_fw_info * fw,uint16_t alt)6888fd43cf6eSHans Rosenfeld iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
6889fd43cf6eSHans Rosenfeld     uint16_t alt)
6890fd43cf6eSHans Rosenfeld {
6891fd43cf6eSHans Rosenfeld 	_NOTE(ARGUNUSED(sc));
6892fd43cf6eSHans Rosenfeld 	const struct iwn_fw_tlv_hdr *hdr;
6893fd43cf6eSHans Rosenfeld 	const struct iwn_fw_tlv *tlv;
6894fd43cf6eSHans Rosenfeld 	const uint8_t *ptr, *end;
6895fd43cf6eSHans Rosenfeld 	uint64_t altmask;
6896fd43cf6eSHans Rosenfeld 	uint32_t len;
6897fd43cf6eSHans Rosenfeld 
6898fd43cf6eSHans Rosenfeld 	if (fw->size < sizeof (*hdr)) {
6899fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6900fd43cf6eSHans Rosenfeld 		    "!firmware too short: %lld bytes", (longlong_t)fw->size);
6901fd43cf6eSHans Rosenfeld 		return EINVAL;
6902fd43cf6eSHans Rosenfeld 	}
6903fd43cf6eSHans Rosenfeld 	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
6904fd43cf6eSHans Rosenfeld 	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
6905fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
6906fd43cf6eSHans Rosenfeld 		    "!bad firmware signature 0x%08x", le32toh(hdr->signature));
6907fd43cf6eSHans Rosenfeld 		return EINVAL;
6908fd43cf6eSHans Rosenfeld 	}
6909fd43cf6eSHans Rosenfeld 
6910fd43cf6eSHans Rosenfeld 	/*
6911fd43cf6eSHans Rosenfeld 	 * Select the closest supported alternative that is less than
6912fd43cf6eSHans Rosenfeld 	 * or equal to the specified one.
6913fd43cf6eSHans Rosenfeld 	 */
6914fd43cf6eSHans Rosenfeld 	altmask = le64toh(hdr->altmask);
6915fd43cf6eSHans Rosenfeld 	while (alt > 0 && !(altmask & (1ULL << alt)))
6916fd43cf6eSHans Rosenfeld 		alt--;	/* Downgrade. */
6917fd43cf6eSHans Rosenfeld 	IWN_DBG("using alternative %d", alt);
6918fd43cf6eSHans Rosenfeld 
6919fd43cf6eSHans Rosenfeld 	ptr = (const uint8_t *)(hdr + 1);
6920fd43cf6eSHans Rosenfeld 	end = (const uint8_t *)(fw->data + fw->size);
6921fd43cf6eSHans Rosenfeld 
6922fd43cf6eSHans Rosenfeld 	/* Parse type-length-value fields. */
6923fd43cf6eSHans Rosenfeld 	while (ptr + sizeof (*tlv) <= end) {
6924fd43cf6eSHans Rosenfeld 		tlv = (const struct iwn_fw_tlv *)ptr;
6925fd43cf6eSHans Rosenfeld 		len = le32toh(tlv->len);
6926fd43cf6eSHans Rosenfeld 
6927fd43cf6eSHans Rosenfeld 		ptr += sizeof (*tlv);
6928fd43cf6eSHans Rosenfeld 		if (ptr + len > end) {
6929fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
6930fd43cf6eSHans Rosenfeld 			    "!firmware too short: %lld bytes",
6931fd43cf6eSHans Rosenfeld 			    (longlong_t)fw->size);
6932fd43cf6eSHans Rosenfeld 			return EINVAL;
6933fd43cf6eSHans Rosenfeld 		}
6934fd43cf6eSHans Rosenfeld 		/* Skip other alternatives. */
6935fd43cf6eSHans Rosenfeld 		if (tlv->alt != 0 && le16toh(tlv->alt) != alt) {
6936fd43cf6eSHans Rosenfeld 			IWN_DBG("skipping other alternative");
6937fd43cf6eSHans Rosenfeld 			goto next;
6938fd43cf6eSHans Rosenfeld 		}
6939fd43cf6eSHans Rosenfeld 
6940fd43cf6eSHans Rosenfeld 		switch (le16toh(tlv->type)) {
6941fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_MAIN_TEXT:
6942fd43cf6eSHans Rosenfeld 			fw->main.text = ptr;
6943fd43cf6eSHans Rosenfeld 			fw->main.textsz = len;
6944fd43cf6eSHans Rosenfeld 			break;
6945fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_MAIN_DATA:
6946fd43cf6eSHans Rosenfeld 			fw->main.data = ptr;
6947fd43cf6eSHans Rosenfeld 			fw->main.datasz = len;
6948fd43cf6eSHans Rosenfeld 			break;
6949fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_INIT_TEXT:
6950fd43cf6eSHans Rosenfeld 			fw->init.text = ptr;
6951fd43cf6eSHans Rosenfeld 			fw->init.textsz = len;
6952fd43cf6eSHans Rosenfeld 			break;
6953fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_INIT_DATA:
6954fd43cf6eSHans Rosenfeld 			fw->init.data = ptr;
6955fd43cf6eSHans Rosenfeld 			fw->init.datasz = len;
6956fd43cf6eSHans Rosenfeld 			break;
6957fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_BOOT_TEXT:
6958fd43cf6eSHans Rosenfeld 			fw->boot.text = ptr;
6959fd43cf6eSHans Rosenfeld 			fw->boot.textsz = len;
6960fd43cf6eSHans Rosenfeld 			break;
6961fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_ENH_SENS:
6962fd43cf6eSHans Rosenfeld 			if (len != 0) {
6963fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN,
6964fd43cf6eSHans Rosenfeld 				    "!TLV type %d has invalid size %u",
6965fd43cf6eSHans Rosenfeld 				    le16toh(tlv->type), len);
6966fd43cf6eSHans Rosenfeld 				goto next;
6967fd43cf6eSHans Rosenfeld 			}
6968fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_ENH_SENS;
6969fd43cf6eSHans Rosenfeld 			break;
6970fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_PHY_CALIB:
6971fd43cf6eSHans Rosenfeld 			if (len != sizeof(uint32_t)) {
6972fd43cf6eSHans Rosenfeld 				dev_err(sc->sc_dip, CE_WARN,
6973fd43cf6eSHans Rosenfeld 				    "!TLV type %d has invalid size %u",
6974fd43cf6eSHans Rosenfeld 				    le16toh(tlv->type), len);
6975fd43cf6eSHans Rosenfeld 				goto next;
6976fd43cf6eSHans Rosenfeld 			}
6977fd43cf6eSHans Rosenfeld 			if (le32toh(*ptr) <= IWN5000_PHY_CALIB_MAX) {
6978fd43cf6eSHans Rosenfeld 				sc->reset_noise_gain = le32toh(*ptr);
6979fd43cf6eSHans Rosenfeld 				sc->noise_gain = le32toh(*ptr) + 1;
6980fd43cf6eSHans Rosenfeld 			}
6981fd43cf6eSHans Rosenfeld 			break;
6982fd43cf6eSHans Rosenfeld 		case IWN_FW_TLV_FLAGS:
6983fd43cf6eSHans Rosenfeld 			if (len < sizeof(uint32_t))
6984fd43cf6eSHans Rosenfeld 				break;
6985fd43cf6eSHans Rosenfeld 			if (len % sizeof(uint32_t))
6986fd43cf6eSHans Rosenfeld 				break;
6987fd43cf6eSHans Rosenfeld 			sc->tlv_feature_flags = le32toh(*ptr);
6988fd43cf6eSHans Rosenfeld 			IWN_DBG("feature: 0x%08x", sc->tlv_feature_flags);
6989fd43cf6eSHans Rosenfeld 			break;
6990fd43cf6eSHans Rosenfeld 		default:
6991fd43cf6eSHans Rosenfeld 			IWN_DBG("TLV type %d not handled", le16toh(tlv->type));
6992fd43cf6eSHans Rosenfeld 			break;
6993fd43cf6eSHans Rosenfeld 		}
6994fd43cf6eSHans Rosenfeld  next:		/* TLV fields are 32-bit aligned. */
6995fd43cf6eSHans Rosenfeld 		ptr += (len + 3) & ~3;
6996fd43cf6eSHans Rosenfeld 	}
6997fd43cf6eSHans Rosenfeld 	return 0;
6998fd43cf6eSHans Rosenfeld }
6999fd43cf6eSHans Rosenfeld 
7000fd43cf6eSHans Rosenfeld static int
iwn_read_firmware(struct iwn_softc * sc)7001fd43cf6eSHans Rosenfeld iwn_read_firmware(struct iwn_softc *sc)
7002fd43cf6eSHans Rosenfeld {
7003fd43cf6eSHans Rosenfeld 	struct iwn_fw_info *fw = &sc->fw;
7004fd43cf6eSHans Rosenfeld 	firmware_handle_t fwh;
7005fd43cf6eSHans Rosenfeld 	int error;
7006fd43cf6eSHans Rosenfeld 
7007fd43cf6eSHans Rosenfeld 	/*
7008fd43cf6eSHans Rosenfeld 	 * Some PHY calibration commands are firmware-dependent; these
7009fd43cf6eSHans Rosenfeld 	 * are the default values that will be overridden if
7010fd43cf6eSHans Rosenfeld 	 * necessary.
7011fd43cf6eSHans Rosenfeld 	 */
7012fd43cf6eSHans Rosenfeld 	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
7013fd43cf6eSHans Rosenfeld 	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
7014fd43cf6eSHans Rosenfeld 
7015fd43cf6eSHans Rosenfeld 	/* Initialize for error returns */
7016fd43cf6eSHans Rosenfeld 	fw->data = NULL;
7017fd43cf6eSHans Rosenfeld 	fw->size = 0;
7018fd43cf6eSHans Rosenfeld 
7019fd43cf6eSHans Rosenfeld 	/* Open firmware image. */
7020fd43cf6eSHans Rosenfeld 	if ((error = firmware_open("iwn", sc->fwname, &fwh)) != 0) {
7021fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7022fd43cf6eSHans Rosenfeld 		    "!could not get firmware handle %s", sc->fwname);
7023fd43cf6eSHans Rosenfeld 		return error;
7024fd43cf6eSHans Rosenfeld 	}
7025fd43cf6eSHans Rosenfeld 	fw->size = firmware_get_size(fwh);
7026fd43cf6eSHans Rosenfeld 	if (fw->size < sizeof (uint32_t)) {
7027fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7028fd43cf6eSHans Rosenfeld 		    "!firmware too short: %lld bytes", (longlong_t)fw->size);
7029fd43cf6eSHans Rosenfeld 		(void) firmware_close(fwh);
7030fd43cf6eSHans Rosenfeld 		return EINVAL;
7031fd43cf6eSHans Rosenfeld 	}
7032fd43cf6eSHans Rosenfeld 
7033fd43cf6eSHans Rosenfeld 	/* Read the firmware. */
7034fd43cf6eSHans Rosenfeld 	fw->data = kmem_alloc(fw->size, KM_SLEEP);
7035fd43cf6eSHans Rosenfeld 	error = firmware_read(fwh, 0, fw->data, fw->size);
7036fd43cf6eSHans Rosenfeld 	(void) firmware_close(fwh);
7037fd43cf6eSHans Rosenfeld 	if (error != 0) {
7038fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7039fd43cf6eSHans Rosenfeld 		    "!could not read firmware %s", sc->fwname);
7040fd43cf6eSHans Rosenfeld 		goto out;
7041fd43cf6eSHans Rosenfeld 	}
7042fd43cf6eSHans Rosenfeld 
7043fd43cf6eSHans Rosenfeld 	/* Retrieve text and data sections. */
7044fd43cf6eSHans Rosenfeld 	/*LINTED: E_PTR_BAD_CAST_ALIGN*/
7045fd43cf6eSHans Rosenfeld 	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
7046fd43cf6eSHans Rosenfeld 		error = iwn_read_firmware_leg(sc, fw);
7047fd43cf6eSHans Rosenfeld 	else
7048fd43cf6eSHans Rosenfeld 		error = iwn_read_firmware_tlv(sc, fw, 1);
7049fd43cf6eSHans Rosenfeld 	if (error != 0) {
7050fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7051fd43cf6eSHans Rosenfeld 		    "!could not read firmware sections");
7052fd43cf6eSHans Rosenfeld 		goto out;
7053fd43cf6eSHans Rosenfeld 	}
7054fd43cf6eSHans Rosenfeld 
7055fd43cf6eSHans Rosenfeld 	/* Make sure text and data sections fit in hardware memory. */
7056fd43cf6eSHans Rosenfeld 	if (fw->main.textsz > sc->fw_text_maxsz ||
7057fd43cf6eSHans Rosenfeld 	    fw->main.datasz > sc->fw_data_maxsz ||
7058fd43cf6eSHans Rosenfeld 	    fw->init.textsz > sc->fw_text_maxsz ||
7059fd43cf6eSHans Rosenfeld 	    fw->init.datasz > sc->fw_data_maxsz ||
7060fd43cf6eSHans Rosenfeld 	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
7061fd43cf6eSHans Rosenfeld 	    (fw->boot.textsz & 3) != 0) {
7062fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7063fd43cf6eSHans Rosenfeld 		    "!firmware sections too large");
7064fd43cf6eSHans Rosenfeld 		goto out;
7065fd43cf6eSHans Rosenfeld 	}
7066fd43cf6eSHans Rosenfeld 
7067fd43cf6eSHans Rosenfeld 	/* We can proceed with loading the firmware. */
7068fd43cf6eSHans Rosenfeld 	return 0;
7069fd43cf6eSHans Rosenfeld out:
7070fd43cf6eSHans Rosenfeld 	kmem_free(fw->data, fw->size);
7071fd43cf6eSHans Rosenfeld 	fw->data = NULL;
7072fd43cf6eSHans Rosenfeld 	fw->size = 0;
7073fd43cf6eSHans Rosenfeld 	return error ? error : EINVAL;
7074fd43cf6eSHans Rosenfeld }
7075fd43cf6eSHans Rosenfeld 
7076fd43cf6eSHans Rosenfeld static int
iwn_clock_wait(struct iwn_softc * sc)7077fd43cf6eSHans Rosenfeld iwn_clock_wait(struct iwn_softc *sc)
7078fd43cf6eSHans Rosenfeld {
7079fd43cf6eSHans Rosenfeld 	int ntries;
7080fd43cf6eSHans Rosenfeld 
7081fd43cf6eSHans Rosenfeld 	/* Set "initialization complete" bit. */
7082fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
7083fd43cf6eSHans Rosenfeld 
7084fd43cf6eSHans Rosenfeld 	/* Wait for clock stabilization. */
7085fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 2500; ntries++) {
7086fd43cf6eSHans Rosenfeld 		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
7087fd43cf6eSHans Rosenfeld 			return 0;
7088fd43cf6eSHans Rosenfeld 		DELAY(10);
7089fd43cf6eSHans Rosenfeld 	}
7090fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN,
7091fd43cf6eSHans Rosenfeld 	    "!timeout waiting for clock stabilization");
7092fd43cf6eSHans Rosenfeld 	return ETIMEDOUT;
7093fd43cf6eSHans Rosenfeld }
7094fd43cf6eSHans Rosenfeld 
7095fd43cf6eSHans Rosenfeld static int
iwn_apm_init(struct iwn_softc * sc)7096fd43cf6eSHans Rosenfeld iwn_apm_init(struct iwn_softc *sc)
7097fd43cf6eSHans Rosenfeld {
7098fd43cf6eSHans Rosenfeld 	uint32_t reg;
7099fd43cf6eSHans Rosenfeld 	int error;
7100fd43cf6eSHans Rosenfeld 
7101fd43cf6eSHans Rosenfeld 	/* Disable L0s exit timer (NMI bug workaround). */
7102fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
7103fd43cf6eSHans Rosenfeld 	/* Don't wait for ICH L0s (ICH bug workaround). */
7104fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
7105fd43cf6eSHans Rosenfeld 
7106fd43cf6eSHans Rosenfeld 	/* Set FH wait threshold to max (HW bug under stress workaround). */
7107fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
7108fd43cf6eSHans Rosenfeld 
7109fd43cf6eSHans Rosenfeld 	/* Enable HAP INTA to move adapter from L1a to L0s. */
7110fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
7111fd43cf6eSHans Rosenfeld 
7112fd43cf6eSHans Rosenfeld 	/* Retrieve PCIe Active State Power Management (ASPM). */
7113fd43cf6eSHans Rosenfeld 	reg = pci_config_get32(sc->sc_pcih,
7114fd43cf6eSHans Rosenfeld 	    sc->sc_cap_off + PCIE_LINKCTL);
7115fd43cf6eSHans Rosenfeld 	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
7116fd43cf6eSHans Rosenfeld 	if (reg & PCIE_LINKCTL_ASPM_CTL_L1)	/* L1 Entry enabled. */
7117fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
7118fd43cf6eSHans Rosenfeld 	else
7119fd43cf6eSHans Rosenfeld 		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
7120fd43cf6eSHans Rosenfeld 
7121fd43cf6eSHans Rosenfeld 	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
7122fd43cf6eSHans Rosenfeld 	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
7123fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
7124fd43cf6eSHans Rosenfeld 
7125fd43cf6eSHans Rosenfeld 	/* Wait for clock stabilization before accessing prph. */
7126fd43cf6eSHans Rosenfeld 	if ((error = iwn_clock_wait(sc)) != 0)
7127fd43cf6eSHans Rosenfeld 		return error;
7128fd43cf6eSHans Rosenfeld 
7129fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
7130fd43cf6eSHans Rosenfeld 		return error;
7131fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
7132fd43cf6eSHans Rosenfeld 		/* Enable DMA and BSM (Bootstrap State Machine). */
7133fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN_APMG_CLK_EN,
7134fd43cf6eSHans Rosenfeld 		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
7135fd43cf6eSHans Rosenfeld 		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
7136fd43cf6eSHans Rosenfeld 	} else {
7137fd43cf6eSHans Rosenfeld 		/* Enable DMA. */
7138fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN_APMG_CLK_EN,
7139fd43cf6eSHans Rosenfeld 		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
7140fd43cf6eSHans Rosenfeld 	}
7141fd43cf6eSHans Rosenfeld 	DELAY(20);
7142fd43cf6eSHans Rosenfeld 	/* Disable L1-Active. */
7143fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
7144fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7145fd43cf6eSHans Rosenfeld 
7146fd43cf6eSHans Rosenfeld 	return 0;
7147fd43cf6eSHans Rosenfeld }
7148fd43cf6eSHans Rosenfeld 
7149fd43cf6eSHans Rosenfeld static void
iwn_apm_stop_master(struct iwn_softc * sc)7150fd43cf6eSHans Rosenfeld iwn_apm_stop_master(struct iwn_softc *sc)
7151fd43cf6eSHans Rosenfeld {
7152fd43cf6eSHans Rosenfeld 	int ntries;
7153fd43cf6eSHans Rosenfeld 
7154fd43cf6eSHans Rosenfeld 	/* Stop busmaster DMA activity. */
7155fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
7156fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 100; ntries++) {
7157fd43cf6eSHans Rosenfeld 		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
7158fd43cf6eSHans Rosenfeld 			return;
7159fd43cf6eSHans Rosenfeld 		DELAY(10);
7160fd43cf6eSHans Rosenfeld 	}
7161fd43cf6eSHans Rosenfeld 	dev_err(sc->sc_dip, CE_WARN,
7162fd43cf6eSHans Rosenfeld 	    "!timeout waiting for master");
7163fd43cf6eSHans Rosenfeld }
7164fd43cf6eSHans Rosenfeld 
7165fd43cf6eSHans Rosenfeld static void
iwn_apm_stop(struct iwn_softc * sc)7166fd43cf6eSHans Rosenfeld iwn_apm_stop(struct iwn_softc *sc)
7167fd43cf6eSHans Rosenfeld {
7168fd43cf6eSHans Rosenfeld 	iwn_apm_stop_master(sc);
7169fd43cf6eSHans Rosenfeld 
7170fd43cf6eSHans Rosenfeld 	/* Reset the entire device. */
7171fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
7172fd43cf6eSHans Rosenfeld 	DELAY(10);
7173fd43cf6eSHans Rosenfeld 	/* Clear "initialization complete" bit. */
7174fd43cf6eSHans Rosenfeld 	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
7175fd43cf6eSHans Rosenfeld }
7176fd43cf6eSHans Rosenfeld 
7177fd43cf6eSHans Rosenfeld static int
iwn4965_nic_config(struct iwn_softc * sc)7178fd43cf6eSHans Rosenfeld iwn4965_nic_config(struct iwn_softc *sc)
7179fd43cf6eSHans Rosenfeld {
7180fd43cf6eSHans Rosenfeld 	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
7181fd43cf6eSHans Rosenfeld 		/*
7182fd43cf6eSHans Rosenfeld 		 * I don't believe this to be correct but this is what the
7183fd43cf6eSHans Rosenfeld 		 * vendor driver is doing. Probably the bits should not be
7184fd43cf6eSHans Rosenfeld 		 * shifted in IWN_RFCFG_*.
7185fd43cf6eSHans Rosenfeld 		 */
7186fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7187fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_TYPE(sc->rfcfg) |
7188fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_STEP(sc->rfcfg) |
7189fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_DASH(sc->rfcfg));
7190fd43cf6eSHans Rosenfeld 	}
7191fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7192fd43cf6eSHans Rosenfeld 	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7193fd43cf6eSHans Rosenfeld 	return 0;
7194fd43cf6eSHans Rosenfeld }
7195fd43cf6eSHans Rosenfeld 
7196fd43cf6eSHans Rosenfeld static int
iwn5000_nic_config(struct iwn_softc * sc)7197fd43cf6eSHans Rosenfeld iwn5000_nic_config(struct iwn_softc *sc)
7198fd43cf6eSHans Rosenfeld {
7199fd43cf6eSHans Rosenfeld 	uint32_t tmp;
7200fd43cf6eSHans Rosenfeld 	int error;
7201fd43cf6eSHans Rosenfeld 
7202fd43cf6eSHans Rosenfeld 	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
7203fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7204fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_TYPE(sc->rfcfg) |
7205fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_STEP(sc->rfcfg) |
7206fd43cf6eSHans Rosenfeld 		    IWN_RFCFG_DASH(sc->rfcfg));
7207fd43cf6eSHans Rosenfeld 	}
7208fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7209fd43cf6eSHans Rosenfeld 	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7210fd43cf6eSHans Rosenfeld 
7211fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
7212fd43cf6eSHans Rosenfeld 		return error;
7213fd43cf6eSHans Rosenfeld 	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
7214fd43cf6eSHans Rosenfeld 
7215fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
7216fd43cf6eSHans Rosenfeld 		/*
7217fd43cf6eSHans Rosenfeld 		 * Select first Switching Voltage Regulator (1.32V) to
7218fd43cf6eSHans Rosenfeld 		 * solve a stability issue related to noisy DC2DC line
7219fd43cf6eSHans Rosenfeld 		 * in the silicon of 1000 Series.
7220fd43cf6eSHans Rosenfeld 		 */
7221fd43cf6eSHans Rosenfeld 		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
7222fd43cf6eSHans Rosenfeld 		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
7223fd43cf6eSHans Rosenfeld 		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
7224fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
7225fd43cf6eSHans Rosenfeld 	}
7226fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7227fd43cf6eSHans Rosenfeld 
7228fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
7229fd43cf6eSHans Rosenfeld 		/* Use internal power amplifier only. */
7230fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
7231fd43cf6eSHans Rosenfeld 	}
7232fd43cf6eSHans Rosenfeld 	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
7233fd43cf6eSHans Rosenfeld 		sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
7234fd43cf6eSHans Rosenfeld 		/* Indicate that ROM calibration version is >=6. */
7235fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
7236fd43cf6eSHans Rosenfeld 	}
7237fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
7238fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
7239fd43cf6eSHans Rosenfeld 	if (sc->hw_type == IWN_HW_REV_TYPE_2030 ||
7240fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_2000 ||
7241fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_135  ||
7242fd43cf6eSHans Rosenfeld 	    sc->hw_type == IWN_HW_REV_TYPE_105)
7243fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_IQ_INVERT);
7244fd43cf6eSHans Rosenfeld 	return 0;
7245fd43cf6eSHans Rosenfeld }
7246fd43cf6eSHans Rosenfeld 
7247fd43cf6eSHans Rosenfeld /*
7248fd43cf6eSHans Rosenfeld  * Take NIC ownership over Intel Active Management Technology (AMT).
7249fd43cf6eSHans Rosenfeld  */
7250fd43cf6eSHans Rosenfeld static int
iwn_hw_prepare(struct iwn_softc * sc)7251fd43cf6eSHans Rosenfeld iwn_hw_prepare(struct iwn_softc *sc)
7252fd43cf6eSHans Rosenfeld {
7253fd43cf6eSHans Rosenfeld 	int ntries;
7254fd43cf6eSHans Rosenfeld 
7255fd43cf6eSHans Rosenfeld 	/* Check if hardware is ready. */
7256fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7257fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 5; ntries++) {
7258fd43cf6eSHans Rosenfeld 		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7259fd43cf6eSHans Rosenfeld 		    IWN_HW_IF_CONFIG_NIC_READY)
7260fd43cf6eSHans Rosenfeld 			return 0;
7261fd43cf6eSHans Rosenfeld 		DELAY(10);
7262fd43cf6eSHans Rosenfeld 	}
7263fd43cf6eSHans Rosenfeld 
7264fd43cf6eSHans Rosenfeld 	/* Hardware not ready, force into ready state. */
7265fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
7266fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 15000; ntries++) {
7267fd43cf6eSHans Rosenfeld 		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
7268fd43cf6eSHans Rosenfeld 		    IWN_HW_IF_CONFIG_PREPARE_DONE))
7269fd43cf6eSHans Rosenfeld 			break;
7270fd43cf6eSHans Rosenfeld 		DELAY(10);
7271fd43cf6eSHans Rosenfeld 	}
7272fd43cf6eSHans Rosenfeld 	if (ntries == 15000)
7273fd43cf6eSHans Rosenfeld 		return ETIMEDOUT;
7274fd43cf6eSHans Rosenfeld 
7275fd43cf6eSHans Rosenfeld 	/* Hardware should be ready now. */
7276fd43cf6eSHans Rosenfeld 	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7277fd43cf6eSHans Rosenfeld 	for (ntries = 0; ntries < 5; ntries++) {
7278fd43cf6eSHans Rosenfeld 		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7279fd43cf6eSHans Rosenfeld 		    IWN_HW_IF_CONFIG_NIC_READY)
7280fd43cf6eSHans Rosenfeld 			return 0;
7281fd43cf6eSHans Rosenfeld 		DELAY(10);
7282fd43cf6eSHans Rosenfeld 	}
7283fd43cf6eSHans Rosenfeld 	return ETIMEDOUT;
7284fd43cf6eSHans Rosenfeld }
7285fd43cf6eSHans Rosenfeld 
7286fd43cf6eSHans Rosenfeld static int
iwn_hw_init(struct iwn_softc * sc)7287fd43cf6eSHans Rosenfeld iwn_hw_init(struct iwn_softc *sc)
7288fd43cf6eSHans Rosenfeld {
7289fd43cf6eSHans Rosenfeld 	struct iwn_ops *ops = &sc->ops;
7290fd43cf6eSHans Rosenfeld 	int error, chnl, qid;
7291fd43cf6eSHans Rosenfeld 	clock_t clk;
7292fd43cf6eSHans Rosenfeld 	uint32_t rx_config;
7293fd43cf6eSHans Rosenfeld 
7294fd43cf6eSHans Rosenfeld 	ASSERT(mutex_owned(&sc->sc_mtx));
7295fd43cf6eSHans Rosenfeld 
7296fd43cf6eSHans Rosenfeld 	/* Clear pending interrupts. */
7297fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7298fd43cf6eSHans Rosenfeld 
7299fd43cf6eSHans Rosenfeld 	if ((error = iwn_apm_init(sc)) != 0) {
7300fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7301fd43cf6eSHans Rosenfeld 		    "!could not power ON adapter");
7302fd43cf6eSHans Rosenfeld 		return error;
7303fd43cf6eSHans Rosenfeld 	}
7304fd43cf6eSHans Rosenfeld 
7305fd43cf6eSHans Rosenfeld 	/* Select VMAIN power source. */
7306fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
7307fd43cf6eSHans Rosenfeld 		return error;
7308fd43cf6eSHans Rosenfeld 	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
7309fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7310fd43cf6eSHans Rosenfeld 
7311fd43cf6eSHans Rosenfeld 	/* Perform adapter-specific initialization. */
7312fd43cf6eSHans Rosenfeld 	if ((error = ops->nic_config(sc)) != 0)
7313fd43cf6eSHans Rosenfeld 		return error;
7314fd43cf6eSHans Rosenfeld 
7315fd43cf6eSHans Rosenfeld 	/* Initialize RX ring. */
7316fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
7317fd43cf6eSHans Rosenfeld 		return error;
7318fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
7319fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
7320fd43cf6eSHans Rosenfeld 	/* Set physical address of RX ring (256-byte aligned). */
7321fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
7322fd43cf6eSHans Rosenfeld 	/* Set physical address of RX status (16-byte aligned). */
7323fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
7324fd43cf6eSHans Rosenfeld 	/* Enable RX. */
7325fd43cf6eSHans Rosenfeld 	rx_config =
7326fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_ENA	   |
7327fd43cf6eSHans Rosenfeld #if IWN_RBUF_SIZE == 8192
7328fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_RB_SIZE_8K    |
7329fd43cf6eSHans Rosenfeld #endif
7330fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
7331fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
7332fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
7333fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
7334fd43cf6eSHans Rosenfeld 	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG);
7335fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_CONFIG, rx_config);
7336fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7337fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
7338fd43cf6eSHans Rosenfeld 
7339fd43cf6eSHans Rosenfeld 	if ((error = iwn_nic_lock(sc)) != 0)
7340fd43cf6eSHans Rosenfeld 		return error;
7341fd43cf6eSHans Rosenfeld 
7342fd43cf6eSHans Rosenfeld 	/* Initialize TX scheduler. */
7343fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7344fd43cf6eSHans Rosenfeld 
7345fd43cf6eSHans Rosenfeld 	/* Set physical address of "keep warm" page (16-byte aligned). */
7346fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
7347fd43cf6eSHans Rosenfeld 
7348fd43cf6eSHans Rosenfeld 	/* Initialize TX rings. */
7349fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < sc->ntxqs; qid++) {
7350fd43cf6eSHans Rosenfeld 		struct iwn_tx_ring *txq = &sc->txq[qid];
7351fd43cf6eSHans Rosenfeld 
7352fd43cf6eSHans Rosenfeld 		/* Set physical address of TX ring (256-byte aligned). */
7353fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
7354fd43cf6eSHans Rosenfeld 		    txq->desc_dma.paddr >> 8);
7355fd43cf6eSHans Rosenfeld 	}
7356fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7357fd43cf6eSHans Rosenfeld 
7358fd43cf6eSHans Rosenfeld 	/* Enable DMA channels. */
7359fd43cf6eSHans Rosenfeld 	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7360fd43cf6eSHans Rosenfeld 		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
7361fd43cf6eSHans Rosenfeld 		    IWN_FH_TX_CONFIG_DMA_ENA |
7362fd43cf6eSHans Rosenfeld 		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
7363fd43cf6eSHans Rosenfeld 	}
7364fd43cf6eSHans Rosenfeld 
7365fd43cf6eSHans Rosenfeld 	/* Clear "radio off" and "commands blocked" bits. */
7366fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7367fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
7368fd43cf6eSHans Rosenfeld 
7369fd43cf6eSHans Rosenfeld 	/* Clear pending interrupts. */
7370fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7371fd43cf6eSHans Rosenfeld 	/* Enable interrupt coalescing. */
7372fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 32);
7373fd43cf6eSHans Rosenfeld 	/* Enable interrupts. */
7374fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7375fd43cf6eSHans Rosenfeld 
7376fd43cf6eSHans Rosenfeld 	/* _Really_ make sure "radio off" bit is cleared! */
7377fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7378fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7379fd43cf6eSHans Rosenfeld 
7380fd43cf6eSHans Rosenfeld 	/* Enable shadow registers. */
7381fd43cf6eSHans Rosenfeld 	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
7382fd43cf6eSHans Rosenfeld 		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
7383fd43cf6eSHans Rosenfeld 
7384fd43cf6eSHans Rosenfeld 	if ((error = ops->load_firmware(sc)) != 0) {
7385fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7386fd43cf6eSHans Rosenfeld 		    "!could not load firmware");
7387fd43cf6eSHans Rosenfeld 		return error;
7388fd43cf6eSHans Rosenfeld 	}
7389fd43cf6eSHans Rosenfeld 	/* Wait at most one second for firmware alive notification. */
7390fd43cf6eSHans Rosenfeld 	clk = ddi_get_lbolt() + drv_usectohz(1000000);
7391fd43cf6eSHans Rosenfeld 	while ((sc->sc_flags & IWN_FLAG_FW_ALIVE) == 0) {
7392fd43cf6eSHans Rosenfeld 		if (cv_timedwait(&sc->sc_alive_cv, &sc->sc_mtx, clk) < 0) {
7393fd43cf6eSHans Rosenfeld 			dev_err(sc->sc_dip, CE_WARN,
7394fd43cf6eSHans Rosenfeld 			    "!timeout waiting for adapter to initialize");
7395fd43cf6eSHans Rosenfeld 			return (IWN_FAIL);
7396fd43cf6eSHans Rosenfeld 		}
7397fd43cf6eSHans Rosenfeld 	}
7398fd43cf6eSHans Rosenfeld 	/* Do post-firmware initialization. */
7399fd43cf6eSHans Rosenfeld 	return ops->post_alive(sc);
7400fd43cf6eSHans Rosenfeld }
7401fd43cf6eSHans Rosenfeld 
7402fd43cf6eSHans Rosenfeld static void
iwn_hw_stop(struct iwn_softc * sc,boolean_t lock)7403fd43cf6eSHans Rosenfeld iwn_hw_stop(struct iwn_softc *sc, boolean_t lock)
7404fd43cf6eSHans Rosenfeld {
7405fd43cf6eSHans Rosenfeld 	int chnl, qid, ntries;
7406fd43cf6eSHans Rosenfeld 
7407fd43cf6eSHans Rosenfeld 	if (lock) {
7408fd43cf6eSHans Rosenfeld 		mutex_enter(&sc->sc_mtx);
7409fd43cf6eSHans Rosenfeld 	}
7410fd43cf6eSHans Rosenfeld 
7411fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
7412fd43cf6eSHans Rosenfeld 
7413fd43cf6eSHans Rosenfeld 	/* Disable interrupts. */
7414fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT_MASK, 0);
7415fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7416fd43cf6eSHans Rosenfeld 	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
7417fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7418fd43cf6eSHans Rosenfeld 
7419fd43cf6eSHans Rosenfeld 	/* Make sure we no longer hold the NIC lock. */
7420fd43cf6eSHans Rosenfeld 	iwn_nic_unlock(sc);
7421fd43cf6eSHans Rosenfeld 
7422fd43cf6eSHans Rosenfeld 	/* Stop TX scheduler. */
7423fd43cf6eSHans Rosenfeld 	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7424fd43cf6eSHans Rosenfeld 
7425fd43cf6eSHans Rosenfeld 	/* Stop all DMA channels. */
7426fd43cf6eSHans Rosenfeld 	if (iwn_nic_lock(sc) == 0) {
7427fd43cf6eSHans Rosenfeld 		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7428fd43cf6eSHans Rosenfeld 			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
7429fd43cf6eSHans Rosenfeld 			for (ntries = 0; ntries < 200; ntries++) {
7430fd43cf6eSHans Rosenfeld 				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
7431fd43cf6eSHans Rosenfeld 				    IWN_FH_TX_STATUS_IDLE(chnl))
7432fd43cf6eSHans Rosenfeld 					break;
7433fd43cf6eSHans Rosenfeld 				DELAY(10);
7434fd43cf6eSHans Rosenfeld 			}
7435fd43cf6eSHans Rosenfeld 		}
7436fd43cf6eSHans Rosenfeld 		iwn_nic_unlock(sc);
7437fd43cf6eSHans Rosenfeld 	}
7438fd43cf6eSHans Rosenfeld 
7439fd43cf6eSHans Rosenfeld 	/* Stop RX ring. */
7440fd43cf6eSHans Rosenfeld 	iwn_reset_rx_ring(sc, &sc->rxq);
7441fd43cf6eSHans Rosenfeld 
7442fd43cf6eSHans Rosenfeld 	/* Reset all TX rings. */
7443fd43cf6eSHans Rosenfeld 	for (qid = 0; qid < sc->ntxqs; qid++)
7444fd43cf6eSHans Rosenfeld 		iwn_reset_tx_ring(sc, &sc->txq[qid]);
7445fd43cf6eSHans Rosenfeld 
7446fd43cf6eSHans Rosenfeld 	if (iwn_nic_lock(sc) == 0) {
7447fd43cf6eSHans Rosenfeld 		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
7448fd43cf6eSHans Rosenfeld 		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
7449fd43cf6eSHans Rosenfeld 		iwn_nic_unlock(sc);
7450fd43cf6eSHans Rosenfeld 	}
7451fd43cf6eSHans Rosenfeld 	DELAY(5);
7452fd43cf6eSHans Rosenfeld 	/* Power OFF adapter. */
7453fd43cf6eSHans Rosenfeld 	iwn_apm_stop(sc);
7454fd43cf6eSHans Rosenfeld 
7455fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~(IWN_FLAG_HW_INITED | IWN_FLAG_FW_ALIVE);
7456fd43cf6eSHans Rosenfeld 
7457fd43cf6eSHans Rosenfeld 	if (lock) {
7458fd43cf6eSHans Rosenfeld 		mutex_exit(&sc->sc_mtx);
7459fd43cf6eSHans Rosenfeld 	}
7460fd43cf6eSHans Rosenfeld }
7461fd43cf6eSHans Rosenfeld 
7462fd43cf6eSHans Rosenfeld static int
iwn_init(struct iwn_softc * sc)7463fd43cf6eSHans Rosenfeld iwn_init(struct iwn_softc *sc)
7464fd43cf6eSHans Rosenfeld {
7465fd43cf6eSHans Rosenfeld 	int error;
7466fd43cf6eSHans Rosenfeld 
7467fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
7468fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_HW_INITED)
7469fd43cf6eSHans Rosenfeld 		goto out;
7470fd43cf6eSHans Rosenfeld 	if ((error = iwn_hw_prepare(sc)) != 0) {
7471fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!hardware not ready");
7472fd43cf6eSHans Rosenfeld 		goto fail;
7473fd43cf6eSHans Rosenfeld 	}
7474fd43cf6eSHans Rosenfeld 
7475fd43cf6eSHans Rosenfeld 	/* Check that the radio is not disabled by hardware switch. */
7476fd43cf6eSHans Rosenfeld 	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
7477fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN,
7478fd43cf6eSHans Rosenfeld 		    "!radio is disabled by hardware switch");
7479fd43cf6eSHans Rosenfeld 		error = EPERM;	/* :-) */
7480fd43cf6eSHans Rosenfeld 		goto fail;
7481fd43cf6eSHans Rosenfeld 	}
7482fd43cf6eSHans Rosenfeld 
7483fd43cf6eSHans Rosenfeld 	/* Read firmware images from the filesystem. */
7484fd43cf6eSHans Rosenfeld 	if ((error = iwn_read_firmware(sc)) != 0) {
7485fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not read firmware");
7486fd43cf6eSHans Rosenfeld 		goto fail;
7487fd43cf6eSHans Rosenfeld 	}
7488fd43cf6eSHans Rosenfeld 
7489fd43cf6eSHans Rosenfeld 	/* Initialize interrupt mask to default value. */
7490fd43cf6eSHans Rosenfeld 	sc->int_mask = IWN_INT_MASK_DEF;
7491fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7492fd43cf6eSHans Rosenfeld 
7493fd43cf6eSHans Rosenfeld 	/* Initialize hardware and upload firmware. */
7494fd43cf6eSHans Rosenfeld 	ASSERT(sc->fw.data != NULL && sc->fw.size > 0);
7495fd43cf6eSHans Rosenfeld 	error = iwn_hw_init(sc);
7496fd43cf6eSHans Rosenfeld 	if (error != 0) {
7497fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not initialize hardware");
7498fd43cf6eSHans Rosenfeld 		goto fail;
7499fd43cf6eSHans Rosenfeld 	}
7500fd43cf6eSHans Rosenfeld 
7501fd43cf6eSHans Rosenfeld 	/* Configure adapter now that it is ready. */
7502fd43cf6eSHans Rosenfeld 	if ((error = iwn_config(sc)) != 0) {
7503fd43cf6eSHans Rosenfeld 		dev_err(sc->sc_dip, CE_WARN, "!could not configure device");
7504fd43cf6eSHans Rosenfeld 		goto fail;
7505fd43cf6eSHans Rosenfeld 	}
7506fd43cf6eSHans Rosenfeld 
7507fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_HW_INITED;
7508fd43cf6eSHans Rosenfeld out:
7509fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
7510fd43cf6eSHans Rosenfeld 	return 0;
7511fd43cf6eSHans Rosenfeld 
7512fd43cf6eSHans Rosenfeld fail:
7513fd43cf6eSHans Rosenfeld 	iwn_hw_stop(sc, B_FALSE);
7514fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
7515fd43cf6eSHans Rosenfeld 	return error;
7516fd43cf6eSHans Rosenfeld }
7517fd43cf6eSHans Rosenfeld 
7518fd43cf6eSHans Rosenfeld /*
7519fd43cf6eSHans Rosenfeld  * XXX code from usr/src/uts/common/io/net80211/net880211_output.c
7520fd43cf6eSHans Rosenfeld  * Copyright (c) 2001 Atsushi Onoe
7521fd43cf6eSHans Rosenfeld  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
7522fd43cf6eSHans Rosenfeld  * Copyright (c) 2007-2009 Damien Bergamini
7523fd43cf6eSHans Rosenfeld  * All rights reserved.
7524fd43cf6eSHans Rosenfeld  */
7525fd43cf6eSHans Rosenfeld 
7526fd43cf6eSHans Rosenfeld /*
7527fd43cf6eSHans Rosenfeld  * Add SSID element to a frame
7528fd43cf6eSHans Rosenfeld  */
7529fd43cf6eSHans Rosenfeld static uint8_t *
ieee80211_add_ssid(uint8_t * frm,const uint8_t * ssid,uint32_t len)7530fd43cf6eSHans Rosenfeld ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len)
7531fd43cf6eSHans Rosenfeld {
7532fd43cf6eSHans Rosenfeld 	*frm++ = IEEE80211_ELEMID_SSID;
7533fd43cf6eSHans Rosenfeld 	*frm++ = (uint8_t)len;
7534fd43cf6eSHans Rosenfeld 	bcopy(ssid, frm, len);
7535fd43cf6eSHans Rosenfeld 	return (frm + len);
7536fd43cf6eSHans Rosenfeld }
7537fd43cf6eSHans Rosenfeld 
7538fd43cf6eSHans Rosenfeld /*
7539fd43cf6eSHans Rosenfeld  * Add supported rates information element to a frame.
7540fd43cf6eSHans Rosenfeld  */
7541fd43cf6eSHans Rosenfeld static uint8_t *
ieee80211_add_rates(uint8_t * frm,const struct ieee80211_rateset * rs)7542fd43cf6eSHans Rosenfeld ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
7543fd43cf6eSHans Rosenfeld {
7544fd43cf6eSHans Rosenfeld 	uint8_t nrates;
7545fd43cf6eSHans Rosenfeld 
7546fd43cf6eSHans Rosenfeld 	*frm++ = IEEE80211_ELEMID_RATES;
7547fd43cf6eSHans Rosenfeld 	nrates = rs->ir_nrates;
7548fd43cf6eSHans Rosenfeld 	if (nrates > IEEE80211_RATE_SIZE)
7549fd43cf6eSHans Rosenfeld 		nrates = IEEE80211_RATE_SIZE;
7550fd43cf6eSHans Rosenfeld 	*frm++ = nrates;
7551fd43cf6eSHans Rosenfeld 	bcopy(rs->ir_rates, frm, nrates);
7552fd43cf6eSHans Rosenfeld 	return (frm + nrates);
7553fd43cf6eSHans Rosenfeld }
7554fd43cf6eSHans Rosenfeld 
7555fd43cf6eSHans Rosenfeld /*
7556fd43cf6eSHans Rosenfeld  * Add extended supported rates element to a frame, usually for 11g mode
7557fd43cf6eSHans Rosenfeld  */
7558fd43cf6eSHans Rosenfeld static uint8_t *
ieee80211_add_xrates(uint8_t * frm,const struct ieee80211_rateset * rs)7559fd43cf6eSHans Rosenfeld ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
7560fd43cf6eSHans Rosenfeld {
7561fd43cf6eSHans Rosenfeld 	if (rs->ir_nrates > IEEE80211_RATE_SIZE) {
7562fd43cf6eSHans Rosenfeld 		uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE;
7563fd43cf6eSHans Rosenfeld 
7564fd43cf6eSHans Rosenfeld 		*frm++ = IEEE80211_ELEMID_XRATES;
7565fd43cf6eSHans Rosenfeld 		*frm++ = nrates;
7566fd43cf6eSHans Rosenfeld 		bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates);
7567fd43cf6eSHans Rosenfeld 		frm += nrates;
7568fd43cf6eSHans Rosenfeld 	}
7569fd43cf6eSHans Rosenfeld 	return (frm);
7570fd43cf6eSHans Rosenfeld }
7571fd43cf6eSHans Rosenfeld 
7572fd43cf6eSHans Rosenfeld /*
7573fd43cf6eSHans Rosenfeld  * XXX: Hack to set the current channel to the value advertised in beacons or
7574fd43cf6eSHans Rosenfeld  * probe responses. Only used during AP detection.
7575fd43cf6eSHans Rosenfeld  * XXX: Duplicated from if_iwi.c
7576fd43cf6eSHans Rosenfeld  */
7577fd43cf6eSHans Rosenfeld static void
iwn_fix_channel(struct iwn_softc * sc,mblk_t * m,struct iwn_rx_stat * stat)7578fd43cf6eSHans Rosenfeld iwn_fix_channel(struct iwn_softc *sc, mblk_t *m,
7579fd43cf6eSHans Rosenfeld     struct iwn_rx_stat *stat)
7580fd43cf6eSHans Rosenfeld {
7581fd43cf6eSHans Rosenfeld 	struct ieee80211com *ic = &sc->sc_ic;
7582fd43cf6eSHans Rosenfeld 	struct ieee80211_frame *wh;
7583fd43cf6eSHans Rosenfeld 	uint8_t subtype;
7584fd43cf6eSHans Rosenfeld 	uint8_t *frm, *efrm;
7585fd43cf6eSHans Rosenfeld 
7586fd43cf6eSHans Rosenfeld 	wh = (struct ieee80211_frame *)m->b_rptr;
7587fd43cf6eSHans Rosenfeld 
7588fd43cf6eSHans Rosenfeld 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
7589fd43cf6eSHans Rosenfeld 		return;
7590fd43cf6eSHans Rosenfeld 
7591fd43cf6eSHans Rosenfeld 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
7592fd43cf6eSHans Rosenfeld 
7593fd43cf6eSHans Rosenfeld 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
7594fd43cf6eSHans Rosenfeld 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
7595fd43cf6eSHans Rosenfeld 		return;
7596fd43cf6eSHans Rosenfeld 
7597fd43cf6eSHans Rosenfeld 	if (sc->sc_flags & IWN_FLAG_SCANNING_5GHZ) {
7598fd43cf6eSHans Rosenfeld 		int chan = le16toh(stat->chan);
7599fd43cf6eSHans Rosenfeld 		if (chan < __arraycount(ic->ic_sup_channels))
7600fd43cf6eSHans Rosenfeld 			ic->ic_curchan = &ic->ic_sup_channels[chan];
7601fd43cf6eSHans Rosenfeld 		return;
7602fd43cf6eSHans Rosenfeld 	}
7603fd43cf6eSHans Rosenfeld 
7604fd43cf6eSHans Rosenfeld 	frm = (uint8_t *)(wh + 1);
7605fd43cf6eSHans Rosenfeld 	efrm = (uint8_t *)m->b_wptr;
7606fd43cf6eSHans Rosenfeld 
7607fd43cf6eSHans Rosenfeld 	frm += 12;      /* skip tstamp, bintval and capinfo fields */
7608fd43cf6eSHans Rosenfeld 	while (frm < efrm) {
7609fd43cf6eSHans Rosenfeld 		if (*frm == IEEE80211_ELEMID_DSPARMS)
7610fd43cf6eSHans Rosenfeld #if IEEE80211_CHAN_MAX < 255
7611fd43cf6eSHans Rosenfeld 		if (frm[2] <= IEEE80211_CHAN_MAX)
7612fd43cf6eSHans Rosenfeld #endif
7613fd43cf6eSHans Rosenfeld 			ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
7614fd43cf6eSHans Rosenfeld 
7615fd43cf6eSHans Rosenfeld 		frm += frm[1] + 2;
7616fd43cf6eSHans Rosenfeld 	}
7617fd43cf6eSHans Rosenfeld }
7618fd43cf6eSHans Rosenfeld 
7619fd43cf6eSHans Rosenfeld /*
7620fd43cf6eSHans Rosenfeld  * invoked by GLD to start or open NIC
7621fd43cf6eSHans Rosenfeld  */
7622fd43cf6eSHans Rosenfeld static int
iwn_m_start(void * arg)7623fd43cf6eSHans Rosenfeld iwn_m_start(void *arg)
7624fd43cf6eSHans Rosenfeld {
7625fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
7626fd43cf6eSHans Rosenfeld 	ieee80211com_t	*ic;
7627fd43cf6eSHans Rosenfeld 	int err = IWN_FAIL;
7628fd43cf6eSHans Rosenfeld 
7629fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
7630fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
7631fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
7632fd43cf6eSHans Rosenfeld 
7633fd43cf6eSHans Rosenfeld 	err = iwn_init(sc);
7634fd43cf6eSHans Rosenfeld 	if (err != IWN_SUCCESS) {
7635fd43cf6eSHans Rosenfeld 		/*
7636fd43cf6eSHans Rosenfeld 		 * If initialization failed because the RF switch is off,
7637fd43cf6eSHans Rosenfeld 		 * return success anyway to make the 'plumb' succeed.
7638fd43cf6eSHans Rosenfeld 		 * The iwn_thread() tries to re-init background.
7639fd43cf6eSHans Rosenfeld 		 */
7640fd43cf6eSHans Rosenfeld 		if (err == EPERM &&
7641fd43cf6eSHans Rosenfeld 		    !(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
7642fd43cf6eSHans Rosenfeld 			mutex_enter(&sc->sc_mtx);
7643fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER;
7644fd43cf6eSHans Rosenfeld 			sc->sc_flags |= IWN_FLAG_RADIO_OFF;
7645fd43cf6eSHans Rosenfeld 			mutex_exit(&sc->sc_mtx);
7646fd43cf6eSHans Rosenfeld 			return (IWN_SUCCESS);
7647fd43cf6eSHans Rosenfeld 		}
7648fd43cf6eSHans Rosenfeld 
7649fd43cf6eSHans Rosenfeld 		return (err);
7650fd43cf6eSHans Rosenfeld 	}
7651fd43cf6eSHans Rosenfeld 
7652fd43cf6eSHans Rosenfeld 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
7653fd43cf6eSHans Rosenfeld 
7654fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
7655fd43cf6eSHans Rosenfeld 	sc->sc_flags |= IWN_FLAG_RUNNING;
7656fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
7657fd43cf6eSHans Rosenfeld 
7658fd43cf6eSHans Rosenfeld 	return (IWN_SUCCESS);
7659fd43cf6eSHans Rosenfeld }
7660fd43cf6eSHans Rosenfeld 
7661fd43cf6eSHans Rosenfeld /*
7662fd43cf6eSHans Rosenfeld  * invoked by GLD to stop or down NIC
7663fd43cf6eSHans Rosenfeld  */
7664fd43cf6eSHans Rosenfeld static void
iwn_m_stop(void * arg)7665fd43cf6eSHans Rosenfeld iwn_m_stop(void *arg)
7666fd43cf6eSHans Rosenfeld {
7667fd43cf6eSHans Rosenfeld 	struct iwn_softc *sc;
7668fd43cf6eSHans Rosenfeld 	ieee80211com_t	*ic;
7669fd43cf6eSHans Rosenfeld 
7670fd43cf6eSHans Rosenfeld 	sc = (struct iwn_softc *)arg;
7671fd43cf6eSHans Rosenfeld 	ASSERT(sc != NULL);
7672fd43cf6eSHans Rosenfeld 	ic = &sc->sc_ic;
7673fd43cf6eSHans Rosenfeld 
7674fd43cf6eSHans Rosenfeld 	iwn_hw_stop(sc, B_TRUE);
7675fd43cf6eSHans Rosenfeld 
7676fd43cf6eSHans Rosenfeld 	/*
7677fd43cf6eSHans Rosenfeld 	 * release buffer for calibration
7678fd43cf6eSHans Rosenfeld 	 */
7679fd43cf6eSHans Rosenfeld 
7680fd43cf6eSHans Rosenfeld 	ieee80211_stop_watchdog(ic);
7681fd43cf6eSHans Rosenfeld 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
7682fd43cf6eSHans Rosenfeld 
7683fd43cf6eSHans Rosenfeld 	mutex_enter(&sc->sc_mtx);
7684fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER;
7685fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_RATE_AUTO_CTL;
7686fd43cf6eSHans Rosenfeld 
7687fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_RUNNING;
7688fd43cf6eSHans Rosenfeld 	sc->sc_flags &= ~IWN_FLAG_SCANNING;
7689fd43cf6eSHans Rosenfeld 	mutex_exit(&sc->sc_mtx);
7690fd43cf6eSHans Rosenfeld }
7691fd43cf6eSHans Rosenfeld 
7692fd43cf6eSHans Rosenfeld 
7693fd43cf6eSHans Rosenfeld /*
7694fd43cf6eSHans Rosenfeld  * Module Loading Data & Entry Points
7695fd43cf6eSHans Rosenfeld  */
7696fd43cf6eSHans Rosenfeld DDI_DEFINE_STREAM_OPS(iwn_devops, nulldev, nulldev, iwn_attach,
7697fd43cf6eSHans Rosenfeld     iwn_detach, nodev, NULL, D_MP, NULL, iwn_quiesce);
7698fd43cf6eSHans Rosenfeld 
7699fd43cf6eSHans Rosenfeld static struct modldrv iwn_modldrv = {
7700fd43cf6eSHans Rosenfeld 	&mod_driverops,
7701fd43cf6eSHans Rosenfeld 	"Intel WiFi Link 4965 and 1000/5000/6000 series driver",
7702fd43cf6eSHans Rosenfeld 	&iwn_devops
7703fd43cf6eSHans Rosenfeld };
7704fd43cf6eSHans Rosenfeld 
7705fd43cf6eSHans Rosenfeld static struct modlinkage iwn_modlinkage = {
7706fd43cf6eSHans Rosenfeld 	MODREV_1,
7707fd43cf6eSHans Rosenfeld 	&iwn_modldrv,
7708fd43cf6eSHans Rosenfeld 	NULL
7709fd43cf6eSHans Rosenfeld };
7710fd43cf6eSHans Rosenfeld 
7711fd43cf6eSHans Rosenfeld int
_init(void)7712fd43cf6eSHans Rosenfeld _init(void)
7713fd43cf6eSHans Rosenfeld {
7714fd43cf6eSHans Rosenfeld 	int	status;
7715fd43cf6eSHans Rosenfeld 
7716fd43cf6eSHans Rosenfeld 	status = ddi_soft_state_init(&iwn_state,
7717fd43cf6eSHans Rosenfeld 	    sizeof (struct iwn_softc), 1);
7718fd43cf6eSHans Rosenfeld 	if (status != DDI_SUCCESS)
7719fd43cf6eSHans Rosenfeld 		return (status);
7720fd43cf6eSHans Rosenfeld 
7721fd43cf6eSHans Rosenfeld 	mac_init_ops(&iwn_devops, "iwn");
7722fd43cf6eSHans Rosenfeld 	status = mod_install(&iwn_modlinkage);
7723fd43cf6eSHans Rosenfeld 	if (status != DDI_SUCCESS) {
7724fd43cf6eSHans Rosenfeld 		mac_fini_ops(&iwn_devops);
7725fd43cf6eSHans Rosenfeld 		ddi_soft_state_fini(&iwn_state);
7726fd43cf6eSHans Rosenfeld 	}
7727fd43cf6eSHans Rosenfeld 
7728fd43cf6eSHans Rosenfeld 	return (status);
7729fd43cf6eSHans Rosenfeld }
7730fd43cf6eSHans Rosenfeld 
7731fd43cf6eSHans Rosenfeld int
_fini(void)7732fd43cf6eSHans Rosenfeld _fini(void)
7733fd43cf6eSHans Rosenfeld {
7734fd43cf6eSHans Rosenfeld 	int status;
7735fd43cf6eSHans Rosenfeld 
7736fd43cf6eSHans Rosenfeld 	status = mod_remove(&iwn_modlinkage);
7737fd43cf6eSHans Rosenfeld 	if (status == DDI_SUCCESS) {
7738fd43cf6eSHans Rosenfeld 		mac_fini_ops(&iwn_devops);
7739fd43cf6eSHans Rosenfeld 		ddi_soft_state_fini(&iwn_state);
7740fd43cf6eSHans Rosenfeld 	}
7741fd43cf6eSHans Rosenfeld 
7742fd43cf6eSHans Rosenfeld 	return (status);
7743fd43cf6eSHans Rosenfeld }
7744fd43cf6eSHans Rosenfeld 
7745fd43cf6eSHans Rosenfeld int
_info(struct modinfo * mip)7746fd43cf6eSHans Rosenfeld _info(struct modinfo *mip)
7747fd43cf6eSHans Rosenfeld {
7748fd43cf6eSHans Rosenfeld 	return (mod_info(&iwn_modlinkage, mip));
7749fd43cf6eSHans Rosenfeld }
7750