if_iwn.c revision 221657
1178676Ssam/*-
2198429Srpaulo * Copyright (c) 2007-2009
3178676Ssam *	Damien Bergamini <damien.bergamini@free.fr>
4178676Ssam * Copyright (c) 2008
5178676Ssam *	Benjamin Close <benjsc@FreeBSD.org>
6178676Ssam * Copyright (c) 2008 Sam Leffler, Errno Consulting
7178676Ssam *
8178676Ssam * Permission to use, copy, modify, and distribute this software for any
9178676Ssam * purpose with or without fee is hereby granted, provided that the above
10178676Ssam * copyright notice and this permission notice appear in all copies.
11178676Ssam *
12178676Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13178676Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14178676Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15178676Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16178676Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17178676Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18178676Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19178676Ssam */
20178676Ssam
21178676Ssam/*
22201209Srpaulo * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network
23201209Srpaulo * adapters.
24178676Ssam */
25178676Ssam
26178676Ssam#include <sys/cdefs.h>
27178676Ssam__FBSDID("$FreeBSD: head/sys/dev/iwn/if_iwn.c 221657 2011-05-08 12:23:01Z bschmidt $");
28178676Ssam
29178676Ssam#include <sys/param.h>
30178676Ssam#include <sys/sockio.h>
31178676Ssam#include <sys/sysctl.h>
32178676Ssam#include <sys/mbuf.h>
33178676Ssam#include <sys/kernel.h>
34178676Ssam#include <sys/socket.h>
35178676Ssam#include <sys/systm.h>
36178676Ssam#include <sys/malloc.h>
37178676Ssam#include <sys/bus.h>
38178676Ssam#include <sys/rman.h>
39178676Ssam#include <sys/endian.h>
40178676Ssam#include <sys/firmware.h>
41178676Ssam#include <sys/limits.h>
42178676Ssam#include <sys/module.h>
43178676Ssam#include <sys/queue.h>
44178676Ssam#include <sys/taskqueue.h>
45178676Ssam
46178676Ssam#include <machine/bus.h>
47178676Ssam#include <machine/resource.h>
48178676Ssam#include <machine/clock.h>
49178676Ssam
50178676Ssam#include <dev/pci/pcireg.h>
51178676Ssam#include <dev/pci/pcivar.h>
52178676Ssam
53178676Ssam#include <net/bpf.h>
54178676Ssam#include <net/if.h>
55178676Ssam#include <net/if_arp.h>
56178676Ssam#include <net/ethernet.h>
57178676Ssam#include <net/if_dl.h>
58178676Ssam#include <net/if_media.h>
59178676Ssam#include <net/if_types.h>
60178676Ssam
61178676Ssam#include <netinet/in.h>
62178676Ssam#include <netinet/in_systm.h>
63178676Ssam#include <netinet/in_var.h>
64178676Ssam#include <netinet/if_ether.h>
65178676Ssam#include <netinet/ip.h>
66178676Ssam
67178676Ssam#include <net80211/ieee80211_var.h>
68178676Ssam#include <net80211/ieee80211_radiotap.h>
69178676Ssam#include <net80211/ieee80211_regdomain.h>
70206358Srpaulo#include <net80211/ieee80211_ratectl.h>
71178676Ssam
72178676Ssam#include <dev/iwn/if_iwnreg.h>
73178676Ssam#include <dev/iwn/if_iwnvar.h>
74178676Ssam
75220723Sbschmidtstruct iwn_ident {
76220723Sbschmidt	uint16_t	vendor;
77220723Sbschmidt	uint16_t	device;
78220723Sbschmidt	const char	*name;
79220723Sbschmidt};
80220723Sbschmidt
81220895Sbschmidtstatic const struct iwn_ident iwn_ident_table[] = {
82220895Sbschmidt	{ 0x8086, 0x0082, "Intel(R) Centrino(R) Advanced-N 6205"	 },
83220895Sbschmidt	{ 0x8086, 0x0083, "Intel(R) Centrino(R) Wireless-N 1000"	 },
84220895Sbschmidt	{ 0x8086, 0x0084, "Intel(R) Centrino(R) Wireless-N 1000"	 },
85220895Sbschmidt	{ 0x8086, 0x0085, "Intel(R) Centrino(R) Advanced-N 6205"	 },
86220895Sbschmidt	{ 0x8086, 0x0087, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" },
87220895Sbschmidt	{ 0x8086, 0x0089, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" },
88220895Sbschmidt	{ 0x8086, 0x008a, "Intel(R) Centrino(R) Wireless-N 1030"	 },
89220895Sbschmidt	{ 0x8086, 0x008b, "Intel(R) Centrino(R) Wireless-N 1030"	 },
90220895Sbschmidt	{ 0x8086, 0x0090, "Intel(R) Centrino(R) Advanced-N 6230"	 },
91220895Sbschmidt	{ 0x8086, 0x0091, "Intel(R) Centrino(R) Advanced-N 6230"	 },
92220895Sbschmidt	{ 0x8086, 0x4229, "Intel(R) Wireless WiFi Link 4965"		 },
93220895Sbschmidt	{ 0x8086, 0x422b, "Intel(R) Centrino(R) Ultimate-N 6300"	 },
94220895Sbschmidt	{ 0x8086, 0x422c, "Intel(R) Centrino(R) Advanced-N 6200"	 },
95221634Sbschmidt	{ 0x8086, 0x422d, "Intel(R) Wireless WiFi Link 4965"		 },
96220895Sbschmidt	{ 0x8086, 0x4230, "Intel(R) Wireless WiFi Link 4965"		 },
97220895Sbschmidt	{ 0x8086, 0x4232, "Intel(R) WiFi Link 5100"			 },
98221634Sbschmidt	{ 0x8086, 0x4233, "Intel(R) Wireless WiFi Link 4965"		 },
99220895Sbschmidt	{ 0x8086, 0x4235, "Intel(R) Ultimate N WiFi Link 5300"		 },
100220895Sbschmidt	{ 0x8086, 0x4236, "Intel(R) Ultimate N WiFi Link 5300"		 },
101220895Sbschmidt	{ 0x8086, 0x4237, "Intel(R) WiFi Link 5100"			 },
102220895Sbschmidt	{ 0x8086, 0x4238, "Intel(R) Centrino(R) Ultimate-N 6300"	 },
103220895Sbschmidt	{ 0x8086, 0x4239, "Intel(R) Centrino(R) Advanced-N 6200"	 },
104220895Sbschmidt	{ 0x8086, 0x423a, "Intel(R) WiMAX/WiFi Link 5350"		 },
105220895Sbschmidt	{ 0x8086, 0x423b, "Intel(R) WiMAX/WiFi Link 5350"		 },
106220895Sbschmidt	{ 0x8086, 0x423c, "Intel(R) WiMAX/WiFi Link 5150"		 },
107220895Sbschmidt	{ 0x8086, 0x423d, "Intel(R) WiMAX/WiFi Link 5150"		 },
108220723Sbschmidt	{ 0, 0, NULL }
109220723Sbschmidt};
110220723Sbschmidt
111178676Ssamstatic int	iwn_probe(device_t);
112178676Ssamstatic int	iwn_attach(device_t);
113220728Sbschmidtstatic int	iwn4965_attach(struct iwn_softc *, uint16_t);
114220728Sbschmidtstatic int	iwn5000_attach(struct iwn_softc *, uint16_t);
115206477Sbschmidtstatic void	iwn_radiotap_attach(struct iwn_softc *);
116220723Sbschmidtstatic void	iwn_sysctlattach(struct iwn_softc *);
117178676Ssamstatic struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
118178676Ssam		    const char name[IFNAMSIZ], int unit, int opmode,
119178676Ssam		    int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
120178676Ssam		    const uint8_t mac[IEEE80211_ADDR_LEN]);
121178676Ssamstatic void	iwn_vap_delete(struct ieee80211vap *);
122206474Sbschmidtstatic int	iwn_detach(device_t);
123220723Sbschmidtstatic int	iwn_shutdown(device_t);
124220723Sbschmidtstatic int	iwn_suspend(device_t);
125220723Sbschmidtstatic int	iwn_resume(device_t);
126206477Sbschmidtstatic int	iwn_nic_lock(struct iwn_softc *);
127206477Sbschmidtstatic int	iwn_eeprom_lock(struct iwn_softc *);
128206477Sbschmidtstatic int	iwn_init_otprom(struct iwn_softc *);
129206477Sbschmidtstatic int	iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
130206474Sbschmidtstatic void	iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int);
131178676Ssamstatic int	iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
132220691Sbschmidt		    void **, bus_size_t, bus_size_t);
133178676Ssamstatic void	iwn_dma_contig_free(struct iwn_dma_info *);
134206477Sbschmidtstatic int	iwn_alloc_sched(struct iwn_softc *);
135206477Sbschmidtstatic void	iwn_free_sched(struct iwn_softc *);
136206477Sbschmidtstatic int	iwn_alloc_kw(struct iwn_softc *);
137206477Sbschmidtstatic void	iwn_free_kw(struct iwn_softc *);
138206477Sbschmidtstatic int	iwn_alloc_ict(struct iwn_softc *);
139206477Sbschmidtstatic void	iwn_free_ict(struct iwn_softc *);
140206477Sbschmidtstatic int	iwn_alloc_fwmem(struct iwn_softc *);
141206477Sbschmidtstatic void	iwn_free_fwmem(struct iwn_softc *);
142206477Sbschmidtstatic int	iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
143206477Sbschmidtstatic void	iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
144206477Sbschmidtstatic void	iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
145206477Sbschmidtstatic int	iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
146178676Ssam		    int);
147206477Sbschmidtstatic void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
148206477Sbschmidtstatic void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
149206477Sbschmidtstatic void	iwn5000_ict_reset(struct iwn_softc *);
150206477Sbschmidtstatic int	iwn_read_eeprom(struct iwn_softc *,
151198429Srpaulo		    uint8_t macaddr[IEEE80211_ADDR_LEN]);
152206477Sbschmidtstatic void	iwn4965_read_eeprom(struct iwn_softc *);
153206477Sbschmidtstatic void	iwn4965_print_power_group(struct iwn_softc *, int);
154206477Sbschmidtstatic void	iwn5000_read_eeprom(struct iwn_softc *);
155206474Sbschmidtstatic uint32_t	iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
156206474Sbschmidtstatic void	iwn_read_eeprom_band(struct iwn_softc *, int);
157206474Sbschmidtstatic void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
158220726Sbschmidtstatic void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
159220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
160220723Sbschmidt		    struct ieee80211_channel *);
161220723Sbschmidtstatic int	iwn_setregdomain(struct ieee80211com *,
162220723Sbschmidt		    struct ieee80211_regdomain *, int,
163220726Sbschmidt		    struct ieee80211_channel[]);
164206477Sbschmidtstatic void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
165206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
166198429Srpaulo		    const uint8_t mac[IEEE80211_ADDR_LEN]);
167220715Sbschmidtstatic void	iwn_newassoc(struct ieee80211_node *, int);
168206477Sbschmidtstatic int	iwn_media_change(struct ifnet *);
169206477Sbschmidtstatic int	iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
170220667Sbschmidtstatic void	iwn_calib_timeout(void *);
171206477Sbschmidtstatic void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
172198429Srpaulo		    struct iwn_rx_data *);
173206477Sbschmidtstatic void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
174178676Ssam		    struct iwn_rx_data *);
175206477Sbschmidtstatic void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
176201209Srpaulo		    struct iwn_rx_data *);
177220674Sbschmidtstatic void	iwn5000_rx_calib_results(struct iwn_softc *,
178220674Sbschmidt		    struct iwn_rx_desc *, struct iwn_rx_data *);
179206477Sbschmidtstatic void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
180198429Srpaulo		    struct iwn_rx_data *);
181206477Sbschmidtstatic void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
182198429Srpaulo		    struct iwn_rx_data *);
183206477Sbschmidtstatic void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
184198429Srpaulo		    struct iwn_rx_data *);
185206477Sbschmidtstatic void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
186198429Srpaulo		    uint8_t);
187221651Sbschmidtstatic void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *);
188206477Sbschmidtstatic void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
189206477Sbschmidtstatic void	iwn_notif_intr(struct iwn_softc *);
190206477Sbschmidtstatic void	iwn_wakeup_intr(struct iwn_softc *);
191206477Sbschmidtstatic void	iwn_rftoggle_intr(struct iwn_softc *);
192206477Sbschmidtstatic void	iwn_fatal_intr(struct iwn_softc *);
193206477Sbschmidtstatic void	iwn_intr(void *);
194206477Sbschmidtstatic void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
195198429Srpaulo		    uint16_t);
196206477Sbschmidtstatic void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
197198429Srpaulo		    uint16_t);
198206475Sbschmidt#ifdef notyet
199206477Sbschmidtstatic void	iwn5000_reset_sched(struct iwn_softc *, int, int);
200206475Sbschmidt#endif
201206477Sbschmidtstatic int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
202220720Sbschmidt		    struct ieee80211_node *);
203220720Sbschmidtstatic int	iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
204220720Sbschmidt		    struct ieee80211_node *,
205220720Sbschmidt		    const struct ieee80211_bpf_params *params);
206198429Srpaulostatic int	iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
207198429Srpaulo		    const struct ieee80211_bpf_params *);
208206477Sbschmidtstatic void	iwn_start(struct ifnet *);
209206477Sbschmidtstatic void	iwn_start_locked(struct ifnet *);
210220667Sbschmidtstatic void	iwn_watchdog(void *);
211206477Sbschmidtstatic int	iwn_ioctl(struct ifnet *, u_long, caddr_t);
212206477Sbschmidtstatic int	iwn_cmd(struct iwn_softc *, int, const void *, int, int);
213206477Sbschmidtstatic int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
214198429Srpaulo		    int);
215206477Sbschmidtstatic int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
216198429Srpaulo		    int);
217220715Sbschmidtstatic int	iwn_set_link_quality(struct iwn_softc *,
218220715Sbschmidt		    struct ieee80211_node *);
219206477Sbschmidtstatic int	iwn_add_broadcast_node(struct iwn_softc *, int);
220220721Sbschmidtstatic int	iwn_updateedca(struct ieee80211com *);
221201209Srpaulostatic void	iwn_update_mcast(struct ifnet *);
222206477Sbschmidtstatic void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
223206477Sbschmidtstatic int	iwn_set_critical_temp(struct iwn_softc *);
224206477Sbschmidtstatic int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
225206477Sbschmidtstatic void	iwn4965_power_calibration(struct iwn_softc *, int);
226206477Sbschmidtstatic int	iwn4965_set_txpower(struct iwn_softc *,
227201882Skeramida		    struct ieee80211_channel *, int);
228206477Sbschmidtstatic int	iwn5000_set_txpower(struct iwn_softc *,
229201882Skeramida		    struct ieee80211_channel *, int);
230206477Sbschmidtstatic int	iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
231206477Sbschmidtstatic int	iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
232206477Sbschmidtstatic int	iwn_get_noise(const struct iwn_rx_general_stats *);
233206477Sbschmidtstatic int	iwn4965_get_temperature(struct iwn_softc *);
234206477Sbschmidtstatic int	iwn5000_get_temperature(struct iwn_softc *);
235206477Sbschmidtstatic int	iwn_init_sensitivity(struct iwn_softc *);
236206477Sbschmidtstatic void	iwn_collect_noise(struct iwn_softc *,
237178676Ssam		    const struct iwn_rx_general_stats *);
238206477Sbschmidtstatic int	iwn4965_init_gains(struct iwn_softc *);
239206477Sbschmidtstatic int	iwn5000_init_gains(struct iwn_softc *);
240206477Sbschmidtstatic int	iwn4965_set_gains(struct iwn_softc *);
241206477Sbschmidtstatic int	iwn5000_set_gains(struct iwn_softc *);
242206477Sbschmidtstatic void	iwn_tune_sensitivity(struct iwn_softc *,
243178676Ssam		    const struct iwn_rx_stats *);
244206477Sbschmidtstatic int	iwn_send_sensitivity(struct iwn_softc *);
245206477Sbschmidtstatic int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
246220662Sbschmidtstatic int	iwn_send_btcoex(struct iwn_softc *);
247220891Sbschmidtstatic int	iwn_send_advanced_btcoex(struct iwn_softc *);
248206477Sbschmidtstatic int	iwn_config(struct iwn_softc *);
249220634Sbschmidtstatic uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
250206477Sbschmidtstatic int	iwn_scan(struct iwn_softc *);
251206477Sbschmidtstatic int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
252206477Sbschmidtstatic int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
253221650Sbschmidtstatic int	iwn_ampdu_rx_start(struct ieee80211_node *,
254221650Sbschmidt		    struct ieee80211_rx_ampdu *, int, int, int);
255221650Sbschmidtstatic void	iwn_ampdu_rx_stop(struct ieee80211_node *,
256221650Sbschmidt		    struct ieee80211_rx_ampdu *);
257221651Sbschmidtstatic int	iwn_addba_request(struct ieee80211_node *,
258221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
259221651Sbschmidtstatic int	iwn_addba_response(struct ieee80211_node *,
260221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
261206474Sbschmidtstatic int	iwn_ampdu_tx_start(struct ieee80211com *,
262206474Sbschmidt		    struct ieee80211_node *, uint8_t);
263221651Sbschmidtstatic void	iwn_ampdu_tx_stop(struct ieee80211_node *,
264221651Sbschmidt		    struct ieee80211_tx_ampdu *);
265206474Sbschmidtstatic void	iwn4965_ampdu_tx_start(struct iwn_softc *,
266221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
267221651Sbschmidtstatic void	iwn4965_ampdu_tx_stop(struct iwn_softc *, int,
268220726Sbschmidt		    uint8_t, uint16_t);
269206474Sbschmidtstatic void	iwn5000_ampdu_tx_start(struct iwn_softc *,
270221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
271221651Sbschmidtstatic void	iwn5000_ampdu_tx_stop(struct iwn_softc *, int,
272220726Sbschmidt		    uint8_t, uint16_t);
273220674Sbschmidtstatic int	iwn5000_query_calibration(struct iwn_softc *);
274220674Sbschmidtstatic int	iwn5000_send_calibration(struct iwn_softc *);
275206477Sbschmidtstatic int	iwn5000_send_wimax_coex(struct iwn_softc *);
276220677Sbschmidtstatic int	iwn5000_crystal_calib(struct iwn_softc *);
277220676Sbschmidtstatic int	iwn5000_temp_offset_calib(struct iwn_softc *);
278206477Sbschmidtstatic int	iwn4965_post_alive(struct iwn_softc *);
279206477Sbschmidtstatic int	iwn5000_post_alive(struct iwn_softc *);
280206477Sbschmidtstatic int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
281198429Srpaulo		    int);
282206477Sbschmidtstatic int	iwn4965_load_firmware(struct iwn_softc *);
283206477Sbschmidtstatic int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
284198429Srpaulo		    const uint8_t *, int);
285206477Sbschmidtstatic int	iwn5000_load_firmware(struct iwn_softc *);
286210111Sbschmidtstatic int	iwn_read_firmware_leg(struct iwn_softc *,
287210111Sbschmidt		    struct iwn_fw_info *);
288210111Sbschmidtstatic int	iwn_read_firmware_tlv(struct iwn_softc *,
289210111Sbschmidt		    struct iwn_fw_info *, uint16_t);
290206477Sbschmidtstatic int	iwn_read_firmware(struct iwn_softc *);
291206477Sbschmidtstatic int	iwn_clock_wait(struct iwn_softc *);
292206477Sbschmidtstatic int	iwn_apm_init(struct iwn_softc *);
293206477Sbschmidtstatic void	iwn_apm_stop_master(struct iwn_softc *);
294206477Sbschmidtstatic void	iwn_apm_stop(struct iwn_softc *);
295206477Sbschmidtstatic int	iwn4965_nic_config(struct iwn_softc *);
296206477Sbschmidtstatic int	iwn5000_nic_config(struct iwn_softc *);
297206477Sbschmidtstatic int	iwn_hw_prepare(struct iwn_softc *);
298206477Sbschmidtstatic int	iwn_hw_init(struct iwn_softc *);
299206477Sbschmidtstatic void	iwn_hw_stop(struct iwn_softc *);
300220723Sbschmidtstatic void	iwn_radio_on(void *, int);
301220723Sbschmidtstatic void	iwn_radio_off(void *, int);
302206477Sbschmidtstatic void	iwn_init_locked(struct iwn_softc *);
303206477Sbschmidtstatic void	iwn_init(void *);
304206477Sbschmidtstatic void	iwn_stop_locked(struct iwn_softc *);
305206477Sbschmidtstatic void	iwn_stop(struct iwn_softc *);
306220726Sbschmidtstatic void	iwn_scan_start(struct ieee80211com *);
307220726Sbschmidtstatic void	iwn_scan_end(struct ieee80211com *);
308220726Sbschmidtstatic void	iwn_set_channel(struct ieee80211com *);
309220726Sbschmidtstatic void	iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
310220726Sbschmidtstatic void	iwn_scan_mindwell(struct ieee80211_scan_state *);
311198429Srpaulostatic void	iwn_hw_reset(void *, int);
312178676Ssam
313178676Ssam#define IWN_DEBUG
314178676Ssam#ifdef IWN_DEBUG
315178676Ssamenum {
316178676Ssam	IWN_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
317178676Ssam	IWN_DEBUG_RECV		= 0x00000002,	/* basic recv operation */
318178676Ssam	IWN_DEBUG_STATE		= 0x00000004,	/* 802.11 state transitions */
319178676Ssam	IWN_DEBUG_TXPOW		= 0x00000008,	/* tx power processing */
320178676Ssam	IWN_DEBUG_RESET		= 0x00000010,	/* reset processing */
321178676Ssam	IWN_DEBUG_OPS		= 0x00000020,	/* iwn_ops processing */
322178676Ssam	IWN_DEBUG_BEACON 	= 0x00000040,	/* beacon handling */
323178676Ssam	IWN_DEBUG_WATCHDOG 	= 0x00000080,	/* watchdog timeout */
324178676Ssam	IWN_DEBUG_INTR		= 0x00000100,	/* ISR */
325178676Ssam	IWN_DEBUG_CALIBRATE	= 0x00000200,	/* periodic calibration */
326178676Ssam	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
327178676Ssam	IWN_DEBUG_LED		= 0x00000800,	/* led management */
328178676Ssam	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
329178676Ssam	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
330178676Ssam	IWN_DEBUG_ANY		= 0xffffffff
331178676Ssam};
332178676Ssam
333178676Ssam#define DPRINTF(sc, m, fmt, ...) do {			\
334178676Ssam	if (sc->sc_debug & (m))				\
335178676Ssam		printf(fmt, __VA_ARGS__);		\
336178676Ssam} while (0)
337178676Ssam
338220723Sbschmidtstatic const char *
339220723Sbschmidtiwn_intr_str(uint8_t cmd)
340220723Sbschmidt{
341220723Sbschmidt	switch (cmd) {
342220723Sbschmidt	/* Notifications */
343220723Sbschmidt	case IWN_UC_READY:		return "UC_READY";
344220723Sbschmidt	case IWN_ADD_NODE_DONE:		return "ADD_NODE_DONE";
345220723Sbschmidt	case IWN_TX_DONE:		return "TX_DONE";
346220723Sbschmidt	case IWN_START_SCAN:		return "START_SCAN";
347220723Sbschmidt	case IWN_STOP_SCAN:		return "STOP_SCAN";
348220723Sbschmidt	case IWN_RX_STATISTICS:		return "RX_STATS";
349220723Sbschmidt	case IWN_BEACON_STATISTICS:	return "BEACON_STATS";
350220723Sbschmidt	case IWN_STATE_CHANGED:		return "STATE_CHANGED";
351220723Sbschmidt	case IWN_BEACON_MISSED:		return "BEACON_MISSED";
352220723Sbschmidt	case IWN_RX_PHY:		return "RX_PHY";
353220723Sbschmidt	case IWN_MPDU_RX_DONE:		return "MPDU_RX_DONE";
354220723Sbschmidt	case IWN_RX_DONE:		return "RX_DONE";
355220723Sbschmidt
356220723Sbschmidt	/* Command Notifications */
357220723Sbschmidt	case IWN_CMD_RXON:		return "IWN_CMD_RXON";
358220723Sbschmidt	case IWN_CMD_RXON_ASSOC:	return "IWN_CMD_RXON_ASSOC";
359220723Sbschmidt	case IWN_CMD_EDCA_PARAMS:	return "IWN_CMD_EDCA_PARAMS";
360220723Sbschmidt	case IWN_CMD_TIMING:		return "IWN_CMD_TIMING";
361220723Sbschmidt	case IWN_CMD_LINK_QUALITY:	return "IWN_CMD_LINK_QUALITY";
362220723Sbschmidt	case IWN_CMD_SET_LED:		return "IWN_CMD_SET_LED";
363220723Sbschmidt	case IWN5000_CMD_WIMAX_COEX:	return "IWN5000_CMD_WIMAX_COEX";
364220723Sbschmidt	case IWN5000_CMD_CALIB_CONFIG:	return "IWN5000_CMD_CALIB_CONFIG";
365220723Sbschmidt	case IWN5000_CMD_CALIB_RESULT:	return "IWN5000_CMD_CALIB_RESULT";
366220723Sbschmidt	case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
367220723Sbschmidt	case IWN_CMD_SET_POWER_MODE:	return "IWN_CMD_SET_POWER_MODE";
368220723Sbschmidt	case IWN_CMD_SCAN:		return "IWN_CMD_SCAN";
369220723Sbschmidt	case IWN_CMD_SCAN_RESULTS:	return "IWN_CMD_SCAN_RESULTS";
370220723Sbschmidt	case IWN_CMD_TXPOWER:		return "IWN_CMD_TXPOWER";
371220723Sbschmidt	case IWN_CMD_TXPOWER_DBM:	return "IWN_CMD_TXPOWER_DBM";
372220723Sbschmidt	case IWN5000_CMD_TX_ANT_CONFIG:	return "IWN5000_CMD_TX_ANT_CONFIG";
373220723Sbschmidt	case IWN_CMD_BT_COEX:		return "IWN_CMD_BT_COEX";
374220723Sbschmidt	case IWN_CMD_SET_CRITICAL_TEMP:	return "IWN_CMD_SET_CRITICAL_TEMP";
375220723Sbschmidt	case IWN_CMD_SET_SENSITIVITY:	return "IWN_CMD_SET_SENSITIVITY";
376220723Sbschmidt	case IWN_CMD_PHY_CALIB:		return "IWN_CMD_PHY_CALIB";
377220723Sbschmidt	}
378220723Sbschmidt	return "UNKNOWN INTR NOTIF/CMD";
379220723Sbschmidt}
380178676Ssam#else
381178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
382178676Ssam#endif
383178676Ssam
384220723Sbschmidtstatic device_method_t iwn_methods[] = {
385220723Sbschmidt	/* Device interface */
386220723Sbschmidt	DEVMETHOD(device_probe,		iwn_probe),
387220723Sbschmidt	DEVMETHOD(device_attach,	iwn_attach),
388220723Sbschmidt	DEVMETHOD(device_detach,	iwn_detach),
389220723Sbschmidt	DEVMETHOD(device_shutdown,	iwn_shutdown),
390220723Sbschmidt	DEVMETHOD(device_suspend,	iwn_suspend),
391220723Sbschmidt	DEVMETHOD(device_resume,	iwn_resume),
392220723Sbschmidt	{ 0, 0 }
393178676Ssam};
394178676Ssam
395220723Sbschmidtstatic driver_t iwn_driver = {
396220723Sbschmidt	"iwn",
397220723Sbschmidt	iwn_methods,
398220726Sbschmidt	sizeof(struct iwn_softc)
399178676Ssam};
400220723Sbschmidtstatic devclass_t iwn_devclass;
401178676Ssam
402220723SbschmidtDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0);
403220726Sbschmidt
404220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1);
405220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1);
406220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1);
407220723Sbschmidt
408178676Ssamstatic int
409178676Ssamiwn_probe(device_t dev)
410178676Ssam{
411198429Srpaulo	const struct iwn_ident *ident;
412178676Ssam
413198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
414198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
415198429Srpaulo		    pci_get_device(dev) == ident->device) {
416198429Srpaulo			device_set_desc(dev, ident->name);
417198429Srpaulo			return 0;
418198429Srpaulo		}
419198429Srpaulo	}
420198429Srpaulo	return ENXIO;
421178676Ssam}
422178676Ssam
423178676Ssamstatic int
424178676Ssamiwn_attach(device_t dev)
425178676Ssam{
426178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
427178676Ssam	struct ieee80211com *ic;
428178676Ssam	struct ifnet *ifp;
429220721Sbschmidt	uint32_t reg;
430184233Smav	int i, error, result;
431190526Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
432178676Ssam
433178676Ssam	sc->sc_dev = dev;
434178676Ssam
435198429Srpaulo	/*
436198429Srpaulo	 * Get the offset of the PCI Express Capability Structure in PCI
437198429Srpaulo	 * Configuration Space.
438198429Srpaulo	 */
439219902Sjhb	error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
440198429Srpaulo	if (error != 0) {
441198429Srpaulo		device_printf(dev, "PCIe capability structure not found!\n");
442198429Srpaulo		return error;
443178676Ssam	}
444178676Ssam
445198429Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
446178676Ssam	pci_write_config(dev, 0x41, 0, 1);
447178676Ssam
448198429Srpaulo	/* Hardware bug workaround. */
449220721Sbschmidt	reg = pci_read_config(dev, PCIR_COMMAND, 1);
450220721Sbschmidt	if (reg & PCIM_CMD_INTxDIS) {
451198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n",
452198429Srpaulo		    __func__);
453220721Sbschmidt		reg &= ~PCIM_CMD_INTxDIS;
454220721Sbschmidt		pci_write_config(dev, PCIR_COMMAND, reg, 1);
455198429Srpaulo	}
456198429Srpaulo
457198429Srpaulo	/* Enable bus-mastering. */
458178676Ssam	pci_enable_busmaster(dev);
459178676Ssam
460198429Srpaulo	sc->mem_rid = PCIR_BAR(0);
461178676Ssam	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
462198429Srpaulo	    RF_ACTIVE);
463220726Sbschmidt	if (sc->mem == NULL) {
464220724Sbschmidt		device_printf(dev, "can't map mem space\n");
465198429Srpaulo		error = ENOMEM;
466178676Ssam		return error;
467178676Ssam	}
468178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
469178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
470220726Sbschmidt
471178676Ssam	sc->irq_rid = 0;
472184233Smav	if ((result = pci_msi_count(dev)) == 1 &&
473184233Smav	    pci_alloc_msi(dev, &result) == 0)
474184233Smav		sc->irq_rid = 1;
475220725Sbschmidt	/* Install interrupt handler. */
476178676Ssam	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
477198429Srpaulo	    RF_ACTIVE | RF_SHAREABLE);
478178676Ssam	if (sc->irq == NULL) {
479220724Sbschmidt		device_printf(dev, "can't map interrupt\n");
480178676Ssam		error = ENOMEM;
481198429Srpaulo		goto fail;
482178676Ssam	}
483178676Ssam
484178676Ssam	IWN_LOCK_INIT(sc);
485178676Ssam
486220728Sbschmidt	/* Read hardware revision and attach. */
487220728Sbschmidt	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf;
488220728Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
489220728Sbschmidt		error = iwn4965_attach(sc, pci_get_device(dev));
490220728Sbschmidt	else
491220728Sbschmidt		error = iwn5000_attach(sc, pci_get_device(dev));
492220728Sbschmidt	if (error != 0) {
493220728Sbschmidt		device_printf(dev, "could not attach device, error %d\n",
494220728Sbschmidt		    error);
495198429Srpaulo		goto fail;
496198429Srpaulo	}
497198429Srpaulo
498220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
499198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
500178676Ssam		goto fail;
501178676Ssam	}
502178676Ssam
503198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
504220726Sbschmidt	if ((error = iwn_alloc_fwmem(sc)) != 0) {
505178676Ssam		device_printf(dev,
506198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
507198429Srpaulo		    error);
508178676Ssam		goto fail;
509178676Ssam	}
510178676Ssam
511198429Srpaulo	/* Allocate "Keep Warm" page. */
512220726Sbschmidt	if ((error = iwn_alloc_kw(sc)) != 0) {
513178676Ssam		device_printf(dev,
514220724Sbschmidt		    "could not allocate keep warm page, error %d\n", error);
515178676Ssam		goto fail;
516178676Ssam	}
517178676Ssam
518201209Srpaulo	/* Allocate ICT table for 5000 Series. */
519201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
520201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
521220724Sbschmidt		device_printf(dev, "could not allocate ICT table, error %d\n",
522220724Sbschmidt		    error);
523201209Srpaulo		goto fail;
524201209Srpaulo	}
525201209Srpaulo
526198429Srpaulo	/* Allocate TX scheduler "rings". */
527220726Sbschmidt	if ((error = iwn_alloc_sched(sc)) != 0) {
528178676Ssam		device_printf(dev,
529220726Sbschmidt		    "could not allocate TX scheduler rings, error %d\n", error);
530178676Ssam		goto fail;
531178676Ssam	}
532178676Ssam
533220725Sbschmidt	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
534220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
535220726Sbschmidt		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
536178676Ssam			device_printf(dev,
537220724Sbschmidt			    "could not allocate TX ring %d, error %d\n", i,
538220724Sbschmidt			    error);
539178676Ssam			goto fail;
540178676Ssam		}
541178676Ssam	}
542178676Ssam
543198429Srpaulo	/* Allocate RX ring. */
544220726Sbschmidt	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
545220724Sbschmidt		device_printf(dev, "could not allocate RX ring, error %d\n",
546220724Sbschmidt		    error);
547178676Ssam		goto fail;
548178676Ssam	}
549178676Ssam
550198429Srpaulo	/* Clear pending interrupts. */
551198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
552198429Srpaulo
553178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
554178676Ssam	if (ifp == NULL) {
555178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
556178676Ssam		goto fail;
557178676Ssam	}
558220726Sbschmidt
559178676Ssam	ic = ifp->if_l2com;
560198429Srpaulo	ic->ic_ifp = ifp;
561178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
562178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
563178676Ssam
564198429Srpaulo	/* Set device capabilities. */
565178676Ssam	ic->ic_caps =
566178957Ssam		  IEEE80211_C_STA		/* station mode supported */
567178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
568178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
569178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
570178676Ssam		| IEEE80211_C_WPA
571178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
572178676Ssam#if 0
573178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
574178676Ssam#endif
575178676Ssam		| IEEE80211_C_WME		/* WME */
576178676Ssam		;
577221640Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_4965)
578221640Sbschmidt		ic->ic_caps |= IEEE80211_C_BGSCAN; /* background scanning */
579221640Sbschmidt
580221642Sbschmidt	/* Read MAC address, channels, etc from EEPROM. */
581221642Sbschmidt	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
582221642Sbschmidt		device_printf(dev, "could not read EEPROM, error %d\n",
583221642Sbschmidt		    error);
584221642Sbschmidt		goto fail;
585221642Sbschmidt	}
586221642Sbschmidt
587221642Sbschmidt	/* Count the number of available chains. */
588221642Sbschmidt	sc->ntxchains =
589221642Sbschmidt	    ((sc->txchainmask >> 2) & 1) +
590221642Sbschmidt	    ((sc->txchainmask >> 1) & 1) +
591221642Sbschmidt	    ((sc->txchainmask >> 0) & 1);
592221642Sbschmidt	sc->nrxchains =
593221642Sbschmidt	    ((sc->rxchainmask >> 2) & 1) +
594221642Sbschmidt	    ((sc->rxchainmask >> 1) & 1) +
595221642Sbschmidt	    ((sc->rxchainmask >> 0) & 1);
596221642Sbschmidt	if (bootverbose) {
597221642Sbschmidt		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
598221642Sbschmidt		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
599221642Sbschmidt		    macaddr, ":");
600221642Sbschmidt	}
601221642Sbschmidt
602221657Sbschmidt	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
603221657Sbschmidt		ic->ic_rxstream = sc->nrxchains;
604221657Sbschmidt		ic->ic_txstream = sc->ntxchains;
605221657Sbschmidt		ic->ic_htcaps =
606221657Sbschmidt			  IEEE80211_HTCAP_SMPS_OFF	/* SMPS mode disabled */
607221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
608221657Sbschmidt#ifdef notyet
609221657Sbschmidt			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
610221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
611221657Sbschmidt			| IEEE80211_HTCAP_GREENFIELD
612201209Srpaulo#if IWN_RBUF_SIZE == 8192
613221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_7935	/* max A-MSDU length */
614221657Sbschmidt#else
615221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
616178678Ssam#endif
617201209Srpaulo#endif
618221657Sbschmidt			/* s/w capabilities */
619221657Sbschmidt			| IEEE80211_HTC_HT		/* HT operation */
620221657Sbschmidt			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
621221657Sbschmidt#ifdef notyet
622221657Sbschmidt			| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
623201209Srpaulo#endif
624221657Sbschmidt			;
625221657Sbschmidt	}
626201209Srpaulo
627178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
628178676Ssam	ifp->if_softc = sc;
629178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
630178676Ssam	ifp->if_init = iwn_init;
631178676Ssam	ifp->if_ioctl = iwn_ioctl;
632178676Ssam	ifp->if_start = iwn_start;
633207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
634207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
635178676Ssam	IFQ_SET_READY(&ifp->if_snd);
636178676Ssam
637190526Ssam	ieee80211_ifattach(ic, macaddr);
638178676Ssam	ic->ic_vap_create = iwn_vap_create;
639178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
640178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
641178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
642221650Sbschmidt	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
643220723Sbschmidt	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
644221650Sbschmidt	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
645220723Sbschmidt	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
646221651Sbschmidt	sc->sc_addba_request = ic->ic_addba_request;
647221651Sbschmidt	ic->ic_addba_request = iwn_addba_request;
648221651Sbschmidt	sc->sc_addba_response = ic->ic_addba_response;
649221651Sbschmidt	ic->ic_addba_response = iwn_addba_response;
650221651Sbschmidt	sc->sc_addba_stop = ic->ic_addba_stop;
651221651Sbschmidt	ic->ic_addba_stop = iwn_ampdu_tx_stop;
652220715Sbschmidt	ic->ic_newassoc = iwn_newassoc;
653220721Sbschmidt	ic->ic_wme.wme_update = iwn_updateedca;
654201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
655198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
656198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
657198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
658198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
659198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
660201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
661178676Ssam
662198429Srpaulo	iwn_radiotap_attach(sc);
663220667Sbschmidt
664220667Sbschmidt	callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
665220667Sbschmidt	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
666220726Sbschmidt	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
667220726Sbschmidt	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
668220726Sbschmidt	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
669220667Sbschmidt
670178676Ssam	iwn_sysctlattach(sc);
671178676Ssam
672198429Srpaulo	/*
673198429Srpaulo	 * Hook our interrupt after all initialization is complete.
674198429Srpaulo	 */
675198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
676178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
677198429Srpaulo	if (error != 0) {
678220724Sbschmidt		device_printf(dev, "can't establish interrupt, error %d\n",
679198429Srpaulo		    error);
680198429Srpaulo		goto fail;
681198429Srpaulo	}
682178676Ssam
683220724Sbschmidt	if (bootverbose)
684220724Sbschmidt		ieee80211_announce(ic);
685178676Ssam	return 0;
686178676Ssamfail:
687220635Sbschmidt	iwn_detach(dev);
688178676Ssam	return error;
689178676Ssam}
690178676Ssam
691220728Sbschmidtstatic int
692220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid)
693178676Ssam{
694220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
695198429Srpaulo
696220728Sbschmidt	ops->load_firmware = iwn4965_load_firmware;
697220728Sbschmidt	ops->read_eeprom = iwn4965_read_eeprom;
698220728Sbschmidt	ops->post_alive = iwn4965_post_alive;
699220728Sbschmidt	ops->nic_config = iwn4965_nic_config;
700220728Sbschmidt	ops->update_sched = iwn4965_update_sched;
701220728Sbschmidt	ops->get_temperature = iwn4965_get_temperature;
702220728Sbschmidt	ops->get_rssi = iwn4965_get_rssi;
703220728Sbschmidt	ops->set_txpower = iwn4965_set_txpower;
704220728Sbschmidt	ops->init_gains = iwn4965_init_gains;
705220728Sbschmidt	ops->set_gains = iwn4965_set_gains;
706220728Sbschmidt	ops->add_node = iwn4965_add_node;
707220728Sbschmidt	ops->tx_done = iwn4965_tx_done;
708220728Sbschmidt	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
709220728Sbschmidt	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
710220728Sbschmidt	sc->ntxqs = IWN4965_NTXQUEUES;
711221651Sbschmidt	sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
712220728Sbschmidt	sc->ndmachnls = IWN4965_NDMACHNLS;
713220728Sbschmidt	sc->broadcast_id = IWN4965_ID_BROADCAST;
714220728Sbschmidt	sc->rxonsz = IWN4965_RXONSZ;
715220728Sbschmidt	sc->schedsz = IWN4965_SCHEDSZ;
716220728Sbschmidt	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
717220728Sbschmidt	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
718220728Sbschmidt	sc->fwsz = IWN4965_FWSZ;
719220728Sbschmidt	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
720220728Sbschmidt	sc->limits = &iwn4965_sensitivity_limits;
721220728Sbschmidt	sc->fwname = "iwn4965fw";
722220728Sbschmidt	/* Override chains masks, ROM is known to be broken. */
723220728Sbschmidt	sc->txchainmask = IWN_ANT_AB;
724220728Sbschmidt	sc->rxchainmask = IWN_ANT_ABC;
725220728Sbschmidt
726220728Sbschmidt	return 0;
727220728Sbschmidt}
728220728Sbschmidt
729220728Sbschmidtstatic int
730220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid)
731220728Sbschmidt{
732220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
733220728Sbschmidt
734220728Sbschmidt	ops->load_firmware = iwn5000_load_firmware;
735220728Sbschmidt	ops->read_eeprom = iwn5000_read_eeprom;
736220728Sbschmidt	ops->post_alive = iwn5000_post_alive;
737220728Sbschmidt	ops->nic_config = iwn5000_nic_config;
738220728Sbschmidt	ops->update_sched = iwn5000_update_sched;
739220728Sbschmidt	ops->get_temperature = iwn5000_get_temperature;
740220728Sbschmidt	ops->get_rssi = iwn5000_get_rssi;
741220728Sbschmidt	ops->set_txpower = iwn5000_set_txpower;
742220728Sbschmidt	ops->init_gains = iwn5000_init_gains;
743220728Sbschmidt	ops->set_gains = iwn5000_set_gains;
744220728Sbschmidt	ops->add_node = iwn5000_add_node;
745220728Sbschmidt	ops->tx_done = iwn5000_tx_done;
746220728Sbschmidt	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
747220728Sbschmidt	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
748220728Sbschmidt	sc->ntxqs = IWN5000_NTXQUEUES;
749221651Sbschmidt	sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
750220728Sbschmidt	sc->ndmachnls = IWN5000_NDMACHNLS;
751220728Sbschmidt	sc->broadcast_id = IWN5000_ID_BROADCAST;
752220728Sbschmidt	sc->rxonsz = IWN5000_RXONSZ;
753220728Sbschmidt	sc->schedsz = IWN5000_SCHEDSZ;
754220728Sbschmidt	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
755220728Sbschmidt	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
756220728Sbschmidt	sc->fwsz = IWN5000_FWSZ;
757220728Sbschmidt	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
758220866Sbschmidt	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
759220866Sbschmidt	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
760220728Sbschmidt
761198429Srpaulo	switch (sc->hw_type) {
762198429Srpaulo	case IWN_HW_REV_TYPE_5100:
763201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
764198439Srpaulo		sc->fwname = "iwn5000fw";
765220727Sbschmidt		/* Override chains masks, ROM is known to be broken. */
766201209Srpaulo		sc->txchainmask = IWN_ANT_B;
767201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
768198429Srpaulo		break;
769198429Srpaulo	case IWN_HW_REV_TYPE_5150:
770201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
771198439Srpaulo		sc->fwname = "iwn5150fw";
772198429Srpaulo		break;
773198429Srpaulo	case IWN_HW_REV_TYPE_5300:
774198429Srpaulo	case IWN_HW_REV_TYPE_5350:
775201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
776198439Srpaulo		sc->fwname = "iwn5000fw";
777198429Srpaulo		break;
778198429Srpaulo	case IWN_HW_REV_TYPE_1000:
779206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
780198439Srpaulo		sc->fwname = "iwn1000fw";
781198429Srpaulo		break;
782198429Srpaulo	case IWN_HW_REV_TYPE_6000:
783201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
784198439Srpaulo		sc->fwname = "iwn6000fw";
785220728Sbschmidt		if (pid == 0x422c || pid == 0x4239) {
786201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
787220727Sbschmidt			/* Override chains masks, ROM is known to be broken. */
788201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
789201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
790201209Srpaulo		}
791198429Srpaulo		break;
792198429Srpaulo	case IWN_HW_REV_TYPE_6050:
793201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
794210109Sbschmidt		sc->fwname = "iwn6050fw";
795220867Sbschmidt		/* Override chains masks, ROM is known to be broken. */
796220867Sbschmidt		sc->txchainmask = IWN_ANT_AB;
797220867Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
798198429Srpaulo		break;
799210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
800210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
801220894Sbschmidt		if (pid != 0x0082 && pid != 0x0085) {
802220894Sbschmidt			sc->fwname = "iwn6000g2bfw";
803220891Sbschmidt			sc->sc_flags |= IWN_FLAG_ADV_BTCOEX;
804220894Sbschmidt		} else
805220894Sbschmidt			sc->fwname = "iwn6000g2afw";
806210109Sbschmidt		break;
807198429Srpaulo	default:
808198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
809198429Srpaulo		    sc->hw_type);
810220728Sbschmidt		return ENOTSUP;
811198429Srpaulo	}
812220728Sbschmidt	return 0;
813178676Ssam}
814178676Ssam
815178676Ssam/*
816198429Srpaulo * Attach the interface to 802.11 radiotap.
817178676Ssam */
818206477Sbschmidtstatic void
819198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
820178676Ssam{
821178676Ssam	struct ifnet *ifp = sc->sc_ifp;
822178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
823178676Ssam
824198429Srpaulo	ieee80211_radiotap_attach(ic,
825198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
826198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
827198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
828198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
829178676Ssam}
830178676Ssam
831220723Sbschmidtstatic void
832220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc)
833220723Sbschmidt{
834220723Sbschmidt	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
835220723Sbschmidt	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
836220723Sbschmidt
837220723Sbschmidt#ifdef IWN_DEBUG
838220723Sbschmidt	sc->sc_debug = 0;
839220723Sbschmidt	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
840220723Sbschmidt	    "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs");
841220723Sbschmidt#endif
842220723Sbschmidt}
843220723Sbschmidt
844178676Ssamstatic struct ieee80211vap *
845178676Ssamiwn_vap_create(struct ieee80211com *ic,
846220726Sbschmidt    const char name[IFNAMSIZ], int unit, int opmode, int flags,
847220726Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
848220726Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
849178676Ssam{
850178676Ssam	struct iwn_vap *ivp;
851178676Ssam	struct ieee80211vap *vap;
852178676Ssam
853178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
854178676Ssam		return NULL;
855178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
856178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
857178676Ssam	if (ivp == NULL)
858178676Ssam		return NULL;
859178676Ssam	vap = &ivp->iv_vap;
860178676Ssam	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
861178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
862198429Srpaulo	/* Override with driver methods. */
863178676Ssam	ivp->iv_newstate = vap->iv_newstate;
864178676Ssam	vap->iv_newstate = iwn_newstate;
865178676Ssam
866206358Srpaulo	ieee80211_ratectl_init(vap);
867198429Srpaulo	/* Complete setup. */
868206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
869178676Ssam	ic->ic_opmode = opmode;
870178676Ssam	return vap;
871178676Ssam}
872178676Ssam
873178676Ssamstatic void
874178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
875178676Ssam{
876178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
877178676Ssam
878206358Srpaulo	ieee80211_ratectl_deinit(vap);
879178676Ssam	ieee80211_vap_detach(vap);
880178676Ssam	free(ivp, M_80211_VAP);
881178676Ssam}
882178676Ssam
883206477Sbschmidtstatic int
884220635Sbschmidtiwn_detach(device_t dev)
885178676Ssam{
886178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
887198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
888198429Srpaulo	struct ieee80211com *ic;
889220721Sbschmidt	int qid;
890178676Ssam
891198429Srpaulo	if (ifp != NULL) {
892198429Srpaulo		ic = ifp->if_l2com;
893198429Srpaulo
894198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
895198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
896198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
897198429Srpaulo
898198429Srpaulo		iwn_stop(sc);
899220667Sbschmidt		callout_drain(&sc->watchdog_to);
900220667Sbschmidt		callout_drain(&sc->calib_to);
901198429Srpaulo		ieee80211_ifdetach(ic);
902198429Srpaulo	}
903198429Srpaulo
904220725Sbschmidt	/* Uninstall interrupt handler. */
905220723Sbschmidt	if (sc->irq != NULL) {
906220723Sbschmidt		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
907220723Sbschmidt		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
908220723Sbschmidt		if (sc->irq_rid == 1)
909220723Sbschmidt			pci_release_msi(dev);
910220723Sbschmidt	}
911220723Sbschmidt
912201209Srpaulo	/* Free DMA resources. */
913198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
914220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
915220728Sbschmidt		iwn_free_tx_ring(sc, &sc->txq[qid]);
916198429Srpaulo	iwn_free_sched(sc);
917198429Srpaulo	iwn_free_kw(sc);
918201209Srpaulo	if (sc->ict != NULL)
919201209Srpaulo		iwn_free_ict(sc);
920198429Srpaulo	iwn_free_fwmem(sc);
921198429Srpaulo
922198429Srpaulo	if (sc->mem != NULL)
923198429Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
924198429Srpaulo
925198429Srpaulo	if (ifp != NULL)
926198429Srpaulo		if_free(ifp);
927198429Srpaulo
928198429Srpaulo	IWN_LOCK_DESTROY(sc);
929178676Ssam	return 0;
930178676Ssam}
931178676Ssam
932178676Ssamstatic int
933220723Sbschmidtiwn_shutdown(device_t dev)
934220723Sbschmidt{
935220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
936220723Sbschmidt
937220723Sbschmidt	iwn_stop(sc);
938220723Sbschmidt	return 0;
939220723Sbschmidt}
940220723Sbschmidt
941220723Sbschmidtstatic int
942220723Sbschmidtiwn_suspend(device_t dev)
943220723Sbschmidt{
944220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
945220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
946220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
947220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
948220723Sbschmidt
949220723Sbschmidt	iwn_stop(sc);
950220723Sbschmidt	if (vap != NULL)
951220723Sbschmidt		ieee80211_stop(vap);
952220723Sbschmidt	return 0;
953220723Sbschmidt}
954220723Sbschmidt
955220723Sbschmidtstatic int
956220723Sbschmidtiwn_resume(device_t dev)
957220723Sbschmidt{
958220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
959220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
960220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
961220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
962220723Sbschmidt
963220723Sbschmidt	/* Clear device-specific "PCI retry timeout" register (41h). */
964220723Sbschmidt	pci_write_config(dev, 0x41, 0, 1);
965220723Sbschmidt
966220723Sbschmidt	if (ifp->if_flags & IFF_UP) {
967220723Sbschmidt		iwn_init(sc);
968220723Sbschmidt		if (vap != NULL)
969220723Sbschmidt			ieee80211_init(vap);
970220723Sbschmidt		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
971220723Sbschmidt			iwn_start(ifp);
972220723Sbschmidt	}
973220723Sbschmidt	return 0;
974220723Sbschmidt}
975220723Sbschmidt
976220723Sbschmidtstatic int
977198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
978178676Ssam{
979198429Srpaulo	int ntries;
980178676Ssam
981198429Srpaulo	/* Request exclusive access to NIC. */
982198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
983178676Ssam
984198429Srpaulo	/* Spin until we actually get the lock. */
985198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
986198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
987220726Sbschmidt		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
988198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
989198429Srpaulo			return 0;
990198429Srpaulo		DELAY(10);
991198429Srpaulo	}
992198429Srpaulo	return ETIMEDOUT;
993198429Srpaulo}
994198429Srpaulo
995198429Srpaulostatic __inline void
996198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
997198429Srpaulo{
998198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
999198429Srpaulo}
1000198429Srpaulo
1001198429Srpaulostatic __inline uint32_t
1002198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1003198429Srpaulo{
1004198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1005201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1006198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
1007198429Srpaulo}
1008198429Srpaulo
1009198429Srpaulostatic __inline void
1010198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1011198429Srpaulo{
1012198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1013201209Srpaulo	IWN_BARRIER_WRITE(sc);
1014198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1015198429Srpaulo}
1016198429Srpaulo
1017198429Srpaulostatic __inline void
1018198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1019198429Srpaulo{
1020198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1021198429Srpaulo}
1022198429Srpaulo
1023198429Srpaulostatic __inline void
1024198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1025198429Srpaulo{
1026198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1027198429Srpaulo}
1028198429Srpaulo
1029198429Srpaulostatic __inline void
1030198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1031198429Srpaulo    const uint32_t *data, int count)
1032198429Srpaulo{
1033198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
1034198429Srpaulo		iwn_prph_write(sc, addr, *data);
1035198429Srpaulo}
1036198429Srpaulo
1037198429Srpaulostatic __inline uint32_t
1038198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1039198429Srpaulo{
1040198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1041201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1042198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
1043198429Srpaulo}
1044198429Srpaulo
1045198429Srpaulostatic __inline void
1046198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1047198429Srpaulo{
1048198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1049201209Srpaulo	IWN_BARRIER_WRITE(sc);
1050198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1051198429Srpaulo}
1052198429Srpaulo
1053198429Srpaulostatic __inline void
1054198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1055198429Srpaulo{
1056198429Srpaulo	uint32_t tmp;
1057198429Srpaulo
1058198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
1059198429Srpaulo	if (addr & 3)
1060198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
1061198429Srpaulo	else
1062198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
1063198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
1064198429Srpaulo}
1065198429Srpaulo
1066198429Srpaulostatic __inline void
1067198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1068198429Srpaulo    int count)
1069198429Srpaulo{
1070198429Srpaulo	for (; count > 0; count--, addr += 4)
1071198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
1072198429Srpaulo}
1073198429Srpaulo
1074198429Srpaulostatic __inline void
1075198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1076198429Srpaulo    int count)
1077198429Srpaulo{
1078198429Srpaulo	for (; count > 0; count--, addr += 4)
1079198429Srpaulo		iwn_mem_write(sc, addr, val);
1080198429Srpaulo}
1081198429Srpaulo
1082206477Sbschmidtstatic int
1083198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1084198429Srpaulo{
1085198429Srpaulo	int i, ntries;
1086198429Srpaulo
1087198429Srpaulo	for (i = 0; i < 100; i++) {
1088198429Srpaulo		/* Request exclusive access to EEPROM. */
1089198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1090198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1091198429Srpaulo
1092198429Srpaulo		/* Spin until we actually get the lock. */
1093198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1094198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1095198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1096198429Srpaulo				return 0;
1097198429Srpaulo			DELAY(10);
1098198429Srpaulo		}
1099198429Srpaulo	}
1100198429Srpaulo	return ETIMEDOUT;
1101198429Srpaulo}
1102198429Srpaulo
1103198429Srpaulostatic __inline void
1104198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1105198429Srpaulo{
1106198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1107198429Srpaulo}
1108198429Srpaulo
1109198429Srpaulo/*
1110198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1111198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1112198429Srpaulo */
1113206477Sbschmidtstatic int
1114198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1115198429Srpaulo{
1116203934Sbschmidt	uint16_t prev, base, next;
1117201209Srpaulo	int count, error;
1118198429Srpaulo
1119201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1120220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
1121198429Srpaulo		return error;
1122198429Srpaulo
1123220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
1124198429Srpaulo		return error;
1125198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1126198429Srpaulo	DELAY(5);
1127198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1128198429Srpaulo	iwn_nic_unlock(sc);
1129198429Srpaulo
1130201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1131201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1132201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1133201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1134201209Srpaulo	}
1135198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1136198429Srpaulo	/* Clear ECC status. */
1137198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1138198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1139198429Srpaulo
1140201209Srpaulo	/*
1141203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1142203934Sbschmidt	 * for HW without OTP shadow RAM.
1143201209Srpaulo	 */
1144201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1145201209Srpaulo		/* Switch to absolute addressing mode. */
1146201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1147203934Sbschmidt		base = prev = 0;
1148201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1149201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1150201209Srpaulo			if (error != 0)
1151201209Srpaulo				return error;
1152201209Srpaulo			if (next == 0)	/* End of linked-list. */
1153201209Srpaulo				break;
1154203934Sbschmidt			prev = base;
1155201209Srpaulo			base = le16toh(next);
1156201209Srpaulo		}
1157203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1158201209Srpaulo			return EIO;
1159201209Srpaulo		/* Skip "next" word. */
1160203934Sbschmidt		sc->prom_base = prev + 1;
1161201209Srpaulo	}
1162178676Ssam	return 0;
1163178676Ssam}
1164178676Ssam
1165206477Sbschmidtstatic int
1166198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1167198429Srpaulo{
1168220723Sbschmidt	uint8_t *out = data;
1169198429Srpaulo	uint32_t val, tmp;
1170198429Srpaulo	int ntries;
1171198429Srpaulo
1172201209Srpaulo	addr += sc->prom_base;
1173198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1174198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1175201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1176198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1177198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1178198429Srpaulo				break;
1179198429Srpaulo			DELAY(5);
1180198429Srpaulo		}
1181201209Srpaulo		if (ntries == 10) {
1182198429Srpaulo			device_printf(sc->sc_dev,
1183198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1184198429Srpaulo			return ETIMEDOUT;
1185198429Srpaulo		}
1186198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1187198429Srpaulo			/* OTPROM, check for ECC errors. */
1188198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1189198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1190198429Srpaulo				device_printf(sc->sc_dev,
1191198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1192198429Srpaulo				return EIO;
1193198429Srpaulo			}
1194198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1195198429Srpaulo				/* Correctable ECC error, clear bit. */
1196198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1197198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1198198429Srpaulo			}
1199198429Srpaulo		}
1200198429Srpaulo		*out++ = val >> 16;
1201198429Srpaulo		if (count > 1)
1202198429Srpaulo			*out++ = val >> 24;
1203198429Srpaulo	}
1204198429Srpaulo	return 0;
1205198429Srpaulo}
1206198429Srpaulo
1207178676Ssamstatic void
1208178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1209178676Ssam{
1210198429Srpaulo	if (error != 0)
1211198429Srpaulo		return;
1212198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1213198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1214178676Ssam}
1215178676Ssam
1216198429Srpaulostatic int
1217178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1218220691Sbschmidt    void **kvap, bus_size_t size, bus_size_t alignment)
1219178676Ssam{
1220198429Srpaulo	int error;
1221178676Ssam
1222220723Sbschmidt	dma->tag = NULL;
1223178676Ssam	dma->size = size;
1224178676Ssam
1225198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1226178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1227220691Sbschmidt	    1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag);
1228220711Sbschmidt	if (error != 0)
1229178676Ssam		goto fail;
1230220711Sbschmidt
1231178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1232220691Sbschmidt	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1233220711Sbschmidt	if (error != 0)
1234178676Ssam		goto fail;
1235220711Sbschmidt
1236220691Sbschmidt	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1237220691Sbschmidt	    iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1238220711Sbschmidt	if (error != 0)
1239178676Ssam		goto fail;
1240178676Ssam
1241220704Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1242220704Sbschmidt
1243178676Ssam	if (kvap != NULL)
1244178676Ssam		*kvap = dma->vaddr;
1245220726Sbschmidt
1246178676Ssam	return 0;
1247220726Sbschmidt
1248220726Sbschmidtfail:	iwn_dma_contig_free(dma);
1249178676Ssam	return error;
1250178676Ssam}
1251178676Ssam
1252206477Sbschmidtstatic void
1253178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1254178676Ssam{
1255220701Sbschmidt	if (dma->map != NULL) {
1256220701Sbschmidt		if (dma->vaddr != NULL) {
1257220701Sbschmidt			bus_dmamap_sync(dma->tag, dma->map,
1258220701Sbschmidt			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1259220701Sbschmidt			bus_dmamap_unload(dma->tag, dma->map);
1260178676Ssam			bus_dmamem_free(dma->tag, &dma->vaddr, dma->map);
1261220701Sbschmidt			dma->vaddr = NULL;
1262178676Ssam		}
1263220701Sbschmidt		bus_dmamap_destroy(dma->tag, dma->map);
1264220701Sbschmidt		dma->map = NULL;
1265220701Sbschmidt	}
1266220701Sbschmidt	if (dma->tag != NULL) {
1267178676Ssam		bus_dma_tag_destroy(dma->tag);
1268220701Sbschmidt		dma->tag = NULL;
1269178676Ssam	}
1270178676Ssam}
1271178676Ssam
1272206477Sbschmidtstatic int
1273198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1274178676Ssam{
1275198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1276220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1277220728Sbschmidt	    sc->schedsz, 1024);
1278178676Ssam}
1279178676Ssam
1280206477Sbschmidtstatic void
1281198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1282178676Ssam{
1283198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1284178676Ssam}
1285178676Ssam
1286206477Sbschmidtstatic int
1287178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1288178676Ssam{
1289198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1290220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1291178676Ssam}
1292178676Ssam
1293206477Sbschmidtstatic void
1294178676Ssamiwn_free_kw(struct iwn_softc *sc)
1295178676Ssam{
1296178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1297178676Ssam}
1298178676Ssam
1299206477Sbschmidtstatic int
1300201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1301201209Srpaulo{
1302201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1303220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1304220691Sbschmidt	    IWN_ICT_SIZE, 4096);
1305201209Srpaulo}
1306201209Srpaulo
1307206477Sbschmidtstatic void
1308201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1309201209Srpaulo{
1310201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1311201209Srpaulo}
1312201209Srpaulo
1313206477Sbschmidtstatic int
1314178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1315178676Ssam{
1316198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1317220728Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1318178676Ssam}
1319178676Ssam
1320206477Sbschmidtstatic void
1321178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1322178676Ssam{
1323178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1324178676Ssam}
1325178676Ssam
1326206477Sbschmidtstatic int
1327178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1328178676Ssam{
1329198429Srpaulo	bus_size_t size;
1330178676Ssam	int i, error;
1331178676Ssam
1332178676Ssam	ring->cur = 0;
1333178676Ssam
1334198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1335198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1336220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1337220691Sbschmidt	    size, 256);
1338178676Ssam	if (error != 0) {
1339178676Ssam		device_printf(sc->sc_dev,
1340220711Sbschmidt		    "%s: could not allocate RX ring DMA memory, error %d\n",
1341178676Ssam		    __func__, error);
1342178676Ssam		goto fail;
1343178676Ssam	}
1344178676Ssam
1345220702Sbschmidt	/* Allocate RX status area (16-byte aligned). */
1346220702Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1347220702Sbschmidt	    sizeof (struct iwn_rx_status), 16);
1348198429Srpaulo	if (error != 0) {
1349198429Srpaulo		device_printf(sc->sc_dev,
1350220711Sbschmidt		    "%s: could not allocate RX status DMA memory, error %d\n",
1351178676Ssam		    __func__, error);
1352198429Srpaulo		goto fail;
1353198429Srpaulo	}
1354178676Ssam
1355220702Sbschmidt	/* Create RX buffer DMA tag. */
1356220702Sbschmidt	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1357220702Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1358220702Sbschmidt	    IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL,
1359220702Sbschmidt	    &ring->data_dmat);
1360198429Srpaulo	if (error != 0) {
1361198429Srpaulo		device_printf(sc->sc_dev,
1362220711Sbschmidt		    "%s: could not create RX buf DMA tag, error %d\n",
1363198429Srpaulo		    __func__, error);
1364198429Srpaulo		goto fail;
1365198429Srpaulo	}
1366198429Srpaulo
1367178676Ssam	/*
1368198429Srpaulo	 * Allocate and map RX buffers.
1369178676Ssam	 */
1370178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1371178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1372178676Ssam		bus_addr_t paddr;
1373178676Ssam
1374201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1375178676Ssam		if (error != 0) {
1376178676Ssam			device_printf(sc->sc_dev,
1377220711Sbschmidt			    "%s: could not create RX buf DMA map, error %d\n",
1378178676Ssam			    __func__, error);
1379178676Ssam			goto fail;
1380178676Ssam		}
1381198429Srpaulo
1382220692Sbschmidt		data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR,
1383220692Sbschmidt		    IWN_RBUF_SIZE);
1384198439Srpaulo		if (data->m == NULL) {
1385178676Ssam			device_printf(sc->sc_dev,
1386220711Sbschmidt			    "%s: could not allocate RX mbuf\n", __func__);
1387220710Sbschmidt			error = ENOBUFS;
1388178676Ssam			goto fail;
1389178676Ssam		}
1390198429Srpaulo
1391201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1392220692Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1393220692Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
1394178676Ssam		if (error != 0 && error != EFBIG) {
1395178676Ssam			device_printf(sc->sc_dev,
1396220711Sbschmidt			    "%s: can't not map mbuf, error %d\n", __func__,
1397220711Sbschmidt			    error);
1398178676Ssam			goto fail;
1399178676Ssam		}
1400178676Ssam
1401198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1402178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1403178676Ssam	}
1404220726Sbschmidt
1405178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1406178676Ssam	    BUS_DMASYNC_PREWRITE);
1407220726Sbschmidt
1408178676Ssam	return 0;
1409220726Sbschmidt
1410220726Sbschmidtfail:	iwn_free_rx_ring(sc, ring);
1411178676Ssam	return error;
1412178676Ssam}
1413178676Ssam
1414206477Sbschmidtstatic void
1415178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1416178676Ssam{
1417178676Ssam	int ntries;
1418178676Ssam
1419198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1420198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1421198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1422198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1423198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1424198429Srpaulo				break;
1425198429Srpaulo			DELAY(10);
1426198429Srpaulo		}
1427198429Srpaulo		iwn_nic_unlock(sc);
1428198429Srpaulo	}
1429178676Ssam	ring->cur = 0;
1430198429Srpaulo	sc->last_rx_valid = 0;
1431178676Ssam}
1432178676Ssam
1433206477Sbschmidtstatic void
1434178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1435178676Ssam{
1436178676Ssam	int i;
1437178676Ssam
1438178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1439198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1440178676Ssam
1441198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1442198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1443198429Srpaulo
1444198429Srpaulo		if (data->m != NULL) {
1445201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1446198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1447201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1448198429Srpaulo			m_freem(data->m);
1449220710Sbschmidt			data->m = NULL;
1450198429Srpaulo		}
1451201209Srpaulo		if (data->map != NULL)
1452201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1453198429Srpaulo	}
1454220701Sbschmidt	if (ring->data_dmat != NULL) {
1455220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1456220701Sbschmidt		ring->data_dmat = NULL;
1457220701Sbschmidt	}
1458178676Ssam}
1459178676Ssam
1460206477Sbschmidtstatic int
1461178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1462178676Ssam{
1463220723Sbschmidt	bus_addr_t paddr;
1464178676Ssam	bus_size_t size;
1465178676Ssam	int i, error;
1466178676Ssam
1467178676Ssam	ring->qid = qid;
1468178676Ssam	ring->queued = 0;
1469178676Ssam	ring->cur = 0;
1470178676Ssam
1471220725Sbschmidt	/* Allocate TX descriptors (256-byte aligned). */
1472220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1473220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1474220691Sbschmidt	    size, 256);
1475178676Ssam	if (error != 0) {
1476178676Ssam		device_printf(sc->sc_dev,
1477198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1478178676Ssam		    __func__, error);
1479178676Ssam		goto fail;
1480178676Ssam	}
1481198429Srpaulo
1482220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
1483220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
1484220691Sbschmidt	    size, 4);
1485178676Ssam	if (error != 0) {
1486178676Ssam		device_printf(sc->sc_dev,
1487198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1488178676Ssam		    __func__, error);
1489178676Ssam		goto fail;
1490178676Ssam	}
1491178676Ssam
1492198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1493220726Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
1494220726Sbschmidt	    IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
1495220726Sbschmidt	    &ring->data_dmat);
1496198429Srpaulo	if (error != 0) {
1497198429Srpaulo		device_printf(sc->sc_dev,
1498220711Sbschmidt		    "%s: could not create TX buf DMA tag, error %d\n",
1499178676Ssam		    __func__, error);
1500198429Srpaulo		goto fail;
1501198429Srpaulo	}
1502178676Ssam
1503198429Srpaulo	paddr = ring->cmd_dma.paddr;
1504178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1505178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1506178676Ssam
1507198429Srpaulo		data->cmd_paddr = paddr;
1508198429Srpaulo		data->scratch_paddr = paddr + 12;
1509198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1510198429Srpaulo
1511201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1512178676Ssam		if (error != 0) {
1513178676Ssam			device_printf(sc->sc_dev,
1514220711Sbschmidt			    "%s: could not create TX buf DMA map, error %d\n",
1515178676Ssam			    __func__, error);
1516178676Ssam			goto fail;
1517178676Ssam		}
1518178676Ssam	}
1519178676Ssam	return 0;
1520220726Sbschmidt
1521220726Sbschmidtfail:	iwn_free_tx_ring(sc, ring);
1522178676Ssam	return error;
1523178676Ssam}
1524178676Ssam
1525206477Sbschmidtstatic void
1526178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1527178676Ssam{
1528198429Srpaulo	int i;
1529178676Ssam
1530178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1531178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1532178676Ssam
1533178676Ssam		if (data->m != NULL) {
1534220704Sbschmidt			bus_dmamap_sync(ring->data_dmat, data->map,
1535220704Sbschmidt			    BUS_DMASYNC_POSTWRITE);
1536201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1537178676Ssam			m_freem(data->m);
1538178676Ssam			data->m = NULL;
1539178676Ssam		}
1540178676Ssam	}
1541198429Srpaulo	/* Clear TX descriptors. */
1542198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1543198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1544198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1545198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1546178676Ssam	ring->queued = 0;
1547178676Ssam	ring->cur = 0;
1548178676Ssam}
1549178676Ssam
1550206477Sbschmidtstatic void
1551178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1552178676Ssam{
1553178676Ssam	int i;
1554178676Ssam
1555178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1556178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1557178676Ssam
1558201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1559201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1560178676Ssam
1561201209Srpaulo		if (data->m != NULL) {
1562201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1563201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1564201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1565201209Srpaulo			m_freem(data->m);
1566178676Ssam		}
1567201209Srpaulo		if (data->map != NULL)
1568201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1569178676Ssam	}
1570220701Sbschmidt	if (ring->data_dmat != NULL) {
1571220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1572220701Sbschmidt		ring->data_dmat = NULL;
1573220701Sbschmidt	}
1574178676Ssam}
1575178676Ssam
1576206477Sbschmidtstatic void
1577201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1578201209Srpaulo{
1579201209Srpaulo	/* Disable interrupts. */
1580201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1581201209Srpaulo
1582201209Srpaulo	/* Reset ICT table. */
1583201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1584201209Srpaulo	sc->ict_cur = 0;
1585201209Srpaulo
1586220725Sbschmidt	/* Set physical address of ICT table (4KB aligned). */
1587201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1588201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1589201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1590201209Srpaulo
1591201209Srpaulo	/* Enable periodic RX interrupt. */
1592201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1593201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1594201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1595201209Srpaulo
1596201209Srpaulo	/* Re-enable interrupts. */
1597201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1598201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1599201209Srpaulo}
1600201209Srpaulo
1601206477Sbschmidtstatic int
1602198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1603178676Ssam{
1604220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
1605220723Sbschmidt	uint16_t val;
1606198429Srpaulo	int error;
1607178676Ssam
1608198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1609198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1610198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1611198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1612198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1613198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1614178676Ssam
1615201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1616220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
1617201209Srpaulo		device_printf(sc->sc_dev,
1618220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
1619220726Sbschmidt		    error);
1620201209Srpaulo		return error;
1621201209Srpaulo	}
1622201209Srpaulo
1623198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1624198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1625198429Srpaulo		return EIO;
1626198429Srpaulo	}
1627220726Sbschmidt	if ((error = iwn_eeprom_lock(sc)) != 0) {
1628220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
1629198429Srpaulo		    __func__, error);
1630198429Srpaulo		return error;
1631198429Srpaulo	}
1632201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1633220726Sbschmidt		if ((error = iwn_init_otprom(sc)) != 0) {
1634201209Srpaulo			device_printf(sc->sc_dev,
1635201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1636201209Srpaulo			    __func__, error);
1637201209Srpaulo			return error;
1638201209Srpaulo		}
1639198429Srpaulo	}
1640178676Ssam
1641220729Sbschmidt	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
1642220729Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
1643220729Sbschmidt	/* Check if HT support is bonded out. */
1644220729Sbschmidt	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
1645220729Sbschmidt		sc->sc_flags |= IWN_FLAG_HAS_11N;
1646220729Sbschmidt
1647198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1648198429Srpaulo	sc->rfcfg = le16toh(val);
1649198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1650220727Sbschmidt	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
1651220727Sbschmidt	if (sc->txchainmask == 0)
1652220727Sbschmidt		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
1653220727Sbschmidt	if (sc->rxchainmask == 0)
1654220727Sbschmidt		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
1655178676Ssam
1656198429Srpaulo	/* Read MAC address. */
1657198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1658178676Ssam
1659198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1660220728Sbschmidt	ops->read_eeprom(sc);
1661178676Ssam
1662201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1663201209Srpaulo
1664198429Srpaulo	iwn_eeprom_unlock(sc);
1665198429Srpaulo	return 0;
1666178676Ssam}
1667178676Ssam
1668206477Sbschmidtstatic void
1669198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1670178676Ssam{
1671201209Srpaulo	uint32_t addr;
1672220723Sbschmidt	uint16_t val;
1673198429Srpaulo	int i;
1674178676Ssam
1675220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1676198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1677178676Ssam
1678220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1679221636Sbschmidt	for (i = 0; i < 7; i++) {
1680201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1681201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1682201209Srpaulo	}
1683198429Srpaulo
1684198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1685198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1686198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1687198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1688198429Srpaulo	/* Check that EEPROM values are within valid range. */
1689198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1690198429Srpaulo		sc->maxpwr5GHz = 38;
1691198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1692198429Srpaulo		sc->maxpwr2GHz = 38;
1693198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1694198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1695198429Srpaulo
1696198429Srpaulo	/* Read samples for each TX power group. */
1697198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1698198429Srpaulo	    sizeof sc->bands);
1699198429Srpaulo
1700198429Srpaulo	/* Read voltage at which samples were taken. */
1701198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1702198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1703198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1704198429Srpaulo	    sc->eeprom_voltage);
1705198429Srpaulo
1706198429Srpaulo#ifdef IWN_DEBUG
1707198429Srpaulo	/* Print samples. */
1708201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1709198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1710198429Srpaulo			iwn4965_print_power_group(sc, i);
1711178676Ssam	}
1712198429Srpaulo#endif
1713178676Ssam}
1714178676Ssam
1715198429Srpaulo#ifdef IWN_DEBUG
1716206477Sbschmidtstatic void
1717198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1718178676Ssam{
1719198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1720198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1721198429Srpaulo	int j, c;
1722178676Ssam
1723198429Srpaulo	printf("===band %d===\n", i);
1724198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1725198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1726198429Srpaulo	for (c = 0; c < 2; c++) {
1727198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1728198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1729198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1730198429Srpaulo			    chans[0].samples[c][j].temp,
1731198429Srpaulo			    chans[0].samples[c][j].gain,
1732198429Srpaulo			    chans[0].samples[c][j].power,
1733198429Srpaulo			    chans[0].samples[c][j].pa_det);
1734198429Srpaulo		}
1735198429Srpaulo	}
1736198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1737198429Srpaulo	for (c = 0; c < 2; c++) {
1738198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1739198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1740198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1741198429Srpaulo			    chans[1].samples[c][j].temp,
1742198429Srpaulo			    chans[1].samples[c][j].gain,
1743198429Srpaulo			    chans[1].samples[c][j].power,
1744198429Srpaulo			    chans[1].samples[c][j].pa_det);
1745198429Srpaulo		}
1746198429Srpaulo	}
1747178676Ssam}
1748198429Srpaulo#endif
1749178676Ssam
1750206477Sbschmidtstatic void
1751198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1752178676Ssam{
1753206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1754220674Sbschmidt	int32_t volt;
1755220723Sbschmidt	uint32_t base, addr;
1756220723Sbschmidt	uint16_t val;
1757198429Srpaulo	int i;
1758178676Ssam
1759220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1760198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1761198429Srpaulo	base = le16toh(val);
1762198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1763198429Srpaulo	    sc->eeprom_domain, 4);
1764178676Ssam
1765220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1766221636Sbschmidt	for (i = 0; i < 7; i++) {
1767221635Sbschmidt		if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1768221635Sbschmidt			addr = base + iwn6000_regulatory_bands[i];
1769221635Sbschmidt		else
1770221635Sbschmidt			addr = base + iwn5000_regulatory_bands[i];
1771201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1772198429Srpaulo	}
1773178676Ssam
1774201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1775201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1776201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1777201209Srpaulo
1778198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1779198429Srpaulo	base = le16toh(val);
1780206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1781206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1782220726Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
1783220726Sbschmidt	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
1784210108Sbschmidt	sc->calib_ver = hdr.version;
1785206444Sbschmidt
1786198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1787201209Srpaulo		/* Compute temperature offset. */
1788198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1789220674Sbschmidt		sc->eeprom_temp = le16toh(val);
1790198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1791198429Srpaulo		volt = le16toh(val);
1792220674Sbschmidt		sc->temp_off = sc->eeprom_temp - (volt / -5);
1793201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1794220674Sbschmidt		    sc->eeprom_temp, volt, sc->temp_off);
1795220674Sbschmidt	} else {
1796220674Sbschmidt		/* Read crystal calibration. */
1797220674Sbschmidt		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
1798220674Sbschmidt		    &sc->eeprom_crystal, sizeof (uint32_t));
1799220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
1800220674Sbschmidt		    le32toh(sc->eeprom_crystal));
1801178676Ssam	}
1802178676Ssam}
1803178676Ssam
1804201209Srpaulo/*
1805201209Srpaulo * Translate EEPROM flags to net80211.
1806201209Srpaulo */
1807201209Srpaulostatic uint32_t
1808201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1809201209Srpaulo{
1810201209Srpaulo	uint32_t nflags;
1811201209Srpaulo
1812201209Srpaulo	nflags = 0;
1813201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1814201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1815201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1816201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1817201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1818201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1819201209Srpaulo		/* XXX apparently IBSS may still be marked */
1820201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1821201209Srpaulo	}
1822201209Srpaulo
1823201209Srpaulo	return nflags;
1824201209Srpaulo}
1825201209Srpaulo
1826198429Srpaulostatic void
1827201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1828178676Ssam{
1829198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1830198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1831201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1832201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1833198429Srpaulo	struct ieee80211_channel *c;
1834220687Sbschmidt	uint8_t chan;
1835220687Sbschmidt	int i, nflags;
1836178676Ssam
1837198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1838198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1839198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1840198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1841198429Srpaulo			    band->chan[i], channels[i].flags,
1842198429Srpaulo			    channels[i].maxpwr);
1843198429Srpaulo			continue;
1844198429Srpaulo		}
1845198429Srpaulo		chan = band->chan[i];
1846201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1847178676Ssam
1848198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1849198429Srpaulo		c->ic_ieee = chan;
1850198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1851198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1852206445Sbschmidt
1853201209Srpaulo		if (n == 0) {	/* 2GHz band */
1854220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
1855198429Srpaulo			/* G =>'s B is supported */
1856198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1857198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1858198429Srpaulo			c[0] = c[-1];
1859198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1860198429Srpaulo		} else {	/* 5GHz band */
1861220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
1862198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1863178676Ssam		}
1864220723Sbschmidt
1865220723Sbschmidt		/* Save maximum allowed TX power for this channel. */
1866220723Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1867220723Sbschmidt
1868220723Sbschmidt		DPRINTF(sc, IWN_DEBUG_RESET,
1869220726Sbschmidt		    "add chan %d flags 0x%x maxpwr %d\n", chan,
1870220726Sbschmidt		    channels[i].flags, channels[i].maxpwr);
1871220723Sbschmidt
1872221636Sbschmidt		if (sc->sc_flags & IWN_FLAG_HAS_11N) {
1873221636Sbschmidt			/* add HT20, HT40 added separately */
1874221636Sbschmidt			c = &ic->ic_channels[ic->ic_nchans++];
1875221636Sbschmidt			c[0] = c[-1];
1876221636Sbschmidt			c->ic_flags |= IEEE80211_CHAN_HT20;
1877221636Sbschmidt		}
1878178676Ssam	}
1879178676Ssam}
1880178676Ssam
1881198429Srpaulostatic void
1882201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1883178676Ssam{
1884198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1885198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1886201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1887201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1888198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
1889221636Sbschmidt	uint8_t chan;
1890221636Sbschmidt	int i, nflags;
1891178676Ssam
1892221636Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_HAS_11N))
1893221636Sbschmidt		return;
1894221636Sbschmidt
1895198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1896221636Sbschmidt		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1897198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1898198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1899198429Srpaulo			    band->chan[i], channels[i].flags,
1900198429Srpaulo			    channels[i].maxpwr);
1901198429Srpaulo			continue;
1902198429Srpaulo		}
1903221636Sbschmidt		chan = band->chan[i];
1904221636Sbschmidt		nflags = iwn_eeprom_channel_flags(&channels[i]);
1905221636Sbschmidt
1906198429Srpaulo		/*
1907198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
1908198429Srpaulo		 * center channel, then the extension channel above.
1909198429Srpaulo		 */
1910221636Sbschmidt		cent = ieee80211_find_channel_byieee(ic, chan,
1911221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
1912198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
1913198429Srpaulo			device_printf(sc->sc_dev,
1914221636Sbschmidt			    "%s: no entry for channel %d\n", __func__, chan);
1915198429Srpaulo			continue;
1916198429Srpaulo		}
1917198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
1918221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
1919198429Srpaulo		if (extc == NULL) {
1920198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1921221636Sbschmidt			    "%s: skip chan %d, extension channel not found\n",
1922221636Sbschmidt			    __func__, chan);
1923198429Srpaulo			continue;
1924198429Srpaulo		}
1925178676Ssam
1926198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
1927198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
1928221636Sbschmidt		    chan, channels[i].flags, channels[i].maxpwr);
1929178676Ssam
1930198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1931198429Srpaulo		c[0] = cent[0];
1932198429Srpaulo		c->ic_extieee = extc->ic_ieee;
1933198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1934221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40U | nflags;
1935198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1936198429Srpaulo		c[0] = extc[0];
1937198429Srpaulo		c->ic_extieee = cent->ic_ieee;
1938198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1939221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40D | nflags;
1940178676Ssam	}
1941198429Srpaulo}
1942178676Ssam
1943198429Srpaulostatic void
1944201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
1945198429Srpaulo{
1946198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1947198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1948178676Ssam
1949201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
1950201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
1951201209Srpaulo
1952198429Srpaulo	if (n < 5)
1953201209Srpaulo		iwn_read_eeprom_band(sc, n);
1954198429Srpaulo	else
1955201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
1956198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
1957178676Ssam}
1958178676Ssam
1959220723Sbschmidtstatic struct iwn_eeprom_chan *
1960220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
1961220723Sbschmidt{
1962221636Sbschmidt	int band, chan, i, j;
1963220723Sbschmidt
1964221636Sbschmidt	if (IEEE80211_IS_CHAN_HT40(c)) {
1965221636Sbschmidt		band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
1966221636Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(c))
1967221636Sbschmidt			chan = c->ic_extieee;
1968221636Sbschmidt		else
1969221636Sbschmidt			chan = c->ic_ieee;
1970221636Sbschmidt		for (i = 0; i < iwn_bands[band].nchan; i++) {
1971221636Sbschmidt			if (iwn_bands[band].chan[i] == chan)
1972221636Sbschmidt				return &sc->eeprom_channels[band][i];
1973220723Sbschmidt		}
1974221636Sbschmidt	} else {
1975221636Sbschmidt		for (j = 0; j < 5; j++) {
1976221636Sbschmidt			for (i = 0; i < iwn_bands[j].nchan; i++) {
1977221636Sbschmidt				if (iwn_bands[j].chan[i] == c->ic_ieee)
1978221636Sbschmidt					return &sc->eeprom_channels[j][i];
1979221636Sbschmidt			}
1980221636Sbschmidt		}
1981220723Sbschmidt	}
1982220723Sbschmidt	return NULL;
1983220723Sbschmidt}
1984220723Sbschmidt
1985220723Sbschmidt/*
1986220723Sbschmidt * Enforce flags read from EEPROM.
1987220723Sbschmidt */
1988220723Sbschmidtstatic int
1989220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
1990220723Sbschmidt    int nchan, struct ieee80211_channel chans[])
1991220723Sbschmidt{
1992220723Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
1993220723Sbschmidt	int i;
1994220723Sbschmidt
1995220723Sbschmidt	for (i = 0; i < nchan; i++) {
1996220723Sbschmidt		struct ieee80211_channel *c = &chans[i];
1997220723Sbschmidt		struct iwn_eeprom_chan *channel;
1998220723Sbschmidt
1999220723Sbschmidt		channel = iwn_find_eeprom_channel(sc, c);
2000220723Sbschmidt		if (channel == NULL) {
2001220723Sbschmidt			if_printf(ic->ic_ifp,
2002220723Sbschmidt			    "%s: invalid channel %u freq %u/0x%x\n",
2003220723Sbschmidt			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
2004220723Sbschmidt			return EINVAL;
2005220723Sbschmidt		}
2006220723Sbschmidt		c->ic_flags |= iwn_eeprom_channel_flags(channel);
2007220723Sbschmidt	}
2008220723Sbschmidt
2009220723Sbschmidt	return 0;
2010220723Sbschmidt}
2011220723Sbschmidt
2012201209Srpaulo#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
2013201209Srpaulo
2014206477Sbschmidtstatic void
2015201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2016201209Srpaulo{
2017201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
2018221637Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2019221637Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
2020221637Sbschmidt	struct ieee80211_channel *c;
2021201209Srpaulo	uint16_t val, base;
2022201209Srpaulo	int8_t maxpwr;
2023221637Sbschmidt	uint8_t flags;
2024221637Sbschmidt	int i, j;
2025201209Srpaulo
2026201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2027201209Srpaulo	base = le16toh(val);
2028201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2029201209Srpaulo	    enhinfo, sizeof enhinfo);
2030201209Srpaulo
2031201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
2032221637Sbschmidt		flags = enhinfo[i].flags;
2033221637Sbschmidt		if (!(flags & IWN_ENHINFO_VALID))
2034201209Srpaulo			continue;	/* Skip invalid entries. */
2035201209Srpaulo
2036201209Srpaulo		maxpwr = 0;
2037201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
2038201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2039201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
2040201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2041201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
2042201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2043201209Srpaulo		if (sc->ntxchains == 2)
2044201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2045201209Srpaulo		else if (sc->ntxchains == 3)
2046201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2047201209Srpaulo
2048221637Sbschmidt		for (j = 0; j < ic->ic_nchans; j++) {
2049221637Sbschmidt			c = &ic->ic_channels[j];
2050221637Sbschmidt			if ((flags & IWN_ENHINFO_5GHZ)) {
2051221637Sbschmidt				if (!IEEE80211_IS_CHAN_A(c))
2052221637Sbschmidt					continue;
2053221637Sbschmidt			} else if ((flags & IWN_ENHINFO_OFDM)) {
2054221637Sbschmidt				if (!IEEE80211_IS_CHAN_G(c))
2055221637Sbschmidt					continue;
2056221637Sbschmidt			} else if (!IEEE80211_IS_CHAN_B(c))
2057221637Sbschmidt				continue;
2058221637Sbschmidt			if ((flags & IWN_ENHINFO_HT40)) {
2059221637Sbschmidt				if (!IEEE80211_IS_CHAN_HT40(c))
2060221637Sbschmidt					continue;
2061221637Sbschmidt			} else {
2062221637Sbschmidt				if (IEEE80211_IS_CHAN_HT40(c))
2063221637Sbschmidt					continue;
2064221637Sbschmidt			}
2065221637Sbschmidt			if (enhinfo[i].chan != 0 &&
2066221637Sbschmidt			    enhinfo[i].chan != c->ic_ieee)
2067221637Sbschmidt				continue;
2068221637Sbschmidt
2069221637Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
2070221637Sbschmidt			    "channel %d(%x), maxpwr %d\n", c->ic_ieee,
2071221637Sbschmidt			    c->ic_flags, maxpwr / 2);
2072221637Sbschmidt			c->ic_maxregpower = maxpwr / 2;
2073221637Sbschmidt			c->ic_maxpower = maxpwr;
2074221637Sbschmidt		}
2075201209Srpaulo	}
2076201209Srpaulo}
2077201209Srpaulo
2078206477Sbschmidtstatic struct ieee80211_node *
2079198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2080178676Ssam{
2081198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
2082198429Srpaulo}
2083178676Ssam
2084221648Sbschmidtstatic __inline int
2085221648Sbschmidtrate2plcp(int rate)
2086221648Sbschmidt{
2087221648Sbschmidt	switch (rate & 0xff) {
2088221648Sbschmidt	case 12:	return 0xd;
2089221648Sbschmidt	case 18:	return 0xf;
2090221648Sbschmidt	case 24:	return 0x5;
2091221648Sbschmidt	case 36:	return 0x7;
2092221648Sbschmidt	case 48:	return 0x9;
2093221648Sbschmidt	case 72:	return 0xb;
2094221648Sbschmidt	case 96:	return 0x1;
2095221648Sbschmidt	case 108:	return 0x3;
2096221648Sbschmidt	case 2:		return 10;
2097221648Sbschmidt	case 4:		return 20;
2098221648Sbschmidt	case 11:	return 55;
2099221648Sbschmidt	case 22:	return 110;
2100221648Sbschmidt	}
2101221648Sbschmidt	return 0;
2102221648Sbschmidt}
2103221648Sbschmidt
2104220715Sbschmidtstatic void
2105220715Sbschmidtiwn_newassoc(struct ieee80211_node *ni, int isnew)
2106220715Sbschmidt{
2107221648Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
2108221648Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2109220715Sbschmidt	struct iwn_node *wn = (void *)ni;
2110221649Sbschmidt	uint8_t txant1, txant2;
2111221648Sbschmidt	int i, plcp, rate, ridx;
2112220715Sbschmidt
2113221648Sbschmidt	/* Use the first valid TX antenna. */
2114221649Sbschmidt	txant1 = IWN_LSB(sc->txchainmask);
2115221649Sbschmidt	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
2116221648Sbschmidt
2117221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
2118221649Sbschmidt		ridx = ni->ni_rates.rs_nrates - 1;
2119221649Sbschmidt		for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) {
2120221649Sbschmidt			plcp = ni->ni_htrates.rs_rates[i] | IWN_RFLAG_MCS;
2121221649Sbschmidt			if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
2122221649Sbschmidt				plcp |= IWN_RFLAG_HT40;
2123221649Sbschmidt				if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
2124221649Sbschmidt					plcp |= IWN_RFLAG_SGI;
2125221649Sbschmidt			} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
2126221649Sbschmidt				plcp |= IWN_RFLAG_SGI;
2127221649Sbschmidt			if (i > 7)
2128221649Sbschmidt				plcp |= IWN_RFLAG_ANT(txant1 | txant2);
2129221649Sbschmidt			else
2130221649Sbschmidt				plcp |= IWN_RFLAG_ANT(txant1);
2131221649Sbschmidt			if (ridx >= 0) {
2132221649Sbschmidt				rate = ni->ni_rates.rs_rates[ridx];
2133221649Sbschmidt				rate &= IEEE80211_RATE_VAL;
2134221649Sbschmidt				wn->ridx[rate] = plcp;
2135221649Sbschmidt			}
2136221649Sbschmidt			wn->ridx[IEEE80211_RATE_MCS | i] = plcp;
2137221649Sbschmidt			ridx--;
2138221649Sbschmidt		}
2139221649Sbschmidt	} else {
2140221649Sbschmidt		for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
2141221649Sbschmidt			rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL;
2142221648Sbschmidt
2143221649Sbschmidt			plcp = rate2plcp(rate);
2144221649Sbschmidt			ridx = ic->ic_rt->rateCodeToIndex[rate];
2145221649Sbschmidt			if (ridx < IWN_RIDX_OFDM6 &&
2146221649Sbschmidt			    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2147221649Sbschmidt				plcp |= IWN_RFLAG_CCK;
2148221649Sbschmidt			plcp |= IWN_RFLAG_ANT(txant1);
2149221649Sbschmidt			wn->ridx[rate] = htole32(plcp);
2150221649Sbschmidt		}
2151220715Sbschmidt	}
2152220715Sbschmidt}
2153220715Sbschmidt
2154206477Sbschmidtstatic int
2155198429Srpauloiwn_media_change(struct ifnet *ifp)
2156178676Ssam{
2157220726Sbschmidt	int error;
2158220726Sbschmidt
2159220726Sbschmidt	error = ieee80211_media_change(ifp);
2160198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
2161198429Srpaulo	return (error == ENETRESET ? 0 : error);
2162198429Srpaulo}
2163178676Ssam
2164206477Sbschmidtstatic int
2165198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2166198429Srpaulo{
2167198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
2168198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
2169198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2170220688Sbschmidt	int error = 0;
2171178676Ssam
2172198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2173220726Sbschmidt	    ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2174178676Ssam
2175198429Srpaulo	IEEE80211_UNLOCK(ic);
2176198429Srpaulo	IWN_LOCK(sc);
2177220667Sbschmidt	callout_stop(&sc->calib_to);
2178178676Ssam
2179210114Sbschmidt	switch (nstate) {
2180210114Sbschmidt	case IEEE80211_S_ASSOC:
2181210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
2182210114Sbschmidt			break;
2183210114Sbschmidt		/* FALLTHROUGH */
2184210114Sbschmidt	case IEEE80211_S_AUTH:
2185210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
2186210114Sbschmidt			break;
2187210114Sbschmidt
2188210114Sbschmidt		/*
2189210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
2190210114Sbschmidt		 * reassociations correctly.
2191210114Sbschmidt		 */
2192198439Srpaulo		sc->rxon.associd = 0;
2193198439Srpaulo		sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
2194220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2195220667Sbschmidt
2196220688Sbschmidt		if ((error = iwn_auth(sc, vap)) != 0) {
2197220688Sbschmidt			device_printf(sc->sc_dev,
2198220688Sbschmidt			    "%s: could not move to auth state\n", __func__);
2199220688Sbschmidt		}
2200210114Sbschmidt		break;
2201210114Sbschmidt
2202210114Sbschmidt	case IEEE80211_S_RUN:
2203198429Srpaulo		/*
2204210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
2205210114Sbschmidt		 */
2206220667Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN) {
2207220667Sbschmidt			sc->calib_cnt = 0;
2208210114Sbschmidt			break;
2209210114Sbschmidt		}
2210210114Sbschmidt
2211210114Sbschmidt		/*
2212198429Srpaulo		 * !RUN -> RUN requires setting the association id
2213198429Srpaulo		 * which is done with a firmware cmd.  We also defer
2214198429Srpaulo		 * starting the timers until that work is done.
2215198429Srpaulo		 */
2216220688Sbschmidt		if ((error = iwn_run(sc, vap)) != 0) {
2217220688Sbschmidt			device_printf(sc->sc_dev,
2218220688Sbschmidt			    "%s: could not move to run state\n", __func__);
2219220688Sbschmidt		}
2220210114Sbschmidt		break;
2221210114Sbschmidt
2222220667Sbschmidt	case IEEE80211_S_INIT:
2223220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2224220667Sbschmidt		break;
2225220667Sbschmidt
2226210114Sbschmidt	default:
2227210114Sbschmidt		break;
2228178676Ssam	}
2229198429Srpaulo	IWN_UNLOCK(sc);
2230198429Srpaulo	IEEE80211_LOCK(ic);
2231220688Sbschmidt	if (error != 0)
2232220688Sbschmidt		return error;
2233198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
2234178676Ssam}
2235178676Ssam
2236220667Sbschmidtstatic void
2237220667Sbschmidtiwn_calib_timeout(void *arg)
2238220667Sbschmidt{
2239220667Sbschmidt	struct iwn_softc *sc = arg;
2240220667Sbschmidt
2241220667Sbschmidt	IWN_LOCK_ASSERT(sc);
2242220667Sbschmidt
2243220667Sbschmidt	/* Force automatic TX power calibration every 60 secs. */
2244220667Sbschmidt	if (++sc->calib_cnt >= 120) {
2245220667Sbschmidt		uint32_t flags = 0;
2246220667Sbschmidt
2247220667Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2248220667Sbschmidt		    "sending request for statistics");
2249220667Sbschmidt		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2250220667Sbschmidt		    sizeof flags, 1);
2251220667Sbschmidt		sc->calib_cnt = 0;
2252220667Sbschmidt	}
2253220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
2254220667Sbschmidt	    sc);
2255220667Sbschmidt}
2256220667Sbschmidt
2257198429Srpaulo/*
2258198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
2259198429Srpaulo * followed by an MPDU_RX_DONE notification.
2260198429Srpaulo */
2261206477Sbschmidtstatic void
2262198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2263198429Srpaulo    struct iwn_rx_data *data)
2264178676Ssam{
2265198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2266198429Srpaulo
2267198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2268202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2269198429Srpaulo
2270198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2271198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2272198429Srpaulo	sc->last_rx_valid = 1;
2273178676Ssam}
2274178676Ssam
2275198429Srpaulo/*
2276198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2277198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2278198429Srpaulo */
2279206477Sbschmidtstatic void
2280198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2281178676Ssam    struct iwn_rx_data *data)
2282178676Ssam{
2283220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2284178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2285178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2286178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2287178676Ssam	struct ieee80211_frame *wh;
2288178676Ssam	struct ieee80211_node *ni;
2289198429Srpaulo	struct mbuf *m, *m1;
2290178676Ssam	struct iwn_rx_stat *stat;
2291178676Ssam	caddr_t head;
2292178676Ssam	bus_addr_t paddr;
2293198429Srpaulo	uint32_t flags;
2294198429Srpaulo	int error, len, rssi, nf;
2295178676Ssam
2296198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2297198429Srpaulo		/* Check for prior RX_PHY notification. */
2298178676Ssam		if (!sc->last_rx_valid) {
2299178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2300201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2301178676Ssam			return;
2302178676Ssam		}
2303178676Ssam		stat = &sc->last_rx_stat;
2304178676Ssam	} else
2305178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2306178676Ssam
2307201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2308198439Srpaulo
2309178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2310178676Ssam		device_printf(sc->sc_dev,
2311220724Sbschmidt		    "%s: invalid RX statistic header, len %d\n", __func__,
2312220724Sbschmidt		    stat->cfg_phy_len);
2313178676Ssam		return;
2314178676Ssam	}
2315198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2316198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2317198429Srpaulo		head = (caddr_t)(mpdu + 1);
2318198429Srpaulo		len = le16toh(mpdu->len);
2319178676Ssam	} else {
2320178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2321178676Ssam		len = le16toh(stat->len);
2322178676Ssam	}
2323178676Ssam
2324198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2325198429Srpaulo
2326198429Srpaulo	/* Discard frames with a bad FCS early. */
2327198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2328220724Sbschmidt		DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
2329198429Srpaulo		    __func__, flags);
2330178676Ssam		ifp->if_ierrors++;
2331178676Ssam		return;
2332178676Ssam	}
2333198429Srpaulo	/* Discard frames that are too short. */
2334198429Srpaulo	if (len < sizeof (*wh)) {
2335178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2336178676Ssam		    __func__, len);
2337178676Ssam		ifp->if_ierrors++;
2338178676Ssam		return;
2339178676Ssam	}
2340178676Ssam
2341220692Sbschmidt	m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
2342198429Srpaulo	if (m1 == NULL) {
2343178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2344178676Ssam		    __func__);
2345178676Ssam		ifp->if_ierrors++;
2346178676Ssam		return;
2347178676Ssam	}
2348201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2349201209Srpaulo
2350220692Sbschmidt	error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
2351220692Sbschmidt	    IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2352178676Ssam	if (error != 0 && error != EFBIG) {
2353178676Ssam		device_printf(sc->sc_dev,
2354178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2355198429Srpaulo		m_freem(m1);
2356220693Sbschmidt
2357220693Sbschmidt		/* Try to reload the old mbuf. */
2358220693Sbschmidt		error = bus_dmamap_load(ring->data_dmat, data->map,
2359220693Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
2360220693Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
2361220693Sbschmidt		if (error != 0 && error != EFBIG) {
2362220693Sbschmidt			panic("%s: could not load old RX mbuf", __func__);
2363220693Sbschmidt		}
2364220693Sbschmidt		/* Physical address may have changed. */
2365220693Sbschmidt		ring->desc[ring->cur] = htole32(paddr >> 8);
2366220693Sbschmidt		bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
2367220693Sbschmidt		    BUS_DMASYNC_PREWRITE);
2368178676Ssam		ifp->if_ierrors++;
2369178676Ssam		return;
2370178676Ssam	}
2371178676Ssam
2372178676Ssam	m = data->m;
2373198429Srpaulo	data->m = m1;
2374198429Srpaulo	/* Update RX descriptor. */
2375198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2376201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2377201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2378198429Srpaulo
2379198429Srpaulo	/* Finalize mbuf. */
2380178676Ssam	m->m_pkthdr.rcvif = ifp;
2381178676Ssam	m->m_data = head;
2382178676Ssam	m->m_pkthdr.len = m->m_len = len;
2383178676Ssam
2384198429Srpaulo	/* Grab a reference to the source node. */
2385178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2386178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2387178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2388178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2389178676Ssam
2390220728Sbschmidt	rssi = ops->get_rssi(sc, stat);
2391220689Sbschmidt
2392192468Ssam	if (ieee80211_radiotap_active(ic)) {
2393178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2394178676Ssam
2395178676Ssam		tap->wr_flags = 0;
2396201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2397192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2398220723Sbschmidt		tap->wr_dbm_antsignal = (int8_t)rssi;
2399220723Sbschmidt		tap->wr_dbm_antnoise = (int8_t)nf;
2400220723Sbschmidt		tap->wr_tsft = stat->tstamp;
2401201209Srpaulo		switch (stat->rate) {
2402201209Srpaulo		/* CCK rates. */
2403201209Srpaulo		case  10: tap->wr_rate =   2; break;
2404201209Srpaulo		case  20: tap->wr_rate =   4; break;
2405201209Srpaulo		case  55: tap->wr_rate =  11; break;
2406201209Srpaulo		case 110: tap->wr_rate =  22; break;
2407201209Srpaulo		/* OFDM rates. */
2408201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2409201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2410201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2411201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2412201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2413201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2414201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2415201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2416201209Srpaulo		/* Unknown rate: should not happen. */
2417201209Srpaulo		default:  tap->wr_rate =   0;
2418201209Srpaulo		}
2419178676Ssam	}
2420178676Ssam
2421178676Ssam	IWN_UNLOCK(sc);
2422178676Ssam
2423198429Srpaulo	/* Send the frame to the 802.11 layer. */
2424178676Ssam	if (ni != NULL) {
2425221650Sbschmidt		if (ni->ni_flags & IEEE80211_NODE_HT)
2426221650Sbschmidt			m->m_flags |= M_AMPDU;
2427220726Sbschmidt		(void)ieee80211_input(ni, m, rssi - nf, nf);
2428198429Srpaulo		/* Node is no longer needed. */
2429178676Ssam		ieee80211_free_node(ni);
2430178676Ssam	} else
2431220726Sbschmidt		(void)ieee80211_input_all(ic, m, rssi - nf, nf);
2432178676Ssam
2433178676Ssam	IWN_LOCK(sc);
2434178676Ssam}
2435178676Ssam
2436201209Srpaulo/* Process an incoming Compressed BlockAck. */
2437206477Sbschmidtstatic void
2438201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2439201209Srpaulo    struct iwn_rx_data *data)
2440201209Srpaulo{
2441221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2442221651Sbschmidt	struct iwn_node *wn;
2443221651Sbschmidt	struct ieee80211_node *ni;
2444201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2445201209Srpaulo	struct iwn_tx_ring *txq;
2446221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2447221651Sbschmidt	uint64_t bitmap;
2448221651Sbschmidt	uint8_t tid;
2449221651Sbschmidt	int ackfailcnt = 0, i, shift;
2450201209Srpaulo
2451220704Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2452220704Sbschmidt
2453221651Sbschmidt	txq = &sc->txq[le16toh(ba->qid)];
2454221651Sbschmidt	tap = sc->qid2tap[le16toh(ba->qid)];
2455221651Sbschmidt	tid = WME_AC_TO_TID(tap->txa_ac);
2456221651Sbschmidt	ni = tap->txa_ni;
2457221651Sbschmidt	wn = (void *)ni;
2458221651Sbschmidt
2459221651Sbschmidt	if (wn->agg[tid].bitmap == 0)
2460221651Sbschmidt		return;
2461221651Sbschmidt
2462221651Sbschmidt	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
2463221651Sbschmidt	if (shift < 0)
2464221651Sbschmidt		shift += 0x100;
2465221651Sbschmidt
2466221651Sbschmidt	if (wn->agg[tid].nframes > (64 - shift))
2467221651Sbschmidt		return;
2468221651Sbschmidt
2469221651Sbschmidt	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
2470221651Sbschmidt	for (i = 0; bitmap; i++) {
2471221651Sbschmidt		if ((bitmap & 1) == 0) {
2472221651Sbschmidt			ifp->if_oerrors++;
2473221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2474221651Sbschmidt			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2475221651Sbschmidt		} else {
2476221651Sbschmidt			ifp->if_opackets++;
2477221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2478221651Sbschmidt			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2479221651Sbschmidt		}
2480221651Sbschmidt		bitmap >>= 1;
2481221651Sbschmidt	}
2482201209Srpaulo}
2483201209Srpaulo
2484198429Srpaulo/*
2485220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
2486220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2487220674Sbschmidt */
2488220674Sbschmidtstatic void
2489220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2490220674Sbschmidt    struct iwn_rx_data *data)
2491220674Sbschmidt{
2492220674Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2493220674Sbschmidt	int len, idx = -1;
2494220674Sbschmidt
2495220674Sbschmidt	/* Runtime firmware should not send such a notification. */
2496220674Sbschmidt	if (sc->sc_flags & IWN_FLAG_CALIB_DONE)
2497220674Sbschmidt		return;
2498220674Sbschmidt
2499220674Sbschmidt	len = (le32toh(desc->len) & 0x3fff) - 4;
2500220674Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2501220674Sbschmidt
2502220674Sbschmidt	switch (calib->code) {
2503220674Sbschmidt	case IWN5000_PHY_CALIB_DC:
2504220867Sbschmidt		if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 &&
2505220867Sbschmidt		    (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2506220867Sbschmidt		     sc->hw_type >= IWN_HW_REV_TYPE_6000))
2507220674Sbschmidt			idx = 0;
2508220674Sbschmidt		break;
2509220674Sbschmidt	case IWN5000_PHY_CALIB_LO:
2510220674Sbschmidt		idx = 1;
2511220674Sbschmidt		break;
2512220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
2513220674Sbschmidt		idx = 2;
2514220674Sbschmidt		break;
2515220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2516220674Sbschmidt		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2517220674Sbschmidt		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2518220674Sbschmidt			idx = 3;
2519220674Sbschmidt		break;
2520220674Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
2521220674Sbschmidt		idx = 4;
2522220674Sbschmidt		break;
2523220674Sbschmidt	}
2524220674Sbschmidt	if (idx == -1)	/* Ignore other results. */
2525220674Sbschmidt		return;
2526220674Sbschmidt
2527220674Sbschmidt	/* Save calibration result. */
2528220674Sbschmidt	if (sc->calibcmd[idx].buf != NULL)
2529220674Sbschmidt		free(sc->calibcmd[idx].buf, M_DEVBUF);
2530220674Sbschmidt	sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
2531220674Sbschmidt	if (sc->calibcmd[idx].buf == NULL) {
2532220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2533220674Sbschmidt		    "not enough memory for calibration result %d\n",
2534220674Sbschmidt		    calib->code);
2535220674Sbschmidt		return;
2536220674Sbschmidt	}
2537220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2538220674Sbschmidt	    "saving calibration result code=%d len=%d\n", calib->code, len);
2539220674Sbschmidt	sc->calibcmd[idx].len = len;
2540220674Sbschmidt	memcpy(sc->calibcmd[idx].buf, calib, len);
2541220674Sbschmidt}
2542220674Sbschmidt
2543220674Sbschmidt/*
2544198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2545198429Srpaulo * The latter is sent by the firmware after each received beacon.
2546198429Srpaulo */
2547206477Sbschmidtstatic void
2548198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2549198429Srpaulo    struct iwn_rx_data *data)
2550198429Srpaulo{
2551220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2552178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2553178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2554178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2555178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2556178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2557198429Srpaulo	int temp;
2558178676Ssam
2559220725Sbschmidt	/* Ignore statistics received during a scan. */
2560178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2561178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN))
2562178676Ssam		return;
2563178676Ssam
2564202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2565220724Sbschmidt
2566220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n",
2567220724Sbschmidt	    __func__, desc->type);
2568220667Sbschmidt	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2569178676Ssam
2570198429Srpaulo	/* Test if temperature has changed. */
2571178676Ssam	if (stats->general.temp != sc->rawtemp) {
2572198429Srpaulo		/* Convert "raw" temperature to degC. */
2573178676Ssam		sc->rawtemp = stats->general.temp;
2574220728Sbschmidt		temp = ops->get_temperature(sc);
2575178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2576178676Ssam		    __func__, temp);
2577178676Ssam
2578220725Sbschmidt		/* Update TX power if need be (4965AGN only). */
2579198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2580198429Srpaulo			iwn4965_power_calibration(sc, temp);
2581178676Ssam	}
2582178676Ssam
2583178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2584198429Srpaulo		return;	/* Reply to a statistics request. */
2585178676Ssam
2586178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2587178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2588178676Ssam
2589198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2590198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2591178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2592178676Ssam		    "received statistics without RSSI");
2593178676Ssam		return;
2594178676Ssam	}
2595178676Ssam
2596178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2597198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2598178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2599178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2600178676Ssam}
2601178676Ssam
2602198429Srpaulo/*
2603198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2604198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2605198429Srpaulo */
2606206477Sbschmidtstatic void
2607198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2608198429Srpaulo    struct iwn_rx_data *data)
2609178676Ssam{
2610198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2611221651Sbschmidt	struct iwn_tx_ring *ring;
2612221651Sbschmidt	int qid;
2613198429Srpaulo
2614221651Sbschmidt	qid = desc->qid & 0xf;
2615221651Sbschmidt	ring = &sc->txq[qid];
2616221651Sbschmidt
2617198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2618198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2619201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2620201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2621198429Srpaulo	    le32toh(stat->status));
2622201209Srpaulo
2623207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2624221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2625221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2626221651Sbschmidt		    &stat->status);
2627221651Sbschmidt	} else {
2628221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2629221651Sbschmidt		    le32toh(stat->status) & 0xff);
2630221651Sbschmidt	}
2631198429Srpaulo}
2632198429Srpaulo
2633206477Sbschmidtstatic void
2634198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2635198429Srpaulo    struct iwn_rx_data *data)
2636198429Srpaulo{
2637198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2638221651Sbschmidt	struct iwn_tx_ring *ring;
2639221651Sbschmidt	int qid;
2640198429Srpaulo
2641221651Sbschmidt	qid = desc->qid & 0xf;
2642221651Sbschmidt	ring = &sc->txq[qid];
2643221651Sbschmidt
2644198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2645198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2646201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2647201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2648198429Srpaulo	    le32toh(stat->status));
2649198429Srpaulo
2650201209Srpaulo#ifdef notyet
2651198429Srpaulo	/* Reset TX scheduler slot. */
2652198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2653201209Srpaulo#endif
2654202986Srpaulo
2655207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2656221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2657221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2658221651Sbschmidt		    &stat->status);
2659221651Sbschmidt	} else {
2660221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2661221651Sbschmidt		    le16toh(stat->status) & 0xff);
2662221651Sbschmidt	}
2663198429Srpaulo}
2664198429Srpaulo
2665198429Srpaulo/*
2666198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2667198429Srpaulo */
2668206477Sbschmidtstatic void
2669201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2670198429Srpaulo    uint8_t status)
2671198429Srpaulo{
2672178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2673178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2674178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2675178676Ssam	struct mbuf *m;
2676178676Ssam	struct ieee80211_node *ni;
2677206358Srpaulo	struct ieee80211vap *vap;
2678178676Ssam
2679178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2680178676Ssam
2681198429Srpaulo	/* Unmap and free mbuf. */
2682201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2683201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2684178676Ssam	m = data->m, data->m = NULL;
2685178676Ssam	ni = data->ni, data->ni = NULL;
2686206358Srpaulo	vap = ni->ni_vap;
2687178676Ssam
2688178676Ssam	if (m->m_flags & M_TXCB) {
2689178676Ssam		/*
2690178676Ssam		 * Channels marked for "radar" require traffic to be received
2691178676Ssam		 * to unlock before we can transmit.  Until traffic is seen
2692178676Ssam		 * any attempt to transmit is returned immediately with status
2693178676Ssam		 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2694178676Ssam		 * happen on first authenticate after scanning.  To workaround
2695178676Ssam		 * this we ignore a failure of this sort in AUTH state so the
2696178676Ssam		 * 802.11 layer will fall back to using a timeout to wait for
2697178676Ssam		 * the AUTH reply.  This allows the firmware time to see
2698178676Ssam		 * traffic so a subsequent retry of AUTH succeeds.  It's
2699178676Ssam		 * unclear why the firmware does not maintain state for
2700178676Ssam		 * channels recently visited as this would allow immediate
2701178676Ssam		 * use of the channel after a scan (where we see traffic).
2702178676Ssam		 */
2703178676Ssam		if (status == IWN_TX_FAIL_TX_LOCKED &&
2704178676Ssam		    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2705178676Ssam			ieee80211_process_callback(ni, m, 0);
2706178676Ssam		else
2707178676Ssam			ieee80211_process_callback(ni, m,
2708178676Ssam			    (status & IWN_TX_FAIL) != 0);
2709178676Ssam	}
2710201209Srpaulo
2711201209Srpaulo	/*
2712201209Srpaulo	 * Update rate control statistics for the node.
2713201209Srpaulo	 */
2714220719Sbschmidt	if (status & IWN_TX_FAIL) {
2715201209Srpaulo		ifp->if_oerrors++;
2716206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2717206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2718201209Srpaulo	} else {
2719220719Sbschmidt		ifp->if_opackets++;
2720206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2721206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2722201209Srpaulo	}
2723178676Ssam	m_freem(m);
2724178676Ssam	ieee80211_free_node(ni);
2725178676Ssam
2726178676Ssam	sc->sc_tx_timer = 0;
2727198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2728198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2729198429Srpaulo		if (sc->qfullmsk == 0 &&
2730198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2731198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2732198429Srpaulo			iwn_start_locked(ifp);
2733198429Srpaulo		}
2734198429Srpaulo	}
2735178676Ssam}
2736178676Ssam
2737198429Srpaulo/*
2738198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2739198429Srpaulo * processes waiting for a synchronous command completion.
2740198429Srpaulo */
2741206477Sbschmidtstatic void
2742198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2743178676Ssam{
2744178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2745178676Ssam	struct iwn_tx_data *data;
2746178676Ssam
2747178676Ssam	if ((desc->qid & 0xf) != 4)
2748198429Srpaulo		return;	/* Not a command ack. */
2749178676Ssam
2750178676Ssam	data = &ring->data[desc->idx];
2751178676Ssam
2752198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2753178676Ssam	if (data->m != NULL) {
2754220704Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2755220704Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2756201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2757178676Ssam		m_freem(data->m);
2758178676Ssam		data->m = NULL;
2759178676Ssam	}
2760198439Srpaulo	wakeup(&ring->desc[desc->idx]);
2761178676Ssam}
2762178676Ssam
2763221651Sbschmidtstatic void
2764221651Sbschmidtiwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
2765221651Sbschmidt    void *stat)
2766221651Sbschmidt{
2767221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2768221651Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[qid];
2769221651Sbschmidt	struct iwn_tx_data *data;
2770221651Sbschmidt	struct mbuf *m;
2771221651Sbschmidt	struct iwn_node *wn;
2772221651Sbschmidt	struct ieee80211_node *ni;
2773221651Sbschmidt	struct ieee80211vap *vap;
2774221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2775221651Sbschmidt	uint64_t bitmap;
2776221651Sbschmidt	uint32_t *status = stat;
2777221651Sbschmidt	uint16_t *aggstatus = stat;
2778221651Sbschmidt	uint8_t tid;
2779221651Sbschmidt	int bit, i, lastidx, seqno, shift, start;
2780221651Sbschmidt
2781221651Sbschmidt#ifdef NOT_YET
2782221651Sbschmidt	if (nframes == 1) {
2783221651Sbschmidt		if ((*status & 0xff) != 1 && (*status & 0xff) != 2)
2784221651Sbschmidt			printf("ieee80211_send_bar()\n");
2785221651Sbschmidt	}
2786221651Sbschmidt#endif
2787221651Sbschmidt
2788221651Sbschmidt	bitmap = 0;
2789221651Sbschmidt	start = idx;
2790221651Sbschmidt	for (i = 0; i < nframes; i++) {
2791221651Sbschmidt		if (le16toh(aggstatus[i * 2]) & 0xc)
2792221651Sbschmidt			continue;
2793221651Sbschmidt
2794221651Sbschmidt		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
2795221651Sbschmidt		bit = idx - start;
2796221651Sbschmidt		shift = 0;
2797221651Sbschmidt		if (bit >= 64) {
2798221651Sbschmidt			shift = 0x100 - idx + start;
2799221651Sbschmidt			bit = 0;
2800221651Sbschmidt			start = idx;
2801221651Sbschmidt		} else if (bit <= -64)
2802221651Sbschmidt			bit = 0x100 - start + idx;
2803221651Sbschmidt		else if (bit < 0) {
2804221651Sbschmidt			shift = start - idx;
2805221651Sbschmidt			start = idx;
2806221651Sbschmidt			bit = 0;
2807221651Sbschmidt		}
2808221651Sbschmidt		bitmap = bitmap << shift;
2809221651Sbschmidt		bitmap |= 1ULL << bit;
2810221651Sbschmidt	}
2811221651Sbschmidt	tap = sc->qid2tap[qid];
2812221651Sbschmidt	tid = WME_AC_TO_TID(tap->txa_ac);
2813221651Sbschmidt	wn = (void *)tap->txa_ni;
2814221651Sbschmidt	wn->agg[tid].bitmap = bitmap;
2815221651Sbschmidt	wn->agg[tid].startidx = start;
2816221651Sbschmidt	wn->agg[tid].nframes = nframes;
2817221651Sbschmidt
2818221651Sbschmidt	seqno = le32toh(*(status + nframes)) & 0xfff;
2819221651Sbschmidt	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
2820221651Sbschmidt		data = &ring->data[ring->read];
2821221651Sbschmidt
2822221651Sbschmidt		KASSERT(data->ni != NULL, ("no node"));
2823221651Sbschmidt
2824221651Sbschmidt		/* Unmap and free mbuf. */
2825221651Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2826221651Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2827221651Sbschmidt		bus_dmamap_unload(ring->data_dmat, data->map);
2828221651Sbschmidt		m = data->m, data->m = NULL;
2829221651Sbschmidt		ni = data->ni, data->ni = NULL;
2830221651Sbschmidt		vap = ni->ni_vap;
2831221651Sbschmidt
2832221651Sbschmidt		if (m->m_flags & M_TXCB)
2833221651Sbschmidt			ieee80211_process_callback(ni, m, 1);
2834221651Sbschmidt
2835221651Sbschmidt		m_freem(m);
2836221651Sbschmidt		ieee80211_free_node(ni);
2837221651Sbschmidt
2838221651Sbschmidt		ring->queued--;
2839221651Sbschmidt		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
2840221651Sbschmidt	}
2841221651Sbschmidt
2842221651Sbschmidt	sc->sc_tx_timer = 0;
2843221651Sbschmidt	if (ring->queued < IWN_TX_RING_LOMARK) {
2844221651Sbschmidt		sc->qfullmsk &= ~(1 << ring->qid);
2845221651Sbschmidt		if (sc->qfullmsk == 0 &&
2846221651Sbschmidt		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2847221651Sbschmidt			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2848221651Sbschmidt			iwn_start_locked(ifp);
2849221651Sbschmidt		}
2850221651Sbschmidt	}
2851221651Sbschmidt}
2852221651Sbschmidt
2853198429Srpaulo/*
2854198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
2855198429Srpaulo */
2856206477Sbschmidtstatic void
2857178676Ssamiwn_notif_intr(struct iwn_softc *sc)
2858178676Ssam{
2859220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2860178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2861178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2862178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2863178676Ssam	uint16_t hw;
2864178676Ssam
2865198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
2866198429Srpaulo	    BUS_DMASYNC_POSTREAD);
2867198429Srpaulo
2868198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
2869178676Ssam	while (sc->rxq.cur != hw) {
2870178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
2871198429Srpaulo		struct iwn_rx_desc *desc;
2872178676Ssam
2873201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2874201209Srpaulo		    BUS_DMASYNC_POSTREAD);
2875198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
2876198429Srpaulo
2877178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
2878178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
2879198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
2880178676Ssam		    desc->type, iwn_intr_str(desc->type),
2881178676Ssam		    le16toh(desc->len));
2882178676Ssam
2883198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
2884198429Srpaulo			iwn_cmd_done(sc, desc);
2885178676Ssam
2886178676Ssam		switch (desc->type) {
2887198429Srpaulo		case IWN_RX_PHY:
2888198429Srpaulo			iwn_rx_phy(sc, desc, data);
2889178676Ssam			break;
2890178676Ssam
2891198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
2892198429Srpaulo		case IWN_MPDU_RX_DONE:
2893198429Srpaulo			/* An 802.11 frame has been received. */
2894198429Srpaulo			iwn_rx_done(sc, desc, data);
2895178676Ssam			break;
2896178676Ssam
2897201209Srpaulo		case IWN_RX_COMPRESSED_BA:
2898201209Srpaulo			/* A Compressed BlockAck has been received. */
2899201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
2900201209Srpaulo			break;
2901201209Srpaulo
2902178676Ssam		case IWN_TX_DONE:
2903198429Srpaulo			/* An 802.11 frame has been transmitted. */
2904220728Sbschmidt			ops->tx_done(sc, desc, data);
2905178676Ssam			break;
2906178676Ssam
2907178676Ssam		case IWN_RX_STATISTICS:
2908178676Ssam		case IWN_BEACON_STATISTICS:
2909198429Srpaulo			iwn_rx_statistics(sc, desc, data);
2910178676Ssam			break;
2911178676Ssam
2912198429Srpaulo		case IWN_BEACON_MISSED:
2913198429Srpaulo		{
2914178676Ssam			struct iwn_beacon_missed *miss =
2915178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
2916202986Srpaulo			int misses;
2917178676Ssam
2918201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2919201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2920202986Srpaulo			misses = le32toh(miss->consecutive);
2921201209Srpaulo
2922178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2923178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
2924178676Ssam			    misses, le32toh(miss->total));
2925178676Ssam			/*
2926178676Ssam			 * If more than 5 consecutive beacons are missed,
2927178676Ssam			 * reinitialize the sensitivity state machine.
2928178676Ssam			 */
2929220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
2930220660Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) != 0) {
2931220660Sbschmidt				if (misses > 5)
2932220660Sbschmidt					(void)iwn_init_sensitivity(sc);
2933220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
2934220660Sbschmidt					IWN_UNLOCK(sc);
2935220660Sbschmidt					ieee80211_beacon_miss(ic);
2936220660Sbschmidt					IWN_LOCK(sc);
2937220660Sbschmidt				}
2938201209Srpaulo			}
2939178676Ssam			break;
2940178676Ssam		}
2941198429Srpaulo		case IWN_UC_READY:
2942198429Srpaulo		{
2943178676Ssam			struct iwn_ucode_info *uc =
2944178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
2945178676Ssam
2946198429Srpaulo			/* The microcontroller is ready. */
2947201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2948201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2949178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
2950178676Ssam			    "microcode alive notification version=%d.%d "
2951178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
2952178676Ssam			    uc->subtype, le32toh(uc->valid));
2953178676Ssam
2954178676Ssam			if (le32toh(uc->valid) != 1) {
2955178676Ssam				device_printf(sc->sc_dev,
2956198429Srpaulo				    "microcontroller initialization failed");
2957178676Ssam				break;
2958178676Ssam			}
2959178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
2960201209Srpaulo				/* Save microcontroller report. */
2961178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
2962178676Ssam			}
2963198429Srpaulo			/* Save the address of the error log in SRAM. */
2964198429Srpaulo			sc->errptr = le32toh(uc->errptr);
2965178676Ssam			break;
2966178676Ssam		}
2967198429Srpaulo		case IWN_STATE_CHANGED:
2968198429Srpaulo		{
2969178676Ssam			uint32_t *status = (uint32_t *)(desc + 1);
2970178676Ssam
2971178676Ssam			/*
2972178676Ssam			 * State change allows hardware switch change to be
2973178676Ssam			 * noted. However, we handle this in iwn_intr as we
2974178676Ssam			 * get both the enable/disble intr.
2975178676Ssam			 */
2976201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2977201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2978178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
2979178676Ssam			    le32toh(*status));
2980178676Ssam			break;
2981178676Ssam		}
2982198429Srpaulo		case IWN_START_SCAN:
2983198429Srpaulo		{
2984178676Ssam			struct iwn_start_scan *scan =
2985178676Ssam			    (struct iwn_start_scan *)(desc + 1);
2986178676Ssam
2987201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2988201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2989178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2990178676Ssam			    "%s: scanning channel %d status %x\n",
2991178676Ssam			    __func__, scan->chan, le32toh(scan->status));
2992178676Ssam			break;
2993178676Ssam		}
2994198429Srpaulo		case IWN_STOP_SCAN:
2995198429Srpaulo		{
2996178676Ssam			struct iwn_stop_scan *scan =
2997178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
2998178676Ssam
2999201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3000201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3001178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3002178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
3003178676Ssam			    scan->nchan, scan->status, scan->chan);
3004178676Ssam
3005201209Srpaulo			IWN_UNLOCK(sc);
3006191746Sthompsa			ieee80211_scan_next(vap);
3007201209Srpaulo			IWN_LOCK(sc);
3008178676Ssam			break;
3009178676Ssam		}
3010198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
3011220674Sbschmidt			iwn5000_rx_calib_results(sc, desc, data);
3012198429Srpaulo			break;
3013198429Srpaulo
3014198429Srpaulo		case IWN5000_CALIBRATION_DONE:
3015201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
3016198429Srpaulo			wakeup(sc);
3017198429Srpaulo			break;
3018178676Ssam		}
3019198429Srpaulo
3020178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
3021178676Ssam	}
3022178676Ssam
3023198429Srpaulo	/* Tell the firmware what we have processed. */
3024178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
3025198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
3026178676Ssam}
3027178676Ssam
3028198429Srpaulo/*
3029198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
3030198429Srpaulo * from power-down sleep mode.
3031198429Srpaulo */
3032206477Sbschmidtstatic void
3033198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
3034198429Srpaulo{
3035198429Srpaulo	int qid;
3036198429Srpaulo
3037198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
3038198429Srpaulo	    __func__);
3039198429Srpaulo
3040198429Srpaulo	/* Wakeup RX and TX rings. */
3041198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
3042220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
3043198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
3044198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
3045198429Srpaulo	}
3046198429Srpaulo}
3047198429Srpaulo
3048206477Sbschmidtstatic void
3049191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
3050191746Sthompsa{
3051191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
3052191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
3053198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
3054191746Sthompsa
3055191746Sthompsa	IWN_LOCK_ASSERT(sc);
3056191746Sthompsa
3057191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
3058198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
3059198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
3060191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
3061191746Sthompsa	else
3062191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
3063191746Sthompsa}
3064191746Sthompsa
3065198429Srpaulo/*
3066198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
3067198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
3068198429Srpaulo * can help us to identify certain classes of problems.
3069198429Srpaulo */
3070206477Sbschmidtstatic void
3071201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
3072191746Sthompsa{
3073198429Srpaulo	struct iwn_fw_dump dump;
3074198429Srpaulo	int i;
3075191746Sthompsa
3076191746Sthompsa	IWN_LOCK_ASSERT(sc);
3077191746Sthompsa
3078201209Srpaulo	/* Force a complete recalibration on next init. */
3079201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
3080201209Srpaulo
3081198429Srpaulo	/* Check that the error log address is valid. */
3082198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
3083198429Srpaulo	    sc->errptr + sizeof (dump) >
3084220728Sbschmidt	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
3085220726Sbschmidt		printf("%s: bad firmware error log address 0x%08x\n", __func__,
3086220726Sbschmidt		    sc->errptr);
3087198429Srpaulo		return;
3088198429Srpaulo	}
3089198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
3090220726Sbschmidt		printf("%s: could not read firmware error log\n", __func__);
3091198429Srpaulo		return;
3092198429Srpaulo	}
3093198429Srpaulo	/* Read firmware error log from SRAM. */
3094198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
3095198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
3096198429Srpaulo	iwn_nic_unlock(sc);
3097198429Srpaulo
3098198429Srpaulo	if (dump.valid == 0) {
3099220726Sbschmidt		printf("%s: firmware error log is empty\n", __func__);
3100198429Srpaulo		return;
3101198429Srpaulo	}
3102198429Srpaulo	printf("firmware error log:\n");
3103198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
3104198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
3105198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
3106198429Srpaulo	    dump.id);
3107198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
3108198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
3109198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
3110198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
3111198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
3112198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
3113198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
3114198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
3115198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
3116198429Srpaulo
3117198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
3118198429Srpaulo	printf("driver status:\n");
3119220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
3120198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
3121198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
3122198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
3123198429Srpaulo	}
3124198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
3125191746Sthompsa}
3126191746Sthompsa
3127206477Sbschmidtstatic void
3128178676Ssamiwn_intr(void *arg)
3129178676Ssam{
3130178676Ssam	struct iwn_softc *sc = arg;
3131198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3132201209Srpaulo	uint32_t r1, r2, tmp;
3133178676Ssam
3134178676Ssam	IWN_LOCK(sc);
3135178676Ssam
3136198429Srpaulo	/* Disable interrupts. */
3137201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
3138178676Ssam
3139201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
3140201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3141201209Srpaulo		tmp = 0;
3142201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
3143201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
3144201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
3145201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
3146201209Srpaulo		}
3147201209Srpaulo		tmp = le32toh(tmp);
3148206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
3149206444Sbschmidt			tmp = 0;
3150206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
3151206444Sbschmidt			tmp |= 0x8000;
3152201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
3153201209Srpaulo		r2 = 0;	/* Unused. */
3154201209Srpaulo	} else {
3155201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
3156201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
3157201209Srpaulo			return;	/* Hardware gone! */
3158201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
3159201209Srpaulo	}
3160178676Ssam
3161198429Srpaulo	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2);
3162198429Srpaulo
3163201209Srpaulo	if (r1 == 0 && r2 == 0)
3164198429Srpaulo		goto done;	/* Interrupt not for us. */
3165178676Ssam
3166198429Srpaulo	/* Acknowledge interrupts. */
3167198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
3168201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
3169201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
3170178676Ssam
3171198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
3172191746Sthompsa		iwn_rftoggle_intr(sc);
3173201209Srpaulo		goto done;
3174198429Srpaulo	}
3175198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
3176198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
3177198429Srpaulo		    __func__);
3178198429Srpaulo	}
3179198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
3180220724Sbschmidt		device_printf(sc->sc_dev, "%s: fatal firmware error\n",
3181220724Sbschmidt		    __func__);
3182220725Sbschmidt		/* Dump firmware error log and stop. */
3183201209Srpaulo		iwn_fatal_intr(sc);
3184201209Srpaulo		ifp->if_flags &= ~IFF_UP;
3185201209Srpaulo		iwn_stop_locked(sc);
3186178676Ssam		goto done;
3187178676Ssam	}
3188201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
3189201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
3190201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3191201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
3192201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
3193201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3194201209Srpaulo			    IWN_INT_PERIODIC_DIS);
3195201209Srpaulo			iwn_notif_intr(sc);
3196201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
3197201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3198201209Srpaulo				    IWN_INT_PERIODIC_ENA);
3199201209Srpaulo			}
3200201209Srpaulo		} else
3201201209Srpaulo			iwn_notif_intr(sc);
3202201209Srpaulo	}
3203178676Ssam
3204201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
3205201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
3206201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
3207198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
3208201209Srpaulo	}
3209198429Srpaulo
3210198429Srpaulo	if (r1 & IWN_INT_ALIVE)
3211198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
3212198429Srpaulo
3213198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
3214198429Srpaulo		iwn_wakeup_intr(sc);
3215198429Srpaulo
3216201209Srpaulodone:
3217198429Srpaulo	/* Re-enable interrupts. */
3218201209Srpaulo	if (ifp->if_flags & IFF_UP)
3219201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3220198429Srpaulo
3221178676Ssam	IWN_UNLOCK(sc);
3222178676Ssam}
3223178676Ssam
3224198429Srpaulo/*
3225198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3226220725Sbschmidt * 5000 adapters use a slightly different format).
3227198429Srpaulo */
3228206477Sbschmidtstatic void
3229198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3230198429Srpaulo    uint16_t len)
3231178676Ssam{
3232198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
3233178676Ssam
3234198429Srpaulo	*w = htole16(len + 8);
3235198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3236198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3237201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3238198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3239198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3240198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3241198439Srpaulo	}
3242198429Srpaulo}
3243198429Srpaulo
3244206477Sbschmidtstatic void
3245198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3246198429Srpaulo    uint16_t len)
3247198429Srpaulo{
3248198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3249198429Srpaulo
3250198429Srpaulo	*w = htole16(id << 12 | (len + 8));
3251198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3252198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3253198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3254198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3255198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3256198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3257198439Srpaulo	}
3258198429Srpaulo}
3259198429Srpaulo
3260206475Sbschmidt#ifdef notyet
3261206477Sbschmidtstatic void
3262198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3263198429Srpaulo{
3264198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3265198429Srpaulo
3266198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
3267198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3268198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3269198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3270198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3271198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3272206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
3273198439Srpaulo	}
3274198429Srpaulo}
3275206475Sbschmidt#endif
3276198429Srpaulo
3277206477Sbschmidtstatic int
3278220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3279178676Ssam{
3280221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3281198429Srpaulo	const struct ieee80211_txparam *tp;
3282178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
3283178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3284198429Srpaulo	struct iwn_node *wn = (void *)ni;
3285220720Sbschmidt	struct iwn_tx_ring *ring;
3286178676Ssam	struct iwn_tx_desc *desc;
3287178676Ssam	struct iwn_tx_data *data;
3288178676Ssam	struct iwn_tx_cmd *cmd;
3289178676Ssam	struct iwn_cmd_data *tx;
3290178676Ssam	struct ieee80211_frame *wh;
3291198429Srpaulo	struct ieee80211_key *k = NULL;
3292220700Sbschmidt	struct mbuf *m1;
3293178676Ssam	uint32_t flags;
3294220720Sbschmidt	uint16_t qos;
3295178676Ssam	u_int hdrlen;
3296220723Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3297220723Sbschmidt	uint8_t tid, ridx, txant, type;
3298220720Sbschmidt	int ac, i, totlen, error, pad, nsegs = 0, rate;
3299178676Ssam
3300178676Ssam	IWN_LOCK_ASSERT(sc);
3301178676Ssam
3302198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3303198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3304178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3305178676Ssam
3306220720Sbschmidt	/* Select EDCA Access Category and TX ring for this frame. */
3307220720Sbschmidt	if (IEEE80211_QOS_HAS_SEQ(wh)) {
3308220720Sbschmidt		qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
3309220720Sbschmidt		tid = qos & IEEE80211_QOS_TID;
3310220720Sbschmidt	} else {
3311220720Sbschmidt		qos = 0;
3312220720Sbschmidt		tid = 0;
3313220720Sbschmidt	}
3314220720Sbschmidt	ac = M_WME_GETAC(m);
3315220720Sbschmidt
3316221651Sbschmidt	if (IEEE80211_AMPDU_RUNNING(&ni->ni_tx_ampdu[ac])) {
3317221651Sbschmidt		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
3318221651Sbschmidt
3319221651Sbschmidt		ring = &sc->txq[*(int *)tap->txa_private];
3320221651Sbschmidt		*(uint16_t *)wh->i_seq =
3321221651Sbschmidt		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
3322221651Sbschmidt		ni->ni_txseqs[tid]++;
3323221651Sbschmidt	} else {
3324221651Sbschmidt		ring = &sc->txq[ac];
3325221651Sbschmidt	}
3326198429Srpaulo	desc = &ring->desc[ring->cur];
3327198429Srpaulo	data = &ring->data[ring->cur];
3328198429Srpaulo
3329198429Srpaulo	/* Choose a TX rate index. */
3330201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
3331178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
3332178676Ssam		rate = tp->mgmtrate;
3333198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3334178676Ssam		rate = tp->mcastrate;
3335178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
3336178676Ssam		rate = tp->ucastrate;
3337178676Ssam	else {
3338206358Srpaulo		/* XXX pass pktlen */
3339206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3340178676Ssam		rate = ni->ni_txrate;
3341178676Ssam	}
3342221648Sbschmidt	ridx = ic->ic_rt->rateCodeToIndex[rate];
3343178676Ssam
3344198429Srpaulo	/* Encrypt the frame if need be. */
3345178676Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3346220725Sbschmidt		/* Retrieve key for TX. */
3347198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
3348178676Ssam		if (k == NULL) {
3349198429Srpaulo			m_freem(m);
3350178676Ssam			return ENOBUFS;
3351178676Ssam		}
3352220725Sbschmidt		/* 802.11 header may have moved. */
3353198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3354198429Srpaulo	}
3355198429Srpaulo	totlen = m->m_pkthdr.len;
3356178676Ssam
3357192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
3358178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3359178676Ssam
3360178676Ssam		tap->wt_flags = 0;
3361221648Sbschmidt		tap->wt_rate = rate;
3362178676Ssam		if (k != NULL)
3363178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3364178676Ssam
3365198429Srpaulo		ieee80211_radiotap_tx(vap, m);
3366178676Ssam	}
3367178676Ssam
3368198429Srpaulo	/* Prepare TX firmware command. */
3369198429Srpaulo	cmd = &ring->cmd[ring->cur];
3370198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3371198429Srpaulo	cmd->flags = 0;
3372198429Srpaulo	cmd->qid = ring->qid;
3373198429Srpaulo	cmd->idx = ring->cur;
3374198429Srpaulo
3375198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3376198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3377198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3378198429Srpaulo
3379198429Srpaulo	flags = 0;
3380220720Sbschmidt	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3381220720Sbschmidt		/* Unicast frame, check if an ACK is expected. */
3382220720Sbschmidt		if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3383220720Sbschmidt		    IEEE80211_QOS_ACKPOLICY_NOACK)
3384220720Sbschmidt			flags |= IWN_TX_NEED_ACK;
3385220720Sbschmidt	}
3386198429Srpaulo	if ((wh->i_fc[0] &
3387198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3388198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3389198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3390178676Ssam
3391198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3392198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3393178676Ssam
3394198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3395198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3396198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
3397198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
3398198429Srpaulo			flags |= IWN_TX_NEED_RTS;
3399178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3400201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
3401178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3402198429Srpaulo				flags |= IWN_TX_NEED_CTS;
3403178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3404198429Srpaulo				flags |= IWN_TX_NEED_RTS;
3405178676Ssam		}
3406198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3407198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3408198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3409198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3410198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
3411198429Srpaulo			} else
3412198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
3413198429Srpaulo		}
3414201209Srpaulo	}
3415178676Ssam
3416198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3417198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3418220728Sbschmidt		tx->id = sc->broadcast_id;
3419198429Srpaulo	else
3420198429Srpaulo		tx->id = wn->id;
3421198429Srpaulo
3422178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
3423178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3424178676Ssam
3425198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
3426178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3427178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
3428178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3429178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3430198429Srpaulo			tx->timeout = htole16(3);
3431178676Ssam		else
3432198429Srpaulo			tx->timeout = htole16(2);
3433178676Ssam	} else
3434198429Srpaulo		tx->timeout = htole16(0);
3435178676Ssam
3436178676Ssam	if (hdrlen & 3) {
3437201209Srpaulo		/* First segment length must be a multiple of 4. */
3438178676Ssam		flags |= IWN_TX_NEED_PADDING;
3439178676Ssam		pad = 4 - (hdrlen & 3);
3440178676Ssam	} else
3441178676Ssam		pad = 0;
3442178676Ssam
3443198429Srpaulo	tx->len = htole16(totlen);
3444220720Sbschmidt	tx->tid = tid;
3445201209Srpaulo	tx->rts_ntries = 60;
3446201209Srpaulo	tx->data_ntries = 15;
3447178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3448221648Sbschmidt	tx->rate = wn->ridx[rate];
3449220728Sbschmidt	if (tx->id == sc->broadcast_id) {
3450201209Srpaulo		/* Group or management frame. */
3451201209Srpaulo		tx->linkq = 0;
3452198429Srpaulo		/* XXX Alternate between antenna A and B? */
3453201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
3454221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3455201209Srpaulo	} else {
3456220715Sbschmidt		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
3457201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
3458201209Srpaulo	}
3459198429Srpaulo	/* Set physical address of "scratch area". */
3460201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3461201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3462178676Ssam
3463198429Srpaulo	/* Copy 802.11 header in TX command. */
3464178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3465178676Ssam
3466198429Srpaulo	/* Trim 802.11 header. */
3467198429Srpaulo	m_adj(m, hdrlen);
3468198429Srpaulo	tx->security = 0;
3469198429Srpaulo	tx->flags = htole32(flags);
3470198429Srpaulo
3471220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3472220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3473220700Sbschmidt	if (error != 0) {
3474220700Sbschmidt		if (error != EFBIG) {
3475220700Sbschmidt			device_printf(sc->sc_dev,
3476220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3477220700Sbschmidt			m_freem(m);
3478220700Sbschmidt			return error;
3479178676Ssam		}
3480220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3481220700Sbschmidt		m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3482220700Sbschmidt		if (m1 == NULL) {
3483220700Sbschmidt			device_printf(sc->sc_dev,
3484220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3485220700Sbschmidt			m_freem(m);
3486220700Sbschmidt			return ENOBUFS;
3487220700Sbschmidt		}
3488220700Sbschmidt		m = m1;
3489220700Sbschmidt
3490220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3491220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3492178676Ssam		if (error != 0) {
3493178676Ssam			device_printf(sc->sc_dev,
3494220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3495198429Srpaulo			m_freem(m);
3496178676Ssam			return error;
3497178676Ssam		}
3498178676Ssam	}
3499178676Ssam
3500198429Srpaulo	data->m = m;
3501178676Ssam	data->ni = ni;
3502178676Ssam
3503178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3504198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3505178676Ssam
3506198429Srpaulo	/* Fill TX descriptor. */
3507220700Sbschmidt	desc->nsegs = 1;
3508220700Sbschmidt	if (m->m_len != 0)
3509220700Sbschmidt		desc->nsegs += nsegs;
3510198429Srpaulo	/* First DMA segment is used by the TX command. */
3511201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3512201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3513198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3514198429Srpaulo	/* Other DMA segments are for data payload. */
3515220700Sbschmidt	seg = &segs[0];
3516178676Ssam	for (i = 1; i <= nsegs; i++) {
3517220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3518220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3519220700Sbschmidt		    seg->ds_len << 4);
3520220700Sbschmidt		seg++;
3521178676Ssam	}
3522178676Ssam
3523201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3524201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3525198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3526198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3527198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3528178676Ssam
3529198429Srpaulo	/* Update TX scheduler. */
3530220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3531178676Ssam
3532198429Srpaulo	/* Kick TX ring. */
3533178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3534198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3535178676Ssam
3536198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3537198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3538198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3539178676Ssam
3540178676Ssam	return 0;
3541178676Ssam}
3542178676Ssam
3543178676Ssamstatic int
3544201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3545220720Sbschmidt    struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
3546178676Ssam{
3547221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3548178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3549198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3550198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3551198429Srpaulo	struct iwn_tx_cmd *cmd;
3552198429Srpaulo	struct iwn_cmd_data *tx;
3553198429Srpaulo	struct ieee80211_frame *wh;
3554220720Sbschmidt	struct iwn_tx_ring *ring;
3555178676Ssam	struct iwn_tx_desc *desc;
3556178676Ssam	struct iwn_tx_data *data;
3557220700Sbschmidt	struct mbuf *m1;
3558220700Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3559198429Srpaulo	uint32_t flags;
3560198429Srpaulo	u_int hdrlen;
3561220720Sbschmidt	int ac, totlen, error, pad, nsegs = 0, i, rate;
3562201209Srpaulo	uint8_t ridx, type, txant;
3563178676Ssam
3564198429Srpaulo	IWN_LOCK_ASSERT(sc);
3565178676Ssam
3566201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3567198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3568198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3569198429Srpaulo
3570220720Sbschmidt	ac = params->ibp_pri & 3;
3571220720Sbschmidt
3572220720Sbschmidt	ring = &sc->txq[ac];
3573178676Ssam	desc = &ring->desc[ring->cur];
3574178676Ssam	data = &ring->data[ring->cur];
3575178676Ssam
3576198429Srpaulo	/* Choose a TX rate index. */
3577198429Srpaulo	rate = params->ibp_rate0;
3578221648Sbschmidt	ridx = ic->ic_rt->rateCodeToIndex[rate];
3579221648Sbschmidt	if (ridx == (uint8_t)-1) {
3580198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3581201209Srpaulo		m_freem(m);
3582198429Srpaulo		return EINVAL;
3583198429Srpaulo	}
3584198429Srpaulo
3585201209Srpaulo	totlen = m->m_pkthdr.len;
3586198429Srpaulo
3587201209Srpaulo	/* Prepare TX firmware command. */
3588198429Srpaulo	cmd = &ring->cmd[ring->cur];
3589198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3590198429Srpaulo	cmd->flags = 0;
3591198429Srpaulo	cmd->qid = ring->qid;
3592198429Srpaulo	cmd->idx = ring->cur;
3593198429Srpaulo
3594198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3595201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3596198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3597198429Srpaulo
3598198429Srpaulo	flags = 0;
3599198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3600198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3601198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3602198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3603198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3604198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3605198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3606198429Srpaulo		} else
3607198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3608198429Srpaulo	}
3609198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3610198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3611198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3612198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3613198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3614198429Srpaulo		} else
3615198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3616198429Srpaulo	}
3617198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3618198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3619198429Srpaulo
3620220725Sbschmidt		/* Tell HW to set timestamp in probe responses. */
3621198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3622198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3623198429Srpaulo
3624198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3625198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3626198429Srpaulo			tx->timeout = htole16(3);
3627198429Srpaulo		else
3628198429Srpaulo			tx->timeout = htole16(2);
3629198429Srpaulo	} else
3630198429Srpaulo		tx->timeout = htole16(0);
3631198429Srpaulo
3632198429Srpaulo	if (hdrlen & 3) {
3633201209Srpaulo		/* First segment length must be a multiple of 4. */
3634198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3635198429Srpaulo		pad = 4 - (hdrlen & 3);
3636198429Srpaulo	} else
3637198429Srpaulo		pad = 0;
3638198429Srpaulo
3639198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3640198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3641198429Srpaulo
3642198429Srpaulo		tap->wt_flags = 0;
3643198429Srpaulo		tap->wt_rate = rate;
3644198429Srpaulo
3645201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3646198429Srpaulo	}
3647198429Srpaulo
3648198429Srpaulo	tx->len = htole16(totlen);
3649198429Srpaulo	tx->tid = 0;
3650220728Sbschmidt	tx->id = sc->broadcast_id;
3651198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3652198429Srpaulo	tx->data_ntries = params->ibp_try0;
3653198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3654221648Sbschmidt	tx->rate = htole32(rate2plcp(rate));
3655221648Sbschmidt	if (ridx < IWN_RIDX_OFDM6 &&
3656221648Sbschmidt	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
3657221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_CCK);
3658201209Srpaulo	/* Group or management frame. */
3659201209Srpaulo	tx->linkq = 0;
3660201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3661221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3662198429Srpaulo	/* Set physical address of "scratch area". */
3663220694Sbschmidt	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3664220694Sbschmidt	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3665198429Srpaulo
3666198429Srpaulo	/* Copy 802.11 header in TX command. */
3667198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3668198429Srpaulo
3669198429Srpaulo	/* Trim 802.11 header. */
3670201209Srpaulo	m_adj(m, hdrlen);
3671198429Srpaulo	tx->security = 0;
3672198429Srpaulo	tx->flags = htole32(flags);
3673198429Srpaulo
3674220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3675220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3676220700Sbschmidt	if (error != 0) {
3677220700Sbschmidt		if (error != EFBIG) {
3678220700Sbschmidt			device_printf(sc->sc_dev,
3679220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3680220700Sbschmidt			m_freem(m);
3681220700Sbschmidt			return error;
3682178676Ssam		}
3683220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3684220700Sbschmidt		m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3685220700Sbschmidt		if (m1 == NULL) {
3686220700Sbschmidt			device_printf(sc->sc_dev,
3687220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3688220700Sbschmidt			m_freem(m);
3689220700Sbschmidt			return ENOBUFS;
3690220700Sbschmidt		}
3691220700Sbschmidt		m = m1;
3692220700Sbschmidt
3693220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3694220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3695178676Ssam		if (error != 0) {
3696178676Ssam			device_printf(sc->sc_dev,
3697220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3698201209Srpaulo			m_freem(m);
3699178676Ssam			return error;
3700178676Ssam		}
3701178676Ssam	}
3702178676Ssam
3703201209Srpaulo	data->m = m;
3704178676Ssam	data->ni = ni;
3705178676Ssam
3706178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3707201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3708178676Ssam
3709198429Srpaulo	/* Fill TX descriptor. */
3710220700Sbschmidt	desc->nsegs = 1;
3711220700Sbschmidt	if (m->m_len != 0)
3712220700Sbschmidt		desc->nsegs += nsegs;
3713198429Srpaulo	/* First DMA segment is used by the TX command. */
3714201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3715201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3716198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3717198429Srpaulo	/* Other DMA segments are for data payload. */
3718220700Sbschmidt	seg = &segs[0];
3719178676Ssam	for (i = 1; i <= nsegs; i++) {
3720220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3721220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3722220700Sbschmidt		    seg->ds_len << 4);
3723220700Sbschmidt		seg++;
3724178676Ssam	}
3725178676Ssam
3726201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3727201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3728201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3729201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3730201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3731201209Srpaulo
3732198429Srpaulo	/* Update TX scheduler. */
3733220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3734178676Ssam
3735198429Srpaulo	/* Kick TX ring. */
3736178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3737198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3738178676Ssam
3739198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3740198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3741198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3742178676Ssam
3743178676Ssam	return 0;
3744178676Ssam}
3745178676Ssam
3746178676Ssamstatic int
3747178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3748220720Sbschmidt    const struct ieee80211_bpf_params *params)
3749178676Ssam{
3750178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3751178676Ssam	struct ifnet *ifp = ic->ic_ifp;
3752178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3753198429Srpaulo	int error = 0;
3754178676Ssam
3755178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3756178676Ssam		ieee80211_free_node(ni);
3757178676Ssam		m_freem(m);
3758178676Ssam		return ENETDOWN;
3759178676Ssam	}
3760178676Ssam
3761178676Ssam	IWN_LOCK(sc);
3762178676Ssam	if (params == NULL) {
3763178676Ssam		/*
3764178676Ssam		 * Legacy path; interpret frame contents to decide
3765178676Ssam		 * precisely how to send the frame.
3766178676Ssam		 */
3767220720Sbschmidt		error = iwn_tx_data(sc, m, ni);
3768178676Ssam	} else {
3769178676Ssam		/*
3770178676Ssam		 * Caller supplied explicit parameters to use in
3771178676Ssam		 * sending the frame.
3772178676Ssam		 */
3773220720Sbschmidt		error = iwn_tx_data_raw(sc, m, ni, params);
3774178676Ssam	}
3775178676Ssam	if (error != 0) {
3776178676Ssam		/* NB: m is reclaimed on tx failure */
3777178676Ssam		ieee80211_free_node(ni);
3778178676Ssam		ifp->if_oerrors++;
3779178676Ssam	}
3780220667Sbschmidt	sc->sc_tx_timer = 5;
3781220667Sbschmidt
3782178676Ssam	IWN_UNLOCK(sc);
3783178676Ssam	return error;
3784178676Ssam}
3785178676Ssam
3786206477Sbschmidtstatic void
3787198429Srpauloiwn_start(struct ifnet *ifp)
3788198429Srpaulo{
3789198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3790198429Srpaulo
3791198429Srpaulo	IWN_LOCK(sc);
3792198429Srpaulo	iwn_start_locked(ifp);
3793198429Srpaulo	IWN_UNLOCK(sc);
3794198429Srpaulo}
3795198429Srpaulo
3796206477Sbschmidtstatic void
3797198429Srpauloiwn_start_locked(struct ifnet *ifp)
3798198429Srpaulo{
3799198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3800198429Srpaulo	struct ieee80211_node *ni;
3801198429Srpaulo	struct mbuf *m;
3802198429Srpaulo
3803198429Srpaulo	IWN_LOCK_ASSERT(sc);
3804198429Srpaulo
3805220720Sbschmidt	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
3806220720Sbschmidt	    (ifp->if_drv_flags & IFF_DRV_OACTIVE))
3807220720Sbschmidt		return;
3808220720Sbschmidt
3809198429Srpaulo	for (;;) {
3810198429Srpaulo		if (sc->qfullmsk != 0) {
3811198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
3812198429Srpaulo			break;
3813198429Srpaulo		}
3814198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
3815198429Srpaulo		if (m == NULL)
3816198429Srpaulo			break;
3817198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
3818220720Sbschmidt		if (iwn_tx_data(sc, m, ni) != 0) {
3819220720Sbschmidt			ieee80211_free_node(ni);
3820198429Srpaulo			ifp->if_oerrors++;
3821220720Sbschmidt			continue;
3822198429Srpaulo		}
3823198429Srpaulo		sc->sc_tx_timer = 5;
3824198429Srpaulo	}
3825198429Srpaulo}
3826198429Srpaulo
3827178676Ssamstatic void
3828220667Sbschmidtiwn_watchdog(void *arg)
3829178676Ssam{
3830220667Sbschmidt	struct iwn_softc *sc = arg;
3831220667Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
3832220667Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
3833178676Ssam
3834220667Sbschmidt	IWN_LOCK_ASSERT(sc);
3835220667Sbschmidt
3836220667Sbschmidt	KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
3837220667Sbschmidt
3838220668Sbschmidt	if (sc->sc_tx_timer > 0) {
3839220668Sbschmidt		if (--sc->sc_tx_timer == 0) {
3840220667Sbschmidt			if_printf(ifp, "device timeout\n");
3841220667Sbschmidt			ieee80211_runtask(ic, &sc->sc_reinit_task);
3842220667Sbschmidt			return;
3843220667Sbschmidt		}
3844178676Ssam	}
3845220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
3846178676Ssam}
3847178676Ssam
3848206477Sbschmidtstatic int
3849178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
3850178676Ssam{
3851178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3852178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3853201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3854178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
3855201209Srpaulo	int error = 0, startall = 0, stop = 0;
3856178676Ssam
3857178676Ssam	switch (cmd) {
3858220723Sbschmidt	case SIOCGIFADDR:
3859220723Sbschmidt		error = ether_ioctl(ifp, cmd, data);
3860220723Sbschmidt		break;
3861178676Ssam	case SIOCSIFFLAGS:
3862178704Sthompsa		IWN_LOCK(sc);
3863178676Ssam		if (ifp->if_flags & IFF_UP) {
3864178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
3865178676Ssam				iwn_init_locked(sc);
3866201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
3867201209Srpaulo					startall = 1;
3868201209Srpaulo				else
3869201209Srpaulo					stop = 1;
3870178676Ssam			}
3871178676Ssam		} else {
3872178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3873178676Ssam				iwn_stop_locked(sc);
3874178676Ssam		}
3875178704Sthompsa		IWN_UNLOCK(sc);
3876178704Sthompsa		if (startall)
3877178704Sthompsa			ieee80211_start_all(ic);
3878201209Srpaulo		else if (vap != NULL && stop)
3879201209Srpaulo			ieee80211_stop(vap);
3880178676Ssam		break;
3881178676Ssam	case SIOCGIFMEDIA:
3882178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
3883178676Ssam		break;
3884178704Sthompsa	default:
3885178704Sthompsa		error = EINVAL;
3886178704Sthompsa		break;
3887178676Ssam	}
3888178676Ssam	return error;
3889178676Ssam}
3890178676Ssam
3891178676Ssam/*
3892178676Ssam * Send a command to the firmware.
3893178676Ssam */
3894206477Sbschmidtstatic int
3895178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
3896178676Ssam{
3897221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3898178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
3899178676Ssam	struct iwn_tx_desc *desc;
3900198429Srpaulo	struct iwn_tx_data *data;
3901178676Ssam	struct iwn_tx_cmd *cmd;
3902198439Srpaulo	struct mbuf *m;
3903178676Ssam	bus_addr_t paddr;
3904198439Srpaulo	int totlen, error;
3905178676Ssam
3906221650Sbschmidt	if (async == 0)
3907221650Sbschmidt		IWN_LOCK_ASSERT(sc);
3908178676Ssam
3909178676Ssam	desc = &ring->desc[ring->cur];
3910198429Srpaulo	data = &ring->data[ring->cur];
3911198429Srpaulo	totlen = 4 + size;
3912178676Ssam
3913198439Srpaulo	if (size > sizeof cmd->data) {
3914198439Srpaulo		/* Command is too large to fit in a descriptor. */
3915198439Srpaulo		if (totlen > MCLBYTES)
3916198439Srpaulo			return EINVAL;
3917201209Srpaulo		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
3918198439Srpaulo		if (m == NULL)
3919198439Srpaulo			return ENOMEM;
3920198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
3921201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
3922198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
3923198439Srpaulo		if (error != 0) {
3924198439Srpaulo			m_freem(m);
3925198439Srpaulo			return error;
3926198439Srpaulo		}
3927198439Srpaulo		data->m = m;
3928198439Srpaulo	} else {
3929198439Srpaulo		cmd = &ring->cmd[ring->cur];
3930198439Srpaulo		paddr = data->cmd_paddr;
3931198439Srpaulo	}
3932198439Srpaulo
3933178676Ssam	cmd->code = code;
3934178676Ssam	cmd->flags = 0;
3935178676Ssam	cmd->qid = ring->qid;
3936178676Ssam	cmd->idx = ring->cur;
3937178676Ssam	memcpy(cmd->data, buf, size);
3938178676Ssam
3939198429Srpaulo	desc->nsegs = 1;
3940198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
3941198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
3942178676Ssam
3943178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
3944178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
3945178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
3946178676Ssam
3947198439Srpaulo	if (size > sizeof cmd->data) {
3948201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3949198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3950198439Srpaulo	} else {
3951201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3952198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3953198439Srpaulo	}
3954198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3955198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3956198439Srpaulo
3957198429Srpaulo	/* Update TX scheduler. */
3958220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, 0, 0);
3959198429Srpaulo
3960198429Srpaulo	/* Kick command ring. */
3961178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3962198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3963178676Ssam
3964198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
3965178676Ssam}
3966178676Ssam
3967206477Sbschmidtstatic int
3968198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3969198429Srpaulo{
3970198429Srpaulo	struct iwn4965_node_info hnode;
3971198429Srpaulo	caddr_t src, dst;
3972198429Srpaulo
3973198429Srpaulo	/*
3974198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
3975198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
3976198429Srpaulo	 * fields before sending the command.
3977198429Srpaulo	 */
3978198429Srpaulo	src = (caddr_t)node;
3979198429Srpaulo	dst = (caddr_t)&hnode;
3980198429Srpaulo	memcpy(dst, src, 48);
3981198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
3982198429Srpaulo	memcpy(dst + 48, src + 72, 20);
3983198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
3984198429Srpaulo}
3985198429Srpaulo
3986206477Sbschmidtstatic int
3987198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3988198429Srpaulo{
3989198429Srpaulo	/* Direct mapping. */
3990198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
3991198429Srpaulo}
3992198429Srpaulo
3993206477Sbschmidtstatic int
3994220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
3995178676Ssam{
3996220715Sbschmidt	struct iwn_node *wn = (void *)ni;
3997220715Sbschmidt	struct ieee80211_rateset *rs = &ni->ni_rates;
3998198429Srpaulo	struct iwn_cmd_link_quality linkq;
3999220715Sbschmidt	uint8_t txant;
4000221648Sbschmidt	int i, rate, txrate;
4001178676Ssam
4002198429Srpaulo	/* Use the first valid TX antenna. */
4003201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4004178676Ssam
4005198429Srpaulo	memset(&linkq, 0, sizeof linkq);
4006220715Sbschmidt	linkq.id = wn->id;
4007198429Srpaulo	linkq.antmsk_1stream = txant;
4008201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
4009221651Sbschmidt	linkq.ampdu_max = 64;
4010198429Srpaulo	linkq.ampdu_threshold = 3;
4011198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4012198429Srpaulo
4013220715Sbschmidt	/* Start at highest available bit-rate. */
4014221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4015221649Sbschmidt		txrate = ni->ni_htrates.rs_nrates - 1;
4016221649Sbschmidt	else
4017221649Sbschmidt		txrate = rs->rs_nrates - 1;
4018178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
4019221649Sbschmidt		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4020221649Sbschmidt			rate = IEEE80211_RATE_MCS | txrate;
4021221649Sbschmidt		else
4022221649Sbschmidt			rate = rs->rs_rates[txrate] & IEEE80211_RATE_VAL;
4023221648Sbschmidt		linkq.retry[i] = wn->ridx[rate];
4024221648Sbschmidt
4025221649Sbschmidt		if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) &&
4026221649Sbschmidt		    (le32toh(wn->ridx[rate]) & 0xff) > 7)
4027221649Sbschmidt			linkq.mimo = i + 1;
4028221649Sbschmidt
4029220715Sbschmidt		/* Next retry at immediate lower bit-rate. */
4030220715Sbschmidt		if (txrate > 0)
4031220715Sbschmidt			txrate--;
4032178676Ssam	}
4033220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
4034178676Ssam}
4035178676Ssam
4036178676Ssam/*
4037198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
4038178676Ssam */
4039206477Sbschmidtstatic int
4040201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
4041178676Ssam{
4042220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4043198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4044220715Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4045178676Ssam	struct iwn_node_info node;
4046220715Sbschmidt	struct iwn_cmd_link_quality linkq;
4047220715Sbschmidt	uint8_t txant;
4048220715Sbschmidt	int i, error;
4049178676Ssam
4050178676Ssam	memset(&node, 0, sizeof node);
4051198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
4052220728Sbschmidt	node.id = sc->broadcast_id;
4053198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
4054220728Sbschmidt	if ((error = ops->add_node(sc, &node, async)) != 0)
4055198429Srpaulo		return error;
4056178676Ssam
4057220715Sbschmidt	/* Use the first valid TX antenna. */
4058220715Sbschmidt	txant = IWN_LSB(sc->txchainmask);
4059220715Sbschmidt
4060220715Sbschmidt	memset(&linkq, 0, sizeof linkq);
4061220728Sbschmidt	linkq.id = sc->broadcast_id;
4062220715Sbschmidt	linkq.antmsk_1stream = txant;
4063220715Sbschmidt	linkq.antmsk_2stream = IWN_ANT_AB;
4064220715Sbschmidt	linkq.ampdu_max = 64;
4065220715Sbschmidt	linkq.ampdu_threshold = 3;
4066220715Sbschmidt	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4067220715Sbschmidt
4068220715Sbschmidt	/* Use lowest mandatory bit-rate. */
4069220715Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
4070221648Sbschmidt		linkq.retry[0] = htole32(0xd);
4071220715Sbschmidt	else
4072221648Sbschmidt		linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
4073221648Sbschmidt	linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
4074220715Sbschmidt	/* Use same bit-rate for all TX retries. */
4075220715Sbschmidt	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
4076221648Sbschmidt		linkq.retry[i] = linkq.retry[0];
4077220715Sbschmidt	}
4078220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
4079178676Ssam}
4080178676Ssam
4081206477Sbschmidtstatic int
4082220721Sbschmidtiwn_updateedca(struct ieee80211com *ic)
4083178676Ssam{
4084178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
4085178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
4086178676Ssam	struct iwn_edca_params cmd;
4087220721Sbschmidt	int aci;
4088178676Ssam
4089178676Ssam	memset(&cmd, 0, sizeof cmd);
4090178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
4091220721Sbschmidt	for (aci = 0; aci < WME_NUM_AC; aci++) {
4092220721Sbschmidt		const struct wmeParams *ac =
4093220721Sbschmidt		    &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
4094220721Sbschmidt		cmd.ac[aci].aifsn = ac->wmep_aifsn;
4095220721Sbschmidt		cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
4096220721Sbschmidt		cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
4097220721Sbschmidt		cmd.ac[aci].txoplimit =
4098220721Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
4099178676Ssam	}
4100201209Srpaulo	IEEE80211_UNLOCK(ic);
4101178676Ssam	IWN_LOCK(sc);
4102220725Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
4103178676Ssam	IWN_UNLOCK(sc);
4104201209Srpaulo	IEEE80211_LOCK(ic);
4105178676Ssam	return 0;
4106178676Ssam#undef IWN_EXP2
4107178676Ssam}
4108178676Ssam
4109201209Srpaulostatic void
4110201209Srpauloiwn_update_mcast(struct ifnet *ifp)
4111201209Srpaulo{
4112201209Srpaulo	/* Ignore */
4113201209Srpaulo}
4114201209Srpaulo
4115206477Sbschmidtstatic void
4116178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
4117178676Ssam{
4118178676Ssam	struct iwn_cmd_led led;
4119178676Ssam
4120198429Srpaulo	/* Clear microcode LED ownership. */
4121198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
4122198429Srpaulo
4123178676Ssam	led.which = which;
4124198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
4125178676Ssam	led.off = off;
4126178676Ssam	led.on = on;
4127198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
4128178676Ssam}
4129178676Ssam
4130178676Ssam/*
4131201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
4132201209Srpaulo * and notify us.
4133178676Ssam */
4134206477Sbschmidtstatic int
4135178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
4136178676Ssam{
4137178676Ssam	struct iwn_critical_temp crit;
4138201209Srpaulo	int32_t temp;
4139178676Ssam
4140198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
4141178676Ssam
4142201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
4143201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
4144201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
4145201209Srpaulo		temp = IWN_CTOK(110);
4146201209Srpaulo	else
4147201209Srpaulo		temp = 110;
4148178676Ssam	memset(&crit, 0, sizeof crit);
4149201209Srpaulo	crit.tempR = htole32(temp);
4150220726Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
4151178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
4152178676Ssam}
4153178676Ssam
4154206477Sbschmidtstatic int
4155198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
4156178676Ssam{
4157198429Srpaulo	struct iwn_cmd_timing cmd;
4158178676Ssam	uint64_t val, mod;
4159178676Ssam
4160198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4161198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
4162198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
4163198429Srpaulo	cmd.lintval = htole16(10);
4164178676Ssam
4165198429Srpaulo	/* Compute remaining time until next beacon. */
4166220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
4167198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
4168198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
4169178676Ssam
4170198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
4171198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
4172178676Ssam
4173198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
4174178676Ssam}
4175178676Ssam
4176206477Sbschmidtstatic void
4177198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
4178178676Ssam{
4179201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
4180201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
4181201882Skeramida
4182220725Sbschmidt	/* Adjust TX power if need be (delta >= 3 degC). */
4183178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
4184178676Ssam	    __func__, sc->temp, temp);
4185198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
4186198429Srpaulo		/* Record temperature of last calibration. */
4187198429Srpaulo		sc->temp = temp;
4188201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
4189178676Ssam	}
4190178676Ssam}
4191178676Ssam
4192178676Ssam/*
4193198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
4194178676Ssam * This function takes into account the regulatory information from EEPROM,
4195178676Ssam * the current temperature and the current voltage.
4196178676Ssam */
4197206477Sbschmidtstatic int
4198201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4199201882Skeramida    int async)
4200178676Ssam{
4201198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
4202178676Ssam#define fdivround(a, b, n)	\
4203178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
4204198429Srpaulo/* Linear interpolation. */
4205178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
4206178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
4207178676Ssam
4208178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
4209178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4210198429Srpaulo	struct iwn4965_cmd_txpower cmd;
4211198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
4212220723Sbschmidt	const uint8_t *rf_gain, *dsp_gain;
4213178676Ssam	int32_t vdiff, tdiff;
4214178676Ssam	int i, c, grp, maxpwr;
4215198429Srpaulo	uint8_t chan;
4216178676Ssam
4217220687Sbschmidt	/* Retrieve current channel from last RXON. */
4218220687Sbschmidt	chan = sc->rxon.chan;
4219201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
4220201209Srpaulo	    chan);
4221178676Ssam
4222178676Ssam	memset(&cmd, 0, sizeof cmd);
4223178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4224178676Ssam	cmd.chan = chan;
4225178676Ssam
4226178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4227178676Ssam		maxpwr   = sc->maxpwr5GHz;
4228198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
4229198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
4230178676Ssam	} else {
4231178676Ssam		maxpwr   = sc->maxpwr2GHz;
4232198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
4233198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
4234178676Ssam	}
4235178676Ssam
4236198429Srpaulo	/* Compute voltage compensation. */
4237178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4238178676Ssam	if (vdiff > 0)
4239178676Ssam		vdiff *= 2;
4240178676Ssam	if (abs(vdiff) > 2)
4241178676Ssam		vdiff = 0;
4242178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4243178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
4244178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
4245178676Ssam
4246201209Srpaulo	/* Get channel attenuation group. */
4247178676Ssam	if (chan <= 20)		/* 1-20 */
4248178676Ssam		grp = 4;
4249178676Ssam	else if (chan <= 43)	/* 34-43 */
4250178676Ssam		grp = 0;
4251178676Ssam	else if (chan <= 70)	/* 44-70 */
4252178676Ssam		grp = 1;
4253178676Ssam	else if (chan <= 124)	/* 71-124 */
4254178676Ssam		grp = 2;
4255178676Ssam	else			/* 125-200 */
4256178676Ssam		grp = 3;
4257178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4258178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
4259178676Ssam
4260201209Srpaulo	/* Get channel sub-band. */
4261178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
4262178676Ssam		if (sc->bands[i].lo != 0 &&
4263178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4264178676Ssam			break;
4265198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4266198429Srpaulo		return EINVAL;
4267178676Ssam	chans = sc->bands[i].chans;
4268178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4269178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
4270178676Ssam
4271198429Srpaulo	for (c = 0; c < 2; c++) {
4272178676Ssam		uint8_t power, gain, temp;
4273178676Ssam		int maxchpwr, pwr, ridx, idx;
4274178676Ssam
4275178676Ssam		power = interpolate(chan,
4276178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
4277178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
4278178676Ssam		gain  = interpolate(chan,
4279178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
4280178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
4281178676Ssam		temp  = interpolate(chan,
4282178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
4283178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
4284178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4285178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
4286178676Ssam		    __func__, c, power, gain, temp);
4287178676Ssam
4288198429Srpaulo		/* Compute temperature compensation. */
4289178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4290178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4291178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
4292178676Ssam		    __func__, tdiff, sc->temp, temp);
4293178676Ssam
4294178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4295201209Srpaulo			/* Convert dBm to half-dBm. */
4296198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
4297198429Srpaulo			if ((ridx / 8) & 1)
4298198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4299178676Ssam
4300198429Srpaulo			pwr = maxpwr;
4301178676Ssam
4302198429Srpaulo			/* Adjust TX power based on rate. */
4303198429Srpaulo			if ((ridx % 8) == 5)
4304198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
4305198429Srpaulo			else if ((ridx % 8) == 6)
4306198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
4307198429Srpaulo			else if ((ridx % 8) == 7)
4308198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
4309198429Srpaulo			else
4310198429Srpaulo				pwr -= 10;	/* Others: -5dB */
4311178676Ssam
4312201209Srpaulo			/* Do not exceed channel max TX power. */
4313178676Ssam			if (pwr > maxchpwr)
4314178676Ssam				pwr = maxchpwr;
4315178676Ssam
4316178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
4317178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
4318178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
4319178676Ssam
4320178676Ssam			if (cmd.band == 0)
4321178676Ssam				idx += 9;	/* 5GHz */
4322178676Ssam			if (ridx == IWN_RIDX_MAX)
4323178676Ssam				idx += 5;	/* CCK */
4324178676Ssam
4325198429Srpaulo			/* Make sure idx stays in a valid range. */
4326178676Ssam			if (idx < 0)
4327178676Ssam				idx = 0;
4328198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
4329198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
4330178676Ssam
4331178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4332178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
4333178676Ssam			    __func__, c, ridx, idx);
4334178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4335178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4336178676Ssam		}
4337178676Ssam	}
4338178676Ssam
4339178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4340178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
4341178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4342178676Ssam
4343178676Ssam#undef interpolate
4344178676Ssam#undef fdivround
4345178676Ssam}
4346178676Ssam
4347206477Sbschmidtstatic int
4348201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4349201882Skeramida    int async)
4350198429Srpaulo{
4351198429Srpaulo	struct iwn5000_cmd_txpower cmd;
4352198429Srpaulo
4353198429Srpaulo	/*
4354198429Srpaulo	 * TX power calibration is handled automatically by the firmware
4355198429Srpaulo	 * for 5000 Series.
4356198429Srpaulo	 */
4357198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4358198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4359198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4360198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4361198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
4362198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4363198429Srpaulo}
4364198429Srpaulo
4365178676Ssam/*
4366198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
4367178676Ssam */
4368206477Sbschmidtstatic int
4369198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4370178676Ssam{
4371198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
4372198429Srpaulo	uint8_t mask, agc;
4373198429Srpaulo	int rssi;
4374178676Ssam
4375201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4376198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4377178676Ssam
4378178676Ssam	rssi = 0;
4379220689Sbschmidt	if (mask & IWN_ANT_A)
4380220689Sbschmidt		rssi = MAX(rssi, phy->rssi[0]);
4381220689Sbschmidt	if (mask & IWN_ANT_B)
4382220689Sbschmidt		rssi = MAX(rssi, phy->rssi[2]);
4383220689Sbschmidt	if (mask & IWN_ANT_C)
4384220689Sbschmidt		rssi = MAX(rssi, phy->rssi[4]);
4385198429Srpaulo
4386220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4387220724Sbschmidt	    "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
4388220724Sbschmidt	    mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
4389178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
4390178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
4391178676Ssam}
4392178676Ssam
4393206477Sbschmidtstatic int
4394198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4395198429Srpaulo{
4396198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
4397220723Sbschmidt	uint8_t agc;
4398198429Srpaulo	int rssi;
4399198429Srpaulo
4400198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4401198429Srpaulo
4402198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4403198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
4404198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4405198429Srpaulo
4406220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4407220724Sbschmidt	    "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
4408201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
4409198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
4410198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
4411198429Srpaulo}
4412198429Srpaulo
4413178676Ssam/*
4414198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
4415178676Ssam */
4416206477Sbschmidtstatic int
4417178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
4418178676Ssam{
4419178676Ssam	int i, total, nbant, noise;
4420178676Ssam
4421178676Ssam	total = nbant = 0;
4422178676Ssam	for (i = 0; i < 3; i++) {
4423198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4424198429Srpaulo			continue;
4425198429Srpaulo		total += noise;
4426198429Srpaulo		nbant++;
4427178676Ssam	}
4428198429Srpaulo	/* There should be at least one antenna but check anyway. */
4429178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
4430178676Ssam}
4431178676Ssam
4432178676Ssam/*
4433198429Srpaulo * Compute temperature (in degC) from last received statistics.
4434178676Ssam */
4435206477Sbschmidtstatic int
4436198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
4437178676Ssam{
4438178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4439178676Ssam	int32_t r1, r2, r3, r4, temp;
4440178676Ssam
4441178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
4442178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
4443178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4444178676Ssam	r4 = le32toh(sc->rawtemp);
4445178676Ssam
4446220725Sbschmidt	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4447178676Ssam		return 0;
4448178676Ssam
4449198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4450220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4451198429Srpaulo	/* Compute temperature in Kelvin. */
4452178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4453178676Ssam	temp = (temp * 97) / 100 + 8;
4454178676Ssam
4455201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4456201209Srpaulo	    IWN_KTOC(temp));
4457178676Ssam	return IWN_KTOC(temp);
4458178676Ssam}
4459178676Ssam
4460206477Sbschmidtstatic int
4461198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4462198429Srpaulo{
4463201209Srpaulo	int32_t temp;
4464201209Srpaulo
4465198429Srpaulo	/*
4466198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4467220725Sbschmidt	 * TX power calibration is handled by firmware.
4468198429Srpaulo	 */
4469201209Srpaulo	temp = le32toh(sc->rawtemp);
4470201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4471201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4472201209Srpaulo		temp = IWN_KTOC(temp);
4473201209Srpaulo	}
4474201209Srpaulo	return temp;
4475198429Srpaulo}
4476198429Srpaulo
4477178676Ssam/*
4478178676Ssam * Initialize sensitivity calibration state machine.
4479178676Ssam */
4480206477Sbschmidtstatic int
4481178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4482178676Ssam{
4483220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4484178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4485198429Srpaulo	uint32_t flags;
4486178676Ssam	int error;
4487178676Ssam
4488198429Srpaulo	/* Reset calibration state machine. */
4489178676Ssam	memset(calib, 0, sizeof (*calib));
4490178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4491178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4492198429Srpaulo	/* Set initial correlation values. */
4493201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4494201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4495206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4496201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4497198429Srpaulo	calib->cck_x4      = 125;
4498201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4499201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4500178676Ssam
4501198429Srpaulo	/* Write initial sensitivity. */
4502220726Sbschmidt	if ((error = iwn_send_sensitivity(sc)) != 0)
4503178676Ssam		return error;
4504178676Ssam
4505198429Srpaulo	/* Write initial gains. */
4506220728Sbschmidt	if ((error = ops->init_gains(sc)) != 0)
4507198429Srpaulo		return error;
4508198429Srpaulo
4509198429Srpaulo	/* Request statistics at each beacon interval. */
4510198429Srpaulo	flags = 0;
4511220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
4512220724Sbschmidt	    __func__);
4513198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4514178676Ssam}
4515178676Ssam
4516178676Ssam/*
4517178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4518178676Ssam * after association and use them to determine connected antennas and
4519198429Srpaulo * to set differential gains.
4520178676Ssam */
4521206477Sbschmidtstatic void
4522198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4523178676Ssam    const struct iwn_rx_general_stats *stats)
4524178676Ssam{
4525220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4526178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4527198429Srpaulo	uint32_t val;
4528198429Srpaulo	int i;
4529178676Ssam
4530198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4531178676Ssam	for (i = 0; i < 3; i++) {
4532178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4533178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4534178676Ssam	}
4535198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4536178676Ssam	if (++calib->nbeacons < 20)
4537178676Ssam		return;
4538178676Ssam
4539198429Srpaulo	/* Determine highest average RSSI. */
4540198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4541198429Srpaulo	val = MAX(calib->rssi[2], val);
4542178676Ssam
4543198429Srpaulo	/* Determine which antennas are connected. */
4544210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4545178676Ssam	for (i = 0; i < 3; i++)
4546210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4547210110Sbschmidt			sc->chainmask &= ~(1 << i);
4548210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4549210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4550210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4551210110Sbschmidt
4552198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4553201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4554201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4555178676Ssam
4556220728Sbschmidt	(void)ops->set_gains(sc);
4557198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4558198429Srpaulo
4559198429Srpaulo#ifdef notyet
4560198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4561201209Srpaulo	sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4562220728Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
4563198429Srpaulo#endif
4564198429Srpaulo
4565198429Srpaulo#if 0
4566198429Srpaulo	/* XXX: not yet */
4567198429Srpaulo	/* Enable power-saving mode if requested by user. */
4568198429Srpaulo	if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
4569198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4570198429Srpaulo#endif
4571198429Srpaulo}
4572198429Srpaulo
4573206477Sbschmidtstatic int
4574198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4575198429Srpaulo{
4576198429Srpaulo	struct iwn_phy_calib_gain cmd;
4577198429Srpaulo
4578198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4579198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4580198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4581198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4582198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4583198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4584198429Srpaulo}
4585198429Srpaulo
4586206477Sbschmidtstatic int
4587198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4588198429Srpaulo{
4589198429Srpaulo	struct iwn_phy_calib cmd;
4590198429Srpaulo
4591198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4592220866Sbschmidt	cmd.code = sc->reset_noise_gain;
4593198429Srpaulo	cmd.ngroups = 1;
4594198429Srpaulo	cmd.isvalid = 1;
4595198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4596198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4597198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4598198429Srpaulo}
4599198429Srpaulo
4600206477Sbschmidtstatic int
4601198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4602198429Srpaulo{
4603198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4604198429Srpaulo	struct iwn_phy_calib_gain cmd;
4605198429Srpaulo	int i, delta, noise;
4606198429Srpaulo
4607198429Srpaulo	/* Get minimal noise among connected antennas. */
4608201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4609178676Ssam	for (i = 0; i < 3; i++)
4610201209Srpaulo		if (sc->chainmask & (1 << i))
4611198429Srpaulo			noise = MIN(calib->noise[i], noise);
4612178676Ssam
4613178676Ssam	memset(&cmd, 0, sizeof cmd);
4614198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4615198429Srpaulo	/* Set differential gains for connected antennas. */
4616178676Ssam	for (i = 0; i < 3; i++) {
4617201209Srpaulo		if (sc->chainmask & (1 << i)) {
4618198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4619198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4620198429Srpaulo			/* NB: delta <= 0 */
4621198429Srpaulo			/* Limit to [-4.5dB,0]. */
4622198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4623198429Srpaulo			if (delta < 0)
4624198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4625178676Ssam		}
4626178676Ssam	}
4627178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4628198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4629201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4630198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4631178676Ssam}
4632178676Ssam
4633206477Sbschmidtstatic int
4634198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4635198429Srpaulo{
4636198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4637198429Srpaulo	struct iwn_phy_calib_gain cmd;
4638220723Sbschmidt	int i, ant, div, delta;
4639198429Srpaulo
4640206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
4641206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
4642198429Srpaulo
4643198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4644220866Sbschmidt	cmd.code = sc->noise_gain;
4645198429Srpaulo	cmd.ngroups = 1;
4646198429Srpaulo	cmd.isvalid = 1;
4647201209Srpaulo	/* Get first available RX antenna as referential. */
4648201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
4649201209Srpaulo	/* Set differential gains for other antennas. */
4650201209Srpaulo	for (i = ant + 1; i < 3; i++) {
4651201209Srpaulo		if (sc->chainmask & (1 << i)) {
4652201209Srpaulo			/* The delta is relative to antenna "ant". */
4653201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
4654206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
4655198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
4656198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
4657198429Srpaulo			if (delta < 0)
4658198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
4659198429Srpaulo		}
4660198429Srpaulo	}
4661198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4662198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
4663201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
4664198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4665198429Srpaulo}
4666198429Srpaulo
4667178676Ssam/*
4668198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
4669178676Ssam * during the last beacon period.
4670178676Ssam */
4671206477Sbschmidtstatic void
4672178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
4673178676Ssam{
4674198429Srpaulo#define inc(val, inc, max)			\
4675178676Ssam	if ((val) < (max)) {			\
4676178676Ssam		if ((val) < (max) - (inc))	\
4677178676Ssam			(val) += (inc);		\
4678178676Ssam		else				\
4679178676Ssam			(val) = (max);		\
4680178676Ssam		needs_update = 1;		\
4681178676Ssam	}
4682198429Srpaulo#define dec(val, dec, min)			\
4683178676Ssam	if ((val) > (min)) {			\
4684178676Ssam		if ((val) > (min) + (dec))	\
4685178676Ssam			(val) -= (dec);		\
4686178676Ssam		else				\
4687178676Ssam			(val) = (min);		\
4688178676Ssam		needs_update = 1;		\
4689178676Ssam	}
4690178676Ssam
4691201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
4692178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4693178676Ssam	uint32_t val, rxena, fa;
4694178676Ssam	uint32_t energy[3], energy_min;
4695198439Srpaulo	uint8_t noise[3], noise_ref;
4696198429Srpaulo	int i, needs_update = 0;
4697178676Ssam
4698198429Srpaulo	/* Check that we've been enabled long enough. */
4699220726Sbschmidt	if ((rxena = le32toh(stats->general.load)) == 0)
4700178676Ssam		return;
4701178676Ssam
4702198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
4703178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
4704178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
4705220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4706178676Ssam
4707198429Srpaulo	/* Save counters values for next call. */
4708178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
4709178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
4710178676Ssam
4711178676Ssam	if (fa > 50 * rxena) {
4712198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4713178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4714178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
4715198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
4716198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
4717198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
4718198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
4719178676Ssam
4720178676Ssam	} else if (fa < 5 * rxena) {
4721198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4722178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4723178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
4724198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
4725198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
4726198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
4727198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
4728178676Ssam	}
4729178676Ssam
4730198429Srpaulo	/* Compute maximum noise among 3 receivers. */
4731178676Ssam	for (i = 0; i < 3; i++)
4732178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
4733198429Srpaulo	val = MAX(noise[0], noise[1]);
4734198429Srpaulo	val = MAX(noise[2], val);
4735198429Srpaulo	/* Insert it into our samples table. */
4736178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
4737178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
4738178676Ssam
4739198429Srpaulo	/* Compute maximum noise among last 20 samples. */
4740178676Ssam	noise_ref = calib->noise_samples[0];
4741178676Ssam	for (i = 1; i < 20; i++)
4742198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
4743178676Ssam
4744198429Srpaulo	/* Compute maximum energy among 3 receivers. */
4745178676Ssam	for (i = 0; i < 3; i++)
4746178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
4747198429Srpaulo	val = MIN(energy[0], energy[1]);
4748198429Srpaulo	val = MIN(energy[2], val);
4749198429Srpaulo	/* Insert it into our samples table. */
4750178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
4751178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
4752178676Ssam
4753198429Srpaulo	/* Compute minimum energy among last 10 samples. */
4754178676Ssam	energy_min = calib->energy_samples[0];
4755178676Ssam	for (i = 1; i < 10; i++)
4756198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
4757178676Ssam	energy_min += 6;
4758178676Ssam
4759198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
4760178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
4761178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
4762220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4763178676Ssam
4764198429Srpaulo	/* Save counters values for next call. */
4765178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
4766178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
4767178676Ssam
4768178676Ssam	if (fa > 50 * rxena) {
4769198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4770178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4771178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
4772178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
4773178676Ssam		calib->low_fa = 0;
4774178676Ssam
4775198429Srpaulo		if (calib->cck_x4 > 160) {
4776178676Ssam			calib->noise_ref = noise_ref;
4777178676Ssam			if (calib->energy_cck > 2)
4778198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
4779178676Ssam		}
4780198429Srpaulo		if (calib->cck_x4 < 160) {
4781198429Srpaulo			calib->cck_x4 = 161;
4782178676Ssam			needs_update = 1;
4783178676Ssam		} else
4784198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
4785178676Ssam
4786198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
4787178676Ssam
4788178676Ssam	} else if (fa < 5 * rxena) {
4789198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4790178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4791178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
4792178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
4793178676Ssam		calib->low_fa++;
4794178676Ssam
4795198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
4796198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
4797220726Sbschmidt		     calib->low_fa > 100)) {
4798198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
4799198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
4800198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
4801178676Ssam		}
4802178676Ssam	} else {
4803198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
4804178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4805178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
4806178676Ssam		calib->low_fa = 0;
4807178676Ssam		calib->noise_ref = noise_ref;
4808178676Ssam
4809178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
4810198429Srpaulo			/* Previous interval had many false alarms. */
4811198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
4812178676Ssam		}
4813178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
4814178676Ssam	}
4815178676Ssam
4816178676Ssam	if (needs_update)
4817178676Ssam		(void)iwn_send_sensitivity(sc);
4818198429Srpaulo#undef dec
4819198429Srpaulo#undef inc
4820178676Ssam}
4821178676Ssam
4822206477Sbschmidtstatic int
4823178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
4824178676Ssam{
4825178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4826220729Sbschmidt	struct iwn_enhanced_sensitivity_cmd cmd;
4827220729Sbschmidt	int len;
4828178676Ssam
4829178676Ssam	memset(&cmd, 0, sizeof cmd);
4830220729Sbschmidt	len = sizeof (struct iwn_sensitivity_cmd);
4831178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
4832198429Srpaulo	/* OFDM modulation. */
4833220726Sbschmidt	cmd.corr_ofdm_x1       = htole16(calib->ofdm_x1);
4834220726Sbschmidt	cmd.corr_ofdm_mrc_x1   = htole16(calib->ofdm_mrc_x1);
4835220726Sbschmidt	cmd.corr_ofdm_x4       = htole16(calib->ofdm_x4);
4836220726Sbschmidt	cmd.corr_ofdm_mrc_x4   = htole16(calib->ofdm_mrc_x4);
4837220726Sbschmidt	cmd.energy_ofdm        = htole16(sc->limits->energy_ofdm);
4838220726Sbschmidt	cmd.energy_ofdm_th     = htole16(62);
4839198429Srpaulo	/* CCK modulation. */
4840220726Sbschmidt	cmd.corr_cck_x4        = htole16(calib->cck_x4);
4841220726Sbschmidt	cmd.corr_cck_mrc_x4    = htole16(calib->cck_mrc_x4);
4842220726Sbschmidt	cmd.energy_cck         = htole16(calib->energy_cck);
4843198429Srpaulo	/* Barker modulation: use default values. */
4844220726Sbschmidt	cmd.corr_barker        = htole16(190);
4845220726Sbschmidt	cmd.corr_barker_mrc    = htole16(390);
4846178676Ssam
4847202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4848178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
4849198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
4850198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
4851198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
4852220729Sbschmidt
4853220729Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
4854220729Sbschmidt		goto send;
4855220729Sbschmidt	/* Enhanced sensitivity settings. */
4856220729Sbschmidt	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
4857220729Sbschmidt	cmd.ofdm_det_slope_mrc = htole16(668);
4858220729Sbschmidt	cmd.ofdm_det_icept_mrc = htole16(4);
4859220729Sbschmidt	cmd.ofdm_det_slope     = htole16(486);
4860220729Sbschmidt	cmd.ofdm_det_icept     = htole16(37);
4861220729Sbschmidt	cmd.cck_det_slope_mrc  = htole16(853);
4862220729Sbschmidt	cmd.cck_det_icept_mrc  = htole16(4);
4863220729Sbschmidt	cmd.cck_det_slope      = htole16(476);
4864220729Sbschmidt	cmd.cck_det_icept      = htole16(99);
4865220729Sbschmidtsend:
4866220729Sbschmidt	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
4867178676Ssam}
4868178676Ssam
4869198429Srpaulo/*
4870198429Srpaulo * Set STA mode power saving level (between 0 and 5).
4871198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
4872198429Srpaulo */
4873206477Sbschmidtstatic int
4874198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
4875198429Srpaulo{
4876220723Sbschmidt	struct iwn_pmgt_cmd cmd;
4877198429Srpaulo	const struct iwn_pmgt *pmgt;
4878198429Srpaulo	uint32_t max, skip_dtim;
4879220721Sbschmidt	uint32_t reg;
4880198429Srpaulo	int i;
4881198429Srpaulo
4882198429Srpaulo	/* Select which PS parameters to use. */
4883198429Srpaulo	if (dtim <= 2)
4884198429Srpaulo		pmgt = &iwn_pmgt[0][level];
4885198429Srpaulo	else if (dtim <= 10)
4886198429Srpaulo		pmgt = &iwn_pmgt[1][level];
4887198429Srpaulo	else
4888198429Srpaulo		pmgt = &iwn_pmgt[2][level];
4889198429Srpaulo
4890198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4891198429Srpaulo	if (level != 0)	/* not CAM */
4892198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
4893198429Srpaulo	if (level == 5)
4894198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
4895201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
4896220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
4897220721Sbschmidt	if (!(reg & 0x1))	/* L0s Entry disabled. */
4898198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
4899198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
4900198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
4901198429Srpaulo
4902198429Srpaulo	if (dtim == 0) {
4903198429Srpaulo		dtim = 1;
4904198429Srpaulo		skip_dtim = 0;
4905198429Srpaulo	} else
4906198429Srpaulo		skip_dtim = pmgt->skip_dtim;
4907198429Srpaulo	if (skip_dtim != 0) {
4908198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
4909198429Srpaulo		max = pmgt->intval[4];
4910198429Srpaulo		if (max == (uint32_t)-1)
4911198429Srpaulo			max = dtim * (skip_dtim + 1);
4912198429Srpaulo		else if (max > dtim)
4913198429Srpaulo			max = (max / dtim) * dtim;
4914198429Srpaulo	} else
4915198429Srpaulo		max = dtim;
4916198429Srpaulo	for (i = 0; i < 5; i++)
4917198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
4918198429Srpaulo
4919198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
4920198429Srpaulo	    level);
4921198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
4922198429Srpaulo}
4923198429Srpaulo
4924206477Sbschmidtstatic int
4925220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
4926220662Sbschmidt{
4927220662Sbschmidt	struct iwn_bluetooth cmd;
4928220662Sbschmidt
4929220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
4930220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
4931220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
4932220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
4933220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
4934220662Sbschmidt	    __func__);
4935220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
4936220662Sbschmidt}
4937220662Sbschmidt
4938220662Sbschmidtstatic int
4939220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc)
4940220891Sbschmidt{
4941220891Sbschmidt	static const uint32_t btcoex_3wire[12] = {
4942220891Sbschmidt		0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
4943220891Sbschmidt		0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
4944220891Sbschmidt		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
4945220891Sbschmidt	};
4946220891Sbschmidt	struct iwn6000_btcoex_config btconfig;
4947220891Sbschmidt	struct iwn_btcoex_priotable btprio;
4948220891Sbschmidt	struct iwn_btcoex_prot btprot;
4949220891Sbschmidt	int error, i;
4950220891Sbschmidt
4951220891Sbschmidt	memset(&btconfig, 0, sizeof btconfig);
4952220891Sbschmidt	btconfig.flags = 145;
4953220891Sbschmidt	btconfig.max_kill = 5;
4954220891Sbschmidt	btconfig.bt3_t7_timer = 1;
4955220891Sbschmidt	btconfig.kill_ack = htole32(0xffff0000);
4956220891Sbschmidt	btconfig.kill_cts = htole32(0xffff0000);
4957220891Sbschmidt	btconfig.sample_time = 2;
4958220891Sbschmidt	btconfig.bt3_t2_timer = 0xc;
4959220891Sbschmidt	for (i = 0; i < 12; i++)
4960220891Sbschmidt		btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
4961220891Sbschmidt	btconfig.valid = htole16(0xff);
4962220891Sbschmidt	btconfig.prio_boost = 0xf0;
4963220891Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET,
4964220891Sbschmidt	    "%s: configuring advanced bluetooth coexistence\n", __func__);
4965220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1);
4966220891Sbschmidt	if (error != 0)
4967220891Sbschmidt		return error;
4968220891Sbschmidt
4969220891Sbschmidt	memset(&btprio, 0, sizeof btprio);
4970220891Sbschmidt	btprio.calib_init1 = 0x6;
4971220891Sbschmidt	btprio.calib_init2 = 0x7;
4972220891Sbschmidt	btprio.calib_periodic_low1 = 0x2;
4973220891Sbschmidt	btprio.calib_periodic_low2 = 0x3;
4974220891Sbschmidt	btprio.calib_periodic_high1 = 0x4;
4975220891Sbschmidt	btprio.calib_periodic_high2 = 0x5;
4976220891Sbschmidt	btprio.dtim = 0x6;
4977220891Sbschmidt	btprio.scan52 = 0x8;
4978220891Sbschmidt	btprio.scan24 = 0xa;
4979220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
4980220891Sbschmidt	    1);
4981220891Sbschmidt	if (error != 0)
4982220891Sbschmidt		return error;
4983220891Sbschmidt
4984220891Sbschmidt	/* Force BT state machine change. */
4985220891Sbschmidt	memset(&btprot, 0, sizeof btprio);
4986220891Sbschmidt	btprot.open = 1;
4987220891Sbschmidt	btprot.type = 1;
4988220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
4989220891Sbschmidt	if (error != 0)
4990220891Sbschmidt		return error;
4991220891Sbschmidt	btprot.open = 0;
4992220891Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
4993220891Sbschmidt}
4994220891Sbschmidt
4995220891Sbschmidtstatic int
4996198429Srpauloiwn_config(struct iwn_softc *sc)
4997198429Srpaulo{
4998220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4999198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5000198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5001201209Srpaulo	uint32_t txmask;
5002220723Sbschmidt	uint16_t rxchain;
5003198429Srpaulo	int error;
5004198429Srpaulo
5005220676Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
5006220676Sbschmidt		/* Set radio temperature sensor offset. */
5007220676Sbschmidt		error = iwn5000_temp_offset_calib(sc);
5008220676Sbschmidt		if (error != 0) {
5009220676Sbschmidt			device_printf(sc->sc_dev,
5010220676Sbschmidt			    "%s: could not set temperature offset\n", __func__);
5011220676Sbschmidt			return error;
5012220676Sbschmidt		}
5013220676Sbschmidt	}
5014220676Sbschmidt
5015220725Sbschmidt	/* Configure valid TX chains for >=5000 Series. */
5016201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
5017201209Srpaulo		txmask = htole32(sc->txchainmask);
5018201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
5019201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
5020201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
5021201209Srpaulo		    sizeof txmask, 0);
5022201209Srpaulo		if (error != 0) {
5023201209Srpaulo			device_printf(sc->sc_dev,
5024201209Srpaulo			    "%s: could not configure valid TX chains, "
5025201209Srpaulo			    "error %d\n", __func__, error);
5026201209Srpaulo			return error;
5027201209Srpaulo		}
5028198429Srpaulo	}
5029198429Srpaulo
5030198429Srpaulo	/* Configure bluetooth coexistence. */
5031220891Sbschmidt	if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX)
5032220891Sbschmidt		error = iwn_send_advanced_btcoex(sc);
5033220891Sbschmidt	else
5034220891Sbschmidt		error = iwn_send_btcoex(sc);
5035198429Srpaulo	if (error != 0) {
5036198429Srpaulo		device_printf(sc->sc_dev,
5037198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
5038198429Srpaulo		    __func__, error);
5039198429Srpaulo		return error;
5040198429Srpaulo	}
5041198429Srpaulo
5042201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
5043198429Srpaulo	memset(&sc->rxon, 0, sizeof (struct iwn_rxon));
5044198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp));
5045198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp));
5046198429Srpaulo	sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
5047198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5048198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
5049198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5050198429Srpaulo	switch (ic->ic_opmode) {
5051198429Srpaulo	case IEEE80211_M_STA:
5052198429Srpaulo		sc->rxon.mode = IWN_MODE_STA;
5053198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
5054198429Srpaulo		break;
5055198429Srpaulo	case IEEE80211_M_MONITOR:
5056198429Srpaulo		sc->rxon.mode = IWN_MODE_MONITOR;
5057198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST |
5058198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
5059198429Srpaulo		break;
5060198429Srpaulo	default:
5061198429Srpaulo		/* Should not get there. */
5062198429Srpaulo		break;
5063198429Srpaulo	}
5064198429Srpaulo	sc->rxon.cck_mask  = 0x0f;	/* not yet negotiated */
5065198429Srpaulo	sc->rxon.ofdm_mask = 0xff;	/* not yet negotiated */
5066198429Srpaulo	sc->rxon.ht_single_mask = 0xff;
5067198429Srpaulo	sc->rxon.ht_dual_mask = 0xff;
5068201209Srpaulo	sc->rxon.ht_triple_mask = 0xff;
5069201209Srpaulo	rxchain =
5070201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5071201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
5072201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
5073198429Srpaulo	sc->rxon.rxchain = htole16(rxchain);
5074198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
5075220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0);
5076198429Srpaulo	if (error != 0) {
5077220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed\n",
5078220726Sbschmidt		    __func__);
5079198429Srpaulo		return error;
5080198429Srpaulo	}
5081198429Srpaulo
5082220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
5083220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
5084220726Sbschmidt		    __func__);
5085201209Srpaulo		return error;
5086201209Srpaulo	}
5087201209Srpaulo
5088198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5089220728Sbschmidt	if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
5090220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not set TX power\n",
5091220726Sbschmidt		    __func__);
5092198429Srpaulo		return error;
5093198429Srpaulo	}
5094198429Srpaulo
5095220726Sbschmidt	if ((error = iwn_set_critical_temp(sc)) != 0) {
5096198429Srpaulo		device_printf(sc->sc_dev,
5097220724Sbschmidt		    "%s: could not set critical temperature\n", __func__);
5098198429Srpaulo		return error;
5099198429Srpaulo	}
5100198429Srpaulo
5101201209Srpaulo	/* Set power saving level to CAM during initialization. */
5102220726Sbschmidt	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
5103198429Srpaulo		device_printf(sc->sc_dev,
5104201209Srpaulo		    "%s: could not set power saving level\n", __func__);
5105198429Srpaulo		return error;
5106198429Srpaulo	}
5107198429Srpaulo	return 0;
5108198429Srpaulo}
5109198429Srpaulo
5110220634Sbschmidt/*
5111220634Sbschmidt * Add an ssid element to a frame.
5112220634Sbschmidt */
5113220634Sbschmidtstatic uint8_t *
5114220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
5115220634Sbschmidt{
5116220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
5117220634Sbschmidt	*frm++ = len;
5118220634Sbschmidt	memcpy(frm, ssid, len);
5119220634Sbschmidt	return frm + len;
5120220634Sbschmidt}
5121220634Sbschmidt
5122206477Sbschmidtstatic int
5123198429Srpauloiwn_scan(struct iwn_softc *sc)
5124198429Srpaulo{
5125198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5126198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5127198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
5128221641Sbschmidt	struct ieee80211_node *ni = ss->ss_vap->iv_bss;
5129198429Srpaulo	struct iwn_scan_hdr *hdr;
5130198429Srpaulo	struct iwn_cmd_data *tx;
5131198429Srpaulo	struct iwn_scan_essid *essid;
5132198429Srpaulo	struct iwn_scan_chan *chan;
5133198429Srpaulo	struct ieee80211_frame *wh;
5134198429Srpaulo	struct ieee80211_rateset *rs;
5135198429Srpaulo	struct ieee80211_channel *c;
5136220726Sbschmidt	uint8_t *buf, *frm;
5137220723Sbschmidt	uint16_t rxchain;
5138220726Sbschmidt	uint8_t txant;
5139220634Sbschmidt	int buflen, error;
5140198429Srpaulo
5141198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
5142198429Srpaulo	if (buf == NULL) {
5143198429Srpaulo		device_printf(sc->sc_dev,
5144198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
5145198429Srpaulo		    __func__);
5146198429Srpaulo		return ENOMEM;
5147198429Srpaulo	}
5148198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
5149198429Srpaulo	/*
5150198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
5151198429Srpaulo	 * after sending the probe request.
5152198429Srpaulo	 */
5153198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
5154198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
5155198429Srpaulo
5156198429Srpaulo	/* Select antennas for scanning. */
5157201209Srpaulo	rxchain =
5158201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5159201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
5160201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
5161198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
5162198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
5163198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
5164201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC);
5165198429Srpaulo	} else	/* Use all available RX antennas. */
5166201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
5167198429Srpaulo	hdr->rxchain = htole16(rxchain);
5168198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
5169198429Srpaulo
5170198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
5171198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
5172220728Sbschmidt	tx->id = sc->broadcast_id;
5173198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
5174198429Srpaulo
5175198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
5176198429Srpaulo		/* Send probe requests at 6Mbps. */
5177221648Sbschmidt		tx->rate = htole32(0xd);
5178201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
5179198429Srpaulo	} else {
5180198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
5181198429Srpaulo		/* Send probe requests at 1Mbps. */
5182221648Sbschmidt		tx->rate = htole32(10 | IWN_RFLAG_CCK);
5183201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
5184198429Srpaulo	}
5185198429Srpaulo	/* Use the first valid TX antenna. */
5186201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
5187221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
5188198429Srpaulo
5189198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
5190198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
5191198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
5192198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
5193198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
5194198429Srpaulo	}
5195198429Srpaulo	/*
5196198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
5197198429Srpaulo	 * copy & paste of what is done in net80211.
5198198429Srpaulo	 */
5199198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
5200198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
5201198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
5202198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
5203198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
5204198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
5205198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
5206198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
5207198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
5208198429Srpaulo
5209198429Srpaulo	frm = (uint8_t *)(wh + 1);
5210220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
5211220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
5212220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
5213220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
5214221641Sbschmidt	if (ic->ic_htcaps & IEEE80211_HTC_HT)
5215221641Sbschmidt		frm = ieee80211_add_htcap(frm, ni);
5216198429Srpaulo
5217198429Srpaulo	/* Set length of probe request. */
5218198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
5219198429Srpaulo
5220198429Srpaulo	c = ic->ic_curchan;
5221198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
5222201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5223198429Srpaulo	chan->flags = 0;
5224198429Srpaulo	if (ss->ss_nssid > 0)
5225198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5226198429Srpaulo	chan->dsp_gain = 0x6e;
5227201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
5228201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5229198429Srpaulo		chan->rf_gain = 0x3b;
5230198429Srpaulo		chan->active  = htole16(24);
5231198429Srpaulo		chan->passive = htole16(110);
5232201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5233201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
5234201209Srpaulo		chan->rf_gain = 0x3b;
5235201209Srpaulo		chan->active  = htole16(24);
5236201209Srpaulo		if (sc->rxon.associd)
5237201209Srpaulo			chan->passive = htole16(78);
5238201209Srpaulo		else
5239201209Srpaulo			chan->passive = htole16(110);
5240207709Sbschmidt		hdr->crc_threshold = 0xffff;
5241201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5242201209Srpaulo		chan->rf_gain = 0x28;
5243201209Srpaulo		chan->active  = htole16(36);
5244201209Srpaulo		chan->passive = htole16(120);
5245201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5246198429Srpaulo	} else {
5247198429Srpaulo		chan->rf_gain = 0x28;
5248198429Srpaulo		chan->active  = htole16(36);
5249201209Srpaulo		if (sc->rxon.associd)
5250201209Srpaulo			chan->passive = htole16(88);
5251201209Srpaulo		else
5252201209Srpaulo			chan->passive = htole16(120);
5253207709Sbschmidt		hdr->crc_threshold = 0xffff;
5254198429Srpaulo	}
5255198429Srpaulo
5256201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
5257201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
5258198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
5259198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
5260198429Srpaulo	    chan->active, chan->passive);
5261198429Srpaulo
5262201209Srpaulo	hdr->nchan++;
5263201209Srpaulo	chan++;
5264198429Srpaulo	buflen = (uint8_t *)chan - buf;
5265198429Srpaulo	hdr->len = htole16(buflen);
5266198429Srpaulo
5267198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
5268198429Srpaulo	    hdr->nchan);
5269198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5270198429Srpaulo	free(buf, M_DEVBUF);
5271198429Srpaulo	return error;
5272198429Srpaulo}
5273198429Srpaulo
5274206477Sbschmidtstatic int
5275191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
5276178676Ssam{
5277220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5278178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5279178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5280178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5281178676Ssam	int error;
5282178676Ssam
5283201209Srpaulo	/* Update adapter configuration. */
5284198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5285220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5286198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5287178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5288198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5289198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5290198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5291198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5292198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5293178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5294198429Srpaulo		sc->rxon.cck_mask  = 0;
5295198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5296178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5297198429Srpaulo		sc->rxon.cck_mask  = 0x03;
5298198429Srpaulo		sc->rxon.ofdm_mask = 0;
5299178676Ssam	} else {
5300220725Sbschmidt		/* Assume 802.11b/g. */
5301198429Srpaulo		sc->rxon.cck_mask  = 0x0f;
5302198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5303178676Ssam	}
5304220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
5305220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask,
5306220724Sbschmidt	    sc->rxon.ofdm_mask);
5307220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5308178676Ssam	if (error != 0) {
5309220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
5310220726Sbschmidt		    __func__, error);
5311178676Ssam		return error;
5312178676Ssam	}
5313178676Ssam
5314198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5315220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5316178676Ssam		device_printf(sc->sc_dev,
5317220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5318178676Ssam		return error;
5319178676Ssam	}
5320178676Ssam	/*
5321201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
5322178676Ssam	 * add the broadcast node again.
5323178676Ssam	 */
5324220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5325178676Ssam		device_printf(sc->sc_dev,
5326220726Sbschmidt		    "%s: could not add broadcast node, error %d\n", __func__,
5327220726Sbschmidt		    error);
5328178676Ssam		return error;
5329178676Ssam	}
5330178676Ssam	return 0;
5331178676Ssam}
5332178676Ssam
5333206477Sbschmidtstatic int
5334191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
5335178676Ssam{
5336220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5337178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5338178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5339178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5340178676Ssam	struct iwn_node_info node;
5341221653Sbschmidt	uint32_t htflags = 0;
5342201209Srpaulo	int error;
5343178676Ssam
5344178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
5345201209Srpaulo		/* Link LED blinks while monitoring. */
5346220674Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
5347178676Ssam		return 0;
5348178676Ssam	}
5349220726Sbschmidt	if ((error = iwn_set_timing(sc, ni)) != 0) {
5350198429Srpaulo		device_printf(sc->sc_dev,
5351198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
5352198429Srpaulo		return error;
5353198429Srpaulo	}
5354178676Ssam
5355201209Srpaulo	/* Update adapter configuration. */
5356201209Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5357198429Srpaulo	sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd));
5358220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5359220636Sbschmidt	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5360201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5361201209Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5362178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5363198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5364178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5365198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5366201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5367201209Srpaulo		sc->rxon.cck_mask  = 0;
5368201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5369201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5370201209Srpaulo		sc->rxon.cck_mask  = 0x03;
5371201209Srpaulo		sc->rxon.ofdm_mask = 0;
5372201209Srpaulo	} else {
5373220725Sbschmidt		/* Assume 802.11b/g. */
5374201209Srpaulo		sc->rxon.cck_mask  = 0x0f;
5375201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5376201209Srpaulo	}
5377178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5378221653Sbschmidt		htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
5379221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
5380221653Sbschmidt			switch (ic->ic_curhtprotmode) {
5381221653Sbschmidt			case IEEE80211_HTINFO_OPMODE_HT20PR:
5382221653Sbschmidt				htflags |= IWN_RXON_HT_MODEPURE40;
5383221653Sbschmidt				break;
5384221653Sbschmidt			default:
5385221653Sbschmidt				htflags |= IWN_RXON_HT_MODEMIXED;
5386221653Sbschmidt				break;
5387221653Sbschmidt			}
5388221653Sbschmidt		}
5389221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
5390221653Sbschmidt			htflags |= IWN_RXON_HT_HT40MINUS;
5391221653Sbschmidt	}
5392221653Sbschmidt	sc->rxon.flags |= htole32(htflags);
5393198429Srpaulo	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
5394220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
5395220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags);
5396220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5397178676Ssam	if (error != 0) {
5398178676Ssam		device_printf(sc->sc_dev,
5399220726Sbschmidt		    "%s: could not update configuration, error %d\n", __func__,
5400220726Sbschmidt		    error);
5401178676Ssam		return error;
5402178676Ssam	}
5403178676Ssam
5404198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5405220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5406178676Ssam		device_printf(sc->sc_dev,
5407220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5408178676Ssam		return error;
5409178676Ssam	}
5410178676Ssam
5411220715Sbschmidt	/* Fake a join to initialize the TX rate. */
5412220715Sbschmidt	((struct iwn_node *)ni)->id = IWN_ID_BSS;
5413220715Sbschmidt	iwn_newassoc(ni, 1);
5414220715Sbschmidt
5415198429Srpaulo	/* Add BSS node. */
5416178676Ssam	memset(&node, 0, sizeof node);
5417178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
5418178676Ssam	node.id = IWN_ID_BSS;
5419221653Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5420221653Sbschmidt		switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
5421221653Sbschmidt		case IEEE80211_HTCAP_SMPS_ENA:
5422221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
5423221653Sbschmidt			break;
5424221653Sbschmidt		case IEEE80211_HTCAP_SMPS_DYNAMIC:
5425221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
5426221653Sbschmidt			break;
5427221653Sbschmidt		}
5428221653Sbschmidt		node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
5429221653Sbschmidt		    IWN_AMDPU_DENSITY(5));	/* 4us */
5430221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
5431221653Sbschmidt			node.htflags |= htole32(IWN_NODE_HT40);
5432221653Sbschmidt	}
5433220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
5434220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5435178676Ssam	if (error != 0) {
5436220724Sbschmidt		device_printf(sc->sc_dev,
5437220724Sbschmidt		    "%s: could not add BSS node, error %d\n", __func__, error);
5438178676Ssam		return error;
5439178676Ssam	}
5440220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
5441220724Sbschmidt	    __func__, node.id);
5442220726Sbschmidt	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
5443178676Ssam		device_printf(sc->sc_dev,
5444220724Sbschmidt		    "%s: could not setup link quality for node %d, error %d\n",
5445178676Ssam		    __func__, node.id, error);
5446178676Ssam		return error;
5447178676Ssam	}
5448178676Ssam
5449220726Sbschmidt	if ((error = iwn_init_sensitivity(sc)) != 0) {
5450178676Ssam		device_printf(sc->sc_dev,
5451220726Sbschmidt		    "%s: could not set sensitivity, error %d\n", __func__,
5452220726Sbschmidt		    error);
5453178676Ssam		return error;
5454178676Ssam	}
5455198429Srpaulo	/* Start periodic calibration timer. */
5456178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
5457220667Sbschmidt	sc->calib_cnt = 0;
5458220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
5459220667Sbschmidt	    sc);
5460178676Ssam
5461198429Srpaulo	/* Link LED always on while associated. */
5462178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5463178676Ssam	return 0;
5464178676Ssam}
5465178676Ssam
5466178676Ssam/*
5467201209Srpaulo * This function is called by upper layer when an ADDBA request is received
5468201209Srpaulo * from another STA and before the ADDBA response is sent.
5469201209Srpaulo */
5470206477Sbschmidtstatic int
5471221650Sbschmidtiwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
5472221650Sbschmidt    int baparamset, int batimeout, int baseqctl)
5473201209Srpaulo{
5474221650Sbschmidt#define MS(_v, _f)	(((_v) & _f) >> _f##_S)
5475221650Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5476220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5477201209Srpaulo	struct iwn_node *wn = (void *)ni;
5478201209Srpaulo	struct iwn_node_info node;
5479221650Sbschmidt	uint16_t ssn;
5480221650Sbschmidt	uint8_t tid;
5481221650Sbschmidt	int error;
5482201209Srpaulo
5483221650Sbschmidt	tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID);
5484221650Sbschmidt	ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START);
5485221650Sbschmidt
5486201209Srpaulo	memset(&node, 0, sizeof node);
5487201209Srpaulo	node.id = wn->id;
5488201209Srpaulo	node.control = IWN_NODE_UPDATE;
5489201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
5490201209Srpaulo	node.addba_tid = tid;
5491221650Sbschmidt	node.addba_ssn = htole16(ssn);
5492201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
5493221650Sbschmidt	    wn->id, tid, ssn);
5494221650Sbschmidt	error = ops->add_node(sc, &node, 1);
5495221650Sbschmidt	if (error != 0)
5496221650Sbschmidt		return error;
5497221650Sbschmidt	return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
5498221650Sbschmidt#undef MS
5499201209Srpaulo}
5500201209Srpaulo
5501201209Srpaulo/*
5502201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
5503220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame).
5504201209Srpaulo */
5505206477Sbschmidtstatic void
5506221650Sbschmidtiwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
5507201209Srpaulo{
5508221650Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
5509221650Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
5510220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5511201209Srpaulo	struct iwn_node *wn = (void *)ni;
5512201209Srpaulo	struct iwn_node_info node;
5513221650Sbschmidt	uint8_t tid;
5514201209Srpaulo
5515221650Sbschmidt	/* XXX: tid as an argument */
5516221650Sbschmidt	for (tid = 0; tid < WME_NUM_TID; tid++) {
5517221650Sbschmidt		if (&ni->ni_rx_ampdu[tid] == rap)
5518221650Sbschmidt			break;
5519221650Sbschmidt	}
5520221650Sbschmidt
5521201209Srpaulo	memset(&node, 0, sizeof node);
5522201209Srpaulo	node.id = wn->id;
5523201209Srpaulo	node.control = IWN_NODE_UPDATE;
5524201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5525201209Srpaulo	node.delba_tid = tid;
5526201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5527220728Sbschmidt	(void)ops->add_node(sc, &node, 1);
5528221650Sbschmidt	sc->sc_ampdu_rx_stop(ni, rap);
5529201209Srpaulo}
5530201209Srpaulo
5531221651Sbschmidtstatic int
5532221651Sbschmidtiwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5533221651Sbschmidt    int dialogtoken, int baparamset, int batimeout)
5534221651Sbschmidt{
5535221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5536221651Sbschmidt	int qid;
5537221651Sbschmidt
5538221651Sbschmidt	for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
5539221651Sbschmidt		if (sc->qid2tap[qid] == NULL)
5540221651Sbschmidt			break;
5541221651Sbschmidt	}
5542221651Sbschmidt	if (qid == sc->ntxqs) {
5543221651Sbschmidt		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
5544221651Sbschmidt		    __func__);
5545221651Sbschmidt		return 0;
5546221651Sbschmidt	}
5547221651Sbschmidt	tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
5548221651Sbschmidt	if (tap->txa_private == NULL) {
5549221651Sbschmidt		device_printf(sc->sc_dev,
5550221651Sbschmidt		    "%s: failed to alloc TX aggregation structure\n", __func__);
5551221651Sbschmidt		return 0;
5552221651Sbschmidt	}
5553221651Sbschmidt	sc->qid2tap[qid] = tap;
5554221651Sbschmidt	*(int *)tap->txa_private = qid;
5555221651Sbschmidt	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
5556221651Sbschmidt	    batimeout);
5557221651Sbschmidt}
5558221651Sbschmidt
5559221651Sbschmidtstatic int
5560221651Sbschmidtiwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5561221651Sbschmidt    int code, int baparamset, int batimeout)
5562221651Sbschmidt{
5563221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5564221651Sbschmidt	int qid = *(int *)tap->txa_private;
5565221651Sbschmidt	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
5566221651Sbschmidt	int ret;
5567221651Sbschmidt
5568221651Sbschmidt	if (code == IEEE80211_STATUS_SUCCESS) {
5569221651Sbschmidt		ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
5570221651Sbschmidt		ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
5571221651Sbschmidt		if (ret != 1)
5572221651Sbschmidt			return ret;
5573221651Sbschmidt	} else {
5574221651Sbschmidt		sc->qid2tap[qid] = NULL;
5575221651Sbschmidt		free(tap->txa_private, M_DEVBUF);
5576221651Sbschmidt		tap->txa_private = NULL;
5577221651Sbschmidt	}
5578221651Sbschmidt	return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
5579221651Sbschmidt}
5580221651Sbschmidt
5581201209Srpaulo/*
5582201209Srpaulo * This function is called by upper layer when an ADDBA response is received
5583201209Srpaulo * from another STA.
5584201209Srpaulo */
5585206477Sbschmidtstatic int
5586201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
5587201209Srpaulo    uint8_t tid)
5588201209Srpaulo{
5589221651Sbschmidt	struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)];
5590221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5591220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5592201209Srpaulo	struct iwn_node *wn = (void *)ni;
5593201209Srpaulo	struct iwn_node_info node;
5594221651Sbschmidt	int error, qid;
5595201209Srpaulo
5596201209Srpaulo	/* Enable TX for the specified RA/TID. */
5597201209Srpaulo	wn->disable_tid &= ~(1 << tid);
5598201209Srpaulo	memset(&node, 0, sizeof node);
5599201209Srpaulo	node.id = wn->id;
5600201209Srpaulo	node.control = IWN_NODE_UPDATE;
5601201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
5602201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
5603220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5604201209Srpaulo	if (error != 0)
5605221651Sbschmidt		return 0;
5606201209Srpaulo
5607201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5608221651Sbschmidt		return 0;
5609221651Sbschmidt	qid = *(int *)tap->txa_private;
5610221651Sbschmidt	ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
5611201209Srpaulo	iwn_nic_unlock(sc);
5612221651Sbschmidt
5613221651Sbschmidt	iwn_set_link_quality(sc, ni);
5614221651Sbschmidt	return 1;
5615201209Srpaulo}
5616201209Srpaulo
5617206477Sbschmidtstatic void
5618221651Sbschmidtiwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
5619201209Srpaulo{
5620221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5621220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5622221651Sbschmidt	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
5623221651Sbschmidt	int qid;
5624201209Srpaulo
5625221651Sbschmidt	if (tap->txa_private == NULL)
5626221651Sbschmidt		return;
5627221651Sbschmidt
5628221651Sbschmidt	qid = *(int *)tap->txa_private;
5629220726Sbschmidt	if (iwn_nic_lock(sc) != 0)
5630201209Srpaulo		return;
5631221651Sbschmidt	ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
5632201209Srpaulo	iwn_nic_unlock(sc);
5633221651Sbschmidt	sc->qid2tap[qid] = NULL;
5634221651Sbschmidt	free(tap->txa_private, M_DEVBUF);
5635221651Sbschmidt	tap->txa_private = NULL;
5636201209Srpaulo}
5637201209Srpaulo
5638206477Sbschmidtstatic void
5639201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5640221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
5641201209Srpaulo{
5642201209Srpaulo	struct iwn_node *wn = (void *)ni;
5643201209Srpaulo
5644201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5645201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5646201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5647201209Srpaulo
5648201209Srpaulo	/* Assign RA/TID translation to the queue. */
5649201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
5650201209Srpaulo	    wn->id << 4 | tid);
5651201209Srpaulo
5652201209Srpaulo	/* Enable chain-building mode for the queue. */
5653201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
5654201209Srpaulo
5655201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5656221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
5657201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5658201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5659201209Srpaulo
5660201209Srpaulo	/* Set scheduler window size. */
5661201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
5662201209Srpaulo	    IWN_SCHED_WINSZ);
5663201209Srpaulo	/* Set scheduler frame limit. */
5664201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5665201209Srpaulo	    IWN_SCHED_LIMIT << 16);
5666201209Srpaulo
5667201209Srpaulo	/* Enable interrupts for the queue. */
5668201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5669201209Srpaulo
5670201209Srpaulo	/* Mark the queue as active. */
5671201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5672201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
5673201209Srpaulo	    iwn_tid2fifo[tid] << 1);
5674201209Srpaulo}
5675201209Srpaulo
5676206477Sbschmidtstatic void
5677221651Sbschmidtiwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
5678201209Srpaulo{
5679201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5680201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5681201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5682201209Srpaulo
5683201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5684201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5685201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5686201209Srpaulo
5687201209Srpaulo	/* Disable interrupts for the queue. */
5688201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5689201209Srpaulo
5690201209Srpaulo	/* Mark the queue as inactive. */
5691201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5692201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
5693201209Srpaulo}
5694201209Srpaulo
5695206477Sbschmidtstatic void
5696201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5697221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
5698201209Srpaulo{
5699201209Srpaulo	struct iwn_node *wn = (void *)ni;
5700201209Srpaulo
5701201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5702201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5703201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5704201209Srpaulo
5705201209Srpaulo	/* Assign RA/TID translation to the queue. */
5706201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
5707201209Srpaulo	    wn->id << 4 | tid);
5708201209Srpaulo
5709201209Srpaulo	/* Enable chain-building mode for the queue. */
5710201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
5711201209Srpaulo
5712201209Srpaulo	/* Enable aggregation for the queue. */
5713201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5714201209Srpaulo
5715201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5716221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
5717201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5718201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5719201209Srpaulo
5720201209Srpaulo	/* Set scheduler window size and frame limit. */
5721201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5722201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5723201209Srpaulo
5724201209Srpaulo	/* Enable interrupts for the queue. */
5725201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5726201209Srpaulo
5727201209Srpaulo	/* Mark the queue as active. */
5728201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5729201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
5730201209Srpaulo}
5731201209Srpaulo
5732206477Sbschmidtstatic void
5733221651Sbschmidtiwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
5734201209Srpaulo{
5735201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5736201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5737201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5738201209Srpaulo
5739201209Srpaulo	/* Disable aggregation for the queue. */
5740201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5741201209Srpaulo
5742201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5743201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5744201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5745201209Srpaulo
5746201209Srpaulo	/* Disable interrupts for the queue. */
5747201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5748201209Srpaulo
5749201209Srpaulo	/* Mark the queue as inactive. */
5750201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5751201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
5752201209Srpaulo}
5753201209Srpaulo
5754201209Srpaulo/*
5755220674Sbschmidt * Query calibration tables from the initialization firmware.  We do this
5756220674Sbschmidt * only once at first boot.  Called from a process context.
5757212853Sbschmidt */
5758212853Sbschmidtstatic int
5759220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc)
5760212853Sbschmidt{
5761198429Srpaulo	struct iwn5000_calib_config cmd;
5762198429Srpaulo	int error;
5763178676Ssam
5764198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5765220674Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5766220674Sbschmidt	cmd.ucode.once.start  = 0xffffffff;
5767220674Sbschmidt	cmd.ucode.once.send   = 0xffffffff;
5768220674Sbschmidt	cmd.ucode.flags       = 0xffffffff;
5769220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
5770220674Sbschmidt	    __func__);
5771198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
5772198429Srpaulo	if (error != 0)
5773198429Srpaulo		return error;
5774178676Ssam
5775198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
5776201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
5777220674Sbschmidt		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
5778201209Srpaulo	return error;
5779198429Srpaulo}
5780198429Srpaulo
5781198429Srpaulo/*
5782220674Sbschmidt * Send calibration results to the runtime firmware.  These results were
5783220674Sbschmidt * obtained on first boot from the initialization firmware.
5784198429Srpaulo */
5785212854Sbschmidtstatic int
5786220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc)
5787198429Srpaulo{
5788220674Sbschmidt	int idx, error;
5789198429Srpaulo
5790220674Sbschmidt	for (idx = 0; idx < 5; idx++) {
5791220674Sbschmidt		if (sc->calibcmd[idx].buf == NULL)
5792220674Sbschmidt			continue;	/* No results available. */
5793198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5794220674Sbschmidt		    "send calibration result idx=%d len=%d\n", idx,
5795220674Sbschmidt		    sc->calibcmd[idx].len);
5796220674Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
5797220674Sbschmidt		    sc->calibcmd[idx].len, 0);
5798220674Sbschmidt		if (error != 0) {
5799220674Sbschmidt			device_printf(sc->sc_dev,
5800220674Sbschmidt			    "%s: could not send calibration result, error %d\n",
5801220674Sbschmidt			    __func__, error);
5802220674Sbschmidt			return error;
5803220674Sbschmidt		}
5804178676Ssam	}
5805220674Sbschmidt	return 0;
5806198429Srpaulo}
5807178676Ssam
5808206477Sbschmidtstatic int
5809201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
5810201209Srpaulo{
5811201209Srpaulo	struct iwn5000_wimax_coex wimax;
5812201209Srpaulo
5813201209Srpaulo#ifdef notyet
5814201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5815201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
5816201209Srpaulo		wimax.flags =
5817201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
5818201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
5819201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
5820201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
5821201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
5822201209Srpaulo		    sizeof iwn6050_wimax_events);
5823201209Srpaulo	} else
5824201209Srpaulo#endif
5825201209Srpaulo	{
5826201209Srpaulo		/* Disable WiMAX coexistence. */
5827201209Srpaulo		wimax.flags = 0;
5828201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
5829201209Srpaulo	}
5830201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
5831201209Srpaulo	    __func__);
5832201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
5833201209Srpaulo}
5834201209Srpaulo
5835220674Sbschmidtstatic int
5836220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc)
5837220674Sbschmidt{
5838220674Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
5839220674Sbschmidt
5840220674Sbschmidt	memset(&cmd, 0, sizeof cmd);
5841220674Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
5842220674Sbschmidt	cmd.ngroups = 1;
5843220674Sbschmidt	cmd.isvalid = 1;
5844220674Sbschmidt	cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
5845220674Sbschmidt	cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
5846220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
5847220674Sbschmidt	    cmd.cap_pin[0], cmd.cap_pin[1]);
5848220674Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5849220674Sbschmidt}
5850220674Sbschmidt
5851220676Sbschmidtstatic int
5852220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc)
5853220676Sbschmidt{
5854220676Sbschmidt	struct iwn5000_phy_calib_temp_offset cmd;
5855220676Sbschmidt
5856220676Sbschmidt	memset(&cmd, 0, sizeof cmd);
5857220676Sbschmidt	cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
5858220676Sbschmidt	cmd.ngroups = 1;
5859220676Sbschmidt	cmd.isvalid = 1;
5860220676Sbschmidt	if (sc->eeprom_temp != 0)
5861220676Sbschmidt		cmd.offset = htole16(sc->eeprom_temp);
5862220676Sbschmidt	else
5863220676Sbschmidt		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
5864220676Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
5865220676Sbschmidt	    le16toh(cmd.offset));
5866220676Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5867220676Sbschmidt}
5868220676Sbschmidt
5869198429Srpaulo/*
5870198429Srpaulo * This function is called after the runtime firmware notifies us of its
5871220725Sbschmidt * readiness (called in a process context).
5872198429Srpaulo */
5873206477Sbschmidtstatic int
5874198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
5875198429Srpaulo{
5876198429Srpaulo	int error, qid;
5877178676Ssam
5878198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5879198429Srpaulo		return error;
5880178676Ssam
5881201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5882198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5883198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
5884201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
5885178676Ssam
5886220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
5887198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5888178676Ssam
5889198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5890178676Ssam
5891198429Srpaulo	/* Disable chain mode for all our 16 queues. */
5892198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
5893198429Srpaulo
5894198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
5895198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
5896198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5897198429Srpaulo
5898198429Srpaulo		/* Set scheduler window size. */
5899198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5900198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
5901198429Srpaulo		/* Set scheduler frame limit. */
5902198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5903198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5904198429Srpaulo		    IWN_SCHED_LIMIT << 16);
5905178676Ssam	}
5906178676Ssam
5907198429Srpaulo	/* Enable interrupts for all our 16 queues. */
5908198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
5909198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5910198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
5911178676Ssam
5912198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5913198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5914198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
5915198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5916198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
5917198429Srpaulo	}
5918198429Srpaulo	iwn_nic_unlock(sc);
5919198429Srpaulo	return 0;
5920198429Srpaulo}
5921178676Ssam
5922198429Srpaulo/*
5923198429Srpaulo * This function is called after the initialization or runtime firmware
5924220725Sbschmidt * notifies us of its readiness (called in a process context).
5925198429Srpaulo */
5926206477Sbschmidtstatic int
5927198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
5928198429Srpaulo{
5929198429Srpaulo	int error, qid;
5930178676Ssam
5931201209Srpaulo	/* Switch to using ICT interrupt mode. */
5932201209Srpaulo	iwn5000_ict_reset(sc);
5933201209Srpaulo
5934220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5935198429Srpaulo		return error;
5936178676Ssam
5937201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5938198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5939198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
5940201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
5941178676Ssam
5942220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
5943198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5944178676Ssam
5945198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5946178676Ssam
5947201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
5948201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
5949198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
5950178676Ssam
5951198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
5952198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
5953198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5954198429Srpaulo
5955198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5956198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
5957198429Srpaulo		/* Set scheduler window size and frame limit. */
5958198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5959198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5960198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5961178676Ssam	}
5962178676Ssam
5963198429Srpaulo	/* Enable interrupts for all our 20 queues. */
5964198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
5965198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5966198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
5967178676Ssam
5968198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5969198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5970198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
5971198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5972198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
5973198429Srpaulo	}
5974198429Srpaulo	iwn_nic_unlock(sc);
5975178676Ssam
5976201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
5977201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
5978178676Ssam	if (error != 0) {
5979178676Ssam		device_printf(sc->sc_dev,
5980198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
5981178676Ssam		    __func__, error);
5982178676Ssam		return error;
5983178676Ssam	}
5984220674Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
5985220674Sbschmidt		/* Perform crystal calibration. */
5986220674Sbschmidt		error = iwn5000_crystal_calib(sc);
5987198429Srpaulo		if (error != 0) {
5988198429Srpaulo			device_printf(sc->sc_dev,
5989220674Sbschmidt			    "%s: crystal calibration failed, error %d\n",
5990220674Sbschmidt			    __func__, error);
5991198429Srpaulo			return error;
5992198429Srpaulo		}
5993220674Sbschmidt	}
5994220674Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
5995220674Sbschmidt		/* Query calibration from the initialization firmware. */
5996220674Sbschmidt		if ((error = iwn5000_query_calibration(sc)) != 0) {
5997198429Srpaulo			device_printf(sc->sc_dev,
5998220674Sbschmidt			    "%s: could not query calibration, error %d\n",
5999198429Srpaulo			    __func__, error);
6000198429Srpaulo			return error;
6001198429Srpaulo		}
6002198429Srpaulo		/*
6003201209Srpaulo		 * We have the calibration results now, reboot with the
6004201209Srpaulo		 * runtime firmware (call ourselves recursively!)
6005198429Srpaulo		 */
6006198429Srpaulo		iwn_hw_stop(sc);
6007198429Srpaulo		error = iwn_hw_init(sc);
6008198429Srpaulo	} else {
6009220674Sbschmidt		/* Send calibration results to runtime firmware. */
6010220674Sbschmidt		error = iwn5000_send_calibration(sc);
6011198429Srpaulo	}
6012198429Srpaulo	return error;
6013198429Srpaulo}
6014178676Ssam
6015198429Srpaulo/*
6016198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
6017220725Sbschmidt * the NIC internal memory (no DMA transfer).
6018198429Srpaulo */
6019206477Sbschmidtstatic int
6020198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
6021198429Srpaulo{
6022198429Srpaulo	int error, ntries;
6023198429Srpaulo
6024198429Srpaulo	size /= sizeof (uint32_t);
6025198429Srpaulo
6026220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6027198429Srpaulo		return error;
6028198429Srpaulo
6029198429Srpaulo	/* Copy microcode image into NIC memory. */
6030198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
6031198429Srpaulo	    (const uint32_t *)ucode, size);
6032198429Srpaulo
6033198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
6034198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
6035198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
6036198429Srpaulo
6037198429Srpaulo	/* Start boot load now. */
6038198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
6039198429Srpaulo
6040198429Srpaulo	/* Wait for transfer to complete. */
6041198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
6042198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
6043198429Srpaulo		    IWN_BSM_WR_CTRL_START))
6044198429Srpaulo			break;
6045198429Srpaulo		DELAY(10);
6046198429Srpaulo	}
6047198429Srpaulo	if (ntries == 1000) {
6048198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6049198429Srpaulo		    __func__);
6050198429Srpaulo		iwn_nic_unlock(sc);
6051198429Srpaulo		return ETIMEDOUT;
6052198429Srpaulo	}
6053198429Srpaulo
6054198429Srpaulo	/* Enable boot after power up. */
6055198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
6056198429Srpaulo
6057198429Srpaulo	iwn_nic_unlock(sc);
6058198429Srpaulo	return 0;
6059178676Ssam}
6060178676Ssam
6061206477Sbschmidtstatic int
6062198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
6063178676Ssam{
6064198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
6065198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6066178676Ssam	int error;
6067178676Ssam
6068198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
6069198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
6070220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6071198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6072198429Srpaulo	    fw->init.text, fw->init.textsz);
6073220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6074198429Srpaulo
6075198429Srpaulo	/* Tell adapter where to find initialization sections. */
6076220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6077198429Srpaulo		return error;
6078198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6079198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
6080198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6081198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6082198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
6083198429Srpaulo	iwn_nic_unlock(sc);
6084198429Srpaulo
6085198429Srpaulo	/* Load firmware boot code. */
6086198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
6087178676Ssam	if (error != 0) {
6088198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6089198429Srpaulo		    __func__);
6090178676Ssam		return error;
6091178676Ssam	}
6092198429Srpaulo	/* Now press "execute". */
6093198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6094178676Ssam
6095198429Srpaulo	/* Wait at most one second for first alive notification. */
6096220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6097178676Ssam		device_printf(sc->sc_dev,
6098198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6099178676Ssam		    __func__, error);
6100178676Ssam		return error;
6101178676Ssam	}
6102178676Ssam
6103198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
6104198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
6105198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
6106178676Ssam
6107198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
6108198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
6109220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6110198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6111198429Srpaulo	    fw->main.text, fw->main.textsz);
6112220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6113198429Srpaulo
6114198429Srpaulo	/* Tell adapter where to find runtime sections. */
6115220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6116198429Srpaulo		return error;
6117198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6118198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
6119198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6120198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6121198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
6122198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
6123198429Srpaulo	iwn_nic_unlock(sc);
6124198429Srpaulo
6125198429Srpaulo	return 0;
6126198429Srpaulo}
6127198429Srpaulo
6128206477Sbschmidtstatic int
6129198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
6130198429Srpaulo    const uint8_t *section, int size)
6131198429Srpaulo{
6132198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6133198429Srpaulo	int error;
6134198429Srpaulo
6135198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
6136198429Srpaulo	memcpy(dma->vaddr, section, size);
6137220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6138198429Srpaulo
6139220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6140198429Srpaulo		return error;
6141198429Srpaulo
6142198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6143198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
6144198429Srpaulo
6145198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
6146198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
6147198429Srpaulo	    IWN_LOADDR(dma->paddr));
6148198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
6149198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
6150198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
6151198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
6152198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
6153198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
6154198429Srpaulo
6155198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
6156198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6157198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
6158198429Srpaulo
6159198429Srpaulo	iwn_nic_unlock(sc);
6160198429Srpaulo
6161198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
6162220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
6163198429Srpaulo}
6164198429Srpaulo
6165206477Sbschmidtstatic int
6166198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
6167198429Srpaulo{
6168198429Srpaulo	struct iwn_fw_part *fw;
6169198429Srpaulo	int error;
6170198429Srpaulo
6171198429Srpaulo	/* Load the initialization firmware on first boot only. */
6172201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
6173201209Srpaulo	    &sc->fw.main : &sc->fw.init;
6174198429Srpaulo
6175198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
6176198429Srpaulo	    fw->text, fw->textsz);
6177178676Ssam	if (error != 0) {
6178178676Ssam		device_printf(sc->sc_dev,
6179198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6180198429Srpaulo		    __func__, ".text", error);
6181178676Ssam		return error;
6182178676Ssam	}
6183198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
6184198429Srpaulo	    fw->data, fw->datasz);
6185178676Ssam	if (error != 0) {
6186178676Ssam		device_printf(sc->sc_dev,
6187198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6188198429Srpaulo		    __func__, ".data", error);
6189178676Ssam		return error;
6190178676Ssam	}
6191178676Ssam
6192198429Srpaulo	/* Now press "execute". */
6193198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6194198429Srpaulo	return 0;
6195198429Srpaulo}
6196198429Srpaulo
6197210111Sbschmidt/*
6198210111Sbschmidt * Extract text and data sections from a legacy firmware image.
6199210111Sbschmidt */
6200206477Sbschmidtstatic int
6201210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
6202198429Srpaulo{
6203201209Srpaulo	const uint32_t *ptr;
6204210111Sbschmidt	size_t hdrlen = 24;
6205201209Srpaulo	uint32_t rev;
6206198429Srpaulo
6207220661Sbschmidt	ptr = (const uint32_t *)fw->data;
6208201209Srpaulo	rev = le32toh(*ptr++);
6209210111Sbschmidt
6210201209Srpaulo	/* Check firmware API version. */
6211201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
6212201209Srpaulo		device_printf(sc->sc_dev,
6213201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
6214201209Srpaulo		return EINVAL;
6215201209Srpaulo	}
6216201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
6217201209Srpaulo		/* Skip build number (version 2 header). */
6218210111Sbschmidt		hdrlen += 4;
6219201209Srpaulo		ptr++;
6220201209Srpaulo	}
6221210111Sbschmidt	if (fw->size < hdrlen) {
6222220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6223210111Sbschmidt		    __func__, fw->size);
6224210111Sbschmidt		return EINVAL;
6225210111Sbschmidt	}
6226201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
6227201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
6228201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
6229201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
6230201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
6231198429Srpaulo
6232198429Srpaulo	/* Check that all firmware sections fit. */
6233210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
6234210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
6235220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6236210111Sbschmidt		    __func__, fw->size);
6237198429Srpaulo		return EINVAL;
6238178676Ssam	}
6239198429Srpaulo
6240198429Srpaulo	/* Get pointers to firmware sections. */
6241201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
6242198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
6243198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
6244198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
6245198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
6246178676Ssam	return 0;
6247178676Ssam}
6248178676Ssam
6249210111Sbschmidt/*
6250210111Sbschmidt * Extract text and data sections from a TLV firmware image.
6251210111Sbschmidt */
6252220661Sbschmidtstatic int
6253210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
6254210111Sbschmidt    uint16_t alt)
6255210111Sbschmidt{
6256210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
6257210111Sbschmidt	const struct iwn_fw_tlv *tlv;
6258210111Sbschmidt	const uint8_t *ptr, *end;
6259210111Sbschmidt	uint64_t altmask;
6260220866Sbschmidt	uint32_t len, tmp;
6261210111Sbschmidt
6262210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
6263220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6264210111Sbschmidt		    __func__, fw->size);
6265210111Sbschmidt		return EINVAL;
6266210111Sbschmidt	}
6267210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
6268210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
6269220724Sbschmidt		device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
6270210111Sbschmidt		    __func__, le32toh(hdr->signature));
6271210111Sbschmidt		return EINVAL;
6272210111Sbschmidt	}
6273220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
6274220724Sbschmidt	    le32toh(hdr->build));
6275210111Sbschmidt
6276210111Sbschmidt	/*
6277210111Sbschmidt	 * Select the closest supported alternative that is less than
6278210111Sbschmidt	 * or equal to the specified one.
6279210111Sbschmidt	 */
6280210111Sbschmidt	altmask = le64toh(hdr->altmask);
6281210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
6282210111Sbschmidt		alt--;	/* Downgrade. */
6283220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
6284210111Sbschmidt
6285210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
6286210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
6287210111Sbschmidt
6288210111Sbschmidt	/* Parse type-length-value fields. */
6289210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
6290210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
6291210111Sbschmidt		len = le32toh(tlv->len);
6292210111Sbschmidt
6293210111Sbschmidt		ptr += sizeof (*tlv);
6294210111Sbschmidt		if (ptr + len > end) {
6295210111Sbschmidt			device_printf(sc->sc_dev,
6296220724Sbschmidt			    "%s: firmware too short: %zu bytes\n", __func__,
6297220724Sbschmidt			    fw->size);
6298210111Sbschmidt			return EINVAL;
6299210111Sbschmidt		}
6300210111Sbschmidt		/* Skip other alternatives. */
6301210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
6302210111Sbschmidt			goto next;
6303210111Sbschmidt
6304210111Sbschmidt		switch (le16toh(tlv->type)) {
6305210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
6306210111Sbschmidt			fw->main.text = ptr;
6307210111Sbschmidt			fw->main.textsz = len;
6308210111Sbschmidt			break;
6309210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
6310210111Sbschmidt			fw->main.data = ptr;
6311210111Sbschmidt			fw->main.datasz = len;
6312210111Sbschmidt			break;
6313210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
6314210111Sbschmidt			fw->init.text = ptr;
6315210111Sbschmidt			fw->init.textsz = len;
6316210111Sbschmidt			break;
6317210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
6318210111Sbschmidt			fw->init.data = ptr;
6319210111Sbschmidt			fw->init.datasz = len;
6320210111Sbschmidt			break;
6321210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
6322210111Sbschmidt			fw->boot.text = ptr;
6323210111Sbschmidt			fw->boot.textsz = len;
6324210111Sbschmidt			break;
6325220866Sbschmidt		case IWN_FW_TLV_ENH_SENS:
6326220866Sbschmidt			if (!len)
6327220866Sbschmidt				sc->sc_flags |= IWN_FLAG_ENH_SENS;
6328220866Sbschmidt			break;
6329220866Sbschmidt		case IWN_FW_TLV_PHY_CALIB:
6330220866Sbschmidt			tmp = htole32(*ptr);
6331220866Sbschmidt			if (tmp < 253) {
6332220866Sbschmidt				sc->reset_noise_gain = tmp;
6333220866Sbschmidt				sc->noise_gain = tmp + 1;
6334220866Sbschmidt			}
6335220866Sbschmidt			break;
6336210111Sbschmidt		default:
6337210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
6338220724Sbschmidt			    "TLV type %d not handled\n", le16toh(tlv->type));
6339210111Sbschmidt			break;
6340210111Sbschmidt		}
6341220726Sbschmidt next:		/* TLV fields are 32-bit aligned. */
6342210111Sbschmidt		ptr += (len + 3) & ~3;
6343210111Sbschmidt	}
6344210111Sbschmidt	return 0;
6345210111Sbschmidt}
6346210111Sbschmidt
6347206477Sbschmidtstatic int
6348210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
6349210111Sbschmidt{
6350210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
6351210111Sbschmidt	int error;
6352210111Sbschmidt
6353210111Sbschmidt	IWN_UNLOCK(sc);
6354210111Sbschmidt
6355210111Sbschmidt	memset(fw, 0, sizeof (*fw));
6356210111Sbschmidt
6357210111Sbschmidt	/* Read firmware image from filesystem. */
6358210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
6359210111Sbschmidt	if (sc->fw_fp == NULL) {
6360220724Sbschmidt		device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
6361220724Sbschmidt		    __func__, sc->fwname);
6362210111Sbschmidt		IWN_LOCK(sc);
6363210111Sbschmidt		return EINVAL;
6364210111Sbschmidt	}
6365210111Sbschmidt	IWN_LOCK(sc);
6366210111Sbschmidt
6367210111Sbschmidt	fw->size = sc->fw_fp->datasize;
6368210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
6369210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
6370220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6371210111Sbschmidt		    __func__, fw->size);
6372220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6373220661Sbschmidt		sc->fw_fp = NULL;
6374210111Sbschmidt		return EINVAL;
6375210111Sbschmidt	}
6376210111Sbschmidt
6377210111Sbschmidt	/* Retrieve text and data sections. */
6378210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
6379210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
6380210111Sbschmidt	else
6381210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
6382210111Sbschmidt	if (error != 0) {
6383210111Sbschmidt		device_printf(sc->sc_dev,
6384220724Sbschmidt		    "%s: could not read firmware sections, error %d\n",
6385220724Sbschmidt		    __func__, error);
6386220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6387220661Sbschmidt		sc->fw_fp = NULL;
6388210111Sbschmidt		return error;
6389210111Sbschmidt	}
6390210111Sbschmidt
6391210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
6392220728Sbschmidt	if (fw->main.textsz > sc->fw_text_maxsz ||
6393220728Sbschmidt	    fw->main.datasz > sc->fw_data_maxsz ||
6394220728Sbschmidt	    fw->init.textsz > sc->fw_text_maxsz ||
6395220728Sbschmidt	    fw->init.datasz > sc->fw_data_maxsz ||
6396210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
6397210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
6398220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware sections too large\n",
6399220724Sbschmidt		    __func__);
6400220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6401220661Sbschmidt		sc->fw_fp = NULL;
6402210111Sbschmidt		return EINVAL;
6403210111Sbschmidt	}
6404210111Sbschmidt
6405210111Sbschmidt	/* We can proceed with loading the firmware. */
6406210111Sbschmidt	return 0;
6407210111Sbschmidt}
6408210111Sbschmidt
6409210111Sbschmidtstatic int
6410198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
6411198429Srpaulo{
6412198429Srpaulo	int ntries;
6413178676Ssam
6414198429Srpaulo	/* Set "initialization complete" bit. */
6415198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6416198429Srpaulo
6417198429Srpaulo	/* Wait for clock stabilization. */
6418201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
6419198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
6420198429Srpaulo			return 0;
6421201209Srpaulo		DELAY(10);
6422178676Ssam	}
6423198429Srpaulo	device_printf(sc->sc_dev,
6424198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
6425198429Srpaulo	return ETIMEDOUT;
6426198429Srpaulo}
6427178676Ssam
6428206477Sbschmidtstatic int
6429201209Srpauloiwn_apm_init(struct iwn_softc *sc)
6430198429Srpaulo{
6431220721Sbschmidt	uint32_t reg;
6432198429Srpaulo	int error;
6433178676Ssam
6434220725Sbschmidt	/* Disable L0s exit timer (NMI bug workaround). */
6435198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6436220725Sbschmidt	/* Don't wait for ICH L0s (ICH bug workaround). */
6437198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6438178676Ssam
6439220725Sbschmidt	/* Set FH wait threshold to max (HW bug under stress workaround). */
6440198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6441198429Srpaulo
6442201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6443198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6444198429Srpaulo
6445201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6446220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6447201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6448220721Sbschmidt	if (reg & 0x02)	/* L1 Entry enabled. */
6449201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6450201209Srpaulo	else
6451201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6452201209Srpaulo
6453201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6454210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6455198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6456198429Srpaulo
6457201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6458220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
6459198429Srpaulo		return error;
6460198429Srpaulo
6461220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6462198429Srpaulo		return error;
6463201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6464220725Sbschmidt		/* Enable DMA and BSM (Bootstrap State Machine). */
6465201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6466201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6467201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6468201209Srpaulo	} else {
6469201209Srpaulo		/* Enable DMA. */
6470201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6471201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6472201209Srpaulo	}
6473198429Srpaulo	DELAY(20);
6474201209Srpaulo	/* Disable L1-Active. */
6475198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6476198429Srpaulo	iwn_nic_unlock(sc);
6477198429Srpaulo
6478198429Srpaulo	return 0;
6479198429Srpaulo}
6480198429Srpaulo
6481206477Sbschmidtstatic void
6482198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6483178676Ssam{
6484178676Ssam	int ntries;
6485178676Ssam
6486201209Srpaulo	/* Stop busmaster DMA activity. */
6487198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6488178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6489198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6490198429Srpaulo			return;
6491178676Ssam		DELAY(10);
6492178676Ssam	}
6493220726Sbschmidt	device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
6494178676Ssam}
6495178676Ssam
6496206477Sbschmidtstatic void
6497198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6498198429Srpaulo{
6499198429Srpaulo	iwn_apm_stop_master(sc);
6500198429Srpaulo
6501201209Srpaulo	/* Reset the entire device. */
6502198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6503198429Srpaulo	DELAY(10);
6504198429Srpaulo	/* Clear "initialization complete" bit. */
6505198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6506198429Srpaulo}
6507198429Srpaulo
6508206477Sbschmidtstatic int
6509198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
6510178676Ssam{
6511198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
6512198429Srpaulo		/*
6513198429Srpaulo		 * I don't believe this to be correct but this is what the
6514198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
6515198429Srpaulo		 * shifted in IWN_RFCFG_*.
6516198429Srpaulo		 */
6517198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6518198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6519198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6520198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6521198429Srpaulo	}
6522198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6523198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6524198429Srpaulo	return 0;
6525198429Srpaulo}
6526178676Ssam
6527206477Sbschmidtstatic int
6528198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
6529198429Srpaulo{
6530198429Srpaulo	uint32_t tmp;
6531198429Srpaulo	int error;
6532178676Ssam
6533198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
6534198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6535198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6536198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6537198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6538198429Srpaulo	}
6539198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6540198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6541198429Srpaulo
6542220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6543198429Srpaulo		return error;
6544198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
6545201209Srpaulo
6546201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
6547201209Srpaulo		/*
6548201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
6549201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
6550201209Srpaulo		 * in the silicon of 1000 Series.
6551201209Srpaulo		 */
6552201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
6553201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
6554201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
6555201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
6556201209Srpaulo	}
6557198429Srpaulo	iwn_nic_unlock(sc);
6558201209Srpaulo
6559201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
6560201209Srpaulo		/* Use internal power amplifier only. */
6561201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
6562201209Srpaulo	}
6563220676Sbschmidt	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
6564220676Sbschmidt	     sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
6565210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
6566210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
6567206444Sbschmidt	}
6568220729Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
6569220729Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
6570198429Srpaulo	return 0;
6571198429Srpaulo}
6572198429Srpaulo
6573198429Srpaulo/*
6574198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
6575198429Srpaulo */
6576206477Sbschmidtstatic int
6577198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
6578198429Srpaulo{
6579198429Srpaulo	int ntries;
6580198429Srpaulo
6581201209Srpaulo	/* Check if hardware is ready. */
6582201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6583201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6584201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6585201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6586201209Srpaulo			return 0;
6587201209Srpaulo		DELAY(10);
6588201209Srpaulo	}
6589201209Srpaulo
6590201209Srpaulo	/* Hardware not ready, force into ready state. */
6591198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
6592198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
6593198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
6594198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
6595178676Ssam			break;
6596178676Ssam		DELAY(10);
6597178676Ssam	}
6598198429Srpaulo	if (ntries == 15000)
6599178676Ssam		return ETIMEDOUT;
6600198429Srpaulo
6601201209Srpaulo	/* Hardware should be ready now. */
6602198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6603198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6604198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6605198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6606198429Srpaulo			return 0;
6607198429Srpaulo		DELAY(10);
6608178676Ssam	}
6609198429Srpaulo	return ETIMEDOUT;
6610178676Ssam}
6611178676Ssam
6612206477Sbschmidtstatic int
6613198429Srpauloiwn_hw_init(struct iwn_softc *sc)
6614178676Ssam{
6615220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6616198429Srpaulo	int error, chnl, qid;
6617178676Ssam
6618198429Srpaulo	/* Clear pending interrupts. */
6619198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6620178676Ssam
6621220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
6622198429Srpaulo		device_printf(sc->sc_dev,
6623220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
6624220726Sbschmidt		    error);
6625198429Srpaulo		return error;
6626178676Ssam	}
6627178676Ssam
6628198429Srpaulo	/* Select VMAIN power source. */
6629220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6630198429Srpaulo		return error;
6631198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
6632198429Srpaulo	iwn_nic_unlock(sc);
6633178676Ssam
6634198429Srpaulo	/* Perform adapter-specific initialization. */
6635220728Sbschmidt	if ((error = ops->nic_config(sc)) != 0)
6636198429Srpaulo		return error;
6637178676Ssam
6638198429Srpaulo	/* Initialize RX ring. */
6639220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6640198429Srpaulo		return error;
6641198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
6642198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
6643220725Sbschmidt	/* Set physical address of RX ring (256-byte aligned). */
6644198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
6645220725Sbschmidt	/* Set physical address of RX status (16-byte aligned). */
6646198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
6647198429Srpaulo	/* Enable RX. */
6648198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
6649198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
6650198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
6651198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
6652198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
6653198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
6654198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
6655198429Srpaulo	iwn_nic_unlock(sc);
6656198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
6657178676Ssam
6658220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6659198429Srpaulo		return error;
6660178676Ssam
6661198429Srpaulo	/* Initialize TX scheduler. */
6662220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6663178676Ssam
6664220725Sbschmidt	/* Set physical address of "keep warm" page (16-byte aligned). */
6665198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
6666198429Srpaulo
6667198429Srpaulo	/* Initialize TX rings. */
6668220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
6669198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
6670198429Srpaulo
6671220725Sbschmidt		/* Set physical address of TX ring (256-byte aligned). */
6672198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
6673198429Srpaulo		    txq->desc_dma.paddr >> 8);
6674178676Ssam	}
6675198429Srpaulo	iwn_nic_unlock(sc);
6676178676Ssam
6677198429Srpaulo	/* Enable DMA channels. */
6678220728Sbschmidt	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6679198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
6680198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
6681198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
6682198429Srpaulo	}
6683198429Srpaulo
6684198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
6685198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6686198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
6687198429Srpaulo
6688198429Srpaulo	/* Clear pending interrupts. */
6689198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6690198429Srpaulo	/* Enable interrupt coalescing. */
6691198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
6692198429Srpaulo	/* Enable interrupts. */
6693201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6694198429Srpaulo
6695198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
6696198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6697198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6698198429Srpaulo
6699220729Sbschmidt	/* Enable shadow registers. */
6700220729Sbschmidt	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
6701220729Sbschmidt		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
6702220729Sbschmidt
6703220728Sbschmidt	if ((error = ops->load_firmware(sc)) != 0) {
6704178676Ssam		device_printf(sc->sc_dev,
6705220726Sbschmidt		    "%s: could not load firmware, error %d\n", __func__,
6706220726Sbschmidt		    error);
6707198429Srpaulo		return error;
6708178676Ssam	}
6709198429Srpaulo	/* Wait at most one second for firmware alive notification. */
6710220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6711198429Srpaulo		device_printf(sc->sc_dev,
6712198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6713198429Srpaulo		    __func__, error);
6714198429Srpaulo		return error;
6715198429Srpaulo	}
6716198429Srpaulo	/* Do post-firmware initialization. */
6717220728Sbschmidt	return ops->post_alive(sc);
6718198429Srpaulo}
6719178676Ssam
6720206477Sbschmidtstatic void
6721198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
6722198429Srpaulo{
6723198429Srpaulo	int chnl, qid, ntries;
6724178676Ssam
6725198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
6726178676Ssam
6727198429Srpaulo	/* Disable interrupts. */
6728201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
6729198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6730198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
6731201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6732178676Ssam
6733198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
6734198429Srpaulo	iwn_nic_unlock(sc);
6735178676Ssam
6736198429Srpaulo	/* Stop TX scheduler. */
6737220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6738178676Ssam
6739198429Srpaulo	/* Stop all DMA channels. */
6740198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6741220728Sbschmidt		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6742198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
6743198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
6744220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
6745198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
6746198429Srpaulo					break;
6747198429Srpaulo				DELAY(10);
6748198429Srpaulo			}
6749198429Srpaulo		}
6750198429Srpaulo		iwn_nic_unlock(sc);
6751198429Srpaulo	}
6752178676Ssam
6753198429Srpaulo	/* Stop RX ring. */
6754198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
6755178676Ssam
6756198429Srpaulo	/* Reset all TX rings. */
6757220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
6758198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
6759178676Ssam
6760198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6761201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
6762201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6763198429Srpaulo		iwn_nic_unlock(sc);
6764178676Ssam	}
6765198429Srpaulo	DELAY(5);
6766198429Srpaulo	/* Power OFF adapter. */
6767198429Srpaulo	iwn_apm_stop(sc);
6768198429Srpaulo}
6769178676Ssam
6770206477Sbschmidtstatic void
6771220723Sbschmidtiwn_radio_on(void *arg0, int pending)
6772220723Sbschmidt{
6773220723Sbschmidt	struct iwn_softc *sc = arg0;
6774220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6775220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6776220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6777220723Sbschmidt
6778220723Sbschmidt	if (vap != NULL) {
6779220723Sbschmidt		iwn_init(sc);
6780220723Sbschmidt		ieee80211_init(vap);
6781220723Sbschmidt	}
6782220723Sbschmidt}
6783220723Sbschmidt
6784220723Sbschmidtstatic void
6785220723Sbschmidtiwn_radio_off(void *arg0, int pending)
6786220723Sbschmidt{
6787220723Sbschmidt	struct iwn_softc *sc = arg0;
6788220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6789220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6790220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6791220723Sbschmidt
6792220723Sbschmidt	iwn_stop(sc);
6793220723Sbschmidt	if (vap != NULL)
6794220723Sbschmidt		ieee80211_stop(vap);
6795220723Sbschmidt
6796220723Sbschmidt	/* Enable interrupts to get RF toggle notification. */
6797220723Sbschmidt	IWN_LOCK(sc);
6798220723Sbschmidt	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6799220723Sbschmidt	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6800220723Sbschmidt	IWN_UNLOCK(sc);
6801220723Sbschmidt}
6802220723Sbschmidt
6803220723Sbschmidtstatic void
6804198429Srpauloiwn_init_locked(struct iwn_softc *sc)
6805198429Srpaulo{
6806198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6807198429Srpaulo	int error;
6808178676Ssam
6809198429Srpaulo	IWN_LOCK_ASSERT(sc);
6810178676Ssam
6811220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
6812220724Sbschmidt		device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
6813198429Srpaulo		    __func__, error);
6814198429Srpaulo		goto fail;
6815198429Srpaulo	}
6816198429Srpaulo
6817201209Srpaulo	/* Initialize interrupt mask to default value. */
6818201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
6819201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6820201209Srpaulo
6821198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
6822198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
6823178676Ssam		device_printf(sc->sc_dev,
6824201209Srpaulo		    "radio is disabled by hardware switch\n");
6825201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
6826201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
6827201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6828201209Srpaulo		return;
6829178676Ssam	}
6830178676Ssam
6831198429Srpaulo	/* Read firmware images from the filesystem. */
6832220726Sbschmidt	if ((error = iwn_read_firmware(sc)) != 0) {
6833178676Ssam		device_printf(sc->sc_dev,
6834220726Sbschmidt		    "%s: could not read firmware, error %d\n", __func__,
6835220726Sbschmidt		    error);
6836198429Srpaulo		goto fail;
6837178676Ssam	}
6838178676Ssam
6839198429Srpaulo	/* Initialize hardware and upload firmware. */
6840198429Srpaulo	error = iwn_hw_init(sc);
6841201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6842201209Srpaulo	sc->fw_fp = NULL;
6843198429Srpaulo	if (error != 0) {
6844198429Srpaulo		device_printf(sc->sc_dev,
6845220726Sbschmidt		    "%s: could not initialize hardware, error %d\n", __func__,
6846220726Sbschmidt		    error);
6847198429Srpaulo		goto fail;
6848198429Srpaulo	}
6849178676Ssam
6850198429Srpaulo	/* Configure adapter now that it is ready. */
6851220726Sbschmidt	if ((error = iwn_config(sc)) != 0) {
6852178676Ssam		device_printf(sc->sc_dev,
6853220726Sbschmidt		    "%s: could not configure device, error %d\n", __func__,
6854220726Sbschmidt		    error);
6855198429Srpaulo		goto fail;
6856178676Ssam	}
6857178676Ssam
6858178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
6859178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
6860198429Srpaulo
6861220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
6862198429Srpaulo	return;
6863198429Srpaulo
6864220726Sbschmidtfail:	iwn_stop_locked(sc);
6865178676Ssam}
6866178676Ssam
6867206477Sbschmidtstatic void
6868178676Ssamiwn_init(void *arg)
6869178676Ssam{
6870178676Ssam	struct iwn_softc *sc = arg;
6871178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6872178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6873178676Ssam
6874178676Ssam	IWN_LOCK(sc);
6875178676Ssam	iwn_init_locked(sc);
6876178676Ssam	IWN_UNLOCK(sc);
6877178676Ssam
6878178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6879178676Ssam		ieee80211_start_all(ic);
6880178676Ssam}
6881178676Ssam
6882206477Sbschmidtstatic void
6883178676Ssamiwn_stop_locked(struct iwn_softc *sc)
6884178676Ssam{
6885178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6886178676Ssam
6887178676Ssam	IWN_LOCK_ASSERT(sc);
6888178676Ssam
6889178676Ssam	sc->sc_tx_timer = 0;
6890220667Sbschmidt	callout_stop(&sc->watchdog_to);
6891220667Sbschmidt	callout_stop(&sc->calib_to);
6892178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
6893178676Ssam
6894198429Srpaulo	/* Power OFF hardware. */
6895198429Srpaulo	iwn_hw_stop(sc);
6896198429Srpaulo}
6897178676Ssam
6898206477Sbschmidtstatic void
6899178676Ssamiwn_stop(struct iwn_softc *sc)
6900178676Ssam{
6901178676Ssam	IWN_LOCK(sc);
6902178676Ssam	iwn_stop_locked(sc);
6903178676Ssam	IWN_UNLOCK(sc);
6904178676Ssam}
6905178676Ssam
6906178676Ssam/*
6907178676Ssam * Callback from net80211 to start a scan.
6908178676Ssam */
6909178676Ssamstatic void
6910178676Ssamiwn_scan_start(struct ieee80211com *ic)
6911178676Ssam{
6912178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6913178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6914178676Ssam
6915191746Sthompsa	IWN_LOCK(sc);
6916191746Sthompsa	/* make the link LED blink while we're scanning */
6917191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
6918191746Sthompsa	IWN_UNLOCK(sc);
6919178676Ssam}
6920178676Ssam
6921178676Ssam/*
6922178676Ssam * Callback from net80211 to terminate a scan.
6923178676Ssam */
6924178676Ssamstatic void
6925178676Ssamiwn_scan_end(struct ieee80211com *ic)
6926178676Ssam{
6927201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
6928201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
6929201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6930201209Srpaulo
6931201209Srpaulo	IWN_LOCK(sc);
6932201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
6933201209Srpaulo		/* Set link LED to ON status if we are associated */
6934201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
6935201209Srpaulo	}
6936201209Srpaulo	IWN_UNLOCK(sc);
6937178676Ssam}
6938178676Ssam
6939178676Ssam/*
6940178676Ssam * Callback from net80211 to force a channel change.
6941178676Ssam */
6942178676Ssamstatic void
6943178676Ssamiwn_set_channel(struct ieee80211com *ic)
6944178676Ssam{
6945198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
6946178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6947178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6948178676Ssam
6949191746Sthompsa	IWN_LOCK(sc);
6950201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
6951201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
6952201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
6953201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
6954191746Sthompsa	IWN_UNLOCK(sc);
6955178676Ssam}
6956178676Ssam
6957178676Ssam/*
6958178676Ssam * Callback from net80211 to start scanning of the current channel.
6959178676Ssam */
6960178676Ssamstatic void
6961178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6962178676Ssam{
6963178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
6964178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
6965191746Sthompsa	int error;
6966178676Ssam
6967191746Sthompsa	IWN_LOCK(sc);
6968191746Sthompsa	error = iwn_scan(sc);
6969191746Sthompsa	IWN_UNLOCK(sc);
6970191746Sthompsa	if (error != 0)
6971191746Sthompsa		ieee80211_cancel_scan(vap);
6972178676Ssam}
6973178676Ssam
6974178676Ssam/*
6975178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
6976178676Ssam * The intent is to terminate the scan but we just let the firmware
6977178676Ssam * notify us when it's finished as we have no safe way to abort it.
6978178676Ssam */
6979178676Ssamstatic void
6980178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
6981178676Ssam{
6982178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
6983178676Ssam}
6984178676Ssam
6985178676Ssamstatic void
6986198429Srpauloiwn_hw_reset(void *arg0, int pending)
6987178676Ssam{
6988178676Ssam	struct iwn_softc *sc = arg0;
6989178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6990178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6991178676Ssam
6992201209Srpaulo	iwn_stop(sc);
6993191746Sthompsa	iwn_init(sc);
6994191746Sthompsa	ieee80211_notify_radio(ic, 1);
6995191746Sthompsa}
6996